Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/tools/regression/compiler_status.cpp @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

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