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 // test application related headers
39 #include "cmd_line_utils.hpp"
40 #include "testwave_app.hpp"
41 #include "collect_hooks_information.hpp"
43 # ifdef BOOST_NO_STDC_NAMESPACE
46 using ::asctime
; using ::gmtime
; using ::localtime
;
47 using ::difftime
; using ::time
; using ::tm
; using ::mktime
; using ::system
;
51 namespace po
= boost::program_options
;
52 namespace fs
= boost::filesystem
;
54 ///////////////////////////////////////////////////////////////////////////////
55 // testwave version definitions
56 #define TESTWAVE_VERSION_MAJOR 0
57 #define TESTWAVE_VERSION_MINOR 6
58 #define TESTWAVE_VERSION_SUBMINOR 0
62 ///////////////////////////////////////////////////////////////////////////
63 template <typename Iterator
>
65 handle_next_token(Iterator
&it
, Iterator
const& end
,
68 typedef typename
Iterator::value_type token_type
;
70 token_type tok
= *it
++;
71 result
= result
+ tok
.get_value().c_str();
72 return (it
== end
) ? false : true;
75 ///////////////////////////////////////////////////////////////////////////
76 template <typename String
>
77 String
const& handle_quoted_filepath(String
&name
)
79 using boost::wave::util::impl::unescape_lit
;
81 String unesc_name
= unescape_lit(name
.substr(1, name
.size()-2));
82 fs::path
p (boost::wave::util::create_path(unesc_name
.c_str()));
84 name
= String("\"") + boost::wave::util::leaf(p
).c_str() + String("\"");
88 ///////////////////////////////////////////////////////////////////////////
89 template <typename Iterator
>
90 bool handle_line_directive(Iterator
&it
, Iterator
const& end
,
93 typedef typename
Iterator::value_type token_type
;
94 typedef typename
token_type::string_type string_type
;
96 if (!handle_next_token(it
, end
, result
) || // #line
97 !handle_next_token(it
, end
, result
) || // whitespace
98 !handle_next_token(it
, end
, result
) || // number
99 !handle_next_token(it
, end
, result
)) // whitespace
104 using boost::wave::util::impl::unescape_lit
;
106 token_type filename
= *it
;
107 string_type name
= filename
.get_value();
109 handle_quoted_filepath(name
);
110 result
= result
+ name
.c_str();
114 template <typename T
>
116 variables_map_as(po::variable_value
const& v
, T
*)
118 #if (__GNUC__ == 3 && (__GNUC_MINOR__ == 2 || __GNUC_MINOR__ == 3)) || \
119 BOOST_WORKAROUND(__MWERKS__, < 0x3200)
120 // gcc 3.2.x and 3.3.x choke on vm[...].as<...>()
121 // CW 8.3 has problems with the v.as<T>() below
122 T
const* r
= boost::any_cast
<T
>(&v
.value());
124 boost::throw_exception(boost::bad_any_cast());
133 ///////////////////////////////////////////////////////////////////////////
135 // This function compares the real result and the expected one but first
136 // replaces all occurrences in the expected result of
137 // $E: to the result of preprocessing the given expression
138 // $F: to the passed full filepath
139 // $P: to the full path
140 // $B: to the full path (same as $P, but using forward slash '/' on Windows)
141 // $V: to the current Boost version number
143 ///////////////////////////////////////////////////////////////////////////
145 testwave_app::got_expected_result(std::string
const& filename
,
146 std::string
const& result
, std::string
& expected
)
148 using boost::wave::util::impl::escape_lit
;
150 std::string full_result
;
151 std::string::size_type pos
= 0;
152 std::string::size_type pos1
= expected
.find_first_of("$");
154 if (pos1
!= std::string::npos
) {
156 switch(expected
[pos1
+1]) {
157 case 'E': // preprocess the given token sequence
159 if ('(' == expected
[pos1
+2]) {
160 std::size_t p
= expected
.find_first_of(")", pos1
+1);
161 if (std::string::npos
== p
) {
163 << "testwave: unmatched parenthesis in $E"
164 " directive" << std::endl
;
167 std::string source
= expected
.substr(pos1
+3, p
-pos1
-3);
168 std::string result
, error
, hooks
;
169 bool pp_result
= preprocess_file(filename
, source
,
170 result
, error
, hooks
, true);
173 << "testwave: preprocessing error in $E directive: "
174 << error
<< std::endl
;
177 full_result
= full_result
+
178 expected
.substr(pos
, pos1
-pos
) + result
;
179 pos1
= expected
.find_first_of ("$",
180 pos
= pos1
+ 4 + source
.size());
185 case 'F': // insert base file name
186 full_result
= full_result
+
187 expected
.substr(pos
, pos1
-pos
) + escape_lit(filename
);
188 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
191 case 'P': // insert full path
192 case 'B': // same as 'P', but forward slashes on Windows
195 boost::wave::util::complete_path(
196 boost::wave::util::create_path(filename
),
197 boost::wave::util::current_path())
200 if ('(' == expected
[pos1
+2]) {
201 // the $P(basename) syntax is used
202 std::size_t p
= expected
.find_first_of(")", pos1
+1);
203 if (std::string::npos
== p
) {
205 << "testwave: unmatched parenthesis in $P"
206 " directive" << std::endl
;
209 std::string base
= expected
.substr(pos1
+3, p
-pos1
-3);
210 fullpath
= boost::wave::util::branch_path(fullpath
) /
211 boost::wave::util::create_path(base
);
212 full_result
+= expected
.substr(pos
, pos1
-pos
);
213 if ('P' == expected
[pos1
+1]) {
214 #if defined(BOOST_WINDOWS)
215 std::string p
= replace_slashes(
216 boost::wave::util::native_file_string(
217 boost::wave::util::normalize(fullpath
)),
221 boost::wave::util::native_file_string(
222 boost::wave::util::normalize(fullpath
)));
224 full_result
+= escape_lit(p
);
227 #if defined(BOOST_WINDOWS)
228 std::string p
= replace_slashes(
229 boost::wave::util::normalize(fullpath
).string());
232 boost::wave::util::normalize(fullpath
).string());
234 full_result
+= escape_lit(p
);
236 pos1
= expected
.find_first_of ("$",
237 pos
= pos1
+ 4 + base
.size());
240 // the $P is used on its own
241 full_result
+= expected
.substr(pos
, pos1
-pos
);
242 if ('P' == expected
[pos1
+1]) {
243 full_result
+= escape_lit(
244 boost::wave::util::native_file_string(fullpath
));
247 #if defined(BOOST_WINDOWS)
248 std::string p
= replace_slashes(fullpath
.string());
250 std::string
p (fullpath
.string());
252 full_result
+= escape_lit(fullpath
.string());
254 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
259 case 'R': // insert relative file name
260 case 'S': // same as 'R', but forward slashes on Windows
263 boost::wave::util::as_relative_to(
264 boost::wave::util::create_path(filename
),
265 boost::wave::util::current_path(),
268 if ('(' == expected
[pos1
+2]) {
269 // the $R(basename) syntax is used
270 std::size_t p
= expected
.find_first_of(")", pos1
+1);
271 if (std::string::npos
== p
) {
273 << "testwave: unmatched parenthesis in $R"
274 " directive" << std::endl
;
277 std::string base
= expected
.substr(pos1
+3, p
-pos1
-3);
278 relpath
= boost::wave::util::branch_path(relpath
) /
279 boost::wave::util::create_path(base
);
280 full_result
+= expected
.substr(pos
, pos1
-pos
);
281 if ('R' == expected
[pos1
+1]) {
282 full_result
+= escape_lit(
283 boost::wave::util::native_file_string(
284 boost::wave::util::normalize(relpath
)));
287 #if defined(BOOST_WINDOWS)
288 std::string p
= replace_slashes(
289 boost::wave::util::normalize(relpath
).string());
292 boost::wave::util::normalize(relpath
).string());
294 full_result
+= escape_lit(p
);
296 pos1
= expected
.find_first_of ("$",
297 pos
= pos1
+ 4 + base
.size());
300 // the $R is used on its own
301 full_result
+= expected
.substr(pos
, pos1
-pos
);
302 if ('R' == expected
[pos1
+1]) {
303 full_result
+= escape_lit(
304 boost::wave::util::native_file_string(relpath
));
307 #if defined(BOOST_WINDOWS)
308 std::string p
= replace_slashes(relpath
.string());
310 std::string
p (relpath
.string());
312 full_result
+= escape_lit(p
);
314 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
319 case 'V': // insert Boost version
320 full_result
= full_result
+
321 expected
.substr(pos
, pos1
-pos
) + BOOST_LIB_VERSION
;
322 pos1
= expected
.find_first_of ("$", pos
= pos1
+ 2);
326 full_result
= full_result
+
327 expected
.substr(pos
, pos1
-pos
);
328 pos1
= expected
.find_first_of ("$", (pos
= pos1
) + 1);
332 } while(pos1
!= std::string::npos
);
333 full_result
+= expected
.substr(pos
);
336 full_result
= expected
;
339 expected
= full_result
;
340 return full_result
== result
;
343 ///////////////////////////////////////////////////////////////////////////////
344 testwave_app::testwave_app(po::variables_map
const& vm
)
345 : debuglevel(1), desc_options("Preprocessor configuration options"),
348 desc_options
.add_options()
349 ("include,I", po::value
<cmd_line_utils::include_paths
>()->composing(),
350 "specify an additional include directory")
351 ("sysinclude,S", po::value
<std::vector
<std::string
> >()->composing(),
352 "specify an additional system include directory")
353 ("forceinclude,F", po::value
<std::vector
<std::string
> >()->composing(),
354 "force inclusion of the given file")
355 ("define,D", po::value
<std::vector
<std::string
> >()->composing(),
356 "specify a macro to define (as macro[=[value]])")
357 ("predefine,P", po::value
<std::vector
<std::string
> >()->composing(),
358 "specify a macro to predefine (as macro[=[value]])")
359 ("undefine,U", po::value
<std::vector
<std::string
> >()->composing(),
360 "specify a macro to undefine")
361 ("nesting,n", po::value
<int>(),
362 "specify a new maximal include nesting depth")
363 ("long_long", "enable long long support in C++ mode")
364 ("preserve", "preserve comments")
365 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
366 ("variadics", "enable certain C99 extensions in C++ mode")
367 ("c99", "enable C99 mode (implies --variadics)")
369 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
370 ("noguard,G", "disable include guard detection")
372 ("skipped_token_hooks", "record skipped_token hook calls")
373 #if BOOST_WAVE_SUPPORT_CPP0X != 0
374 ("c++11", "enable C++11 mode (implies --variadics and --long_long)")
379 ///////////////////////////////////////////////////////////////////////////////
381 // Test the given file (i.e. preprocess the file and compare the result
382 // against the embedded 'R' comments, if an error occurs compare the error
383 // message against the given 'E' comments, if no error occurred, compare the
384 // generated hooks result against the given 'H' comments).
386 ///////////////////////////////////////////////////////////////////////////////
388 testwave_app::test_a_file(std::string filename
)
390 // read the input file into a string
392 if (!read_file(filename
, instr
))
393 return false; // error was reported already
395 bool test_hooks
= true;
396 if (global_vm
.count("hooks"))
397 test_hooks
= variables_map_as(global_vm
["hooks"], (bool *)NULL
);
399 // extract expected output, preprocess the data and compare results
400 std::string expected
, expected_hooks
;
401 if (extract_expected_output(filename
, instr
, expected
, expected_hooks
)) {
402 bool retval
= true; // assume success
403 bool printed_result
= false;
404 std::string result
, error
, hooks
;
405 bool pp_result
= preprocess_file(filename
, instr
, result
, error
, hooks
);
406 if (pp_result
|| !result
.empty()) {
407 // did we expect an error?
408 std::string expected_error
;
409 if (!extract_special_information(filename
, instr
, 'E', expected_error
))
412 if (!expected_error
.empty() &&
413 !got_expected_result(filename
, error
, expected_error
))
415 // we expected an error but got none (or a different one)
416 if (debuglevel
> 2) {
418 << filename
<< ": failed" << std::endl
419 << "result: " << std::endl
<< result
<< std::endl
;
421 if (!error
.empty()) {
422 std::cerr
<< "expected result: " << std::endl
423 << expected
<< std::endl
;
425 if (!expected_error
.empty()) {
426 std::cerr
<< "expected error: " << std::endl
427 << expected_error
<< std::endl
;
430 else if (debuglevel
> 1) {
431 std::cerr
<< filename
<< ": failed" << std::endl
;
435 else if (!got_expected_result(filename
, result
, expected
)) {
436 // no preprocessing error encountered
437 if (debuglevel
> 2) {
439 << filename
<< ": failed" << std::endl
440 << "result: " << std::endl
<< result
<< std::endl
441 << "expected: " << std::endl
<< expected
<< std::endl
;
443 else if (debuglevel
> 1) {
444 std::cerr
<< filename
<< ": failed" << std::endl
;
449 // preprocessing succeeded, check hook information, if appropriate
450 if (test_hooks
&& !expected_hooks
.empty() &&
451 !got_expected_result(filename
, hooks
, expected_hooks
))
453 if (debuglevel
> 2) {
454 std::cerr
<< filename
<< ": failed" << std::endl
455 << "hooks result: " << std::endl
<< hooks
457 std::cerr
<< "expected hooks result: " << std::endl
458 << expected_hooks
<< std::endl
;
460 else if (debuglevel
> 1) {
461 std::cerr
<< filename
<< ": failed" << std::endl
;
467 // print success message, if appropriate
469 if (debuglevel
> 5) {
471 << filename
<< ": succeeded" << std::endl
472 << "result: " << std::endl
<< result
<< std::endl
473 << "hooks result: " << std::endl
<< hooks
<< std::endl
;
475 else if (debuglevel
> 4) {
477 << filename
<< ": succeeded" << std::endl
478 << "result: " << std::endl
<< result
<< std::endl
;
480 else if (debuglevel
> 3) {
481 std::cerr
<< filename
<< ": succeeded" << std::endl
;
483 printed_result
= true;
488 // there was a preprocessing error, was it expected?
489 std::string expected_error
;
490 if (!extract_special_information(filename
, instr
, 'E', expected_error
))
493 if (!got_expected_result(filename
, error
, expected_error
)) {
494 // the error was unexpected
495 if (debuglevel
> 2) {
497 << filename
<< ": failed" << std::endl
;
499 if (!expected_error
.empty()) {
501 << "error result: " << std::endl
<< error
<< std::endl
502 << "expected error: " << std::endl
503 << expected_error
<< std::endl
;
506 std::cerr
<< "unexpected error: " << error
<< std::endl
;
509 else if (debuglevel
> 1) {
510 std::cerr
<< filename
<< ": failed" << std::endl
;
516 if (debuglevel
> 5) {
518 << filename
<< ": succeeded (caught expected error)"
519 << std::endl
<< "error result: " << std::endl
<< error
522 if (!printed_result
) {
524 << "hooks result: " << std::endl
<< hooks
528 else if (debuglevel
> 4) {
530 << filename
<< ": succeeded (caught expected error)"
531 << std::endl
<< "error result: " << std::endl
<< error
534 else if (debuglevel
> 3) {
535 // caught the expected error message
536 std::cerr
<< filename
<< ": succeeded" << std::endl
;
544 << filename
<< ": no information about expected results found"
550 ///////////////////////////////////////////////////////////////////////////////
552 // print the current version of this program
554 ///////////////////////////////////////////////////////////////////////////////
556 testwave_app::print_version()
558 // get time of last compilation of this file
559 boost::wave::util::time_conversion_helper
compilation_time(__DATE__
" " __TIME__
);
561 // calculate the number of days since Feb 12 2005
562 // (the day the testwave project was started)
565 using namespace std
; // some platforms have memset in namespace std
566 memset (&first_day
, 0, sizeof(std::tm
));
567 first_day
.tm_mon
= 1; // Feb
568 first_day
.tm_mday
= 12; // 12
569 first_day
.tm_year
= 105; // 2005
571 long seconds
= long(std::difftime(compilation_time
.get_time(),
572 std::mktime(&first_day
)));
575 << TESTWAVE_VERSION_MAJOR
<< '.'
576 << TESTWAVE_VERSION_MINOR
<< '.'
577 << TESTWAVE_VERSION_SUBMINOR
<< '.'
578 << seconds
/(3600*24) // get number of days from seconds
580 return 0; // exit app
583 ///////////////////////////////////////////////////////////////////////////////
585 // print the copyright statement
587 ///////////////////////////////////////////////////////////////////////////////
589 testwave_app::print_copyright()
591 char const *copyright
[] = {
593 "Testwave: A test driver for the Boost.Wave C++ preprocessor library",
594 "http://www.boost.org/",
596 "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost",
597 "Software License, Version 1.0. (See accompanying file",
598 "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
602 for (int i
= 0; 0 != copyright
[i
]; ++i
)
603 std::cout
<< copyright
[i
] << std::endl
;
605 return 0; // exit app
608 ///////////////////////////////////////////////////////////////////////////////
610 // Read the given file into a string
612 ///////////////////////////////////////////////////////////////////////////////
614 testwave_app::read_file(std::string
const& filename
, std::string
& instr
)
616 // open the given file and report error, if appropriate
617 std::ifstream
instream(filename
.c_str());
618 if (!instream
.is_open()) {
619 std::cerr
<< "testwave: could not open input file: "
620 << filename
<< std::endl
;
623 else if (9 == debuglevel
) {
624 std::cerr
<< "read_file: succeeded to open input file: "
625 << filename
<< std::endl
;
627 instream
.unsetf(std::ios::skipws
);
629 // read the input file into a string
631 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
632 // this is known to be very slow for large files on some systems
633 std::copy (std::istream_iterator
<char>(instream
),
634 std::istream_iterator
<char>(),
635 std::inserter(instr
, instr
.end()));
637 instr
= std::string(std::istreambuf_iterator
<char>(instream
.rdbuf()),
638 std::istreambuf_iterator
<char>());
641 if (9 == debuglevel
) {
642 std::cerr
<< "read_file: succeeded to read input file: "
643 << filename
<< std::endl
;
648 ///////////////////////////////////////////////////////////////////////////////
651 std::string
const& trim_whitespace(std::string
& value
)
653 std::string::size_type first
= value
.find_first_not_of(" \t");
654 if (std::string::npos
== first
)
657 std::string::size_type last
= value
.find_last_not_of(" \t");
658 BOOST_ASSERT(std::string::npos
!= last
);
659 value
= value
.substr(first
, last
-first
+1);
665 ///////////////////////////////////////////////////////////////////////////////
667 // Extract special information from comments marked with the given letter
669 ///////////////////////////////////////////////////////////////////////////////
671 testwave_app::extract_special_information(std::string
const& filename
,
672 std::string
const& instr
, char flag
, std::string
& content
)
674 if (9 == debuglevel
) {
675 std::cerr
<< "extract_special_information: extracting special information ('"
676 << flag
<< "') from input file: " << filename
<< std::endl
;
679 // tokenize the input data into C++ tokens using the C++ lexer
680 typedef boost::wave::cpplexer::lex_token
<> token_type
;
681 typedef boost::wave::cpplexer::lex_iterator
<token_type
> lexer_type
;
682 typedef token_type::position_type position_type
;
684 boost::wave::language_support
const lang_opts
=
685 (boost::wave::language_support
)(
686 boost::wave::support_option_variadics
|
687 boost::wave::support_option_long_long
|
688 boost::wave::support_option_no_character_validation
|
689 boost::wave::support_option_convert_trigraphs
|
690 boost::wave::support_option_insert_whitespace
);
692 position_type
pos(filename
.c_str());
693 lexer_type it
= lexer_type(instr
.begin(), instr
.end(), pos
, lang_opts
);
694 lexer_type end
= lexer_type();
697 // look for C or C++ comments starting with the special character
698 for (/**/; it
!= end
; ++it
) {
699 using namespace boost::wave
;
700 token_id id
= token_id(*it
);
701 if (T_CCOMMENT
== id
) {
702 std::string value
= (*it
).get_value().c_str();
703 if (flag
== value
[2]) {
704 if (value
.size() > 3 && '(' == value
[3]) {
705 std::size_t p
= value
.find_first_of(")");
706 if (std::string::npos
== p
) {
708 << "testwave: missing closing parenthesis in '"
709 << flag
<< "()' directive" << std::endl
;
712 std::string source
= value
.substr(4, p
-4);
713 std::string result
, error
, hooks
;
714 bool pp_result
= preprocess_file(filename
, source
,
715 result
, error
, hooks
, true);
718 << "testwave: preprocessing error in '" << flag
719 << "()' directive: " << error
<< std::endl
;
723 // include this text into the extracted information
724 // only if the result is not zero
725 using namespace std
; // some system have atoi in namespace std
726 if (0 != atoi(result
.c_str())) {
727 std::string
thiscontent(value
.substr(p
+1));
728 if (9 == debuglevel
) {
729 std::cerr
<< "extract_special_information: extracted: "
730 << thiscontent
<< std::endl
;
732 trim_whitespace(thiscontent
);
733 content
+= thiscontent
;
737 std::string
thiscontent(value
.substr(3, value
.size()-5));
738 if (9 == debuglevel
) {
739 std::cerr
<< "extract_special_information: extracted: "
740 << thiscontent
<< std::endl
;
742 trim_whitespace(thiscontent
);
743 content
+= thiscontent
;
747 else if (T_CPPCOMMENT
== id
) {
748 std::string value
= (*it
).get_value().c_str();
749 if (flag
== value
[2]) {
750 if (value
.size() > 3 && '(' == value
[3]) {
751 std::size_t p
= value
.find_first_of(")");
752 if (std::string::npos
== p
) {
754 << "testwave: missing closing parenthesis in '"
755 << flag
<< "()' directive" << std::endl
;
758 std::string source
= value
.substr(4, p
-4);
759 std::string result
, error
, hooks
;
760 bool pp_result
= preprocess_file(filename
, source
,
761 result
, error
, hooks
, true);
764 << "testwave: preprocessing error in '" << flag
765 << "()' directive: " << error
<< std::endl
;
769 // include this text into the extracted information
770 // only if the result is not zero
771 using namespace std
; // some system have atoi in namespace std
772 if (0 != atoi(result
.c_str())) {
773 std::string
thiscontent(value
.substr((' ' == value
[p
+1]) ? p
+2 : p
+1));
774 if (9 == debuglevel
) {
775 std::cerr
<< "extract_special_information: extracted: "
776 << thiscontent
<< std::endl
;
778 trim_whitespace(thiscontent
);
779 content
+= thiscontent
;
783 std::string
thiscontent(value
.substr((' ' == value
[3]) ? 4 : 3));
784 if (9 == debuglevel
) {
785 std::cerr
<< "extract_special_information: extracted: "
788 trim_whitespace(content
);
789 content
+= thiscontent
;
795 catch (boost::wave::cpplexer::lexing_exception
const &e
) {
798 << e
.file_name() << "(" << e
.line_no() << "): "
799 << e
.description() << std::endl
;
803 if (9 == debuglevel
) {
804 std::cerr
<< "extract_special_information: succeeded extracting special information ('"
805 << flag
<< "')" << std::endl
;
810 ///////////////////////////////////////////////////////////////////////////////
812 // Extract the expected output from the given input data
814 // The expected output has to be provided inside of special comments which
815 // start with a capital 'R'. All such comments are concatenated and returned
816 // through the parameter 'expected'.
818 ///////////////////////////////////////////////////////////////////////////////
820 testwave_app::extract_expected_output(std::string
const& filename
,
821 std::string
const& instr
, std::string
& expected
, std::string
& expectedhooks
)
823 return extract_special_information(filename
, instr
, 'R', expected
) &&
824 extract_special_information(filename
, instr
, 'H', expectedhooks
);
827 ///////////////////////////////////////////////////////////////////////////////
829 // Extracts the required preprocessing options from the given input data and
830 // initialises the given Wave context object accordingly.
831 // We allow the same (applicable) options to be used as are valid for the wave
832 // driver executable.
834 ///////////////////////////////////////////////////////////////////////////////
835 template <typename Context
>
837 testwave_app::extract_options(std::string
const& filename
,
838 std::string
const& instr
, Context
& ctx
, bool single_line
,
839 po::variables_map
& vm
)
841 if (9 == debuglevel
) {
842 std::cerr
<< "extract_options: extracting options" << std::endl
;
845 // extract the required information from the comments flagged by a
848 if (!extract_special_information(filename
, instr
, 'O', options
))
852 // parse the configuration information into a program_options_description
854 cmd_line_utils::read_config_options(debuglevel
, options
, desc_options
, vm
);
855 initialise_options(ctx
, vm
, single_line
);
857 catch (std::exception
const &e
) {
858 std::cerr
<< filename
<< ": exception caught: " << e
.what()
863 if (9 == debuglevel
) {
864 std::cerr
<< "extract_options: succeeded extracting options"
871 template <typename Context
>
873 testwave_app::initialise_options(Context
& ctx
, po::variables_map
const& vm
,
876 if (9 == debuglevel
) {
877 std::cerr
<< "initialise_options: initializing options" << std::endl
;
880 if (vm
.count("skipped_token_hooks")) {
881 if (9 == debuglevel
) {
882 std::cerr
<< "initialise_options: option: skipped_token_hooks" << std::endl
;
884 ctx
.get_hooks().set_skipped_token_hooks(true);
887 // initialize the given context from the parsed options
888 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
889 // enable C99 mode, if appropriate (implies variadics)
890 if (vm
.count("c99")) {
891 if (9 == debuglevel
) {
892 std::cerr
<< "initialise_options: option: c99" << std::endl
;
895 boost::wave::language_support(
896 boost::wave::support_c99
897 | boost::wave::support_option_emit_line_directives
898 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
899 | boost::wave::support_option_include_guard_detection
901 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
902 | boost::wave::support_option_emit_pragma_directives
904 | boost::wave::support_option_insert_whitespace
907 else if (vm
.count("variadics")) {
908 // enable variadics and placemarkers, if appropriate
909 if (9 == debuglevel
) {
910 std::cerr
<< "initialise_options: option: variadics" << std::endl
;
912 ctx
.set_language(boost::wave::enable_variadics(ctx
.get_language()));
914 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
916 #if BOOST_WAVE_SUPPORT_CPP0X
917 if (vm
.count("c++11")) {
918 if (9 == debuglevel
) {
919 std::cerr
<< "initialise_options: option: c++11" << std::endl
;
922 boost::wave::language_support(
923 boost::wave::support_cpp0x
924 | boost::wave::support_option_convert_trigraphs
925 | boost::wave::support_option_long_long
926 | boost::wave::support_option_emit_line_directives
927 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
928 | boost::wave::support_option_include_guard_detection
930 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
931 | boost::wave::support_option_emit_pragma_directives
933 | boost::wave::support_option_insert_whitespace
938 // enable long_long mode, if appropriate
939 if (vm
.count("long_long")) {
940 if (9 == debuglevel
) {
941 std::cerr
<< "initialise_options: option: long_long" << std::endl
;
943 ctx
.set_language(boost::wave::enable_long_long(ctx
.get_language()));
946 // enable preserving comments mode, if appropriate
947 if (vm
.count("preserve")) {
948 if (9 == debuglevel
) {
949 std::cerr
<< "initialise_options: option: preserve" << std::endl
;
952 boost::wave::enable_preserve_comments(ctx
.get_language()));
955 // disable automatic include guard detection
956 if (vm
.count("noguard")) {
957 if (9 == debuglevel
) {
958 std::cerr
<< "initialise_options: option: guard" << std::endl
;
961 boost::wave::enable_include_guard_detection(ctx
.get_language(), false));
964 // enable trigraph conversion
965 if (9 == debuglevel
) {
966 std::cerr
<< "initialise_options: option: convert_trigraphs" << std::endl
;
968 ctx
.set_language(boost::wave::enable_convert_trigraphs(ctx
.get_language()));
970 // enable single_line mode
972 if (9 == debuglevel
) {
973 std::cerr
<< "initialise_options: option: single_line" << std::endl
;
975 ctx
.set_language(boost::wave::enable_single_line(ctx
.get_language()));
976 ctx
.set_language(boost::wave::enable_emit_line_directives(ctx
.get_language(), false));
979 // add include directories to the system include search paths
980 if (vm
.count("sysinclude")) {
981 std::vector
<std::string
> const& syspaths
=
982 variables_map_as(vm
["sysinclude"], (std::vector
<std::string
> *)NULL
);
984 std::vector
<std::string
>::const_iterator end
= syspaths
.end();
985 for (std::vector
<std::string
>::const_iterator cit
= syspaths
.begin();
988 if (9 == debuglevel
) {
989 std::cerr
<< "initialise_options: option: -S" << *cit
992 ctx
.add_sysinclude_path((*cit
).c_str());
996 // add include directories to the user include search paths
997 if (vm
.count("include")) {
998 cmd_line_utils::include_paths
const &ip
=
999 variables_map_as(vm
["include"], (cmd_line_utils::include_paths
*)NULL
);
1000 std::vector
<std::string
>::const_iterator end
= ip
.paths
.end();
1002 for (std::vector
<std::string
>::const_iterator cit
= ip
.paths
.begin();
1005 if (9 == debuglevel
) {
1006 std::cerr
<< "initialise_options: option: -I" << *cit
1009 ctx
.add_include_path((*cit
).c_str());
1012 // if on the command line was given -I- , this has to be propagated
1013 if (ip
.seen_separator
) {
1014 if (9 == debuglevel
) {
1015 std::cerr
<< "initialise_options: option: -I-" << std::endl
;
1017 ctx
.set_sysinclude_delimiter();
1020 // add system include directories to the include path
1021 std::vector
<std::string
>::const_iterator sysend
= ip
.syspaths
.end();
1022 for (std::vector
<std::string
>::const_iterator syscit
= ip
.syspaths
.begin();
1023 syscit
!= sysend
; ++syscit
)
1025 if (9 == debuglevel
) {
1026 std::cerr
<< "initialise_options: option: -S" << *syscit
1029 ctx
.add_sysinclude_path((*syscit
).c_str());
1033 // add additional defined macros
1034 if (vm
.count("define")) {
1035 std::vector
<std::string
> const ¯os
=
1036 variables_map_as(vm
["define"], (std::vector
<std::string
>*)NULL
);
1037 std::vector
<std::string
>::const_iterator end
= macros
.end();
1038 for (std::vector
<std::string
>::const_iterator cit
= macros
.begin();
1041 if (9 == debuglevel
) {
1042 std::cerr
<< "initialise_options: option: -D" << *cit
1045 ctx
.add_macro_definition(*cit
, true);
1049 // add additional predefined macros
1050 if (vm
.count("predefine")) {
1051 std::vector
<std::string
> const &predefmacros
=
1052 variables_map_as(vm
["predefine"], (std::vector
<std::string
>*)NULL
);
1053 std::vector
<std::string
>::const_iterator end
= predefmacros
.end();
1054 for (std::vector
<std::string
>::const_iterator cit
= predefmacros
.begin();
1057 if (9 == debuglevel
) {
1058 std::cerr
<< "initialise_options: option: -P" << *cit
1061 ctx
.add_macro_definition(*cit
, true);
1065 // undefine specified macros
1066 if (vm
.count("undefine")) {
1067 std::vector
<std::string
> const &undefmacros
=
1068 variables_map_as(vm
["undefine"], (std::vector
<std::string
>*)NULL
);
1069 std::vector
<std::string
>::const_iterator end
= undefmacros
.end();
1070 for (std::vector
<std::string
>::const_iterator cit
= undefmacros
.begin();
1073 if (9 == debuglevel
) {
1074 std::cerr
<< "initialise_options: option: -U" << *cit
1077 ctx
.remove_macro_definition(*cit
);
1081 // maximal include nesting depth
1082 if (vm
.count("nesting")) {
1083 int max_depth
= variables_map_as(vm
["nesting"], (int*)NULL
);
1084 if (max_depth
< 1 || max_depth
> 100000) {
1085 std::cerr
<< "testwave: bogus maximal include nesting depth: "
1086 << max_depth
<< std::endl
;
1089 else if (9 == debuglevel
) {
1090 std::cerr
<< "initialise_options: option: -n" << max_depth
1093 ctx
.set_max_include_nesting_depth(max_depth
);
1096 if (9 == debuglevel
) {
1097 std::cerr
<< "initialise_options: succeeded to initialize options"
1103 ///////////////////////////////////////////////////////////////////////////////
1104 // construct a SIZEOF macro definition string and predefine this macro
1105 template <typename Context
>
1107 testwave_app::add_sizeof_definition(Context
& ctx
, char const *name
, int value
)
1109 BOOST_WAVETEST_OSSTREAM strm
;
1110 strm
<< "__TESTWAVE_SIZEOF_" << name
<< "__=" << value
;
1112 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1113 if (!ctx
.add_macro_definition(macro
, true)) {
1114 std::cerr
<< "testwave: failed to predefine macro: " << macro
1118 else if (9 == debuglevel
) {
1119 std::cerr
<< "add_sizeof_definition: predefined macro: " << macro
1125 // construct a MIN macro definition string and predefine this macro
1126 template <typename T
, typename Context
>
1128 testwave_app::add_min_definition(Context
& ctx
, char const *name
)
1130 BOOST_WAVETEST_OSSTREAM strm
;
1131 if (!std::numeric_limits
<T
>::is_signed
) {
1132 strm
<< "__TESTWAVE_" << name
<< "_MIN__="
1134 << (std::numeric_limits
<T
>::min
)() << "U";
1137 strm
<< "__TESTWAVE_" << name
<< "_MIN__=( "
1138 << (std::numeric_limits
<T
>::min
)()+1 << "-1)";
1141 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1142 if (!ctx
.add_macro_definition(macro
, true)) {
1143 std::cerr
<< "testwave: failed to predefine macro: " << macro
1147 else if (9 == debuglevel
) {
1148 std::cerr
<< "add_min_definition: predefined macro: " << macro
1154 // construct a MAX macro definition string and predefine this macro
1155 template <typename T
, typename Context
>
1157 testwave_app::add_max_definition(Context
& ctx
, char const *name
)
1159 BOOST_WAVETEST_OSSTREAM strm
;
1160 if (!std::numeric_limits
<T
>::is_signed
) {
1161 strm
<< "__TESTWAVE_" << name
<< "_MAX__="
1163 << (std::numeric_limits
<T
>::max
)() << "U";
1166 strm
<< "__TESTWAVE_" << name
<< "_MAX__="
1167 << (std::numeric_limits
<T
>::max
)();
1170 std::string
macro(BOOST_WAVETEST_GETSTRING(strm
));
1171 if (!ctx
.add_macro_definition(macro
, true)) {
1172 std::cerr
<< "testwave: failed to predefine macro: " << macro
1176 else if (9 == debuglevel
) {
1177 std::cerr
<< "add_max_definition: predefined macro: " << macro
1183 // Predefine __TESTWAVE_HAS_STRICT_LEXER__
1184 template <typename Context
>
1186 testwave_app::add_strict_lexer_definition(Context
& ctx
)
1188 std::string
macro("__TESTWAVE_HAS_STRICT_LEXER__=1");
1189 if (!ctx
.add_macro_definition(macro
, true)) {
1190 std::cerr
<< "testwave: failed to predefine macro: " << macro
1194 else if (9 == debuglevel
) {
1195 std::cerr
<< "add_strict_lexer_definition: predefined macro: " << macro
1201 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1202 // Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1203 template <typename Context
>
1205 testwave_app::add_support_ms_extensions_definition(Context
& ctx
)
1207 std::string
macro("__TESTWAVE_SUPPORT_MS_EXTENSIONS__=1");
1208 if (!ctx
.add_macro_definition(macro
, true)) {
1209 std::cerr
<< "testwave: failed to predefine macro: " << macro
1213 else if (9 == debuglevel
) {
1214 std::cerr
<< "add_support_ms_extensions_definition: predefined macro: "
1222 ///////////////////////////////////////////////////////////////////////////////
1224 // Add special predefined macros to the context object.
1226 // This adds a lot of macros to the test environment, which allows to adjust
1227 // the test cases for different platforms.
1229 ///////////////////////////////////////////////////////////////////////////////
1230 template <typename Context
>
1232 testwave_app::add_predefined_macros(Context
& ctx
)
1234 // add the __TESTWAVE_SIZEOF_<type>__ macros
1235 if (!add_sizeof_definition(ctx
, "CHAR", sizeof(char)) ||
1236 !add_sizeof_definition(ctx
, "SHORT", sizeof(short)) ||
1237 !add_sizeof_definition(ctx
, "INT", sizeof(int)) ||
1238 #if defined(BOOST_HAS_LONG_LONG)
1239 !add_sizeof_definition(ctx
, "LONGLONG", sizeof(boost::long_long_type
)) ||
1241 !add_sizeof_definition(ctx
, "LONG", sizeof(long)))
1243 std::cerr
<< "testwave: failed to add a predefined macro (SIZEOF)."
1248 // add the __TESTWAVE_<type>_MIN__ macros
1249 if (/*!add_min_definition<char>(ctx, "CHAR") ||*/
1250 /*!add_min_definition<unsigned char>(ctx, "UCHAR") ||*/
1251 !add_min_definition
<short>(ctx
, "SHORT") ||
1252 !add_min_definition
<unsigned short>(ctx
, "USHORT") ||
1253 !add_min_definition
<int>(ctx
, "INT") ||
1254 !add_min_definition
<unsigned int>(ctx
, "UINT") ||
1255 #if defined(BOOST_HAS_LONG_LONG)
1256 !add_min_definition
<boost::long_long_type
>(ctx
, "LONGLONG") ||
1257 !add_min_definition
<boost::ulong_long_type
>(ctx
, "ULONGLONG") ||
1259 !add_min_definition
<long>(ctx
, "LONG") ||
1260 !add_min_definition
<unsigned long>(ctx
, "ULONG"))
1262 std::cerr
<< "testwave: failed to add a predefined macro (MIN)."
1266 // add the __TESTWAVE_<type>_MAX__ macros
1267 if (/*!add_max_definition<char>(ctx, "CHAR") ||*/
1268 /*!add_max_definition<unsigned char>(ctx, "UCHAR") ||*/
1269 !add_max_definition
<short>(ctx
, "SHORT") ||
1270 !add_max_definition
<unsigned short>(ctx
, "USHORT") ||
1271 !add_max_definition
<int>(ctx
, "INT") ||
1272 !add_max_definition
<unsigned int>(ctx
, "UINT") ||
1273 #if defined(BOOST_HAS_LONG_LONG)
1274 !add_max_definition
<boost::long_long_type
>(ctx
, "LONGLONG") ||
1275 !add_max_definition
<boost::ulong_long_type
>(ctx
, "ULONGLONG") ||
1277 !add_max_definition
<long>(ctx
, "LONG") ||
1278 !add_max_definition
<unsigned long>(ctx
, "ULONG"))
1280 std::cerr
<< "testwave: failed to add a predefined macro (MAX)."
1284 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1285 // Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1286 if (!add_support_ms_extensions_definition(ctx
))
1288 std::cerr
<< "testwave: failed to add a predefined macro "
1289 "(__TESTWAVE_SUPPORT_MS_EXTENSIONS__)."
1294 #if BOOST_WAVE_USE_STRICT_LEXER != 0
1295 return add_strict_lexer_definition(ctx
);
1301 ///////////////////////////////////////////////////////////////////////////////
1303 // Preprocess the given input data and return the generated output through
1304 // the parameter 'result'.
1306 ///////////////////////////////////////////////////////////////////////////////
1308 testwave_app::preprocess_file(std::string filename
, std::string
const& instr
,
1309 std::string
& result
, std::string
& error
, std::string
& hooks
,
1312 // create the wave::context object and initialize it from the file to
1313 // preprocess (may contain options inside of special comments)
1314 typedef boost::wave::cpplexer::lex_token
<> token_type
;
1315 typedef boost::wave::cpplexer::lex_iterator
<token_type
> lexer_type
;
1316 typedef boost::wave::context
<
1317 std::string::const_iterator
, lexer_type
,
1318 boost::wave::iteration_context_policies::load_file_to_string
,
1319 collect_hooks_information
<token_type
> >
1322 if (9 == debuglevel
) {
1323 std::cerr
<< "preprocess_file: preprocessing input file: " << filename
1328 // create preprocessing context
1329 context_type
ctx(instr
.begin(), instr
.end(), filename
.c_str(),
1330 collect_hooks_information
<token_type
>(hooks
));
1332 // initialize the context from the options given on the command line
1333 if (!initialise_options(ctx
, global_vm
, single_line
))
1336 // extract the options from the input data and initialize the context
1337 boost::program_options::variables_map local_vm
;
1338 if (!extract_options(filename
, instr
, ctx
, single_line
, local_vm
))
1341 // add special predefined macros
1342 if (!add_predefined_macros(ctx
))
1345 // preprocess the input, loop over all generated tokens collecting the
1347 context_type::iterator_type it
= ctx
.begin();
1348 context_type::iterator_type end
= ctx
.end();
1350 if (local_vm
.count("forceinclude")) {
1351 // add the filenames to force as include files in _reverse_ order
1352 // the second parameter 'is_last' of the force_include function should
1353 // be set to true for the last (first given) file.
1354 std::vector
<std::string
> const &force
=
1355 local_vm
["forceinclude"].as
<std::vector
<std::string
> >();
1356 std::vector
<std::string
>::const_reverse_iterator rend
= force
.rend();
1357 for (std::vector
<std::string
>::const_reverse_iterator cit
= force
.rbegin();
1360 std::string
forceinclude(*cit
);
1361 if (9 == debuglevel
) {
1362 std::cerr
<< "preprocess_file: option: forceinclude ("
1363 << forceinclude
<< ")" << std::endl
;
1365 it
.force_include(forceinclude
.c_str(), ++cit
== rend
);
1369 // perform actual preprocessing
1370 for (/**/; it
!= end
; ++it
)
1372 using namespace boost::wave
;
1374 if (T_PP_LINE
== token_id(*it
)) {
1375 // special handling of the whole #line directive is required to
1376 // allow correct file name matching
1377 if (!handle_line_directive(it
, end
, result
))
1378 return false; // unexpected eof
1381 // add the value of the current token
1382 result
= result
+ (*it
).get_value().c_str();
1387 catch (boost::wave::cpplexer::lexing_exception
const& e
) {
1389 BOOST_WAVETEST_OSSTREAM strm
;
1390 std::string filename
= e
.file_name();
1392 << handle_filepath(filename
) << "(" << e
.line_no() << "): "
1393 << e
.description() << std::endl
;
1395 error
= BOOST_WAVETEST_GETSTRING(strm
);
1398 catch (boost::wave::cpp_exception
const& e
) {
1399 // some preprocessing error
1400 BOOST_WAVETEST_OSSTREAM strm
;
1401 std::string filename
= e
.file_name();
1403 << handle_filepath(filename
) << "(" << e
.line_no() << "): "
1404 << e
.description() << std::endl
;
1406 error
= BOOST_WAVETEST_GETSTRING(strm
);
1410 if (9 == debuglevel
) {
1411 std::cerr
<< "preprocess_file: succeeded to preprocess input file: "
1412 << filename
<< std::endl
;