cb7e674efc
There was a small missing parameter, as a result it was not possible to redirect stdout to a file.
539 lines
19 KiB
C++
539 lines
19 KiB
C++
/*=============================================================================
|
|
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;
|
|
}
|
|
}
|
|
|