1 /*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
5 Copyright (c) 2001-2013 Hartmut Kaiser. Distributed under the Boost
6 Software License, Version 1.0. (See accompanying file
7 LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 =============================================================================*/
10 // disable stupid compiler warnings
11 #include <boost/config/warning_disable.hpp>
20 #include <boost/config.hpp>
21 #include <boost/assert.hpp>
22 #include <boost/throw_exception.hpp>
23 #include <boost/filesystem/path.hpp>
24 #include <boost/filesystem/operations.hpp>
25 #include <boost/detail/workaround.hpp>
29 // always use new hooks
30 #define BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS 0
32 #include <boost/wave.hpp>
34 // include the lexer related stuff
35 #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type
36 #include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer type
38 ///////////////////////////////////////////////////////////////////////////////
39 // Include lexer specifics, import lexer names
40 #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
41 #include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
44 ///////////////////////////////////////////////////////////////////////////////
45 // Include the grammar definitions, if these shouldn't be compiled separately
46 // (ATTENTION: _very_ large compilation times!)
47 #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
48 #include <boost/wave/grammars/cpp_intlit_grammar.hpp>
49 #include <boost/wave/grammars/cpp_chlit_grammar.hpp>
50 #include <boost/wave/grammars/cpp_grammar.hpp>
51 #include <boost/wave/grammars/cpp_expression_grammar.hpp>
52 #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
53 #include <boost/wave/grammars/cpp_defined_grammar.hpp>
56 // test application related headers
57 #include "cmd_line_utils.hpp"
58 #include "testwave_app.hpp"
59 #include "collect_hooks_information.hpp"
61 # ifdef BOOST_NO_STDC_NAMESPACE
64 using ::asctime
; using ::gmtime
; using ::localtime
;
65 using ::difftime
; using ::time
; using ::tm
; using ::mktime
; using ::system
;
69 namespace po
= boost::program_options
;
70 namespace fs
= boost::filesystem
;
72 ///////////////////////////////////////////////////////////////////////////////
73 // testwave version definitions
74 #define TESTWAVE_VERSION_MAJOR 0
75 #define TESTWAVE_VERSION_MINOR 6
76 #define TESTWAVE_VERSION_SUBMINOR 0
80 ///////////////////////////////////////////////////////////////////////////
81 template <typename Iterator
>
83 handle_next_token(Iterator
&it
, Iterator
const& end
,
86 typedef typename
Iterator::value_type token_type
;
88 token_type tok
= *it
++;
89 result
= result
+ tok
.get_value().c_str();
90 return (it
== end
) ? false : true;
93 ///////////////////////////////////////////////////////////////////////////
94 template <typename String
>
95 String
const& handle_quoted_filepath(String
&name
)
97 using boost::wave::util::impl::unescape_lit
;
99 String unesc_name
= unescape_lit(name
.substr(1, name
.size()-2));
100 fs::path
p (boost::wave::util::create_path(unesc_name
.c_str()));
102 name
= String("\"") + boost::wave::util::leaf(p
).c_str() + String("\"");
106 ///////////////////////////////////////////////////////////////////////////
107 template <typename Iterator
>
108 bool handle_line_directive(Iterator
&it
, Iterator
const& end
,
111 typedef typename
Iterator::value_type token_type
;
112 typedef typename
token_type::string_type string_type
;
114 if (!handle_next_token(it
, end
, result
) || // #line
115 !handle_next_token(it
, end
, result
) || // whitespace
116 !handle_next_token(it
, end
, result
) || // number
117 !handle_next_token(it
, end
, result
)) // whitespace
122 using boost::wave::util::impl::unescape_lit
;
124 token_type filename
= *it
;
125 string_type name
= filename
.get_value();
127 handle_quoted_filepath(name
);
128 result
= result
+ name
.c_str();
132 template <typename T
>
134 variables_map_as(po::variable_value
const& v
, T
*)
136 #if (__GNUC__ == 3 && (__GNUC_MINOR__ == 2 || __GNUC_MINOR__ == 3)) || \
137 BOOST_WORKAROUND(__MWERKS__, < 0x3200)
138 // gcc 3.2.x and 3.3.x choke on vm[...].as<...>()
139 // CW 8.3 has problems with the v.as<T>() below
140 T
const* r
= boost::any_cast
<T
>(&v
.value());
142 boost::throw_exception(boost::bad_any_cast());
151 ///////////////////////////////////////////////////////////////////////////
153 // This function compares the real result and the expected one but first
154 // replaces all occurrences in the expected result of
155 // $E: to the result of preprocessing the given expression
156 // $F: to the passed full filepath
157 // $P: to the full path
158 // $B: to the full path (same as $P, but using forward slash '/' on Windows)
159 // $V: to the current Boost version number
161 ///////////////////////////////////////////////////////////////////////////
163 testwave_app::got_expected_result(std::string
const& filename
,
164 std::string
const& result
, std::string
& expected
)
166 using boost::wave::util::impl::escape_lit
;
168 std::string full_result
;
169 std::string::size_type pos
= 0;
170 std::string::size_type pos1
= expected
.find_first_of("$");
172 if (pos1
!= std::string::npos
) {
174 switch(expected
[pos1
+1]) {
175 case 'E': // preprocess the given token sequence
177 if ('(' == expected
[pos1
+2]) {
178 std::size_t p
= expected
.find_first_of(")", pos1
+1);
179 if (std::string::npos
== p
) {
181 << "testwave: unmatched parenthesis in $E"
182 " directive" << std::endl
;
185 std::string source
= expected
.substr(pos1
+3, p
-pos1
-3);
186 std::string result
, error
, hooks
;
187 bool pp_result
= preprocess_file(filename
, source
,
188 result
, error
, hooks
, "", true);
191 << "testwave: preprocessing error in $E directive: "
192 << error
<< std::endl
;
195 full_result
= full_result
+
196 expected
.substr(pos
, pos1
-pos
) + result
;
197 pos1
= expected
.find_first_of ("$",
198 pos
= pos1
+ 4 + source
.size());
203 case 'F': // insert base file name
204 full_result
= full_result
+
205 expected
.substr(pos
, pos1
-pos
) + escape_lit(filename
);
206 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
209 case 'P': // insert full path
210 case 'B': // same as 'P', but forward slashes on Windows
213 boost::wave::util::complete_path(
214 boost::wave::util::create_path(filename
),
215 boost::wave::util::current_path())
218 if ('(' == expected
[pos1
+2]) {
219 // the $P(basename) syntax is used
220 std::size_t p
= expected
.find_first_of(")", pos1
+1);
221 if (std::string::npos
== p
) {
223 << "testwave: unmatched parenthesis in $P"
224 " directive" << std::endl
;
227 std::string base
= expected
.substr(pos1
+3, p
-pos1
-3);
228 fullpath
= boost::wave::util::branch_path(fullpath
) /
229 boost::wave::util::create_path(base
);
230 full_result
+= expected
.substr(pos
, pos1
-pos
);
231 if ('P' == expected
[pos1
+1]) {
232 #if defined(BOOST_WINDOWS)
233 std::string p
= replace_slashes(
234 boost::wave::util::native_file_string(
235 boost::wave::util::normalize(fullpath
)),
239 boost::wave::util::native_file_string(
240 boost::wave::util::normalize(fullpath
)));
242 full_result
+= escape_lit(p
);
245 #if defined(BOOST_WINDOWS)
246 std::string p
= replace_slashes(
247 boost::wave::util::normalize(fullpath
).string());
250 boost::wave::util::normalize(fullpath
).string());
252 full_result
+= escape_lit(p
);
254 pos1
= expected
.find_first_of ("$",
255 pos
= pos1
+ 4 + base
.size());
258 // the $P is used on its own
259 full_result
+= expected
.substr(pos
, pos1
-pos
);
260 if ('P' == expected
[pos1
+1]) {
261 full_result
+= escape_lit(
262 boost::wave::util::native_file_string(fullpath
));
265 #if defined(BOOST_WINDOWS)
266 std::string p
= replace_slashes(fullpath
.string());
268 std::string
p (fullpath
.string());
270 full_result
+= escape_lit(fullpath
.string());
272 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
277 case 'R': // insert relative file name
278 case 'S': // same as 'R', but forward slashes on Windows
281 boost::wave::util::as_relative_to(
282 boost::wave::util::create_path(filename
),
283 boost::wave::util::current_path(),
286 if ('(' == expected
[pos1
+2]) {
287 // the $R(basename) syntax is used
288 std::size_t p
= expected
.find_first_of(")", pos1
+1);
289 if (std::string::npos
== p
) {
291 << "testwave: unmatched parenthesis in $R"
292 " directive" << std::endl
;
295 std::string base
= expected
.substr(pos1
+3, p
-pos1
-3);
296 relpath
= boost::wave::util::branch_path(relpath
) /
297 boost::wave::util::create_path(base
);
298 full_result
+= expected
.substr(pos
, pos1
-pos
);
299 if ('R' == expected
[pos1
+1]) {
300 full_result
+= escape_lit(
301 boost::wave::util::native_file_string(
302 boost::wave::util::normalize(relpath
)));
305 #if defined(BOOST_WINDOWS)
306 std::string p
= replace_slashes(
307 boost::wave::util::normalize(relpath
).string());
310 boost::wave::util::normalize(relpath
).string());
312 full_result
+= escape_lit(p
);
314 pos1
= expected
.find_first_of ("$",
315 pos
= pos1
+ 4 + base
.size());
318 // the $R is used on its own
319 full_result
+= expected
.substr(pos
, pos1
-pos
);
320 if ('R' == expected
[pos1
+1]) {
321 full_result
+= escape_lit(
322 boost::wave::util::native_file_string(relpath
));
325 #if defined(BOOST_WINDOWS)
326 std::string p
= replace_slashes(relpath
.string());
328 std::string
p (relpath
.string());
330 full_result
+= escape_lit(p
);
332 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
337 case 'V': // insert Boost version
338 full_result
= full_result
+
339 expected
.substr(pos
, pos1
-pos
) + BOOST_LIB_VERSION
;
340 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
344 full_result
= full_result
+
345 expected
.substr(pos
, pos1
-pos
);
346 pos1
= expected
.find_first_of ("$", (pos
= pos1
) + 1);
350 } while(pos1
!= std::string::npos
);
351 full_result
+= expected
.substr(pos
);
354 full_result
= expected
;
357 expected
= full_result
;
358 return full_result
== result
;
361 ///////////////////////////////////////////////////////////////////////////////
362 testwave_app::testwave_app(po::variables_map
const& vm
)
363 : debuglevel(1), desc_options("Preprocessor configuration options"),
366 desc_options
.add_options()
367 ("include,I", po::value
<cmd_line_utils::include_paths
>()->composing(),
368 "specify an additional include directory")
369 ("sysinclude,S", po::value
<std::vector
<std::string
> >()->composing(),
370 "specify an additional system include directory")
371 ("forceinclude,F", po::value
<std::vector
<std::string
> >()->composing(),
372 "force inclusion of the given file")
373 ("define,D", po::value
<std::vector
<std::string
> >()->composing(),
374 "specify a macro to define (as macro[=[value]])")
375 ("predefine,P", po::value
<std::vector
<std::string
> >()->composing(),
376 "specify a macro to predefine (as macro[=[value]])")
377 ("undefine,U", po::value
<std::vector
<std::string
> >()->composing(),
378 "specify a macro to undefine")
379 ("nesting,n", po::value
<int>(),
380 "specify a new maximal include nesting depth")
381 ("long_long", "enable long long support in C++ mode")
382 ("preserve", "preserve comments")
383 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
384 ("variadics", "enable certain C99 extensions in C++ mode")
385 ("c99", "enable C99 mode (implies --variadics)")
387 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
388 ("noguard,G", "disable include guard detection")
390 ("skipped_token_hooks", "record skipped_token hook calls")
391 #if BOOST_WAVE_SUPPORT_CPP0X != 0
392 ("c++11", "enable C++11 mode (implies --variadics and --long_long)")
394 ("warning,W", po::value
<std::vector
<std::string
> >()->composing(),
399 ///////////////////////////////////////////////////////////////////////////////
401 // Test the given file (i.e. preprocess the file and compare the result
402 // against the embedded 'R' comments, if an error occurs compare the error
403 // message against the given 'E' comments, if no error occurred, compare the
404 // generated hooks result against the given 'H' comments).
406 ///////////////////////////////////////////////////////////////////////////////
408 testwave_app::test_a_file(std::string filename
)
410 // read the input file into a string
412 if (!read_file(filename
, instr
))
413 return false; // error was reported already
415 bool test_hooks
= true;
416 if (global_vm
.count("hooks"))
417 test_hooks
= variables_map_as(global_vm
["hooks"], (bool *)NULL
);
419 std::string expected_cfg_macro
;
420 extract_special_information(filename
, instr
, 'D', expected_cfg_macro
);
422 // extract expected output, preprocess the data and compare results
423 std::string expected
, expected_hooks
;
424 if (extract_expected_output(filename
, instr
, expected
, expected_hooks
)) {
425 bool retval
= true; // assume success
426 bool printed_result
= false;
427 std::string result
, error
, hooks
;
428 bool pp_result
= preprocess_file(filename
, instr
, result
, error
, hooks
,
430 if (pp_result
|| !result
.empty()) {
431 // did we expect an error?
432 std::string expected_error
;
433 if (!extract_special_information(filename
, instr
, 'E', expected_error
))
436 if (!expected_error
.empty() &&
437 !got_expected_result(filename
, error
, expected_error
))
439 // we expected an error but got none (or a different one)
440 if (debuglevel
> 2) {
442 << filename
<< ": failed" << std::endl
443 << "result: " << std::endl
<< result
<< std::endl
;
445 if (!error
.empty()) {
446 std::cerr
<< "expected result: " << std::endl
447 << expected
<< std::endl
;
449 if (!expected_error
.empty()) {
450 std::cerr
<< "expected error: " << std::endl
451 << expected_error
<< std::endl
;
454 else if (debuglevel
> 1) {
455 std::cerr
<< filename
<< ": failed" << std::endl
;
459 else if (!got_expected_result(filename
, result
, expected
)) {
460 // no preprocessing error encountered
461 if (debuglevel
> 2) {
463 << filename
<< ": failed" << std::endl
464 << "result: " << std::endl
<< result
<< std::endl
465 << "expected: " << std::endl
<< expected
<< std::endl
;
467 else if (debuglevel
> 1) {
468 std::cerr
<< filename
<< ": failed" << std::endl
;
473 // preprocessing succeeded, check hook information, if appropriate
474 if (test_hooks
&& !expected_hooks
.empty() &&
475 !got_expected_result(filename
, hooks
, expected_hooks
))
477 if (debuglevel
> 2) {
478 std::cerr
<< filename
<< ": failed" << std::endl
479 << "hooks result: " << std::endl
<< hooks
481 std::cerr
<< "expected hooks result: " << std::endl
482 << expected_hooks
<< std::endl
;
484 else if (debuglevel
> 1) {
485 std::cerr
<< filename
<< ": failed" << std::endl
;
491 // print success message, if appropriate
493 if (debuglevel
> 5) {
495 << filename
<< ": succeeded" << std::endl
496 << "result: " << std::endl
<< result
<< std::endl
497 << "hooks result: " << std::endl
<< hooks
<< std::endl
;
499 else if (debuglevel
> 4) {
501 << filename
<< ": succeeded" << std::endl
502 << "result: " << std::endl
<< result
<< std::endl
;
504 else if (debuglevel
> 3) {
505 std::cerr
<< filename
<< ": succeeded" << std::endl
;
507 printed_result
= true;
512 // there was a preprocessing error, was it expected?
513 std::string expected_error
;
514 if (!extract_special_information(filename
, instr
, 'E', expected_error
))
517 if (!got_expected_result(filename
, error
, expected_error
)) {
518 // the error was unexpected
519 if (debuglevel
> 2) {
521 << filename
<< ": failed" << std::endl
;
523 if (!expected_error
.empty()) {
525 << "error result: " << std::endl
<< error
<< std::endl
526 << "expected error: " << std::endl
527 << expected_error
<< std::endl
;
530 std::cerr
<< "unexpected error: " << error
<< std::endl
;
533 else if (debuglevel
> 1) {
534 std::cerr
<< filename
<< ": failed" << std::endl
;
540 if (debuglevel
> 5) {
542 << filename
<< ": succeeded (caught expected error)"
543 << std::endl
<< "error result: " << std::endl
<< error
546 if (!printed_result
) {
548 << "hooks result: " << std::endl
<< hooks
552 else if (debuglevel
> 4) {
554 << filename
<< ": succeeded (caught expected error)"
555 << std::endl
<< "error result: " << std::endl
<< error
558 else if (debuglevel
> 3) {
559 // caught the expected error message
560 std::cerr
<< filename
<< ": succeeded" << std::endl
;
568 << filename
<< ": no information about expected results found"
574 ///////////////////////////////////////////////////////////////////////////////
576 // print the current version of this program
578 ///////////////////////////////////////////////////////////////////////////////
580 testwave_app::print_version()
582 // get time of last compilation of this file
583 boost::wave::util::time_conversion_helper
compilation_time(__DATE__
" " __TIME__
);
585 // calculate the number of days since Feb 12 2005
586 // (the day the testwave project was started)
589 using namespace std
; // some platforms have memset in namespace std
590 memset (&first_day
, 0, sizeof(std::tm
));
591 first_day
.tm_mon
= 1; // Feb
592 first_day
.tm_mday
= 12; // 12
593 first_day
.tm_year
= 105; // 2005
595 long seconds
= long(std::difftime(compilation_time
.get_time(),
596 std::mktime(&first_day
)));
599 << TESTWAVE_VERSION_MAJOR
<< '.'
600 << TESTWAVE_VERSION_MINOR
<< '.'
601 << TESTWAVE_VERSION_SUBMINOR
<< '.'
602 << seconds
/(3600*24) // get number of days from seconds
604 return 0; // exit app
607 ///////////////////////////////////////////////////////////////////////////////
609 // print the copyright statement
611 ///////////////////////////////////////////////////////////////////////////////
613 testwave_app::print_copyright()
615 char const *copyright
[] = {
617 "Testwave: A test driver for the Boost.Wave C++ preprocessor library",
618 "http://www.boost.org/",
620 "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost",
621 "Software License, Version 1.0. (See accompanying file",
622 "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
626 for (int i
= 0; 0 != copyright
[i
]; ++i
)
627 std::cout
<< copyright
[i
] << std::endl
;
629 return 0; // exit app
632 ///////////////////////////////////////////////////////////////////////////////
634 // Read the given file into a string
636 ///////////////////////////////////////////////////////////////////////////////
638 testwave_app::read_file(std::string
const& filename
, std::string
& instr
)
640 // open the given file and report error, if appropriate
641 std::ifstream
instream(filename
.c_str());
642 if (!instream
.is_open()) {
643 std::cerr
<< "testwave: could not open input file: "
644 << filename
<< std::endl
;
647 else if (9 == debuglevel
) {
648 std::cerr
<< "read_file: succeeded to open input file: "
649 << filename
<< std::endl
;
651 instream
.unsetf(std::ios::skipws
);
653 // read the input file into a string
655 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
656 // this is known to be very slow for large files on some systems
657 std::copy (std::istream_iterator
<char>(instream
),
658 std::istream_iterator
<char>(),
659 std::inserter(instr
, instr
.end()));
661 instr
= std::string(std::istreambuf_iterator
<char>(instream
.rdbuf()),
662 std::istreambuf_iterator
<char>());
665 if (9 == debuglevel
) {
666 std::cerr
<< "read_file: succeeded to read input file: "
667 << filename
<< std::endl
;
672 ///////////////////////////////////////////////////////////////////////////////
675 std::string
const& trim_whitespace(std::string
& value
)
677 std::string::size_type first
= value
.find_first_not_of(" \t");
678 if (std::string::npos
== first
)
681 std::string::size_type last
= value
.find_last_not_of(" \t");
682 BOOST_ASSERT(std::string::npos
!= last
);
683 value
= value
.substr(first
, last
-first
+1);
689 ///////////////////////////////////////////////////////////////////////////////
691 // Extract special information from comments marked with the given letter
693 ///////////////////////////////////////////////////////////////////////////////
695 testwave_app::extract_special_information(std::string
const& filename
,
696 std::string
const& instr
, char flag
, std::string
& content
)
698 if (9 == debuglevel
) {
699 std::cerr
<< "extract_special_information: extracting special information ('"
700 << flag
<< "') from input file: " << filename
<< std::endl
;
703 // tokenize the input data into C++ tokens using the C++ lexer
704 typedef boost::wave::cpplexer::lex_token
<> token_type
;
705 typedef boost::wave::cpplexer::lex_iterator
<token_type
> lexer_type
;
706 typedef token_type::position_type position_type
;
708 boost::wave::language_support
const lang_opts
=
709 (boost::wave::language_support
)(
710 boost::wave::support_option_variadics
|
711 boost::wave::support_option_long_long
|
712 boost::wave::support_option_no_character_validation
|
713 boost::wave::support_option_convert_trigraphs
|
714 boost::wave::support_option_insert_whitespace
);
716 position_type
pos(filename
.c_str());
717 lexer_type it
= lexer_type(instr
.begin(), instr
.end(), pos
, lang_opts
);
718 lexer_type end
= lexer_type();
721 // look for C or C++ comments starting with the special character
722 for (/**/; it
!= end
; ++it
) {
723 using namespace boost::wave
;
724 token_id id
= token_id(*it
);
725 if (T_CCOMMENT
== id
) {
726 std::string value
= (*it
).get_value().c_str();
727 if (flag
== value
[2]) {
728 if (value
.size() > 3 && '(' == value
[3]) {
729 std::size_t p
= value
.find_first_of(")");
730 if (std::string::npos
== p
) {
732 << "testwave: missing closing parenthesis in '"
733 << flag
<< "()' directive" << std::endl
;
736 std::string source
= value
.substr(4, p
-4);
737 std::string result
, error
, hooks
;
738 bool pp_result
= preprocess_file(filename
, source
,
739 result
, error
, hooks
, "", true);
742 << "testwave: preprocessing error in '" << flag
743 << "()' directive: " << error
<< std::endl
;
747 // include this text into the extracted information
748 // only if the result is not zero
749 using namespace std
; // some system have atoi in namespace std
750 if (0 != atoi(result
.c_str())) {
751 std::string
thiscontent(value
.substr(p
+1));
752 if (9 == debuglevel
) {
753 std::cerr
<< "extract_special_information: extracted: "
754 << thiscontent
<< std::endl
;
756 trim_whitespace(thiscontent
);
757 content
+= thiscontent
;
761 std::string
thiscontent(value
.substr(3, value
.size()-5));
762 if (9 == debuglevel
) {
763 std::cerr
<< "extract_special_information: extracted: "
764 << thiscontent
<< std::endl
;
766 trim_whitespace(thiscontent
);
767 content
+= thiscontent
;
771 else if (T_CPPCOMMENT
== id
) {
772 std::string value
= (*it
).get_value().c_str();
773 if (flag
== value
[2]) {
774 if (value
.size() > 3 && '(' == value
[3]) {
775 std::size_t p
= value
.find_first_of(")");
776 if (std::string::npos
== p
) {
778 << "testwave: missing closing parenthesis in '"
779 << flag
<< "()' directive" << std::endl
;
782 std::string source
= value
.substr(4, p
-4);
783 std::string result
, error
, hooks
;
784 bool pp_result
= preprocess_file(filename
, source
,
785 result
, error
, hooks
, "", true);
788 << "testwave: preprocessing error in '" << flag
789 << "()' directive: " << error
<< std::endl
;
793 // include this text into the extracted information
794 // only if the result is not zero
795 using namespace std
; // some system have atoi in namespace std
796 if (0 != atoi(result
.c_str())) {
797 std::string
thiscontent(value
.substr((' ' == value
[p
+1]) ? p
+2 : p
+1));
798 if (9 == debuglevel
) {
799 std::cerr
<< "extract_special_information: extracted: "
800 << thiscontent
<< std::endl
;
802 trim_whitespace(thiscontent
);
803 content
+= thiscontent
;
807 std::string
thiscontent(value
.substr((' ' == value
[3]) ? 4 : 3));
808 if (9 == debuglevel
) {
809 std::cerr
<< "extract_special_information: extracted: "
812 trim_whitespace(content
);
813 content
+= thiscontent
;
819 catch (boost::wave::cpplexer::lexing_exception
const &e
) {
822 << e
.file_name() << "(" << e
.line_no() << "): "
823 << e
.description() << std::endl
;
827 if (9 == debuglevel
) {
828 std::cerr
<< "extract_special_information: succeeded extracting special information ('"
829 << flag
<< "')" << std::endl
;
834 ///////////////////////////////////////////////////////////////////////////////
836 // Extract the expected output from the given input data
838 // The expected output has to be provided inside of special comments which
839 // start with a capital 'R'. All such comments are concatenated and returned
840 // through the parameter 'expected'.
842 ///////////////////////////////////////////////////////////////////////////////
844 testwave_app::extract_expected_output(std::string
const& filename
,
845 std::string
const& instr
, std::string
& expected
, std::string
& expectedhooks
)
847 return extract_special_information(filename
, instr
, 'R', expected
) &&
848 extract_special_information(filename
, instr
, 'H', expectedhooks
);
851 ///////////////////////////////////////////////////////////////////////////////
853 // Extracts the required preprocessing options from the given input data and
854 // initialises the given Wave context object accordingly.
855 // We allow the same (applicable) options to be used as are valid for the wave
856 // driver executable.
858 ///////////////////////////////////////////////////////////////////////////////
859 template <typename Context
>
861 testwave_app::extract_options(std::string
const& filename
,
862 std::string
const& instr
, Context
& ctx
, bool single_line
,
863 po::variables_map
& vm
)
865 if (9 == debuglevel
) {
866 std::cerr
<< "extract_options: extracting options" << std::endl
;
869 // extract the required information from the comments flagged by a
872 if (!extract_special_information(filename
, instr
, 'O', options
))
876 // parse the configuration information into a program_options_description
878 cmd_line_utils::read_config_options(debuglevel
, options
, desc_options
, vm
);
879 initialise_options(ctx
, vm
, single_line
);
881 catch (std::exception
const &e
) {
882 std::cerr
<< filename
<< ": exception caught: " << e
.what()
887 if (9 == debuglevel
) {
888 std::cerr
<< "extract_options: succeeded extracting options"
895 template <typename Context
>
897 testwave_app::initialise_options(Context
& ctx
, po::variables_map
const& vm
,
900 if (9 == debuglevel
) {
901 std::cerr
<< "initialise_options: initializing options" << std::endl
;
904 if (vm
.count("skipped_token_hooks")) {
905 if (9 == debuglevel
) {
906 std::cerr
<< "initialise_options: option: skipped_token_hooks" << std::endl
;
908 ctx
.get_hooks().set_skipped_token_hooks(true);
911 // initialize the given context from the parsed options
912 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
913 // enable C99 mode, if appropriate (implies variadics)
914 if (vm
.count("c99")) {
915 if (9 == debuglevel
) {
916 std::cerr
<< "initialise_options: option: c99" << std::endl
;
919 boost::wave::language_support(
920 boost::wave::support_c99
921 | boost::wave::support_option_emit_line_directives
922 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
923 | boost::wave::support_option_include_guard_detection
925 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
926 | boost::wave::support_option_emit_pragma_directives
928 | boost::wave::support_option_insert_whitespace
931 else if (vm
.count("variadics")) {
932 // enable variadics and placemarkers, if appropriate
933 if (9 == debuglevel
) {
934 std::cerr
<< "initialise_options: option: variadics" << std::endl
;
936 ctx
.set_language(boost::wave::enable_variadics(ctx
.get_language()));
938 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
940 #if BOOST_WAVE_SUPPORT_CPP0X
941 if (vm
.count("c++11")) {
942 if (9 == debuglevel
) {
943 std::cerr
<< "initialise_options: option: c++11" << std::endl
;
946 boost::wave::language_support(
947 boost::wave::support_cpp0x
948 | boost::wave::support_option_convert_trigraphs
949 | boost::wave::support_option_long_long
950 | boost::wave::support_option_emit_line_directives
951 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
952 | boost::wave::support_option_include_guard_detection
954 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
955 | boost::wave::support_option_emit_pragma_directives
957 | boost::wave::support_option_insert_whitespace
962 // enable long_long mode, if appropriate
963 if (vm
.count("long_long")) {
964 if (9 == debuglevel
) {
965 std::cerr
<< "initialise_options: option: long_long" << std::endl
;
967 ctx
.set_language(boost::wave::enable_long_long(ctx
.get_language()));
970 // enable preserving comments mode, if appropriate
971 if (vm
.count("preserve")) {
972 if (9 == debuglevel
) {
973 std::cerr
<< "initialise_options: option: preserve" << std::endl
;
976 boost::wave::enable_preserve_comments(ctx
.get_language()));
979 // disable automatic include guard detection
980 if (vm
.count("noguard")) {
981 if (9 == debuglevel
) {
982 std::cerr
<< "initialise_options: option: guard" << std::endl
;
985 boost::wave::enable_include_guard_detection(ctx
.get_language(), false));
988 // enable trigraph conversion
989 if (9 == debuglevel
) {
990 std::cerr
<< "initialise_options: option: convert_trigraphs" << std::endl
;
992 ctx
.set_language(boost::wave::enable_convert_trigraphs(ctx
.get_language()));
994 // enable single_line mode
996 if (9 == debuglevel
) {
997 std::cerr
<< "initialise_options: option: single_line" << std::endl
;
999 ctx
.set_language(boost::wave::enable_single_line(ctx
.get_language()));
1000 ctx
.set_language(boost::wave::enable_emit_line_directives(ctx
.get_language(), false));
1003 // add include directories to the system include search paths
1004 if (vm
.count("sysinclude")) {
1005 std::vector
<std::string
> const& syspaths
=
1006 variables_map_as(vm
["sysinclude"], (std::vector
<std::string
> *)NULL
);
1008 std::vector
<std::string
>::const_iterator end
= syspaths
.end();
1009 for (std::vector
<std::string
>::const_iterator cit
= syspaths
.begin();
1012 if (9 == debuglevel
) {
1013 std::cerr
<< "initialise_options: option: -S" << *cit
1016 ctx
.add_sysinclude_path((*cit
).c_str());
1020 // add include directories to the user include search paths
1021 if (vm
.count("include")) {
1022 cmd_line_utils::include_paths
const &ip
=
1023 variables_map_as(vm
["include"], (cmd_line_utils::include_paths
*)NULL
);
1024 std::vector
<std::string
>::const_iterator end
= ip
.paths
.end();
1026 for (std::vector
<std::string
>::const_iterator cit
= ip
.paths
.begin();
1029 if (9 == debuglevel
) {
1030 std::cerr
<< "initialise_options: option: -I" << *cit
1033 ctx
.add_include_path((*cit
).c_str());
1036 // if on the command line was given -I- , this has to be propagated
1037 if (ip
.seen_separator
) {
1038 if (9 == debuglevel
) {
1039 std::cerr
<< "initialise_options: option: -I-" << std::endl
;
1041 ctx
.set_sysinclude_delimiter();
1044 // add system include directories to the include path
1045 std::vector
<std::string
>::const_iterator sysend
= ip
.syspaths
.end();
1046 for (std::vector
<std::string
>::const_iterator syscit
= ip
.syspaths
.begin();
1047 syscit
!= sysend
; ++syscit
)
1049 if (9 == debuglevel
) {
1050 std::cerr
<< "initialise_options: option: -S" << *syscit
1053 ctx
.add_sysinclude_path((*syscit
).c_str());
1057 // add additional defined macros
1058 if (vm
.count("define")) {
1059 std::vector
<std::string
> const ¯os
=
1060 variables_map_as(vm
["define"], (std::vector
<std::string
>*)NULL
);
1061 std::vector
<std::string
>::const_iterator end
= macros
.end();
1062 for (std::vector
<std::string
>::const_iterator cit
= macros
.begin();
1065 if (9 == debuglevel
) {
1066 std::cerr
<< "initialise_options: option: -D" << *cit
1069 ctx
.add_macro_definition(*cit
, true);
1073 // add additional predefined macros
1074 if (vm
.count("predefine")) {
1075 std::vector
<std::string
> const &predefmacros
=
1076 variables_map_as(vm
["predefine"], (std::vector
<std::string
>*)NULL
);
1077 std::vector
<std::string
>::const_iterator end
= predefmacros
.end();
1078 for (std::vector
<std::string
>::const_iterator cit
= predefmacros
.begin();
1081 if (9 == debuglevel
) {
1082 std::cerr
<< "initialise_options: option: -P" << *cit
1085 ctx
.add_macro_definition(*cit
, true);
1089 // undefine specified macros
1090 if (vm
.count("undefine")) {
1091 std::vector
<std::string
> const &undefmacros
=
1092 variables_map_as(vm
["undefine"], (std::vector
<std::string
>*)NULL
);
1093 std::vector
<std::string
>::const_iterator end
= undefmacros
.end();
1094 for (std::vector
<std::string
>::const_iterator cit
= undefmacros
.begin();
1097 if (9 == debuglevel
) {
1098 std::cerr
<< "initialise_options: option: -U" << *cit
1101 ctx
.remove_macro_definition(*cit
);
1105 // maximal include nesting depth
1106 if (vm
.count("nesting")) {
1107 int max_depth
= variables_map_as(vm
["nesting"], (int*)NULL
);
1108 if (max_depth
< 1 || max_depth
> 100000) {
1109 std::cerr
<< "testwave: bogus maximal include nesting depth: "
1110 << max_depth
<< std::endl
;
1113 else if (9 == debuglevel
) {
1114 std::cerr
<< "initialise_options: option: -n" << max_depth
1117 ctx
.set_max_include_nesting_depth(max_depth
);
1120 if (9 == debuglevel
) {
1121 std::cerr
<< "initialise_options: succeeded to initialize options"
1127 ///////////////////////////////////////////////////////////////////////////////
1128 // construct a SIZEOF macro definition string and predefine this macro
1129 template <typename Context
>
1131 testwave_app::add_sizeof_definition(Context
& ctx
, char const *name
, int value
)
1133 BOOST_WAVETEST_OSSTREAM strm
;
1134 strm
<< "__TESTWAVE_SIZEOF_" << name
<< "__=" << value
;
1136 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1137 if (!ctx
.add_macro_definition(macro
, true)) {
1138 std::cerr
<< "testwave: failed to predefine macro: " << macro
1142 else if (9 == debuglevel
) {
1143 std::cerr
<< "add_sizeof_definition: predefined macro: " << macro
1149 // construct a MIN macro definition string and predefine this macro
1150 template <typename T
, typename Context
>
1152 testwave_app::add_min_definition(Context
& ctx
, char const *name
)
1154 BOOST_WAVETEST_OSSTREAM strm
;
1155 if (!std::numeric_limits
<T
>::is_signed
) {
1156 strm
<< "__TESTWAVE_" << name
<< "_MIN__="
1158 << (std::numeric_limits
<T
>::min
)() << "U";
1161 strm
<< "__TESTWAVE_" << name
<< "_MIN__=( "
1162 << (std::numeric_limits
<T
>::min
)()+1 << "-1)";
1165 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1166 if (!ctx
.add_macro_definition(macro
, true)) {
1167 std::cerr
<< "testwave: failed to predefine macro: " << macro
1171 else if (9 == debuglevel
) {
1172 std::cerr
<< "add_min_definition: predefined macro: " << macro
1178 // construct a MAX macro definition string and predefine this macro
1179 template <typename T
, typename Context
>
1181 testwave_app::add_max_definition(Context
& ctx
, char const *name
)
1183 BOOST_WAVETEST_OSSTREAM strm
;
1184 if (!std::numeric_limits
<T
>::is_signed
) {
1185 strm
<< "__TESTWAVE_" << name
<< "_MAX__="
1187 << (std::numeric_limits
<T
>::max
)() << "U";
1190 strm
<< "__TESTWAVE_" << name
<< "_MAX__="
1191 << (std::numeric_limits
<T
>::max
)();
1194 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1195 if (!ctx
.add_macro_definition(macro
, true)) {
1196 std::cerr
<< "testwave: failed to predefine macro: " << macro
1200 else if (9 == debuglevel
) {
1201 std::cerr
<< "add_max_definition: predefined macro: " << macro
1207 // Predefine __TESTWAVE_HAS_STRICT_LEXER__
1208 template <typename Context
>
1210 testwave_app::add_strict_lexer_definition(Context
& ctx
)
1212 std::string
macro("__TESTWAVE_HAS_STRICT_LEXER__=1");
1213 if (!ctx
.add_macro_definition(macro
, true)) {
1214 std::cerr
<< "testwave: failed to predefine macro: " << macro
1218 else if (9 == debuglevel
) {
1219 std::cerr
<< "add_strict_lexer_definition: predefined macro: " << macro
1225 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1226 // Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1227 template <typename Context
>
1229 testwave_app::add_support_ms_extensions_definition(Context
& ctx
)
1231 std::string
macro("__TESTWAVE_SUPPORT_MS_EXTENSIONS__=1");
1232 if (!ctx
.add_macro_definition(macro
, true)) {
1233 std::cerr
<< "testwave: failed to predefine macro: " << macro
1237 else if (9 == debuglevel
) {
1238 std::cerr
<< "add_support_ms_extensions_definition: predefined macro: "
1246 ///////////////////////////////////////////////////////////////////////////////
1248 // Add special predefined macros to the context object.
1250 // This adds a lot of macros to the test environment, which allows to adjust
1251 // the test cases for different platforms.
1253 ///////////////////////////////////////////////////////////////////////////////
1254 template <typename Context
>
1256 testwave_app::add_predefined_macros(Context
& ctx
)
1258 // add the __TESTWAVE_SIZEOF_<type>__ macros
1259 if (!add_sizeof_definition(ctx
, "CHAR", sizeof(char)) ||
1260 !add_sizeof_definition(ctx
, "SHORT", sizeof(short)) ||
1261 !add_sizeof_definition(ctx
, "INT", sizeof(int)) ||
1262 #if defined(BOOST_HAS_LONG_LONG)
1263 !add_sizeof_definition(ctx
, "LONGLONG", sizeof(boost::long_long_type
)) ||
1265 !add_sizeof_definition(ctx
, "LONG", sizeof(long)))
1267 std::cerr
<< "testwave: failed to add a predefined macro (SIZEOF)."
1272 // add the __TESTWAVE_<type>_MIN__ macros
1273 if (/*!add_min_definition<char>(ctx, "CHAR") ||*/
1274 /*!add_min_definition<unsigned char>(ctx, "UCHAR") ||*/
1275 !add_min_definition
<short>(ctx
, "SHORT") ||
1276 !add_min_definition
<unsigned short>(ctx
, "USHORT") ||
1277 !add_min_definition
<int>(ctx
, "INT") ||
1278 !add_min_definition
<unsigned int>(ctx
, "UINT") ||
1279 #if defined(BOOST_HAS_LONG_LONG)
1280 !add_min_definition
<boost::long_long_type
>(ctx
, "LONGLONG") ||
1281 !add_min_definition
<boost::ulong_long_type
>(ctx
, "ULONGLONG") ||
1283 !add_min_definition
<long>(ctx
, "LONG") ||
1284 !add_min_definition
<unsigned long>(ctx
, "ULONG"))
1286 std::cerr
<< "testwave: failed to add a predefined macro (MIN)."
1290 // add the __TESTWAVE_<type>_MAX__ macros
1291 if (/*!add_max_definition<char>(ctx, "CHAR") ||*/
1292 /*!add_max_definition<unsigned char>(ctx, "UCHAR") ||*/
1293 !add_max_definition
<short>(ctx
, "SHORT") ||
1294 !add_max_definition
<unsigned short>(ctx
, "USHORT") ||
1295 !add_max_definition
<int>(ctx
, "INT") ||
1296 !add_max_definition
<unsigned int>(ctx
, "UINT") ||
1297 #if defined(BOOST_HAS_LONG_LONG)
1298 !add_max_definition
<boost::long_long_type
>(ctx
, "LONGLONG") ||
1299 !add_max_definition
<boost::ulong_long_type
>(ctx
, "ULONGLONG") ||
1301 !add_max_definition
<long>(ctx
, "LONG") ||
1302 !add_max_definition
<unsigned long>(ctx
, "ULONG"))
1304 std::cerr
<< "testwave: failed to add a predefined macro (MAX)."
1308 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1309 // Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1310 if (!add_support_ms_extensions_definition(ctx
))
1312 std::cerr
<< "testwave: failed to add a predefined macro "
1313 "(__TESTWAVE_SUPPORT_MS_EXTENSIONS__)."
1318 #if BOOST_WAVE_USE_STRICT_LEXER != 0
1319 return add_strict_lexer_definition(ctx
);
1325 ///////////////////////////////////////////////////////////////////////////////
1327 // Preprocess the given input data and return the generated output through
1328 // the parameter 'result'.
1330 ///////////////////////////////////////////////////////////////////////////////
1332 testwave_app::preprocess_file(std::string filename
, std::string
const& instr
,
1333 std::string
& result
, std::string
& error
, std::string
& hooks
,
1334 std::string
const& expected_cfg_macro
, bool single_line
)
1336 // create the wave::context object and initialize it from the file to
1337 // preprocess (may contain options inside of special comments)
1338 typedef boost::wave::cpplexer::lex_token
<> token_type
;
1339 typedef boost::wave::cpplexer::lex_iterator
<token_type
> lexer_type
;
1340 typedef boost::wave::context
<
1341 std::string::const_iterator
, lexer_type
,
1342 boost::wave::iteration_context_policies::load_file_to_string
,
1343 collect_hooks_information
<token_type
> >
1346 if (9 == debuglevel
) {
1347 std::cerr
<< "preprocess_file: preprocessing input file: " << filename
1352 // create preprocessing context
1353 context_type
ctx(instr
.begin(), instr
.end(), filename
.c_str(),
1354 collect_hooks_information
<token_type
>(hooks
));
1356 // initialize the context from the options given on the command line
1357 if (!initialise_options(ctx
, global_vm
, single_line
))
1360 // extract the options from the input data and initialize the context
1361 boost::program_options::variables_map local_vm
;
1362 if (!extract_options(filename
, instr
, ctx
, single_line
, local_vm
))
1365 // add special predefined macros
1366 if (!add_predefined_macros(ctx
))
1369 if (!expected_cfg_macro
.empty() &&
1370 !ctx
.is_defined_macro(expected_cfg_macro
))
1372 // skip this test as it is for a disabled configuration
1376 // preprocess the input, loop over all generated tokens collecting the
1378 context_type::iterator_type it
= ctx
.begin();
1379 context_type::iterator_type end
= ctx
.end();
1381 if (local_vm
.count("forceinclude")) {
1382 // add the filenames to force as include files in _reverse_ order
1383 // the second parameter 'is_last' of the force_include function should
1384 // be set to true for the last (first given) file.
1385 std::vector
<std::string
> const &force
=
1386 local_vm
["forceinclude"].as
<std::vector
<std::string
> >();
1387 std::vector
<std::string
>::const_reverse_iterator rend
= force
.rend();
1388 for (std::vector
<std::string
>::const_reverse_iterator cit
= force
.rbegin();
1391 std::string
forceinclude(*cit
);
1392 if (9 == debuglevel
) {
1393 std::cerr
<< "preprocess_file: option: forceinclude ("
1394 << forceinclude
<< ")" << std::endl
;
1396 it
.force_include(forceinclude
.c_str(), ++cit
== rend
);
1400 // perform actual preprocessing
1401 for (/**/; it
!= end
; ++it
)
1403 using namespace boost::wave
;
1405 if (T_PP_LINE
== token_id(*it
)) {
1406 // special handling of the whole #line directive is required to
1407 // allow correct file name matching
1408 if (!handle_line_directive(it
, end
, result
))
1409 return false; // unexpected eof
1412 // add the value of the current token
1413 result
= result
+ (*it
).get_value().c_str();
1418 catch (boost::wave::cpplexer::lexing_exception
const& e
) {
1420 BOOST_WAVETEST_OSSTREAM strm
;
1421 std::string filename
= e
.file_name();
1423 << handle_filepath(filename
) << "(" << e
.line_no() << "): "
1424 << e
.description() << std::endl
;
1426 error
= BOOST_WAVETEST_GETSTRING(strm
);
1429 catch (boost::wave::cpp_exception
const& e
) {
1430 // some preprocessing error
1431 BOOST_WAVETEST_OSSTREAM strm
;
1432 std::string filename
= e
.file_name();
1434 << handle_filepath(filename
) << "(" << e
.line_no() << "): "
1435 << e
.description() << std::endl
;
1437 error
= BOOST_WAVETEST_GETSTRING(strm
);
1441 if (9 == debuglevel
) {
1442 std::cerr
<< "preprocess_file: succeeded to preprocess input file: "
1443 << filename
<< std::endl
;