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 #include <boost/filesystem/path.hpp>
62 #include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
64 # ifdef BOOST_NO_STDC_NAMESPACE
67 using ::asctime
; using ::gmtime
; using ::localtime
;
68 using ::difftime
; using ::time
; using ::tm
; using ::mktime
; using ::system
;
72 namespace po
= boost::program_options
;
73 namespace fs
= boost::filesystem
;
75 ///////////////////////////////////////////////////////////////////////////////
76 // testwave version definitions
77 #define TESTWAVE_VERSION_MAJOR 0
78 #define TESTWAVE_VERSION_MINOR 6
79 #define TESTWAVE_VERSION_SUBMINOR 0
82 struct fs_path_imbue_utf8
84 explicit fs_path_imbue_utf8(bool enable
)
85 : m_enabled(enable
), m_prevLocale()
87 if (!m_enabled
) return;
88 static std::locale global_loc
= std::locale();
89 static std::locale
utf_8_loc(global_loc
, new boost::filesystem::detail::utf8_codecvt_facet
);
91 m_prevLocale
= boost::filesystem::path::imbue(utf_8_loc
);
96 if (!m_enabled
) return;
97 boost::filesystem::path::imbue(m_prevLocale
);
100 fs_path_imbue_utf8();
101 fs_path_imbue_utf8(fs_path_imbue_utf8
const&);
102 fs_path_imbue_utf8
& operator=(fs_path_imbue_utf8
const&);
105 std::locale m_prevLocale
;
108 ///////////////////////////////////////////////////////////////////////////
109 template <typename Iterator
>
111 handle_next_token(Iterator
&it
, Iterator
const& end
,
114 typedef typename
Iterator::value_type token_type
;
116 token_type tok
= *it
++;
117 result
= result
+ tok
.get_value().c_str();
118 return (it
== end
) ? false : true;
121 ///////////////////////////////////////////////////////////////////////////
122 template <typename String
>
123 String
const& handle_quoted_filepath(String
&name
)
125 using boost::wave::util::impl::unescape_lit
;
127 String unesc_name
= unescape_lit(name
.substr(1, name
.size()-2));
128 fs::path
p (boost::wave::util::create_path(unesc_name
.c_str()));
130 name
= String("\"") + boost::wave::util::leaf(p
).c_str() + String("\"");
134 ///////////////////////////////////////////////////////////////////////////
135 template <typename Iterator
>
136 bool handle_line_directive(Iterator
&it
, Iterator
const& end
,
139 typedef typename
Iterator::value_type token_type
;
140 typedef typename
token_type::string_type string_type
;
142 if (!handle_next_token(it
, end
, result
) || // #line
143 !handle_next_token(it
, end
, result
) || // whitespace
144 !handle_next_token(it
, end
, result
) || // number
145 !handle_next_token(it
, end
, result
)) // whitespace
150 using boost::wave::util::impl::unescape_lit
;
152 token_type filename
= *it
;
153 string_type name
= filename
.get_value();
155 handle_quoted_filepath(name
);
156 result
= result
+ name
.c_str();
160 template <typename T
>
162 variables_map_as(po::variable_value
const& v
, T
*)
164 #if (__GNUC__ == 3 && (__GNUC_MINOR__ == 2 || __GNUC_MINOR__ == 3)) || \
165 BOOST_WORKAROUND(__MWERKS__, < 0x3200)
166 // gcc 3.2.x and 3.3.x choke on vm[...].as<...>()
167 // CW 8.3 has problems with the v.as<T>() below
168 T
const* r
= boost::any_cast
<T
>(&v
.value());
170 boost::throw_exception(boost::bad_any_cast());
179 ///////////////////////////////////////////////////////////////////////////
181 // This function compares the real result and the expected one but first
182 // replaces all occurrences in the expected result of
183 // $E: to the result of preprocessing the given expression
184 // $F: to the passed full filepath
185 // $P: to the full path
186 // $B: to the full path (same as $P, but using forward slash '/' on Windows)
187 // $V: to the current Boost version number
189 ///////////////////////////////////////////////////////////////////////////
191 testwave_app::got_expected_result(std::string
const& filename
,
192 std::string
const& result
, std::string
& expected
)
194 using boost::wave::util::impl::escape_lit
;
196 std::string full_result
;
197 std::string::size_type pos
= 0;
198 std::string::size_type pos1
= expected
.find_first_of("$");
200 if (pos1
!= std::string::npos
) {
202 switch(expected
[pos1
+1]) {
203 case 'E': // preprocess the given token sequence
205 if ('(' == expected
[pos1
+2]) {
206 std::size_t p
= expected
.find_first_of(")", pos1
+1);
207 if (std::string::npos
== p
) {
209 << "testwave: unmatched parenthesis in $E"
210 " directive" << std::endl
;
213 std::string source
= expected
.substr(pos1
+3, p
-pos1
-3);
214 std::string result
, error
, hooks
;
215 bool pp_result
= preprocess_file(filename
, source
,
216 result
, error
, hooks
, "", true);
219 << "testwave: preprocessing error in $E directive: "
220 << error
<< std::endl
;
223 full_result
= full_result
+
224 expected
.substr(pos
, pos1
-pos
) + result
;
225 pos1
= expected
.find_first_of ("$",
226 pos
= pos1
+ 4 + source
.size());
231 case 'F': // insert base file name
232 full_result
= full_result
+
233 expected
.substr(pos
, pos1
-pos
) + escape_lit(filename
);
234 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
237 case 'P': // insert full path
238 case 'B': // same as 'P', but forward slashes on Windows
241 boost::wave::util::complete_path(
242 boost::wave::util::create_path(filename
),
243 boost::wave::util::current_path())
246 if ('(' == expected
[pos1
+2]) {
247 // the $P(basename) syntax is used
248 std::size_t p
= expected
.find_first_of(")", pos1
+1);
249 if (std::string::npos
== p
) {
251 << "testwave: unmatched parenthesis in $P"
252 " directive" << std::endl
;
255 std::string base
= expected
.substr(pos1
+3, p
-pos1
-3);
256 fullpath
= boost::wave::util::branch_path(fullpath
) /
257 boost::wave::util::create_path(base
);
258 full_result
+= expected
.substr(pos
, pos1
-pos
);
259 if ('P' == expected
[pos1
+1]) {
260 #if defined(BOOST_WINDOWS)
261 std::string p
= replace_slashes(
262 boost::wave::util::native_file_string(
263 boost::wave::util::normalize(fullpath
)),
267 boost::wave::util::native_file_string(
268 boost::wave::util::normalize(fullpath
)));
270 full_result
+= escape_lit(p
);
273 #if defined(BOOST_WINDOWS)
274 std::string p
= replace_slashes(
275 boost::wave::util::normalize(fullpath
).string());
278 boost::wave::util::normalize(fullpath
).string());
280 full_result
+= escape_lit(p
);
282 pos1
= expected
.find_first_of ("$",
283 pos
= pos1
+ 4 + base
.size());
286 // the $P is used on its own
287 full_result
+= expected
.substr(pos
, pos1
-pos
);
288 if ('P' == expected
[pos1
+1]) {
289 full_result
+= escape_lit(
290 boost::wave::util::native_file_string(fullpath
));
293 #if defined(BOOST_WINDOWS)
294 std::string p
= replace_slashes(fullpath
.string());
296 std::string
p (fullpath
.string());
298 full_result
+= escape_lit(fullpath
.string());
300 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
305 case 'R': // insert relative file name
306 case 'S': // same as 'R', but forward slashes on Windows
309 boost::wave::util::as_relative_to(
310 boost::wave::util::create_path(filename
),
311 boost::wave::util::current_path(),
314 if ('(' == expected
[pos1
+2]) {
315 // the $R(basename) syntax is used
316 std::size_t p
= expected
.find_first_of(")", pos1
+1);
317 if (std::string::npos
== p
) {
319 << "testwave: unmatched parenthesis in $R"
320 " directive" << std::endl
;
323 std::string base
= expected
.substr(pos1
+3, p
-pos1
-3);
324 relpath
= boost::wave::util::branch_path(relpath
) /
325 boost::wave::util::create_path(base
);
326 full_result
+= expected
.substr(pos
, pos1
-pos
);
327 if ('R' == expected
[pos1
+1]) {
328 full_result
+= escape_lit(
329 boost::wave::util::native_file_string(
330 boost::wave::util::normalize(relpath
)));
333 #if defined(BOOST_WINDOWS)
334 std::string p
= replace_slashes(
335 boost::wave::util::normalize(relpath
).string());
338 boost::wave::util::normalize(relpath
).string());
340 full_result
+= escape_lit(p
);
342 pos1
= expected
.find_first_of ("$",
343 pos
= pos1
+ 4 + base
.size());
346 // the $R is used on its own
347 full_result
+= expected
.substr(pos
, pos1
-pos
);
348 if ('R' == expected
[pos1
+1]) {
349 full_result
+= escape_lit(
350 boost::wave::util::native_file_string(relpath
));
353 #if defined(BOOST_WINDOWS)
354 std::string p
= replace_slashes(relpath
.string());
356 std::string
p (relpath
.string());
358 full_result
+= escape_lit(p
);
360 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
365 case 'V': // insert Boost version
366 full_result
= full_result
+
367 expected
.substr(pos
, pos1
-pos
) + BOOST_LIB_VERSION
;
368 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
372 full_result
= full_result
+
373 expected
.substr(pos
, pos1
-pos
);
374 pos1
= expected
.find_first_of ("$", (pos
= pos1
) + 1);
378 } while(pos1
!= std::string::npos
);
379 full_result
+= expected
.substr(pos
);
382 full_result
= expected
;
385 expected
= full_result
;
386 return full_result
== result
;
389 ///////////////////////////////////////////////////////////////////////////////
390 testwave_app::testwave_app(po::variables_map
const& vm
)
391 : debuglevel(1), desc_options("Preprocessor configuration options"),
394 desc_options
.add_options()
395 ("include,I", po::value
<cmd_line_utils::include_paths
>()->composing(),
396 "specify an additional include directory")
397 ("sysinclude,S", po::value
<std::vector
<std::string
> >()->composing(),
398 "specify an additional system include directory")
399 ("forceinclude,F", po::value
<std::vector
<std::string
> >()->composing(),
400 "force inclusion of the given file")
401 ("define,D", po::value
<std::vector
<std::string
> >()->composing(),
402 "specify a macro to define (as macro[=[value]])")
403 ("predefine,P", po::value
<std::vector
<std::string
> >()->composing(),
404 "specify a macro to predefine (as macro[=[value]])")
405 ("undefine,U", po::value
<std::vector
<std::string
> >()->composing(),
406 "specify a macro to undefine")
407 ("nesting,n", po::value
<int>(),
408 "specify a new maximal include nesting depth")
409 ("long_long", "enable long long support in C++ mode")
410 ("preserve", "preserve comments")
411 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
412 ("variadics", "enable certain C99 extensions in C++ mode")
413 ("c99", "enable C99 mode (implies --variadics)")
415 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
416 ("noguard,G", "disable include guard detection")
418 ("skipped_token_hooks", "record skipped_token hook calls")
419 #if BOOST_WAVE_SUPPORT_CPP0X != 0
420 ("c++11", "enable C++11 mode (implies --variadics and --long_long)")
422 ("warning,W", po::value
<std::vector
<std::string
> >()->composing(),
427 ///////////////////////////////////////////////////////////////////////////////
429 // Test the given file (i.e. preprocess the file and compare the result
430 // against the embedded 'R' comments, if an error occurs compare the error
431 // message against the given 'E' comments, if no error occurred, compare the
432 // generated hooks result against the given 'H' comments).
434 ///////////////////////////////////////////////////////////////////////////////
436 testwave_app::test_a_file(std::string filename
)
438 // read the input file into a string
440 if (!read_file(filename
, instr
))
441 return false; // error was reported already
443 std::string use_utf8
;
444 extract_special_information(filename
, instr
, 'U', use_utf8
);
445 fs_path_imbue_utf8
to_utf8(use_utf8
.substr(0,3) == "yes");
447 bool test_hooks
= true;
448 if (global_vm
.count("hooks"))
449 test_hooks
= variables_map_as(global_vm
["hooks"], (bool *)NULL
);
451 std::string expected_cfg_macro
;
452 extract_special_information(filename
, instr
, 'D', expected_cfg_macro
);
454 // extract expected output, preprocess the data and compare results
455 std::string expected
, expected_hooks
;
456 if (extract_expected_output(filename
, instr
, expected
, expected_hooks
)) {
457 bool retval
= true; // assume success
458 bool printed_result
= false;
459 std::string result
, error
, hooks
;
460 bool pp_result
= preprocess_file(filename
, instr
, result
, error
, hooks
,
462 if (pp_result
|| !result
.empty()) {
463 // did we expect an error?
464 std::string expected_error
;
465 if (!extract_special_information(filename
, instr
, 'E', expected_error
))
468 if (!expected_error
.empty() &&
469 !got_expected_result(filename
, error
, expected_error
))
471 // we expected an error but got none (or a different one)
472 if (debuglevel
> 2) {
474 << filename
<< ": failed" << std::endl
475 << "result: " << std::endl
<< result
<< std::endl
;
477 if (!error
.empty()) {
478 std::cerr
<< "expected result: " << std::endl
479 << expected
<< std::endl
;
481 if (!expected_error
.empty()) {
482 std::cerr
<< "expected error: " << std::endl
483 << expected_error
<< std::endl
;
486 else if (debuglevel
> 1) {
487 std::cerr
<< filename
<< ": failed" << std::endl
;
491 else if (!got_expected_result(filename
, result
, expected
)) {
492 // no preprocessing error encountered
493 if (debuglevel
> 2) {
495 << filename
<< ": failed" << std::endl
496 << "result: " << std::endl
<< result
<< std::endl
497 << "expected: " << std::endl
<< expected
<< std::endl
;
499 else if (debuglevel
> 1) {
500 std::cerr
<< filename
<< ": failed" << std::endl
;
505 // preprocessing succeeded, check hook information, if appropriate
506 if (test_hooks
&& !expected_hooks
.empty() &&
507 !got_expected_result(filename
, hooks
, expected_hooks
))
509 if (debuglevel
> 2) {
510 std::cerr
<< filename
<< ": failed" << std::endl
511 << "hooks result: " << std::endl
<< hooks
513 std::cerr
<< "expected hooks result: " << std::endl
514 << expected_hooks
<< std::endl
;
516 else if (debuglevel
> 1) {
517 std::cerr
<< filename
<< ": failed" << std::endl
;
523 // print success message, if appropriate
525 if (debuglevel
> 5) {
527 << filename
<< ": succeeded" << std::endl
528 << "result: " << std::endl
<< result
<< std::endl
529 << "hooks result: " << std::endl
<< hooks
<< std::endl
;
531 else if (debuglevel
> 4) {
533 << filename
<< ": succeeded" << std::endl
534 << "result: " << std::endl
<< result
<< std::endl
;
536 else if (debuglevel
> 3) {
537 std::cerr
<< filename
<< ": succeeded" << std::endl
;
539 printed_result
= true;
544 // there was a preprocessing error, was it expected?
545 std::string expected_error
;
546 if (!extract_special_information(filename
, instr
, 'E', expected_error
))
549 if (!got_expected_result(filename
, error
, expected_error
)) {
550 // the error was unexpected
551 if (debuglevel
> 2) {
553 << filename
<< ": failed" << std::endl
;
555 if (!expected_error
.empty()) {
557 << "error result: " << std::endl
<< error
<< std::endl
558 << "expected error: " << std::endl
559 << expected_error
<< std::endl
;
562 std::cerr
<< "unexpected error: " << error
<< std::endl
;
565 else if (debuglevel
> 1) {
566 std::cerr
<< filename
<< ": failed" << std::endl
;
572 if (debuglevel
> 5) {
574 << filename
<< ": succeeded (caught expected error)"
575 << std::endl
<< "error result: " << std::endl
<< error
578 if (!printed_result
) {
580 << "hooks result: " << std::endl
<< hooks
584 else if (debuglevel
> 4) {
586 << filename
<< ": succeeded (caught expected error)"
587 << std::endl
<< "error result: " << std::endl
<< error
590 else if (debuglevel
> 3) {
591 // caught the expected error message
592 std::cerr
<< filename
<< ": succeeded" << std::endl
;
600 << filename
<< ": no information about expected results found"
606 ///////////////////////////////////////////////////////////////////////////////
608 // print the current version of this program
610 ///////////////////////////////////////////////////////////////////////////////
612 testwave_app::print_version()
614 // get time of last compilation of this file
615 boost::wave::util::time_conversion_helper
compilation_time(__DATE__
" " __TIME__
);
617 // calculate the number of days since Feb 12 2005
618 // (the day the testwave project was started)
621 using namespace std
; // some platforms have memset in namespace std
622 memset (&first_day
, 0, sizeof(std::tm
));
623 first_day
.tm_mon
= 1; // Feb
624 first_day
.tm_mday
= 12; // 12
625 first_day
.tm_year
= 105; // 2005
627 long seconds
= long(std::difftime(compilation_time
.get_time(),
628 std::mktime(&first_day
)));
631 << TESTWAVE_VERSION_MAJOR
<< '.'
632 << TESTWAVE_VERSION_MINOR
<< '.'
633 << TESTWAVE_VERSION_SUBMINOR
<< '.'
634 << seconds
/(3600*24) // get number of days from seconds
636 return 0; // exit app
639 ///////////////////////////////////////////////////////////////////////////////
641 // print the copyright statement
643 ///////////////////////////////////////////////////////////////////////////////
645 testwave_app::print_copyright()
647 char const *copyright
[] = {
649 "Testwave: A test driver for the Boost.Wave C++ preprocessor library",
650 "http://www.boost.org/",
652 "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost",
653 "Software License, Version 1.0. (See accompanying file",
654 "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
658 for (int i
= 0; 0 != copyright
[i
]; ++i
)
659 std::cout
<< copyright
[i
] << std::endl
;
661 return 0; // exit app
664 ///////////////////////////////////////////////////////////////////////////////
666 // Read the given file into a string
668 ///////////////////////////////////////////////////////////////////////////////
670 testwave_app::read_file(std::string
const& filename
, std::string
& instr
)
672 // open the given file and report error, if appropriate
673 std::ifstream
instream(filename
.c_str());
674 if (!instream
.is_open()) {
675 std::cerr
<< "testwave: could not open input file: "
676 << filename
<< std::endl
;
679 else if (9 == debuglevel
) {
680 std::cerr
<< "read_file: succeeded to open input file: "
681 << filename
<< std::endl
;
683 instream
.unsetf(std::ios::skipws
);
685 // read the input file into a string
687 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
688 // this is known to be very slow for large files on some systems
689 std::copy (std::istream_iterator
<char>(instream
),
690 std::istream_iterator
<char>(),
691 std::inserter(instr
, instr
.end()));
693 instr
= std::string(std::istreambuf_iterator
<char>(instream
.rdbuf()),
694 std::istreambuf_iterator
<char>());
697 if (9 == debuglevel
) {
698 std::cerr
<< "read_file: succeeded to read input file: "
699 << filename
<< std::endl
;
704 ///////////////////////////////////////////////////////////////////////////////
707 std::string
const& trim_whitespace(std::string
& value
)
709 std::string::size_type first
= value
.find_first_not_of(" \t");
710 if (std::string::npos
== first
)
713 std::string::size_type last
= value
.find_last_not_of(" \t");
714 BOOST_ASSERT(std::string::npos
!= last
);
715 value
= value
.substr(first
, last
-first
+1);
721 ///////////////////////////////////////////////////////////////////////////////
723 // Extract special information from comments marked with the given letter
725 ///////////////////////////////////////////////////////////////////////////////
727 testwave_app::extract_special_information(std::string
const& filename
,
728 std::string
const& instr
, char flag
, std::string
& content
)
730 if (9 == debuglevel
) {
731 std::cerr
<< "extract_special_information: extracting special information ('"
732 << flag
<< "') from input file: " << filename
<< std::endl
;
735 // tokenize the input data into C++ tokens using the C++ lexer
736 typedef boost::wave::cpplexer::lex_token
<> token_type
;
737 typedef boost::wave::cpplexer::lex_iterator
<token_type
> lexer_type
;
738 typedef token_type::position_type position_type
;
740 boost::wave::language_support
const lang_opts
=
741 (boost::wave::language_support
)(
742 boost::wave::support_option_variadics
|
743 boost::wave::support_option_long_long
|
744 boost::wave::support_option_no_character_validation
|
745 boost::wave::support_option_convert_trigraphs
|
746 boost::wave::support_option_insert_whitespace
);
748 position_type
pos(filename
.c_str());
749 lexer_type it
= lexer_type(instr
.begin(), instr
.end(), pos
, lang_opts
);
750 lexer_type end
= lexer_type();
753 // look for C or C++ comments starting with the special character
754 for (/**/; it
!= end
; ++it
) {
755 using namespace boost::wave
;
756 token_id id
= token_id(*it
);
757 if (T_CCOMMENT
== id
) {
758 std::string value
= (*it
).get_value().c_str();
759 if (flag
== value
[2]) {
760 if (value
.size() > 3 && '(' == value
[3]) {
761 std::size_t p
= value
.find_first_of(")");
762 if (std::string::npos
== p
) {
764 << "testwave: missing closing parenthesis in '"
765 << flag
<< "()' directive" << std::endl
;
768 std::string source
= value
.substr(4, p
-4);
769 std::string result
, error
, hooks
;
770 bool pp_result
= preprocess_file(filename
, source
,
771 result
, error
, hooks
, "", true);
774 << "testwave: preprocessing error in '" << flag
775 << "()' directive: " << error
<< std::endl
;
779 // include this text into the extracted information
780 // only if the result is not zero
781 using namespace std
; // some system have atoi in namespace std
782 if (0 != atoi(result
.c_str())) {
783 std::string
thiscontent(value
.substr(p
+1));
784 if (9 == debuglevel
) {
785 std::cerr
<< "extract_special_information: extracted: "
786 << thiscontent
<< std::endl
;
788 trim_whitespace(thiscontent
);
789 content
+= thiscontent
;
793 std::string
thiscontent(value
.substr(3, value
.size()-5));
794 if (9 == debuglevel
) {
795 std::cerr
<< "extract_special_information: extracted: "
796 << thiscontent
<< std::endl
;
798 trim_whitespace(thiscontent
);
799 content
+= thiscontent
;
803 else if (T_CPPCOMMENT
== id
) {
804 std::string value
= (*it
).get_value().c_str();
805 if (flag
== value
[2]) {
806 if (value
.size() > 3 && '(' == value
[3]) {
807 std::size_t p
= value
.find_first_of(")");
808 if (std::string::npos
== p
) {
810 << "testwave: missing closing parenthesis in '"
811 << flag
<< "()' directive" << std::endl
;
814 std::string source
= value
.substr(4, p
-4);
815 std::string result
, error
, hooks
;
816 bool pp_result
= preprocess_file(filename
, source
,
817 result
, error
, hooks
, "", true);
820 << "testwave: preprocessing error in '" << flag
821 << "()' directive: " << error
<< std::endl
;
825 // include this text into the extracted information
826 // only if the result is not zero
827 using namespace std
; // some system have atoi in namespace std
828 if (0 != atoi(result
.c_str())) {
829 std::string
thiscontent(value
.substr((' ' == value
[p
+1]) ? p
+2 : p
+1));
830 if (9 == debuglevel
) {
831 std::cerr
<< "extract_special_information: extracted: "
832 << thiscontent
<< std::endl
;
834 trim_whitespace(thiscontent
);
835 content
+= thiscontent
;
839 std::string
thiscontent(value
.substr((' ' == value
[3]) ? 4 : 3));
840 if (9 == debuglevel
) {
841 std::cerr
<< "extract_special_information: extracted: "
844 trim_whitespace(content
);
845 content
+= thiscontent
;
851 catch (boost::wave::cpplexer::lexing_exception
const &e
) {
854 << e
.file_name() << "(" << e
.line_no() << "): "
855 << e
.description() << std::endl
;
859 if (9 == debuglevel
) {
860 std::cerr
<< "extract_special_information: succeeded extracting special information ('"
861 << flag
<< "')" << std::endl
;
866 ///////////////////////////////////////////////////////////////////////////////
868 // Extract the expected output from the given input data
870 // The expected output has to be provided inside of special comments which
871 // start with a capital 'R'. All such comments are concatenated and returned
872 // through the parameter 'expected'.
874 ///////////////////////////////////////////////////////////////////////////////
876 testwave_app::extract_expected_output(std::string
const& filename
,
877 std::string
const& instr
, std::string
& expected
, std::string
& expectedhooks
)
879 return extract_special_information(filename
, instr
, 'R', expected
) &&
880 extract_special_information(filename
, instr
, 'H', expectedhooks
);
883 ///////////////////////////////////////////////////////////////////////////////
885 // Extracts the required preprocessing options from the given input data and
886 // initialises the given Wave context object accordingly.
887 // We allow the same (applicable) options to be used as are valid for the wave
888 // driver executable.
890 ///////////////////////////////////////////////////////////////////////////////
891 template <typename Context
>
893 testwave_app::extract_options(std::string
const& filename
,
894 std::string
const& instr
, Context
& ctx
, bool single_line
,
895 po::variables_map
& vm
)
897 if (9 == debuglevel
) {
898 std::cerr
<< "extract_options: extracting options" << std::endl
;
901 // extract the required information from the comments flagged by a
904 if (!extract_special_information(filename
, instr
, 'O', options
))
908 // parse the configuration information into a program_options_description
910 cmd_line_utils::read_config_options(debuglevel
, options
, desc_options
, vm
);
911 initialise_options(ctx
, vm
, single_line
);
913 catch (std::exception
const &e
) {
914 std::cerr
<< filename
<< ": exception caught: " << e
.what()
919 if (9 == debuglevel
) {
920 std::cerr
<< "extract_options: succeeded extracting options"
927 template <typename Context
>
929 testwave_app::initialise_options(Context
& ctx
, po::variables_map
const& vm
,
932 if (9 == debuglevel
) {
933 std::cerr
<< "initialise_options: initializing options" << std::endl
;
936 if (vm
.count("skipped_token_hooks")) {
937 if (9 == debuglevel
) {
938 std::cerr
<< "initialise_options: option: skipped_token_hooks" << std::endl
;
940 ctx
.get_hooks().set_skipped_token_hooks(true);
943 // initialize the given context from the parsed options
944 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
945 // enable C99 mode, if appropriate (implies variadics)
946 if (vm
.count("c99")) {
947 if (9 == debuglevel
) {
948 std::cerr
<< "initialise_options: option: c99" << std::endl
;
951 boost::wave::language_support(
952 boost::wave::support_c99
953 | boost::wave::support_option_emit_line_directives
954 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
955 | boost::wave::support_option_include_guard_detection
957 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
958 | boost::wave::support_option_emit_pragma_directives
960 | boost::wave::support_option_insert_whitespace
963 else if (vm
.count("variadics")) {
964 // enable variadics and placemarkers, if appropriate
965 if (9 == debuglevel
) {
966 std::cerr
<< "initialise_options: option: variadics" << std::endl
;
968 ctx
.set_language(boost::wave::enable_variadics(ctx
.get_language()));
970 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
972 #if BOOST_WAVE_SUPPORT_CPP0X
973 if (vm
.count("c++11")) {
974 if (9 == debuglevel
) {
975 std::cerr
<< "initialise_options: option: c++11" << std::endl
;
978 boost::wave::language_support(
979 boost::wave::support_cpp0x
980 | boost::wave::support_option_convert_trigraphs
981 | boost::wave::support_option_long_long
982 | boost::wave::support_option_emit_line_directives
983 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
984 | boost::wave::support_option_include_guard_detection
986 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
987 | boost::wave::support_option_emit_pragma_directives
989 | boost::wave::support_option_insert_whitespace
994 // enable long_long mode, if appropriate
995 if (vm
.count("long_long")) {
996 if (9 == debuglevel
) {
997 std::cerr
<< "initialise_options: option: long_long" << std::endl
;
999 ctx
.set_language(boost::wave::enable_long_long(ctx
.get_language()));
1002 // enable preserving comments mode, if appropriate
1003 if (vm
.count("preserve")) {
1004 if (9 == debuglevel
) {
1005 std::cerr
<< "initialise_options: option: preserve" << std::endl
;
1008 boost::wave::enable_preserve_comments(ctx
.get_language()));
1011 // disable automatic include guard detection
1012 if (vm
.count("noguard")) {
1013 if (9 == debuglevel
) {
1014 std::cerr
<< "initialise_options: option: guard" << std::endl
;
1017 boost::wave::enable_include_guard_detection(ctx
.get_language(), false));
1020 // enable trigraph conversion
1021 if (9 == debuglevel
) {
1022 std::cerr
<< "initialise_options: option: convert_trigraphs" << std::endl
;
1024 ctx
.set_language(boost::wave::enable_convert_trigraphs(ctx
.get_language()));
1026 // enable single_line mode
1028 if (9 == debuglevel
) {
1029 std::cerr
<< "initialise_options: option: single_line" << std::endl
;
1031 ctx
.set_language(boost::wave::enable_single_line(ctx
.get_language()));
1032 ctx
.set_language(boost::wave::enable_emit_line_directives(ctx
.get_language(), false));
1035 // add include directories to the system include search paths
1036 if (vm
.count("sysinclude")) {
1037 std::vector
<std::string
> const& syspaths
=
1038 variables_map_as(vm
["sysinclude"], (std::vector
<std::string
> *)NULL
);
1040 std::vector
<std::string
>::const_iterator end
= syspaths
.end();
1041 for (std::vector
<std::string
>::const_iterator cit
= syspaths
.begin();
1044 std::string
full(*cit
);
1045 got_expected_result(ctx
.get_current_filename(),"",full
);
1047 if (9 == debuglevel
) {
1048 std::cerr
<< "initialise_options: option: -S" << *cit
1051 ctx
.add_sysinclude_path(full
.c_str());
1055 // add include directories to the user include search paths
1056 if (vm
.count("include")) {
1057 cmd_line_utils::include_paths
const &ip
=
1058 variables_map_as(vm
["include"], (cmd_line_utils::include_paths
*)NULL
);
1059 std::vector
<std::string
>::const_iterator end
= ip
.paths
.end();
1061 for (std::vector
<std::string
>::const_iterator cit
= ip
.paths
.begin();
1064 std::string
full(*cit
);
1065 got_expected_result(ctx
.get_current_filename(),"",full
);
1067 if (9 == debuglevel
) {
1068 std::cerr
<< "initialise_options: option: -I" << *cit
1071 ctx
.add_include_path(full
.c_str());
1074 // if on the command line was given -I- , this has to be propagated
1075 if (ip
.seen_separator
) {
1076 if (9 == debuglevel
) {
1077 std::cerr
<< "initialise_options: option: -I-" << std::endl
;
1079 ctx
.set_sysinclude_delimiter();
1082 // add system include directories to the include path
1083 std::vector
<std::string
>::const_iterator sysend
= ip
.syspaths
.end();
1084 for (std::vector
<std::string
>::const_iterator syscit
= ip
.syspaths
.begin();
1085 syscit
!= sysend
; ++syscit
)
1087 if (9 == debuglevel
) {
1088 std::cerr
<< "initialise_options: option: -S" << *syscit
1091 ctx
.add_sysinclude_path((*syscit
).c_str());
1095 // add additional defined macros
1096 if (vm
.count("define")) {
1097 std::vector
<std::string
> const ¯os
=
1098 variables_map_as(vm
["define"], (std::vector
<std::string
>*)NULL
);
1099 std::vector
<std::string
>::const_iterator end
= macros
.end();
1100 for (std::vector
<std::string
>::const_iterator cit
= macros
.begin();
1103 if (9 == debuglevel
) {
1104 std::cerr
<< "initialise_options: option: -D" << *cit
1107 ctx
.add_macro_definition(*cit
, true);
1111 // add additional predefined macros
1112 if (vm
.count("predefine")) {
1113 std::vector
<std::string
> const &predefmacros
=
1114 variables_map_as(vm
["predefine"], (std::vector
<std::string
>*)NULL
);
1115 std::vector
<std::string
>::const_iterator end
= predefmacros
.end();
1116 for (std::vector
<std::string
>::const_iterator cit
= predefmacros
.begin();
1119 if (9 == debuglevel
) {
1120 std::cerr
<< "initialise_options: option: -P" << *cit
1123 ctx
.add_macro_definition(*cit
, true);
1127 // undefine specified macros
1128 if (vm
.count("undefine")) {
1129 std::vector
<std::string
> const &undefmacros
=
1130 variables_map_as(vm
["undefine"], (std::vector
<std::string
>*)NULL
);
1131 std::vector
<std::string
>::const_iterator end
= undefmacros
.end();
1132 for (std::vector
<std::string
>::const_iterator cit
= undefmacros
.begin();
1135 if (9 == debuglevel
) {
1136 std::cerr
<< "initialise_options: option: -U" << *cit
1139 ctx
.remove_macro_definition(*cit
);
1143 // maximal include nesting depth
1144 if (vm
.count("nesting")) {
1145 int max_depth
= variables_map_as(vm
["nesting"], (int*)NULL
);
1146 if (max_depth
< 1 || max_depth
> 100000) {
1147 std::cerr
<< "testwave: bogus maximal include nesting depth: "
1148 << max_depth
<< std::endl
;
1151 else if (9 == debuglevel
) {
1152 std::cerr
<< "initialise_options: option: -n" << max_depth
1155 ctx
.set_max_include_nesting_depth(max_depth
);
1158 if (9 == debuglevel
) {
1159 std::cerr
<< "initialise_options: succeeded to initialize options"
1165 ///////////////////////////////////////////////////////////////////////////////
1166 // construct a SIZEOF macro definition string and predefine this macro
1167 template <typename Context
>
1169 testwave_app::add_sizeof_definition(Context
& ctx
, char const *name
, int value
)
1171 BOOST_WAVETEST_OSSTREAM strm
;
1172 strm
<< "__TESTWAVE_SIZEOF_" << name
<< "__=" << value
;
1174 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1175 if (!ctx
.add_macro_definition(macro
, true)) {
1176 std::cerr
<< "testwave: failed to predefine macro: " << macro
1180 else if (9 == debuglevel
) {
1181 std::cerr
<< "add_sizeof_definition: predefined macro: " << macro
1187 // construct a MIN macro definition string and predefine this macro
1188 template <typename T
, typename Context
>
1190 testwave_app::add_min_definition(Context
& ctx
, char const *name
)
1192 BOOST_WAVETEST_OSSTREAM strm
;
1193 if (!std::numeric_limits
<T
>::is_signed
) {
1194 strm
<< "__TESTWAVE_" << name
<< "_MIN__="
1196 << (std::numeric_limits
<T
>::min
)() << "U";
1199 strm
<< "__TESTWAVE_" << name
<< "_MIN__=( "
1200 << (std::numeric_limits
<T
>::min
)()+1 << "-1)";
1203 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1204 if (!ctx
.add_macro_definition(macro
, true)) {
1205 std::cerr
<< "testwave: failed to predefine macro: " << macro
1209 else if (9 == debuglevel
) {
1210 std::cerr
<< "add_min_definition: predefined macro: " << macro
1216 // construct a MAX macro definition string and predefine this macro
1217 template <typename T
, typename Context
>
1219 testwave_app::add_max_definition(Context
& ctx
, char const *name
)
1221 BOOST_WAVETEST_OSSTREAM strm
;
1222 if (!std::numeric_limits
<T
>::is_signed
) {
1223 strm
<< "__TESTWAVE_" << name
<< "_MAX__="
1225 << (std::numeric_limits
<T
>::max
)() << "U";
1228 strm
<< "__TESTWAVE_" << name
<< "_MAX__="
1229 << (std::numeric_limits
<T
>::max
)();
1232 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1233 if (!ctx
.add_macro_definition(macro
, true)) {
1234 std::cerr
<< "testwave: failed to predefine macro: " << macro
1238 else if (9 == debuglevel
) {
1239 std::cerr
<< "add_max_definition: predefined macro: " << macro
1245 // Predefine __TESTWAVE_HAS_STRICT_LEXER__
1246 template <typename Context
>
1248 testwave_app::add_strict_lexer_definition(Context
& ctx
)
1250 std::string
macro("__TESTWAVE_HAS_STRICT_LEXER__=1");
1251 if (!ctx
.add_macro_definition(macro
, true)) {
1252 std::cerr
<< "testwave: failed to predefine macro: " << macro
1256 else if (9 == debuglevel
) {
1257 std::cerr
<< "add_strict_lexer_definition: predefined macro: " << macro
1263 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1264 // Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1265 template <typename Context
>
1267 testwave_app::add_support_ms_extensions_definition(Context
& ctx
)
1269 std::string
macro("__TESTWAVE_SUPPORT_MS_EXTENSIONS__=1");
1270 if (!ctx
.add_macro_definition(macro
, true)) {
1271 std::cerr
<< "testwave: failed to predefine macro: " << macro
1275 else if (9 == debuglevel
) {
1276 std::cerr
<< "add_support_ms_extensions_definition: predefined macro: "
1284 ///////////////////////////////////////////////////////////////////////////////
1286 // Add special predefined macros to the context object.
1288 // This adds a lot of macros to the test environment, which allows to adjust
1289 // the test cases for different platforms.
1291 ///////////////////////////////////////////////////////////////////////////////
1292 template <typename Context
>
1294 testwave_app::add_predefined_macros(Context
& ctx
)
1296 // add the __TESTWAVE_SIZEOF_<type>__ macros
1297 if (!add_sizeof_definition(ctx
, "CHAR", sizeof(char)) ||
1298 !add_sizeof_definition(ctx
, "SHORT", sizeof(short)) ||
1299 !add_sizeof_definition(ctx
, "INT", sizeof(int)) ||
1300 #if defined(BOOST_HAS_LONG_LONG)
1301 !add_sizeof_definition(ctx
, "LONGLONG", sizeof(boost::long_long_type
)) ||
1303 !add_sizeof_definition(ctx
, "LONG", sizeof(long)))
1305 std::cerr
<< "testwave: failed to add a predefined macro (SIZEOF)."
1310 // add the __TESTWAVE_<type>_MIN__ macros
1311 if (/*!add_min_definition<char>(ctx, "CHAR") ||*/
1312 /*!add_min_definition<unsigned char>(ctx, "UCHAR") ||*/
1313 !add_min_definition
<short>(ctx
, "SHORT") ||
1314 !add_min_definition
<unsigned short>(ctx
, "USHORT") ||
1315 !add_min_definition
<int>(ctx
, "INT") ||
1316 !add_min_definition
<unsigned int>(ctx
, "UINT") ||
1317 #if defined(BOOST_HAS_LONG_LONG)
1318 !add_min_definition
<boost::long_long_type
>(ctx
, "LONGLONG") ||
1319 !add_min_definition
<boost::ulong_long_type
>(ctx
, "ULONGLONG") ||
1321 !add_min_definition
<long>(ctx
, "LONG") ||
1322 !add_min_definition
<unsigned long>(ctx
, "ULONG"))
1324 std::cerr
<< "testwave: failed to add a predefined macro (MIN)."
1328 // add the __TESTWAVE_<type>_MAX__ macros
1329 if (/*!add_max_definition<char>(ctx, "CHAR") ||*/
1330 /*!add_max_definition<unsigned char>(ctx, "UCHAR") ||*/
1331 !add_max_definition
<short>(ctx
, "SHORT") ||
1332 !add_max_definition
<unsigned short>(ctx
, "USHORT") ||
1333 !add_max_definition
<int>(ctx
, "INT") ||
1334 !add_max_definition
<unsigned int>(ctx
, "UINT") ||
1335 #if defined(BOOST_HAS_LONG_LONG)
1336 !add_max_definition
<boost::long_long_type
>(ctx
, "LONGLONG") ||
1337 !add_max_definition
<boost::ulong_long_type
>(ctx
, "ULONGLONG") ||
1339 !add_max_definition
<long>(ctx
, "LONG") ||
1340 !add_max_definition
<unsigned long>(ctx
, "ULONG"))
1342 std::cerr
<< "testwave: failed to add a predefined macro (MAX)."
1346 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1347 // Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1348 if (!add_support_ms_extensions_definition(ctx
))
1350 std::cerr
<< "testwave: failed to add a predefined macro "
1351 "(__TESTWAVE_SUPPORT_MS_EXTENSIONS__)."
1356 #if BOOST_WAVE_USE_STRICT_LEXER != 0
1357 return add_strict_lexer_definition(ctx
);
1363 ///////////////////////////////////////////////////////////////////////////////
1365 // Preprocess the given input data and return the generated output through
1366 // the parameter 'result'.
1368 ///////////////////////////////////////////////////////////////////////////////
1370 testwave_app::preprocess_file(std::string filename
, std::string
const& instr
,
1371 std::string
& result
, std::string
& error
, std::string
& hooks
,
1372 std::string
const& expected_cfg_macro
, bool single_line
)
1374 // create the wave::context object and initialize it from the file to
1375 // preprocess (may contain options inside of special comments)
1376 typedef boost::wave::cpplexer::lex_token
<> token_type
;
1377 typedef boost::wave::cpplexer::lex_iterator
<token_type
> lexer_type
;
1378 typedef boost::wave::context
<
1379 std::string::const_iterator
, lexer_type
,
1380 boost::wave::iteration_context_policies::load_file_to_string
,
1381 collect_hooks_information
<token_type
> >
1384 if (9 == debuglevel
) {
1385 std::cerr
<< "preprocess_file: preprocessing input file: " << filename
1390 // create preprocessing context
1391 context_type
ctx(instr
.begin(), instr
.end(), filename
.c_str(),
1392 collect_hooks_information
<token_type
>(hooks
));
1394 // initialize the context from the options given on the command line
1395 if (!initialise_options(ctx
, global_vm
, single_line
))
1398 // extract the options from the input data and initialize the context
1399 boost::program_options::variables_map local_vm
;
1400 if (!extract_options(filename
, instr
, ctx
, single_line
, local_vm
))
1403 // add special predefined macros
1404 if (!add_predefined_macros(ctx
))
1407 if (!expected_cfg_macro
.empty() &&
1408 !ctx
.is_defined_macro(expected_cfg_macro
))
1410 // skip this test as it is for a disabled configuration
1414 // preprocess the input, loop over all generated tokens collecting the
1416 context_type::iterator_type it
= ctx
.begin();
1417 context_type::iterator_type end
= ctx
.end();
1419 if (local_vm
.count("forceinclude")) {
1420 // add the filenames to force as include files in _reverse_ order
1421 // the second parameter 'is_last' of the force_include function should
1422 // be set to true for the last (first given) file.
1423 std::vector
<std::string
> const &force
=
1424 local_vm
["forceinclude"].as
<std::vector
<std::string
> >();
1425 std::vector
<std::string
>::const_reverse_iterator rend
= force
.rend();
1426 for (std::vector
<std::string
>::const_reverse_iterator cit
= force
.rbegin();
1429 std::string
forceinclude(*cit
);
1430 if (9 == debuglevel
) {
1431 std::cerr
<< "preprocess_file: option: forceinclude ("
1432 << forceinclude
<< ")" << std::endl
;
1434 it
.force_include(forceinclude
.c_str(), ++cit
== rend
);
1438 // perform actual preprocessing
1439 for (/**/; it
!= end
; ++it
)
1441 using namespace boost::wave
;
1443 if (T_PP_LINE
== token_id(*it
)) {
1444 // special handling of the whole #line directive is required to
1445 // allow correct file name matching
1446 if (!handle_line_directive(it
, end
, result
))
1447 return false; // unexpected eof
1450 // add the value of the current token
1451 result
= result
+ (*it
).get_value().c_str();
1456 catch (boost::wave::cpplexer::lexing_exception
const& e
) {
1458 BOOST_WAVETEST_OSSTREAM strm
;
1459 std::string filename
= e
.file_name();
1461 << handle_filepath(filename
) << "(" << e
.line_no() << "): "
1462 << e
.description() << std::endl
;
1464 error
= BOOST_WAVETEST_GETSTRING(strm
);
1467 catch (boost::wave::cpp_exception
const& e
) {
1468 // some preprocessing error
1469 BOOST_WAVETEST_OSSTREAM strm
;
1470 std::string filename
= e
.file_name();
1472 << handle_filepath(filename
) << "(" << e
.line_no() << "): "
1473 << e
.description() << std::endl
;
1475 error
= BOOST_WAVETEST_GETSTRING(strm
);
1479 if (9 == debuglevel
) {
1480 std::cerr
<< "preprocess_file: succeeded to preprocess input file: "
1481 << filename
<< std::endl
;