e) const {
bool operator()(const xml::element_ptr & e) const {
return attribute_value(*e, "result") == "fail";
}
};
// do_cell ---------------------------------------------------------------//
bool do_cell(
const fs::path & target_dir,
const string & lib_name,
const string & test_name,
string & target,
bool profile
){
// return true if any results except pass_msg
bool pass = false;
fs::path xml_file_path( target_dir / "test_log.xml" );
if ( !fs::exists( xml_file_path ) )
{
fs::path test_path = target_dir / (test_name + ".test");
target += "| ";
target += fs::exists( test_path) ? pass_msg : fail_msg;
target += " | ";
return true;
}
string test_type( "unknown" );
bool always_show_run_output( false );
fs::ifstream file( xml_file_path );
xml::element_ptr dbp = xml::parse( file, xml_file_path.string() );
const xml::element & db( *dbp );
always_show_run_output
= attribute_value( db, "show-run-output" ) == "true";
// if we don't find any failures
// mark it as a pass
pass = (db.elements.end() == std::find_if(
db.elements.begin(),
db.elements.end(),
has_fail_result()
));
int anything_generated = 0;
if (!no_links){
anything_generated =
generate_report(
db,
lib_name,
test_type,
target_dir,
pass,
always_show_run_output
);
}
// generate the status table cell pass/warn/fail HTML
target += "";
if ( anything_generated != 0 )
{
target += "";
target += pass
? (anything_generated < 2 ? pass_msg : warn_msg)
: fail_msg;
target += "";
}
else target += pass ? pass_msg : fail_msg;
// if profiling
if(profile && pass){
// add link to profile
target += " Profile";
}
target += " | ";
return (anything_generated != 0) || !pass;
}
bool visit_node_tree(
const col_node & node,
fs::path dir_root,
const string & lib_name,
const string & test_name,
string & target,
bool profile
){
bool retval = false;
if(node.is_leaf){
return do_cell(
dir_root,
lib_name,
test_name,
target,
profile
);
}
BOOST_FOREACH(
const col_node::subcolumn & s,
node.m_subcolumns
){
fs::path subdir = dir_root / s.first;
retval |= visit_node_tree(
s.second,
subdir,
lib_name,
test_name,
target,
s.first == "profile"
);
}
return retval;
}
// emit results for each test
void do_row(
col_node test_node,
const fs::path & test_dir,
const string & lib_name,
const string & test_name,
string & target
){
string::size_type row_start_pos = target.size();
target += "";
target += "| ";
//target += "";
target += test_name;
//target += "";
target += " | ";
bool no_warn_save = no_warn;
// emit cells on this row
bool anything_to_report = visit_node_tree(
test_node,
test_dir,
lib_name,
test_name,
target,
false
);
target += "
";
if ( ignore_pass
&& ! anything_to_report )
target.erase( row_start_pos );
no_warn = no_warn_save;
}
// do_table_body -----------------------------------------------------------//
void do_table_body(
col_node root_node,
const string & lib_name,
const fs::path & test_lib_dir
){
// rows are held in a vector so they can be sorted, if desired.
std::vector results;
BOOST_FOREACH(
fs::directory_entry & d,
std::make_pair(
fs::directory_iterator(test_lib_dir),
fs::directory_iterator()
)
){
if(! fs::is_directory(d))
continue;
// if the file name contains ".test"
if(d.path().extension() != ".test")
continue;
string test_name = d.path().stem().string();
results.push_back( std::string() );
do_row(
root_node, //*test_node_itr++,
d, // test dir
lib_name,
test_name,
results[results.size()-1]
);
}
std::sort( results.begin(), results.end() );
BOOST_FOREACH(string &s, results)
report << s << "\n";
}
// column header-----------------------------------------------------------//
int header_depth(const col_node & root){
int max_depth = 1;
BOOST_FOREACH(
const col_node::subcolumn &s,
root.m_subcolumns
){
max_depth = (std::max)(max_depth, s.second.rows);
}
return max_depth;
}
void header_cell(int rows, int cols, const std::string & name){
// add row cells
report << "" ;
report << name;
report << " | \n";
}
void emit_column_headers(
const col_node & node,
int display_row,
int current_row,
int row_count
){
if(current_row < display_row){
if(! node.m_subcolumns.empty()){
BOOST_FOREACH(
const col_node::subcolumn &s,
node.m_subcolumns
){
emit_column_headers(
s.second,
display_row,
current_row + 1,
row_count
);
}
}
return;
}
/*
if(node.is_leaf && ! node.m_subcolumns.empty()){
header_cell(row_count - current_row, 1, std::string(""));
}
*/
BOOST_FOREACH(col_node::subcolumn s, node.m_subcolumns){
if(1 == s.second.rows)
header_cell(row_count - current_row, s.second.cols, s.first);
else
header_cell(1, s.second.cols, s.first);
}
}
fs::path find_lib_test_dir(fs::path const& initial_path){
// walk up from the path were we started until we find
// bin or bin.v2
fs::path test_lib_dir = initial_path;
do{
if(fs::is_directory( test_lib_dir / "bin.v2")){
test_lib_dir /= "bin.v2";
break;
}
if(fs::is_directory( test_lib_dir / "bin")){
// v1 includes the word boost
test_lib_dir /= "bin";
if(fs::is_directory( test_lib_dir / "boost")){
test_lib_dir /= "boost";
}
break;
}
}while(! test_lib_dir.empty());
if(test_lib_dir.empty())
throw std::string("binary path not found");
return test_lib_dir;
}
string find_lib_name(fs::path lib_test_dir){
// search the path backwards for the magic name "libs"
fs::path::iterator e_itr = lib_test_dir.end();
while(lib_test_dir.begin() != e_itr){
if(*--e_itr == "libs")
break;
}
// if its found
if(lib_test_dir.begin() != e_itr){
// use the whole path since the "libs"
++e_itr;
}
// otherwise, just use the last two components
else{
e_itr = lib_test_dir.end();
if(e_itr != lib_test_dir.begin()){
if(--e_itr != lib_test_dir.begin()){
--e_itr;
}
}
}
fs::path library_name;
while(lib_test_dir.end() != e_itr){
library_name /= *e_itr++;
}
return library_name.string();
}
fs::path find_boost_root(fs::path initial_path){
fs::path boost_root = initial_path;
for(;;){
if(fs::is_directory( boost_root / "boost")){
break;
}
if(boost_root.empty())
throw std::string("boost root not found");
boost_root.remove_filename();
}
return boost_root;
}
// do_table ----------------------------------------------------------------//
void do_table(const fs::path & lib_test_dir, const string & lib_name)
{
col_node root_node;
BOOST_FOREACH(
fs::directory_entry & d,
std::make_pair(
fs::directory_iterator(lib_test_dir),
fs::directory_iterator()
)
){
if(! fs::is_directory(d))
continue;
fs::path p = d.path();
if(p.extension() != ".test")
continue;
build_node_tree(d, root_node);
}
// visit directory nodes and record nodetree
report << "\n";
// emit
root_node.get_spans();
int row_count = header_depth(root_node);
report << "\n";
report << "| Test Name | \n";
// emit column headers
int row_index = 0;
for(;;){
emit_column_headers(root_node, row_index, 0, row_count);
report << "
" ;
if(++row_index == row_count)
break;
report << "\n";
}
// now the rest of the table body
do_table_body(root_node, lib_name, lib_test_dir);
report << "
\n";
}
}// unnamed namespace
// main --------------------------------------------------------------------//
#define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE
#include
int cpp_main( int argc, char * argv[] ) // note name!
{
fs::path initial_path = fs::initial_path();
while ( argc > 1 && *argv[1] == '-' )
{
if ( argc > 2 && std::strcmp( argv[1], "--compiler" ) == 0 )
{ specific_compiler = argv[2]; --argc; ++argv; }
else if ( argc > 2 && std::strcmp( argv[1], "--locate-root" ) == 0 )
{ locate_root = fs::path( argv[2] ); --argc; ++argv; }
else if ( std::strcmp( argv[1], "--ignore-pass" ) == 0 ) ignore_pass = true;
else if ( std::strcmp( argv[1], "--no-warn" ) == 0 ) no_warn = true;
else if ( std::strcmp( argv[1], "--v2" ) == 0 )
{--argc; ++argv ;} // skip
else if ( argc > 2 && std::strcmp( argv[1], "--jamfile" ) == 0)
{--argc; ++argv;} // skip
else { std::cerr << "Unknown option: " << argv[1] << "\n"; argc = 1; }
--argc;
++argv;
}
if ( argc != 2 && argc != 3 )
{
std::cerr <<
"Usage: library_status [options...] status-file [links-file]\n"
" boost-root is the path to the boost tree root directory.\n"
" status-file and links-file are paths to the output files.\n"
" options: --compiler name Run for named compiler only\n"
" --ignore-pass Do not report tests which pass all compilers\n"
" --no-warn Warnings not reported if test passes\n"
" --locate-root path Path to ALL_LOCATE_TARGET for bjam;\n"
" default boost-root.\n"
"Example: library_status --compiler gcc /boost-root cs.html cs-links.html\n"
"Note: Only the leaf of the links-file path is\n"
"used in status-file HTML links. Thus for browsing, status-file,\n"
"links-file must be in the same directory.\n"
;
return 1;
}
if(locate_root.empty())
if(! fs::exists("bin") && ! fs::exists("bin.v2"))
locate_root = find_boost_root(initial_path);
report.open( fs::path( argv[1] ) );
if ( !report )
{
std::cerr << "Could not open report output file: " << argv[2] << std::endl;
return 1;
}
if ( argc == 3 )
{
fs::path links_path( argv[2] );
links_name = links_path.filename().string();
links_file.open( links_path );
if ( !links_file )
{
std::cerr << "Could not open links output file: " << argv[3] << std::endl;
return 1;
}
}
else no_links = true;
const string library_name = find_lib_name(initial_path);
char run_date[128];
std::time_t tod;
std::time( &tod );
std::strftime( run_date, sizeof(run_date),
"%X UTC, %A %d %B %Y", std::gmtime( &tod ) );
report
<< "\n"
<< "\n"
<< "Boost Library Status Automatic Test\n"
<< "\n"
<< "\n"
<< "\n"
<< "Library Status: " + library_name + "
\n"
<< "Run Date: "
<< run_date
<< "\n
"
;
report << "\n
\n
\n";
if ( !no_links )
{
links_file
<< "\n"
<< "\n"
<< "Boost Library Status Error Log\n"
<< "\n"
<< "\n"
<< "\n"
<< "Library Status: " + library_name + "
\n"
<< "Run Date: "
<< run_date
<< "\n
\n
\n"
;
}
// detect whether in a a directory which looks like
// bin//test
// or just
// bin
fs::path library_test_directory = find_lib_test_dir(locate_root);
// if libs exists, drop down a couple of levels
if(fs::is_directory( library_test_directory / "libs")){
library_test_directory /= "libs";
library_test_directory /= library_name;
}
do_table(library_test_directory, library_name);
report << "\n"
"\n"
;
if ( !no_links )
{
links_file
<< "\n"
"\n"
;
}
return 0;
}