library_status.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. // Generate Compiler Status HTML from jam regression test output -----------//
  2. // Copyright Beman Dawes 2002. Distributed under the Boost
  3. // Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. // See http://www.boost.org/tools/regression/ for documentation.
  6. //Note: This version of the original program builds a large table
  7. //which includes all build variations such as build/release, static/dynamic, etc.
  8. /*******************************************************************************
  9. This program was designed to work unchanged on all platforms and
  10. configurations. All output which is platform or configuration dependent
  11. is obtained from external sources such as the .xml file from
  12. process_jam_log execution, the tools/build/xxx-tools.jam files, or the
  13. output of the config_info tests.
  14. Please avoid adding platform or configuration dependencies during
  15. program maintenance.
  16. *******************************************************************************/
  17. #include "boost/filesystem/operations.hpp"
  18. #include "boost/filesystem/fstream.hpp"
  19. namespace fs = boost::filesystem;
  20. #include "detail/tiny_xml.hpp"
  21. namespace xml = boost::tiny_xml;
  22. #include "boost/iterator/transform_iterator.hpp"
  23. #include <cstdlib> // for abort, exit
  24. #include <string>
  25. #include <vector>
  26. #include <set>
  27. #include <utility> // for make_pair on STLPort
  28. #include <map>
  29. #include <algorithm>
  30. #include <iostream>
  31. #include <fstream>
  32. #include <ctime>
  33. #include <stdexcept>
  34. #include <cassert>
  35. #include <utility>
  36. using std::string;
  37. const string pass_msg( "Pass" );
  38. const string warn_msg( "<i>Warn</i>" );
  39. const string fail_msg( "<font color=\"#FF0000\"><i>Fail</i></font>" );
  40. const string note_msg( "<sup>*</sup>" );
  41. const string missing_residue_msg( "<i>Missing</i>" );
  42. const std::size_t max_compile_msg_size = 10000;
  43. namespace
  44. {
  45. fs::path boost_root; // boost-root complete path
  46. fs::path locate_root; // locate-root (AKA ALL_LOCATE_TARGET) complete path
  47. bool ignore_pass = false;
  48. bool no_warn = false;
  49. bool no_links = false;
  50. fs::directory_iterator end_itr;
  51. // transform pathname to something html can accept
  52. struct char_xlate {
  53. typedef char result_type;
  54. result_type operator()(char c) const{
  55. if(c == '/')
  56. return '-';
  57. return c;
  58. }
  59. };
  60. typedef boost::transform_iterator<char_xlate, std::string::const_iterator> html_from_path;
  61. template<class I1, class I2>
  62. std::ostream & operator<<(
  63. std::ostream &os,
  64. std::pair<I1, I2> p
  65. ){
  66. while(p.first != p.second)
  67. os << *p.first++;
  68. return os;
  69. }
  70. struct col_node {
  71. int rows, cols;
  72. bool has_leaf;
  73. typedef std::map<std::string, col_node> subcolumns_t;
  74. subcolumns_t m_subcolumns;
  75. bool operator<(const col_node &cn) const;
  76. col_node() :
  77. has_leaf(false)
  78. {}
  79. std::pair<int, int> get_spans();
  80. };
  81. std::pair<int, int> col_node::get_spans(){
  82. rows = 1;
  83. cols = 0;
  84. if(has_leaf){
  85. cols = 1;
  86. }
  87. if(! m_subcolumns.empty()){
  88. subcolumns_t::iterator itr;
  89. for(itr = m_subcolumns.begin(); itr != m_subcolumns.end(); ++itr){
  90. std::pair<int, int> spans;
  91. spans = itr->second.get_spans();
  92. rows = std::max(rows, spans.first);
  93. cols += spans.second;
  94. }
  95. ++rows;
  96. }
  97. return std::make_pair(rows, cols);
  98. }
  99. void build_node_tree(const fs::path & dir_root, col_node & node){
  100. fs::path xml_file_path( dir_root / "test_log.xml" );
  101. if (fs::exists( xml_file_path ) )
  102. {
  103. node.has_leaf = true;
  104. }
  105. fs::directory_iterator itr(dir_root);
  106. while(itr != end_itr){
  107. if(fs::is_directory(*itr)){
  108. std::pair<col_node::subcolumns_t::iterator, bool> result
  109. = node.m_subcolumns.insert(
  110. std::make_pair(itr->leaf(), col_node())
  111. );
  112. build_node_tree(*itr, result.first->second);
  113. }
  114. ++itr;
  115. }
  116. }
  117. fs::ofstream report;
  118. fs::ofstream links_file;
  119. string links_name;
  120. fs::path notes_path;
  121. string notes_html;
  122. fs::path notes_map_path;
  123. typedef std::multimap< string, string > notes_map; // key is test_name-toolset,
  124. // value is note bookmark
  125. notes_map notes;
  126. string specific_compiler; // if running on one toolset only
  127. const string empty_string;
  128. // build notes_bookmarks from notes HTML -----------------------------------//
  129. void build_notes_bookmarks()
  130. {
  131. if ( notes_map_path.empty() ) return;
  132. fs::ifstream notes_map_file( notes_map_path );
  133. if ( !notes_map_file )
  134. {
  135. std::cerr << "Could not open --notes-map input file: " << notes_map_path.string() << std::endl;
  136. std::exit( 1 );
  137. }
  138. string line;
  139. while( std::getline( notes_map_file, line ) )
  140. {
  141. string::size_type pos = 0;
  142. if ( (pos = line.find( ',', pos )) == string::npos ) continue;
  143. string key(line.substr( 0, pos ) );
  144. string bookmark( line.substr( pos+1 ) );
  145. // std::cout << "inserting \"" << key << "\",\"" << bookmark << "\"\n";
  146. notes.insert( notes_map::value_type( key, bookmark ) );
  147. }
  148. }
  149. // load_notes_html ---------------------------------------------------------//
  150. bool load_notes_html()
  151. {
  152. if ( notes_path.empty() ) return false;
  153. fs::ifstream notes_file( notes_path );
  154. if ( !notes_file )
  155. {
  156. std::cerr << "Could not open --notes input file: " << notes_path.string() << std::endl;
  157. std::exit( 1 );
  158. }
  159. string line;
  160. bool in_body( false );
  161. while( std::getline( notes_file, line ) )
  162. {
  163. if ( in_body && line.find( "</body>" ) != string::npos ) in_body = false;
  164. if ( in_body ) notes_html += line;
  165. else if ( line.find( "<body>" ) ) in_body = true;
  166. }
  167. return true;
  168. }
  169. // extract object library name from target directory string ----------------//
  170. string extract_object_library_name( const string & s )
  171. {
  172. string t( s );
  173. string::size_type pos = t.find( "/build/" );
  174. if ( pos != string::npos ) pos += 7;
  175. else if ( (pos = t.find( "/test/" )) != string::npos ) pos += 6;
  176. else return "";
  177. return t.substr( pos, t.find( "/", pos ) - pos );
  178. }
  179. // element_content ---------------------------------------------------------//
  180. const string & element_content(
  181. const xml::element & root, const string & name )
  182. {
  183. const static string empty_string;
  184. xml::element_list::const_iterator itr;
  185. for ( itr = root.elements.begin();
  186. itr != root.elements.end() && (*itr)->name != name;
  187. ++itr ) {}
  188. return itr != root.elements.end() ? (*itr)->content : empty_string;
  189. }
  190. // find_element ------------------------------------------------------------//
  191. const xml::element & find_element(
  192. const xml::element & root, const string & name )
  193. {
  194. static const xml::element empty_element;
  195. xml::element_list::const_iterator itr;
  196. for ( itr = root.elements.begin();
  197. itr != root.elements.end() && (*itr)->name != name;
  198. ++itr ) {}
  199. return itr != root.elements.end() ? *((*itr).get()) : empty_element;
  200. }
  201. // attribute_value ----------------------------------------------------------//
  202. const string & attribute_value(
  203. const xml::element & element,
  204. const string & attribute_name
  205. ){
  206. xml::attribute_list::const_iterator atr;
  207. for(
  208. atr = element.attributes.begin();
  209. atr != element.attributes.end();
  210. ++atr
  211. ){
  212. if(atr->name == attribute_name)
  213. return atr->value;
  214. }
  215. static const string empty_string;
  216. return empty_string;
  217. }
  218. // generate_report ---------------------------------------------------------//
  219. // return 0 if nothing generated, 1 otherwise, except 2 if compiler msgs
  220. int generate_report(
  221. const xml::element & db,
  222. const std::string source_library_name,
  223. const string & test_type,
  224. const fs::path & target_dir,
  225. bool pass,
  226. bool always_show_run_output
  227. )
  228. {
  229. // compile msgs sometimes modified, so make a local copy
  230. string compile( ((pass && no_warn)
  231. ? empty_string : element_content( db, "compile" )) );
  232. const string & link( pass ? empty_string : element_content( db, "link" ) );
  233. const string & run( (pass && !always_show_run_output)
  234. ? empty_string : element_content( db, "run" ) );
  235. string lib( (pass ? empty_string : element_content( db, "lib" )) );
  236. // some compilers output the filename even if there are no errors or
  237. // warnings; detect this if one line of output and it contains no space.
  238. string::size_type pos = compile.find( '\n', 1 );
  239. if ( pos != string::npos && compile.size()-pos <= 2
  240. && compile.find( ' ' ) == string::npos ) compile.clear();
  241. if ( lib.empty()
  242. && (
  243. compile.empty() || test_type == "compile_fail"
  244. )
  245. && link.empty()
  246. && run.empty()
  247. )
  248. return 0;
  249. int result = 1; // some kind of msg for sure
  250. // limit compile message length
  251. if ( compile.size() > max_compile_msg_size )
  252. {
  253. compile.erase( max_compile_msg_size );
  254. compile += "...\n (remainder deleted because of excessive size)\n";
  255. }
  256. links_file << "<h2><a name=\"";
  257. links_file << std::make_pair(
  258. html_from_path(target_dir.string().begin()),
  259. html_from_path(target_dir.string().end())
  260. )
  261. << "\">"
  262. << std::make_pair(
  263. html_from_path(target_dir.string().begin()),
  264. html_from_path(target_dir.string().end())
  265. )
  266. ;
  267. links_file << "</a></h2>\n";;
  268. if ( !compile.empty() )
  269. {
  270. ++result;
  271. links_file << "<h3>Compiler output:</h3><pre>"
  272. << compile << "</pre>\n";
  273. }
  274. if ( !link.empty() )
  275. links_file << "<h3>Linker output:</h3><pre>" << link << "</pre>\n";
  276. if ( !run.empty() )
  277. links_file << "<h3>Run output:</h3><pre>" << run << "</pre>\n";
  278. // for an object library failure, generate a reference to the object
  279. // library failure message, and (once only) generate the object
  280. // library failure message itself
  281. static std::set< string > failed_lib_target_dirs; // only generate once
  282. if ( !lib.empty() )
  283. {
  284. if ( lib[0] == '\n' ) lib.erase( 0, 1 );
  285. string object_library_name( extract_object_library_name( lib ) );
  286. // changing the target directory naming scheme breaks
  287. // extract_object_library_name()
  288. assert( !object_library_name.empty() );
  289. if ( object_library_name.empty() )
  290. std::cerr << "Failed to extract object library name from " << lib << "\n";
  291. links_file << "<h3>Library build failure: </h3>\n"
  292. "See <a href=\"#"
  293. << source_library_name << "-"
  294. << object_library_name << "-"
  295. << std::make_pair(
  296. html_from_path(target_dir.string().begin()),
  297. html_from_path(target_dir.string().end())
  298. )
  299. << source_library_name << " - "
  300. << object_library_name << " - "
  301. << std::make_pair(
  302. html_from_path(target_dir.string().begin()),
  303. html_from_path(target_dir.string().end())
  304. )
  305. << "</a>";
  306. if ( failed_lib_target_dirs.find( lib ) == failed_lib_target_dirs.end() )
  307. {
  308. failed_lib_target_dirs.insert( lib );
  309. fs::path pth( locate_root / lib / "test_log.xml" );
  310. fs::ifstream file( pth );
  311. if ( file )
  312. {
  313. xml::element_ptr db = xml::parse( file, pth.string() );
  314. generate_report(
  315. *db,
  316. source_library_name,
  317. test_type,
  318. target_dir,
  319. false,
  320. false
  321. );
  322. }
  323. else
  324. {
  325. links_file << "<h2><a name=\""
  326. << object_library_name << "-"
  327. << std::make_pair(
  328. html_from_path(target_dir.string().begin()),
  329. html_from_path(target_dir.string().end())
  330. )
  331. << "\">"
  332. << object_library_name << " - "
  333. << std::make_pair(
  334. html_from_path(target_dir.string().begin()),
  335. html_from_path(target_dir.string().end())
  336. )
  337. << "</a></h2>\n"
  338. << "test_log.xml not found\n";
  339. }
  340. }
  341. }
  342. return result;
  343. }
  344. // add_notes --------------------------------------------------------------//
  345. void add_notes( const string & key, bool fail, string & sep, string & target )
  346. {
  347. notes_map::const_iterator itr = notes.lower_bound( key );
  348. if ( itr != notes.end() && itr->first == key )
  349. {
  350. for ( ; itr != notes.end() && itr->first == key; ++itr )
  351. {
  352. string note_desc( itr->second[0] == '-'
  353. ? itr->second.substr( 1 ) : itr->second );
  354. if ( fail || itr->second[0] == '-' )
  355. {
  356. target += sep;
  357. sep = ",";
  358. target += "<a href=\"";
  359. target += "#";
  360. target += note_desc;
  361. target += "\">";
  362. target += note_desc;
  363. target += "</a>";
  364. }
  365. }
  366. }
  367. }
  368. // do_cell ---------------------------------------------------------------//
  369. bool do_cell(
  370. const fs::path & target_dir,
  371. const string & lib_name,
  372. string & target,
  373. bool profile
  374. ){
  375. // return true if any results except pass_msg
  376. bool pass = false;
  377. fs::path xml_file_path( target_dir / "test_log.xml" );
  378. if ( !fs::exists( xml_file_path ) )
  379. {
  380. // suppress message because there are too many of them.
  381. // "missing" is a legitmate result as its not a requirement
  382. // that every test be run in every figuration.
  383. //std::cerr << "Missing jam_log.xml in target:\n "
  384. // << target_dir.string() << "\n";
  385. target += "<td align=\"right\">" + missing_residue_msg + "</td>";
  386. return true;
  387. }
  388. int anything_generated = 0;
  389. bool note = false;
  390. fs::ifstream file( xml_file_path );
  391. if ( !file ) // could not open jam_log.xml
  392. {
  393. std::cerr << "Can't open jam_log.xml in target:\n "
  394. << target_dir.string() << "\n";
  395. target += "<td>" + missing_residue_msg + "</td>";
  396. return false;
  397. }
  398. string test_type( "unknown" );
  399. bool always_show_run_output( false );
  400. xml::element_ptr dbp = xml::parse( file, xml_file_path.string() );
  401. const xml::element & db( *dbp );
  402. test_type = attribute_value( db, "test-type" );
  403. always_show_run_output
  404. = attribute_value( db, "show-run-output" ) == "true";
  405. std::string test_type_base( test_type );
  406. if ( test_type_base.size() > 5 )
  407. {
  408. const string::size_type trailer = test_type_base.size() - 5;
  409. if ( test_type_base.substr( trailer ) == "_fail" )
  410. {
  411. test_type_base.erase( trailer );
  412. }
  413. }
  414. if ( test_type_base.size() > 4 )
  415. {
  416. const string::size_type trailer = test_type_base.size() - 4;
  417. if ( test_type_base.substr( trailer ) == "_pyd" )
  418. {
  419. test_type_base.erase( trailer );
  420. }
  421. }
  422. const xml::element & test_type_element( find_element( db, test_type_base ) );
  423. pass = !test_type_element.name.empty()
  424. && attribute_value( test_type_element, "result" ) != "fail";
  425. if (!no_links){
  426. if(!test_type_element.name.empty())
  427. note = attribute_value( test_type_element, "result" ) == "note";
  428. anything_generated =
  429. generate_report(
  430. db,
  431. lib_name,
  432. test_type,
  433. target_dir,
  434. pass,
  435. always_show_run_output || note
  436. );
  437. }
  438. // generate the status table cell pass/warn/fail HTML
  439. target += "<td align=\"right\">";
  440. if ( anything_generated != 0 )
  441. {
  442. target += "<a href=\"";
  443. target += links_name;
  444. target += "#";
  445. std::copy(
  446. html_from_path(target_dir.string().begin()),
  447. html_from_path(target_dir.string().end()),
  448. std::back_inserter(target)
  449. );
  450. target += "\">";
  451. target += pass
  452. ? (anything_generated < 2 ? pass_msg : warn_msg)
  453. : fail_msg;
  454. target += "</a>";
  455. if ( pass && note ) target += note_msg;
  456. }
  457. else target += pass ? pass_msg : fail_msg;
  458. // if profiling
  459. if(profile && pass){
  460. // add link to profile
  461. target += " <a href=\"";
  462. target += (target_dir / "profile.txt").string();
  463. target += "\"><i>Profile</i></a>";
  464. }
  465. // if notes, generate the superscript HTML
  466. // if ( !notes.empty() )
  467. // target += get_notes( toolset, lib_name, test_name, !pass );
  468. target += "</td>";
  469. return (anything_generated != 0) || !pass;
  470. }
  471. bool visit_node_tree(
  472. const col_node & node,
  473. fs::path dir_root,
  474. const string & lib_name,
  475. string & target,
  476. bool profile
  477. ){
  478. bool retval = false;
  479. if(node.has_leaf){
  480. retval = do_cell(
  481. dir_root,
  482. lib_name,
  483. target,
  484. profile
  485. );
  486. }
  487. col_node::subcolumns_t::const_iterator col_itr;
  488. for(
  489. col_itr = node.m_subcolumns.begin();
  490. col_itr != node.m_subcolumns.end();
  491. ++col_itr
  492. ){
  493. fs::path subdir = dir_root / col_itr->first;
  494. retval |= visit_node_tree(
  495. col_itr->second,
  496. subdir,
  497. lib_name,
  498. target,
  499. col_itr->first == "profile"
  500. );
  501. }
  502. return retval;
  503. }
  504. // emit results for each test
  505. void do_row(
  506. col_node test_node,
  507. const fs::path & test_dir,
  508. const string & lib_name,
  509. const string & test_name,
  510. string & target
  511. ){
  512. string::size_type row_start_pos = target.size();
  513. target += "<tr>";
  514. target += "<td>";
  515. //target += "<a href=\"" + url_prefix_dir_view + "/libs/" + lib_name + "\">";
  516. target += test_name;
  517. target += "</a>";
  518. target += "</td>";
  519. // target += "<td>" + test_type + "</td>";
  520. bool no_warn_save = no_warn;
  521. // if ( test_type.find( "fail" ) != string::npos ) no_warn = true;
  522. // emit cells on this row
  523. bool anything_to_report = visit_node_tree(
  524. test_node,
  525. test_dir,
  526. lib_name,
  527. target,
  528. false
  529. );
  530. target += "</tr>";
  531. if ( ignore_pass
  532. && ! anything_to_report )
  533. target.erase( row_start_pos );
  534. no_warn = no_warn_save;
  535. }
  536. // do_table_body -----------------------------------------------------------//
  537. void do_table_body(
  538. col_node root_node,
  539. const string & lib_name,
  540. const fs::path & test_lib_dir
  541. ){
  542. // rows are held in a vector so they can be sorted, if desired.
  543. std::vector<string> results;
  544. for ( fs::directory_iterator itr( test_lib_dir ); itr != end_itr; ++itr )
  545. {
  546. if(! fs::is_directory(*itr))
  547. continue;
  548. string test_name = itr->leaf();
  549. // strip off the ".test" is there is one
  550. string::size_type s = test_name.find( ".test" );
  551. if(string::npos != s)
  552. test_name.resize(s);
  553. results.push_back( std::string() );
  554. do_row(
  555. root_node, //*test_node_itr++,
  556. *itr, // test dir
  557. lib_name,
  558. test_name,
  559. results[results.size()-1]
  560. );
  561. }
  562. std::sort( results.begin(), results.end() );
  563. for (
  564. std::vector<string>::iterator v(results.begin());
  565. v != results.end();
  566. ++v
  567. ){
  568. report << *v << "\n";
  569. }
  570. }
  571. // column header-----------------------------------------------------------//
  572. int header_depth(const col_node & root){
  573. col_node::subcolumns_t::const_iterator itr;
  574. int max_depth = 1;
  575. for(itr = root.m_subcolumns.begin(); itr != root.m_subcolumns.end(); ++itr){
  576. max_depth = std::max(max_depth, itr->second.rows);
  577. }
  578. return max_depth;
  579. }
  580. void header_cell(int rows, int cols, const std::string & name){
  581. // add row cells
  582. report << "<td align=\"center\" " ;
  583. if(1 < cols)
  584. report << "colspan=\"" << cols << "\" " ;
  585. if(1 < rows)
  586. // span rows to the end the header
  587. report << "rowspan=\"" << rows << "\" " ;
  588. report << ">" ;
  589. report << name;
  590. report << "</td>\n";
  591. }
  592. void emit_column_headers(
  593. const col_node & node,
  594. int display_row,
  595. int current_row,
  596. int row_count
  597. ){
  598. if(current_row < display_row){
  599. if(! node.m_subcolumns.empty()){
  600. col_node::subcolumns_t::const_iterator itr;
  601. for(itr = node.m_subcolumns.begin(); itr != node.m_subcolumns.end(); ++itr){
  602. emit_column_headers(itr->second, display_row, current_row + 1, row_count);
  603. }
  604. }
  605. return;
  606. }
  607. if(node.has_leaf && ! node.m_subcolumns.empty()){
  608. header_cell(row_count - current_row, 1, std::string(""));
  609. }
  610. col_node::subcolumns_t::const_iterator itr;
  611. for(itr = node.m_subcolumns.begin(); itr != node.m_subcolumns.end(); ++itr){
  612. if(1 == itr->second.rows)
  613. header_cell(row_count - current_row, itr->second.cols, itr->first);
  614. else
  615. header_cell(1, itr->second.cols, itr->first);
  616. }
  617. }
  618. fs::path find_lib_test_dir(){
  619. // walk up from the path were we started until we find
  620. // bin or bin.v2
  621. fs::path::const_iterator leaf_itr = fs::initial_path().end();
  622. fs::path test_lib_dir = fs::initial_path();
  623. for(;;){
  624. if(fs::is_directory( test_lib_dir / "bin.v2")){
  625. test_lib_dir /= "bin.v2";
  626. break;
  627. }
  628. if(fs::is_directory( test_lib_dir / "bin")){
  629. // v1 includes the word boost
  630. test_lib_dir /= "bin";
  631. test_lib_dir /= "boost";
  632. break;
  633. }
  634. if(test_lib_dir.empty())
  635. throw std::string("binary path not found");
  636. if(*leaf_itr != "libs")
  637. --leaf_itr;
  638. test_lib_dir.remove_leaf();
  639. }
  640. if(leaf_itr == fs::initial_path().end())
  641. throw std::string("must be run from within a library directory");
  642. while(leaf_itr != fs::initial_path().end()){
  643. test_lib_dir /= *leaf_itr++; // append "libs"
  644. }
  645. return test_lib_dir;
  646. }
  647. // note : uncomment the #if/#endif and what this compile !!!
  648. string find_lib_name(fs::path lib_test_dir){
  649. unsigned int count;
  650. fs::path::iterator e_itr = lib_test_dir.end();
  651. for(count = 0;; ++count){
  652. if(*--e_itr == "libs")
  653. break;
  654. if(lib_test_dir.empty())
  655. throw std::string("must be run from within a library directory");
  656. }
  657. string library_name;
  658. for(;;){
  659. library_name.append(*++e_itr);
  660. if(1 == --count)
  661. break;
  662. library_name.append("/");
  663. }
  664. return library_name;
  665. }
  666. fs::path find_boost_root(){
  667. fs::path boost_root = fs::initial_path();
  668. for(;;){
  669. if(fs::is_directory( boost_root / "boost")){
  670. break;
  671. }
  672. if(boost_root.empty())
  673. throw std::string("boost root not found");
  674. boost_root.remove_leaf();
  675. }
  676. return boost_root;
  677. }
  678. // do_table ----------------------------------------------------------------//
  679. void do_table(const string & lib_name)
  680. {
  681. col_node root_node;
  682. fs::path lib_test_dir = find_lib_test_dir();
  683. for ( fs::directory_iterator itr(lib_test_dir); itr != end_itr; ++itr )
  684. {
  685. if(! fs::is_directory(*itr))
  686. continue;
  687. build_node_tree(*itr, root_node);
  688. }
  689. // visit directory nodes and record nodetree
  690. report << "<table border=\"1\" cellspacing=\"0\" cellpadding=\"5\">\n";
  691. // emit
  692. root_node.get_spans();
  693. int row_count = header_depth(root_node);
  694. report << "<tr>\n";
  695. report << "<td rowspan=\"" << row_count << "\">Test Name</td>\n";
  696. // emit column headers
  697. int row_index = 0;
  698. for(;;){
  699. emit_column_headers(root_node, row_index, 0, row_count);
  700. report << "</tr>" ;
  701. if(++row_index == row_count)
  702. break;
  703. report << "<tr>\n";
  704. }
  705. // now the rest of the table body
  706. do_table_body(root_node, lib_name, lib_test_dir);
  707. report << "</table>\n";
  708. }
  709. }// unnamed namespace
  710. // main --------------------------------------------------------------------//
  711. #define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE
  712. #include <boost/test/included/prg_exec_monitor.hpp>
  713. int cpp_main( int argc, char * argv[] ) // note name!
  714. {
  715. fs::path comment_path;
  716. while ( argc > 1 && *argv[1] == '-' )
  717. {
  718. if ( argc > 2 && std::strcmp( argv[1], "--compiler" ) == 0 )
  719. { specific_compiler = argv[2]; --argc; ++argv; }
  720. else if ( argc > 2 && std::strcmp( argv[1], "--locate-root" ) == 0 )
  721. { locate_root = fs::path( argv[2], fs::native ); --argc; ++argv; }
  722. else if ( argc > 2 && std::strcmp( argv[1], "--boost-root" ) == 0 )
  723. { boost_root = fs::path( argv[2], fs::native ); --argc; ++argv; }
  724. else if ( argc > 2 && std::strcmp( argv[1], "--comment" ) == 0 )
  725. { comment_path = fs::path( argv[2], fs::native ); --argc; ++argv; }
  726. else if ( argc > 2 && std::strcmp( argv[1], "--notes" ) == 0 )
  727. { notes_path = fs::path( argv[2], fs::native ); --argc; ++argv; }
  728. else if ( argc > 2 && std::strcmp( argv[1], "--notes-map" ) == 0 )
  729. { notes_map_path = fs::path( argv[2], fs::native ); --argc; ++argv; }
  730. else if ( std::strcmp( argv[1], "--ignore-pass" ) == 0 ) ignore_pass = true;
  731. else if ( std::strcmp( argv[1], "--no-warn" ) == 0 ) no_warn = true;
  732. else if ( std::strcmp( argv[1], "--v2" ) == 0 )
  733. {--argc; ++argv ;} // skip
  734. else if ( argc > 2 && std::strcmp( argv[1], "--jamfile" ) == 0)
  735. {--argc; ++argv;} // skip
  736. else { std::cerr << "Unknown option: " << argv[1] << "\n"; argc = 1; }
  737. --argc;
  738. ++argv;
  739. }
  740. if ( argc != 2 && argc != 3 )
  741. {
  742. std::cerr <<
  743. "Usage: library_status [options...] status-file [links-file]\n"
  744. " boost-root is the path to the boost tree root directory.\n"
  745. " status-file and links-file are paths to the output files.\n"
  746. " options: --compiler name Run for named compiler only\n"
  747. " --ignore-pass Do not report tests which pass all compilers\n"
  748. " --no-warn Warnings not reported if test passes\n"
  749. " --boost-root path default derived from current path.\n"
  750. " --locate-root path Path to ALL_LOCATE_TARGET for bjam;\n"
  751. " default boost-root.\n"
  752. " --comment path Path to file containing HTML\n"
  753. " to be copied into status-file.\n"
  754. " --notes path Path to file containing HTML\n"
  755. " to be copied into status-file.\n"
  756. " --notes-map path Path to file of toolset/test,n lines, where\n"
  757. " n is number of note bookmark in --notes file.\n"
  758. "Example: compiler_status --compiler gcc /boost-root cs.html cs-links.html\n"
  759. "Note: Only the leaf of the links-file path and --notes file string are\n"
  760. "used in status-file HTML links. Thus for browsing, status-file,\n"
  761. "links-file, and --notes file must all be in the same directory.\n"
  762. ;
  763. return 1;
  764. }
  765. if(boost_root.empty())
  766. boost_root = find_boost_root();
  767. if ( locate_root.empty() )
  768. locate_root = boost_root;
  769. report.open( fs::path( argv[1], fs::native ) );
  770. if ( !report )
  771. {
  772. std::cerr << "Could not open report output file: " << argv[2] << std::endl;
  773. return 1;
  774. }
  775. if ( argc == 3 )
  776. {
  777. fs::path links_path( argv[2], fs::native );
  778. links_name = links_path.leaf();
  779. links_file.open( links_path );
  780. if ( !links_file )
  781. {
  782. std::cerr << "Could not open links output file: " << argv[3] << std::endl;
  783. return 1;
  784. }
  785. }
  786. else no_links = true;
  787. build_notes_bookmarks();
  788. const string library_name = find_lib_name(fs::initial_path());
  789. char run_date[128];
  790. std::time_t tod;
  791. std::time( &tod );
  792. std::strftime( run_date, sizeof(run_date),
  793. "%X UTC, %A %d %B %Y", std::gmtime( &tod ) );
  794. report
  795. << "<html>\n"
  796. << "<head>\n"
  797. << "<title>Boost Library Status Automatic Test</title>\n"
  798. << "</head>\n"
  799. << "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
  800. << "<table border=\"0\">\n"
  801. << "<tr>\n"
  802. << "<td><img border=\"0\" "
  803. << "src=\""
  804. << boost_root / "boost.png"
  805. << "\" width=\"277\" "
  806. << "height=\"86\"></td>\n"
  807. << "<td>\n"
  808. << "<h1>Library Status: " + library_name + "</h1>\n"
  809. << "<b>Run Date:</b> "
  810. << run_date
  811. << "\n"
  812. ;
  813. if ( !comment_path.empty() )
  814. {
  815. fs::ifstream comment_file( comment_path );
  816. if ( !comment_file )
  817. {
  818. std::cerr << "Could not open \"--comment\" input file: " << comment_path.string() << std::endl;
  819. return 1;
  820. }
  821. char c;
  822. while ( comment_file.get( c ) ) { report.put( c ); }
  823. }
  824. report << "</td>\n</table>\n<br>\n";
  825. if ( !no_links )
  826. {
  827. links_file
  828. << "<html>\n"
  829. << "<head>\n"
  830. << "<title>Boost Library Status Error Log</title>\n"
  831. << "</head>\n"
  832. << "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
  833. << "<table border=\"0\">\n"
  834. << "<tr>\n"
  835. << "<td><img border=\"0\" src=\""
  836. << boost_root / "boost.png"
  837. << "\" width=\"277\" "
  838. << "height=\"86\"></td>\n"
  839. << "<td>\n"
  840. << "<h1>Library Status: " + library_name + "</h1>\n"
  841. << "<b>Run Date:</b> "
  842. << run_date
  843. << "\n</td>\n</table>\n<br>\n"
  844. ;
  845. }
  846. do_table(library_name);
  847. if ( load_notes_html() ) report << notes_html << "\n";
  848. report << "</body>\n"
  849. "</html>\n"
  850. ;
  851. if ( !no_links )
  852. {
  853. links_file
  854. << "</body>\n"
  855. "</html>\n"
  856. ;
  857. }
  858. return 0;
  859. }
粤ICP备19079148号