1/*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
3 http://www.boost.org/
4
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=============================================================================*/
9
10// disable stupid compiler warnings
11#include <boost/config/warning_disable.hpp>
12
13// system headers
14#include <string>
15#include <iosfwd>
16#include <vector>
17#include <ctime>
18
19// include boost
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>
26
27// include Wave
28
29#include <boost/wave.hpp>
30
31// include the lexer related stuff
32#include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type
33#include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer type
34
35///////////////////////////////////////////////////////////////////////////////
36// Include lexer specifics, import lexer names
37#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
38#include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
39#endif
40
41///////////////////////////////////////////////////////////////////////////////
42// Include the grammar definitions, if these shouldn't be compiled separately
43// (ATTENTION: _very_ large compilation times!)
44#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
45#include <boost/wave/grammars/cpp_intlit_grammar.hpp>
46#include <boost/wave/grammars/cpp_chlit_grammar.hpp>
47#include <boost/wave/grammars/cpp_grammar.hpp>
48#include <boost/wave/grammars/cpp_expression_grammar.hpp>
49#include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
50#include <boost/wave/grammars/cpp_defined_grammar.hpp>
51#endif
52
53// test application related headers
54#include "cmd_line_utils.hpp"
55#include "testwave_app.hpp"
56#include "collect_hooks_information.hpp"
57
58#include <boost/filesystem/path.hpp>
59#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
60
61# ifdef BOOST_NO_STDC_NAMESPACE
62namespace std
63{
64 using ::asctime; using ::gmtime; using ::localtime;
65 using ::difftime; using ::time; using ::tm; using ::mktime; using ::system;
66}
67# endif
68
69namespace po = boost::program_options;
70namespace fs = boost::filesystem;
71
72///////////////////////////////////////////////////////////////////////////////
73// testwave version definitions
74#define TESTWAVE_VERSION_MAJOR 0
75#define TESTWAVE_VERSION_MINOR 6
76#define TESTWAVE_VERSION_SUBMINOR 0
77
78namespace {
79 struct fs_path_imbue_utf8
80 {
81 explicit fs_path_imbue_utf8(bool enable)
82 : m_enabled(enable), m_prevLocale()
83 {
84 if (!m_enabled) return;
85 static std::locale global_loc = std::locale();
86 static std::locale utf_8_loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet);
87
88 m_prevLocale = boost::filesystem::path::imbue(loc: utf_8_loc);
89
90 }
91 ~fs_path_imbue_utf8()
92 {
93 if (!m_enabled) return;
94 boost::filesystem::path::imbue(loc: m_prevLocale);
95 }
96 private:
97 fs_path_imbue_utf8();
98 fs_path_imbue_utf8(fs_path_imbue_utf8 const&);
99 fs_path_imbue_utf8& operator=(fs_path_imbue_utf8 const&);
100
101 bool m_enabled;
102 std::locale m_prevLocale;
103 };
104
105 ///////////////////////////////////////////////////////////////////////////
106 template <typename Iterator>
107 inline bool
108 handle_next_token(Iterator &it, Iterator const& end,
109 std::string &result)
110 {
111 typedef typename Iterator::value_type token_type;
112
113 token_type tok = *it++;
114 result = result + tok.get_value().c_str();
115 return (it == end) ? false : true;
116 }
117
118 ///////////////////////////////////////////////////////////////////////////
119 template <typename String>
120 String const& handle_quoted_filepath(String &name)
121 {
122 using boost::wave::util::impl::unescape_lit;
123
124 String unesc_name = unescape_lit(name.substr(1, name.size()-2));
125 fs::path p (boost::wave::util::create_path(unesc_name.c_str()));
126
127 name = String("\"") + boost::wave::util::leaf(p).c_str() + String("\"");
128 return name;
129 }
130
131 ///////////////////////////////////////////////////////////////////////////
132 template <typename Iterator>
133 bool handle_line_directive(Iterator &it, Iterator const& end,
134 std::string &result)
135 {
136 typedef typename Iterator::value_type token_type;
137 typedef typename token_type::string_type string_type;
138
139 if (!handle_next_token(it, end, result) || // #line
140 !handle_next_token(it, end, result) || // whitespace
141 !handle_next_token(it, end, result) || // number
142 !handle_next_token(it, end, result)) // whitespace
143 {
144 return false;
145 }
146
147 using boost::wave::util::impl::unescape_lit;
148
149 token_type filename = *it;
150 string_type name = filename.get_value();
151
152 handle_quoted_filepath(name);
153 result = result + name.c_str();
154 return true;
155 }
156
157 template <typename T>
158 inline T const&
159 variables_map_as(po::variable_value const& v, T*)
160 {
161#if (__GNUC__ == 3 && (__GNUC_MINOR__ == 2 || __GNUC_MINOR__ == 3)) || \
162 BOOST_WORKAROUND(__MWERKS__, < 0x3200)
163// gcc 3.2.x and 3.3.x choke on vm[...].as<...>()
164// CW 8.3 has problems with the v.as<T>() below
165 T const* r = boost::any_cast<T>(&v.value());
166 if (!r)
167 boost::throw_exception(boost::bad_any_cast());
168 return *r;
169#else
170 return v.as<T>();
171#endif
172 }
173
174}
175
176///////////////////////////////////////////////////////////////////////////
177//
178// This function compares the real result and the expected one but first
179// replaces all occurrences in the expected result of
180// $E: to the result of preprocessing the given expression
181// $F: to the passed full filepath
182// $P: to the full path
183// $B: to the full path (same as $P, but using forward slash '/' on Windows)
184// $V: to the current Boost version number
185//
186///////////////////////////////////////////////////////////////////////////
187bool
188testwave_app::got_expected_result(std::string const& filename,
189 std::string const& result, std::string& expected)
190{
191 using boost::wave::util::impl::escape_lit;
192
193 std::string full_result;
194 std::string::size_type pos = 0;
195 std::string::size_type pos1 = expected.find_first_of(s: "$");
196
197 if (pos1 != std::string::npos) {
198 do {
199 switch(expected[pos1+1]) {
200 case 'E': // preprocess the given token sequence
201 {
202 if ('(' == expected[pos1+2]) {
203 std::size_t p = expected.find_first_of(s: ")", pos: pos1+1);
204 if (std::string::npos == p) {
205 std::cerr
206 << "testwave: unmatched parenthesis in $E"
207 " directive" << std::endl;
208 return false;
209 }
210 std::string source = expected.substr(pos: pos1+3, n: p-pos1-3);
211 std::string result, error, hooks;
212 bool pp_result;
213 std::tie(args&: pp_result, args: std::ignore) = preprocess_file(filename, instr: source,
214 result, error, hooks, expected_cfg_macro: "", single_line: true);
215 if (!pp_result) {
216 std::cerr
217 << "testwave: preprocessing error in $E directive: "
218 << error << std::endl;
219 return false;
220 }
221 full_result = full_result +
222 expected.substr(pos: pos, n: pos1-pos) + result;
223 pos1 = expected.find_first_of (s: "$",
224 pos: pos = pos1 + 4 + source.size());
225 }
226 }
227 break;
228
229 case 'F': // insert base file name
230 full_result = full_result +
231 expected.substr(pos: pos, n: pos1-pos) + escape_lit(value: filename);
232 pos1 = expected.find_first_of (s: "$", pos: pos = pos1 + 2);
233 break;
234
235 case 'P': // insert full path
236 case 'B': // same as 'P', but forward slashes on Windows
237 {
238 fs::path fullpath (
239 boost::wave::util::complete_path(
240 p: boost::wave::util::create_path(p: filename),
241 base: boost::wave::util::current_path())
242 );
243
244 if ('(' == expected[pos1+2]) {
245 // the $P(basename) syntax is used
246 std::size_t p = expected.find_first_of(s: ")", pos: pos1+1);
247 if (std::string::npos == p) {
248 std::cerr
249 << "testwave: unmatched parenthesis in $P"
250 " directive" << std::endl;
251 return false;
252 }
253 std::string base = expected.substr(pos: pos1+3, n: p-pos1-3);
254 fullpath = boost::wave::util::branch_path(p: fullpath) /
255 boost::wave::util::create_path(p: base);
256 full_result += expected.substr(pos: pos, n: pos1-pos);
257 if ('P' == expected[pos1+1]) {
258#if defined(BOOST_WINDOWS)
259 std::string p = replace_slashes(
260 boost::wave::util::native_file_string(
261 boost::wave::util::normalize(fullpath)),
262 "/", '\\');
263#else
264 std::string p (
265 boost::wave::util::native_file_string(
266 p: boost::wave::util::normalize(p&: fullpath)));
267#endif
268 full_result += escape_lit(value: p);
269 }
270 else {
271#if defined(BOOST_WINDOWS)
272 std::string p = replace_slashes(
273 boost::wave::util::normalize(fullpath).string());
274#else
275 std::string p (
276 boost::wave::util::normalize(p&: fullpath).string());
277#endif
278 full_result += escape_lit(value: p);
279 }
280 pos1 = expected.find_first_of (s: "$",
281 pos: pos = pos1 + 4 + base.size());
282 }
283 else {
284 // the $P is used on its own
285 full_result += expected.substr(pos: pos, n: pos1-pos);
286 if ('P' == expected[pos1+1]) {
287 full_result += escape_lit(
288 value: boost::wave::util::native_file_string(p: fullpath));
289 }
290 else {
291#if defined(BOOST_WINDOWS)
292 std::string p = replace_slashes(fullpath.string());
293#else
294 std::string p (fullpath.string());
295#endif
296 full_result += escape_lit(value: fullpath.string());
297 }
298 pos1 = expected.find_first_of (s: "$", pos: pos = pos1 + 2);
299 }
300 }
301 break;
302
303 case 'R': // insert relative file name
304 case 'S': // same as 'R', but forward slashes on Windows
305 {
306 fs::path relpath;
307 boost::wave::util::as_relative_to(
308 path: boost::wave::util::create_path(p: filename),
309 base: boost::wave::util::current_path(),
310 result&: relpath);
311
312 if ('(' == expected[pos1+2]) {
313 // the $R(basename) syntax is used
314 std::size_t p = expected.find_first_of(s: ")", pos: pos1+1);
315 if (std::string::npos == p) {
316 std::cerr
317 << "testwave: unmatched parenthesis in $R"
318 " directive" << std::endl;
319 return false;
320 }
321 std::string base = expected.substr(pos: pos1+3, n: p-pos1-3);
322 relpath = boost::wave::util::branch_path(p: relpath) /
323 boost::wave::util::create_path(p: base);
324 full_result += expected.substr(pos: pos, n: pos1-pos);
325 if ('R' == expected[pos1+1]) {
326 full_result += escape_lit(
327 value: boost::wave::util::native_file_string(
328 p: boost::wave::util::normalize(p&: relpath)));
329 }
330 else {
331#if defined(BOOST_WINDOWS)
332 std::string p = replace_slashes(
333 boost::wave::util::normalize(relpath).string());
334#else
335 std::string p (
336 boost::wave::util::normalize(p&: relpath).string());
337#endif
338 full_result += escape_lit(value: p);
339 }
340 pos1 = expected.find_first_of (s: "$",
341 pos: pos = pos1 + 4 + base.size());
342 }
343 else {
344 // the $R is used on its own
345 full_result += expected.substr(pos: pos, n: pos1-pos);
346 if ('R' == expected[pos1+1]) {
347 full_result += escape_lit(
348 value: boost::wave::util::native_file_string(p: relpath));
349 }
350 else {
351#if defined(BOOST_WINDOWS)
352 std::string p = replace_slashes(relpath.string());
353#else
354 std::string p (relpath.string());
355#endif
356 full_result += escape_lit(value: p);
357 }
358 pos1 = expected.find_first_of (s: "$", pos: pos = pos1 + 2);
359 }
360 }
361 break;
362
363 case 'V': // insert Boost version
364 full_result = full_result +
365 expected.substr(pos: pos, n: pos1-pos) + BOOST_LIB_VERSION;
366 pos1 = expected.find_first_of (s: "$", pos: pos = pos1 + 2);
367 break;
368
369 default:
370 full_result = full_result +
371 expected.substr(pos: pos, n: pos1-pos);
372 pos1 = expected.find_first_of (s: "$", pos: (pos = pos1) + 1);
373 break;
374 }
375
376 } while(pos1 != std::string::npos);
377 full_result += expected.substr(pos: pos);
378 }
379 else {
380 full_result = expected;
381 }
382
383 expected = full_result;
384 return full_result == result;
385}
386
387///////////////////////////////////////////////////////////////////////////////
388testwave_app::testwave_app(po::variables_map const& vm)
389: debuglevel(1), desc_options("Preprocessor configuration options"),
390 global_vm(vm)
391{
392 desc_options.add_options()
393 ("include,I", po::value<cmd_line_utils::include_paths>()->composing(),
394 "specify an additional include directory")
395 ("sysinclude,S", po::value<std::vector<std::string> >()->composing(),
396 "specify an additional system include directory")
397 ("forceinclude,F", po::value<std::vector<std::string> >()->composing(),
398 "force inclusion of the given file")
399 ("define,D", po::value<std::vector<std::string> >()->composing(),
400 "specify a macro to define (as macro[=[value]])")
401 ("predefine,P", po::value<std::vector<std::string> >()->composing(),
402 "specify a macro to predefine (as macro[=[value]])")
403 ("undefine,U", po::value<std::vector<std::string> >()->composing(),
404 "specify a macro to undefine")
405 ("nesting,n", po::value<int>(),
406 "specify a new maximal include nesting depth")
407 ("long_long", "enable long long support in C++ mode")
408 ("preserve", "preserve comments")
409#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
410 ("variadics", "enable certain C99 extensions in C++ mode")
411 ("c99", "enable C99 mode (implies --variadics)")
412#endif
413#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
414 ("noguard,G", "disable include guard detection")
415#endif
416 ("skipped_token_hooks", "record skipped_token hook calls")
417#if BOOST_WAVE_SUPPORT_CPP0X != 0
418 ("c++11", "enable C++11 mode (implies --variadics and --long_long)")
419#endif
420#if BOOST_WAVE_SUPPORT_CPP1Z != 0
421 ("c++17", "enable C++17 mode (implies --variadics and --long_long, adds __has_include)")
422#endif
423#if BOOST_WAVE_SUPPORT_CPP2A != 0
424 ("c++20", "enable C++20 mode (implies --variadics and --long_long, adds __has_include and __VA_OPT__)")
425#endif
426 ("warning,W", po::value<std::vector<std::string> >()->composing(),
427 "Warning settings.")
428 ;
429}
430
431///////////////////////////////////////////////////////////////////////////////
432//
433// Test the given file (i.e. preprocess the file and compare the result
434// against the embedded 'R' comments, if an error occurs compare the error
435// message against the given 'E' comments. Then, compare the
436// generated hooks result against the given 'H' comments).
437//
438///////////////////////////////////////////////////////////////////////////////
439bool
440testwave_app::test_a_file(std::string filename)
441{
442 // read the input file into a string
443 std::string instr;
444 if (!read_file(filename, instr))
445 return false; // error was reported already
446
447 std::string use_utf8;
448 extract_special_information(filename, instr, flag: 'U', content&: use_utf8);
449 fs_path_imbue_utf8 to_utf8(use_utf8.substr(pos: 0,n: 3) == "yes");
450
451 bool test_hooks = true;
452 if (global_vm.count(x: "hooks"))
453 test_hooks = variables_map_as(v: global_vm["hooks"], (bool *)NULL);
454
455 std::string expected_cfg_macro;
456 extract_special_information(filename, instr, flag: 'D', content&: expected_cfg_macro);
457
458 // extract expected output, preprocess the data and compare results
459 std::string expected, expected_hooks;
460 if (extract_expected_output(filename, instr, expected, expectedhooks&: expected_hooks)) {
461 bool retval = true; // assume success
462 bool printed_result = false;
463 std::string result, error, hooks;
464 bool pp_result;
465 bool suppressed; // true if there is an absent expected config macro
466 std::tie(args&: pp_result, args&: suppressed) =
467 preprocess_file(filename, instr, result, error, hooks, expected_cfg_macro);
468 if (pp_result || !result.empty()) {
469 // did we expect an error?
470 std::string expected_error;
471 if (!extract_special_information(filename, instr, flag: 'E', content&: expected_error))
472 return false;
473
474 if (!expected_error.empty() &&
475 !got_expected_result(filename, result: error, expected&: expected_error))
476 {
477 // we expected an error but got none (or a different one)
478 if (debuglevel > 2) {
479 std::cerr
480 << filename << ": failed" << std::endl
481 << "result: " << std::endl << result << std::endl;
482
483 if (!error.empty()) {
484 std::cerr << "expected result: " << std::endl
485 << expected << std::endl;
486 }
487 if (!expected_error.empty()) {
488 std::cerr << "expected error: " << std::endl
489 << expected_error << std::endl;
490 }
491 }
492 else if (debuglevel > 1) {
493 std::cerr << filename << ": failed" << std::endl;
494 }
495 retval = false;
496 }
497 else if (suppressed)
498 {
499 // no need to check result or hooks as the test was not run
500 }
501 else if (!got_expected_result(filename, result, expected)) {
502 // no preprocessing error encountered
503 if (debuglevel > 2) {
504 std::cerr
505 << filename << ": failed" << std::endl
506 << "result: " << std::endl << result << std::endl
507 << "expected: " << std::endl << expected << std::endl;
508 }
509 else if (debuglevel > 1) {
510 std::cerr << filename << ": failed" << std::endl;
511 }
512 retval = false;
513 }
514 else {
515 // preprocessing succeeded, check hook information, if appropriate
516 if (test_hooks && !expected_hooks.empty() &&
517 !got_expected_result(filename, result: hooks, expected&: expected_hooks))
518 {
519 if (debuglevel > 2) {
520 std::cerr << filename << ": failed" << std::endl
521 << "hooks result: " << std::endl << hooks
522 << std::endl;
523 std::cerr << "expected hooks result: " << std::endl
524 << expected_hooks << std::endl;
525 }
526 else if (debuglevel > 1) {
527 std::cerr << filename << ": failed" << std::endl;
528 }
529 retval = false;
530 }
531 }
532
533 // print success message, if appropriate
534 if (retval) {
535 if (debuglevel > 5) {
536 std::cerr
537 << filename << ": succeeded" << std::endl
538 << "result: " << std::endl << result << std::endl
539 << "hooks result: " << std::endl << hooks << std::endl;
540 }
541 else if (debuglevel > 4) {
542 std::cerr
543 << filename << ": succeeded" << std::endl
544 << "result: " << std::endl << result << std::endl;
545 }
546 else if (debuglevel > 3) {
547 std::cerr << filename << ": succeeded" << std::endl;
548 }
549 printed_result = true;
550 }
551 }
552
553 if (!pp_result) {
554 // there was a preprocessing error, was it expected?
555 std::string expected_error;
556 if (!extract_special_information(filename, instr, flag: 'E', content&: expected_error))
557 return false;
558
559 if (!got_expected_result(filename, result: error, expected&: expected_error)) {
560 // the error was unexpected
561 if (debuglevel > 2) {
562 std::cerr
563 << filename << ": failed" << std::endl;
564
565 if (!expected_error.empty()) {
566 std::cerr
567 << "error result: " << std::endl << error << std::endl
568 << "expected error: " << std::endl
569 << expected_error << std::endl;
570 }
571 else {
572 std::cerr << "unexpected error: " << error << std::endl;
573 }
574 }
575 else if (debuglevel > 1) {
576 std::cerr << filename << ": failed" << std::endl;
577 }
578 retval = false;
579 } else {
580 // expected error; check the hooks also
581 if (test_hooks && !expected_hooks.empty() &&
582 !got_expected_result(filename, result: hooks, expected&: expected_hooks))
583 {
584 if (debuglevel > 2) {
585 std::cerr << filename << ": failed (though caught expected error)" << std::endl
586 << "hooks result: " << std::endl << hooks
587 << std::endl;
588 std::cerr << "expected hooks result: " << std::endl
589 << expected_hooks << std::endl;
590 }
591 else if (debuglevel > 1) {
592 std::cerr << filename << ": failed" << std::endl;
593 }
594 retval = false;
595 }
596 }
597
598 if (retval) {
599 if (debuglevel > 5) {
600 std::cerr
601 << filename << ": succeeded (caught expected error)"
602 << std::endl << "error result: " << std::endl << error
603 << std::endl;
604
605 if (!printed_result) {
606 std::cerr
607 << "hooks result: " << std::endl << hooks
608 << std::endl;
609 }
610 }
611 else if (debuglevel > 4) {
612 std::cerr
613 << filename << ": succeeded (caught expected error)"
614 << std::endl << "error result: " << std::endl << error
615 << std::endl;
616 }
617 else if (debuglevel > 3) {
618 // caught the expected error message
619 std::cerr << filename << ": succeeded" << std::endl;
620 }
621 }
622 }
623 return retval;
624 }
625 else {
626 std::cerr
627 << filename << ": no information about expected results found"
628 << std::endl;
629 }
630 return false;
631}
632
633///////////////////////////////////////////////////////////////////////////////
634//
635// print the current version of this program
636//
637///////////////////////////////////////////////////////////////////////////////
638int
639testwave_app::print_version()
640{
641 // get time of last compilation of this file
642 boost::wave::util::time_conversion_helper compilation_time(__DATE__ " " __TIME__);
643
644 // calculate the number of days since Feb 12 2005
645 // (the day the testwave project was started)
646 std::tm first_day;
647
648 using namespace std; // some platforms have memset in namespace std
649 memset (s: &first_day, c: 0, n: sizeof(std::tm));
650 first_day.tm_mon = 1; // Feb
651 first_day.tm_mday = 12; // 12
652 first_day.tm_year = 105; // 2005
653
654 long seconds = long(std::difftime(time1: compilation_time.get_time(),
655 time0: std::mktime(tp: &first_day)));
656
657 std::cout
658 << TESTWAVE_VERSION_MAJOR << '.'
659 << TESTWAVE_VERSION_MINOR << '.'
660 << TESTWAVE_VERSION_SUBMINOR << '.'
661 << seconds/(3600*24) // get number of days from seconds
662 << std::endl;
663 return 0; // exit app
664}
665
666///////////////////////////////////////////////////////////////////////////////
667//
668// print the copyright statement
669//
670///////////////////////////////////////////////////////////////////////////////
671int
672testwave_app::print_copyright()
673{
674 char const *copyright[] = {
675 "",
676 "Testwave: A test driver for the Boost.Wave C++ preprocessor library",
677 "http://www.boost.org/",
678 "",
679 "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost",
680 "Software License, Version 1.0. (See accompanying file",
681 "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
682 0
683 };
684
685 for (int i = 0; 0 != copyright[i]; ++i)
686 std::cout << copyright[i] << std::endl;
687
688 return 0; // exit app
689}
690
691///////////////////////////////////////////////////////////////////////////////
692//
693// Read the given file into a string
694//
695///////////////////////////////////////////////////////////////////////////////
696bool
697testwave_app::read_file(std::string const& filename, std::string& instr)
698{
699 // open the given file and report error, if appropriate
700 std::ifstream instream(filename.c_str());
701 if (!instream.is_open()) {
702 std::cerr << "testwave: could not open input file: "
703 << filename << std::endl;
704 return false;
705 }
706 else if (9 == debuglevel) {
707 std::cerr << "read_file: succeeded to open input file: "
708 << filename << std::endl;
709 }
710 instream.unsetf(mask: std::ios::skipws);
711
712 // read the input file into a string
713
714#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
715 // this is known to be very slow for large files on some systems
716 std::copy (std::istream_iterator<char>(instream),
717 std::istream_iterator<char>(),
718 std::inserter(instr, instr.end()));
719#else
720 instr = std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
721 std::istreambuf_iterator<char>());
722#endif
723
724 if (9 == debuglevel) {
725 std::cerr << "read_file: succeeded to read input file: "
726 << filename << std::endl;
727 }
728 return true;
729}
730
731///////////////////////////////////////////////////////////////////////////////
732namespace {
733
734 std::string const& trim_whitespace(std::string& value)
735 {
736 std::string::size_type first = value.find_first_not_of(s: " \t");
737 if (std::string::npos == first)
738 value.clear();
739 else {
740 std::string::size_type last = value.find_last_not_of(s: " \t");
741 BOOST_ASSERT(std::string::npos != last);
742 value = value.substr(pos: first, n: last-first+1);
743 }
744 return value;
745 }
746}
747
748///////////////////////////////////////////////////////////////////////////////
749//
750// Extract special information from comments marked with the given letter
751//
752///////////////////////////////////////////////////////////////////////////////
753bool
754testwave_app::extract_special_information(std::string const& filename,
755 std::string const& instr, char flag, std::string& content)
756{
757 if (9 == debuglevel) {
758 std::cerr << "extract_special_information: extracting special information ('"
759 << flag << "') from input file: " << filename << std::endl;
760 }
761
762 // tokenize the input data into C++ tokens using the C++ lexer
763 typedef boost::wave::cpplexer::lex_token<> token_type;
764 typedef boost::wave::cpplexer::lex_iterator<token_type> lexer_type;
765 typedef token_type::position_type position_type;
766
767 boost::wave::language_support const lang_opts =
768 (boost::wave::language_support)(
769 boost::wave::support_option_variadics |
770 boost::wave::support_option_long_long |
771 boost::wave::support_option_no_character_validation |
772 boost::wave::support_option_convert_trigraphs |
773 boost::wave::support_option_insert_whitespace);
774
775 position_type pos(filename.c_str());
776 lexer_type it = lexer_type(instr.begin(), instr.end(), pos, lang_opts);
777 lexer_type end = lexer_type();
778
779 try {
780 // look for C or C++ comments starting with the special character
781 for (/**/; it != end; ++it) {
782 using namespace boost::wave;
783 token_id id = token_id(*it);
784 if (T_CCOMMENT == id) {
785 std::string value = (*it).get_value().c_str();
786 if (flag == value[2]) {
787 if (value.size() > 3 && '(' == value[3]) {
788 std::size_t p = value.find_first_of(s: ")");
789 if (std::string::npos == p) {
790 std::cerr
791 << "testwave: missing closing parenthesis in '"
792 << flag << "()' directive" << std::endl;
793 return false;
794 }
795 std::string source = value.substr(pos: 4, n: p-4);
796 std::string result, error, hooks;
797 bool pp_result;
798 std::tie(args&: pp_result, args: std::ignore) = preprocess_file(filename, instr: source,
799 result, error, hooks, expected_cfg_macro: "", single_line: true);
800 if (!pp_result) {
801 std::cerr
802 << "testwave: preprocessing error in '" << flag
803 << "()' directive: " << error << std::endl;
804 return false;
805 }
806
807 // include this text into the extracted information
808 // only if the result is not zero
809 using namespace std; // some system have atoi in namespace std
810 if (0 != atoi(nptr: result.c_str())) {
811 std::string thiscontent(value.substr(pos: p+1));
812 if (9 == debuglevel) {
813 std::cerr << "extract_special_information: extracted: "
814 << thiscontent << std::endl;
815 }
816 trim_whitespace(value&: thiscontent);
817 content += thiscontent;
818 }
819 }
820 else {
821 std::string thiscontent(value.substr(pos: 3, n: value.size()-5));
822 if (9 == debuglevel) {
823 std::cerr << "extract_special_information: extracted: "
824 << thiscontent << std::endl;
825 }
826 trim_whitespace(value&: thiscontent);
827 content += thiscontent;
828 }
829 }
830 }
831 else if (T_CPPCOMMENT == id) {
832 std::string value = (*it).get_value().c_str();
833 if (flag == value[2]) {
834 if (value.size() > 3 && '(' == value[3]) {
835 std::size_t p = value.find_first_of(s: ")");
836 if (std::string::npos == p) {
837 std::cerr
838 << "testwave: missing closing parenthesis in '"
839 << flag << "()' directive" << std::endl;
840 return false;
841 }
842 std::string source = value.substr(pos: 4, n: p-4);
843 std::string result, error, hooks;
844 bool pp_result;
845 bool suppressed;
846 std::tie(args&: pp_result, args&: suppressed) = preprocess_file(filename, instr: source,
847 result, error, hooks, expected_cfg_macro: "", single_line: true);
848 if (!pp_result) {
849 std::cerr
850 << "testwave: preprocessing error in '" << flag
851 << "()' directive: " << error << std::endl;
852 return false;
853 }
854
855 // include this text into the extracted information
856 // only if the result is not zero
857 using namespace std; // some system have atoi in namespace std
858 if (0 != atoi(nptr: result.c_str())) {
859 std::string thiscontent(value.substr(pos: (' ' == value[p+1]) ? p+2 : p+1));
860 if (9 == debuglevel) {
861 std::cerr << "extract_special_information: extracted: "
862 << thiscontent << std::endl;
863 }
864 trim_whitespace(value&: thiscontent);
865 content += thiscontent;
866 }
867 }
868 else {
869 std::string thiscontent(value.substr(pos: (' ' == value[3]) ? 4 : 3));
870 if (9 == debuglevel) {
871 std::cerr << "extract_special_information: extracted: "
872 << thiscontent;
873 }
874 trim_whitespace(value&: content);
875 content += thiscontent;
876 }
877 }
878 }
879 }
880 }
881 catch (boost::wave::cpplexer::lexing_exception const &e) {
882 // some lexing error
883 std::cerr
884 << e.file_name() << "(" << e.line_no() << "): "
885 << e.description() << std::endl;
886 return false;
887 }
888
889 if (9 == debuglevel) {
890 std::cerr << "extract_special_information: succeeded extracting special information ('"
891 << flag << "')" << std::endl;
892 }
893 return true;
894}
895
896///////////////////////////////////////////////////////////////////////////////
897//
898// Extract the expected output from the given input data
899//
900// The expected output has to be provided inside of special comments which
901// start with a capital 'R'. All such comments are concatenated and returned
902// through the parameter 'expected'.
903//
904///////////////////////////////////////////////////////////////////////////////
905inline bool
906testwave_app::extract_expected_output(std::string const& filename,
907 std::string const& instr, std::string& expected, std::string& expectedhooks)
908{
909 return extract_special_information(filename, instr, flag: 'R', content&: expected) &&
910 extract_special_information(filename, instr, flag: 'H', content&: expectedhooks);
911}
912
913///////////////////////////////////////////////////////////////////////////////
914//
915// Extracts the required preprocessing options from the given input data and
916// initialises the given Wave context object accordingly.
917// We allow the same (applicable) options to be used as are valid for the wave
918// driver executable.
919//
920///////////////////////////////////////////////////////////////////////////////
921template <typename Context>
922bool
923testwave_app::extract_options(std::string const& filename,
924 std::string const& instr, Context& ctx, bool single_line,
925 po::variables_map& vm)
926{
927 if (9 == debuglevel) {
928 std::cerr << "extract_options: extracting options" << std::endl;
929 }
930
931 // extract the required information from the comments flagged by a
932 // capital 'O'
933 std::string options;
934 if (!extract_special_information(filename, instr, flag: 'O', content&: options))
935 return false;
936
937 try {
938 // parse the configuration information into a program_options_description
939 // object
940 cmd_line_utils::read_config_options(debuglevel, indata: options, desc: desc_options, vm);
941 initialise_options(ctx, vm, single_line);
942 }
943 catch (std::exception const &e) {
944 std::cerr << filename << ": exception caught: " << e.what()
945 << std::endl;
946 return false;
947 }
948
949 if (9 == debuglevel) {
950 std::cerr << "extract_options: succeeded extracting options"
951 << std::endl;
952 }
953
954 return true;
955}
956
957template <typename Context>
958bool
959testwave_app::initialise_options(Context& ctx, po::variables_map const& vm,
960 bool single_line)
961{
962 if (9 == debuglevel) {
963 std::cerr << "initialise_options: initializing options" << std::endl;
964 }
965
966 if (vm.count(x: "skipped_token_hooks")) {
967 if (9 == debuglevel) {
968 std::cerr << "initialise_options: option: skipped_token_hooks" << std::endl;
969 }
970 ctx.get_hooks().set_skipped_token_hooks(true);
971 }
972
973// initialize the given context from the parsed options
974#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
975 // enable C99 mode, if appropriate (implies variadics)
976 if (vm.count(x: "c99")) {
977 if (9 == debuglevel) {
978 std::cerr << "initialise_options: option: c99" << std::endl;
979 }
980 ctx.set_language(
981 boost::wave::language_support(
982 boost::wave::support_c99
983 | boost::wave::support_option_emit_line_directives
984#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
985 | boost::wave::support_option_include_guard_detection
986#endif
987#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
988 | boost::wave::support_option_emit_pragma_directives
989#endif
990 | boost::wave::support_option_insert_whitespace
991 ));
992 }
993 else if (vm.count(x: "variadics")) {
994 // enable variadics and placemarkers, if appropriate
995 if (9 == debuglevel) {
996 std::cerr << "initialise_options: option: variadics" << std::endl;
997 }
998 ctx.set_language(boost::wave::enable_variadics(language: ctx.get_language()));
999 }
1000#endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
1001
1002#if BOOST_WAVE_SUPPORT_CPP0X
1003 if (vm.count(x: "c++11")) {
1004 ctx.set_language(
1005 boost::wave::language_support(
1006 boost::wave::support_cpp0x
1007 | boost::wave::support_option_convert_trigraphs
1008 | boost::wave::support_option_long_long
1009 | boost::wave::support_option_emit_line_directives
1010#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1011 | boost::wave::support_option_include_guard_detection
1012#endif
1013#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
1014 | boost::wave::support_option_emit_pragma_directives
1015#endif
1016 | boost::wave::support_option_insert_whitespace
1017 ));
1018 } else {
1019 if (9 == debuglevel) {
1020 std::cerr << "initialise_options: option: c++11" << std::endl;
1021 }
1022 }
1023#endif
1024
1025#if BOOST_WAVE_SUPPORT_CPP1Z
1026 if (vm.count(x: "c++17")) {
1027 ctx.set_language(
1028 boost::wave::language_support(
1029 boost::wave::support_cpp1z
1030#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
1031 | boost::wave::support_option_has_include
1032#endif
1033 | boost::wave::support_option_convert_trigraphs
1034 | boost::wave::support_option_long_long
1035 | boost::wave::support_option_emit_line_directives
1036#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1037 | boost::wave::support_option_include_guard_detection
1038#endif
1039#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
1040 | boost::wave::support_option_emit_pragma_directives
1041#endif
1042 | boost::wave::support_option_insert_whitespace
1043 ));
1044 } else {
1045 if (9 == debuglevel) {
1046 std::cerr << "initialise_options: option: c++17" << std::endl;
1047 }
1048 }
1049
1050#endif
1051
1052#if BOOST_WAVE_SUPPORT_CPP2A
1053 if (vm.count(x: "c++20")) {
1054 ctx.set_language(
1055 boost::wave::language_support(
1056 boost::wave::support_cpp2a
1057#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
1058 | boost::wave::support_option_has_include
1059#endif
1060#if BOOST_WAVE_SUPPORT_VA_OPT != 0
1061 | boost::wave::support_option_va_opt
1062#endif
1063 | boost::wave::support_option_convert_trigraphs
1064 | boost::wave::support_option_long_long
1065 | boost::wave::support_option_emit_line_directives
1066 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1067 | boost::wave::support_option_include_guard_detection
1068 #endif
1069 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
1070 | boost::wave::support_option_emit_pragma_directives
1071 #endif
1072 | boost::wave::support_option_insert_whitespace
1073 ));
1074
1075 if (9 == debuglevel) {
1076 std::cerr << "initialise_options: option: c++20" << std::endl;
1077 }
1078 }
1079#endif
1080
1081 // enable long_long mode, if appropriate
1082 if (vm.count(x: "long_long")) {
1083 if (9 == debuglevel) {
1084 std::cerr << "initialise_options: option: long_long" << std::endl;
1085 }
1086 ctx.set_language(boost::wave::enable_long_long(language: ctx.get_language()));
1087 }
1088
1089 // enable preserving comments mode, if appropriate
1090 if (vm.count(x: "preserve")) {
1091 if (9 == debuglevel) {
1092 std::cerr << "initialise_options: option: preserve" << std::endl;
1093 }
1094 ctx.set_language(
1095 boost::wave::enable_preserve_comments(language: ctx.get_language()));
1096 }
1097
1098 // disable automatic include guard detection
1099 if (vm.count(x: "noguard")) {
1100 if (9 == debuglevel) {
1101 std::cerr << "initialise_options: option: guard" << std::endl;
1102 }
1103 ctx.set_language(
1104 boost::wave::enable_include_guard_detection(language: ctx.get_language(), enable: false));
1105 }
1106
1107 // enable trigraph conversion
1108 if (9 == debuglevel) {
1109 std::cerr << "initialise_options: option: convert_trigraphs" << std::endl;
1110 }
1111 ctx.set_language(boost::wave::enable_convert_trigraphs(language: ctx.get_language()));
1112
1113 // enable single_line mode
1114 if (single_line) {
1115 if (9 == debuglevel) {
1116 std::cerr << "initialise_options: option: single_line" << std::endl;
1117 }
1118 ctx.set_language(boost::wave::enable_single_line(language: ctx.get_language()));
1119 ctx.set_language(boost::wave::enable_emit_line_directives(language: ctx.get_language(), enable: false));
1120 }
1121
1122 // add include directories to the system include search paths
1123 if (vm.count(x: "sysinclude")) {
1124 std::vector<std::string> const& syspaths =
1125 variables_map_as(v: vm["sysinclude"], (std::vector<std::string> *)NULL);
1126
1127 std::vector<std::string>::const_iterator end = syspaths.end();
1128 for (std::vector<std::string>::const_iterator cit = syspaths.begin();
1129 cit != end; ++cit)
1130 {
1131 std::string full(*cit);
1132 got_expected_result(filename: ctx.get_current_filename(),result: "",expected&: full);
1133
1134 if (9 == debuglevel) {
1135 std::cerr << "initialise_options: option: -S" << *cit
1136 << std::endl;
1137 }
1138 ctx.add_sysinclude_path(full.c_str());
1139 }
1140 }
1141
1142 // add include directories to the user include search paths
1143 if (vm.count(x: "include")) {
1144 cmd_line_utils::include_paths const &ip =
1145 variables_map_as(v: vm["include"], (cmd_line_utils::include_paths*)NULL);
1146 std::vector<std::string>::const_iterator end = ip.paths.end();
1147
1148 for (std::vector<std::string>::const_iterator cit = ip.paths.begin();
1149 cit != end; ++cit)
1150 {
1151 std::string full(*cit);
1152 got_expected_result(filename: ctx.get_current_filename(),result: "",expected&: full);
1153
1154 if (9 == debuglevel) {
1155 std::cerr << "initialise_options: option: -I" << *cit
1156 << std::endl;
1157 }
1158 ctx.add_include_path(full.c_str());
1159 }
1160
1161 // if on the command line was given -I- , this has to be propagated
1162 if (ip.seen_separator) {
1163 if (9 == debuglevel) {
1164 std::cerr << "initialise_options: option: -I-" << std::endl;
1165 }
1166 ctx.set_sysinclude_delimiter();
1167 }
1168
1169 // add system include directories to the include path
1170 std::vector<std::string>::const_iterator sysend = ip.syspaths.end();
1171 for (std::vector<std::string>::const_iterator syscit = ip.syspaths.begin();
1172 syscit != sysend; ++syscit)
1173 {
1174 if (9 == debuglevel) {
1175 std::cerr << "initialise_options: option: -S" << *syscit
1176 << std::endl;
1177 }
1178 ctx.add_sysinclude_path((*syscit).c_str());
1179 }
1180 }
1181
1182 // add additional defined macros
1183 if (vm.count(x: "define")) {
1184 std::vector<std::string> const &macros =
1185 variables_map_as(v: vm["define"], (std::vector<std::string>*)NULL);
1186 std::vector<std::string>::const_iterator end = macros.end();
1187 for (std::vector<std::string>::const_iterator cit = macros.begin();
1188 cit != end; ++cit)
1189 {
1190 if (9 == debuglevel) {
1191 std::cerr << "initialise_options: option: -D" << *cit
1192 << std::endl;
1193 }
1194 ctx.add_macro_definition(*cit, true);
1195 }
1196 }
1197
1198 // add additional predefined macros
1199 if (vm.count(x: "predefine")) {
1200 std::vector<std::string> const &predefmacros =
1201 variables_map_as(v: vm["predefine"], (std::vector<std::string>*)NULL);
1202 std::vector<std::string>::const_iterator end = predefmacros.end();
1203 for (std::vector<std::string>::const_iterator cit = predefmacros.begin();
1204 cit != end; ++cit)
1205 {
1206 if (9 == debuglevel) {
1207 std::cerr << "initialise_options: option: -P" << *cit
1208 << std::endl;
1209 }
1210 ctx.add_macro_definition(*cit, true);
1211 }
1212 }
1213
1214 // undefine specified macros
1215 if (vm.count(x: "undefine")) {
1216 std::vector<std::string> const &undefmacros =
1217 variables_map_as(v: vm["undefine"], (std::vector<std::string>*)NULL);
1218 std::vector<std::string>::const_iterator end = undefmacros.end();
1219 for (std::vector<std::string>::const_iterator cit = undefmacros.begin();
1220 cit != end; ++cit)
1221 {
1222 if (9 == debuglevel) {
1223 std::cerr << "initialise_options: option: -U" << *cit
1224 << std::endl;
1225 }
1226 ctx.remove_macro_definition(*cit);
1227 }
1228 }
1229
1230 // maximal include nesting depth
1231 if (vm.count(x: "nesting")) {
1232 int max_depth = variables_map_as(v: vm["nesting"], (int*)NULL);
1233 if (max_depth < 1 || max_depth > 100000) {
1234 std::cerr << "testwave: bogus maximal include nesting depth: "
1235 << max_depth << std::endl;
1236 return false;
1237 }
1238 else if (9 == debuglevel) {
1239 std::cerr << "initialise_options: option: -n" << max_depth
1240 << std::endl;
1241 }
1242 ctx.set_max_include_nesting_depth(max_depth);
1243 }
1244
1245 if (9 == debuglevel) {
1246 std::cerr << "initialise_options: succeeded to initialize options"
1247 << std::endl;
1248 }
1249 return true;
1250}
1251
1252///////////////////////////////////////////////////////////////////////////////
1253// construct a SIZEOF macro definition string and predefine this macro
1254template <typename Context>
1255inline bool
1256testwave_app::add_sizeof_definition(Context& ctx, char const *name, int value)
1257{
1258 BOOST_WAVETEST_OSSTREAM strm;
1259 strm << "__TESTWAVE_SIZEOF_" << name << "__=" << value;
1260
1261 std::string macro(BOOST_WAVETEST_GETSTRING(strm));
1262 if (!ctx.add_macro_definition(macro, true)) {
1263 std::cerr << "testwave: failed to predefine macro: " << macro
1264 << std::endl;
1265 return false;
1266 }
1267 else if (9 == debuglevel) {
1268 std::cerr << "add_sizeof_definition: predefined macro: " << macro
1269 << std::endl;
1270 }
1271 return true;
1272}
1273
1274// construct a MIN macro definition string and predefine this macro
1275template <typename T, typename Context>
1276inline bool
1277testwave_app::add_min_definition(Context& ctx, char const *name)
1278{
1279 BOOST_WAVETEST_OSSTREAM strm;
1280 if (!std::numeric_limits<T>::is_signed) {
1281 strm << "__TESTWAVE_" << name << "_MIN__="
1282 << "0x" << std::hex
1283 << (std::numeric_limits<T>::min)() << "U";
1284 }
1285 else {
1286 strm << "__TESTWAVE_" << name << "_MIN__=( "
1287 << (std::numeric_limits<T>::min)()+1 << "-1)";
1288 }
1289
1290 std::string macro(BOOST_WAVETEST_GETSTRING(strm));
1291 if (!ctx.add_macro_definition(macro, true)) {
1292 std::cerr << "testwave: failed to predefine macro: " << macro
1293 << std::endl;
1294 return false;
1295 }
1296 else if (9 == debuglevel) {
1297 std::cerr << "add_min_definition: predefined macro: " << macro
1298 << std::endl;
1299 }
1300 return true;
1301}
1302
1303// construct a MAX macro definition string and predefine this macro
1304template <typename T, typename Context>
1305inline bool
1306testwave_app::add_max_definition(Context& ctx, char const *name)
1307{
1308 BOOST_WAVETEST_OSSTREAM strm;
1309 if (!std::numeric_limits<T>::is_signed) {
1310 strm << "__TESTWAVE_" << name << "_MAX__="
1311 << "0x" << std::hex
1312 << (std::numeric_limits<T>::max)() << "U";
1313 }
1314 else {
1315 strm << "__TESTWAVE_" << name << "_MAX__="
1316 << (std::numeric_limits<T>::max)();
1317 }
1318
1319 std::string macro(BOOST_WAVETEST_GETSTRING(strm));
1320 if (!ctx.add_macro_definition(macro, true)) {
1321 std::cerr << "testwave: failed to predefine macro: " << macro
1322 << std::endl;
1323 return false;
1324 }
1325 else if (9 == debuglevel) {
1326 std::cerr << "add_max_definition: predefined macro: " << macro
1327 << std::endl;
1328 }
1329 return true;
1330}
1331
1332// Predefine __TESTWAVE_HAS_STRICT_LEXER__
1333template <typename Context>
1334inline bool
1335testwave_app::add_strict_lexer_definition(Context& ctx)
1336{
1337 std::string macro("__TESTWAVE_HAS_STRICT_LEXER__=1");
1338 if (!ctx.add_macro_definition(macro, true)) {
1339 std::cerr << "testwave: failed to predefine macro: " << macro
1340 << std::endl;
1341 return false;
1342 }
1343 else if (9 == debuglevel) {
1344 std::cerr << "add_strict_lexer_definition: predefined macro: " << macro
1345 << std::endl;
1346 }
1347 return true;
1348}
1349
1350#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1351// Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1352template <typename Context>
1353inline bool
1354testwave_app::add_support_ms_extensions_definition(Context& ctx)
1355{
1356 std::string macro("__TESTWAVE_SUPPORT_MS_EXTENSIONS__=1");
1357 if (!ctx.add_macro_definition(macro, true)) {
1358 std::cerr << "testwave: failed to predefine macro: " << macro
1359 << std::endl;
1360 return false;
1361 }
1362 else if (9 == debuglevel) {
1363 std::cerr << "add_support_ms_extensions_definition: predefined macro: "
1364 << macro
1365 << std::endl;
1366 }
1367 return true;
1368}
1369#endif
1370
1371///////////////////////////////////////////////////////////////////////////////
1372//
1373// Add special predefined macros to the context object.
1374//
1375// This adds a lot of macros to the test environment, which allows to adjust
1376// the test cases for different platforms.
1377//
1378///////////////////////////////////////////////////////////////////////////////
1379template <typename Context>
1380bool
1381testwave_app::add_predefined_macros(Context& ctx)
1382{
1383 // add the __TESTWAVE_SIZEOF_<type>__ macros
1384 if (!add_sizeof_definition(ctx, "CHAR", sizeof(char)) ||
1385 !add_sizeof_definition(ctx, "SHORT", sizeof(short)) ||
1386 !add_sizeof_definition(ctx, "INT", sizeof(int)) ||
1387#if defined(BOOST_HAS_LONG_LONG)
1388 !add_sizeof_definition(ctx, "LONGLONG", sizeof(boost::long_long_type)) ||
1389#endif
1390 !add_sizeof_definition(ctx, "LONG", sizeof(long)))
1391 {
1392 std::cerr << "testwave: failed to add a predefined macro (SIZEOF)."
1393 << std::endl;
1394 return false;
1395 }
1396
1397 // add the __TESTWAVE_<type>_MIN__ macros
1398 if (/*!add_min_definition<char>(ctx, "CHAR") ||*/
1399 /*!add_min_definition<unsigned char>(ctx, "UCHAR") ||*/
1400 !add_min_definition<short>(ctx, "SHORT") ||
1401 !add_min_definition<unsigned short>(ctx, "USHORT") ||
1402 !add_min_definition<int>(ctx, "INT") ||
1403 !add_min_definition<unsigned int>(ctx, "UINT") ||
1404#if defined(BOOST_HAS_LONG_LONG)
1405 !add_min_definition<boost::long_long_type>(ctx, "LONGLONG") ||
1406 !add_min_definition<boost::ulong_long_type>(ctx, "ULONGLONG") ||
1407#endif
1408 !add_min_definition<long>(ctx, "LONG") ||
1409 !add_min_definition<unsigned long>(ctx, "ULONG"))
1410 {
1411 std::cerr << "testwave: failed to add a predefined macro (MIN)."
1412 << std::endl;
1413 }
1414
1415 // add the __TESTWAVE_<type>_MAX__ macros
1416 if (/*!add_max_definition<char>(ctx, "CHAR") ||*/
1417 /*!add_max_definition<unsigned char>(ctx, "UCHAR") ||*/
1418 !add_max_definition<short>(ctx, "SHORT") ||
1419 !add_max_definition<unsigned short>(ctx, "USHORT") ||
1420 !add_max_definition<int>(ctx, "INT") ||
1421 !add_max_definition<unsigned int>(ctx, "UINT") ||
1422#if defined(BOOST_HAS_LONG_LONG)
1423 !add_max_definition<boost::long_long_type>(ctx, "LONGLONG") ||
1424 !add_max_definition<boost::ulong_long_type>(ctx, "ULONGLONG") ||
1425#endif
1426 !add_max_definition<long>(ctx, "LONG") ||
1427 !add_max_definition<unsigned long>(ctx, "ULONG"))
1428 {
1429 std::cerr << "testwave: failed to add a predefined macro (MAX)."
1430 << std::endl;
1431 }
1432
1433#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS
1434// Predefine __TESTWAVE_SUPPORT_MS_EXTENSIONS__
1435 if (!add_support_ms_extensions_definition(ctx))
1436 {
1437 std::cerr << "testwave: failed to add a predefined macro "
1438 "(__TESTWAVE_SUPPORT_MS_EXTENSIONS__)."
1439 << std::endl;
1440 }
1441#endif
1442
1443#if BOOST_WAVE_USE_STRICT_LEXER != 0
1444 return add_strict_lexer_definition(ctx);
1445#else
1446 return true;
1447#endif
1448}
1449
1450///////////////////////////////////////////////////////////////////////////////
1451//
1452// Preprocess the given input data and return the generated output through
1453// the parameter 'result'.
1454//
1455///////////////////////////////////////////////////////////////////////////////
1456std::tuple<bool, bool> // pass/fail + suppressed (or not) by absent macro
1457testwave_app::preprocess_file(std::string filename, std::string const& instr,
1458 std::string& result, std::string& error, std::string& hooks,
1459 std::string const& expected_cfg_macro, bool single_line)
1460{
1461 // create the wave::context object and initialize it from the file to
1462 // preprocess (may contain options inside of special comments)
1463 typedef boost::wave::cpplexer::lex_token<> token_type;
1464 typedef boost::wave::cpplexer::lex_iterator<token_type> lexer_type;
1465 typedef boost::wave::context<
1466 std::string::const_iterator, lexer_type,
1467 boost::wave::iteration_context_policies::load_file_to_string,
1468 collect_hooks_information<token_type> >
1469 context_type;
1470
1471 if (9 == debuglevel) {
1472 std::cerr << "preprocess_file: preprocessing input file: " << filename
1473 << std::endl;
1474 }
1475
1476 try {
1477 // create preprocessing context
1478 context_type ctx(instr.begin(), instr.end(), filename.c_str(),
1479 collect_hooks_information<token_type>(hooks));
1480
1481 // initialize the context from the options given on the command line
1482 if (!initialise_options(ctx, vm: global_vm, single_line))
1483 return std::make_tuple(args: false, args: false);
1484
1485 // extract the options from the input data and initialize the context
1486 boost::program_options::variables_map local_vm;
1487 if (!extract_options(filename, instr, ctx, single_line, vm&: local_vm))
1488 return std::make_tuple(args: false, args: false);
1489
1490 // add special predefined macros
1491 if (!add_predefined_macros(ctx))
1492 return std::make_tuple(args: false, args: false);
1493
1494 if (!expected_cfg_macro.empty() &&
1495 !ctx.is_defined_macro(str: expected_cfg_macro))
1496 {
1497 // skip this test as it is for a disabled configuration
1498 return std::make_tuple(args: true, args: true);
1499 }
1500
1501 // preprocess the input, loop over all generated tokens collecting the
1502 // generated text
1503 context_type::iterator_type it = ctx.begin();
1504 context_type::iterator_type end = ctx.end();
1505
1506 if (local_vm.count(x: "forceinclude")) {
1507 // add the filenames to force as include files in _reverse_ order
1508 // the second parameter 'is_last' of the force_include function should
1509 // be set to true for the last (first given) file.
1510 std::vector<std::string> const& force =
1511 local_vm["forceinclude"].as<std::vector<std::string> >();
1512 std::vector<std::string>::const_reverse_iterator rend = force.rend();
1513 for (std::vector<std::string>::const_reverse_iterator cit = force.rbegin();
1514 cit != rend; /**/)
1515 {
1516 std::string forceinclude(*cit);
1517 if (9 == debuglevel) {
1518 std::cerr << "preprocess_file: option: forceinclude ("
1519 << forceinclude << ")" << std::endl;
1520 }
1521 it.force_include(path_: forceinclude.c_str(), is_last: ++cit == rend);
1522 }
1523 }
1524
1525 // perform actual preprocessing
1526 for (/**/; it != end; ++it)
1527 {
1528 using namespace boost::wave;
1529
1530 if (T_PP_LINE == token_id(*it)) {
1531 // special handling of the whole #line directive is required to
1532 // allow correct file name matching
1533 if (!handle_line_directive(it, end, result))
1534 return std::make_tuple(args: false, args: false); // unexpected eof
1535 }
1536 else {
1537 // add the value of the current token
1538 result = result + (*it).get_value().c_str();
1539 }
1540 }
1541 error.clear();
1542 }
1543 catch (boost::wave::cpplexer::lexing_exception const& e) {
1544 // some lexer error
1545 BOOST_WAVETEST_OSSTREAM strm;
1546 std::string filename = e.file_name();
1547 strm
1548 << handle_filepath(name: filename) << "(" << e.line_no() << "): "
1549 << e.description() << std::endl;
1550
1551 error = BOOST_WAVETEST_GETSTRING(strm);
1552 return std::make_tuple(args: false, args: false);
1553 }
1554 catch (boost::wave::cpp_exception const& e) {
1555 // some preprocessing error
1556 BOOST_WAVETEST_OSSTREAM strm;
1557 std::string filename = e.file_name();
1558 strm
1559 << handle_filepath(name: filename) << "(" << e.line_no() << "): "
1560 << e.description() << std::endl;
1561
1562 error = BOOST_WAVETEST_GETSTRING(strm);
1563 return std::make_tuple(args: false, args: false);
1564 }
1565
1566 if (9 == debuglevel) {
1567 std::cerr << "preprocess_file: succeeded to preprocess input file: "
1568 << filename << std::endl;
1569 }
1570
1571 return std::make_tuple(args: true, args: false);
1572}
1573

source code of boost/libs/wave/test/testwave/testwave_app.cpp