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/filesystem/fstream.hpp>
22 #include <boost/timer.hpp>
23 #include <boost/any.hpp>
24 #include <boost/algorithm/cxx11/any_of.hpp>
25 #include <boost/algorithm/string/join.hpp>
26 #include <boost/range/algorithm/find.hpp>
27 #include <boost/range/end.hpp>
28 #include <boost/foreach.hpp>
30 ///////////////////////////////////////////////////////////////////////////////
31 // Include Wave itself
32 #include <boost/wave.hpp>
34 ///////////////////////////////////////////////////////////////////////////////
35 // Include the lexer related stuff
36 #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type
37 #include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer type
39 ///////////////////////////////////////////////////////////////////////////////
40 // Include serialization support, if requested
41 #if BOOST_WAVE_SERIALIZATION != 0
42 #include <boost/serialization/serialization.hpp>
43 #if BOOST_WAVE_BINARY_SERIALIZATION != 0
44 #include <boost/archive/binary_iarchive.hpp>
45 #include <boost/archive/binary_oarchive.hpp>
46 typedef boost::archive::binary_iarchive iarchive
;
47 typedef boost::archive::binary_oarchive oarchive
;
48 #elif BOOST_WAVE_XML_SERIALIZATION != 0
49 #include <boost/archive/xml_iarchive.hpp>
50 #include <boost/archive/xml_oarchive.hpp>
51 typedef boost::archive::xml_iarchive iarchive
;
52 typedef boost::archive::xml_oarchive oarchive
;
54 #include <boost/archive/text_iarchive.hpp>
55 #include <boost/archive/text_oarchive.hpp>
56 typedef boost::archive::text_iarchive iarchive
;
57 typedef boost::archive::text_oarchive oarchive
;
61 ///////////////////////////////////////////////////////////////////////////////
62 // Include the context policies to use
63 #include "trace_macro_expansion.hpp"
65 ///////////////////////////////////////////////////////////////////////////////
66 // Include lexer specifics, import lexer names
67 #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
68 #include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
71 ///////////////////////////////////////////////////////////////////////////////
72 // Include the grammar definitions, if these shouldn't be compiled separately
73 // (ATTENTION: _very_ large compilation times!)
74 #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
75 #include <boost/wave/grammars/cpp_intlit_grammar.hpp>
76 #include <boost/wave/grammars/cpp_chlit_grammar.hpp>
77 #include <boost/wave/grammars/cpp_grammar.hpp>
78 #include <boost/wave/grammars/cpp_expression_grammar.hpp>
79 #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
80 #include <boost/wave/grammars/cpp_defined_grammar.hpp>
83 ///////////////////////////////////////////////////////////////////////////////
84 // Import required names
85 using namespace boost::spirit::classic
;
90 using boost::filesystem::ofstream
;
91 using boost::filesystem::ifstream
;
96 using std::istreambuf_iterator
;
98 ///////////////////////////////////////////////////////////////////////////////
100 // This application uses the lex_iterator and lex_token types predefined
101 // with the Wave library, but it is possible to use your own types.
103 // You may want to have a look at the other samples to see how this is
104 // possible to achieve.
105 typedef boost::wave::cpplexer::lex_token
<> token_type
;
106 typedef boost::wave::cpplexer::lex_iterator
<token_type
>
109 // The C++ preprocessor iterators shouldn't be constructed directly. They
110 // are to be generated through a boost::wave::context<> object. This
111 // boost::wave::context object is additionally to be used to initialize and
112 // define different parameters of the actual preprocessing.
113 typedef boost::wave::context
<
114 std::string::iterator
, lex_iterator_type
,
115 boost::wave::iteration_context_policies::load_file_to_string
,
116 trace_macro_expansion
<token_type
> >
119 ///////////////////////////////////////////////////////////////////////////////
120 // print the current version
121 std::string
get_version()
123 std::string
version (context_type::get_version_string());
124 version
= version
.substr(1, version
.size()-2); // strip quotes
125 version
+= std::string(" (" CPP_VERSION_DATE_STR
")"); // add date
129 ///////////////////////////////////////////////////////////////////////////////
130 // print the current version for interactive sessions
131 int print_interactive_version()
133 cout
<< "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library" << endl
;
134 cout
<< "Version: " << get_version() << endl
;
138 ///////////////////////////////////////////////////////////////////////////////
139 // print the copyright statement
140 int print_copyright()
142 char const *copyright
[] = {
144 "Wave: A Standard conformant C++ preprocessor based on the Boost.Wave library",
145 "http://www.boost.org/",
147 "Copyright (c) 2001-2012 Hartmut Kaiser, Distributed under the Boost",
148 "Software License, Version 1.0. (See accompanying file",
149 "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
153 for (int i
= 0; 0 != copyright
[i
]; ++i
)
154 cout
<< copyright
[i
] << endl
;
156 return 0; // exit app
159 ///////////////////////////////////////////////////////////////////////////////
160 // forward declarations only
161 namespace cmd_line_utils
166 namespace boost
{ namespace program_options
{
168 void validate(boost::any
&v
, std::vector
<std::string
> const &s
,
169 cmd_line_utils::include_paths
*, long);
171 }} // boost::program_options
173 ///////////////////////////////////////////////////////////////////////////////
174 #include <boost/program_options.hpp>
176 namespace po
= boost::program_options
;
177 namespace fs
= boost::filesystem
;
179 ///////////////////////////////////////////////////////////////////////////////
180 namespace cmd_line_utils
{
181 // Additional command line parser which interprets '@something' as an
182 // option "config-file" with the value "something".
183 inline pair
<std::string
, std::string
>
184 at_option_parser(std::string
const&s
)
187 return std::make_pair(std::string("config-file"), s
.substr(1));
189 return pair
<std::string
, std::string
>();
192 // class, which keeps include file information read from the command line
193 class include_paths
{
195 include_paths() : seen_separator(false) {}
197 vector
<std::string
> paths
; // stores user paths
198 vector
<std::string
> syspaths
; // stores system paths
199 bool seen_separator
; // command line contains a '-I-' option
201 // Function which validates additional tokens from command line.
203 validate(boost::any
&v
, vector
<std::string
> const &tokens
)
206 v
= boost::any(include_paths());
208 include_paths
*p
= boost::any_cast
<include_paths
>(&v
);
211 // Assume only one path per '-I' occurrence.
212 std::string
const& t
= po::validators::get_single_string(tokens
);
214 // found -I- option, so switch behaviour
215 p
->seen_separator
= true;
217 else if (p
->seen_separator
) {
218 // store this path as a system path
219 p
->syspaths
.push_back(t
);
222 // store this path as an user path
223 p
->paths
.push_back(t
);
228 // Read all options from a given config file, parse and add them to the
229 // given variables_map
230 bool read_config_file_options(std::string
const &filename
,
231 po::options_description
const &desc
, po::variables_map
&vm
,
232 bool may_fail
= false)
234 ifstream
ifs(filename
.c_str());
236 if (!ifs
.is_open()) {
239 << ": command line warning: config file not found"
245 vector
<std::string
> options
;
248 while (std::getline(ifs
, line
)) {
250 std::string::size_type pos
= line
.find_first_not_of(" \t");
251 if (pos
== std::string::npos
)
254 // skip comment lines
255 if ('#' != line
[pos
]) {
256 // strip leading and trailing whitespace
257 std::string::size_type endpos
= line
.find_last_not_of(" \t");
258 BOOST_ASSERT(endpos
!= std::string::npos
);
259 options
.push_back(line
.substr(pos
, endpos
-pos
+1));
263 if (options
.size() > 0) {
264 using namespace boost::program_options::command_line_style
;
265 po::store(po::command_line_parser(options
)
266 .options(desc
).style(unix_style
).run(), vm
);
272 // predicate to extract all positional arguments from the command line
274 bool operator()(po::option
const &opt
)
276 return (opt
.position_key
== -1) ? true : false;
280 // trim quotes from path names, if any
281 std::string
trim_quotes(std::string
const& file
)
283 if (('"' == file
[0] || '\'' == file
[0]) && file
[0] == file
[file
.size()-1])
285 return file
.substr(1, file
.size()-2);
290 ///////////////////////////////////////////////////////////////////////////////
293 ///////////////////////////////////////////////////////////////////////////////
295 // Special validator overload, which allows to handle the -I- syntax for
296 // switching the semantics of an -I option.
298 ///////////////////////////////////////////////////////////////////////////////
299 namespace boost
{ namespace program_options
{
301 void validate(boost::any
&v
, std::vector
<std::string
> const &s
,
302 cmd_line_utils::include_paths
*, long)
304 cmd_line_utils::include_paths::validate(v
, s
);
307 }} // namespace boost::program_options
309 ///////////////////////////////////////////////////////////////////////////////
312 class auto_stop_watch
: public stop_watch
315 auto_stop_watch(std::ostream
&outstrm_
)
316 : print_time(false), outstrm(outstrm_
)
323 outstrm
<< "Elapsed time: "
324 << this->format_elapsed_time()
329 void set_print_time(bool print_time_
)
331 print_time
= print_time_
;
336 std::ostream
&outstrm
;
339 ///////////////////////////////////////////////////////////////////////////
341 report_iostate_error(std::ios::iostate state
)
343 BOOST_ASSERT(state
& (std::ios::badbit
| std::ios::failbit
| std::ios::eofbit
));
345 if (state
& std::ios::badbit
) {
346 result
+= " the reported problem was: "
347 "loss of integrity of the stream buffer\n";
349 if (state
& std::ios::failbit
) {
350 result
+= " the reported problem was: "
351 "an operation was not processed correctly\n";
353 if (state
& std::ios::eofbit
) {
354 result
+= " the reported problem was: "
355 "end-of-file while writing to the stream\n";
360 ///////////////////////////////////////////////////////////////////////////
361 // Retrieve the position of a macro definition
362 template <typename Context
>
364 get_macro_position(Context
&ctx
,
365 typename
Context::token_type::string_type
const& name
,
366 typename
Context::position_type
&pos
)
368 bool has_parameters
= false;
369 bool is_predefined
= false;
370 std::vector
<typename
Context::token_type
> parameters
;
371 typename
Context::token_sequence_type definition
;
373 return ctx
.get_macro_definition(name
, has_parameters
, is_predefined
,
374 pos
, parameters
, definition
);
377 ///////////////////////////////////////////////////////////////////////////
378 // Generate some meaningful error messages
379 template <typename Exception
>
381 report_error_message(Exception
const &e
, bool treat_warnings_as_error
)
383 // default error reporting
385 << e
.file_name() << ":" << e
.line_no() << ":" << e
.column_no()
386 << ": " << e
.description() << endl
;
388 // errors count as one
389 return (treat_warnings_as_error
||
390 e
.get_severity() == boost::wave::util::severity_error
||
391 e
.get_severity() == boost::wave::util::severity_fatal
) ? 1 : 0;
394 template <typename Context
>
396 report_error_message(Context
&ctx
, boost::wave::cpp_exception
const &e
,
397 bool treat_warnings_as_error
)
399 // default error reporting
400 int result
= report_error_message(e
, treat_warnings_as_error
);
402 using boost::wave::preprocess_exception
;
403 switch(e
.get_errorcode()) {
404 case preprocess_exception::macro_redefinition
:
406 // report the point of the initial macro definition
407 typename
Context::position_type pos
;
408 if (get_macro_position(ctx
, e
.get_related_name(), pos
)) {
411 << preprocess_exception::severity_text(e
.get_severity())
412 << ": this is the location of the previous definition."
417 << e
.file_name() << ":" << e
.line_no() << ":"
418 << e
.column_no() << ": "
419 << preprocess_exception::severity_text(e
.get_severity())
420 << ": not able to retrieve the location of the previous "
421 << "definition." << endl
;
433 ///////////////////////////////////////////////////////////////////////////
434 // Read one logical line of text
436 read_a_line (std::istream
&instream
, std::string
&instring
)
441 std::getline(instream
, line
);
442 if (instream
.rdstate() & std::ios::failbit
)
443 return false; // nothing to do
446 if (line
.find_last_of('\\') == line
.size()-1)
449 instring
+= line
+ '\n';
454 ///////////////////////////////////////////////////////////////////////////
455 // Load and save the internal tables of the wave::context object
456 template <typename Context
>
458 load_state(po::variables_map
const &vm
, Context
&ctx
)
460 #if BOOST_WAVE_SERIALIZATION != 0
462 if (vm
.count("state") > 0) {
463 fs::path
state_file (
464 boost::wave::util::create_path(vm
["state"].as
<std::string
>()));
465 if (state_file
== "-")
466 state_file
= boost::wave::util::create_path("wave.state");
468 std::ios::openmode mode
= std::ios::in
;
470 #if BOOST_WAVE_BINARY_SERIALIZATION != 0
471 mode
= (std::ios::openmode
)(mode
| std::ios::binary
);
473 ifstream
ifs (state_file
.string().c_str(), mode
);
475 using namespace boost::serialization
;
479 ia
>> make_nvp("version", version
); // load version
480 if (version
== CPP_VERSION_FULL_STR
)
481 ia
>> make_nvp("state", ctx
); // load the internal tables from disc
483 cerr
<< "wave: detected version mismatch while loading state, state was not loaded." << endl
;
484 cerr
<< " loaded version: " << version
<< endl
;
485 cerr
<< " expected version: " << CPP_VERSION_FULL_STR
<< endl
;
490 catch (boost::archive::archive_exception
const& e
) {
491 cerr
<< "wave: error while loading state: "
494 catch (boost::wave::preprocess_exception
const& e
) {
495 cerr
<< "wave: error while loading state: "
496 << e
.description() << endl
;
501 template <typename Context
>
503 save_state(po::variables_map
const &vm
, Context
const &ctx
)
505 #if BOOST_WAVE_SERIALIZATION != 0
507 if (vm
.count("state") > 0) {
508 fs::path
state_file (boost::wave::util::create_path(
509 vm
["state"].as
<std::string
>()));
510 if (state_file
== "-")
511 state_file
= boost::wave::util::create_path("wave.state");
513 std::ios::openmode mode
= std::ios::out
;
515 #if BOOST_WAVE_BINARY_SERIALIZATION != 0
516 mode
= (std::ios::openmode
)(mode
| std::ios::binary
);
518 ofstream
ofs(state_file
.string().c_str(), mode
);
519 if (!ofs
.is_open()) {
520 cerr
<< "wave: could not open state file for writing: "
521 << state_file
.string() << endl
;
525 using namespace boost::serialization
;
527 std::string
version(CPP_VERSION_FULL_STR
);
528 oa
<< make_nvp("version", version
); // write version
529 oa
<< make_nvp("state", ctx
); // write the internal tables to disc
533 catch (boost::archive::archive_exception
const& e
) {
534 cerr
<< "wave: error while writing state: "
540 ///////////////////////////////////////////////////////////////////////////
541 // list all defined macros
542 bool list_macro_names(context_type
const& ctx
, std::string filename
)
544 // open file for macro names listing
545 ofstream macronames_out
;
546 fs::path
macronames_file (boost::wave::util::create_path(filename
));
548 if (macronames_file
!= "-") {
549 macronames_file
= boost::wave::util::complete_path(macronames_file
);
550 boost::wave::util::create_directories(
551 boost::wave::util::branch_path(macronames_file
));
552 macronames_out
.open(macronames_file
.string().c_str());
553 if (!macronames_out
.is_open()) {
554 cerr
<< "wave: could not open file for macro name listing: "
555 << macronames_file
.string() << endl
;
560 macronames_out
.copyfmt(cout
);
561 macronames_out
.clear(cout
.rdstate());
562 static_cast<std::basic_ios
<char> &>(macronames_out
).rdbuf(cout
.rdbuf());
565 // simply list all defined macros and its definitions
566 typedef context_type::const_name_iterator name_iterator
;
567 name_iterator end
= ctx
.macro_names_end();
568 for (name_iterator it
= ctx
.macro_names_begin(); it
!= end
; ++it
)
570 typedef std::vector
<context_type::token_type
> parameters_type
;
572 bool has_pars
= false;
574 context_type::position_type pos
;
575 parameters_type pars
;
576 context_type::token_sequence_type def
;
578 if (ctx
.get_macro_definition(*it
, has_pars
, predef
, pos
, pars
, def
))
580 macronames_out
<< (predef
? "-P" : "-D") << *it
;
582 // list the parameter names for function style macros
583 macronames_out
<< "(";
584 parameters_type::const_iterator pend
= pars
.end();
585 for (parameters_type::const_iterator pit
= pars
.begin();
588 macronames_out
<< (*pit
).get_value();
590 macronames_out
<< ", ";
592 macronames_out
<< ")";
594 macronames_out
<< "=";
596 // print the macro definition
597 context_type::token_sequence_type::const_iterator dend
= def
.end();
598 for (context_type::token_sequence_type::const_iterator dit
= def
.begin();
601 macronames_out
<< (*dit
).get_value();
604 macronames_out
<< std::endl
;
610 ///////////////////////////////////////////////////////////////////////////
611 // list macro invocation counts
612 bool list_macro_counts(context_type
const& ctx
, std::string filename
)
614 // open file for macro invocation count listing
615 ofstream macrocounts_out
;
616 fs::path
macrocounts_file (boost::wave::util::create_path(filename
));
618 if (macrocounts_file
!= "-") {
619 macrocounts_file
= boost::wave::util::complete_path(macrocounts_file
);
620 boost::wave::util::create_directories(
621 boost::wave::util::branch_path(macrocounts_file
));
622 macrocounts_out
.open(macrocounts_file
.string().c_str());
623 if (!macrocounts_out
.is_open()) {
624 cerr
<< "wave: could not open file for macro invocation count listing: "
625 << macrocounts_file
.string() << endl
;
630 macrocounts_out
.copyfmt(cout
);
631 macrocounts_out
.clear(cout
.rdstate());
632 static_cast<std::basic_ios
<char> &>(macrocounts_out
).rdbuf(cout
.rdbuf());
635 // list all expanded macro names and their counts in alphabetical order
636 std::map
<std::string
, std::size_t> const& counts
=
637 ctx
.get_hooks().get_macro_counts();
639 typedef std::map
<std::string
, std::size_t>::const_iterator iterator
;
640 iterator end
= counts
.end();
641 for (iterator it
= counts
.begin(); it
!= end
; ++it
)
642 macrocounts_out
<< (*it
).first
<< "," << (*it
).second
<< std::endl
;
647 ///////////////////////////////////////////////////////////////////////////
648 // read all of a file into a string
649 std::string
read_entire_file(std::istream
& instream
)
653 instream
.unsetf(std::ios::skipws
);
655 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
656 // this is known to be very slow for large files on some systems
657 copy (std::istream_iterator
<char>(instream
),
658 std::istream_iterator
<char>(),
659 std::inserter(content
, content
.end()));
661 content
= std::string(std::istreambuf_iterator
<char>(instream
.rdbuf()),
662 std::istreambuf_iterator
<char>());
666 } // anonymous namespace
668 ///////////////////////////////////////////////////////////////////////////////
669 // do the actual preprocessing
671 do_actual_work (std::string file_name
, std::istream
&instream
,
672 po::variables_map
const &vm
, bool input_is_stdin
)
674 // current file position is saved for exception handling
675 boost::wave::util::file_position_type current_position
;
676 auto_stop_watch
elapsed_time(cerr
);
678 const bool treat_warnings_as_error
= vm
.count("warning") &&
679 boost::algorithm::any_of_equal(
680 vm
["warning"].as
<std::vector
<std::string
> >(), "error");
683 // process the given file
684 std::string instring
;
686 instream
.unsetf(std::ios::skipws
);
688 instring
= read_entire_file(instream
);
690 // The preprocessing of the input stream is done on the fly behind the
691 // scenes during iteration over the context_type::iterator_type stream.
694 ofstream includelistout
;
695 ofstream listguardsout
;
697 trace_flags enable_trace
= trace_nothing
;
699 if (vm
.count("traceto")) {
700 // try to open the file, where to put the trace output
701 fs::path
trace_file (boost::wave::util::create_path(
702 vm
["traceto"].as
<std::string
>()));
704 if (trace_file
!= "-") {
705 boost::wave::util::create_directories(
706 boost::wave::util::branch_path(trace_file
));
707 traceout
.open(trace_file
.string().c_str());
708 if (!traceout
.is_open()) {
709 cerr
<< "wave: could not open trace file: " << trace_file
714 enable_trace
= trace_macros
;
716 if ((enable_trace
& trace_macros
) && !traceout
.is_open()) {
717 // by default trace to std::cerr
718 traceout
.copyfmt(cerr
);
719 traceout
.clear(cerr
.rdstate());
720 static_cast<std::basic_ios
<char> &>(traceout
).rdbuf(cerr
.rdbuf());
723 // Open the stream where to output the list of included file names
724 if (vm
.count("listincludes")) {
725 // try to open the file, where to put the include list
726 fs::path
includes_file(boost::wave::util::create_path(
727 vm
["listincludes"].as
<std::string
>()));
729 if (includes_file
!= "-") {
730 boost::wave::util::create_directories(
731 boost::wave::util::branch_path(includes_file
));
732 includelistout
.open(includes_file
.string().c_str());
733 if (!includelistout
.is_open()) {
734 cerr
<< "wave: could not open include list file: "
735 << includes_file
.string() << endl
;
739 enable_trace
= trace_flags(enable_trace
| trace_includes
);
741 if ((enable_trace
& trace_includes
) && !includelistout
.is_open()) {
742 // by default list included names to std::cout
743 includelistout
.copyfmt(cout
);
744 includelistout
.clear(cout
.rdstate());
745 static_cast<std::basic_ios
<char> &>(includelistout
).
749 // Open the stream where to output the list of included file names
750 if (vm
.count("listguards")) {
751 // try to open the file, where to put the include list
752 fs::path
listguards_file(boost::wave::util::create_path(
753 vm
["listguards"].as
<std::string
>()));
755 if (listguards_file
!= "-") {
756 boost::wave::util::create_directories(
757 boost::wave::util::branch_path(listguards_file
));
758 listguardsout
.open(listguards_file
.string().c_str());
759 if (!listguardsout
.is_open()) {
760 cerr
<< "wave: could not open include guard list file: "
761 << listguards_file
.string() << endl
;
765 enable_trace
= trace_flags(enable_trace
| trace_guards
);
767 if ((enable_trace
& trace_guards
) && !listguardsout
.is_open()) {
768 // by default list included names to std::cout
769 listguardsout
.copyfmt(cout
);
770 listguardsout
.clear(cout
.rdstate());
771 static_cast<std::basic_ios
<char> &>(listguardsout
).
775 // enable preserving comments mode
776 bool preserve_comments
= false;
777 bool preserve_whitespace
= false;
778 bool preserve_bol_whitespace
= false;
780 if (vm
.count("preserve")) {
781 int preserve
= vm
["preserve"].as
<int>();
784 case 0: break; // preserve no whitespace
785 case 3: // preserve all whitespace
786 preserve_whitespace
= true;
787 preserve_comments
= true;
788 preserve_bol_whitespace
= true;
791 case 2: // preserve comments and BOL whitespace only
792 preserve_comments
= true;
793 preserve_bol_whitespace
= true;
796 case 1: // preserve BOL whitespace only
797 preserve_bol_whitespace
= true;
801 cerr
<< "wave: bogus preserve whitespace option value: "
802 << preserve
<< ", should be 0, 1, 2, or 3" << endl
;
807 // Since the #pragma wave system() directive may cause a potential security
808 // threat, it has to be enabled explicitly by --extended or -x
809 bool enable_system_command
= false;
811 if (vm
.count("extended"))
812 enable_system_command
= true;
814 // This this the central piece of the Wave library, it provides you with
815 // the iterators to get the preprocessed tokens and allows to configure
816 // the preprocessing stage in advance.
817 bool allow_output
= true; // will be manipulated from inside the hooks object
818 std::string default_outfile
; // will be used from inside the hooks object
819 trace_macro_expansion
<token_type
> hooks(preserve_whitespace
,
820 preserve_bol_whitespace
, output
, traceout
, includelistout
,
821 listguardsout
, enable_trace
, enable_system_command
, allow_output
,
824 // enable macro invocation count, if appropriate
825 if (vm
.count("macrocounts"))
826 hooks
.enable_macro_counting();
828 // check, if we have a license file to prepend
831 if (vm
.count ("license")) {
832 // try to open the file, where to put the preprocessed output
833 std::string
license_file(vm
["license"].as
<std::string
>());
834 ifstream
license_stream(license_file
.c_str());
836 if (!license_stream
.is_open()) {
837 cerr
<< "wave: could not open specified license file: "
838 << license_file
<< endl
;
841 license
= read_entire_file(license_stream
);
842 hooks
.set_license_info(license
);
845 context_type
ctx (instring
.begin(), instring
.end(), file_name
.c_str(), hooks
);
847 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
848 // enable C99 mode, if appropriate (implies variadics)
849 if (vm
.count("c99")) {
850 #if BOOST_WAVE_SUPPORT_CPP0X != 0
851 if (vm
.count("c++11")) {
852 cerr
<< "wave: multiple language options specified: --c99 "
853 "and --c++11" << endl
;
858 boost::wave::language_support(
859 boost::wave::support_c99
860 | boost::wave::support_option_convert_trigraphs
861 | boost::wave::support_option_emit_line_directives
862 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
863 | boost::wave::support_option_include_guard_detection
865 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
866 | boost::wave::support_option_emit_pragma_directives
868 | boost::wave::support_option_insert_whitespace
871 else if (vm
.count("variadics")) {
872 // enable variadics and placemarkers, if appropriate
873 ctx
.set_language(boost::wave::enable_variadics(ctx
.get_language()));
875 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
876 #if BOOST_WAVE_SUPPORT_CPP0X != 0
877 if (vm
.count("c++11")) {
878 if (vm
.count("c99")) {
879 cerr
<< "wave: multiple language options specified: --c99 "
880 "and --c++11" << endl
;
884 boost::wave::language_support(
885 boost::wave::support_cpp0x
886 | boost::wave::support_option_convert_trigraphs
887 | boost::wave::support_option_long_long
888 | boost::wave::support_option_emit_line_directives
889 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
890 | boost::wave::support_option_include_guard_detection
892 #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
893 | boost::wave::support_option_emit_pragma_directives
895 | boost::wave::support_option_insert_whitespace
898 #endif // BOOST_WAVE_SUPPORT_CPP0X != 0
900 // enable long long support, if appropriate
901 if (vm
.count("long_long")) {
903 boost::wave::enable_long_long(ctx
.get_language()));
906 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
907 // disable include guard detection
908 if (vm
.count("noguard")) {
910 boost::wave::enable_include_guard_detection(
911 ctx
.get_language(), false));
915 // enable preserving comments mode
916 if (preserve_comments
) {
918 boost::wave::enable_preserve_comments(ctx
.get_language()));
921 // control the generation of #line directives
922 if (vm
.count("line")) {
923 int lineopt
= vm
["line"].as
<int>();
924 if (0 != lineopt
&& 1 != lineopt
&& 2 != lineopt
) {
925 cerr
<< "wave: bogus value for --line command line option: "
930 boost::wave::enable_emit_line_directives(ctx
.get_language(),
934 ctx
.get_hooks().enable_relative_names_in_line_directives(true);
937 // control whether whitespace should be inserted to disambiguate output
938 if (vm
.count("disambiguate")) {
939 int disambiguateopt
= vm
["disambiguate"].as
<int>();
940 if (0 != disambiguateopt
&& 1 != disambiguateopt
) {
941 cerr
<< "wave: bogus value for --disambiguate command line option: "
942 << disambiguateopt
<< endl
;
946 boost::wave::enable_insert_whitespace(ctx
.get_language(),
947 disambiguateopt
!= 0));
950 // add include directories to the system include search paths
951 if (vm
.count("sysinclude")) {
952 vector
<std::string
> syspaths
= vm
["sysinclude"].as
<vector
<std::string
> >();
954 vector
<std::string
>::const_iterator end
= syspaths
.end();
955 for (vector
<std::string
>::const_iterator cit
= syspaths
.begin();
958 ctx
.add_sysinclude_path(cmd_line_utils::trim_quotes(*cit
).c_str());
962 // add include directories to the include search paths
963 if (vm
.count("include")) {
964 cmd_line_utils::include_paths
const &ip
=
965 vm
["include"].as
<cmd_line_utils::include_paths
>();
966 vector
<std::string
>::const_iterator end
= ip
.paths
.end();
968 for (vector
<std::string
>::const_iterator cit
= ip
.paths
.begin();
971 ctx
.add_include_path(cmd_line_utils::trim_quotes(*cit
).c_str());
974 // if -I- was given on the command line, this has to be propagated
975 if (ip
.seen_separator
)
976 ctx
.set_sysinclude_delimiter();
978 // add system include directories to the include path
979 vector
<std::string
>::const_iterator sysend
= ip
.syspaths
.end();
980 for (vector
<std::string
>::const_iterator syscit
= ip
.syspaths
.begin();
981 syscit
!= sysend
; ++syscit
)
983 ctx
.add_sysinclude_path(cmd_line_utils::trim_quotes(*syscit
).c_str());
987 // add additional defined macros
988 if (vm
.count("define")) {
989 vector
<std::string
> const ¯os
= vm
["define"].as
<vector
<std::string
> >();
990 vector
<std::string
>::const_iterator end
= macros
.end();
991 for (vector
<std::string
>::const_iterator cit
= macros
.begin();
994 ctx
.add_macro_definition(*cit
);
998 // add additional predefined macros
999 if (vm
.count("predefine")) {
1000 vector
<std::string
> const &predefmacros
=
1001 vm
["predefine"].as
<vector
<std::string
> >();
1002 vector
<std::string
>::const_iterator end
= predefmacros
.end();
1003 for (vector
<std::string
>::const_iterator cit
= predefmacros
.begin();
1006 ctx
.add_macro_definition(*cit
, true);
1010 // undefine specified macros
1011 if (vm
.count("undefine")) {
1012 vector
<std::string
> const &undefmacros
=
1013 vm
["undefine"].as
<vector
<std::string
> >();
1014 vector
<std::string
>::const_iterator end
= undefmacros
.end();
1015 for (vector
<std::string
>::const_iterator cit
= undefmacros
.begin();
1018 ctx
.remove_macro_definition(*cit
, true);
1022 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
1023 // suppress expansion of specified macros
1024 if (vm
.count("noexpand")) {
1025 vector
<std::string
> const &noexpandmacros
=
1026 vm
["noexpand"].as
<vector
<std::string
> >();
1027 vector
<std::string
>::const_iterator end
= noexpandmacros
.end();
1028 for (vector
<std::string
>::const_iterator cit
= noexpandmacros
.begin();
1031 ctx
.get_hooks().add_noexpandmacro(*cit
);
1036 // maximal include nesting depth
1037 if (vm
.count("nesting")) {
1038 int max_depth
= vm
["nesting"].as
<int>();
1039 if (max_depth
< 1 || max_depth
> 100000) {
1040 cerr
<< "wave: bogus maximal include nesting depth: "
1041 << max_depth
<< endl
;
1044 ctx
.set_max_include_nesting_depth(max_depth
);
1047 // open the output file
1048 if (vm
.count("output")) {
1049 // try to open the file, where to put the preprocessed output
1050 fs::path
out_file (boost::wave::util::create_path(
1051 vm
["output"].as
<std::string
>()));
1053 if (out_file
== "-") {
1054 allow_output
= false; // inhibit output initially
1055 default_outfile
= "-";
1058 out_file
= boost::wave::util::complete_path(out_file
);
1059 boost::wave::util::create_directories(
1060 boost::wave::util::branch_path(out_file
));
1061 output
.open(out_file
.string().c_str());
1062 if (!output
.is_open()) {
1063 cerr
<< "wave: could not open output file: "
1064 << out_file
.string() << endl
;
1067 if (!license
.empty())
1069 default_outfile
= out_file
.string();
1072 else if (!input_is_stdin
&& vm
.count("autooutput")) {
1073 // generate output in the file <input_base_name>.i
1074 fs::path
out_file (boost::wave::util::create_path(file_name
));
1075 std::string
basename (boost::wave::util::leaf(out_file
));
1076 std::string::size_type pos
= basename
.find_last_of(".");
1078 if (std::string::npos
!= pos
)
1079 basename
= basename
.substr(0, pos
);
1080 out_file
= boost::wave::util::branch_path(out_file
) / (basename
+ ".i");
1082 boost::wave::util::create_directories(
1083 boost::wave::util::branch_path(out_file
));
1084 output
.open(out_file
.string().c_str());
1085 if (!output
.is_open()) {
1086 cerr
<< "wave: could not open output file: "
1087 << out_file
.string() << endl
;
1090 if (!license
.empty())
1092 default_outfile
= out_file
.string();
1095 // we assume the session to be interactive if input is stdin and output is
1096 // stdout and the output is not inhibited
1097 bool is_interactive
= input_is_stdin
&& !output
.is_open() && allow_output
;
1099 if (is_interactive
) {
1100 // if interactive we don't warn for missing endif's etc.
1102 boost::wave::enable_single_line(ctx
.get_language()), false);
1105 // analyze the input file
1106 context_type::iterator_type first
= ctx
.begin();
1107 context_type::iterator_type last
= ctx
.end();
1109 // preprocess the required include files
1110 if (vm
.count("forceinclude")) {
1111 // add the filenames to force as include files in _reverse_ order
1112 // the second parameter 'is_last' of the force_include function should
1113 // be set to true for the last (first given) file.
1114 std::vector
<std::string
> const &force
=
1115 vm
["forceinclude"].as
<std::vector
<std::string
> >();
1116 std::vector
<std::string
>::const_reverse_iterator rend
= force
.rend();
1117 for (std::vector
<std::string
>::const_reverse_iterator cit
= force
.rbegin();
1120 std::string
filename(*cit
);
1121 first
.force_include(filename
.c_str(), ++cit
== rend
);
1125 elapsed_time
.set_print_time(!input_is_stdin
&& vm
.count("timer") > 0);
1126 if (is_interactive
) {
1127 print_interactive_version(); // print welcome message
1128 load_state(vm
, ctx
); // load the internal tables from disc
1130 else if (vm
.count("state")) {
1131 // the option "state" is usable in interactive mode only
1132 cerr
<< "wave: ignoring the command line option 'state', "
1133 << "use it in interactive mode only." << endl
;
1136 // >>>>>>>>>>>>> The actual preprocessing happens here. <<<<<<<<<<<<<<<<<<<
1137 // loop over the input lines if reading from stdin, otherwise this loop
1138 // will be executed once
1140 // loop over all generated tokens outputting the generated text
1141 bool finished
= false;
1143 if (input_is_stdin
) {
1145 cout
<< ">>> "; // prompt if is interactive
1147 // read next line and continue
1149 if (!read_a_line(instream
, instring
))
1150 break; // end of input reached
1151 first
= ctx
.begin(instring
.begin(), instring
.end());
1154 bool need_to_advanve
= false;
1158 if (need_to_advanve
) {
1160 need_to_advanve
= false;
1163 while (first
!= last
) {
1164 // store the last known good token position
1165 current_position
= (*first
).get_position();
1167 // print out the current token value
1169 if (!output
.good()) {
1170 cerr
<< "wave: problem writing to the current "
1171 << "output file" << endl
;
1172 cerr
<< report_iostate_error(output
.rdstate());
1175 if (output
.is_open())
1176 output
<< (*first
).get_value();
1178 cout
<< (*first
).get_value();
1181 // advance to the next token
1186 catch (boost::wave::cpp_exception
const &e
) {
1187 // some preprocessing error
1188 if (is_interactive
|| boost::wave::is_recoverable(e
)) {
1189 error_count
+= report_error_message(ctx
, e
,
1190 treat_warnings_as_error
);
1191 need_to_advanve
= true; // advance to the next token
1194 throw; // re-throw for non-recoverable errors
1197 catch (boost::wave::cpplexer::lexing_exception
const &e
) {
1198 // some preprocessing error
1199 if (is_interactive
||
1200 boost::wave::cpplexer::is_recoverable(e
))
1203 report_error_message(e
, treat_warnings_as_error
);
1204 need_to_advanve
= true; // advance to the next token
1207 throw; // re-throw for non-recoverable errors
1210 } while (!finished
);
1211 } while (input_is_stdin
);
1214 save_state(vm
, ctx
); // write the internal tables to disc
1216 // list all defined macros at the end of the preprocessing
1217 if (vm
.count("macronames")) {
1218 if (!list_macro_names(ctx
, vm
["macronames"].as
<std::string
>()))
1221 if (vm
.count("macrocounts")) {
1222 if (!list_macro_counts(ctx
, vm
["macrocounts"].as
<std::string
>()))
1226 catch (boost::wave::cpp_exception
const &e
) {
1227 // some preprocessing error
1228 report_error_message(e
, treat_warnings_as_error
);
1231 catch (boost::wave::cpplexer::lexing_exception
const &e
) {
1232 // some lexing error
1233 report_error_message(e
, treat_warnings_as_error
);
1236 catch (std::exception
const &e
) {
1237 // use last recognized token to retrieve the error position
1239 << current_position
<< ": "
1240 << "exception caught: " << e
.what()
1245 // use last recognized token to retrieve the error position
1247 << current_position
<< ": "
1248 << "unexpected exception caught." << endl
;
1251 return -error_count
; // returns the number of errors as a negative integer
1254 ///////////////////////////////////////////////////////////////////////////////
1257 main (int argc
, char *argv
[])
1259 const std::string accepted_w_args
[] = {"error"};
1261 // test Wave compilation configuration
1262 if (!BOOST_WAVE_TEST_CONFIGURATION()) {
1263 cout
<< "wave: warning: the library this application was linked against was compiled "
1265 << " using a different configuration (see wave_config.hpp)."
1269 // analyze the command line options and arguments
1271 // declare the options allowed on the command line only
1272 po::options_description
desc_cmdline ("Options allowed on the command line only");
1274 desc_cmdline
.add_options()
1275 ("help,h", "print out program usage (this message)")
1276 ("version,v", "print the version number")
1277 ("copyright", "print out the copyright statement")
1278 ("config-file", po::value
<vector
<std::string
> >()->composing(),
1279 "specify a config file (alternatively: @filepath)")
1282 const std::string w_arg_desc
= "Warning settings. Currently supported: -W" +
1283 boost::algorithm::join(accepted_w_args
, ", -W");
1285 // declare the options allowed on command line and in config files
1286 po::options_description
desc_generic ("Options allowed additionally in a config file");
1288 desc_generic
.add_options()
1289 ("output,o", po::value
<std::string
>(),
1290 "specify a file [arg] to use for output instead of stdout or "
1291 "disable output [-]")
1293 "output goes into a file named <input_basename>.i")
1294 ("license", po::value
<std::string
>(),
1295 "prepend the content of the specified file to each created file")
1296 ("include,I", po::value
<cmd_line_utils::include_paths
>()->composing(),
1297 "specify an additional include directory")
1298 ("sysinclude,S", po::value
<vector
<std::string
> >()->composing(),
1299 "specify an additional system include directory")
1300 ("forceinclude,F", po::value
<std::vector
<std::string
> >()->composing(),
1301 "force inclusion of the given file")
1302 ("define,D", po::value
<std::vector
<std::string
> >()->composing(),
1303 "specify a macro to define (as macro[=[value]])")
1304 ("predefine,P", po::value
<std::vector
<std::string
> >()->composing(),
1305 "specify a macro to predefine (as macro[=[value]])")
1306 ("undefine,U", po::value
<std::vector
<std::string
> >()->composing(),
1307 "specify a macro to undefine")
1308 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
1309 ("noexpand,N", po::value
<std::vector
<std::string
> >()->composing(),
1310 "specify a macro name, which should not be expanded")
1312 ("nesting,n", po::value
<int>(),
1313 "specify a new maximal include nesting depth")
1314 ("warning,W", po::value
<std::vector
<std::string
> >()->composing(),
1318 po::options_description
desc_ext ("Extended options (allowed everywhere)");
1320 desc_ext
.add_options()
1321 ("traceto,t", po::value
<std::string
>(),
1322 "output macro expansion tracing information to a file [arg] "
1324 ("timer", "output overall elapsed computing time to stderr")
1325 ("long_long", "enable long long support in C++ mode")
1326 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
1327 ("variadics", "enable certain C99 extensions in C++ mode")
1328 ("c99", "enable C99 mode (implies --variadics)")
1330 #if BOOST_WAVE_SUPPORT_CPP0X != 0
1331 ("c++11", "enable C++11 mode (implies --variadics and --long_long)")
1333 ("listincludes,l", po::value
<std::string
>(),
1334 "list names of included files to a file [arg] or to stdout [-]")
1335 ("macronames,m", po::value
<std::string
>(),
1336 "list all defined macros to a file [arg] or to stdout [-]")
1337 ("macrocounts,c", po::value
<std::string
>(),
1338 "list macro invocation counts to a file [arg] or to stdout [-]")
1339 ("preserve,p", po::value
<int>()->default_value(0),
1340 "preserve whitespace\n"
1341 "0: no whitespace is preserved (default),\n"
1342 "1: begin of line whitespace is preserved,\n"
1343 "2: comments and begin of line whitespace is preserved,\n"
1344 "3: all whitespace is preserved")
1345 ("line,L", po::value
<int>()->default_value(1),
1346 "control the generation of #line directives\n"
1347 "0: no #line directives are generated,\n"
1348 "1: #line directives will be emitted (default),\n"
1349 "2: #line directives will be emitted using relative\n"
1351 ("disambiguate", po::value
<int>()->default_value(1),
1352 "control whitespace insertion to disambiguate\n"
1353 "consecutive tokens\n"
1354 "0: no additional whitespace is generated,\n"
1355 "1: whitespace is used to disambiguate output (default)")
1356 ("extended,x", "enable the #pragma wave system() directive")
1357 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1358 ("noguard,G", "disable include guard detection")
1359 ("listguards,g", po::value
<std::string
>(),
1360 "list names of files flagged as 'include once' to a file [arg] "
1363 #if BOOST_WAVE_SERIALIZATION != 0
1364 ("state,s", po::value
<std::string
>(),
1365 "load and save state information from/to the given file [arg] "
1366 "or 'wave.state' [-] (interactive mode only)")
1370 // combine the options for the different usage schemes
1371 po::options_description desc_overall_cmdline
;
1372 po::options_description desc_overall_cfgfile
;
1374 desc_overall_cmdline
.add(desc_cmdline
).add(desc_generic
).add(desc_ext
);
1375 desc_overall_cfgfile
.add(desc_generic
).add(desc_ext
);
1377 // parse command line and store results
1378 using namespace boost::program_options::command_line_style
;
1380 po::parsed_options
opts(po::parse_command_line(argc
, argv
,
1381 desc_overall_cmdline
, unix_style
, cmd_line_utils::at_option_parser
));
1382 po::variables_map vm
;
1384 po::store(opts
, vm
);
1387 // // Try to find a wave.cfg in the same directory as the executable was
1388 // // started from. If this exists, treat it as a wave config file
1389 // fs::path filename(argv[0]);
1391 // filename = filename.branch_path() / "wave.cfg";
1392 // cmd_line_utils::read_config_file_options(filename.string(),
1393 // desc_overall_cfgfile, vm, true);
1395 // extract the arguments from the parsed command line
1396 vector
<po::option
> arguments
;
1398 std::remove_copy_if(opts
.options
.begin(), opts
.options
.end(),
1399 back_inserter(arguments
), cmd_line_utils::is_argument());
1401 // try to find a config file somewhere up the filesystem hierarchy
1402 // starting with the input file path. This allows to use a general wave.cfg
1403 // file for all files in a certain project.
1404 if (arguments
.size() > 0 && arguments
[0].value
[0] != "-") {
1405 // construct full path of input file
1406 fs::path
input_dir (boost::wave::util::complete_path(
1407 boost::wave::util::create_path(arguments
[0].value
[0])));
1409 // chop of file name
1410 input_dir
= boost::wave::util::branch_path(
1411 boost::wave::util::normalize(input_dir
));
1413 // walk up the hierarchy, trying to find a file wave.cfg
1414 while (!input_dir
.empty()) {
1415 fs::path filename
= input_dir
/ "wave.cfg";
1416 if (cmd_line_utils::read_config_file_options(filename
.string(),
1417 desc_overall_cfgfile
, vm
, true))
1419 break; // break on the first cfg file found
1421 input_dir
= boost::wave::util::branch_path(input_dir
);
1425 // if there is specified at least one config file, parse it and add the
1426 // options to the main variables_map
1427 if (vm
.count("config-file")) {
1428 vector
<std::string
> const &cfg_files
=
1429 vm
["config-file"].as
<vector
<std::string
> >();
1430 vector
<std::string
>::const_iterator end
= cfg_files
.end();
1431 for (vector
<std::string
>::const_iterator cit
= cfg_files
.begin();
1434 // parse a single config file and store the results
1435 cmd_line_utils::read_config_file_options(*cit
,
1436 desc_overall_cfgfile
, vm
);
1440 // validate warning settings
1441 if (vm
.count("warning"))
1443 BOOST_FOREACH(const std::string
& arg
,
1444 vm
["warning"].as
<std::vector
<std::string
> >())
1446 if (boost::range::find(accepted_w_args
, arg
) ==
1447 boost::end(accepted_w_args
))
1449 cerr
<< "wave: Invalid warning setting: " << arg
<< endl
;
1455 // ... act as required
1456 if (vm
.count("help")) {
1457 po::options_description
desc_help (
1458 "Usage: wave [options] [@config-file(s)] [file]");
1460 desc_help
.add(desc_cmdline
).add(desc_generic
).add(desc_ext
);
1461 cout
<< desc_help
<< endl
;
1465 if (vm
.count("version")) {
1466 cout
<< get_version() << endl
;
1470 if (vm
.count("copyright")) {
1471 return print_copyright();
1474 // if there is no input file given, then take input from stdin
1475 if (0 == arguments
.size() || 0 == arguments
[0].value
.size() ||
1476 arguments
[0].value
[0] == "-")
1478 // preprocess the given input from stdin
1479 return do_actual_work("<stdin>", std::cin
, vm
, true);
1482 if (arguments
.size() > 1) {
1483 // this driver understands to parse one input file only
1484 cerr
<< "wave: more than one input file specified, "
1485 << "ignoring all but the first!" << endl
;
1488 std::string
file_name(arguments
[0].value
[0]);
1489 ifstream
instream(file_name
.c_str());
1491 // preprocess the given input file
1492 if (!instream
.is_open()) {
1493 cerr
<< "wave: could not open input file: " << file_name
<< endl
;
1496 return do_actual_work(file_name
, instream
, vm
, false);
1499 catch (std::exception
const &e
) {
1500 cout
<< "wave: exception caught: " << e
.what() << endl
;
1504 cerr
<< "wave: unexpected exception caught." << endl
;