" )
!= string::npos ) result.insert( 0, 1, '*' );
return result;
}
}
result.clear();
}
return result;
}
// target_directory --------------------------------------------------------//
// this amounts to a request to find a unique leaf directory
fs::path target_directory( const fs::path & root )
{
if ( !fs::exists( root ) ) return fs::path("no-such-path");
fs::path child;
for ( fs::directory_iterator itr( root ); itr != end_itr; ++itr )
{
if ( fs::is_directory( *itr ) )
{
if ( child.is_null() ) child = *itr;
else throw std::runtime_error(
string( "two target possibilities found: \"" )
+ child.generic_path() + "\" and \""
+ (*itr).generic_path() + "\"" );
}
}
if ( child.is_null() ) return root; // this dir has no children
return target_directory( child );
}
// element_content ---------------------------------------------------------//
const string & element_content(
const xml::element_ptr & root, const string & name )
{
static string empty_string;
xml::element_list::iterator itr;
for ( itr = root->elements.begin();
itr != root->elements.end() && (*itr)->name != name;
++itr ) {}
return itr != root->elements.end() ? (*itr)->content : empty_string;
}
// generate_report ---------------------------------------------------------//
// return 0 if nothing generated, 1 otherwise, except 2 if compiler msgs
int generate_report( const xml::element_ptr & db,
const string & test_name,
const string & toolset,
bool pass,
bool always_show_run_output = false )
{
// compile msgs sometimes modified, so make a local copy
string compile( (pass && no_warn)
? empty_string : element_content( db, "compile" ) );
const string & link( pass ? empty_string : element_content( db, "link" ) );
const string & run( (pass && !always_show_run_output)
? empty_string : element_content( db, "run" ) );
string lib( pass ? empty_string : element_content( db, "lib" ) );
// some compilers output the filename even if there are no errors or
// warnings; detect this if one line of output and it contains no space.
string::size_type pos = compile.find( '\n', 1 );
if ( pos != string::npos && compile.size()-pos <= 2
&& compile.find( ' ' ) == string::npos ) compile.clear();
if ( lib.empty() && compile.empty() && link.empty() && run.empty() )
return 0;
int result = 1; // some kind of msg for sure
// limit compile message length
if ( compile.size() > max_compile_msg_size )
{
compile.erase( max_compile_msg_size );
compile += "...\n (remainder deleted because of excessive size)\n";
}
links_file << "\n";
if ( !compile.empty() )
{
++result;
links_file << "Compiler output:
"
<< compile << "\n";
}
if ( !link.empty() )
links_file << "Linker output:
" << link << "
\n";
if ( !run.empty() )
links_file << "Run output:
" << run << "
\n";
static std::set< string > failed_lib_target_dirs;
if ( !lib.empty() )
{
if ( lib[0] == '\n' ) lib.erase( 0, 1 );
string lib_test_name( extract_test_name( lib ) );
links_file << "Library build failure:
\n"
"See "
<< lib_test_name << " / " << toolset << "";
if ( failed_lib_target_dirs.find( lib ) == failed_lib_target_dirs.end() )
{
failed_lib_target_dirs.insert( lib );
fs::ifstream file( boost_root_dir << lib << "test_log.xml" );
if ( file )
{
xml::element_ptr db = xml::parse( file );
generate_report( db, lib_test_name, toolset, false );
}
else
{
links_file << "\n"
"test_log.xml not found\n";
}
}
}
return result;
}
// do_cell -----------------------------------------------------------------//
bool do_cell( const fs::path & test_dir,
const string & test_name,
const string & toolset,
string & target,
bool always_show_run_output )
// return true if any results except pass_msg
{
fs::path target_dir( target_directory( test_dir << toolset ) );
bool pass = false;
// missing jam residue
if ( fs::exists( target_dir << (test_name + ".success") ) ) pass = true;
else if ( !fs::exists( target_dir << (test_name + ".failure") )
&& !fs::exists( target_dir << "test_log.xml" ) )
{
target += "" + missing_residue_msg + " | ";
return true;
}
int anything_generated = 0;
if ( !no_links )
{
fs::ifstream file( target_dir << "test_log.xml" );
if ( !file ) // missing jam_log.xml
{
std::cerr << "Missing jam_log.xml in target \""
<< target_dir.generic_path() << "\"\n";
target += "";
target += pass ? pass_msg : fail_msg;
target += " | ";
return pass;
}
xml::element_ptr db = xml::parse( file );
// generate bookmarked report of results, and link to it
anything_generated
= generate_report( db, test_name, toolset, pass, always_show_run_output );
}
target += "";
if ( anything_generated != 0 )
{
target += "";
target += pass ? (anything_generated < 2 ? pass_msg : warn_msg) : fail_msg;
target += "";
}
else target += pass ? pass_msg : fail_msg;
target += " | ";
return (anything_generated != 0) || !pass;
}
// do_row ------------------------------------------------------------------//
void do_row( const fs::path & boost_root_dir,
const fs::path & test_dir, // "c:/boost_root/status/bin/any_test.test"
const string & test_name, // "any_test"
string & target )
{
// get the path to the test program from the .test file contents
string test_path( test_name ); // test_name is default if missing .test
fs::path file_path;
if ( find_file( test_dir, test_name + ".test", file_path ) )
{
fs::ifstream file( file_path );
if ( file )
{
std::getline( file, test_path );
if ( test_path.size() )
{
if ( test_path[0] == '\"' ) // added for non-Win32 systems
{
test_path = test_path.erase( 0, 1 ); // strip "
test_path.erase( test_path.find( "\"" ) );
}
// test_path is now a disk path, so convert to URL style path
convert_backslashes( test_path );
string::size_type pos = test_path.find( "/libs/" );
if ( pos != string::npos ) test_path.replace( 0, pos, ".." );
}
}
}
// extract the library name from the test_path
string lib_name( test_path );
string::size_type pos = lib_name.find( "/libs/" );
if ( pos != string::npos )
{
lib_name.erase( 0, pos+6 );
pos = lib_name.find( "/" );
if ( pos != string::npos ) lib_name.erase( pos );
}
else lib_name.clear();
// find the library documentation path
string lib_docs_path( "../libs/" + lib_name + "/" );
fs::path cur_lib_docs_path( boost_root_dir << "libs" << lib_name );
if ( fs::exists( cur_lib_docs_path << "index.htm" ) )
lib_docs_path += "index.htm";
else if ( fs::exists( cur_lib_docs_path << "index.html" ) )
lib_docs_path += "index.html";
else if ( fs::exists( cur_lib_docs_path << "doc/index.htm" ) )
lib_docs_path += "doc/index.htm";
else if ( fs::exists( cur_lib_docs_path << "doc/index.html" ) )
lib_docs_path += "doc/index.html";
else if ( fs::exists( cur_lib_docs_path << (lib_name + ".htm") ) )
lib_docs_path += lib_name + ".htm";
else if ( fs::exists( cur_lib_docs_path << (lib_name + ".html") ) )
lib_docs_path += lib_name + ".html";
else if ( fs::exists( cur_lib_docs_path << "doc" << (lib_name + ".htm") ) )
lib_docs_path += lib_name + ".htm";
else if ( fs::exists( cur_lib_docs_path << "doc" << (lib_name + ".html") ) )
lib_docs_path += lib_name + ".html";
// generate the library name, test name, and test type table data
string::size_type row_start_pos = target.size();
target += "| " + lib_name + " | ";
target += "" + test_name + " | ";
string test_type = test_type_desc( test_name );
bool always_show_run_output = false;
if ( !test_type.empty() && test_type[0] == '*' )
{ always_show_run_output = true; test_type.erase( 0, 1 ); }
target += "" + test_type + " | ";
bool no_warn_save = no_warn;
if ( test_type.find( "fail" ) != string::npos ) no_warn = true;
// for each compiler, generate ... | html
bool anything_to_report = false;
for ( std::vector::const_iterator itr=toolsets.begin();
itr != toolsets.end(); ++itr )
{
anything_to_report |= do_cell( test_dir, test_name, *itr, target,
always_show_run_output );
}
target += "
";
if ( ignore_pass && !anything_to_report ) target.erase( row_start_pos );
no_warn = no_warn_save;
}
// do_table_body -----------------------------------------------------------//
void do_table_body(
const fs::path & boost_root_dir, const fs::path & build_dir )
{
// rows are held in a vector so they can be sorted, if desired.
std::vector results;
// each test directory
for ( fs::directory_iterator itr( build_dir ); itr != end_itr; ++itr )
{
if ( fs::is_directory( *itr ) )
{
results.push_back( std::string() ); // no sort required, but leave code
// in place in case that changes
do_row( boost_root_dir, *itr,
itr->leaf().substr( 0, itr->leaf().size()-5 ),
results[results.size()-1] );
}
}
std::sort( results.begin(), results.end() );
for ( std::vector::iterator v(results.begin());
v != results.end(); ++v )
{ report << *v << "\n"; }
}
// do_table ----------------------------------------------------------------//
void do_table( const fs::path & boost_root_dir )
{
// fs::path build_dir( boost_root_dir << "status" << "bin" );
fs::path build_dir( fs::initial_directory() << "bin" );
report << "\n";
// generate the column headings
report << "| Library | Test Name | \n"
"Test Type | \n";
fs::directory_iterator itr( build_dir );
if ( itr != end_itr )
{
fs::directory_iterator compiler_itr( *itr );
if ( specific_compiler.empty() )
std::clog << "Using " << itr->generic_path() << " to determine compilers\n";
for (; compiler_itr != end_itr; ++compiler_itr )
{
if ( fs::is_directory( *compiler_itr ) // check just to be sure
&& compiler_itr->leaf() != "test" ) // avoid strange directory (Jamfile bug?)
{
if ( specific_compiler.size() != 0
&& specific_compiler != compiler_itr->leaf() ) continue;
toolsets.push_back( compiler_itr->leaf() );
string desc( compiler_desc( boost_root_dir,
compiler_itr->leaf() ) );
string vers( version_desc( boost_root_dir,
compiler_itr->leaf() ) );
report << ""
<< (desc.size() ? desc : compiler_itr->leaf())
<< (vers.size() ? (string( " " ) + vers ) : string( "" ))
<< " | \n";
}
}
}
report << "
\n";
// now the rest of the table body
do_table_body( boost_root_dir, build_dir );
report << "
\n";
}
} // unnamed namespace
// main --------------------------------------------------------------------//
#define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE
#include
int cpp_main( int argc, char * argv[] ) // note name!
{
while ( argc > 1 && *argv[1] == '-' )
{
if ( argc > 2 && std::strcmp( argv[1], "--compiler" ) == 0 )
{ specific_compiler = argv[2]; --argc; ++argv; }
else if ( std::strcmp( argv[1], "--ignore-pass" ) == 0 ) ignore_pass = true;
else if ( std::strcmp( argv[1], "--no-warn" ) == 0 ) no_warn = true;
else { std::cerr << "Unknown option: " << argv[1] << "\n"; argc = 1; }
--argc;
++argv;
}
if ( argc != 3 && argc != 4 )
{
std::cerr <<
"usage: compiler_status [options...] boost-root-dir status-file [links-file]\n"
"must be run from directory containing Jamfile\n"
" options: --compiler name Run for named compiler only\n"
" --ignore-pass Do not report tests which pass all compilers\n"
" --no-warn Warnings not reported if test passes\n"
"example: compiler_status --compiler gcc \\boost-root cs.html cs-links.html\n";
return 1;
}
boost_root_dir = fs::path( argv[1], fs::system_specific );
fs::path jamfile_path( fs::initial_directory() << "Jamfile" );
jamfile.open( jamfile_path );
if ( !jamfile )
{
std::cerr << "Could not open Jamfile: " << jamfile_path.file_path() << std::endl;
return 1;
}
report.open( argv[2] );
if ( !report )
{
std::cerr << "Could not open report output file: " << argv[2] << std::endl;
return 1;
}
if ( argc == 4 )
{
links_name = argv[3];
links_file.open( links_name );
if ( !links_file )
{
std::cerr << "Could not open links output file: " << links_name << std::endl;
return 1;
}
}
else no_links = true;
char run_date[128];
std::time_t tod;
std::time( &tod );
std::strftime( run_date, sizeof(run_date),
"%X UTC, %A %d %B %Y", std::gmtime( &tod ) );
report << "\n"
"\n"
"Boost Compiler Status Automatic Test\n"
"\n"
"\n"
"\n"
"\n"
" | \n"
"\n"
"Compiler Status: " + platform_desc( boost_root_dir ) + "\n"
"Run Date: "
<< run_date
<< "\n | \n
\n
\n"
;
do_table( boost_root_dir );
report << "\n"
"\n"
;
return 0;
}