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