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