// Generate Compiler Status HTML from jam regression test output -----------// // (C) Copyright Beman Dawes 2002. Permission to copy, // use, modify, sell and distribute this software is granted provided this // copyright notice appears in all copies. This software is provided "as is" // without express or implied warranty, and with no claim as to its // suitability for any purpose. /******************************************************************************* This program was designed to work unchanged on all platforms and configurations. All output which is platform or configuration dependent is obtained from external sources such as the status/Jamfile, the residue from jam execution, the tools/build/xxx-tools.jam files, or the output of the config_info tests. Please avoid adding platform or configuration dependencies during program maintenance. *******************************************************************************/ #include "boost/filesystem/operations.hpp" #include "boost/filesystem/fstream.hpp" namespace fs = boost::filesystem; #include // for abort #include #include #include #include #include #include using std::string; using std::cout; namespace { fs::directory_iterator end_itr; // It's immportant for reliability that we find the same compilers for each // test, and that they match the column header. So save the names at the // time column headings are generated. std::vector compiler_names; fs::ifstream jamfile; string specific_compiler; // convert backslashes to forward slashes -----------------------------------// void convert_backslashes( string & s ) { for ( string::iterator itr = s.begin(); itr != s.end(); ++itr ) if ( *itr == '\\' ) *itr = '/'; } // find_file ---------------------------------------------------------------// // given a directory to recursively search bool find_file( const fs::path & dir_path, const string & name, fs::path & path_found, const string & ignore_dir_named="" ) { if ( !fs::exists( dir_path ) ) return false; for ( fs::directory_iterator itr( dir_path ); itr != end_itr; ++itr ) if ( fs::directory( *itr ) && itr->leaf() != ignore_dir_named ) { if ( find_file( *itr, name, path_found ) ) return true; } else if ( itr->leaf() == name ) { path_found = *itr; return true; } return false; } // platform_desc -----------------------------------------------------------// // from boost-root/status/bin/config_info.test/xxx/.../config_info.output string platform_desc( const fs::path & boost_root_dir ) { string result; fs::path dot_output_path; // the gcc config_info "Detected Platform" sometimes reports "cygwin", so // prefer any of the other compilers. if ( find_file( boost_root_dir << "status/bin/config_info.test", "config_info.output", dot_output_path, "gcc" ) || find_file( boost_root_dir << "status/bin/config_info.test", "config_info.output", dot_output_path ) ) { fs::ifstream file( dot_output_path ); if ( file ) { while( std::getline( file, result ) ) { if ( result.find( "Detected Platform: " ) == 0 ) { result.erase( 0, 19 ); return result; } } result.clear(); } } return result; } // version_desc ------------------------------------------------------------// // from boost-root/status/bin/config_info.test/xxx/.../config_info.output string version_desc( const fs::path & boost_root_dir, const string & compiler_name ) { string result; fs::path dot_output_path; if ( find_file( boost_root_dir << "status/bin/config_info.test" << compiler_name, "config_info.output", dot_output_path ) ) { fs::ifstream file( dot_output_path ); if ( file ) { if( std::getline( file, result ) ) { string::size_type pos = result.find( "version " ); if ( pos != string::npos ) { result.erase( 0, pos+8 ); } else result.clear(); } } } return result; } // compiler_desc -----------------------------------------------------------// // from boost-root/tools/build/xxx-tools.jam string compiler_desc( const fs::path & boost_root_dir, const string & compiler_name ) { string result; fs::path tools_path( boost_root_dir << "tools/build" << compiler_name + "-tools.jam" ); fs::ifstream file( tools_path ); if ( file ) { while( std::getline( file, result ) ) { if ( result.substr( 0, 3 ) == "#//" ) { result.erase( 0, 3 ); return result; } } result.clear(); } return result; } // test_type_desc ----------------------------------------------------------// // from boost-root/status/Jamfile string test_type_desc( const string & test_name ) { // adding "/" and ".c" eliminates a couple of corner cases. // ".c" rather than ".cpp" because regex library includes some .c tests string search_name1( "/" + test_name + ".c" ); string search_name2( " " + test_name + " " ); string result; if ( jamfile.is_open() ) { jamfile.clear(); jamfile.seekg(0); string line; while( std::getline( jamfile, line ) ) { if ( line.find( ".c" ) != string::npos ) { if ( line.find( "run " ) != string::npos ) result = "run"; else if ( line.find( "run-fail " ) != string::npos ) result = "run-fail"; else if ( line.find( "link " ) != string::npos ) result = "link"; else if ( line.find( "link-fail " ) != string::npos ) result = "link-fail"; else if ( line.find( "compile " ) != string::npos ) result = "compile"; else if ( line.find( "compile-fail " ) != string::npos ) result = "compile-fail"; } if ( result.size() && ( line.find( search_name1 ) != string::npos || line.find( search_name2 ) != string::npos ) ) return result; } result.clear(); } return result; } // found_success -----------------------------------------------------------// bool found_success( const fs::path & dir_path, const string & test_name ) { if ( !fs::exists( dir_path ) || !fs::directory( dir_path ) ) return false; if ( fs::exists( dir_path << (test_name + ".success") ) ) return true; for ( fs::directory_iterator itr( dir_path ); itr != end_itr; ++itr ) { if ( fs::directory( *itr ) && found_success( *itr, test_name ) ) return true; } return false; } // 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() ) { 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 target += "" + lib_name + ""; target += "" + test_name + ""; target += "" + test_type_desc( test_name ) + ""; // for each compiler, generate ... html for ( std::vector::const_iterator itr=compiler_names.begin(); itr != compiler_names.end(); ++itr ) { // target += found_success( test_dir << *itr, test_name ) // ? "Pass" // : "Fail"; if ( found_success( test_dir << *itr, test_name ) ) target += "Pass"; else { target += "Fail"; } } target += ""; } // 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 ) { 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 ) { cout << *v << "\n"; } } // do_table ----------------------------------------------------------------// void do_table( const fs::path & boost_root_dir ) { fs::path build_dir( boost_root_dir << "status" << "bin" ); cout << "\n"; // generate the column headings cout << "\n" "\n"; fs::directory_iterator itr( build_dir ); if ( itr != end_itr ) { fs::directory_iterator compiler_itr( *itr ); std::clog << "Using " << itr->generic_path() << " to determine compilers\n"; std::clog << "Requested compiler is " << specific_compiler << "\n"; for (; compiler_itr != end_itr; ++compiler_itr ) { if ( fs::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; std::clog << " " << compiler_itr->leaf() << "\n"; compiler_names.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() ) ); cout << "\n"; } } } cout << "\n"; // now the rest of the table body do_table_body( boost_root_dir, build_dir ); cout << "
LibraryTest NameTest Type" << (desc.size() ? desc : compiler_itr->leaf()) << (vers.size() ? (string( "
" ) + vers ) : string( "" )) << "
\n"; } } // unnamed namespace // main --------------------------------------------------------------------// #define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE #include int cpp_main( int argc, char * argv[] ) // note name! { if ( argc > 2 && std::strcmp( argv[1], "--compiler" ) == 0 ) { specific_compiler = argv[2]; argc -= 2; argv += 2; } fs::path boost_root_dir; if ( argc == 2 ) boost_root_dir = fs::path( argv[1], fs::path::system_specific ); else { std::cerr << "usage: compiler_status [--compiler name] boost-root-directory\n"; return 1; } jamfile.open( boost_root_dir << "status/Jamfile" ); // may fail; that's OK 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 ) ); cout << "\n" "\n" "Boost Compiler Status Automatic Test\n" "\n" "\n" "\n" "\n" "\n" "\n
\n" "

Compiler Status: " + platform_desc( boost_root_dir ) + "

\n" "Run Date: " << run_date << "\n
\n
\n" ; do_table( boost_root_dir ); cout << "\n" "\n" ; return 0; }