Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/tools/regression/compiler_status.cpp @ 12

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

added boost

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