1 /*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
6 Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost
7 Software License, Version 1.0. (See accompanying file
8 LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 =============================================================================*/
11 #define BOOST_WAVE_SERIALIZATION 0 // enable serialization
12 #define BOOST_WAVE_BINARY_SERIALIZATION 0 // use binary archives
13 #define BOOST_WAVE_XML_SERIALIZATION 1 // use XML archives
15 #include "cpp.hpp" // global configuration
17 ///////////////////////////////////////////////////////////////////////////////
18 // Include additional Boost libraries
19 #include <boost/filesystem/path.hpp>
20 #include <boost/filesystem/convenience.hpp>
21 #include <boost/timer.hpp>
22 #include <boost/any.hpp>
23 #include <boost/algorithm/cxx11/any_of.hpp>
24 #include <boost/algorithm/string/join.hpp>
25 #include <boost/range/algorithm/find.hpp>
26 #include <boost/range/end.hpp>
27 #include <boost/foreach.hpp>
29 ///////////////////////////////////////////////////////////////////////////////
30 // Include Wave itself
31 #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
38 ///////////////////////////////////////////////////////////////////////////////
39 // Include serialization support, if requested
40 #if BOOST_WAVE_SERIALIZATION != 0
41 #include <boost/serialization/serialization.hpp>
42 #if BOOST_WAVE_BINARY_SERIALIZATION != 0
43 #include <boost/archive/binary_iarchive.hpp>
44 #include <boost/archive/binary_oarchive.hpp>
45 typedef boost::archive::binary_iarchive iarchive
;
46 typedef boost::archive::binary_oarchive oarchive
;
47 #elif BOOST_WAVE_XML_SERIALIZATION != 0
48 #include <boost/archive/xml_iarchive.hpp>
49 #include <boost/archive/xml_oarchive.hpp>
50 typedef boost::archive::xml_iarchive iarchive
;
51 typedef boost::archive::xml_oarchive oarchive
;
53 #include <boost/archive/text_iarchive.hpp>
54 #include <boost/archive/text_oarchive.hpp>
55 typedef boost::archive::text_iarchive iarchive
;
56 typedef boost::archive::text_oarchive oarchive
;
60 ///////////////////////////////////////////////////////////////////////////////
61 // Include the context policies to use
62 #include "trace_macro_expansion.hpp"
64 ///////////////////////////////////////////////////////////////////////////////
65 // Include lexer specifics, import lexer names
66 #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
67 #include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
70 ///////////////////////////////////////////////////////////////////////////////
71 // Include the grammar definitions, if these shouldn't be compiled separately
72 // (ATTENTION: _very_ large compilation times!)
73 #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
74 #include <boost/wave/grammars/cpp_intlit_grammar.hpp>
75 #include <boost/wave/grammars/cpp_chlit_grammar.hpp>
76 #include <boost/wave/grammars/cpp_grammar.hpp>
77 #include <boost/wave/grammars/cpp_expression_grammar.hpp>
78 #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
79 #include <boost/wave/grammars/cpp_defined_grammar.hpp>
82 ///////////////////////////////////////////////////////////////////////////////
83 // Import required names
84 using namespace boost::spirit::classic
;
94 using std::istreambuf_iterator
;
96 ///////////////////////////////////////////////////////////////////////////////
98 // This application uses the lex_iterator and lex_token types predefined
99 // with the Wave library, but it is possible to use your own types.
101 // You may want to have a look at the other samples to see how this is
102 // possible to achieve.
103 typedef boost::wave::cpplexer::lex_token
<> token_type
;
104 typedef boost::wave::cpplexer::lex_iterator
<token_type
>
107 // The C++ preprocessor iterators shouldn't be constructed directly. They
108 // are to be generated through a boost::wave::context<> object. This
109 // boost::wave::context object is additionally to be used to initialize and
110 // define different parameters of the actual preprocessing.
111 typedef boost::wave::context
<
112 std::string::iterator
, lex_iterator_type
,
113 boost::wave::iteration_context_policies::load_file_to_string
,
114 trace_macro_expansion
<token_type
> >
117 ///////////////////////////////////////////////////////////////////////////////
118 // print the current version
119 std::string
get_version()
121 std::string
version (context_type::get_version_string());
122 version
= version
.substr(1, version
.size()-2); // strip quotes
123 version
+= std::string(" (" CPP_VERSION_DATE_STR
")"); // add date
127 ///////////////////////////////////////////////////////////////////////////////
128 // print the current version for interactive sessions
129 int print_interactive_version()
131 cout
<< "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library" << endl
;
132 cout
<< "Version: " << get_version() << endl
;
136 ///////////////////////////////////////////////////////////////////////////////
137 // print the copyright statement
138 int print_copyright()
140 char const *copyright
[] = {
142 "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library",
143 "http://www.boost.org/",
145 "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost",
146 "Software License, Version 1.0. (See accompanying file",
147 "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
151 for (int i
= 0; 0 != copyright
[i
]; ++i
)
152 cout
<< copyright
[i
] << endl
;
154 return 0; // exit app
157 ///////////////////////////////////////////////////////////////////////////////
158 // forward declarations only
159 namespace cmd_line_utils
164 namespace boost
{ namespace program_options
{
166 void validate(boost::any
&v
, std::vector
<std::string
> const &s
,
167 cmd_line_utils::include_paths
*, long);
169 }} // boost::program_options
171 ///////////////////////////////////////////////////////////////////////////////
172 #include <boost/program_options.hpp>
174 namespace po
= boost::program_options
;
175 namespace fs
= boost::filesystem
;
177 ///////////////////////////////////////////////////////////////////////////////
178 namespace cmd_line_utils
{
179 // Additional command line parser which interprets '@something' as an
180 // option "config-file" with the value "something".
181 inline pair
<std::string
, std::string
>
182 at_option_parser(std::string
const&s
)
185 return std::make_pair(std::string("config-file"), s
.substr(1));
187 return pair
<std::string
, std::string
>();
190 // class, which keeps include file information read from the command line
191 class include_paths
{
193 include_paths() : seen_separator(false) {}
195 vector
<std::string
> paths
; // stores user paths
196 vector
<std::string
> syspaths
; // stores system paths
197 bool seen_separator
; // command line contains a '-I-' option
199 // Function which validates additional tokens from command line.
201 validate(boost::any
&v
, vector
<std::string
> const &tokens
)
204 v
= boost::any(include_paths());
206 include_paths
*p
= boost::any_cast
<include_paths
>(&v
);
209 // Assume only one path per '-I' occurrence.
210 std::string
const& t
= po::validators::get_single_string(tokens
);
212 // found -I- option, so switch behaviour
213 p
->seen_separator
= true;
215 else if (p
->seen_separator
) {
216 // store this path as a system path
217 p
->syspaths
.push_back(t
);
220 // store this path as an user path
221 p
->paths
.push_back(t
);
226 // Read all options from a given config file, parse and add them to the
227 // given variables_map
228 bool read_config_file_options(std::string
const &filename
,
229 po::options_description
const &desc
, po::variables_map
&vm
,
230 bool may_fail
= false)
232 std::ifstream
ifs(filename
.c_str());
234 if (!ifs
.is_open()) {
237 << ": command line warning: config file not found"
243 vector
<std::string
> options
;
246 while (std::getline(ifs
, line
)) {
248 std::string::size_type pos
= line
.find_first_not_of(" \t");
249 if (pos
== std::string::npos
)
252 // skip comment lines
253 if ('#' != line
[pos
]) {
254 // strip leading and trailing whitespace
255 std::string::size_type endpos
= line
.find_last_not_of(" \t");
256 BOOST_ASSERT(endpos
!= std::string::npos
);
257 options
.push_back(line
.substr(pos
, endpos
-pos
+1));
261 if (options
.size() > 0) {
262 using namespace boost::program_options::command_line_style
;
263 po::store(po::command_line_parser(options
)
264 .options(desc
).style(unix_style
).run(), vm
);
270 // predicate to extract all positional arguments from the command line
272 bool operator()(po::option
const &opt
)
274 return (opt
.position_key
== -1) ? true : false;
278 // trim quotes from path names, if any
279 std::string
trim_quotes(std::string
const& file
)
281 if (('"' == file
[0] || '\'' == file
[0]) && file
[0] == file
[file
.size()-1])
283 return file
.substr(1, file
.size()-2);
288 ///////////////////////////////////////////////////////////////////////////////
291 ///////////////////////////////////////////////////////////////////////////////
293 // Special validator overload, which allows to handle the -I- syntax for
294 // switching the semantics of an -I option.
296 ///////////////////////////////////////////////////////////////////////////////
297 namespace boost
{ namespace program_options
{
299 void validate(boost::any
&v
, std::vector
<std::string
> const &s
,
300 cmd_line_utils::include_paths
*, long)
302 cmd_line_utils::include_paths::validate(v
, s
);
305 }} // namespace boost::program_options
307 ///////////////////////////////////////////////////////////////////////////////
310 class auto_stop_watch
: public stop_watch
313 auto_stop_watch(std::ostream
&outstrm_
)
314 : print_time(false), outstrm(outstrm_
)
321 outstrm
<< "Elapsed time: "
322 << this->format_elapsed_time()
327 void set_print_time(bool print_time_
)
329 print_time
= print_time_
;
334 std::ostream
&outstrm
;
337 ///////////////////////////////////////////////////////////////////////////
339 report_iostate_error(std::ios::iostate state
)
341 BOOST_ASSERT(state
& (std::ios::badbit
| std::ios::failbit
| std::ios::eofbit
));
343 if (state
& std::ios::badbit
) {
344 result
+= " the reported problem was: "
345 "loss of integrity of the stream buffer\n";
347 if (state
& std::ios::failbit
) {
348 result
+= " the reported problem was: "
349 "an operation was not processed correctly\n";
351 if (state
& std::ios::eofbit
) {
352 result
+= " the reported problem was: "
353 "end-of-file while writing to the stream\n";
358 ///////////////////////////////////////////////////////////////////////////
359 // Retrieve the position of a macro definition
360 template <typename Context
>
362 get_macro_position(Context
&ctx
,
363 typename
Context::token_type::string_type
const& name
,
364 typename
Context::position_type
&pos
)
366 bool has_parameters
= false;
367 bool is_predefined
= false;
368 std::vector
<typename
Context::token_type
> parameters
;
369 typename
Context::token_sequence_type definition
;
371 return ctx
.get_macro_definition(name
, has_parameters
, is_predefined
,
372 pos
, parameters
, definition
);
375 ///////////////////////////////////////////////////////////////////////////
376 // Generate some meaningful error messages
377 template <typename Exception
>
379 report_error_message(Exception
const &e
, bool treat_warnings_as_error
)
381 // default error reporting
383 << e
.file_name() << ":" << e
.line_no() << ":" << e
.column_no()
384 << ": " << e
.description() << endl
;
386 // errors count as one
387 return (treat_warnings_as_error
||
388 e
.get_severity() == boost::wave::util::severity_error
||
389 e
.get_severity() == boost::wave::util::severity_fatal
) ? 1 : 0;
392 template <typename Context
>
394 report_error_message(Context
&ctx
, boost::wave::cpp_exception
const &e
,
395 bool treat_warnings_as_error
)
397 // default error reporting
398 int result
= report_error_message(e
, treat_warnings_as_error
);
400 using boost::wave::preprocess_exception
;
401 switch(e
.get_errorcode()) {
402 case preprocess_exception::macro_redefinition
:
404 // report the point of the initial macro definition
405 typename
Context::position_type pos
;
406 if (get_macro_position(ctx
, e
.get_related_name(), pos
)) {
409 << preprocess_exception::severity_text(e
.get_severity())
410 << ": this is the location of the previous definition."
415 << e
.file_name() << ":" << e
.line_no() << ":"
416 << e
.column_no() << ": "
417 << preprocess_exception::severity_text(e
.get_severity())
418 << ": not able to retrieve the location of the previous "
419 << "definition." << endl
;
431 ///////////////////////////////////////////////////////////////////////////
432 // Read one logical line of text
434 read_a_line (std::istream
&instream
, std::string
&instring
)
439 std::getline(instream
, line
);
440 if (instream
.rdstate() & std::ios::failbit
)
441 return false; // nothing to do
444 if (line
.find_last_of('\\') == line
.size()-1)
447 instring
+= line
+ '\n';
452 ///////////////////////////////////////////////////////////////////////////
453 // Load and save the internal tables of the wave::context object
454 template <typename Context
>
456 load_state(po::variables_map
const &vm
, Context
&ctx
)
458 #if BOOST_WAVE_SERIALIZATION != 0
460 if (vm
.count("state") > 0) {
461 fs::path
state_file (
462 boost::wave::util::create_path(vm
["state"].as
<std::string
>()));
463 if (state_file
== "-")
464 state_file
= boost::wave::util::create_path("wave.state");
466 std::ios::openmode mode
= std::ios::in
;
468 #if BOOST_WAVE_BINARY_SERIALIZATION != 0
469 mode
= (std::ios::openmode
)(mode
| std::ios::binary
);
471 std::ifstream
ifs (state_file
.string().c_str(), mode
);
473 using namespace boost::serialization
;
477 ia
>> make_nvp("version", version
); // load version
478 if (version
== CPP_VERSION_FULL_STR
)
479 ia
>> make_nvp("state", ctx
); // load the internal tables from disc
481 cerr
<< "wave: detected version mismatch while loading state, state was not loaded." << endl
;
482 cerr
<< " loaded version: " << version
<< endl
;
483 cerr
<< " expected version: " << CPP_VERSION_FULL_STR
<< endl
;
488 catch (boost::archive::archive_exception
const& e
) {
489 cerr
<< "wave: error while loading state: "
492 catch (boost::wave::preprocess_exception
const& e
) {
493 cerr
<< "wave: error while loading state: "
494 << e
.description() << endl
;
499 template <typename Context
>
501 save_state(po::variables_map
const &vm
, Context
const &ctx
)
503 #if BOOST_WAVE_SERIALIZATION != 0
505 if (vm
.count("state") > 0) {
506 fs::path
state_file (boost::wave::util::create_path(
507 vm
["state"].as
<std::string
>()));
508 if (state_file
== "-")
509 state_file
= boost::wave::util::create_path("wave.state");
511 std::ios::openmode mode
= std::ios::out
;
513 #if BOOST_WAVE_BINARY_SERIALIZATION != 0
514 mode
= (std::ios::openmode
)(mode
| std::ios::binary
);
516 ofstream
ofs(state_file
.string().c_str(), mode
);
517 if (!ofs
.is_open()) {
518 cerr
<< "wave: could not open state file for writing: "
519 << state_file
.string() << endl
;
523 using namespace boost::serialization
;
525 std::string
version(CPP_VERSION_FULL_STR
);
526 oa
<< make_nvp("version", version
); // write version
527 oa
<< make_nvp("state", ctx
); // write the internal tables to disc
531 catch (boost::archive::archive_exception
const& e
) {
532 cerr
<< "wave: error while writing state: "
538 ///////////////////////////////////////////////////////////////////////////
539 // list all defined macros
540 bool list_macro_names(context_type
const& ctx
, std::string filename
)
542 // open file for macro names listing
543 std::ofstream macronames_out
;
544 fs::path
macronames_file (boost::wave::util::create_path(filename
));
546 if (macronames_file
!= "-") {
547 macronames_file
= boost::wave::util::complete_path(macronames_file
);
548 boost::wave::util::create_directories(
549 boost::wave::util::branch_path(macronames_file
));
550 macronames_out
.open(macronames_file
.string().c_str());
551 if (!macronames_out
.is_open()) {
552 cerr
<< "wave: could not open file for macro name listing: "
553 << macronames_file
.string() << endl
;
558 macronames_out
.copyfmt(cout
);
559 macronames_out
.clear(cout
.rdstate());
560 static_cast<std::basic_ios
<char> &>(macronames_out
).rdbuf(cout
.rdbuf());
563 // simply list all defined macros and its definitions
564 typedef context_type::const_name_iterator name_iterator
;
565 name_iterator end
= ctx
.macro_names_end();
566 for (name_iterator it
= ctx
.macro_names_begin(); it
!= end
; ++it
)
568 typedef std::vector
<context_type::token_type
> parameters_type
;
570 bool has_pars
= false;
572 context_type::position_type pos
;
573 parameters_type pars
;
574 context_type::token_sequence_type def
;
576 if (ctx
.get_macro_definition(*it
, has_pars
, predef
, pos
, pars
, def
))
578 macronames_out
<< (predef
? "-P" : "-D") << *it
;
580 // list the parameter names for function style macros
581 macronames_out
<< "(";
582 parameters_type::const_iterator pend
= pars
.end();
583 for (parameters_type::const_iterator pit
= pars
.begin();
586 macronames_out
<< (*pit
).get_value();
588 macronames_out
<< ", ";
590 macronames_out
<< ")";
592 macronames_out
<< "=";
594 // print the macro definition
595 context_type::token_sequence_type::const_iterator dend
= def
.end();
596 for (context_type::token_sequence_type::const_iterator dit
= def
.begin();
599 macronames_out
<< (*dit
).get_value();
602 macronames_out
<< std::endl
;
608 ///////////////////////////////////////////////////////////////////////////
609 // list macro invocation counts
610 bool list_macro_counts(context_type
const& ctx
, std::string filename
)
612 // open file for macro invocation count listing
613 std::ofstream macrocounts_out
;
614 fs::path
macrocounts_file (boost::wave::util::create_path(filename
));
616 if (macrocounts_file
!= "-") {
617 macrocounts_file
= boost::wave::util::complete_path(macrocounts_file
);
618 boost::wave::util::create_directories(
619 boost::wave::util::branch_path(macrocounts_file
));
620 macrocounts_out
.open(macrocounts_file
.string().c_str());
621 if (!macrocounts_out
.is_open()) {
622 cerr
<< "wave: could not open file for macro invocation count listing: "
623 << macrocounts_file
.string() << endl
;
628 macrocounts_out
.copyfmt(cout
);
629 macrocounts_out
.clear(cout
.rdstate());
630 static_cast<std::basic_ios
<char> &>(macrocounts_out
).rdbuf(cout
.rdbuf());
633 // list all expanded macro names and their counts in alphabetical order
634 std::map
<std::string
, std::size_t> const& counts
=
635 ctx
.get_hooks().get_macro_counts();
637 typedef std::map
<std::string
, std::size_t>::const_iterator iterator
;
638 iterator end
= counts
.end();
639 for (iterator it
= counts
.begin(); it
!= end
; ++it
)
640 macrocounts_out
<< (*it
).first
<< "," << (*it
).second
<< std::endl
;
645 ///////////////////////////////////////////////////////////////////////////
646 // read all of a file into a string
647 std::string
read_entire_file(std::istream
& instream
)
651 instream
.unsetf(std::ios::skipws
);
653 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
654 // this is known to be very slow for large files on some systems
655 copy (std::istream_iterator
<char>(instream
),
656 std::istream_iterator
<char>(),
657 std::inserter(content
, content
.end()));
659 content
= std::string(std::istreambuf_iterator
<char>(instream
.rdbuf()),
660 std::istreambuf_iterator
<char>());
664 } // anonymous namespace
666 ///////////////////////////////////////////////////////////////////////////////
667 // do the actual preprocessing
669 do_actual_work (std::string file_name
, std::istream
&instream
,
670 po::variables_map
const &vm
, bool input_is_stdin
)
672 // current file position is saved for exception handling
673 boost::wave::util::file_position_type current_position
;
674 auto_stop_watch
elapsed_time(cerr
);
676 const bool treat_warnings_as_error
= vm
.count("warning") &&
677 boost::algorithm::any_of_equal(
678 vm
["warning"].as
<std::vector
<std::string
> >(), "error");
681 // process the given file
682 std::string instring
;
684 instream
.unsetf(std::ios::skipws
);
686 instring
= read_entire_file(instream
);
688 // The preprocessing of the input stream is done on the fly behind the
689 // scenes during iteration over the context_type::iterator_type stream.
690 std::ofstream output
;
691 std::ofstream traceout
;
692 std::ofstream includelistout
;
693 std::ofstream listguardsout
;
695 trace_flags enable_trace
= trace_nothing
;
697 if (vm
.count("traceto")) {
698 // try to open the file, where to put the trace output
699 fs::path
trace_file (boost::wave::util::create_path(
700 vm
["traceto"].as
<std::string
>()));
702 if (trace_file
!= "-") {
703 boost::wave::util::create_directories(
704 boost::wave::util::branch_path(trace_file
));
705 traceout
.open(trace_file
.string().c_str());
706 if (!traceout
.is_open()) {
707 cerr
<< "wave: could not open trace file: " << trace_file
712 enable_trace
= trace_macros
;
714 if ((enable_trace
& trace_macros
) && !traceout
.is_open()) {
715 // by default trace to std::cerr
716 traceout
.copyfmt(cerr
);
717 traceout
.clear(cerr
.rdstate());
718 static_cast<std::basic_ios
<char> &>(traceout
).rdbuf(cerr
.rdbuf());
721 // Open the stream where to output the list of included file names
722 if (vm
.count("listincludes")) {
723 // try to open the file, where to put the include list
724 fs::path
includes_file(boost::wave::util::create_path(
725 vm
["listincludes"].as
<std::string
>()));
727 if (includes_file
!= "-") {
728 boost::wave::util::create_directories(
729 boost::wave::util::branch_path(includes_file
));
730 includelistout
.open(includes_file
.string().c_str());
731 if (!includelistout
.is_open()) {
732 cerr
<< "wave: could not open include list file: "
733 << includes_file
.string() << endl
;
737 enable_trace
= trace_flags(enable_trace
| trace_includes
);
739 if ((enable_trace
& trace_includes
) && !includelistout
.is_open()) {
740 // by default list included names to std::cout
741 includelistout
.copyfmt(cout
);
742 includelistout
.clear(cout
.rdstate());
743 static_cast<std::basic_ios
<char> &>(includelistout
).
747 // Open the stream where to output the list of included file names
748 if (vm
.count("listguards")) {
749 // try to open the file, where to put the include list
750 fs::path
listguards_file(boost::wave::util::create_path(
751 vm
["listguards"].as
<std::string
>()));
753 if (listguards_file
!= "-") {
754 boost::wave::util::create_directories(
755 boost::wave::util::branch_path(listguards_file
));
756 listguardsout
.open(listguards_file
.string().c_str());
757 if (!listguardsout
.is_open()) {
758 cerr
<< "wave: could not open include guard list file: "
759 << listguards_file
.string() << endl
;
763 enable_trace
= trace_flags(enable_trace
| trace_guards
);
765 if ((enable_trace
& trace_guards
) && !listguardsout
.is_open()) {
766 // by default list included names to std::cout
767 listguardsout
.copyfmt(cout
);
768 listguardsout
.clear(cout
.rdstate());
769 static_cast<std::basic_ios
<char> &>(listguardsout
).
773 // enable preserving comments mode
774 bool preserve_comments
= false;
775 bool preserve_whitespace
= false;
776 bool preserve_bol_whitespace
= false;
778 if (vm
.count("preserve")) {
779 int preserve
= vm
["preserve"].as
<int>();
782 case 0: break; // preserve no whitespace
783 case 3: // preserve all whitespace
784 preserve_whitespace
= true;
785 preserve_comments
= true;
786 preserve_bol_whitespace
= true;
789 case 2: // preserve comments and BOL whitespace only
790 preserve_comments
= true;
791 preserve_bol_whitespace
= true;
794 case 1: // preserve BOL whitespace only
795 preserve_bol_whitespace
= true;
799 cerr
<< "wave: bogus preserve whitespace option value: "
800 << preserve
<< ", should be 0, 1, 2, or 3" << endl
;
805 // Since the #pragma wave system() directive may cause a potential security
806 // threat, it has to be enabled explicitly by --extended or -x
807 bool enable_system_command
= false;
809 if (vm
.count("extended"))
810 enable_system_command
= true;
812 // This this the central piece of the Wave library, it provides you with
813 // the iterators to get the preprocessed tokens and allows to configure
814 // the preprocessing stage in advance.
815 bool allow_output
= true; // will be manipulated from inside the hooks object
816 std::string default_outfile
; // will be used from inside the hooks object
817 trace_macro_expansion
<token_type
> hooks(preserve_whitespace
,
818 preserve_bol_whitespace
, output
, traceout
, includelistout
,
819 listguardsout
, enable_trace
, enable_system_command
, allow_output
,
822 // enable macro invocation count, if appropriate
823 if (vm
.count("macrocounts"))
824 hooks
.enable_macro_counting();
826 // check, if we have a license file to prepend
829 if (vm
.count ("license")) {
830 // try to open the file, where to put the preprocessed output
831 std::string
license_file(vm
["license"].as
<std::string
>());
832 std::ifstream
license_stream(license_file
.c_str());
834 if (!license_stream
.is_open()) {
835 cerr
<< "wave: could not open specified license file: "
836 << license_file
<< endl
;
839 license
= read_entire_file(license_stream
);
840 hooks
.set_license_info(license
);
843 context_type
ctx (instring
.begin(), instring
.end(), file_name
.c_str(), hooks
);
845 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
846 // enable C99 mode, if appropriate (implies variadics)
847 if (vm
.count("c99")) {
848 #if BOOST_WAVE_SUPPORT_CPP0X != 0
849 if (vm
.count("c++11")) {
850 cerr
<< "wave: multiple language options specified: --c99 "
851 "and --c++11" << endl
;
856 boost::wave::language_support(
857 boost::wave::support_c99
858 | boost::wave::support_option_convert_trigraphs
859 | boost::wave::support_option_emit_line_directives
860 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
861 | boost::wave::support_option_include_guard_detection
863 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
864 | boost::wave::support_option_emit_pragma_directives
866 | boost::wave::support_option_insert_whitespace
869 else if (vm
.count("variadics")) {
870 // enable variadics and placemarkers, if appropriate
871 ctx
.set_language(boost::wave::enable_variadics(ctx
.get_language()));
873 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
874 #if BOOST_WAVE_SUPPORT_CPP0X != 0
875 if (vm
.count("c++11")) {
876 if (vm
.count("c99")) {
877 cerr
<< "wave: multiple language options specified: --c99 "
878 "and --c++11" << endl
;
882 boost::wave::language_support(
883 boost::wave::support_cpp0x
884 | boost::wave::support_option_convert_trigraphs
885 | boost::wave::support_option_long_long
886 | boost::wave::support_option_emit_line_directives
887 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
888 | boost::wave::support_option_include_guard_detection
890 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
891 | boost::wave::support_option_emit_pragma_directives
893 | boost::wave::support_option_insert_whitespace
896 #endif // BOOST_WAVE_SUPPORT_CPP0X != 0
898 // enable long long support, if appropriate
899 if (vm
.count("long_long")) {
901 boost::wave::enable_long_long(ctx
.get_language()));
904 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
905 // disable include guard detection
906 if (vm
.count("noguard")) {
908 boost::wave::enable_include_guard_detection(
909 ctx
.get_language(), false));
913 // enable preserving comments mode
914 if (preserve_comments
) {
916 boost::wave::enable_preserve_comments(ctx
.get_language()));
919 // control the generation of #line directives
920 if (vm
.count("line")) {
921 int lineopt
= vm
["line"].as
<int>();
922 if (0 != lineopt
&& 1 != lineopt
&& 2 != lineopt
) {
923 cerr
<< "wave: bogus value for --line command line option: "
928 boost::wave::enable_emit_line_directives(ctx
.get_language(),
932 ctx
.get_hooks().enable_relative_names_in_line_directives(true);
935 // control whether whitespace should be inserted to disambiguate output
936 if (vm
.count("disambiguate")) {
937 int disambiguateopt
= vm
["disambiguate"].as
<int>();
938 if (0 != disambiguateopt
&& 1 != disambiguateopt
) {
939 cerr
<< "wave: bogus value for --disambiguate command line option: "
940 << disambiguateopt
<< endl
;
944 boost::wave::enable_insert_whitespace(ctx
.get_language(),
945 disambiguateopt
!= 0));
948 // add include directories to the system include search paths
949 if (vm
.count("sysinclude")) {
950 vector
<std::string
> syspaths
= vm
["sysinclude"].as
<vector
<std::string
> >();
952 vector
<std::string
>::const_iterator end
= syspaths
.end();
953 for (vector
<std::string
>::const_iterator cit
= syspaths
.begin();
956 ctx
.add_sysinclude_path(cmd_line_utils::trim_quotes(*cit
).c_str());
960 // add include directories to the include search paths
961 if (vm
.count("include")) {
962 cmd_line_utils::include_paths
const &ip
=
963 vm
["include"].as
<cmd_line_utils::include_paths
>();
964 vector
<std::string
>::const_iterator end
= ip
.paths
.end();
966 for (vector
<std::string
>::const_iterator cit
= ip
.paths
.begin();
969 ctx
.add_include_path(cmd_line_utils::trim_quotes(*cit
).c_str());
972 // if -I- was given on the command line, this has to be propagated
973 if (ip
.seen_separator
)
974 ctx
.set_sysinclude_delimiter();
976 // add system include directories to the include path
977 vector
<std::string
>::const_iterator sysend
= ip
.syspaths
.end();
978 for (vector
<std::string
>::const_iterator syscit
= ip
.syspaths
.begin();
979 syscit
!= sysend
; ++syscit
)
981 ctx
.add_sysinclude_path(cmd_line_utils::trim_quotes(*syscit
).c_str());
985 // add additional defined macros
986 if (vm
.count("define")) {
987 vector
<std::string
> const ¯os
= vm
["define"].as
<vector
<std::string
> >();
988 vector
<std::string
>::const_iterator end
= macros
.end();
989 for (vector
<std::string
>::const_iterator cit
= macros
.begin();
992 ctx
.add_macro_definition(*cit
);
996 // add additional predefined macros
997 if (vm
.count("predefine")) {
998 vector
<std::string
> const &predefmacros
=
999 vm
["predefine"].as
<vector
<std::string
> >();
1000 vector
<std::string
>::const_iterator end
= predefmacros
.end();
1001 for (vector
<std::string
>::const_iterator cit
= predefmacros
.begin();
1004 ctx
.add_macro_definition(*cit
, true);
1008 // undefine specified macros
1009 if (vm
.count("undefine")) {
1010 vector
<std::string
> const &undefmacros
=
1011 vm
["undefine"].as
<vector
<std::string
> >();
1012 vector
<std::string
>::const_iterator end
= undefmacros
.end();
1013 for (vector
<std::string
>::const_iterator cit
= undefmacros
.begin();
1016 ctx
.remove_macro_definition(*cit
, true);
1020 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
1021 // suppress expansion of specified macros
1022 if (vm
.count("noexpand")) {
1023 vector
<std::string
> const &noexpandmacros
=
1024 vm
["noexpand"].as
<vector
<std::string
> >();
1025 vector
<std::string
>::const_iterator end
= noexpandmacros
.end();
1026 for (vector
<std::string
>::const_iterator cit
= noexpandmacros
.begin();
1029 ctx
.get_hooks().add_noexpandmacro(*cit
);
1034 // maximal include nesting depth
1035 if (vm
.count("nesting")) {
1036 int max_depth
= vm
["nesting"].as
<int>();
1037 if (max_depth
< 1 || max_depth
> 100000) {
1038 cerr
<< "wave: bogus maximal include nesting depth: "
1039 << max_depth
<< endl
;
1042 ctx
.set_max_include_nesting_depth(max_depth
);
1045 // open the output file
1046 if (vm
.count("output")) {
1047 // try to open the file, where to put the preprocessed output
1048 fs::path
out_file (boost::wave::util::create_path(
1049 vm
["output"].as
<std::string
>()));
1051 if (out_file
== "-") {
1052 allow_output
= false; // inhibit output initially
1053 default_outfile
= "-";
1056 out_file
= boost::wave::util::complete_path(out_file
);
1057 boost::wave::util::create_directories(
1058 boost::wave::util::branch_path(out_file
));
1059 output
.open(out_file
.string().c_str());
1060 if (!output
.is_open()) {
1061 cerr
<< "wave: could not open output file: "
1062 << out_file
.string() << endl
;
1065 if (!license
.empty())
1067 default_outfile
= out_file
.string();
1070 else if (!input_is_stdin
&& vm
.count("autooutput")) {
1071 // generate output in the file <input_base_name>.i
1072 fs::path
out_file (boost::wave::util::create_path(file_name
));
1073 std::string
basename (boost::wave::util::leaf(out_file
));
1074 std::string::size_type pos
= basename
.find_last_of(".");
1076 if (std::string::npos
!= pos
)
1077 basename
= basename
.substr(0, pos
);
1078 out_file
= boost::wave::util::branch_path(out_file
) / (basename
+ ".i");
1080 boost::wave::util::create_directories(
1081 boost::wave::util::branch_path(out_file
));
1082 output
.open(out_file
.string().c_str());
1083 if (!output
.is_open()) {
1084 cerr
<< "wave: could not open output file: "
1085 << out_file
.string() << endl
;
1088 if (!license
.empty())
1090 default_outfile
= out_file
.string();
1093 // we assume the session to be interactive if input is stdin and output is
1094 // stdout and the output is not inhibited
1095 bool is_interactive
= input_is_stdin
&& !output
.is_open() && allow_output
;
1097 if (is_interactive
) {
1098 // if interactive we don't warn for missing endif's etc.
1100 boost::wave::enable_single_line(ctx
.get_language()), false);
1103 // analyze the input file
1104 context_type::iterator_type first
= ctx
.begin();
1105 context_type::iterator_type last
= ctx
.end();
1107 // preprocess the required include files
1108 if (vm
.count("forceinclude")) {
1109 // add the filenames to force as include files in _reverse_ order
1110 // the second parameter 'is_last' of the force_include function should
1111 // be set to true for the last (first given) file.
1112 std::vector
<std::string
> const &force
=
1113 vm
["forceinclude"].as
<std::vector
<std::string
> >();
1114 std::vector
<std::string
>::const_reverse_iterator rend
= force
.rend();
1115 for (std::vector
<std::string
>::const_reverse_iterator cit
= force
.rbegin();
1118 std::string
filename(*cit
);
1119 first
.force_include(filename
.c_str(), ++cit
== rend
);
1123 elapsed_time
.set_print_time(!input_is_stdin
&& vm
.count("timer") > 0);
1124 if (is_interactive
) {
1125 print_interactive_version(); // print welcome message
1126 load_state(vm
, ctx
); // load the internal tables from disc
1128 else if (vm
.count("state")) {
1129 // the option "state" is usable in interactive mode only
1130 cerr
<< "wave: ignoring the command line option 'state', "
1131 << "use it in interactive mode only." << endl
;
1134 // >>>>>>>>>>>>> The actual preprocessing happens here. <<<<<<<<<<<<<<<<<<<
1135 // loop over the input lines if reading from stdin, otherwise this loop
1136 // will be executed once
1138 // loop over all generated tokens outputting the generated text
1139 bool finished
= false;
1141 if (input_is_stdin
) {
1143 cout
<< ">>> "; // prompt if is interactive
1145 // read next line and continue
1147 if (!read_a_line(instream
, instring
))
1148 break; // end of input reached
1149 first
= ctx
.begin(instring
.begin(), instring
.end());
1152 bool need_to_advanve
= false;
1156 if (need_to_advanve
) {
1158 need_to_advanve
= false;
1161 while (first
!= last
) {
1162 // store the last known good token position
1163 current_position
= (*first
).get_position();
1165 // print out the current token value
1167 if (!output
.good()) {
1168 cerr
<< "wave: problem writing to the current "
1169 << "output file" << endl
;
1170 cerr
<< report_iostate_error(output
.rdstate());
1173 if (output
.is_open())
1174 output
<< (*first
).get_value();
1176 cout
<< (*first
).get_value();
1179 // advance to the next token
1184 catch (boost::wave::cpp_exception
const &e
) {
1185 // some preprocessing error
1186 if (is_interactive
|| boost::wave::is_recoverable(e
)) {
1187 error_count
+= report_error_message(ctx
, e
,
1188 treat_warnings_as_error
);
1189 need_to_advanve
= true; // advance to the next token
1192 throw; // re-throw for non-recoverable errors
1195 catch (boost::wave::cpplexer::lexing_exception
const &e
) {
1196 // some preprocessing error
1197 if (is_interactive
||
1198 boost::wave::cpplexer::is_recoverable(e
))
1201 report_error_message(e
, treat_warnings_as_error
);
1202 need_to_advanve
= true; // advance to the next token
1205 throw; // re-throw for non-recoverable errors
1208 } while (!finished
);
1209 } while (input_is_stdin
);
1212 save_state(vm
, ctx
); // write the internal tables to disc
1214 // list all defined macros at the end of the preprocessing
1215 if (vm
.count("macronames")) {
1216 if (!list_macro_names(ctx
, vm
["macronames"].as
<std::string
>()))
1219 if (vm
.count("macrocounts")) {
1220 if (!list_macro_counts(ctx
, vm
["macrocounts"].as
<std::string
>()))
1224 catch (boost::wave::cpp_exception
const &e
) {
1225 // some preprocessing error
1226 report_error_message(e
, treat_warnings_as_error
);
1229 catch (boost::wave::cpplexer::lexing_exception
const &e
) {
1230 // some lexing error
1231 report_error_message(e
, treat_warnings_as_error
);
1234 catch (std::exception
const &e
) {
1235 // use last recognized token to retrieve the error position
1237 << current_position
<< ": "
1238 << "exception caught: " << e
.what()
1243 // use last recognized token to retrieve the error position
1245 << current_position
<< ": "
1246 << "unexpected exception caught." << endl
;
1249 return -error_count
; // returns the number of errors as a negative integer
1252 ///////////////////////////////////////////////////////////////////////////////
1255 main (int argc
, char *argv
[])
1257 const std::string accepted_w_args
[] = {"error"};
1259 // test Wave compilation configuration
1260 if (!BOOST_WAVE_TEST_CONFIGURATION()) {
1261 cout
<< "wave: warning: the library this application was linked against was compiled "
1263 << " using a different configuration (see wave_config.hpp)."
1267 // analyze the command line options and arguments
1269 // declare the options allowed on the command line only
1270 po::options_description
desc_cmdline ("Options allowed on the command line only");
1272 desc_cmdline
.add_options()
1273 ("help,h", "print out program usage (this message)")
1274 ("version,v", "print the version number")
1275 ("copyright", "print out the copyright statement")
1276 ("config-file", po::value
<vector
<std::string
> >()->composing(),
1277 "specify a config file (alternatively: @filepath)")
1280 const std::string w_arg_desc
= "Warning settings. Currently supported: -W" +
1281 boost::algorithm::join(accepted_w_args
, ", -W");
1283 // declare the options allowed on command line and in config files
1284 po::options_description
desc_generic ("Options allowed additionally in a config file");
1286 desc_generic
.add_options()
1287 ("output,o", po::value
<std::string
>(),
1288 "specify a file [arg] to use for output instead of stdout or "
1289 "disable output [-]")
1291 "output goes into a file named <input_basename>.i")
1292 ("license", po::value
<std::string
>(),
1293 "prepend the content of the specified file to each created file")
1294 ("include,I", po::value
<cmd_line_utils::include_paths
>()->composing(),
1295 "specify an additional include directory")
1296 ("sysinclude,S", po::value
<vector
<std::string
> >()->composing(),
1297 "specify an additional system include directory")
1298 ("forceinclude,F", po::value
<std::vector
<std::string
> >()->composing(),
1299 "force inclusion of the given file")
1300 ("define,D", po::value
<std::vector
<std::string
> >()->composing(),
1301 "specify a macro to define (as macro[=[value]])")
1302 ("predefine,P", po::value
<std::vector
<std::string
> >()->composing(),
1303 "specify a macro to predefine (as macro[=[value]])")
1304 ("undefine,U", po::value
<std::vector
<std::string
> >()->composing(),
1305 "specify a macro to undefine")
1306 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
1307 ("noexpand,N", po::value
<std::vector
<std::string
> >()->composing(),
1308 "specify a macro name, which should not be expanded")
1310 ("nesting,n", po::value
<int>(),
1311 "specify a new maximal include nesting depth")
1312 ("warning,W", po::value
<std::vector
<std::string
> >()->composing(),
1316 po::options_description
desc_ext ("Extended options (allowed everywhere)");
1318 desc_ext
.add_options()
1319 ("traceto,t", po::value
<std::string
>(),
1320 "output macro expansion tracing information to a file [arg] "
1322 ("timer", "output overall elapsed computing time to stderr")
1323 ("long_long", "enable long long support in C++ mode")
1324 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
1325 ("variadics", "enable certain C99 extensions in C++ mode")
1326 ("c99", "enable C99 mode (implies --variadics)")
1328 #if BOOST_WAVE_SUPPORT_CPP0X != 0
1329 ("c++11", "enable C++11 mode (implies --variadics and --long_long)")
1331 ("listincludes,l", po::value
<std::string
>(),
1332 "list names of included files to a file [arg] or to stdout [-]")
1333 ("macronames,m", po::value
<std::string
>(),
1334 "list all defined macros to a file [arg] or to stdout [-]")
1335 ("macrocounts,c", po::value
<std::string
>(),
1336 "list macro invocation counts to a file [arg] or to stdout [-]")
1337 ("preserve,p", po::value
<int>()->default_value(0),
1338 "preserve whitespace\n"
1339 "0: no whitespace is preserved (default),\n"
1340 "1: begin of line whitespace is preserved,\n"
1341 "2: comments and begin of line whitespace is preserved,\n"
1342 "3: all whitespace is preserved")
1343 ("line,L", po::value
<int>()->default_value(1),
1344 "control the generation of #line directives\n"
1345 "0: no #line directives are generated,\n"
1346 "1: #line directives will be emitted (default),\n"
1347 "2: #line directives will be emitted using relative\n"
1349 ("disambiguate", po::value
<int>()->default_value(1),
1350 "control whitespace insertion to disambiguate\n"
1351 "consecutive tokens\n"
1352 "0: no additional whitespace is generated,\n"
1353 "1: whitespace is used to disambiguate output (default)")
1354 ("extended,x", "enable the #pragma wave system() directive")
1355 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1356 ("noguard,G", "disable include guard detection")
1357 ("listguards,g", po::value
<std::string
>(),
1358 "list names of files flagged as 'include once' to a file [arg] "
1361 #if BOOST_WAVE_SERIALIZATION != 0
1362 ("state,s", po::value
<std::string
>(),
1363 "load and save state information from/to the given file [arg] "
1364 "or 'wave.state' [-] (interactive mode only)")
1368 // combine the options for the different usage schemes
1369 po::options_description desc_overall_cmdline
;
1370 po::options_description desc_overall_cfgfile
;
1372 desc_overall_cmdline
.add(desc_cmdline
).add(desc_generic
).add(desc_ext
);
1373 desc_overall_cfgfile
.add(desc_generic
).add(desc_ext
);
1375 // parse command line and store results
1376 using namespace boost::program_options::command_line_style
;
1378 po::parsed_options
opts(po::parse_command_line(argc
, argv
,
1379 desc_overall_cmdline
, unix_style
, cmd_line_utils::at_option_parser
));
1380 po::variables_map vm
;
1382 po::store(opts
, vm
);
1385 // // Try to find a wave.cfg in the same directory as the executable was
1386 // // started from. If this exists, treat it as a wave config file
1387 // fs::path filename(argv[0]);
1389 // filename = filename.branch_path() / "wave.cfg";
1390 // cmd_line_utils::read_config_file_options(filename.string(),
1391 // desc_overall_cfgfile, vm, true);
1393 // extract the arguments from the parsed command line
1394 vector
<po::option
> arguments
;
1396 std::remove_copy_if(opts
.options
.begin(), opts
.options
.end(),
1397 back_inserter(arguments
), cmd_line_utils::is_argument());
1399 // try to find a config file somewhere up the filesystem hierarchy
1400 // starting with the input file path. This allows to use a general wave.cfg
1401 // file for all files in a certain project.
1402 if (arguments
.size() > 0 && arguments
[0].value
[0] != "-") {
1403 // construct full path of input file
1404 fs::path
input_dir (boost::wave::util::complete_path(
1405 boost::wave::util::create_path(arguments
[0].value
[0])));
1407 // chop of file name
1408 input_dir
= boost::wave::util::branch_path(
1409 boost::wave::util::normalize(input_dir
));
1411 // walk up the hierarchy, trying to find a file wave.cfg
1412 while (!input_dir
.empty()) {
1413 fs::path filename
= input_dir
/ "wave.cfg";
1414 if (cmd_line_utils::read_config_file_options(filename
.string(),
1415 desc_overall_cfgfile
, vm
, true))
1417 break; // break on the first cfg file found
1419 input_dir
= boost::wave::util::branch_path(input_dir
);
1423 // if there is specified at least one config file, parse it and add the
1424 // options to the main variables_map
1425 if (vm
.count("config-file")) {
1426 vector
<std::string
> const &cfg_files
=
1427 vm
["config-file"].as
<vector
<std::string
> >();
1428 vector
<std::string
>::const_iterator end
= cfg_files
.end();
1429 for (vector
<std::string
>::const_iterator cit
= cfg_files
.begin();
1432 // parse a single config file and store the results
1433 cmd_line_utils::read_config_file_options(*cit
,
1434 desc_overall_cfgfile
, vm
);
1438 // validate warning settings
1439 if (vm
.count("warning"))
1441 BOOST_FOREACH(const std::string
& arg
,
1442 vm
["warning"].as
<std::vector
<std::string
> >())
1444 if (boost::range::find(accepted_w_args
, arg
) ==
1445 boost::end(accepted_w_args
))
1447 cerr
<< "wave: Invalid warning setting: " << arg
<< endl
;
1453 // ... act as required
1454 if (vm
.count("help")) {
1455 po::options_description
desc_help (
1456 "Usage: wave [options] [@config-file(s)] [file]");
1458 desc_help
.add(desc_cmdline
).add(desc_generic
).add(desc_ext
);
1459 cout
<< desc_help
<< endl
;
1463 if (vm
.count("version")) {
1464 cout
<< get_version() << endl
;
1468 if (vm
.count("copyright")) {
1469 return print_copyright();
1472 // if there is no input file given, then take input from stdin
1473 if (0 == arguments
.size() || 0 == arguments
[0].value
.size() ||
1474 arguments
[0].value
[0] == "-")
1476 // preprocess the given input from stdin
1477 return do_actual_work("<stdin>", std::cin
, vm
, true);
1480 if (arguments
.size() > 1) {
1481 // this driver understands to parse one input file only
1482 cerr
<< "wave: more than one input file specified, "
1483 << "ignoring all but the first!" << endl
;
1486 std::string
file_name(arguments
[0].value
[0]);
1487 std::ifstream
instream(file_name
.c_str());
1489 // preprocess the given input file
1490 if (!instream
.is_open()) {
1491 cerr
<< "wave: could not open input file: " << file_name
<< endl
;
1494 return do_actual_work(file_name
, instream
, vm
, false);
1497 catch (std::exception
const &e
) {
1498 cout
<< "wave: exception caught: " << e
.what() << endl
;
1502 cerr
<< "wave: unexpected exception caught." << endl
;