mirror of
https://github.com/boostorg/wave.git
synced 2026-01-20 05:02:45 +00:00
* Check hooks after expected errors, not just when there are none Some unit tests had intentional errors, and code to verify that the appropriate hooks were called, but we never checked. * Update unit tests to match hooks, now that we are checking * Fix bug revealed by checking the hooks Or in other words, bug #161 (thanks abakhirkin for the fix) * Fix link in documentation
1573 lines
60 KiB
C++
1573 lines
60 KiB
C++
/*=============================================================================
|
|
Boost.Wave: A Standard compliant C++ preprocessor library
|
|
http://www.boost.org/
|
|
|
|
Copyright (c) 2001-2013 Hartmut Kaiser. 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)
|
|
=============================================================================*/
|
|
|
|
// disable stupid compiler warnings
|
|
#include <boost/config/warning_disable.hpp>
|
|
|
|
// system headers
|
|
#include <string>
|
|
#include <iosfwd>
|
|
#include <vector>
|
|
#include <ctime>
|
|
|
|
// include boost
|
|
#include <boost/config.hpp>
|
|
#include <boost/assert.hpp>
|
|
#include <boost/throw_exception.hpp>
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <boost/filesystem/operations.hpp>
|
|
#include <boost/detail/workaround.hpp>
|
|
|
|
// include Wave
|
|
|
|
#include <boost/wave.hpp>
|
|
|
|
// include the lexer related stuff
|
|
#include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type
|
|
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer type
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Include lexer specifics, import lexer names
|
|
#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
|
|
#include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Include the grammar definitions, if these shouldn't be compiled separately
|
|
// (ATTENTION: _very_ large compilation times!)
|
|
#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
|
|
#include <boost/wave/grammars/cpp_intlit_grammar.hpp>
|
|
#include <boost/wave/grammars/cpp_chlit_grammar.hpp>
|
|
#include <boost/wave/grammars/cpp_grammar.hpp>
|
|
#include <boost/wave/grammars/cpp_expression_grammar.hpp>
|
|
#include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
|
|
#include <boost/wave/grammars/cpp_defined_grammar.hpp>
|
|
#endif
|
|
|
|
// test application related headers
|
|
#include "cmd_line_utils.hpp"
|
|
#include "testwave_app.hpp"
|
|
#include "collect_hooks_information.hpp"
|
|
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
|
|
|
|
# ifdef BOOST_NO_STDC_NAMESPACE
|
|
namespace std
|
|
{
|
|
using ::asctime; using ::gmtime; using ::localtime;
|
|
using ::difftime; using ::time; using ::tm; using ::mktime; using ::system;
|
|
}
|
|
# endif
|
|
|
|
namespace po = boost::program_options;
|
|
namespace fs = boost::filesystem;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// testwave version definitions
|
|
#define TESTWAVE_VERSION_MAJOR 0
|
|
#define TESTWAVE_VERSION_MINOR 6
|
|
#define TESTWAVE_VERSION_SUBMINOR 0
|
|
|
|
namespace {
|
|
struct fs_path_imbue_utf8
|
|
{
|
|
explicit fs_path_imbue_utf8(bool enable)
|
|
: m_enabled(enable), m_prevLocale()
|
|
{
|
|
if (!m_enabled) return;
|
|
static std::locale global_loc = std::locale();
|
|
static std::locale utf_8_loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet);
|
|
|
|
m_prevLocale = boost::filesystem::path::imbue(utf_8_loc);
|
|
|
|
}
|
|
~fs_path_imbue_utf8()
|
|
{
|
|
if (!m_enabled) return;
|
|
boost::filesystem::path::imbue(m_prevLocale);
|
|
}
|
|
private:
|
|
fs_path_imbue_utf8();
|
|
fs_path_imbue_utf8(fs_path_imbue_utf8 const&);
|
|
fs_path_imbue_utf8& operator=(fs_path_imbue_utf8 const&);
|
|
|
|
bool m_enabled;
|
|
std::locale m_prevLocale;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <typename Iterator>
|
|
inline bool
|
|
handle_next_token(Iterator &it, Iterator const& end,
|
|
std::string &result)
|
|
{
|
|
typedef typename Iterator::value_type token_type;
|
|
|
|
token_type tok = *it++;
|
|
result = result + tok.get_value().c_str();
|
|
return (it == end) ? false : true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <typename String>
|
|
String const& handle_quoted_filepath(String &name)
|
|
{
|
|
using boost::wave::util::impl::unescape_lit;
|
|
|
|
String unesc_name = unescape_lit(name.substr(1, name.size()-2));
|
|
fs::path p (boost::wave::util::create_path(unesc_name.c_str()));
|
|
|
|
name = String("\"") + boost::wave::util::leaf(p).c_str() + String("\"");
|
|
return name;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <typename Iterator>
|
|
bool handle_line_directive(Iterator &it, Iterator const& end,
|
|
std::string &result)
|
|
{
|
|
typedef typename Iterator::value_type token_type;
|
|
typedef typename token_type::string_type string_type;
|
|
|
|
if (!handle_next_token(it, end, result) || // #line
|
|
!handle_next_token(it, end, result) || // whitespace
|
|
!handle_next_token(it, end, result) || // number
|
|
!handle_next_token(it, end, result)) // whitespace
|
|
{
|
|
return false;
|
|
}
|
|
|
|
using boost::wave::util::impl::unescape_lit;
|
|
|
|
token_type filename = *it;
|
|
string_type name = filename.get_value();
|
|
|
|
handle_quoted_filepath(name);
|
|
result = result + name.c_str();
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
inline T const&
|
|
variables_map_as(po::variable_value const& v, T*)
|
|
{
|
|
#if (__GNUC__ == 3 && (__GNUC_MINOR__ == 2 || __GNUC_MINOR__ == 3)) || \
|
|
BOOST_WORKAROUND(__MWERKS__, < 0x3200)
|
|
// gcc 3.2.x and 3.3.x choke on vm[...].as<...>()
|
|
// CW 8.3 has problems with the v.as<T>() below
|
|
T const* r = boost::any_cast<T>(&v.value());
|
|
if (!r)
|
|
boost::throw_exception(boost::bad_any_cast());
|
|
return *r;
|
|
#else
|
|
return v.as<T>();
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This function compares the real result and the expected one but first
|
|
// replaces all occurrences in the expected result of
|
|
// $E: to the result of preprocessing the given expression
|
|
// $F: to the passed full filepath
|
|
// $P: to the full path
|
|
// $B: to the full path (same as $P, but using forward slash '/' on Windows)
|
|
// $V: to the current Boost version number
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
testwave_app::got_expected_result(std::string const& filename,
|
|
std::string const& result, std::string& expected)
|
|
{
|
|
using boost::wave::util::impl::escape_lit;
|
|
|
|
std::string full_result;
|
|
std::string::size_type pos = 0;
|
|
std::string::size_type pos1 = expected.find_first_of("$");
|
|
|
|
if (pos1 != std::string::npos) {
|
|
do {
|
|
switch(expected[pos1+1]) {
|
|
case 'E': // preprocess the given token sequence
|
|
{
|
|
if ('(' == expected[pos1+2]) {
|
|
std::size_t p = expected.find_first_of(")", pos1+1);
|
|
if (std::string::npos == p) {
|
|
std::cerr
|
|
<< "testwave: unmatched parenthesis in $E"
|
|
" directive" << std::endl;
|
|
return false;
|
|
}
|
|
std::string source = expected.substr(pos1+3, p-pos1-3);
|
|
std::string result, error, hooks;
|
|
bool pp_result;
|
|
std::tie(pp_result, std::ignore) = preprocess_file(filename, source,
|
|
result, error, hooks, "", true);
|
|
if (!pp_result) {
|
|
std::cerr
|
|
<< "testwave: preprocessing error in $E directive: "
|
|
<< error << std::endl;
|
|
return false;
|
|
}
|
|
full_result = full_result +
|
|
expected.substr(pos, pos1-pos) + result;
|
|
pos1 = expected.find_first_of ("$",
|
|
pos = pos1 + 4 + source.size());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'F': // insert base file name
|
|
full_result = full_result +
|
|
expected.substr(pos, pos1-pos) + escape_lit(filename);
|
|
pos1 = expected.find_first_of ("$", pos = pos1 + 2);
|
|
break;
|
|
|
|
case 'P': // insert full path
|
|
case 'B': // same as 'P', but forward slashes on Windows
|
|
{
|
|
fs::path fullpath (
|
|
boost::wave::util::complete_path(
|
|
boost::wave::util::create_path(filename),
|
|
boost::wave::util::current_path())
|
|
);
|
|
|
|
if ('(' == expected[pos1+2]) {
|
|
// the $P(basename) syntax is used
|
|
std::size_t p = expected.find_first_of(")", pos1+1);
|
|
if (std::string::npos == p) {
|
|
std::cerr
|
|
<< "testwave: unmatched parenthesis in $P"
|
|
" directive" << std::endl;
|
|
return false;
|
|
}
|
|
std::string base = expected.substr(pos1+3, p-pos1-3);
|
|
fullpath = boost::wave::util::branch_path(fullpath) /
|
|
boost::wave::util::create_path(base);
|
|
full_result += expected.substr(pos, pos1-pos);
|
|
if ('P' == expected[pos1+1]) {
|
|
#if defined(BOOST_WINDOWS)
|
|
std::string p = replace_slashes(
|
|
boost::wave::util::native_file_string(
|
|
boost::wave::util::normalize(fullpath)),
|
|
"/", '\\');
|
|
#else
|
|
std::string p (
|
|
boost::wave::util::native_file_string(
|
|
boost::wave::util::normalize(fullpath)));
|
|
#endif
|
|
full_result += escape_lit(p);
|
|
}
|
|
else {
|
|
#if defined(BOOST_WINDOWS)
|
|
std::string p = replace_slashes(
|
|
boost::wave::util::normalize(fullpath).string());
|
|
#else
|
|
std::string p (
|
|
boost::wave::util::normalize(fullpath).string());
|
|
#endif
|
|
full_result += escape_lit(p);
|
|
}
|
|
pos1 = expected.find_first_of ("$",
|
|
pos = pos1 + 4 + base.size());
|
|
}
|
|
else {
|
|
// the $P is used on its own
|
|
full_result += expected.substr(pos, pos1-pos);
|
|
if ('P' == expected[pos1+1]) {
|
|
full_result += escape_lit(
|
|
boost::wave::util::native_file_string(fullpath));
|
|
}
|
|
else {
|
|
#if defined(BOOST_WINDOWS)
|
|
std::string p = replace_slashes(fullpath.string());
|
|
#else
|
|
std::string p (fullpath.string());
|
|
#endif
|
|
full_result += escape_lit(fullpath.string());
|
|
}
|
|
pos1 = expected.find_first_of ("$", pos = pos1 + 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'R': // insert relative file name
|
|
case 'S': // same as 'R', but forward slashes on Windows
|
|
{
|
|
fs::path relpath;
|
|
boost::wave::util::as_relative_to(
|
|
boost::wave::util::create_path(filename),
|
|
boost::wave::util::current_path(),
|
|
relpath);
|
|
|
|
if ('(' == expected[pos1+2]) {
|
|
// the $R(basename) syntax is used
|
|
std::size_t p = expected.find_first_of(")", pos1+1);
|
|
if (std::string::npos == p) {
|
|
std::cerr
|
|
<< "testwave: unmatched parenthesis in $R"
|
|
" directive" << std::endl;
|
|
return false;
|
|
}
|
|
std::string base = expected.substr(pos1+3, p-pos1-3);
|
|
relpath = boost::wave::util::branch_path(relpath) /
|
|
boost::wave::util::create_path(base);
|
|
full_result += expected.substr(pos, pos1-pos);
|
|
if ('R' == expected[pos1+1]) {
|
|
full_result += escape_lit(
|
|
boost::wave::util::native_file_string(
|
|
boost::wave::util::normalize(relpath)));
|
|
}
|
|
else {
|
|
#if defined(BOOST_WINDOWS)
|
|
std::string p = replace_slashes(
|
|
boost::wave::util::normalize(relpath).string());
|
|
#else
|
|
std::string p (
|
|
boost::wave::util::normalize(relpath).string());
|
|
#endif
|
|
full_result += escape_lit(p);
|
|
}
|
|
pos1 = expected.find_first_of ("$",
|
|
pos = pos1 + 4 + base.size());
|
|
}
|
|
else {
|
|
// the $R is used on its own
|
|
full_result += expected.substr(pos, pos1-pos);
|
|
if ('R' == expected[pos1+1]) {
|
|
full_result += escape_lit(
|
|
boost::wave::util::native_file_string(relpath));
|
|
}
|
|
else {
|
|
#if defined(BOOST_WINDOWS)
|
|
std::string p = replace_slashes(relpath.string());
|
|
#else
|
|
std::string p (relpath.string());
|
|
#endif
|
|
full_result += escape_lit(p);
|
|
}
|
|
pos1 = expected.find_first_of ("$", pos = pos1 + 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'V': // insert Boost version
|
|
full_result = full_result +
|
|
expected.substr(pos, pos1-pos) + BOOST_LIB_VERSION;
|
|
pos1 = expected.find_first_of ("$", pos = pos1 + 2);
|
|
break;
|
|
|
|
default:
|
|
full_result = full_result +
|
|
expected.substr(pos, pos1-pos);
|
|
pos1 = expected.find_first_of ("$", (pos = pos1) + 1);
|
|
break;
|
|
}
|
|
|
|
} while(pos1 != std::string::npos);
|
|
full_result += expected.substr(pos);
|
|
}
|
|
else {
|
|
full_result = expected;
|
|
}
|
|
|
|
expected = full_result;
|
|
return full_result == result;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
testwave_app::testwave_app(po::variables_map const& vm)
|
|
: debuglevel(1), desc_options("Preprocessor configuration options"),
|
|
global_vm(vm)
|
|
{
|
|
desc_options.add_options()
|
|
("include,I", po::value<cmd_line_utils::include_paths>()->composing(),
|
|
"specify an additional include directory")
|
|
("sysinclude,S", po::value<std::vector<std::string> >()->composing(),
|
|
"specify an additional system include directory")
|
|
("forceinclude,F", po::value<std::vector<std::string> >()->composing(),
|
|
"force inclusion of the given file")
|
|
("define,D", po::value<std::vector<std::string> >()->composing(),
|
|
"specify a macro to define (as macro[=[value]])")
|
|
("predefine,P", po::value<std::vector<std::string> >()->composing(),
|
|
"specify a macro to predefine (as macro[=[value]])")
|
|
("undefine,U", po::value<std::vector<std::string> >()->composing(),
|
|
"specify a macro to undefine")
|
|
("nesting,n", po::value<int>(),
|
|
"specify a new maximal include nesting depth")
|
|
("long_long", "enable long long support in C++ mode")
|
|
("preserve", "preserve comments")
|
|
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
|
|
("variadics", "enable certain C99 extensions in C++ mode")
|
|
("c99", "enable C99 mode (implies --variadics)")
|
|
#endif
|
|
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
|
|
("noguard,G", "disable include guard detection")
|
|
#endif
|
|
("skipped_token_hooks", "record skipped_token hook calls")
|
|
#if BOOST_WAVE_SUPPORT_CPP0X != 0
|
|
("c++11", "enable C++11 mode (implies --variadics and --long_long)")
|
|
#endif
|
|
#if BOOST_WAVE_SUPPORT_CPP1Z != 0
|
|
("c++17", "enable C++17 mode (implies --variadics and --long_long, adds __has_include)")
|
|
#endif
|
|
#if BOOST_WAVE_SUPPORT_CPP2A != 0
|
|
("c++20", "enable C++20 mode (implies --variadics and --long_long, adds __has_include and __VA_OPT__)")
|
|
#endif
|
|
("warning,W", po::value<std::vector<std::string> >()->composing(),
|
|
"Warning settings.")
|
|
;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Test the given file (i.e. preprocess the file and compare the result
|
|
// against the embedded 'R' comments, if an error occurs compare the error
|
|
// message against the given 'E' comments. Then, compare the
|
|
// generated hooks result against the given 'H' comments).
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
testwave_app::test_a_file(std::string filename)
|
|
{
|
|
// read the input file into a string
|
|
std::string instr;
|
|
if (!read_file(filename, instr))
|
|
return false; // error was reported already
|
|
|
|
std::string use_utf8;
|
|
extract_special_information(filename, instr, 'U', use_utf8);
|
|
fs_path_imbue_utf8 to_utf8(use_utf8.substr(0,3) == "yes");
|
|
|
|
bool test_hooks = true;
|
|
if (global_vm.count("hooks"))
|
|
test_hooks = variables_map_as(global_vm["hooks"], (bool *)NULL);
|
|
|
|
std::string expected_cfg_macro;
|
|
extract_special_information(filename, instr, 'D', expected_cfg_macro);
|
|
|
|
// extract expected output, preprocess the data and compare results
|
|
std::string expected, expected_hooks;
|
|
if (extract_expected_output(filename, instr, expected, expected_hooks)) {
|
|
bool retval = true; // assume success
|
|
bool printed_result = false;
|
|
std::string result, error, hooks;
|
|
bool pp_result;
|
|
bool suppressed; // true if there is an absent expected config macro
|
|
std::tie(pp_result, suppressed) =
|
|
preprocess_file(filename, instr, result, error, hooks, expected_cfg_macro);
|
|
if (pp_result || !result.empty()) {
|
|
// did we expect an error?
|
|
std::string expected_error;
|
|
if (!extract_special_information(filename, instr, 'E', expected_error))
|
|
return false;
|
|
|
|
if (!expected_error.empty() &&
|
|
!got_expected_result(filename, error, expected_error))
|
|
{
|
|
// we expected an error but got none (or a different one)
|
|
if (debuglevel > 2) {
|
|
std::cerr
|
|
<< filename << ": failed" << std::endl
|
|
<< "result: " << std::endl << result << std::endl;
|
|
|
|
if (!error.empty()) {
|
|
std::cerr << "expected result: " << std::endl
|
|
<< expected << std::endl;
|
|
}
|
|
if (!expected_error.empty()) {
|
|
std::cerr << "expected error: " << std::endl
|
|
<< expected_error << std::endl;
|
|
}
|
|
}
|
|
else if (debuglevel > 1) {
|
|
std::cerr << filename << ": failed" << std::endl;
|
|
}
|
|
retval = false;
|
|
}
|
|
else if (suppressed)
|
|
{
|
|
// no need to check result or hooks as the test was not run
|
|
}
|
|
else if (!got_expected_result(filename, result, expected)) {
|
|
// no preprocessing error encountered
|
|
if (debuglevel > 2) {
|
|
std::cerr
|
|
<< filename << ": failed" << std::endl
|
|
<< "result: " << std::endl << result << std::endl
|
|
<< "expected: " << std::endl << expected << std::endl;
|
|
}
|
|
else if (debuglevel > 1) {
|
|
std::cerr << filename << ": failed" << std::endl;
|
|
}
|
|
retval = false;
|
|
}
|
|
else {
|
|
// preprocessing succeeded, check hook information, if appropriate
|
|
if (test_hooks && !expected_hooks.empty() &&
|
|
!got_expected_result(filename, hooks, expected_hooks))
|
|
{
|
|
if (debuglevel > 2) {
|
|
std::cerr << filename << ": failed" << std::endl
|
|
<< "hooks result: " << std::endl << hooks
|
|
<< std::endl;
|
|
std::cerr << "expected hooks result: " << std::endl
|
|
<< expected_hooks << std::endl;
|
|
}
|
|
else if (debuglevel > 1) {
|
|
std::cerr << filename << ": failed" << std::endl;
|
|
}
|
|
retval = false;
|
|
}
|
|
}
|
|
|
|
// print success message, if appropriate
|
|
if (retval) {
|
|
if (debuglevel > 5) {
|
|
std::cerr
|
|
<< filename << ": succeeded" << std::endl
|
|
<< "result: " << std::endl << result << std::endl
|
|
<< "hooks result: " << std::endl << hooks << std::endl;
|
|
}
|
|
else if (debuglevel > 4) {
|
|
std::cerr
|
|
<< filename << ": succeeded" << std::endl
|
|
<< "result: " << std::endl << result << std::endl;
|
|
}
|
|
else if (debuglevel > 3) {
|
|
std::cerr << filename << ": succeeded" << std::endl;
|
|
}
|
|
printed_result = true;
|
|
}
|
|
}
|
|
|
|
if (!pp_result) {
|
|
// there was a preprocessing error, was it expected?
|
|
std::string expected_error;
|
|
if (!extract_special_information(filename, instr, 'E', expected_error))
|
|
return false;
|
|
|
|
if (!got_expected_result(filename, error, expected_error)) {
|
|
// the error was unexpected
|
|
if (debuglevel > 2) {
|
|
std::cerr
|
|
<< filename << ": failed" << std::endl;
|
|
|
|
if (!expected_error.empty()) {
|
|
std::cerr
|
|
<< "error result: " << std::endl << error << std::endl
|
|
<< "expected error: " << std::endl
|
|
<< expected_error << std::endl;
|
|
}
|
|
else {
|
|
std::cerr << "unexpected error: " << error << std::endl;
|
|
}
|
|
}
|
|
else if (debuglevel > 1) {
|
|
std::cerr << filename << ": failed" << std::endl;
|
|
}
|
|
retval = false;
|
|
} else {
|
|
// expected error; check the hooks also
|
|
if (test_hooks && !expected_hooks.empty() &&
|
|
!got_expected_result(filename, hooks, expected_hooks))
|
|
{
|
|
if (debuglevel > 2) {
|
|
std::cerr << filename << ": failed (though caught expected error)" << std::endl
|
|
<< "hooks result: " << std::endl << hooks
|
|
<< std::endl;
|
|
std::cerr << "expected hooks result: " << std::endl
|
|
<< expected_hooks << std::endl;
|
|
}
|
|
else if (debuglevel > 1) {
|
|
std::cerr << filename << ": failed" << std::endl;
|
|
}
|
|
retval = false;
|
|
}
|
|
}
|
|
|
|
if (retval) {
|
|
if (debuglevel > 5) {
|
|
std::cerr
|
|
<< filename << ": succeeded (caught expected error)"
|
|
<< std::endl << "error result: " << std::endl << error
|
|
<< std::endl;
|
|
|
|
if (!printed_result) {
|
|
std::cerr
|
|
<< "hooks result: " << std::endl << hooks
|
|
<< std::endl;
|
|
}
|
|
}
|
|
else if (debuglevel > 4) {
|
|
std::cerr
|
|
<< filename << ": succeeded (caught expected error)"
|
|
<< std::endl << "error result: " << std::endl << error
|
|
<< std::endl;
|
|
}
|
|
else if (debuglevel > 3) {
|
|
// caught the expected error message
|
|
std::cerr << filename << ": succeeded" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
else {
|
|
std::cerr
|
|
<< filename << ": no information about expected results found"
|
|
<< std::endl;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// print the current version of this program
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
int
|
|
testwave_app::print_version()
|
|
{
|
|
// get time of last compilation of this file
|
|
boost::wave::util::time_conversion_helper compilation_time(__DATE__ " " __TIME__);
|
|
|
|
// calculate the number of days since Feb 12 2005
|
|
// (the day the testwave project was started)
|
|
std::tm first_day;
|
|
|
|
using namespace std; // some platforms have memset in namespace std
|
|
memset (&first_day, 0, sizeof(std::tm));
|
|
first_day.tm_mon = 1; // Feb
|
|
first_day.tm_mday = 12; // 12
|
|
first_day.tm_year = 105; // 2005
|
|
|
|
long seconds = long(std::difftime(compilation_time.get_time(),
|
|
std::mktime(&first_day)));
|
|
|
|
std::cout
|
|
<< TESTWAVE_VERSION_MAJOR << '.'
|
|
<< TESTWAVE_VERSION_MINOR << '.'
|
|
<< TESTWAVE_VERSION_SUBMINOR << '.'
|
|
<< seconds/(3600*24) // get number of days from seconds
|
|
<< std::endl;
|
|
return 0; // exit app
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// print the copyright statement
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
int
|
|
testwave_app::print_copyright()
|
|
{
|
|
char const *copyright[] = {
|
|
"",
|
|
"Testwave: A test driver for the Boost.Wave C++ preprocessor library",
|
|
"http://www.boost.org/",
|
|
"",
|
|
"Copyright (c) 2001-2012 Hartmut Kaiser, 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)",
|
|
0
|
|
};
|
|
|
|
for (int i = 0; 0 != copyright[i]; ++i)
|
|
std::cout << copyright[i] << std::endl;
|
|
|
|
return 0; // exit app
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Read the given file into a string
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
testwave_app::read_file(std::string const& filename, std::string& instr)
|
|
{
|
|
// open the given file and report error, if appropriate
|
|
std::ifstream instream(filename.c_str());
|
|
if (!instream.is_open()) {
|
|
std::cerr << "testwave: could not open input file: "
|
|
<< filename << std::endl;
|
|
return false;
|
|
}
|
|
else if (9 == debuglevel) {
|
|
std::cerr << "read_file: succeeded to open input file: "
|
|
<< filename << std::endl;
|
|
}
|
|
instream.unsetf(std::ios::skipws);
|
|
|
|
// read the input file into a string
|
|
|
|
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
|
|
// this is known to be very slow for large files on some systems
|
|
std::copy (std::istream_iterator<char>(instream),
|
|
std::istream_iterator<char>(),
|
|
std::inserter(instr, instr.end()));
|
|
#else
|
|
instr = std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
|
|
std::istreambuf_iterator<char>());
|
|
#endif
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "read_file: succeeded to read input file: "
|
|
<< filename << std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
namespace {
|
|
|
|
std::string const& trim_whitespace(std::string& value)
|
|
{
|
|
std::string::size_type first = value.find_first_not_of(" \t");
|
|
if (std::string::npos == first)
|
|
value.clear();
|
|
else {
|
|
std::string::size_type last = value.find_last_not_of(" \t");
|
|
BOOST_ASSERT(std::string::npos != last);
|
|
value = value.substr(first, last-first+1);
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Extract special information from comments marked with the given letter
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
testwave_app::extract_special_information(std::string const& filename,
|
|
std::string const& instr, char flag, std::string& content)
|
|
{
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_special_information: extracting special information ('"
|
|
<< flag << "') from input file: " << filename << std::endl;
|
|
}
|
|
|
|
// tokenize the input data into C++ tokens using the C++ lexer
|
|
typedef boost::wave::cpplexer::lex_token<> token_type;
|
|
typedef boost::wave::cpplexer::lex_iterator<token_type> lexer_type;
|
|
typedef token_type::position_type position_type;
|
|
|
|
boost::wave::language_support const lang_opts =
|
|
(boost::wave::language_support)(
|
|
boost::wave::support_option_variadics |
|
|
boost::wave::support_option_long_long |
|
|
boost::wave::support_option_no_character_validation |
|
|
boost::wave::support_option_convert_trigraphs |
|
|
boost::wave::support_option_insert_whitespace);
|
|
|
|
position_type pos(filename.c_str());
|
|
lexer_type it = lexer_type(instr.begin(), instr.end(), pos, lang_opts);
|
|
lexer_type end = lexer_type();
|
|
|
|
try {
|
|
// look for C or C++ comments starting with the special character
|
|
for (/**/; it != end; ++it) {
|
|
using namespace boost::wave;
|
|
token_id id = token_id(*it);
|
|
if (T_CCOMMENT == id) {
|
|
std::string value = (*it).get_value().c_str();
|
|
if (flag == value[2]) {
|
|
if (value.size() > 3 && '(' == value[3]) {
|
|
std::size_t p = value.find_first_of(")");
|
|
if (std::string::npos == p) {
|
|
std::cerr
|
|
<< "testwave: missing closing parenthesis in '"
|
|
<< flag << "()' directive" << std::endl;
|
|
return false;
|
|
}
|
|
std::string source = value.substr(4, p-4);
|
|
std::string result, error, hooks;
|
|
bool pp_result;
|
|
std::tie(pp_result, std::ignore) = preprocess_file(filename, source,
|
|
result, error, hooks, "", true);
|
|
if (!pp_result) {
|
|
std::cerr
|
|
<< "testwave: preprocessing error in '" << flag
|
|
<< "()' directive: " << error << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// include this text into the extracted information
|
|
// only if the result is not zero
|
|
using namespace std; // some system have atoi in namespace std
|
|
if (0 != atoi(result.c_str())) {
|
|
std::string thiscontent(value.substr(p+1));
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_special_information: extracted: "
|
|
<< thiscontent << std::endl;
|
|
}
|
|
trim_whitespace(thiscontent);
|
|
content += thiscontent;
|
|
}
|
|
}
|
|
else {
|
|
std::string thiscontent(value.substr(3, value.size()-5));
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_special_information: extracted: "
|
|
<< thiscontent << std::endl;
|
|
}
|
|
trim_whitespace(thiscontent);
|
|
content += thiscontent;
|
|
}
|
|
}
|
|
}
|
|
else if (T_CPPCOMMENT == id) {
|
|
std::string value = (*it).get_value().c_str();
|
|
if (flag == value[2]) {
|
|
if (value.size() > 3 && '(' == value[3]) {
|
|
std::size_t p = value.find_first_of(")");
|
|
if (std::string::npos == p) {
|
|
std::cerr
|
|
<< "testwave: missing closing parenthesis in '"
|
|
<< flag << "()' directive" << std::endl;
|
|
return false;
|
|
}
|
|
std::string source = value.substr(4, p-4);
|
|
std::string result, error, hooks;
|
|
bool pp_result;
|
|
bool suppressed;
|
|
std::tie(pp_result, suppressed) = preprocess_file(filename, source,
|
|
result, error, hooks, "", true);
|
|
if (!pp_result) {
|
|
std::cerr
|
|
<< "testwave: preprocessing error in '" << flag
|
|
<< "()' directive: " << error << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// include this text into the extracted information
|
|
// only if the result is not zero
|
|
using namespace std; // some system have atoi in namespace std
|
|
if (0 != atoi(result.c_str())) {
|
|
std::string thiscontent(value.substr((' ' == value[p+1]) ? p+2 : p+1));
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_special_information: extracted: "
|
|
<< thiscontent << std::endl;
|
|
}
|
|
trim_whitespace(thiscontent);
|
|
content += thiscontent;
|
|
}
|
|
}
|
|
else {
|
|
std::string thiscontent(value.substr((' ' == value[3]) ? 4 : 3));
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_special_information: extracted: "
|
|
<< thiscontent;
|
|
}
|
|
trim_whitespace(content);
|
|
content += thiscontent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (boost::wave::cpplexer::lexing_exception const &e) {
|
|
// some lexing error
|
|
std::cerr
|
|
<< e.file_name() << "(" << e.line_no() << "): "
|
|
<< e.description() << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_special_information: succeeded extracting special information ('"
|
|
<< flag << "')" << std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Extract the expected output from the given input data
|
|
//
|
|
// The expected output has to be provided inside of special comments which
|
|
// start with a capital 'R'. All such comments are concatenated and returned
|
|
// through the parameter 'expected'.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
inline bool
|
|
testwave_app::extract_expected_output(std::string const& filename,
|
|
std::string const& instr, std::string& expected, std::string& expectedhooks)
|
|
{
|
|
return extract_special_information(filename, instr, 'R', expected) &&
|
|
extract_special_information(filename, instr, 'H', expectedhooks);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Extracts the required preprocessing options from the given input data and
|
|
// initialises the given Wave context object accordingly.
|
|
// We allow the same (applicable) options to be used as are valid for the wave
|
|
// driver executable.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
template <typename Context>
|
|
bool
|
|
testwave_app::extract_options(std::string const& filename,
|
|
std::string const& instr, Context& ctx, bool single_line,
|
|
po::variables_map& vm)
|
|
{
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_options: extracting options" << std::endl;
|
|
}
|
|
|
|
// extract the required information from the comments flagged by a
|
|
// capital 'O'
|
|
std::string options;
|
|
if (!extract_special_information(filename, instr, 'O', options))
|
|
return false;
|
|
|
|
try {
|
|
// parse the configuration information into a program_options_description
|
|
// object
|
|
cmd_line_utils::read_config_options(debuglevel, options, desc_options, vm);
|
|
initialise_options(ctx, vm, single_line);
|
|
}
|
|
catch (std::exception const &e) {
|
|
std::cerr << filename << ": exception caught: " << e.what()
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "extract_options: succeeded extracting options"
|
|
<< std::endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename Context>
|
|
bool
|
|
testwave_app::initialise_options(Context& ctx, po::variables_map const& vm,
|
|
bool single_line)
|
|
{
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: initializing options" << std::endl;
|
|
}
|
|
|
|
if (vm.count("skipped_token_hooks")) {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: skipped_token_hooks" << std::endl;
|
|
}
|
|
ctx.get_hooks().set_skipped_token_hooks(true);
|
|
}
|
|
|
|
// initialize the given context from the parsed options
|
|
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
|
|
// enable C99 mode, if appropriate (implies variadics)
|
|
if (vm.count("c99")) {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: c99" << std::endl;
|
|
}
|
|
ctx.set_language(
|
|
boost::wave::language_support(
|
|
boost::wave::support_c99
|
|
| boost::wave::support_option_emit_line_directives
|
|
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
|
|
| boost::wave::support_option_include_guard_detection
|
|
#endif
|
|
#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
|
|
| boost::wave::support_option_emit_pragma_directives
|
|
#endif
|
|
| boost::wave::support_option_insert_whitespace
|
|
));
|
|
}
|
|
else if (vm.count("variadics")) {
|
|
// enable variadics and placemarkers, if appropriate
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: variadics" << std::endl;
|
|
}
|
|
ctx.set_language(boost::wave::enable_variadics(ctx.get_language()));
|
|
}
|
|
#endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
|
|
|
|
#if BOOST_WAVE_SUPPORT_CPP0X
|
|
if (vm.count("c++11")) {
|
|
ctx.set_language(
|
|
boost::wave::language_support(
|
|
boost::wave::support_cpp0x
|
|
| boost::wave::support_option_convert_trigraphs
|
|
| boost::wave::support_option_long_long
|
|
| boost::wave::support_option_emit_line_directives
|
|
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
|
|
| boost::wave::support_option_include_guard_detection
|
|
#endif
|
|
#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
|
|
| boost::wave::support_option_emit_pragma_directives
|
|
#endif
|
|
| boost::wave::support_option_insert_whitespace
|
|
));
|
|
} else {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: c++11" << std::endl;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if BOOST_WAVE_SUPPORT_CPP1Z
|
|
if (vm.count("c++17")) {
|
|
ctx.set_language(
|
|
boost::wave::language_support(
|
|
boost::wave::support_cpp1z
|
|
#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
|
|
| boost::wave::support_option_has_include
|
|
#endif
|
|
| boost::wave::support_option_convert_trigraphs
|
|
| boost::wave::support_option_long_long
|
|
| boost::wave::support_option_emit_line_directives
|
|
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
|
|
| boost::wave::support_option_include_guard_detection
|
|
#endif
|
|
#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
|
|
| boost::wave::support_option_emit_pragma_directives
|
|
#endif
|
|
| boost::wave::support_option_insert_whitespace
|
|
));
|
|
} else {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: c++17" << std::endl;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if BOOST_WAVE_SUPPORT_CPP2A
|
|
if (vm.count("c++20")) {
|
|
ctx.set_language(
|
|
boost::wave::language_support(
|
|
boost::wave::support_cpp2a
|
|
#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
|
|
| boost::wave::support_option_has_include
|
|
#endif
|
|
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
|
|
| boost::wave::support_option_va_opt
|
|
#endif
|
|
| boost::wave::support_option_convert_trigraphs
|
|
| boost::wave::support_option_long_long
|
|
| boost::wave::support_option_emit_line_directives
|
|
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
|
|
| boost::wave::support_option_include_guard_detection
|
|
#endif
|
|
#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
|
|
| boost::wave::support_option_emit_pragma_directives
|
|
#endif
|
|
| boost::wave::support_option_insert_whitespace
|
|
));
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: c++20" << std::endl;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// enable long_long mode, if appropriate
|
|
if (vm.count("long_long")) {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: long_long" << std::endl;
|
|
}
|
|
ctx.set_language(boost::wave::enable_long_long(ctx.get_language()));
|
|
}
|
|
|
|
// enable preserving comments mode, if appropriate
|
|
if (vm.count("preserve")) {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: preserve" << std::endl;
|
|
}
|
|
ctx.set_language(
|
|
boost::wave::enable_preserve_comments(ctx.get_language()));
|
|
}
|
|
|
|
// disable automatic include guard detection
|
|
if (vm.count("noguard")) {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: guard" << std::endl;
|
|
}
|
|
ctx.set_language(
|
|
boost::wave::enable_include_guard_detection(ctx.get_language(), false));
|
|
}
|
|
|
|
// enable trigraph conversion
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: convert_trigraphs" << std::endl;
|
|
}
|
|
ctx.set_language(boost::wave::enable_convert_trigraphs(ctx.get_language()));
|
|
|
|
// enable single_line mode
|
|
if (single_line) {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: single_line" << std::endl;
|
|
}
|
|
ctx.set_language(boost::wave::enable_single_line(ctx.get_language()));
|
|
ctx.set_language(boost::wave::enable_emit_line_directives(ctx.get_language(), false));
|
|
}
|
|
|
|
// add include directories to the system include search paths
|
|
if (vm.count("sysinclude")) {
|
|
std::vector<std::string> const& syspaths =
|
|
variables_map_as(vm["sysinclude"], (std::vector<std::string> *)NULL);
|
|
|
|
std::vector<std::string>::const_iterator end = syspaths.end();
|
|
for (std::vector<std::string>::const_iterator cit = syspaths.begin();
|
|
cit != end; ++cit)
|
|
{
|
|
std::string full(*cit);
|
|
got_expected_result(ctx.get_current_filename(),"",full);
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -S" << *cit
|
|
<< std::endl;
|
|
}
|
|
ctx.add_sysinclude_path(full.c_str());
|
|
}
|
|
}
|
|
|
|
// add include directories to the user include search paths
|
|
if (vm.count("include")) {
|
|
cmd_line_utils::include_paths const &ip =
|
|
variables_map_as(vm["include"], (cmd_line_utils::include_paths*)NULL);
|
|
std::vector<std::string>::const_iterator end = ip.paths.end();
|
|
|
|
for (std::vector<std::string>::const_iterator cit = ip.paths.begin();
|
|
cit != end; ++cit)
|
|
{
|
|
std::string full(*cit);
|
|
got_expected_result(ctx.get_current_filename(),"",full);
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -I" << *cit
|
|
<< std::endl;
|
|
}
|
|
ctx.add_include_path(full.c_str());
|
|
}
|
|
|
|
// if on the command line was given -I- , this has to be propagated
|
|
if (ip.seen_separator) {
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -I-" << std::endl;
|
|
}
|
|
ctx.set_sysinclude_delimiter();
|
|
}
|
|
|
|
// add system include directories to the include path
|
|
std::vector<std::string>::const_iterator sysend = ip.syspaths.end();
|
|
for (std::vector<std::string>::const_iterator syscit = ip.syspaths.begin();
|
|
syscit != sysend; ++syscit)
|
|
{
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -S" << *syscit
|
|
<< std::endl;
|
|
}
|
|
ctx.add_sysinclude_path((*syscit).c_str());
|
|
}
|
|
}
|
|
|
|
// add additional defined macros
|
|
if (vm.count("define")) {
|
|
std::vector<std::string> const ¯os =
|
|
variables_map_as(vm["define"], (std::vector<std::string>*)NULL);
|
|
std::vector<std::string>::const_iterator end = macros.end();
|
|
for (std::vector<std::string>::const_iterator cit = macros.begin();
|
|
cit != end; ++cit)
|
|
{
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -D" << *cit
|
|
<< std::endl;
|
|
}
|
|
ctx.add_macro_definition(*cit, true);
|
|
}
|
|
}
|
|
|
|
// add additional predefined macros
|
|
if (vm.count("predefine")) {
|
|
std::vector<std::string> const &predefmacros =
|
|
variables_map_as(vm["predefine"], (std::vector<std::string>*)NULL);
|
|
std::vector<std::string>::const_iterator end = predefmacros.end();
|
|
for (std::vector<std::string>::const_iterator cit = predefmacros.begin();
|
|
cit != end; ++cit)
|
|
{
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -P" << *cit
|
|
<< std::endl;
|
|
}
|
|
ctx.add_macro_definition(*cit, true);
|
|
}
|
|
}
|
|
|
|
// undefine specified macros
|
|
if (vm.count("undefine")) {
|
|
std::vector<std::string> const &undefmacros =
|
|
variables_map_as(vm["undefine"], (std::vector<std::string>*)NULL);
|
|
std::vector<std::string>::const_iterator end = undefmacros.end();
|
|
for (std::vector<std::string>::const_iterator cit = undefmacros.begin();
|
|
cit != end; ++cit)
|
|
{
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -U" << *cit
|
|
<< std::endl;
|
|
}
|
|
ctx.remove_macro_definition(*cit);
|
|
}
|
|
}
|
|
|
|
// maximal include nesting depth
|
|
if (vm.count("nesting")) {
|
|
int max_depth = variables_map_as(vm["nesting"], (int*)NULL);
|
|
if (max_depth < 1 || max_depth > 100000) {
|
|
std::cerr << "testwave: bogus maximal include nesting depth: "
|
|
<< max_depth << std::endl;
|
|
return false;
|
|
}
|
|
else if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: option: -n" << max_depth
|
|
<< std::endl;
|
|
}
|
|
ctx.set_max_include_nesting_depth(max_depth);
|
|
}
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "initialise_options: succeeded to initialize options"
|
|
<< std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// construct a SIZEOF macro definition string and predefine this macro
|
|
template <typename Context>
|
|
inline bool
|
|
testwave_app::add_sizeof_definition(Context& ctx, char const *name, int value)
|
|
{
|
|
BOOST_WAVETEST_OSSTREAM strm;
|
|
strm << "__TESTWAVE_SIZEOF_" << name << "__=" << value;
|
|
|
|
std::string macro(BOOST_WAVETEST_GETSTRING(strm));
|
|
if (!ctx.add_macro_definition(macro, true)) {
|
|
std::cerr << "testwave: failed to predefine macro: " << macro
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
else if (9 == debuglevel) {
|
|
std::cerr << "add_sizeof_definition: predefined macro: " << macro
|
|
<< std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// construct a MIN macro definition string and predefine this macro
|
|
template <typename T, typename Context>
|
|
inline bool
|
|
testwave_app::add_min_definition(Context& ctx, char const *name)
|
|
{
|
|
BOOST_WAVETEST_OSSTREAM strm;
|
|
if (!std::numeric_limits<T>::is_signed) {
|
|
strm << "__TESTWAVE_" << name << "_MIN__="
|
|
<< "0x" << std::hex
|
|
<< (std::numeric_limits<T>::min)() << "U";
|
|
}
|
|
else {
|
|
strm << "__TESTWAVE_" << name << "_MIN__=( "
|
|
<< (std::numeric_limits<T>::min)()+1 << "-1)";
|
|
}
|
|
|
|
std::string macro(BOOST_WAVETEST_GETSTRING(strm));
|
|
if (!ctx.add_macro_definition(macro, true)) {
|
|
std::cerr << "testwave: failed to predefine macro: " << macro
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
else if (9 == debuglevel) {
|
|
std::cerr << "add_min_definition: predefined macro: " << macro
|
|
<< std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// construct a MAX macro definition string and predefine this macro
|
|
template <typename T, typename Context>
|
|
inline bool
|
|
testwave_app::add_max_definition(Context& ctx, char const *name)
|
|
{
|
|
BOOST_WAVETEST_OSSTREAM strm;
|
|
if (!std::numeric_limits<T>::is_signed) {
|
|
strm << "__TESTWAVE_" << name << "_MAX__="
|
|
<< "0x" << std::hex
|
|
<< (std::numeric_limits<T>::max)() << "U";
|
|
}
|
|
else {
|
|
strm << "__TESTWAVE_" << name << "_MAX__="
|
|
<< (std::numeric_limits<T>::max)();
|
|
}
|
|
|
|
std::string macro(BOOST_WAVETEST_GETSTRING(strm));
|
|
if (!ctx.add_macro_definition(macro, true)) {
|
|
std::cerr << "testwave: failed to predefine macro: " << macro
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
else if (9 == debuglevel) {
|
|
std::cerr << "add_max_definition: predefined macro: " << macro
|
|
<< std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Predefine __TESTWAVE_HAS_STRICT_LEXER__
|
|
template <typename Context>
|
|
inline bool
|
|
testwave_app::add_strict_lexer_definition(Context& ctx)
|
|
{
|
|
std::string macro("__TESTWAVE_HAS_STRICT_LEXER__=1");
|
|
if (!ctx.add_macro_definition(macro, true)) {
|
|
std::cerr << "testwave: failed to predefine macro: " << macro
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
else if (9 == debuglevel) {
|
|
std::cerr << "add_strict_lexer_definition: predefined macro: " << macro
|
|
<< std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
|
|
// Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
|
|
template <typename Context>
|
|
inline bool
|
|
testwave_app::add_support_ms_extensions_definition(Context& ctx)
|
|
{
|
|
std::string macro("__TESTWAVE_SUPPORT_MS_EXTENSIONS__=1");
|
|
if (!ctx.add_macro_definition(macro, true)) {
|
|
std::cerr << "testwave: failed to predefine macro: " << macro
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
else if (9 == debuglevel) {
|
|
std::cerr << "add_support_ms_extensions_definition: predefined macro: "
|
|
<< macro
|
|
<< std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Add special predefined macros to the context object.
|
|
//
|
|
// This adds a lot of macros to the test environment, which allows to adjust
|
|
// the test cases for different platforms.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
template <typename Context>
|
|
bool
|
|
testwave_app::add_predefined_macros(Context& ctx)
|
|
{
|
|
// add the __TESTWAVE_SIZEOF_<type>__ macros
|
|
if (!add_sizeof_definition(ctx, "CHAR", sizeof(char)) ||
|
|
!add_sizeof_definition(ctx, "SHORT", sizeof(short)) ||
|
|
!add_sizeof_definition(ctx, "INT", sizeof(int)) ||
|
|
#if defined(BOOST_HAS_LONG_LONG)
|
|
!add_sizeof_definition(ctx, "LONGLONG", sizeof(boost::long_long_type)) ||
|
|
#endif
|
|
!add_sizeof_definition(ctx, "LONG", sizeof(long)))
|
|
{
|
|
std::cerr << "testwave: failed to add a predefined macro (SIZEOF)."
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
// add the __TESTWAVE_<type>_MIN__ macros
|
|
if (/*!add_min_definition<char>(ctx, "CHAR") ||*/
|
|
/*!add_min_definition<unsigned char>(ctx, "UCHAR") ||*/
|
|
!add_min_definition<short>(ctx, "SHORT") ||
|
|
!add_min_definition<unsigned short>(ctx, "USHORT") ||
|
|
!add_min_definition<int>(ctx, "INT") ||
|
|
!add_min_definition<unsigned int>(ctx, "UINT") ||
|
|
#if defined(BOOST_HAS_LONG_LONG)
|
|
!add_min_definition<boost::long_long_type>(ctx, "LONGLONG") ||
|
|
!add_min_definition<boost::ulong_long_type>(ctx, "ULONGLONG") ||
|
|
#endif
|
|
!add_min_definition<long>(ctx, "LONG") ||
|
|
!add_min_definition<unsigned long>(ctx, "ULONG"))
|
|
{
|
|
std::cerr << "testwave: failed to add a predefined macro (MIN)."
|
|
<< std::endl;
|
|
}
|
|
|
|
// add the __TESTWAVE_<type>_MAX__ macros
|
|
if (/*!add_max_definition<char>(ctx, "CHAR") ||*/
|
|
/*!add_max_definition<unsigned char>(ctx, "UCHAR") ||*/
|
|
!add_max_definition<short>(ctx, "SHORT") ||
|
|
!add_max_definition<unsigned short>(ctx, "USHORT") ||
|
|
!add_max_definition<int>(ctx, "INT") ||
|
|
!add_max_definition<unsigned int>(ctx, "UINT") ||
|
|
#if defined(BOOST_HAS_LONG_LONG)
|
|
!add_max_definition<boost::long_long_type>(ctx, "LONGLONG") ||
|
|
!add_max_definition<boost::ulong_long_type>(ctx, "ULONGLONG") ||
|
|
#endif
|
|
!add_max_definition<long>(ctx, "LONG") ||
|
|
!add_max_definition<unsigned long>(ctx, "ULONG"))
|
|
{
|
|
std::cerr << "testwave: failed to add a predefined macro (MAX)."
|
|
<< std::endl;
|
|
}
|
|
|
|
#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
|
|
// Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
|
|
if (!add_support_ms_extensions_definition(ctx))
|
|
{
|
|
std::cerr << "testwave: failed to add a predefined macro "
|
|
"(__TESTWAVE_SUPPORT_MS_EXTENSIONS__)."
|
|
<< std::endl;
|
|
}
|
|
#endif
|
|
|
|
#if BOOST_WAVE_USE_STRICT_LEXER != 0
|
|
return add_strict_lexer_definition(ctx);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Preprocess the given input data and return the generated output through
|
|
// the parameter 'result'.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
std::tuple<bool, bool> // pass/fail + suppressed (or not) by absent macro
|
|
testwave_app::preprocess_file(std::string filename, std::string const& instr,
|
|
std::string& result, std::string& error, std::string& hooks,
|
|
std::string const& expected_cfg_macro, bool single_line)
|
|
{
|
|
// create the wave::context object and initialize it from the file to
|
|
// preprocess (may contain options inside of special comments)
|
|
typedef boost::wave::cpplexer::lex_token<> token_type;
|
|
typedef boost::wave::cpplexer::lex_iterator<token_type> lexer_type;
|
|
typedef boost::wave::context<
|
|
std::string::const_iterator, lexer_type,
|
|
boost::wave::iteration_context_policies::load_file_to_string,
|
|
collect_hooks_information<token_type> >
|
|
context_type;
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "preprocess_file: preprocessing input file: " << filename
|
|
<< std::endl;
|
|
}
|
|
|
|
try {
|
|
// create preprocessing context
|
|
context_type ctx(instr.begin(), instr.end(), filename.c_str(),
|
|
collect_hooks_information<token_type>(hooks));
|
|
|
|
// initialize the context from the options given on the command line
|
|
if (!initialise_options(ctx, global_vm, single_line))
|
|
return std::make_tuple(false, false);
|
|
|
|
// extract the options from the input data and initialize the context
|
|
boost::program_options::variables_map local_vm;
|
|
if (!extract_options(filename, instr, ctx, single_line, local_vm))
|
|
return std::make_tuple(false, false);
|
|
|
|
// add special predefined macros
|
|
if (!add_predefined_macros(ctx))
|
|
return std::make_tuple(false, false);
|
|
|
|
if (!expected_cfg_macro.empty() &&
|
|
!ctx.is_defined_macro(expected_cfg_macro))
|
|
{
|
|
// skip this test as it is for a disabled configuration
|
|
return std::make_tuple(true, true);
|
|
}
|
|
|
|
// preprocess the input, loop over all generated tokens collecting the
|
|
// generated text
|
|
context_type::iterator_type it = ctx.begin();
|
|
context_type::iterator_type end = ctx.end();
|
|
|
|
if (local_vm.count("forceinclude")) {
|
|
// add the filenames to force as include files in _reverse_ order
|
|
// the second parameter 'is_last' of the force_include function should
|
|
// be set to true for the last (first given) file.
|
|
std::vector<std::string> const& force =
|
|
local_vm["forceinclude"].as<std::vector<std::string> >();
|
|
std::vector<std::string>::const_reverse_iterator rend = force.rend();
|
|
for (std::vector<std::string>::const_reverse_iterator cit = force.rbegin();
|
|
cit != rend; /**/)
|
|
{
|
|
std::string forceinclude(*cit);
|
|
if (9 == debuglevel) {
|
|
std::cerr << "preprocess_file: option: forceinclude ("
|
|
<< forceinclude << ")" << std::endl;
|
|
}
|
|
it.force_include(forceinclude.c_str(), ++cit == rend);
|
|
}
|
|
}
|
|
|
|
// perform actual preprocessing
|
|
for (/**/; it != end; ++it)
|
|
{
|
|
using namespace boost::wave;
|
|
|
|
if (T_PP_LINE == token_id(*it)) {
|
|
// special handling of the whole #line directive is required to
|
|
// allow correct file name matching
|
|
if (!handle_line_directive(it, end, result))
|
|
return std::make_tuple(false, false); // unexpected eof
|
|
}
|
|
else {
|
|
// add the value of the current token
|
|
result = result + (*it).get_value().c_str();
|
|
}
|
|
}
|
|
error.clear();
|
|
}
|
|
catch (boost::wave::cpplexer::lexing_exception const& e) {
|
|
// some lexer error
|
|
BOOST_WAVETEST_OSSTREAM strm;
|
|
std::string filename = e.file_name();
|
|
strm
|
|
<< handle_filepath(filename) << "(" << e.line_no() << "): "
|
|
<< e.description() << std::endl;
|
|
|
|
error = BOOST_WAVETEST_GETSTRING(strm);
|
|
return std::make_tuple(false, false);
|
|
}
|
|
catch (boost::wave::cpp_exception const& e) {
|
|
// some preprocessing error
|
|
BOOST_WAVETEST_OSSTREAM strm;
|
|
std::string filename = e.file_name();
|
|
strm
|
|
<< handle_filepath(filename) << "(" << e.line_no() << "): "
|
|
<< e.description() << std::endl;
|
|
|
|
error = BOOST_WAVETEST_GETSTRING(strm);
|
|
return std::make_tuple(false, false);
|
|
}
|
|
|
|
if (9 == debuglevel) {
|
|
std::cerr << "preprocess_file: succeeded to preprocess input file: "
|
|
<< filename << std::endl;
|
|
}
|
|
|
|
return std::make_tuple(true, false);
|
|
}
|