123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- /*=============================================================================
- Boost.Wave: A Standard compliant C++ preprocessor library
- Sample: IDL oriented preprocessor
-
- http://www.boost.org/
- Copyright (c) 2001-2010 Hartmut Kaiser. Distributed under the Boost
- Software License, Version 1.0. (See accompanying file
- LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- =============================================================================*/
- #include "idl.hpp" // global configuration
- #include <boost/assert.hpp>
- #include <boost/program_options.hpp>
- #include <boost/filesystem/path.hpp>
- ///////////////////////////////////////////////////////////////////////////////
- // Include Wave itself
- #include <boost/wave.hpp>
- ///////////////////////////////////////////////////////////////////////////////
- // Include the lexer related stuff
- #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type
- #include "idllexer/idl_lex_iterator.hpp" // lexer type
- ///////////////////////////////////////////////////////////////////////////////
- // include lexer specifics, import lexer names
- //
- #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
- #include "idllexer/idl_re2c_lexer.hpp"
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- // include the grammar definitions, if these shouldn't be compiled separately
- // (ATTENTION: _very_ large compilation times!)
- //
- #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
- #include <boost/wave/grammars/cpp_intlit_grammar.hpp>
- #include <boost/wave/grammars/cpp_chlit_grammar.hpp>
- #include <boost/wave/grammars/cpp_grammar.hpp>
- #include <boost/wave/grammars/cpp_expression_grammar.hpp>
- #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
- #include <boost/wave/grammars/cpp_defined_grammar.hpp>
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- // import required names
- using namespace boost::spirit::classic;
- using std::string;
- using std::pair;
- using std::vector;
- using std::getline;
- using std::ifstream;
- using std::cout;
- using std::cerr;
- using std::endl;
- using std::ostream;
- using std::istreambuf_iterator;
- namespace po = boost::program_options;
- namespace fs = boost::filesystem;
- ///////////////////////////////////////////////////////////////////////////////
- // print the current version
- int print_version()
- {
- typedef boost::wave::idllexer::lex_iterator<
- boost::wave::cpplexer::lex_token<> >
- lex_iterator_type;
- typedef boost::wave::context<std::string::iterator, lex_iterator_type>
- context_type;
-
- string version (context_type::get_version_string());
- cout
- << version.substr(1, version.size()-2) // strip quotes
- << " (" << IDL_VERSION_DATE << ")" // add date
- << endl;
- return 0; // exit app
- }
- ///////////////////////////////////////////////////////////////////////////////
- // print the copyright statement
- int print_copyright()
- {
- char const *copyright[] = {
- "",
- "Sample: IDL oriented preprocessor",
- "Based on: Wave, A Standard conformant C++ preprocessor library",
- "It is hosted by http://www.boost.org/.",
- "",
- "Copyright (c) 2001-2010 Hartmut Kaiser, Distributed under the Boost",
- "Software License, Version 1.0. (See accompanying file",
- "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
- 0
- };
-
- for (int i = 0; 0 != copyright[i]; ++i)
- cout << copyright[i] << endl;
-
- return 0; // exit app
- }
- ///////////////////////////////////////////////////////////////////////////////
- namespace cmd_line_util {
- // Additional command line parser which interprets '@something' as an
- // option "config-file" with the value "something".
- pair<string, string> at_option_parser(string const&s)
- {
- if ('@' == s[0])
- return std::make_pair(string("config-file"), s.substr(1));
- else
- return pair<string, string>();
- }
- // class, which keeps include file information read from the command line
- class include_paths {
- public:
- include_paths() : seen_separator(false) {}
- vector<string> paths; // stores user paths
- vector<string> syspaths; // stores system paths
- bool seen_separator; // command line contains a '-I-' option
- // Function which validates additional tokens from command line.
- static void
- validate(boost::any &v, vector<string> const &tokens)
- {
- if (v.empty())
- v = boost::any(include_paths());
- include_paths *p = boost::any_cast<include_paths>(&v);
- BOOST_ASSERT(p);
- // Assume only one path per '-I' occurrence.
- string t = tokens[0];
- if (t == "-") {
- // found -I- option, so switch behaviour
- p->seen_separator = true;
- }
- else if (p->seen_separator) {
- // store this path as a system path
- p->syspaths.push_back(t);
- }
- else {
- // store this path as an user path
- p->paths.push_back(t);
- }
- }
- };
- // Read all options from a given config file, parse and add them to the
- // given variables_map
- void read_config_file_options(string const &filename,
- po::options_description const &desc, po::variables_map &vm,
- bool may_fail = false)
- {
- ifstream ifs(filename.c_str());
- if (!ifs.is_open()) {
- if (!may_fail) {
- cerr << filename
- << ": command line warning: config file not found"
- << endl;
- }
- return;
- }
-
- vector<string> options;
- string line;
- while (std::getline(ifs, line)) {
- // skip empty lines
- string::size_type pos = line.find_first_not_of(" \t");
- if (pos == string::npos)
- continue;
- // skip comment lines
- if ('#' != line[pos])
- options.push_back(line);
- }
- if (options.size() > 0) {
- using namespace boost::program_options::command_line_style;
- po::store(po::command_line_parser(options)
- .options(desc).style(unix_style).run(), vm);
- po::notify(vm);
- }
- }
- // predicate to extract all positional arguments from the command line
- struct is_argument {
- bool operator()(po::option const &opt)
- {
- return (opt.position_key == -1) ? true : false;
- }
- };
- ///////////////////////////////////////////////////////////////////////////////
- }
- ///////////////////////////////////////////////////////////////////////////////
- //
- // Special validator overload, which allows to handle the -I- syntax for
- // switching the semantics of an -I option.
- //
- ///////////////////////////////////////////////////////////////////////////////
- namespace boost { namespace program_options {
- void validate(boost::any &v, std::vector<std::string> const &s,
- cmd_line_util::include_paths *, int)
- {
- cmd_line_util::include_paths::validate(v, s);
- }
- }} // namespace boost::program_options
- ///////////////////////////////////////////////////////////////////////////////
- // do the actual preprocessing
- int
- do_actual_work (std::string file_name, po::variables_map const &vm)
- {
- // current file position is saved for exception handling
- boost::wave::util::file_position_type current_position;
- try {
- // process the given file
- ifstream instream(file_name.c_str());
- string instring;
- if (!instream.is_open()) {
- cerr << "waveidl: could not open input file: " << file_name << endl;
- return -1;
- }
- instream.unsetf(std::ios::skipws);
-
- #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
- // this is known to be very slow for large files on some systems
- copy (istream_iterator<char>(instream),
- istream_iterator<char>(),
- inserter(instring, instring.end()));
- #else
- instring = string(istreambuf_iterator<char>(instream.rdbuf()),
- istreambuf_iterator<char>());
- #endif
- // This sample uses the lex_token type predefined in the Wave library, but
- // but uses a custom lexer type.
- typedef boost::wave::idllexer::lex_iterator<
- boost::wave::cpplexer::lex_token<> >
- lex_iterator_type;
- typedef boost::wave::context<std::string::iterator, lex_iterator_type>
- context_type;
- // The C++ preprocessor iterators shouldn't be constructed directly. They
- // are to be generated through a boost::wave::context<> object. This
- // boost::wave::context object is additionally to be used to initialize and
- // define different parameters of the actual preprocessing.
- // The preprocessing of the input stream is done on the fly behind the
- // scenes during iteration over the context_type::iterator_type stream.
- context_type ctx (instring.begin(), instring.end(), file_name.c_str());
- // add include directories to the system include search paths
- if (vm.count("sysinclude")) {
- vector<string> const &syspaths =
- vm["sysinclude"].as<vector<string> >();
- vector<string>::const_iterator end = syspaths.end();
- for (vector<string>::const_iterator cit = syspaths.begin();
- cit != end; ++cit)
- {
- ctx.add_sysinclude_path((*cit).c_str());
- }
- }
-
- // add include directories to the include search paths
- if (vm.count("include")) {
- cmd_line_util::include_paths const &ip =
- vm["include"].as<cmd_line_util::include_paths>();
- vector<string>::const_iterator end = ip.paths.end();
- for (vector<string>::const_iterator cit = ip.paths.begin();
- cit != end; ++cit)
- {
- ctx.add_include_path((*cit).c_str());
- }
- // if on the command line was given -I- , this has to be propagated
- if (ip.seen_separator)
- ctx.set_sysinclude_delimiter();
-
- // add system include directories to the include path
- vector<string>::const_iterator sysend = ip.syspaths.end();
- for (vector<string>::const_iterator syscit = ip.syspaths.begin();
- syscit != sysend; ++syscit)
- {
- ctx.add_sysinclude_path((*syscit).c_str());
- }
- }
-
- // add additional defined macros
- if (vm.count("define")) {
- vector<string> const ¯os = vm["define"].as<vector<string> >();
- vector<string>::const_iterator end = macros.end();
- for (vector<string>::const_iterator cit = macros.begin();
- cit != end; ++cit)
- {
- ctx.add_macro_definition(*cit);
- }
- }
- // add additional predefined macros
- if (vm.count("predefine")) {
- vector<string> const &predefmacros =
- vm["predefine"].as<vector<string> >();
- vector<string>::const_iterator end = predefmacros.end();
- for (vector<string>::const_iterator cit = predefmacros.begin();
- cit != end; ++cit)
- {
- ctx.add_macro_definition(*cit, true);
- }
- }
- // undefine specified macros
- if (vm.count("undefine")) {
- vector<string> const &undefmacros =
- vm["undefine"].as<vector<string> >();
- vector<string>::const_iterator end = undefmacros.end();
- for (vector<string>::const_iterator cit = undefmacros.begin();
- cit != end; ++cit)
- {
- ctx.remove_macro_definition((*cit).c_str(), true);
- }
- }
- // open the output file
- std::ofstream output;
-
- if (vm.count("output")) {
- // try to open the file, where to put the preprocessed output
- string out_file (vm["output"].as<string>());
-
- output.open(out_file.c_str());
- if (!output.is_open()) {
- cerr << "waveidl: could not open output file: " << out_file
- << endl;
- return -1;
- }
- }
- else {
- // output the preprocessed result to std::cout
- output.copyfmt(cout);
- output.clear(cout.rdstate());
- static_cast<std::basic_ios<char> &>(output).rdbuf(cout.rdbuf());
- }
- // analyze the input file
- context_type::iterator_type first = ctx.begin();
- context_type::iterator_type last = ctx.end();
-
- // loop over all generated tokens outputing the generated text
- while (first != last) {
- // print out the string representation of this token (skip comments)
- using namespace boost::wave;
- // store the last known good token position
- current_position = (*first).get_position();
- token_id id = token_id(*first);
- if (T_CPPCOMMENT == id || T_NEWLINE == id) {
- // C++ comment tokens contain the trailing newline
- output << endl;
- }
- else if (id != T_CCOMMENT) {
- // print out the current token value
- output << (*first).get_value();
- }
- ++first; // advance to the next token
- }
- }
- catch (boost::wave::cpp_exception const& e) {
- // some preprocessing error
- cerr
- << e.file_name() << "(" << e.line_no() << "): "
- << e.description() << endl;
- return 1;
- }
- catch (boost::wave::cpplexer::lexing_exception const& e) {
- // some lexing error
- cerr
- << e.file_name() << "(" << e.line_no() << "): "
- << e.description() << endl;
- return 2;
- }
- catch (std::exception const& e) {
- // use last recognized token to retrieve the error position
- cerr
- << current_position.get_file()
- << "(" << current_position.get_line() << "): "
- << "exception caught: " << e.what()
- << endl;
- return 3;
- }
- catch (...) {
- // use last recognized token to retrieve the error position
- cerr
- << current_position.get_file()
- << "(" << current_position.get_line() << "): "
- << "unexpected exception caught." << endl;
- return 4;
- }
- return 0;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // main entry point
- int
- main (int argc, char *argv[])
- {
- try {
- // analyze the command line options and arguments
-
- // declare the options allowed from the command line only
- po::options_description desc_cmdline ("Options allowed on the command line only");
-
- desc_cmdline.add_options()
- ("help,h", "print out program usage (this message)")
- ("version,v", "print the version number")
- ("copyright,c", "print out the copyright statement")
- ("config-file", po::value<vector<string> >(),
- "specify a config file (alternatively: @filepath)")
- ;
- // declare the options allowed on command line and in config files
- po::options_description desc_generic ("Options allowed additionally in a config file");
- desc_generic.add_options()
- ("output,o", po::value<string>()->composing(),
- "specify a file to use for output instead of stdout")
- ("include,I", po::value<cmd_line_util::include_paths>()->composing(),
- "specify an additional include directory")
- ("sysinclude,S", po::value<vector<string> >()->composing(),
- "specify an additional system include directory")
- ("define,D", po::value<vector<string> >()->composing(),
- "specify a macro to define (as macro[=[value]])")
- ("predefine,P", po::value<vector<string> >()->composing(),
- "specify a macro to predefine (as macro[=[value]])")
- ("undefine,U", po::value<vector<string> >()->composing(),
- "specify a macro to undefine")
- ;
-
- // combine the options for the different usage schemes
- po::options_description desc_overall_cmdline;
- po::options_description desc_overall_cfgfile;
- desc_overall_cmdline.add(desc_cmdline).add(desc_generic);
- desc_overall_cfgfile.add(desc_generic);
-
- // parse command line and store results
- using namespace boost::program_options::command_line_style;
- po::parsed_options opts = po::parse_command_line(argc, argv,
- desc_overall_cmdline, unix_style, cmd_line_util::at_option_parser);
- po::variables_map vm;
-
- po::store(opts, vm);
- po::notify(vm);
-
- // Try to find a waveidl.cfg in the same directory as the executable was
- // started from. If this exists, treat it as a wave config file
- fs::path filename(argv[0], fs::native);
- filename = filename.branch_path() / "waveidl.cfg";
- cmd_line_util::read_config_file_options(filename.string(),
- desc_overall_cfgfile, vm, true);
- // if there is specified at least one config file, parse it and add the
- // options to the main variables_map
- if (vm.count("config-file")) {
- vector<string> const &cfg_files =
- vm["config-file"].as<vector<string> >();
- vector<string>::const_iterator end = cfg_files.end();
- for (vector<string>::const_iterator cit = cfg_files.begin();
- cit != end; ++cit)
- {
- // parse a single config file and store the results
- cmd_line_util::read_config_file_options(*cit,
- desc_overall_cfgfile, vm);
- }
- }
- // ... act as required
- if (vm.count("help")) {
- po::options_description desc_help (
- "Usage: waveidl [options] [@config-file(s)] file");
- desc_help.add(desc_cmdline).add(desc_generic);
- cout << desc_help << endl;
- return 1;
- }
-
- if (vm.count("version")) {
- return print_version();
- }
- if (vm.count("copyright")) {
- return print_copyright();
- }
-
- // extract the arguments from the parsed command line
- vector<po::option> arguments;
-
- std::remove_copy_if(opts.options.begin(), opts.options.end(),
- inserter(arguments, arguments.end()), cmd_line_util::is_argument());
-
- // if there is no input file given, then exit
- if (0 == arguments.size() || 0 == arguments[0].value.size()) {
- cerr << "waveidl: no input file given, "
- << "use --help to get a hint." << endl;
- return 5;
- }
- // preprocess the given input file
- return do_actual_work(arguments[0].value[0], vm);
- }
- catch (std::exception const& e) {
- cout << "waveidl: exception caught: " << e.what() << endl;
- return 6;
- }
- catch (...) {
- cerr << "waveidl: unexpected exception caught." << endl;
- return 7;
- }
- }
|