compiler_status.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. // Generate Compiler Status HTML from jam regression test output -----------//
  2. // Copyright Beman Dawes 2002.
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // See http://www.boost.org/LICENSE_1_0.txt
  5. // See http://www.boost.org/tools/regression/ for documentation.
  6. /*******************************************************************************
  7. Please contact the maintainer, bdawes <at> acm <dot> org, before making
  8. any non-trivial changes.
  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/config/warning_disable.hpp>
  18. #include "boost/config.hpp"
  19. #include "boost/filesystem/operations.hpp"
  20. #include "boost/filesystem/convenience.hpp"
  21. #include "boost/filesystem/fstream.hpp"
  22. #include "detail/tiny_xml.hpp"
  23. namespace fs = boost::filesystem;
  24. namespace xml = boost::tiny_xml;
  25. #include <cstdlib> // for abort, exit
  26. #include <cctype> // for toupper, isdigit
  27. #include <string>
  28. #include <vector>
  29. #include <set>
  30. #include <map>
  31. #include <algorithm>
  32. #include <iostream>
  33. #include <fstream>
  34. #include <ctime>
  35. #include <stdexcept>
  36. #include <cassert>
  37. #include <stdio.h> // for popen, pclose
  38. #if defined(_MSC_VER)
  39. # define POPEN _popen
  40. # define PCLOSE _pclose
  41. #else
  42. # define POPEN popen
  43. # define PCLOSE pclose
  44. #endif
  45. using std::string;
  46. const string pass_msg( "Pass" );
  47. const string warn_msg( "<i>Warn</i>" );
  48. const string fail_msg( "<font color=\"#FF0000\"><i>Fail</i></font>" );
  49. const string note_msg( "<sup>*</sup>" );
  50. const string missing_residue_msg( "<i>Missing</i>" );
  51. const std::size_t max_compile_msg_size = 10000;
  52. namespace
  53. {
  54. fs::path boost_root; // boost-root complete path
  55. fs::path locate_root; // locate-root (AKA ALL_LOCATE_TARGET) complete path
  56. bool compile_time;
  57. bool run_time;
  58. bool ignore_pass;
  59. bool no_warn;
  60. bool no_links;
  61. bool boost_build_v2 = true;
  62. fs::path jamfile_path;
  63. fs::directory_iterator end_itr;
  64. // It's immportant for reliability that we find the same compilers for each
  65. // test, and that they match the column header. So save the names at the
  66. // time column headings are generated.
  67. std::vector<string> toolsets;
  68. fs::ifstream jamfile;
  69. fs::ofstream report;
  70. fs::ofstream links_file;
  71. string links_name;
  72. fs::path notes_path;
  73. string notes_html;
  74. fs::path notes_map_path;
  75. typedef std::multimap< string, string > notes_map; // key is test_name-toolset,
  76. // value is note bookmark
  77. notes_map notes;
  78. string specific_compiler; // if running on one toolset only
  79. const string empty_string;
  80. std::vector<int> error_count;
  81. // prefix for library and test hyperlink prefix
  82. string svn_root ( "http://svn.boost.org/trac/boost/browser/trunk/" );
  83. string url_prefix_dir_view( svn_root );
  84. string url_prefix_checkout_view( svn_root );
  85. string url_suffix_text_view( "" );
  86. // get revision number (as a string) if boost_root is svn working copy -----//
  87. string revision( const fs::path & boost_root )
  88. {
  89. string rev;
  90. string command("cd ");
  91. command += boost_root.string() + " & svn info";
  92. FILE* fp = POPEN(command.c_str(), "r");
  93. if (fp)
  94. {
  95. static const int line_max = 128;
  96. char line[line_max];
  97. while (fgets(line, line_max, fp) != NULL)
  98. {
  99. string ln(line);
  100. if (ln.find("Revision: ") != string::npos)
  101. {
  102. for(auto itr = ln.begin()+10; itr != ln.end() && isdigit(*itr); ++itr)
  103. rev += *itr;
  104. }
  105. }
  106. }
  107. std::cout << "Revision: " << rev << std::endl;
  108. return rev;
  109. }
  110. // build notes_bookmarks from notes HTML -----------------------------------//
  111. void build_notes_bookmarks()
  112. {
  113. if ( notes_map_path.empty() ) return;
  114. fs::ifstream notes_map_file( notes_map_path );
  115. if ( !notes_map_file )
  116. {
  117. std::cerr << "Could not open --notes-map input file: " << notes_map_path.string() << std::endl;
  118. std::exit( 1 );
  119. }
  120. string line;
  121. while( std::getline( notes_map_file, line ) )
  122. {
  123. string::size_type pos = 0;
  124. if ( (pos = line.find( ',', pos )) == string::npos ) continue;
  125. string key(line.substr( 0, pos ) );
  126. string bookmark( line.substr( pos+1 ) );
  127. // std::cout << "inserting \"" << key << "\",\"" << bookmark << "\"\n";
  128. notes.insert( notes_map::value_type( key, bookmark ) );
  129. }
  130. }
  131. // load_notes_html ---------------------------------------------------------//
  132. bool load_notes_html()
  133. {
  134. if ( notes_path.empty() ) return false;
  135. fs::ifstream notes_file( notes_path );
  136. if ( !notes_file )
  137. {
  138. std::cerr << "Could not open --notes input file: " << notes_path.string() << std::endl;
  139. std::exit( 1 );
  140. }
  141. string line;
  142. bool in_body( false );
  143. while( std::getline( notes_file, line ) )
  144. {
  145. if ( in_body && line.find( "</body>" ) != string::npos ) in_body = false;
  146. if ( in_body ) notes_html += line;
  147. else if ( line.find( "<body>" ) ) in_body = true;
  148. }
  149. return true;
  150. }
  151. // relative path between two paths -----------------------------------------//
  152. void relative_path( const fs::path & from, const fs::path & to,
  153. fs::path & target )
  154. {
  155. if ( from.string().size() <= to.string().size() ) return;
  156. target /= "..";
  157. relative_path( from.branch_path(), to, target );
  158. return;
  159. }
  160. // extract object library name from target directory string ----------------//
  161. string extract_object_library_name( const string & s )
  162. {
  163. string t( s );
  164. string::size_type pos = t.find( "/build/" );
  165. if ( pos != string::npos ) pos += 7;
  166. else if ( (pos = t.find( "/test/" )) != string::npos ) pos += 6;
  167. else return "";
  168. return t.substr( pos, t.find( "/", pos ) - pos );
  169. }
  170. // find_file ---------------------------------------------------------------//
  171. // given a directory to recursively search
  172. bool find_file( const fs::path & dir_path, const string & name,
  173. fs::path & path_found, const string & ignore_dir_named="" )
  174. {
  175. if ( !fs::exists( dir_path ) ) return false;
  176. for ( fs::directory_iterator itr( dir_path ); itr != end_itr; ++itr )
  177. if ( fs::is_directory( *itr )
  178. && itr->path().filename() != ignore_dir_named )
  179. {
  180. if ( find_file( *itr, name, path_found ) ) return true;
  181. }
  182. else if ( itr->path().filename() == name )
  183. {
  184. path_found = *itr;
  185. return true;
  186. }
  187. return false;
  188. }
  189. // platform_desc -----------------------------------------------------------//
  190. string platform_desc()
  191. {
  192. string result = BOOST_PLATFORM;
  193. result[0] = std::toupper( result[0] );
  194. return result;
  195. }
  196. // version_desc ------------------------------------------------------------//
  197. // from locate-root/status/bin/config_info.test/xxx/.../config_info.output
  198. string version_desc( const string & compiler_name )
  199. {
  200. string result;
  201. fs::path dot_output_path;
  202. if ( find_file( locate_root / "bin/boost/status/config_info.test"
  203. / compiler_name, "config_info.output", dot_output_path )
  204. || find_file( locate_root / "status/bin/config_info.test"
  205. / compiler_name, "config_info.output", dot_output_path ) )
  206. {
  207. fs::ifstream file( dot_output_path );
  208. if ( file )
  209. {
  210. if( std::getline( file, result ) )
  211. {
  212. string::size_type pos = result.find( "version " );
  213. if ( pos != string::npos )
  214. {
  215. result.erase( 0, pos+8 );
  216. }
  217. else result.clear();
  218. }
  219. }
  220. }
  221. return result;
  222. }
  223. // compiler_desc -----------------------------------------------------------//
  224. // from boost-root/tools/build/xxx-tools.jam
  225. string compiler_desc( const string & compiler_name )
  226. {
  227. string result;
  228. fs::path tools_path( boost_root / "tools/build/v1" / (compiler_name
  229. + "-tools.jam") );
  230. if ( !fs::exists( tools_path ) )
  231. tools_path = boost_root / "tools/build" / (compiler_name + "-tools.jam");
  232. fs::ifstream file( tools_path );
  233. if ( file )
  234. {
  235. while( std::getline( file, result ) )
  236. {
  237. if ( result.substr( 0, 3 ) == "#//" )
  238. {
  239. result.erase( 0, 3 );
  240. return result;
  241. }
  242. }
  243. result.clear();
  244. }
  245. return result;
  246. }
  247. // target_directory --------------------------------------------------------//
  248. // this amounts to a request to find a unique leaf directory
  249. fs::path target_directory( const fs::path & root )
  250. {
  251. if ( !fs::exists( root ) ) return fs::path("no-such-path");
  252. fs::path child;
  253. for ( fs::directory_iterator itr( root ); itr != end_itr; ++itr )
  254. {
  255. if ( fs::is_directory( *itr ) )
  256. {
  257. // SunCC creates an internal subdirectory everywhere it writes
  258. // object files. This confuses the target_directory() algorithm.
  259. // This patch ignores the SunCC internal directory. Jens Maurer
  260. if ( itr->path().filename() == "SunWS_cache" ) continue;
  261. // SGI does something similar for template instantiations. Jens Maurer
  262. if( itr->path().filename() == "ii_files" ) continue;
  263. if ( child.empty() ) child = *itr;
  264. else
  265. {
  266. std::cout << "Warning: only first of two target possibilities will be reported for: \n "
  267. << root.string() << ": " << child.filename()
  268. << " and " << itr->path().filename() << "\n";
  269. }
  270. }
  271. }
  272. if ( child.empty() ) return root; // this dir has no children
  273. return target_directory( child );
  274. }
  275. // element_content ---------------------------------------------------------//
  276. const string & element_content(
  277. const xml::element & root, const string & name )
  278. {
  279. static string empty_string;
  280. xml::element_list::const_iterator itr;
  281. for ( itr = root.elements.begin();
  282. itr != root.elements.end() && (*itr)->name != name;
  283. ++itr ) {}
  284. return itr != root.elements.end() ? (*itr)->content : empty_string;
  285. }
  286. // find_element ------------------------------------------------------------//
  287. const xml::element empty_element;
  288. const xml::element & find_element(
  289. const xml::element & root, const string & name )
  290. {
  291. xml::element_list::const_iterator itr;
  292. for ( itr = root.elements.begin();
  293. itr != root.elements.end() && (*itr)->name != name;
  294. ++itr ) {}
  295. return itr != root.elements.end() ? *((*itr).get()) : empty_element;
  296. }
  297. // attribute_value ----------------------------------------------------------//
  298. const string & attribute_value( const xml::element & element,
  299. const string & attribute_name )
  300. {
  301. static const string empty_string;
  302. xml::attribute_list::const_iterator atr;
  303. for ( atr = element.attributes.begin();
  304. atr != element.attributes.end() && atr->name != attribute_name;
  305. ++atr ) {}
  306. return atr == element.attributes.end() ? empty_string : atr->value;
  307. }
  308. // find_bin_path -----------------------------------------------------------//
  309. // Takes a relative path from boost root to a Jamfile.
  310. // Returns the directory where the build targets from
  311. // that Jamfile are located. If not found, emits a warning
  312. // and returns empty path.
  313. const fs::path find_bin_path(const string& relative)
  314. {
  315. fs::path bin_path;
  316. if (boost_build_v2)
  317. {
  318. if ( relative == "status" )
  319. bin_path = locate_root / "bin.v2" / "libs";
  320. else
  321. {
  322. bin_path = locate_root / "bin.v2" / relative;
  323. if (!fs::exists(bin_path))
  324. bin_path = locate_root / "bin" / relative;
  325. }
  326. if (!fs::exists(bin_path))
  327. {
  328. std::cerr << "warning: could not find build results for '"
  329. << relative << "'.\n";
  330. std::cerr << "warning: tried directory "
  331. << bin_path.string() << "\n";
  332. bin_path = "";
  333. }
  334. }
  335. else
  336. {
  337. bin_path = locate_root / "bin/boost" / relative;
  338. if (!fs::exists(bin_path))
  339. {
  340. bin_path = locate_root / "bin" / relative / "bin";
  341. if (!fs::exists(bin_path))
  342. {
  343. bin_path = fs::path( locate_root / relative / "bin" );
  344. if (!fs::exists(bin_path))
  345. {
  346. bin_path = fs::path( locate_root / "bin/boost/libs" /
  347. relative.substr( relative.find( '/' )+1 ) );
  348. }
  349. }
  350. }
  351. if (!fs::exists(bin_path))
  352. {
  353. std::cerr << "warning: could not find build results for '"
  354. << relative << "'.\n";
  355. bin_path = "";
  356. }
  357. }
  358. return bin_path;
  359. }
  360. // generate_report ---------------------------------------------------------//
  361. // return 0 if nothing generated, 1 otherwise, except 2 if compiler msgs
  362. int generate_report( const xml::element & db,
  363. const string & source_library_name,
  364. const string & test_type,
  365. const string & test_name, // possibly object library name
  366. const string & toolset,
  367. bool pass,
  368. bool always_show_run_output = false )
  369. {
  370. // compile msgs sometimes modified, so make a local copy
  371. string compile( ((pass && no_warn)
  372. ? empty_string : element_content( db, "compile" )) );
  373. const string & link( pass ? empty_string : element_content( db, "link" ) );
  374. const string & run( (pass && !always_show_run_output)
  375. ? empty_string : element_content( db, "run" ) );
  376. string lib( (pass ? empty_string : element_content( db, "lib" )) );
  377. string::size_type pos;
  378. if ( (pos = compile.find("30 DAY EVALUATION LICENSE")) != string::npos )
  379. {
  380. compile.erase(pos, 25);
  381. while ( compile[0] == '\n' || compile[0] == '\r' ) compile.erase(0,1);
  382. }
  383. // some compilers output the filename even if there are no errors or
  384. // warnings; detect this if one line of output and it contains no space.
  385. pos = compile.find( '\n', 1 );
  386. if ( pos != string::npos && compile.size()-pos <= 2
  387. && compile.find( ' ' ) == string::npos ) compile.clear();
  388. if ( lib.empty()
  389. && (compile.empty() || test_type == "compile_fail")
  390. && link.empty() && run.empty() ) return 0;
  391. int result = 1; // some kind of msg for sure
  392. // limit compile message length
  393. if ( compile.size() > max_compile_msg_size )
  394. {
  395. compile.erase( max_compile_msg_size );
  396. compile += "...\n (remainder deleted because of excessive size)\n";
  397. }
  398. links_file << "<h2><a name=\""
  399. << source_library_name << "-" << test_name << "-" << toolset << "\">"
  400. << source_library_name << " - " << test_name << " - " << toolset << "</a></h2>\n";
  401. if ( !compile.empty() )
  402. {
  403. ++result;
  404. links_file << "<h3>Compiler output:</h3><pre>"
  405. << compile << "</pre>\n";
  406. }
  407. if ( !link.empty() )
  408. links_file << "<h3>Linker output:</h3><pre>" << link << "</pre>\n";
  409. if ( !run.empty() )
  410. links_file << "<h3>Run output:</h3><pre>" << run << "</pre>\n";
  411. // for an object library failure, generate a reference to the object
  412. // library failure message, and (once only) generate the object
  413. // library failure message itself
  414. static std::set< string > failed_lib_target_dirs; // only generate once
  415. if ( !lib.empty() )
  416. {
  417. if ( lib[0] == '\n' ) lib.erase( 0, 1 );
  418. string object_library_name( extract_object_library_name( lib ) );
  419. // changing the target directory naming scheme breaks
  420. // extract_object_library_name()
  421. assert( !object_library_name.empty() );
  422. if ( object_library_name.empty() )
  423. std::cerr << "Failed to extract object library name from " << lib << "\n";
  424. links_file << "<h3>Library build failure: </h3>\n"
  425. "See <a href=\"#"
  426. << source_library_name << "-"
  427. << object_library_name << "-" << toolset << "\">"
  428. << source_library_name << " - "
  429. << object_library_name << " - " << toolset << "</a>";
  430. if ( failed_lib_target_dirs.find( lib ) == failed_lib_target_dirs.end() )
  431. {
  432. failed_lib_target_dirs.insert( lib );
  433. fs::path pth( locate_root / lib / "test_log.xml" );
  434. fs::ifstream file( pth );
  435. if ( file )
  436. {
  437. xml::element_ptr db = xml::parse( file, pth.string() );
  438. generate_report( *db, source_library_name, test_type, object_library_name, toolset, false );
  439. }
  440. else
  441. {
  442. links_file << "<h2><a name=\""
  443. << object_library_name << "-" << toolset << "\">"
  444. << object_library_name << " - " << toolset << "</a></h2>\n"
  445. "test_log.xml not found\n";
  446. }
  447. }
  448. }
  449. return result;
  450. }
  451. // add_notes --------------------------------------------------------------//
  452. void add_notes( const string & key, bool fail, string & sep, string & target )
  453. {
  454. notes_map::const_iterator itr = notes.lower_bound( key );
  455. if ( itr != notes.end() && itr->first == key )
  456. {
  457. for ( ; itr != notes.end() && itr->first == key; ++itr )
  458. {
  459. string note_desc( itr->second[0] == '-'
  460. ? itr->second.substr( 1 ) : itr->second );
  461. if ( fail || itr->second[0] == '-' )
  462. {
  463. target += sep;
  464. sep = ",";
  465. target += "<a href=\"";
  466. target += "#";
  467. target += note_desc;
  468. target += "\">";
  469. target += note_desc;
  470. target += "</a>";
  471. }
  472. }
  473. }
  474. }
  475. // get_notes -------------------------------------------------------------//
  476. string get_notes( const string & toolset,
  477. const string & library, const string & test, bool fail )
  478. {
  479. string sep;
  480. string target( "<sup>" );
  481. add_notes( toolset + "/" + library + "/" + test, fail, sep, target );
  482. add_notes( "*/" + library + "/" + test, fail, sep, target );
  483. add_notes( toolset + "/" + library + "/*", fail, sep, target );
  484. add_notes( "*/" + library + "/*", fail, sep, target );
  485. if ( target == "<sup>" ) target.clear();
  486. else target += "</sup>";
  487. return target;
  488. }
  489. // do_cell ---------------------------------------------------------------//
  490. bool do_cell(
  491. int compiler,
  492. const string & lib_name,
  493. const fs::path & test_dir,
  494. const string & test_type,
  495. const string & test_name,
  496. const string & toolset,
  497. string & target,
  498. bool always_show_run_output )
  499. // return true if any results except simple pass_msg
  500. {
  501. fs::path target_dir( target_directory( test_dir / toolset ) );
  502. bool pass = false;
  503. if ( !fs::exists( target_dir / "test_log.xml" ) )
  504. {
  505. std::cerr << "Missing test_log.xml in target:\n "
  506. << target_dir.string() << "\n";
  507. target += "<td>" + missing_residue_msg + "</td>";
  508. return true;
  509. }
  510. int anything_generated = 0;
  511. bool note = false;
  512. fs::path pth( target_dir / "test_log.xml" );
  513. fs::ifstream file( pth );
  514. if ( !file )
  515. {
  516. std::cerr << "Can't open test_log.xml in target:\n "
  517. << target_dir.string() << "\n";
  518. target += "<td>" + missing_residue_msg + "</td>";
  519. return false;
  520. }
  521. xml::element_ptr dbp = xml::parse( file, pth.string() );
  522. const xml::element & db( *dbp );
  523. std::string test_type_base( test_type );
  524. if ( test_type_base == "run_pyd" ) test_type_base = "run";
  525. else if ( test_type_base.size() > 5 )
  526. {
  527. const string::size_type trailer = test_type_base.size() - 5;
  528. if ( test_type_base.substr( trailer ) == "_fail" )
  529. {
  530. test_type_base.erase( trailer );
  531. }
  532. }
  533. const xml::element & test_type_element( find_element( db, test_type_base ) );
  534. pass = !test_type_element.name.empty()
  535. && attribute_value( test_type_element, "result" ) != "fail";
  536. if ( !no_links )
  537. {
  538. note = attribute_value( test_type_element, "result" ) == "note";
  539. // generate bookmarked report of results, and link to it
  540. anything_generated
  541. = generate_report( db, lib_name, test_type, test_name, toolset, pass,
  542. always_show_run_output || note );
  543. }
  544. target += "<td>";
  545. // generate the status table cell pass/warn/fail HTML
  546. if ( anything_generated != 0 )
  547. {
  548. target += "<a href=\"";
  549. target += links_name;
  550. target += "#";
  551. target += lib_name;
  552. target += "-";
  553. target += test_name;
  554. target += "-";
  555. target += toolset;
  556. target += "\">";
  557. target += pass
  558. ? (anything_generated < 2 ? pass_msg : warn_msg)
  559. : fail_msg;
  560. target += "</a>";
  561. if ( pass && note ) target += note_msg;
  562. }
  563. else target += pass ? pass_msg : fail_msg;
  564. // if notes, generate the superscript HTML
  565. if ( !notes.empty() )
  566. target += get_notes( toolset, lib_name, test_name, !pass );
  567. // generate compile-time if requested
  568. if ( compile_time )
  569. {
  570. const xml::element & compile_element( find_element( db, "compile" ) );
  571. if ( !compile_element.name.empty() )
  572. {
  573. string times = attribute_value( compile_element, "timings" );
  574. if ( !times.empty() )
  575. {
  576. target += "<br>";
  577. target += times.substr( 0, times.find( " " ) );
  578. }
  579. }
  580. }
  581. // generate run-time if requested
  582. if ( run_time )
  583. {
  584. const xml::element & run_element( find_element( db, "run" ) );
  585. if ( !run_element.name.empty() )
  586. {
  587. string times = attribute_value( run_element, "timings" );
  588. if ( !times.empty() )
  589. {
  590. target += "<br>";
  591. target += times.substr( 0, times.find( " " ) );
  592. }
  593. }
  594. }
  595. if ( !pass ) ++error_count[compiler];
  596. target += "</td>";
  597. return (anything_generated != 0) || !pass;
  598. }
  599. // do_row ------------------------------------------------------------------//
  600. void do_row(
  601. const fs::path & test_dir, // locate_root / "status/bin/any_test.test"
  602. const string & test_name, // "any_test"
  603. string & target )
  604. {
  605. // get library name, test-type, test-program path, etc., from the .xml file
  606. string lib_name;
  607. string test_path( test_name ); // test_name is default if missing .test
  608. string test_type( "unknown" );
  609. bool always_show_run_output( false );
  610. fs::path xml_file_path;
  611. if ( find_file( test_dir, "test_log.xml", xml_file_path ) )
  612. {
  613. fs::ifstream file( xml_file_path );
  614. if ( file )
  615. {
  616. xml::element_ptr dbp = xml::parse( file, xml_file_path.string() );
  617. const xml::element & db( *dbp );
  618. test_path = attribute_value( db, "test-program" );
  619. lib_name = attribute_value( db, "library" );
  620. test_type = attribute_value( db, "test-type" );
  621. always_show_run_output
  622. = attribute_value( db, "show-run-output" ) == "true";
  623. }
  624. }
  625. // generate the library name, test name, and test type table data
  626. string::size_type row_start_pos = target.size();
  627. target += "<tr><td><a href=\"" + url_prefix_dir_view + "/libs/" + lib_name
  628. + "\">" + lib_name + "</a></td>";
  629. target += "<td><a href=\"" + url_prefix_checkout_view + "/" + test_path
  630. + url_suffix_text_view + "\">" + test_name + "</a>";
  631. if ( compile_time ) target += "<br> Compile time:";
  632. if ( run_time ) target += "<br> Run time:";
  633. target += "</td>";
  634. target += "<td>" + test_type + "</td>";
  635. bool no_warn_save = no_warn;
  636. //if ( test_type.find( "fail" ) != string::npos ) no_warn = true;
  637. // for each compiler, generate <td>...</td> html
  638. bool anything_to_report = false;
  639. int compiler = 0;
  640. for ( std::vector<string>::const_iterator itr=toolsets.begin();
  641. itr != toolsets.end(); ++itr, ++compiler )
  642. {
  643. anything_to_report |= do_cell( compiler, lib_name, test_dir, test_type, test_name, *itr, target,
  644. always_show_run_output );
  645. }
  646. target += "</tr>";
  647. if ( ignore_pass && !anything_to_report ) target.erase( row_start_pos );
  648. no_warn = no_warn_save;
  649. }
  650. // do_rows_for_sub_tree ----------------------------------------------------//
  651. void do_rows_for_sub_tree(
  652. const fs::path & bin_dir, std::vector<string> & results )
  653. {
  654. for ( fs::directory_iterator itr( bin_dir ); itr != end_itr; ++itr )
  655. {
  656. if ( fs::is_directory( *itr )
  657. && itr->path().string().find( ".test" ) == (itr->path().string().size()-5) )
  658. {
  659. results.push_back( std::string() );
  660. do_row( *itr,
  661. itr->path().filename().string().substr( 0,
  662. itr->path().filename().string().size()-5 ),
  663. results[results.size()-1] );
  664. }
  665. }
  666. }
  667. // find_compilers ------------------------------------------------------------//
  668. void find_compilers(const fs::path & bin_dir)
  669. {
  670. fs::directory_iterator compiler_itr( bin_dir );
  671. if ( specific_compiler.empty() )
  672. std::clog << "Using " << bin_dir.string() << " to determine compilers\n";
  673. for (; compiler_itr != end_itr; ++compiler_itr )
  674. {
  675. if ( fs::is_directory( *compiler_itr ) // check just to be sure
  676. && compiler_itr->path().filename() != "test" ) // avoid strange directory (Jamfile bug?)
  677. {
  678. if ( specific_compiler.size() != 0
  679. && specific_compiler != compiler_itr->path().filename() ) continue;
  680. toolsets.push_back( compiler_itr->path().filename().string() );
  681. string desc( compiler_desc( compiler_itr->path().filename().string() ) );
  682. string vers( version_desc( compiler_itr->path().filename().string() ) );
  683. report << "<td>"
  684. << (desc.size() ? desc : compiler_itr->path().filename().string())
  685. << (vers.size() ? (string( "<br>" ) + vers ) : string( "" ))
  686. << "</td>\n";
  687. error_count.push_back( 0 );
  688. }
  689. }
  690. }
  691. // do_table_body -----------------------------------------------------------//
  692. void do_table_body( const fs::path & bin_dir )
  693. {
  694. // rows are held in a vector so they can be sorted, if desired.
  695. std::vector<string> results;
  696. // do primary bin directory
  697. do_rows_for_sub_tree( bin_dir, results );
  698. // do subinclude bin directories
  699. jamfile.clear();
  700. jamfile.seekg(0);
  701. string line;
  702. bool run_tests = false;
  703. while( std::getline( jamfile, line ) )
  704. {
  705. bool v2(false);
  706. string::size_type sub_pos( line.find( "subinclude" ) );
  707. if ( sub_pos == string::npos ) {
  708. sub_pos = line.find( "build-project" );
  709. v2 = true;
  710. }
  711. if ( sub_pos != string::npos
  712. && line.find( '#' ) > sub_pos )
  713. {
  714. if (v2)
  715. sub_pos = line.find_first_not_of( " \t./", sub_pos+13 );
  716. else
  717. sub_pos = line.find_first_not_of( " \t./", sub_pos+10 );
  718. if ( sub_pos == string::npos ) continue;
  719. string subinclude_bin_dir(
  720. line.substr( sub_pos, line.find_first_of( " \t", sub_pos )-sub_pos ) );
  721. fs::path bin_path = find_bin_path(subinclude_bin_dir);
  722. if (!bin_path.empty())
  723. do_rows_for_sub_tree( bin_path, results );
  724. }
  725. if ( ! run_tests )
  726. {
  727. string::size_type run_pos = line.find("run-tests");
  728. if ( run_pos != string::npos && line.find_first_not_of(" \t") == run_pos )
  729. run_tests = true;
  730. }
  731. else
  732. {
  733. if ( line.find(";") != string::npos )
  734. run_tests = false;
  735. else
  736. {
  737. string::size_type pos = line.find_first_not_of( " \t" );
  738. if ( pos != string::npos && line[pos] != '#' )
  739. {
  740. string::size_type end_pos = line.find_first_of(" \t#", pos);
  741. string::iterator end = end_pos != string::npos ? line.begin() + end_pos : line.end();
  742. string run_tests_bin_dir(line.begin() + pos, end);
  743. fs::path bin_path = find_bin_path("libs/" + run_tests_bin_dir);
  744. if (!bin_path.empty())
  745. do_rows_for_sub_tree( bin_path, results );
  746. }
  747. }
  748. }
  749. }
  750. std::sort( results.begin(), results.end() );
  751. for ( std::vector<string>::iterator v(results.begin());
  752. v != results.end(); ++v )
  753. { report << *v << "\n"; }
  754. }
  755. // do_table ----------------------------------------------------------------//
  756. void do_table()
  757. {
  758. // Find test result locations, trying:
  759. // - Boost.Build V1 location with ALL_LOCATE_TARGET
  760. // - Boost.Build V2 location with top-lelve "build-dir"
  761. // - Boost.Build V1 location without ALL_LOCATE_TARGET
  762. string relative( fs::initial_path().string() );
  763. #ifdef BOOST_WINDOWS_API
  764. if (relative.size() > 1 && relative[1] == ':') relative[0] = std::tolower(relative[0]);
  765. #endif
  766. if ( relative.find(boost_root.string()) != string::npos )
  767. relative.erase( 0, boost_root.string().size()+1 );
  768. else if ( relative.find(locate_root.string()) != string::npos )
  769. relative.erase( 0, locate_root.string().size()+1 );
  770. fs::path bin_path = find_bin_path(relative);
  771. report << "<table border=\"1\" cellspacing=\"0\" cellpadding=\"5\">\n";
  772. // generate the column headings
  773. report << "<tr><td>Library</td><td>Test Name</td>\n"
  774. "<td><a href=\"compiler_status.html#test-type\">Test Type</a></td>\n";
  775. if ( relative == "status" )
  776. {
  777. fs::recursive_directory_iterator ritr( bin_path );
  778. fs::recursive_directory_iterator end_ritr;
  779. while ( ritr != end_ritr
  780. && ((ritr->path().string().find( ".test" ) != (ritr->path().string().size()-5))
  781. || !fs::is_directory( *ritr )))
  782. ++ritr; // bypass chaff
  783. if ( ritr != end_ritr )
  784. {
  785. find_compilers( *ritr );
  786. }
  787. }
  788. else
  789. {
  790. fs::directory_iterator itr( bin_path );
  791. while ( itr != end_itr
  792. && ((itr->path().string().find( ".test" ) != (itr->path().string().size()-5))
  793. || !fs::is_directory( *itr )))
  794. ++itr; // bypass chaff
  795. if ( itr != end_itr )
  796. {
  797. find_compilers( *itr );
  798. }
  799. }
  800. report << "</tr>\n";
  801. // now the rest of the table body
  802. do_table_body( bin_path );
  803. // error total row
  804. report << "<tr> <td> &nbsp;</td><td>Number of Failures</td><td> &nbsp;</td>\n";
  805. // for each compiler, generate <td>...</td> html
  806. int compiler = 0;
  807. for ( std::vector<string>::const_iterator itr=toolsets.begin();
  808. itr != toolsets.end(); ++itr, ++compiler )
  809. {
  810. report << "<td align=\"center\">" << error_count[compiler] << "</td>\n";
  811. }
  812. report << "</tr>\n</table>\n";
  813. }
  814. } // unnamed namespace
  815. // main --------------------------------------------------------------------//
  816. #define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE
  817. #include <boost/test/included/prg_exec_monitor.hpp>
  818. int cpp_main( int argc, char * argv[] ) // note name!
  819. {
  820. fs::path comment_path;
  821. while ( argc > 1 && *argv[1] == '-' )
  822. {
  823. if ( argc > 2 && std::strcmp( argv[1], "--compiler" ) == 0 )
  824. { specific_compiler = argv[2]; --argc; ++argv; }
  825. else if ( argc > 2 && std::strcmp( argv[1], "--locate-root" ) == 0 )
  826. { locate_root = fs::path( argv[2] ); --argc; ++argv; }
  827. else if ( argc > 2 && std::strcmp( argv[1], "--comment" ) == 0 )
  828. { comment_path = fs::path( argv[2] ); --argc; ++argv; }
  829. else if ( argc > 2 && std::strcmp( argv[1], "--notes" ) == 0 )
  830. { notes_path = fs::path( argv[2] ); --argc; ++argv; }
  831. else if ( argc > 2 && std::strcmp( argv[1], "--notes-map" ) == 0 )
  832. { notes_map_path = fs::path( argv[2] ); --argc; ++argv; }
  833. else if ( std::strcmp( argv[1], "--ignore-pass" ) == 0 ) ignore_pass = true;
  834. else if ( std::strcmp( argv[1], "--no-warn" ) == 0 ) no_warn = true;
  835. else if ( std::strcmp( argv[1], "--v1" ) == 0 ) boost_build_v2 = false;
  836. else if ( std::strcmp( argv[1], "--v2" ) == 0 ) boost_build_v2 = true;
  837. else if ( argc > 2 && std::strcmp( argv[1], "--jamfile" ) == 0)
  838. { jamfile_path = fs::path( argv[2] ); --argc; ++argv; }
  839. else if ( std::strcmp( argv[1], "--compile-time" ) == 0 ) compile_time = true;
  840. else if ( std::strcmp( argv[1], "--run-time" ) == 0 ) run_time = true;
  841. else { std::cerr << "Unknown option: " << argv[1] << "\n"; argc = 1; }
  842. --argc;
  843. ++argv;
  844. }
  845. if ( argc != 3 && argc != 4 )
  846. {
  847. std::cerr <<
  848. "Usage: compiler_status [options...] boost-root status-file [links-file]\n"
  849. " boost-root is the path to the boost tree root directory.\n"
  850. " status-file and links-file are paths to the output files.\n"
  851. "Must be run from directory containing Jamfile\n"
  852. " options: --compiler name Run for named compiler only\n"
  853. " --locate-root path Path to ALL_LOCATE_TARGET for bjam;\n"
  854. " default boost-root.\n"
  855. " --comment path Path to file containing HTML\n"
  856. " to be copied into status-file.\n"
  857. " --notes path Path to file containing HTML\n"
  858. " to be copied into status-file.\n"
  859. " --notes-map path Path to file of toolset/test,n lines, where\n"
  860. " n is number of note bookmark in --notes file.\n"
  861. " --jamfile path Path to Jamfile. By default \"Jamfile\".\n"
  862. " --v1 Assume Boost.Build version 1.\n"
  863. " --v2 Assume Boost.Build version 2. (default)\n"
  864. " --ignore-pass Ignore passing tests.\n"
  865. " --no-warn Do not report warnings.\n"
  866. " --compile-time Show compile time.\n"
  867. " --run-time Show run time.\n"
  868. "Example: compiler_status --compiler gcc /boost-root cs.html cs-links.html\n"
  869. "Note: Only the leaf of the links-file path and --notes file string are\n"
  870. "used in status-file HTML links. Thus for browsing, status-file,\n"
  871. "links-file, and --notes file must all be in the same directory.\n"
  872. ;
  873. return 1;
  874. }
  875. boost_root = fs::path( argv[1] );
  876. if ( locate_root.empty() ) locate_root = boost_root;
  877. if (jamfile_path.empty())
  878. if (boost_build_v2)
  879. jamfile_path = "Jamfile.v2";
  880. else
  881. jamfile_path = "Jamfile";
  882. jamfile_path = fs::absolute( jamfile_path, fs::initial_path() );
  883. jamfile.open( jamfile_path );
  884. if ( !jamfile )
  885. {
  886. std::cerr << "Could not open Jamfile: " << jamfile_path.string() << std::endl;
  887. return 1;
  888. }
  889. report.open( fs::path( argv[2] ) );
  890. if ( !report )
  891. {
  892. std::cerr << "Could not open report output file: " << argv[2] << std::endl;
  893. return 1;
  894. }
  895. if ( argc == 4 )
  896. {
  897. fs::path links_path( argv[3] );
  898. links_name = links_path.filename().string();
  899. links_file.open( links_path );
  900. if ( !links_file )
  901. {
  902. std::cerr << "Could not open links output file: " << argv[3] << std::endl;
  903. return 1;
  904. }
  905. }
  906. else no_links = true;
  907. build_notes_bookmarks();
  908. char run_date[128];
  909. std::time_t tod;
  910. std::time( &tod );
  911. std::strftime( run_date, sizeof(run_date),
  912. "%X UTC, %A %d %B %Y", std::gmtime( &tod ) );
  913. std::string rev = revision( boost_root );
  914. report << "<html>\n"
  915. "<head>\n"
  916. "<title>Boost Test Results</title>\n"
  917. "</head>\n"
  918. "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
  919. "<table border=\"0\">\n"
  920. "<tr>\n"
  921. "<td><img border=\"0\" src=\"http://www.boost.org/boost.png\" width=\"277\" "
  922. "height=\"86\"></td>\n"
  923. "<td>\n"
  924. "<h1>Boost Test Results - " + platform_desc() + "</h1>\n"
  925. "<b>Run</b> "
  926. << run_date;
  927. if ( !rev.empty() ) report << ", <b>Revision</b> " << rev;
  928. report << "\n";
  929. if ( compile_time )
  930. report << "<p>Times reported are elapsed wall clock time in seconds.</p>\n";
  931. if ( !comment_path.empty() )
  932. {
  933. fs::ifstream comment_file( comment_path );
  934. if ( !comment_file )
  935. {
  936. std::cerr << "Could not open \"--comment\" input file: " << comment_path.string() << std::endl;
  937. return 1;
  938. }
  939. char c;
  940. while ( comment_file.get( c ) ) { report.put( c ); }
  941. }
  942. report << "</td>\n</table>\n<br>\n";
  943. if ( !no_links )
  944. {
  945. links_file
  946. << "<html>\n"
  947. "<head>\n"
  948. "<title>Boost Test Details</title>\n"
  949. "</head>\n"
  950. "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
  951. "<table border=\"0\">\n"
  952. "<tr>\n"
  953. "<td><img border=\"0\" src=\"http://www.boost.org/boost.png\" width=\"277\" "
  954. "height=\"86\"></td>\n"
  955. "<td>\n"
  956. "<h1>Boost Test Details - " + platform_desc() + "</h1>\n"
  957. "<b>Run Date:</b> "
  958. << run_date;
  959. if ( !rev.empty() ) links_file << ", <b>Revision</b> " << rev;
  960. links_file << "\n</td>\n</table>\n<br>\n";
  961. }
  962. do_table();
  963. if ( load_notes_html() ) report << notes_html << "\n";
  964. report << "</body>\n"
  965. "</html>\n"
  966. ;
  967. if ( !no_links )
  968. {
  969. links_file
  970. << "</body>\n"
  971. "</html>\n"
  972. ;
  973. }
  974. return 0;
  975. }
粤ICP备19079148号