compiler_status.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. // Generate Compiler Status HTML from jam regression test output -----------//
  2. // Copyright Beman Dawes 2002.
  3. // See accompanying license for terms and conditions of use.
  4. // See http://www.boost.org/tools/regression for documentation.
  5. /*******************************************************************************
  6. This program was designed to work unchanged on all platforms and
  7. configurations. All output which is platform or configuration dependent
  8. is obtained from external sources such as the .xml file from
  9. process_jam_log execution, the tools/build/xxx-tools.jam files, or the
  10. output of the config_info tests.
  11. Please avoid adding platform or configuration dependencies during
  12. program maintenance.
  13. *******************************************************************************/
  14. #include "boost/filesystem/operations.hpp"
  15. #include "boost/filesystem/fstream.hpp"
  16. #include "detail/tiny_xml.hpp"
  17. namespace fs = boost::filesystem;
  18. namespace xml = boost::tiny_xml;
  19. #include <cstdlib> // for abort, exit
  20. #include <string>
  21. #include <vector>
  22. #include <set>
  23. #include <map>
  24. #include <algorithm>
  25. #include <iostream>
  26. #include <fstream>
  27. #include <ctime>
  28. #include <stdexcept>
  29. using std::string;
  30. const string pass_msg( "Pass" );
  31. const string warn_msg( "<i>Warn</i>" );
  32. const string fail_msg( "<font color=\"#FF0000\"><i>Fail</i></font>" );
  33. const string note_msg( "<sup>*</sup>" );
  34. const string missing_residue_msg( "<i>Missing</i>" );
  35. const std::size_t max_compile_msg_size = 10000;
  36. namespace
  37. {
  38. fs::path boost_root; // boost-root complete path
  39. fs::path locate_root; // locate-root (AKA ALL_LOCATE_TARGET) complete path
  40. bool ignore_pass;
  41. bool no_warn;
  42. bool no_links;
  43. fs::directory_iterator end_itr;
  44. // It's immportant for reliability that we find the same compilers for each
  45. // test, and that they match the column header. So save the names at the
  46. // time column headings are generated.
  47. std::vector<string> toolsets;
  48. fs::ifstream jamfile;
  49. fs::ofstream report;
  50. fs::ofstream links_file;
  51. string links_name;
  52. fs::path notes_path;
  53. string notes_html;
  54. fs::path notes_map_path;
  55. typedef std::multimap< string, string > notes_map; // key is test_name-toolset,
  56. // value is note bookmark
  57. notes_map notes;
  58. string specific_compiler; // if running on one toolset only
  59. const string empty_string;
  60. // build notes_bookmarks from notes HTML -----------------------------------//
  61. void build_notes_bookmarks()
  62. {
  63. if ( notes_map_path.empty() ) return;
  64. fs::ifstream notes_map_file( notes_map_path );
  65. if ( !notes_map_file )
  66. {
  67. std::cerr << "Could not open --notes-map input file: " << notes_map_path.string() << std::endl;
  68. std::exit( 1 );
  69. }
  70. string line;
  71. while( std::getline( notes_map_file, line ) )
  72. {
  73. string::size_type pos = 0;
  74. if ( (pos = line.find( ',', pos )) == string::npos ) continue;
  75. string key(line.substr( 0, pos ) );
  76. string bookmark( line.substr( pos+1 ) );
  77. // std::cout << "inserting \"" << key << "\",\"" << bookmark << "\"\n";
  78. notes.insert( std::make_pair( key, bookmark ) );
  79. }
  80. }
  81. // load_notes_html ---------------------------------------------------------//
  82. bool load_notes_html()
  83. {
  84. if ( notes_path.empty() ) return false;
  85. fs::ifstream notes_file( notes_path );
  86. if ( !notes_file )
  87. {
  88. std::cerr << "Could not open --notes input file: " << notes_path.string() << std::endl;
  89. std::exit( 1 );
  90. }
  91. string line;
  92. bool in_body( false );
  93. while( std::getline( notes_file, line ) )
  94. {
  95. if ( in_body && line.find( "</body>" ) != string::npos ) in_body = false;
  96. if ( in_body ) notes_html += line;
  97. else if ( line.find( "<body>" ) ) in_body = true;
  98. }
  99. return true;
  100. }
  101. // relative path between two paths -----------------------------------------//
  102. void relative_path( const fs::path & from, const fs::path & to,
  103. fs::path & target )
  104. {
  105. if ( from.string().size() <= to.string().size() ) return;
  106. target /= "..";
  107. relative_path( from.branch_path(), to, target );
  108. return;
  109. }
  110. // extract test name from target directory string --------------------------//
  111. string extract_test_name( const string & s )
  112. {
  113. string t( s );
  114. string::size_type pos = t.find( "/bin/" );
  115. if ( pos != string::npos ) pos += 5;
  116. else return "";
  117. return t.substr( pos, t.find( ".", pos ) - pos );
  118. }
  119. // find_file ---------------------------------------------------------------//
  120. // given a directory to recursively search
  121. bool find_file( const fs::path & dir_path, const string & name,
  122. fs::path & path_found, const string & ignore_dir_named="" )
  123. {
  124. if ( !fs::exists( dir_path ) ) return false;
  125. for ( fs::directory_iterator itr( dir_path ); itr != end_itr; ++itr )
  126. if ( fs::is_directory( *itr )
  127. && itr->leaf() != ignore_dir_named )
  128. {
  129. if ( find_file( *itr, name, path_found ) ) return true;
  130. }
  131. else if ( itr->leaf() == name )
  132. {
  133. path_found = *itr;
  134. return true;
  135. }
  136. return false;
  137. }
  138. // platform_desc -----------------------------------------------------------//
  139. // from locate_root/status/bin/config_info.test/xxx/.../config_info.output
  140. string platform_desc()
  141. {
  142. string result;
  143. fs::path dot_output_path;
  144. // the gcc config_info "Detected Platform" sometimes reports "cygwin", so
  145. // prefer any of the other compilers.
  146. if ( find_file( locate_root / "bin/boost/status/config_info.test",
  147. "config_info.output", dot_output_path, "gcc" )
  148. || find_file( locate_root / "bin/boost/status/config_info.test",
  149. "config_info.output", dot_output_path )
  150. || find_file( locate_root / "status/bin/config_info.test",
  151. "config_info.output", dot_output_path, "gcc" )
  152. || find_file( locate_root / "status/bin/config_info.test",
  153. "config_info.output", dot_output_path ) )
  154. {
  155. fs::ifstream file( dot_output_path );
  156. if ( file )
  157. {
  158. while( std::getline( file, result ) )
  159. {
  160. if ( result.find( "Detected Platform: " ) == 0 )
  161. {
  162. result.erase( 0, 19 );
  163. return result;
  164. }
  165. }
  166. result.clear();
  167. }
  168. }
  169. return result;
  170. }
  171. // version_desc ------------------------------------------------------------//
  172. // from locate-root/status/bin/config_info.test/xxx/.../config_info.output
  173. string version_desc( const string & compiler_name )
  174. {
  175. string result;
  176. fs::path dot_output_path;
  177. if ( find_file( locate_root / "bin/boost/status/config_info.test"
  178. / compiler_name, "config_info.output", dot_output_path )
  179. || find_file( locate_root / "status/bin/config_info.test"
  180. / compiler_name, "config_info.output", dot_output_path ) )
  181. {
  182. fs::ifstream file( dot_output_path );
  183. if ( file )
  184. {
  185. if( std::getline( file, result ) )
  186. {
  187. string::size_type pos = result.find( "version " );
  188. if ( pos != string::npos )
  189. {
  190. result.erase( 0, pos+8 );
  191. }
  192. else result.clear();
  193. }
  194. }
  195. }
  196. return result;
  197. }
  198. // compiler_desc -----------------------------------------------------------//
  199. // from boost-root/tools/build/xxx-tools.jam
  200. string compiler_desc( const string & compiler_name )
  201. {
  202. string result;
  203. fs::path tools_path( boost_root / "tools/build" / (compiler_name
  204. + "-tools.jam") );
  205. fs::ifstream file( tools_path );
  206. if ( file )
  207. {
  208. while( std::getline( file, result ) )
  209. {
  210. if ( result.substr( 0, 3 ) == "#//" )
  211. {
  212. result.erase( 0, 3 );
  213. return result;
  214. }
  215. }
  216. result.clear();
  217. }
  218. return result;
  219. }
  220. // target_directory --------------------------------------------------------//
  221. // this amounts to a request to find a unique leaf directory
  222. fs::path target_directory( const fs::path & root )
  223. {
  224. if ( !fs::exists( root ) ) return fs::path("no-such-path");
  225. fs::path child;
  226. for ( fs::directory_iterator itr( root ); itr != end_itr; ++itr )
  227. {
  228. if ( fs::is_directory( *itr ) )
  229. {
  230. // SunCC creates an internal subdirectory everywhere it writes
  231. // object files. This confuses the target_directory() algorithm.
  232. // This patch ignores the SunCC internal directory. Jens Maurer
  233. if ( (*itr).leaf() == "SunWS_cache" ) continue;
  234. // SGI does something similar for template instantiations. Jens Maurer
  235. if( (*itr).leaf() == "ii_files" ) continue;
  236. if ( child.empty() ) child = *itr;
  237. else
  238. {
  239. std::cout << "Warning: only first of two target possibilities will be reported for: \n "
  240. << root.string() << ": " << child.leaf()
  241. << " and " << (*itr).leaf() << "\n";
  242. }
  243. }
  244. }
  245. if ( child.empty() ) return root; // this dir has no children
  246. return target_directory( child );
  247. }
  248. // element_content ---------------------------------------------------------//
  249. const string & element_content(
  250. const xml::element & root, const string & name )
  251. {
  252. static string empty_string;
  253. xml::element_list::const_iterator itr;
  254. for ( itr = root.elements.begin();
  255. itr != root.elements.end() && (*itr)->name != name;
  256. ++itr ) {}
  257. return itr != root.elements.end() ? (*itr)->content : empty_string;
  258. }
  259. // find_element ------------------------------------------------------------//
  260. const xml::element & find_element(
  261. const xml::element & root, const string & name )
  262. {
  263. static xml::element empty_element;
  264. xml::element_list::const_iterator itr;
  265. for ( itr = root.elements.begin();
  266. itr != root.elements.end() && (*itr)->name != name;
  267. ++itr ) {}
  268. return itr != root.elements.end() ? *((*itr).get()) : empty_element;
  269. }
  270. // attribute_value ----------------------------------------------------------//
  271. const string & attribute_value( const xml::element & element,
  272. const string & attribute_name )
  273. {
  274. static const string empty_string;
  275. xml::attribute_list::const_iterator atr;
  276. for ( atr = element.attributes.begin();
  277. atr != element.attributes.end() && atr->name != attribute_name;
  278. ++atr ) {}
  279. return atr == element.attributes.end() ? empty_string : atr->value;
  280. }
  281. // generate_report ---------------------------------------------------------//
  282. // return 0 if nothing generated, 1 otherwise, except 2 if compiler msgs
  283. int generate_report( const xml::element & db,
  284. const string & test_name,
  285. const string & toolset,
  286. bool pass,
  287. bool always_show_run_output = false )
  288. {
  289. // compile msgs sometimes modified, so make a local copy
  290. string compile( (pass && no_warn)
  291. ? empty_string : element_content( db, "compile" ) );
  292. const string & link( pass ? empty_string : element_content( db, "link" ) );
  293. const string & run( (pass && !always_show_run_output)
  294. ? empty_string : element_content( db, "run" ) );
  295. string lib( pass ? empty_string : element_content( db, "lib" ) );
  296. // some compilers output the filename even if there are no errors or
  297. // warnings; detect this if one line of output and it contains no space.
  298. string::size_type pos = compile.find( '\n', 1 );
  299. if ( pos != string::npos && compile.size()-pos <= 2
  300. && compile.find( ' ' ) == string::npos ) compile.clear();
  301. if ( lib.empty() && compile.empty() && link.empty() && run.empty() )
  302. return 0;
  303. int result = 1; // some kind of msg for sure
  304. // limit compile message length
  305. if ( compile.size() > max_compile_msg_size )
  306. {
  307. compile.erase( max_compile_msg_size );
  308. compile += "...\n (remainder deleted because of excessive size)\n";
  309. }
  310. links_file << "<h2><a name=\""
  311. << test_name << "-" << toolset << "\">"
  312. << test_name << " / " << toolset << "</a></h2>\n";
  313. if ( !compile.empty() )
  314. {
  315. ++result;
  316. links_file << "<h3>Compiler output:</h3><pre>"
  317. << compile << "</pre>\n";
  318. }
  319. if ( !link.empty() )
  320. links_file << "<h3>Linker output:</h3><pre>" << link << "</pre>\n";
  321. if ( !run.empty() )
  322. links_file << "<h3>Run output:</h3><pre>" << run << "</pre>\n";
  323. static std::set< string > failed_lib_target_dirs;
  324. if ( !lib.empty() )
  325. {
  326. if ( lib[0] == '\n' ) lib.erase( 0, 1 );
  327. string lib_test_name( extract_test_name( lib ) );
  328. links_file << "<h3>Library build failure: </h3>\n"
  329. "See <a href=\"#" << lib_test_name << "-" << toolset << "\">"
  330. << lib_test_name << " / " << toolset << "</a>";
  331. if ( failed_lib_target_dirs.find( lib ) == failed_lib_target_dirs.end() )
  332. {
  333. failed_lib_target_dirs.insert( lib );
  334. fs::path pth( locate_root / lib / "test_log.xml" );
  335. fs::ifstream file( pth );
  336. if ( file )
  337. {
  338. xml::element_ptr db = xml::parse( file, pth.string() );
  339. generate_report( *db, lib_test_name, toolset, false );
  340. }
  341. else
  342. {
  343. links_file << "<h2><a name=\""
  344. << lib_test_name << "-" << toolset << "\">"
  345. << lib_test_name << " / " << toolset << "</a></h2>\n"
  346. "test_log.xml not found\n";
  347. }
  348. }
  349. }
  350. return result;
  351. }
  352. // do_notes --------------------------------------------------------------//
  353. void do_notes( const string & key, string & sep, string & target )
  354. {
  355. notes_map::const_iterator itr = notes.lower_bound( key );
  356. if ( itr != notes.end() && itr->first == key )
  357. {
  358. target += "<sup>";
  359. for ( ; itr != notes.end() && itr->first == key; ++itr )
  360. {
  361. target += sep;
  362. sep = ",";
  363. target += "<a href=\"";
  364. target += "#";
  365. target += itr->second;
  366. target += "\">";
  367. target += itr->second;
  368. target += "</a>";
  369. }
  370. target += "</sup>";
  371. }
  372. }
  373. // do_cell ---------------------------------------------------------------//
  374. bool do_cell( const string & lib_name,
  375. const fs::path & test_dir,
  376. const string & test_name,
  377. const string & toolset,
  378. string & target,
  379. bool always_show_run_output )
  380. // return true if any results except pass_msg
  381. {
  382. fs::path target_dir( target_directory( test_dir / toolset ) );
  383. bool pass = false;
  384. // missing jam residue
  385. if ( fs::exists( target_dir / (test_name + ".test") ) ) pass = true;
  386. else if ( !fs::exists( target_dir / "test_log.xml" ) )
  387. {
  388. target += "<td>" + missing_residue_msg + "</td>";
  389. return true;
  390. }
  391. int anything_generated = 0;
  392. bool note = false;
  393. // create links file entry
  394. if ( !no_links )
  395. {
  396. fs::path pth( target_dir / "test_log.xml" );
  397. fs::ifstream file( pth );
  398. if ( !file ) // missing jam_log.xml
  399. {
  400. std::cerr << "Missing jam_log.xml in target:\n "
  401. << target_dir.string() << "\n";
  402. target += "<td>";
  403. target += pass ? pass_msg : fail_msg;
  404. target += "</td>";
  405. return pass;
  406. }
  407. xml::element_ptr dbp = xml::parse( file, pth.string() );
  408. const xml::element & db( *dbp );
  409. note = attribute_value( find_element( db, "run" ), "result" ) == "note";
  410. // generate bookmarked report of results, and link to it
  411. anything_generated
  412. = generate_report( db, test_name, toolset, pass,
  413. always_show_run_output || note );
  414. }
  415. // generate the status table cell pass/warn/fail HTML
  416. target += "<td>";
  417. if ( anything_generated != 0 )
  418. {
  419. target += "<a href=\"";
  420. target += links_name;
  421. target += "#";
  422. target += test_name;
  423. target += "-";
  424. target += toolset;
  425. target += "\">";
  426. target += pass ? (anything_generated < 2 ? pass_msg : warn_msg) : fail_msg;
  427. target += "</a>";
  428. if ( pass && note ) target += note_msg;
  429. }
  430. else target += pass ? pass_msg : fail_msg;
  431. // if notes, generate the HTML
  432. if ( !notes.empty() )
  433. {
  434. // test-specific notes
  435. string key( toolset );
  436. key += "/";
  437. key += test_name;
  438. string sep;
  439. do_notes( key, sep, target );
  440. // library-wide notes
  441. key = toolset;
  442. key += "/*";
  443. key += lib_name;
  444. do_notes( key, sep, target );
  445. }
  446. target += "</td>";
  447. return (anything_generated != 0) || !pass;
  448. }
  449. // do_row ------------------------------------------------------------------//
  450. void do_row(
  451. const fs::path & test_dir, // locate_root / "status/bin/any_test.test"
  452. const string & test_name, // "any_test"
  453. string & target )
  454. {
  455. // get library name, test-type, test-program path, etc., from the .xml file
  456. string lib_name;
  457. string test_path( test_name ); // test_name is default if missing .test
  458. string test_type( "unknown" );
  459. bool always_show_run_output( false );
  460. fs::path xml_file_path;
  461. if ( find_file( test_dir, "test_log.xml", xml_file_path ) )
  462. {
  463. fs::ifstream file( xml_file_path );
  464. if ( file )
  465. {
  466. xml::element_ptr dbp = xml::parse( file, xml_file_path.string() );
  467. const xml::element & db( *dbp );
  468. test_path = attribute_value( db, "test-program" );
  469. lib_name = attribute_value( db, "library" );
  470. test_type = attribute_value( db, "test-type" );
  471. always_show_run_output
  472. = attribute_value( db, "show-run-output" ) == "true";
  473. }
  474. }
  475. // path to docs
  476. fs::path rel;
  477. relative_path( fs::initial_path(), boost_root, rel );
  478. string lib_docs_path( rel.string() + "/libs/" + lib_name );
  479. // generate the library name, test name, and test type table data
  480. string::size_type row_start_pos = target.size();
  481. target += "<tr><td><a href=\"" + lib_docs_path + "\">" + lib_name + "</a></td>";
  482. target += "<td><a href=\"" + (rel / test_path).string() + "\">" + test_name + "</a></td>";
  483. target += "<td>" + test_type + "</td>";
  484. bool no_warn_save = no_warn;
  485. if ( test_type.find( "fail" ) != string::npos ) no_warn = true;
  486. // for each compiler, generate <td>...</td> html
  487. bool anything_to_report = false;
  488. for ( std::vector<string>::const_iterator itr=toolsets.begin();
  489. itr != toolsets.end(); ++itr )
  490. {
  491. anything_to_report |= do_cell( lib_name, test_dir, test_name, *itr, target,
  492. always_show_run_output );
  493. }
  494. target += "</tr>";
  495. if ( ignore_pass && !anything_to_report ) target.erase( row_start_pos );
  496. no_warn = no_warn_save;
  497. }
  498. // do_rows_for_sub_tree ----------------------------------------------------//
  499. void do_rows_for_sub_tree(
  500. const fs::path & bin_dir, std::vector<string> & results )
  501. {
  502. for ( fs::directory_iterator itr( bin_dir ); itr != end_itr; ++itr )
  503. {
  504. if ( fs::is_directory( *itr )
  505. && itr->string().find( ".test" ) == (itr->string().size()-5) )
  506. {
  507. results.push_back( std::string() );
  508. do_row( *itr,
  509. itr->leaf().substr( 0, itr->leaf().size()-5 ),
  510. results[results.size()-1] );
  511. }
  512. }
  513. }
  514. // do_table_body -----------------------------------------------------------//
  515. void do_table_body( const fs::path & bin_dir )
  516. {
  517. // rows are held in a vector so they can be sorted, if desired.
  518. std::vector<string> results;
  519. // do primary bin directory
  520. do_rows_for_sub_tree( bin_dir, results );
  521. // do subinclude bin directories
  522. jamfile.clear();
  523. jamfile.seekg(0);
  524. string line;
  525. while( std::getline( jamfile, line ) )
  526. {
  527. string::size_type pos( line.find( "subinclude" ) );
  528. if ( pos == string::npos )
  529. pos = line.find( "build-project" );
  530. if ( pos != string::npos
  531. && line.find( '#' ) > pos )
  532. {
  533. pos = line.find_first_not_of( " \t./", pos+10 );
  534. if ( pos == string::npos ) continue;
  535. string subinclude_bin_dir(
  536. line.substr( pos, line.find_first_of( " \t", pos )-pos ) );
  537. // std::cout << "subinclude: " << subinclude_bin_dir << '\n';
  538. fs::path subinclude_path( locate_root / "bin/boost" / subinclude_bin_dir );
  539. if ( fs::exists( subinclude_path ) )
  540. { do_rows_for_sub_tree( subinclude_path, results ); continue; }
  541. subinclude_path = fs::path( locate_root / "bin"
  542. / subinclude_bin_dir / "bin" );
  543. if ( fs::exists( subinclude_path ) )
  544. { do_rows_for_sub_tree( subinclude_path, results ); continue; }
  545. subinclude_path = fs::path( locate_root / subinclude_bin_dir / "/bin" );
  546. if ( fs::exists( subinclude_path ) )
  547. { do_rows_for_sub_tree( subinclude_path, results ); }
  548. }
  549. }
  550. std::sort( results.begin(), results.end() );
  551. for ( std::vector<string>::iterator v(results.begin());
  552. v != results.end(); ++v )
  553. { report << *v << "\n"; }
  554. }
  555. // do_table ----------------------------------------------------------------//
  556. void do_table()
  557. {
  558. // Find test result locations, trying:
  559. // - Boost.Build V1 location with ALL_LOCATE_TARGET
  560. // - Boost.Build V2 location with top-lelve "build-dir"
  561. // - Boost.Build V1 location without ALL_LOCATE_TARGET
  562. fs::path bin_path( locate_root / "bin/boost/status" );
  563. if (!fs::exists(bin_path))
  564. {
  565. bin_path = locate_root / "bin/status/bin";
  566. if (!fs::exists(bin_path))
  567. {
  568. string relative( fs::initial_path().string() );
  569. relative.erase( 0, boost_root.string().size()+1 );
  570. bin_path = fs::path( locate_root / relative / "bin" );
  571. }
  572. }
  573. report << "<table border=\"1\" cellspacing=\"0\" cellpadding=\"5\">\n";
  574. // generate the column headings
  575. report << "<tr><td>Library</td><td>Test Name</td>\n"
  576. "<td><a href=\"compiler_status.html#test-type\">Test Type</a></td>\n";
  577. fs::directory_iterator itr( bin_path );
  578. while ( itr != end_itr && !fs::is_directory( *itr )
  579. && itr->string().find( ".test" ) != (itr->string().size()-5) )
  580. ++itr; // bypass chaff
  581. if ( itr != end_itr )
  582. {
  583. fs::directory_iterator compiler_itr( *itr );
  584. if ( specific_compiler.empty() )
  585. std::clog << "Using " << itr->string() << " to determine compilers\n";
  586. for (; compiler_itr != end_itr; ++compiler_itr )
  587. {
  588. if ( fs::is_directory( *compiler_itr ) // check just to be sure
  589. && compiler_itr->leaf() != "test" ) // avoid strange directory (Jamfile bug?)
  590. {
  591. if ( specific_compiler.size() != 0
  592. && specific_compiler != compiler_itr->leaf() ) continue;
  593. toolsets.push_back( compiler_itr->leaf() );
  594. string desc( compiler_desc( compiler_itr->leaf() ) );
  595. string vers( version_desc( compiler_itr->leaf() ) );
  596. report << "<td>"
  597. << (desc.size() ? desc : compiler_itr->leaf())
  598. << (vers.size() ? (string( "<br>" ) + vers ) : string( "" ))
  599. << "</td>\n";
  600. }
  601. }
  602. }
  603. report << "</tr>\n";
  604. // now the rest of the table body
  605. do_table_body( bin_path );
  606. report << "</table>\n";
  607. }
  608. } // unnamed namespace
  609. // main --------------------------------------------------------------------//
  610. #define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE
  611. #include <boost/test/included/prg_exec_monitor.hpp>
  612. int cpp_main( int argc, char * argv[] ) // note name!
  613. {
  614. fs::path comment_path;
  615. while ( argc > 1 && *argv[1] == '-' )
  616. {
  617. if ( argc > 2 && std::strcmp( argv[1], "--compiler" ) == 0 )
  618. { specific_compiler = argv[2]; --argc; ++argv; }
  619. else if ( argc > 2 && std::strcmp( argv[1], "--locate-root" ) == 0 )
  620. { locate_root = fs::path( argv[2], fs::native ); --argc; ++argv; }
  621. else if ( argc > 2 && std::strcmp( argv[1], "--comment" ) == 0 )
  622. { comment_path = fs::path( argv[2], fs::native ); --argc; ++argv; }
  623. else if ( argc > 2 && std::strcmp( argv[1], "--notes" ) == 0 )
  624. { notes_path = fs::path( argv[2], fs::native ); --argc; ++argv; }
  625. else if ( argc > 2 && std::strcmp( argv[1], "--notes-map" ) == 0 )
  626. { notes_map_path = fs::path( argv[2], fs::native ); --argc; ++argv; }
  627. else if ( std::strcmp( argv[1], "--ignore-pass" ) == 0 ) ignore_pass = true;
  628. else if ( std::strcmp( argv[1], "--no-warn" ) == 0 ) no_warn = true;
  629. else { std::cerr << "Unknown option: " << argv[1] << "\n"; argc = 1; }
  630. --argc;
  631. ++argv;
  632. }
  633. if ( argc != 3 && argc != 4 )
  634. {
  635. std::cerr <<
  636. "Usage: compiler_status [options...] boost-root status-file [links-file]\n"
  637. " boost-root is the path to the boost tree root directory.\n"
  638. " status-file and links-file are paths to the output files.\n"
  639. "Must be run from directory containing Jamfile\n"
  640. " options: --compiler name Run for named compiler only\n"
  641. " --ignore-pass Do not report tests which pass all compilers\n"
  642. " --no-warn Warnings not reported if test passes\n"
  643. " --locate-root path Path to ALL_LOCATE_TARGET for bjam;\n"
  644. " default boost-root.\n"
  645. " --comment path Path to file containing HTML\n"
  646. " to be copied into status-file.\n"
  647. " --notes path Path to file containing HTML\n"
  648. " to be copied into status-file.\n"
  649. " --notes-map path Path to file of toolset/test,n lines, where\n"
  650. " n is number of note bookmark in --notes file.\n"
  651. "Example: compiler_status --compiler gcc /boost-root cs.html cs-links.html\n"
  652. "Note: Only the leaf of the links-file path and --notes file string are\n"
  653. "used in status-file HTML links. Thus for browsing, status-file,\n"
  654. "links-file, and --notes file must all be in the same directory.\n"
  655. ;
  656. return 1;
  657. }
  658. boost_root = fs::path( argv[1], fs::native );
  659. if ( locate_root.empty() ) locate_root = boost_root;
  660. fs::path jamfile_path( fs::initial_path() / "Jamfile" );
  661. jamfile.open( jamfile_path );
  662. if ( !jamfile )
  663. {
  664. std::cerr << "Could not open Jamfile: " << jamfile_path.native_file_string() << std::endl;
  665. return 1;
  666. }
  667. report.open( fs::path( argv[2], fs::native ) );
  668. if ( !report )
  669. {
  670. std::cerr << "Could not open report output file: " << argv[2] << std::endl;
  671. return 1;
  672. }
  673. if ( argc == 4 )
  674. {
  675. fs::path links_path( argv[3], fs::native );
  676. links_name = links_path.leaf();
  677. links_file.open( links_path );
  678. if ( !links_file )
  679. {
  680. std::cerr << "Could not open links output file: " << argv[3] << std::endl;
  681. return 1;
  682. }
  683. }
  684. else no_links = true;
  685. build_notes_bookmarks();
  686. char run_date[128];
  687. std::time_t tod;
  688. std::time( &tod );
  689. std::strftime( run_date, sizeof(run_date),
  690. "%X UTC, %A %d %B %Y", std::gmtime( &tod ) );
  691. report << "<html>\n"
  692. "<head>\n"
  693. "<title>Boost Compiler Status Automatic Test</title>\n"
  694. "</head>\n"
  695. "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
  696. "<table border=\"0\">\n"
  697. "<tr>\n"
  698. "<td><img border=\"0\" src=\"../c++boost.gif\" width=\"277\" "
  699. "height=\"86\"></td>\n"
  700. "<td>\n"
  701. "<h1>Compiler Status: " + platform_desc() + "</h1>\n"
  702. "<b>Run Date:</b> "
  703. << run_date
  704. << "\n"
  705. ;
  706. if ( !comment_path.empty() )
  707. {
  708. fs::ifstream comment_file( comment_path );
  709. if ( !comment_file )
  710. {
  711. std::cerr << "Could not open \"--comment\" input file: " << comment_path.string() << std::endl;
  712. return 1;
  713. }
  714. char c;
  715. while ( comment_file.get( c ) ) { report.put( c ); }
  716. }
  717. report << "</td>\n</table>\n<br>\n";
  718. if ( !no_links )
  719. {
  720. links_file
  721. << "<html>\n"
  722. "<head>\n"
  723. "<title>Boost Compiler Status Error Log</title>\n"
  724. "</head>\n"
  725. "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
  726. "<table border=\"0\">\n"
  727. "<tr>\n"
  728. "<td><img border=\"0\" src=\"../c++boost.gif\" width=\"277\" "
  729. "height=\"86\"></td>\n"
  730. "<td>\n"
  731. "<h1>Compiler Status: " + platform_desc() + "</h1>\n"
  732. "<b>Run Date:</b> "
  733. << run_date
  734. << "\n</td>\n</table>\n<br>\n"
  735. ;
  736. }
  737. do_table();
  738. if ( load_notes_html() ) report << notes_html << "\n";
  739. report << "</body>\n"
  740. "</html>\n"
  741. ;
  742. if ( !no_links )
  743. {
  744. links_file
  745. << "</body>\n"
  746. "</html>\n"
  747. ;
  748. }
  749. return 0;
  750. }
粤ICP备19079148号