compiler_status.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. // Generate Compiler Status HTML from jam regression test output -----------//
  2. // (C) Copyright Beman Dawes 2002. Permission to copy,
  3. // use, modify, sell and distribute this software is granted provided this
  4. // copyright notice appears in all copies. This software is provided "as is"
  5. // without express or implied warranty, and with no claim as to its
  6. // suitability for any purpose.
  7. /*******************************************************************************
  8. This program was designed to work unchanged on all platforms and
  9. configurations. All output which is platform or configuration dependent
  10. is obtained from external sources such as the Jamfile, the residue
  11. from jam execution, the tools/build/xxx-tools.jam files, or the output
  12. of the config_info tests.
  13. Please avoid adding platform or configuration dependencies during
  14. program maintenance.
  15. *******************************************************************************/
  16. #include "boost/filesystem/operations.hpp"
  17. #include "boost/filesystem/fstream.hpp"
  18. #include "detail/tiny_xml.hpp"
  19. namespace fs = boost::filesystem;
  20. namespace xml = boost::tiny_xml;
  21. #include <cstdlib> // for abort
  22. #include <string>
  23. #include <vector>
  24. #include <set>
  25. #include <algorithm>
  26. #include <iostream>
  27. #include <fstream>
  28. #include <ctime>
  29. #include <stdexcept>
  30. using std::string;
  31. const string pass_msg( "Pass" );
  32. const string warn_msg( "<font color=\"#FF9900\"><i>Warn</i></font>" );
  33. const string fail_msg( "<font color=\"#FF0000\">"
  34. "<big><i>Fail</i></big>"
  35. "</font>" );
  36. const string missing_residue_msg( "<i>Missing</i>" );
  37. const std::size_t max_compile_msg_size = 10000;
  38. namespace
  39. {
  40. fs::path boost_root_dir;
  41. bool ignore_pass;
  42. bool no_warn;
  43. bool no_links;
  44. fs::directory_iterator end_itr;
  45. // It's immportant for reliability that we find the same compilers for each
  46. // test, and that they match the column header. So save the names at the
  47. // time column headings are generated.
  48. std::vector<string> toolsets;
  49. fs::ifstream jamfile;
  50. fs::ofstream report;
  51. fs::ofstream links_file;
  52. string links_name;
  53. string specific_compiler; // if running on one toolset only
  54. const string empty_string;
  55. // convert backslashes to forward slashes -----------------------------------//
  56. void convert_backslashes( string & s )
  57. {
  58. for ( string::iterator itr = s.begin(); itr != s.end(); ++itr )
  59. if ( *itr == '\\' ) *itr = '/';
  60. }
  61. // extra information from target directory string ---------------------------//
  62. string extract_test_name( const string & s )
  63. {
  64. string t( s );
  65. string::size_type pos = t.find( "/bin/" );
  66. if ( pos != string::npos ) pos += 5;
  67. else return "";
  68. return t.substr( pos, t.find( ".", pos ) - pos );
  69. }
  70. // find_file ---------------------------------------------------------------//
  71. // given a directory to recursively search
  72. bool find_file( const fs::path & dir_path, const string & name,
  73. fs::path & path_found, const string & ignore_dir_named="" )
  74. {
  75. if ( !fs::exists( dir_path ) ) return false;
  76. for ( fs::directory_iterator itr( dir_path ); itr != end_itr; ++itr )
  77. if ( fs::is_directory( *itr )
  78. && itr->leaf() != ignore_dir_named )
  79. {
  80. if ( find_file( *itr, name, path_found ) ) return true;
  81. }
  82. else if ( itr->leaf() == name )
  83. {
  84. path_found = *itr;
  85. return true;
  86. }
  87. return false;
  88. }
  89. // platform_desc -----------------------------------------------------------//
  90. // from boost-root/status/bin/config_info.test/xxx/.../config_info.output
  91. string platform_desc( const fs::path & boost_root_dir )
  92. {
  93. string result;
  94. fs::path dot_output_path;
  95. // the gcc config_info "Detected Platform" sometimes reports "cygwin", so
  96. // prefer any of the other compilers.
  97. if ( find_file( boost_root_dir << "status/bin/config_info.test",
  98. "config_info.output", dot_output_path, "gcc" )
  99. || find_file( boost_root_dir << "status/bin/config_info.test",
  100. "config_info.output", dot_output_path ) )
  101. {
  102. fs::ifstream file( dot_output_path );
  103. if ( file )
  104. {
  105. while( std::getline( file, result ) )
  106. {
  107. if ( result.find( "Detected Platform: " ) == 0 )
  108. {
  109. result.erase( 0, 19 );
  110. return result;
  111. }
  112. }
  113. result.clear();
  114. }
  115. }
  116. return result;
  117. }
  118. // version_desc ------------------------------------------------------------//
  119. // from boost-root/status/bin/config_info.test/xxx/.../config_info.output
  120. string version_desc( const fs::path & boost_root_dir,
  121. const string & compiler_name )
  122. {
  123. string result;
  124. fs::path dot_output_path;
  125. if ( find_file( boost_root_dir << "status/bin/config_info.test"
  126. << compiler_name, "config_info.output", dot_output_path ) )
  127. {
  128. fs::ifstream file( dot_output_path );
  129. if ( file )
  130. {
  131. if( std::getline( file, result ) )
  132. {
  133. string::size_type pos = result.find( "version " );
  134. if ( pos != string::npos )
  135. {
  136. result.erase( 0, pos+8 );
  137. }
  138. else result.clear();
  139. }
  140. }
  141. }
  142. return result;
  143. }
  144. // compiler_desc -----------------------------------------------------------//
  145. // from boost-root/tools/build/xxx-tools.jam
  146. string compiler_desc( const fs::path & boost_root_dir,
  147. const string & compiler_name )
  148. {
  149. string result;
  150. fs::path tools_path( boost_root_dir << "tools/build" << compiler_name
  151. + "-tools.jam" );
  152. fs::ifstream file( tools_path );
  153. if ( file )
  154. {
  155. while( std::getline( file, result ) )
  156. {
  157. if ( result.substr( 0, 3 ) == "#//" )
  158. {
  159. result.erase( 0, 3 );
  160. return result;
  161. }
  162. }
  163. result.clear();
  164. }
  165. return result;
  166. }
  167. // test_type_desc ----------------------------------------------------------//
  168. // from jamfile
  169. string test_type_desc( const string & test_name )
  170. {
  171. // adding "/" and ".c" eliminates a couple of corner cases.
  172. // ".c" rather than ".cpp" because regex library includes some .c tests
  173. string search_name1( "/" + test_name + ".c" );
  174. string search_name2( " " + test_name + " " );
  175. string result;
  176. if ( jamfile.is_open() )
  177. {
  178. jamfile.clear();
  179. jamfile.seekg(0);
  180. string line;
  181. while( std::getline( jamfile, line ) )
  182. {
  183. if ( line.find( ".c" ) != string::npos )
  184. {
  185. if ( line.find( "run " ) != string::npos )
  186. result = "run";
  187. else if ( line.find( "run-fail " ) != string::npos )
  188. result = "run-fail";
  189. else if ( line.find( "link " ) != string::npos )
  190. result = "link";
  191. else if ( line.find( "link-fail " ) != string::npos )
  192. result = "link-fail";
  193. else if ( line.find( "compile " ) != string::npos )
  194. result = "compile";
  195. else if ( line.find( "compile-fail " ) != string::npos )
  196. result = "compile-fail";
  197. }
  198. if ( result.size() &&
  199. ( line.find( search_name1 ) != string::npos
  200. || line.find( search_name2 ) != string::npos ) )
  201. {
  202. if ( line.find( "# compiler_status<always_show_run_output>" )
  203. != string::npos ) result.insert( 0, 1, '*' );
  204. return result;
  205. }
  206. }
  207. result.clear();
  208. }
  209. return result;
  210. }
  211. // target_directory --------------------------------------------------------//
  212. // this amounts to a request to find a unique leaf directory
  213. fs::path target_directory( const fs::path & root )
  214. {
  215. if ( !fs::exists( root ) ) return fs::path("no-such-path");
  216. fs::path child;
  217. for ( fs::directory_iterator itr( root ); itr != end_itr; ++itr )
  218. {
  219. if ( fs::is_directory( *itr ) )
  220. {
  221. if ( child.is_null() ) child = *itr;
  222. else throw std::runtime_error(
  223. string( "two target possibilities found: \"" )
  224. + child.generic_path() + "\" and \""
  225. + (*itr).generic_path() + "\"" );
  226. }
  227. }
  228. if ( child.is_null() ) return root; // this dir has no children
  229. return target_directory( child );
  230. }
  231. // element_content ---------------------------------------------------------//
  232. const string & element_content(
  233. const xml::element_ptr & root, const string & name )
  234. {
  235. static string empty_string;
  236. xml::element_list::iterator itr;
  237. for ( itr = root->elements.begin();
  238. itr != root->elements.end() && (*itr)->name != name;
  239. ++itr ) {}
  240. return itr != root->elements.end() ? (*itr)->content : empty_string;
  241. }
  242. // generate_report ---------------------------------------------------------//
  243. // return 0 if nothing generated, 1 otherwise, except 2 if compiler msgs
  244. int generate_report( const xml::element_ptr & db,
  245. const string & test_name,
  246. const string & toolset,
  247. bool pass,
  248. bool always_show_run_output = false )
  249. {
  250. // compile msgs sometimes modified, so make a local copy
  251. string compile( (pass && no_warn)
  252. ? empty_string : element_content( db, "compile" ) );
  253. const string & link( pass ? empty_string : element_content( db, "link" ) );
  254. const string & run( (pass && !always_show_run_output)
  255. ? empty_string : element_content( db, "run" ) );
  256. string lib( pass ? empty_string : element_content( db, "lib" ) );
  257. // some compilers output the filename even if there are no errors or
  258. // warnings; detect this if one line of output and it contains no space.
  259. string::size_type pos = compile.find( '\n', 1 );
  260. if ( pos != string::npos && compile.size()-pos <= 2
  261. && compile.find( ' ' ) == string::npos ) compile.clear();
  262. if ( lib.empty() && compile.empty() && link.empty() && run.empty() )
  263. return 0;
  264. int result = 1; // some kind of msg for sure
  265. // limit compile message length
  266. if ( compile.size() > max_compile_msg_size )
  267. {
  268. compile.erase( max_compile_msg_size );
  269. compile += "...\n (remainder deleted because of excessive size)\n";
  270. }
  271. links_file << "<h2><a name=\""
  272. << test_name << " " << toolset << "\">"
  273. << test_name << " / " << toolset << "</a></h2>\n";
  274. if ( !compile.empty() )
  275. {
  276. ++result;
  277. links_file << "<h3>Compiler output:</h3><pre>"
  278. << compile << "</pre>\n";
  279. }
  280. if ( !link.empty() )
  281. links_file << "<h3>Linker output:</h3><pre>" << link << "</pre>\n";
  282. if ( !run.empty() )
  283. links_file << "<h3>Run output:</h3><pre>" << run << "</pre>\n";
  284. static std::set< string > failed_lib_target_dirs;
  285. if ( !lib.empty() )
  286. {
  287. if ( lib[0] == '\n' ) lib.erase( 0, 1 );
  288. string lib_test_name( extract_test_name( lib ) );
  289. links_file << "<h3>Library build failure: </h3>\n"
  290. "See <a href=\"#" << lib_test_name << " " << toolset << "\">"
  291. << lib_test_name << " / " << toolset << "</a>";
  292. if ( failed_lib_target_dirs.find( lib ) == failed_lib_target_dirs.end() )
  293. {
  294. failed_lib_target_dirs.insert( lib );
  295. fs::ifstream file( boost_root_dir << lib << "test_log.xml" );
  296. if ( file )
  297. {
  298. xml::element_ptr db = xml::parse( file );
  299. generate_report( db, lib_test_name, toolset, false );
  300. }
  301. else
  302. {
  303. links_file << "<h2><a name=\""
  304. << lib_test_name << " " << toolset << "\">"
  305. << lib_test_name << " / " << toolset << "</a></h2>\n"
  306. "test_log.xml not found\n";
  307. }
  308. }
  309. }
  310. return result;
  311. }
  312. // do_cell -----------------------------------------------------------------//
  313. bool do_cell( const fs::path & test_dir,
  314. const string & test_name,
  315. const string & toolset,
  316. string & target,
  317. bool always_show_run_output )
  318. // return true if any results except pass_msg
  319. {
  320. fs::path target_dir( target_directory( test_dir << toolset ) );
  321. bool pass = false;
  322. // missing jam residue
  323. if ( fs::exists( target_dir << (test_name + ".success") ) ) pass = true;
  324. else if ( !fs::exists( target_dir << (test_name + ".failure") )
  325. && !fs::exists( target_dir << "test_log.xml" ) )
  326. {
  327. target += "<td>" + missing_residue_msg + "</td>";
  328. return true;
  329. }
  330. int anything_generated = 0;
  331. if ( !no_links )
  332. {
  333. fs::ifstream file( target_dir << "test_log.xml" );
  334. if ( !file ) // missing jam_log.xml
  335. {
  336. std::cerr << "Missing jam_log.xml in target \""
  337. << target_dir.generic_path() << "\"\n";
  338. target += "<td>";
  339. target += pass ? pass_msg : fail_msg;
  340. target += "</td>";
  341. return pass;
  342. }
  343. xml::element_ptr db = xml::parse( file );
  344. // generate bookmarked report of results, and link to it
  345. anything_generated
  346. = generate_report( db, test_name, toolset, pass, always_show_run_output );
  347. }
  348. target += "<td>";
  349. if ( anything_generated != 0 )
  350. {
  351. target += "<a href=\"";
  352. target += links_name;
  353. target += "#";
  354. target += test_name;
  355. target += " ";
  356. target += toolset;
  357. target += "\">";
  358. target += pass ? (anything_generated < 2 ? pass_msg : warn_msg) : fail_msg;
  359. target += "</a>";
  360. }
  361. else target += pass ? pass_msg : fail_msg;
  362. target += "</td>";
  363. return (anything_generated != 0) || !pass;
  364. }
  365. // do_row ------------------------------------------------------------------//
  366. void do_row( const fs::path & boost_root_dir,
  367. const fs::path & test_dir, // "c:/boost_root/status/bin/any_test.test"
  368. const string & test_name, // "any_test"
  369. string & target )
  370. {
  371. // get the path to the test program from the .test file contents
  372. string test_path( test_name ); // test_name is default if missing .test
  373. fs::path file_path;
  374. if ( find_file( test_dir, test_name + ".test", file_path ) )
  375. {
  376. fs::ifstream file( file_path );
  377. if ( file )
  378. {
  379. std::getline( file, test_path );
  380. if ( test_path.size() )
  381. {
  382. if ( test_path[0] == '\"' ) // added for non-Win32 systems
  383. {
  384. test_path = test_path.erase( 0, 1 ); // strip "
  385. test_path.erase( test_path.find( "\"" ) );
  386. }
  387. // test_path is now a disk path, so convert to URL style path
  388. convert_backslashes( test_path );
  389. string::size_type pos = test_path.find( "/libs/" );
  390. if ( pos != string::npos ) test_path.replace( 0, pos, ".." );
  391. }
  392. }
  393. }
  394. // extract the library name from the test_path
  395. string lib_name( test_path );
  396. string::size_type pos = lib_name.find( "/libs/" );
  397. if ( pos != string::npos )
  398. {
  399. lib_name.erase( 0, pos+6 );
  400. pos = lib_name.find( "/" );
  401. if ( pos != string::npos ) lib_name.erase( pos );
  402. }
  403. else lib_name.clear();
  404. // find the library documentation path
  405. string lib_docs_path( "../libs/" + lib_name + "/" );
  406. fs::path cur_lib_docs_path( boost_root_dir << "libs" << lib_name );
  407. if ( fs::exists( cur_lib_docs_path << "index.htm" ) )
  408. lib_docs_path += "index.htm";
  409. else if ( fs::exists( cur_lib_docs_path << "index.html" ) )
  410. lib_docs_path += "index.html";
  411. else if ( fs::exists( cur_lib_docs_path << "doc/index.htm" ) )
  412. lib_docs_path += "doc/index.htm";
  413. else if ( fs::exists( cur_lib_docs_path << "doc/index.html" ) )
  414. lib_docs_path += "doc/index.html";
  415. else if ( fs::exists( cur_lib_docs_path << (lib_name + ".htm") ) )
  416. lib_docs_path += lib_name + ".htm";
  417. else if ( fs::exists( cur_lib_docs_path << (lib_name + ".html") ) )
  418. lib_docs_path += lib_name + ".html";
  419. else if ( fs::exists( cur_lib_docs_path << "doc" << (lib_name + ".htm") ) )
  420. lib_docs_path += lib_name + ".htm";
  421. else if ( fs::exists( cur_lib_docs_path << "doc" << (lib_name + ".html") ) )
  422. lib_docs_path += lib_name + ".html";
  423. // generate the library name, test name, and test type table data
  424. string::size_type row_start_pos = target.size();
  425. target += "<tr><td><a href=\"" + lib_docs_path + "\">" + lib_name + "</a></td>";
  426. target += "<td><a href=\"" + test_path + "\">" + test_name + "</a></td>";
  427. string test_type = test_type_desc( test_name );
  428. bool always_show_run_output = false;
  429. if ( !test_type.empty() && test_type[0] == '*' )
  430. { always_show_run_output = true; test_type.erase( 0, 1 ); }
  431. target += "<td>" + test_type + "</td>";
  432. bool no_warn_save = no_warn;
  433. if ( test_type.find( "fail" ) != string::npos ) no_warn = true;
  434. // for each compiler, generate <td>...</td> html
  435. bool anything_to_report = false;
  436. for ( std::vector<string>::const_iterator itr=toolsets.begin();
  437. itr != toolsets.end(); ++itr )
  438. {
  439. anything_to_report |= do_cell( test_dir, test_name, *itr, target,
  440. always_show_run_output );
  441. }
  442. target += "</tr>";
  443. if ( ignore_pass && !anything_to_report ) target.erase( row_start_pos );
  444. no_warn = no_warn_save;
  445. }
  446. // do_table_body -----------------------------------------------------------//
  447. void do_table_body(
  448. const fs::path & boost_root_dir, const fs::path & build_dir )
  449. {
  450. // rows are held in a vector so they can be sorted, if desired.
  451. std::vector<string> results;
  452. // each test directory
  453. for ( fs::directory_iterator itr( build_dir ); itr != end_itr; ++itr )
  454. {
  455. if ( fs::is_directory( *itr ) )
  456. {
  457. results.push_back( std::string() ); // no sort required, but leave code
  458. // in place in case that changes
  459. do_row( boost_root_dir, *itr,
  460. itr->leaf().substr( 0, itr->leaf().size()-5 ),
  461. results[results.size()-1] );
  462. }
  463. }
  464. std::sort( results.begin(), results.end() );
  465. for ( std::vector<string>::iterator v(results.begin());
  466. v != results.end(); ++v )
  467. { report << *v << "\n"; }
  468. }
  469. // do_table ----------------------------------------------------------------//
  470. void do_table( const fs::path & boost_root_dir )
  471. {
  472. // fs::path build_dir( boost_root_dir << "status" << "bin" );
  473. fs::path build_dir( fs::initial_directory() << "bin" );
  474. report << "<table border=\"1\" cellspacing=\"0\" cellpadding=\"5\">\n";
  475. // generate the column headings
  476. report << "<tr><td>Library</td><td>Test Name</td>\n"
  477. "<td><a href=\"compiler_status.html#test-type\">Test Type</a></td>\n";
  478. fs::directory_iterator itr( build_dir );
  479. if ( itr != end_itr )
  480. {
  481. fs::directory_iterator compiler_itr( *itr );
  482. if ( specific_compiler.empty() )
  483. std::clog << "Using " << itr->generic_path() << " to determine compilers\n";
  484. for (; compiler_itr != end_itr; ++compiler_itr )
  485. {
  486. if ( fs::is_directory( *compiler_itr ) // check just to be sure
  487. && compiler_itr->leaf() != "test" ) // avoid strange directory (Jamfile bug?)
  488. {
  489. if ( specific_compiler.size() != 0
  490. && specific_compiler != compiler_itr->leaf() ) continue;
  491. toolsets.push_back( compiler_itr->leaf() );
  492. string desc( compiler_desc( boost_root_dir,
  493. compiler_itr->leaf() ) );
  494. string vers( version_desc( boost_root_dir,
  495. compiler_itr->leaf() ) );
  496. report << "<td>"
  497. << (desc.size() ? desc : compiler_itr->leaf())
  498. << (vers.size() ? (string( "<br>" ) + vers ) : string( "" ))
  499. << "</td>\n";
  500. }
  501. }
  502. }
  503. report << "</tr>\n";
  504. // now the rest of the table body
  505. do_table_body( boost_root_dir, build_dir );
  506. report << "</table>\n";
  507. }
  508. } // unnamed namespace
  509. // main --------------------------------------------------------------------//
  510. #define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE
  511. #include <boost/test/included/prg_exec_monitor.hpp>
  512. int cpp_main( int argc, char * argv[] ) // note name!
  513. {
  514. while ( argc > 1 && *argv[1] == '-' )
  515. {
  516. if ( argc > 2 && std::strcmp( argv[1], "--compiler" ) == 0 )
  517. { specific_compiler = argv[2]; --argc; ++argv; }
  518. else if ( std::strcmp( argv[1], "--ignore-pass" ) == 0 ) ignore_pass = true;
  519. else if ( std::strcmp( argv[1], "--no-warn" ) == 0 ) no_warn = true;
  520. else { std::cerr << "Unknown option: " << argv[1] << "\n"; argc = 1; }
  521. --argc;
  522. ++argv;
  523. }
  524. if ( argc != 3 && argc != 4 )
  525. {
  526. std::cerr <<
  527. "usage: compiler_status [options...] boost-root-dir status-file [links-file]\n"
  528. "must be run from directory containing Jamfile\n"
  529. " options: --compiler name Run for named compiler only\n"
  530. " --ignore-pass Do not report tests which pass all compilers\n"
  531. " --no-warn Warnings not reported if test passes\n"
  532. "example: compiler_status --compiler gcc \\boost-root cs.html cs-links.html\n";
  533. return 1;
  534. }
  535. boost_root_dir = fs::path( argv[1], fs::system_specific );
  536. fs::path jamfile_path( fs::initial_directory() << "Jamfile" );
  537. jamfile.open( jamfile_path );
  538. if ( !jamfile )
  539. {
  540. std::cerr << "Could not open Jamfile: " << jamfile_path.file_path() << std::endl;
  541. return 1;
  542. }
  543. report.open( argv[2] );
  544. if ( !report )
  545. {
  546. std::cerr << "Could not open report output file: " << argv[2] << std::endl;
  547. return 1;
  548. }
  549. if ( argc == 4 )
  550. {
  551. links_name = argv[3];
  552. links_file.open( links_name );
  553. if ( !links_file )
  554. {
  555. std::cerr << "Could not open links output file: " << links_name << std::endl;
  556. return 1;
  557. }
  558. }
  559. else no_links = true;
  560. char run_date[128];
  561. std::time_t tod;
  562. std::time( &tod );
  563. std::strftime( run_date, sizeof(run_date),
  564. "%X UTC, %A %d %B %Y", std::gmtime( &tod ) );
  565. report << "<html>\n"
  566. "<head>\n"
  567. "<title>Boost Compiler Status Automatic Test</title>\n"
  568. "</head>\n"
  569. "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
  570. "<table border=\"0\">\n"
  571. "<tr>\n"
  572. "<td><img border=\"0\" src=\"../c++boost.gif\" width=\"277\" "
  573. "height=\"86\"></td>\n"
  574. "<td>\n"
  575. "<h1>Compiler Status: " + platform_desc( boost_root_dir ) + "</h1>\n"
  576. "<b>Run Date:</b> "
  577. << run_date
  578. << "\n</td>\n</table>\n<br>\n"
  579. ;
  580. do_table( boost_root_dir );
  581. report << "</body>\n"
  582. "</html>\n"
  583. ;
  584. return 0;
  585. }
粤ICP备19079148号