/* * * Copyright (c) 2005 Bjorn Roald * Use, modification and distribution is subject to 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) * * This file implements the following: * void bcp_implementation::destination_path(const fs::path& p) * void bcp_implementation::copy_path_and_replace_namespace(const fs::path& p) */ #include "bcp_imp.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace { std::string expression_text; std::string format_string; std::string jamfile_expression_text; std::string jamfile_format_string; std::string header_text; std::string footer_text; std::string replacement_ns; std::string format_replacement_ns; std::string underscored_ns; std::string slashed_ns; std::string dashed_ns; boost::regex expression_cpp; boost::regex expression_jam; // some default data const char* header_text_default = "The following file was processed with $BOOST_ROOT/tools/bcp \n" "to change namespace and paths to include files. The intention \n" "of this processing is to allow non-boost library developers \n" "to use the boost libararies internally in their product without \n" "risking potential conflicts with the use of oter versions \n" "of boost together with their product."; const char* footer_text_default = " ***************** end of file ***************\n"; const int LL = 80; std::string make_comment(const fs::path& p, const std::string& comment) { // std::cout << "in make_comment()\n"; using std::string; string newline = "\n"; // TODO: need to handle configurable dos/unix/mac differences string block_prefix; string block_top_border; string block_bottom_border; string line_prefix; string block_postfix; string escape_this_string; string escaped_string; // c++ style static const boost::regex e_cpp( ".*\\." "(?:" "cxx|cpp|h|hxx|hpp|inc" ")", boost::regex::perl | boost::regex::icase); if ( boost::regex_match(p.leaf(), e_cpp) ) { block_top_border = "//" + std::string( LL-4, '-' ); line_prefix = "// "; block_bottom_border = block_top_border; } // shell style else { static const boost::regex e_shell( ".*\\." "(?:" "ch|in|jam|mk" ")|" "(makefile|Jamfile|Jamrules|configure)", boost::regex::perl | boost::regex::icase); if ( boost::regex_match(p.leaf(), e_shell) ) { block_top_border += "#" + std::string( LL-4, '=' ); line_prefix = "# "; block_bottom_border = block_top_border; } else { // c style static const boost::regex e_c( ".*\\." "(?:" "c|css" ")", boost::regex::perl | boost::regex::icase); if ( boost::regex_match(p.leaf(), e_c) ) { block_prefix = "/* "; line_prefix = " * "; block_postfix = " */"; block_top_border += "/" + std::string( LL-4, '*' ); block_bottom_border += " " + std::string( LL-4, '*' ) + '/'; escape_this_string = "\\*/"; escaped_string = ""; } } } // if we don't know how, we silently do not generate the comment if ( block_prefix == block_postfix && block_postfix == line_prefix && line_prefix.size() == 0 ) return string(); // fix any illegal internal character sequences string escaped_comment; if ( escape_this_string.size() > 0 ) escaped_comment = boost::regex_replace(comment, boost::regex(escape_this_string), escaped_string ); else escaped_comment = comment; string block; // generate single line comment if(escaped_comment.find('\n') == string::npos) { if ( block_prefix.size() == 0 ) block += line_prefix + escaped_comment; else block += block_prefix + escaped_comment + block_postfix; } else // generate multi line { block = block_top_border + newline + line_prefix; for ( unsigned int i = 0 ; i < escaped_comment.size() ; ++i ) { if ( escaped_comment[i] == '\n' ) block += newline + line_prefix; else block += escaped_comment[i]; } block += newline + block_bottom_border + newline; } // std::cout << "end make_comment()\n"; return block; } // list of directories to ignore, ie. leave files unchanged std::set leave_dirs; // list of files to ignore, ie. leave unchanged std::set leave_files; } bool bcp_implementation::diff_only_copy( const std::string& new_file, fs::path destination_path ) { bool diff_found = false; fs::path tmp_path = destination_path.string() + ".bcptmp"; { // write temp file scope fs::remove( tmp_path ); std::ofstream tmp_fs(tmp_path.native_file_string().c_str()); tmp_fs << new_file; // write to tmp destination file } // close file at end of scope if ( fs::exists( destination_path ) && fs::file_size( destination_path ) == fs::file_size( tmp_path ) ) { std::ifstream dest( destination_path.string().c_str() ); std::ifstream tmp( tmp_path.string().c_str() ); diff_found = !std::equal(std::istream_iterator( tmp ), std::istream_iterator(), std::istream_iterator( dest ) ); } else diff_found = true; if ( diff_found ) // move tmp to destination { fs::remove( destination_path ); fs::rename( tmp_path, destination_path ); } else fs::remove( tmp_path ); return diff_found; } void bcp_implementation::set_replacement_namespace(const char* ns) { replacement_ns = ns; m_replace_namespace = true; // set default data, TODO: improve so header and footer can be configured header_text = header_text_default; footer_text = footer_text_default; boost::regex re("::"); underscored_ns = boost::regex_replace(replacement_ns, re, "_"); slashed_ns = boost::regex_replace(replacement_ns, re, "/"); dashed_ns = boost::regex_replace(replacement_ns, re, "-"); // escape hell follows, format string for replacing :: need to be "\:\:" // to avoid colons being interpetet as else in conditional format, // see http://www.boost.org/libs/regex/doc/format_boost_syntax.html format_replacement_ns = boost::regex_replace(replacement_ns, re, "\\\\:\\\\:"); std::string macrofied_ns( underscored_ns ); boost::to_upper( macrofied_ns ); std::cout << "replacement_ns = " << replacement_ns << std::endl << "format_replacement_ns = " << format_replacement_ns << std::endl << "underscored_ns = " << underscored_ns << std::endl << "slashed_ns = " << slashed_ns << std::endl << "dashed_ns = " << dashed_ns << std::endl << "macrofied_ns = " << macrofied_ns << std::endl; expression_text = // don't replace referances: index 1 "(www.boost.org)|" // replace namespace: index 2 "(boost)|" // macro defs: index 3 "(BOOST)" ; expression_cpp = boost::regex(expression_text); format_string = format_string + "(?1www.boost.org)" + "(?2" + format_replacement_ns + ")" + "(?3" + macrofied_ns + ")"; std::cout << "expression_text = " << expression_text << std::endl; std::cout << "format_string = " << format_string << std::endl; jamfile_expression_text = // don't replace referances: index 1 "(www.boost.org)|" // macro defs: index 2 "(BOOST)|" // paths to include dir: index 3 "(/boost/)" ; expression_jam = boost::regex(jamfile_expression_text); jamfile_format_string = jamfile_format_string + "(?1www.boost.org)" + "(?2" + macrofied_ns + ")" + "(?3/" + slashed_ns + "/)"; // leave_dirs.insert( "libs/wave/test/testwave/testfiles"); leave_files.insert( "libs/wave/test/testwave/testfiles/t_9_014.cpp" ); leave_files.insert( "libs/wave/test/testwave/testfiles/t_9_015.cpp" ); leave_files.insert( "libs/wave/test/testwave/testfiles/t_1_001.cpp" ); leave_files.insert( "libs/wave/test/testwave/testfiles/t_1_002.cpp" ); leave_files.insert( "libs/wave/test/testwave/testfiles/t_1_003.cpp" ); leave_files.insert( "libs/wave/test/testwave/testfiles/t_1_004.cpp" ); } fs::path bcp_implementation::destination_path(fs::path p, std::string seperator) { // std::cout << "in destination_path\n"; using std::string; if ( ! m_replace_namespace ) return p; fs::path result; // check for directory named "boost" in in path by // by checking elements in path and rebuilding // the path with replacement for ( fs::path::iterator el = p.begin(); el != p.end(); ++el ) { string element; if ( *el == "boost" ) // directory named boost { element = slashed_ns; } else if ( el->find( "boost" ) != string::npos && *el != "boost-build.jam" && *el != "boost-base.jam" ) { // string "boost" is part of a directory/file name // need to determine how to replace it, use underscore for now element = boost::regex_replace( *el, boost::regex( "boost" ), underscored_ns ); } else { element = *el; } result /= element; } return result; } // intended to be called from inside copy_path() method void bcp_implementation::replace_namespace(const fs::path& p) { // std::cout << "in replace_namespace\n"; assert(m_replace_namespace && !is_binary_file(p)); fs::path dest_p = destination_path( p ); try { // std::cout << "Processing file " // << ( m_boost_path / p).native_file_string().c_str() // << std::endl; std::ifstream fs(( m_boost_path / p).native_file_string().c_str()); std::string in; in.reserve(fs.rdbuf()->in_avail()); char c; while(fs.get(c)) { if(in.capacity() == in.size()) in.reserve(in.capacity() * 3); in.append(1, c); } bool leave_it_unchanged = leave_dirs.find( p.branch_path() ) != leave_dirs.end() || leave_files.find( p ) != leave_files.end(); std::stringstream os; if ( ! leave_it_unchanged ) { std::string file_hdr = header_text; file_hdr += "\nnamespace 'boost' has been replaced with namespace '" + replacement_ns + "'.\n" + "original file: BOOST_PATH/" + p.string(); os << make_comment( p, file_hdr ); std::ostream_iterator out(os); if ( is_bjam_file( p ) ) { boost::regex_replace(out, in.begin(), in.end(), expression_jam, jamfile_format_string, boost::match_default | boost::format_all); } else { boost::regex_replace(out, in.begin(), in.end(), expression_cpp, format_string, boost::match_default | boost::format_all); } os << std::endl << make_comment( p, footer_text ) << std::endl; } else { os << in; } fs::path dest = m_dest_path / dest_p; // diff against existing destination file // then write only if there is a difference, purpose // of this is to avoid excessive rebuilds when maintaining // boost code and testing in a bcp generated source tree if ( m_diff_only_mode ) { if ( diff_only_copy( os.str(), dest ) ) { std::cout << "Writing diff:\t" << dest.string() << std::endl; // fs::remove( dest ); // std::ofstream ofs(dest.native_file_string().c_str()); // ofs << os.str(); // write to destination file } else { std::cout << "Identical:\t" << dest.string() << std::endl; } } else { std::cout << "Writing: \t" << dest.string() << std::endl; fs::remove( dest ); std::ofstream ofs(dest.native_file_string().c_str()); ofs << os.str(); // write to destination file } } catch(std::exception& e) { std::cout << "error: replace_namespace: " << e.what() << "\n"; exit(1); } catch(...) { std::cout << "error: replace_namespace : unexpected exception \n"; exit(1); } }