library_status.cpp 28 KB

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