📄 process_jam_log.cpp
字号:
// process jam regression test output into XML -----------------------------//// Copyright Beman Dawes 2002. Distributed under the Boost// Software License, Version 1.0. (See accompanying file// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)// See http://www.boost.org/tools/regression for documentation.#include "detail/tiny_xml.hpp"#include "boost/filesystem/operations.hpp"#include "boost/filesystem/fstream.hpp"#include "boost/filesystem/exception.hpp"#include "boost/filesystem/convenience.hpp"#include <iostream>#include <string>#include <cstring>#include <map>#include <utility> // for make_pair#include <ctime>#include <cctype> // for tolower#include <cstdlib> // for exitusing std::string;namespace xml = boost::tiny_xml;namespace fs = boost::filesystem;// options static bool echo = false;static bool create_dirs = false;static bool boost_build_v2 = true;namespace{ struct test_info { string file_path; // relative boost-root string type; bool always_show_run_output; }; typedef std::map< string, test_info > test2info_map; // key is test-name test2info_map test2info; fs::path boost_root; fs::path locate_root; // ALL_LOCATE_TARGET (or boost_root if none) // set_boost_root --------------------------------------------------------// void set_boost_root() { boost_root = fs::initial_path(); for(;;) { if ( fs::exists( boost_root / "libs" ) ) { fs::current_path( fs::initial_path() ); // restore initial path return; } fs::current_path( ".." ); if ( boost_root == fs::current_path() ) { fs::current_path( fs::initial_path() ); // restore initial path std::cout << "Abort: process_jam_log must be run from within a boost directory tree\n"; std::exit(1); } boost_root = fs::current_path(); } } // append_html -------------------------------------------------------------// void append_html( const string & src, string & target ) { // there are a few lines we want to ignore if ( src.find( "th target..." ) != string::npos || src.find( "cc1plus.exe: warning: changing search order for system directory" ) != string::npos || src.find( "cc1plus.exe: warning: as it has already been specified as a non-system directory" ) != string::npos ) return; // on some platforms (e.g. tru64cxx) the following line is a real performance boost target.reserve(src.size() * 2 + target.size()); for ( string::size_type pos = 0; pos < src.size(); ++pos ) { if ( src[pos] == '<' ) target += "<"; else if ( src[pos] == '>' ) target += ">"; else if ( src[pos] == '&' ) target += "&"; else target += src[pos]; } } // timestamp ---------------------------------------------------------------// string timestamp() { char run_date[128]; std::time_t tod; std::time( &tod ); std::strftime( run_date, sizeof(run_date), "%Y-%m-%d %X UTC", std::gmtime( &tod ) ); return string( run_date ); }// convert path separators to forward slashes ------------------------------// void convert_path_separators( string & s ) { for ( string::iterator itr = s.begin(); itr != s.end(); ++itr ) if ( *itr == '\\' || *itr == '!' ) *itr = '/'; }// trim_left ----------------------------------------------------------------// std::string trim_left( std::string const& s ) { std::string::size_type const pos( s.find_first_not_of(' ') ); return pos != std::string::npos ? s.substr( pos, s.size() - pos + 1 ) : "" ; } // split --------------------------------------------------------------------// std::vector<std::string> split( std::string const& s ) { std::string::size_type const pos( s.find_first_of(' ') ); std::vector<std::string> result( 1, s.substr( 0, pos ) ); if ( pos == std::string::npos ) return result; std::vector<std::string> const rest( split( trim_left( s.substr( pos, s.size() - pos + 1 ) ) ) ); result.insert( result.end(), rest.begin(), rest.end() ); return result; }// extract a target directory path from a jam target string ----------------//// s may be relative to the initial_path:// ..\..\..\libs\foo\build\bin\libfoo.lib\vc7\debug\runtime-link-dynamic\boo.obj// s may be absolute:// d:\myboost\libs\foo\build\bin\libfoo.lib\vc7\debug\runtime-link-dynamic\boo.obj// return path is always relative to the boost directory tree:// libs/foo/build/bin/libfs.lib/vc7/debug/runtime-link-dynamic string target_directory( const string & s ) { string temp( s ); convert_path_separators( temp ); temp.erase( temp.find_last_of( "/" ) ); // remove leaf temp = split( trim_left( temp ) ).back(); if ( temp[0] == '.' ) temp.erase( 0, temp.find_first_not_of( "./" ) ); else temp.erase( 0, locate_root.string().size()+1 ); if ( echo ) std::cout << "\ttarget_directory( \"" << s << "\") -> \"" << temp << "\"" << std::endl; return temp; } string::size_type target_name_end( const string & s ) { string::size_type pos = s.find( ".test/" ); if ( pos == string::npos ) pos = s.find( ".dll/" ); if ( pos == string::npos ) pos = s.find( ".so/" ); if ( pos == string::npos ) pos = s.find( ".lib/" ); if ( pos == string::npos ) pos = s.find( ".pyd/" ); if ( pos == string::npos ) pos = s.find( ".a/" ); return pos; } string toolset( const string & s ) { string::size_type pos = target_name_end( s ); if ( pos == string::npos ) pos = s.find( "build/" ); if ( pos == string::npos ) return ""; pos = s.find( "/", pos ) + 1; return s.substr( pos, s.find( "/", pos ) - pos ); } string test_name( const string & s ) { string::size_type pos = target_name_end( s ); if ( pos == string::npos ) return ""; string::size_type pos_start = s.rfind( '/', pos ) + 1; return s.substr( pos_start, (s.find( ".test/" ) != string::npos ? pos : s.find( "/", pos )) - pos_start ); } // Take a path to a target directory of test, and // returns library name corresponding to that path. string test_path_to_library_name( string const& path ) { std::string result; string::size_type start_pos( path.find( "libs/" ) ); if ( start_pos != string::npos ) { // The path format is ...libs/functional/hash/test/something.test/.... // So, the part between "libs" and "test/something.test" can be considered // as library name. But, for some libraries tests are located too deep, // say numeric/ublas/test/test1 directory, and some libraries have tests // in several subdirectories (regex/example and regex/test). So, nested // directory may belong to several libraries. // To disambituate, it's possible to place a 'sublibs' file in // a directory. It means that child directories are separate libraries. // It's still possible to have tests in the directory that has 'sublibs' // file. std::string interesting; start_pos += 5; string::size_type end_pos( path.find( ".test/", start_pos ) ); end_pos = path.rfind('/', end_pos); if (path.substr(end_pos - 5, 5) == "/test") interesting = path.substr( start_pos, end_pos - 5 - start_pos ); else interesting = path.substr( start_pos, end_pos - start_pos ); // Take slash separate elements until we have corresponding 'sublibs'. end_pos = 0; for(;;) { end_pos = interesting.find('/', end_pos); if (end_pos == string::npos) { result = interesting; break; } result = interesting.substr(0, end_pos); if ( fs::exists( ( boost_root / "libs" ) / result / "sublibs" ) ) { end_pos = end_pos + 1; } else break; } } return result; } // Tries to find target name in the string 'msg', starting from // position start. // If found, extract the directory name from the target name and // stores it in 'dir', and return the position after the target name. // Otherwise, returns string::npos. string::size_type parse_skipped_msg_aux(const string& msg, string::size_type start, string& dir) { dir.clear(); string::size_type start_pos = msg.find( '<', start ); if ( start_pos == string::npos ) return string::npos; ++start_pos; string::size_type end_pos = msg.find( '>', start_pos ); dir += msg.substr( start_pos, end_pos - start_pos ); if ( boost_build_v2 ) { // The first letter is a magic value indicating // the type of grist. convert_path_separators( dir ); dir.erase( 0, 1 ); // We need path from root, not from 'status' dir. if (dir.find("../") == 0) dir.erase(0,3); else // dir is always relative to the boost directory tree dir.erase( 0, locate_root.string().size()+1 ); } else { if ( dir[0] == '@' ) { // new style build path, rooted build tree convert_path_separators( dir ); dir.replace( 0, 1, "bin/" ); } else { // old style build path, integrated build tree start_pos = dir.rfind( '!' ); convert_path_separators( dir ); string::size_type path_sep_pos = dir.find( '/', start_pos + 1 ); if ( path_sep_pos != string::npos ) dir.insert( path_sep_pos, "/bin" ); else { // see http://article.gmane.org/gmane.comp.lib.boost.devel/146688; // the following code assumes that: a) 'dir' is not empty, // b) 'end_pos != string::npos' and c) 'msg' always ends with '...' if ( dir[dir.size() - 1] == '@' ) dir += "/" + msg.substr( end_pos + 1, msg.size() - end_pos - 1 - 3 ); } } } return end_pos; } // the format of paths is really kinky, so convert to normal form // first path is missing the leading "..\". // first path is missing "\bin" after "status". // second path is missing the leading "..\". // second path is missing "\bin" after "build". // second path uses "!" for some separators. void parse_skipped_msg( const string & msg, string & first_dir, string & second_dir ) { string::size_type pos = parse_skipped_msg_aux(msg, 0, first_dir); if (pos == string::npos) return; parse_skipped_msg_aux(msg, pos, second_dir); }// test_log hides database details -----------------------------------------// class test_log : boost::noncopyable { const string & m_target_directory; xml::element_ptr m_root; public: test_log( const string & target_directory, const string & test_name, const string & toolset, bool force_new_file ) : m_target_directory( target_directory ) { if ( !force_new_file ) { fs::path pth( locate_root / target_directory / "test_log.xml" ); fs::ifstream file( pth ); if ( file ) // existing file { try { m_root = xml::parse( file, pth.string() ); return; } catch(...) { // unable to parse existing XML file, fall through } } } string library_name( test_path_to_library_name( target_directory ) ); test_info info; test2info_map::iterator itr( test2info.find( library_name + "/" + test_name ) ); if ( itr != test2info.end() ) info = itr->second; if ( !info.file_path.empty() ) library_name = test_path_to_library_name( info.file_path ); if ( info.type.empty() ) { if ( target_directory.find( ".lib/" ) != string::npos || target_directory.find( ".dll/" ) != string::npos || target_directory.find( ".so/" ) != string::npos || target_directory.find( ".dylib/" ) != string::npos || target_directory.find( "/build/" ) != string::npos ) { info.type = "lib"; } else if ( target_directory.find( ".pyd/" ) != string::npos ) info.type = "pyd"; } m_root.reset( new xml::element( "test-log" ) ); m_root->attributes.push_back( xml::attribute( "library", library_name ) ); m_root->attributes.push_back( xml::attribute( "test-name", test_name ) ); m_root->attributes.push_back( xml::attribute( "test-type", info.type ) ); m_root->attributes.push_back( xml::attribute( "test-program", info.file_path ) ); m_root->attributes.push_back( xml::attribute( "target-directory", target_directory ) ); m_root->attributes.push_back( xml::attribute( "toolset", toolset ) ); m_root->attributes.push_back( xml::attribute( "show-run-output", info.always_show_run_output ? "true" : "false" ) ); } ~test_log() { fs::path pth( locate_root / m_target_directory / "test_log.xml" ); if ( create_dirs && !fs::exists( pth.branch_path() ) ) fs::create_directories( pth.branch_path() ); fs::ofstream file( pth ); if ( !file ) { std::cout << "*****Warning - can't open output file: " << pth.string() << "\n"; } else xml::write( *m_root, file ); } const string & target_directory() const { return m_target_directory; } void remove_action( const string & action_name ) // no effect if action_name not found { xml::element_list::iterator itr; for ( itr = m_root->elements.begin(); itr != m_root->elements.end() && (*itr)->name != action_name; ++itr ) {} if ( itr != m_root->elements.end() ) m_root->elements.erase( itr ); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -