• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Boost.Wave: A Standard compliant C++ preprocessor library
3 
4     Sample: List include dependencies of a given source file
5 
6     The 'list_includes' sample shows a simple way, how to use the Wave C++
7     preprocessor library to extract a list of included files from a given
8     source file.
9     To get a hint which commandline options are supported, call it with the
10     --help option.
11 
12     http://www.boost.org/
13 
14     Copyright (c) 2001-2010 Hartmut Kaiser. Distributed under the Boost
15     Software License, Version 1.0. (See accompanying file
16     LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
17 =============================================================================*/
18 
19 #include "list_includes.hpp"            // config data
20 
21 ///////////////////////////////////////////////////////////////////////////////
22 //  include required boost libraries
23 #include <boost/assert.hpp>
24 #include <boost/program_options.hpp>
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 //  Include Wave itself
28 #include <boost/wave.hpp>
29 
30 ///////////////////////////////////////////////////////////////////////////////
31 // Include the lexer stuff
32 #include <boost/wave/cpplexer/cpp_lex_token.hpp>    // standard token type
33 #include "lexertl_iterator.hpp"                     // lexertl based lexer
34 
35 ///////////////////////////////////////////////////////////////////////////////
36 // Include the default context trace policies
37 #include <boost/wave/preprocessing_hooks.hpp>
38 
39 #include <iostream>
40 
41 ///////////////////////////////////////////////////////////////////////////////
42 //  include lexer specifics, import lexer names
43 #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
44 #include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
45 #endif
46 
47 ///////////////////////////////////////////////////////////////////////////////
48 //  import required names
49 using namespace boost::spirit::classic;
50 
51 using std::string;
52 using std::vector;
53 using std::set;
54 using std::cout;
55 using std::cerr;
56 using std::endl;
57 using std::ifstream;
58 using std::ostream;
59 using std::istreambuf_iterator;
60 
61 namespace po = boost::program_options;
62 
63 ///////////////////////////////////////////////////////////////////////////////
64 namespace cmd_line_util {
65 
66     // predicate to extract all positional arguments from the command line
67     struct is_argument {
operator ()cmd_line_util::is_argument68         bool operator()(po::option const &opt)
69         {
70           return (opt.position_key == -1) ? true : false;
71         }
72     };
73 
74 ///////////////////////////////////////////////////////////////////////////////
75 }
76 
77 ///////////////////////////////////////////////////////////////////////////////
78 // print the current version
79 
print_version()80 int print_version()
81 {
82 // get time of last compilation of this file
83 boost::wave::util::time_conversion_helper compilation_time(__DATE__ " " __TIME__);
84 
85 // calculate the number of days since Jan 29 2003
86 // (the day the list_includes project was started)
87 std::tm first_day;
88 
89     std::memset (&first_day, 0, sizeof(std::tm));
90     first_day.tm_mon = 0;           // Jan
91     first_day.tm_mday = 29;         // 29
92     first_day.tm_year = 103;        // 2003
93 
94 long seconds = long(std::difftime(compilation_time.get_time(),
95     std::mktime(&first_day)));
96 
97     cout
98         << LIST_INCLUDES_VERSION_MAJOR << '.'
99         << LIST_INCLUDES_VERSION_MINOR << '.'
100         << LIST_INCLUDES_VERSION_SUBMINOR << '.'
101         << seconds/(3600*24);       // get number of days from seconds
102     return 1;                       // exit app
103 }
104 
105 ///////////////////////////////////////////////////////////////////////////////
106 //  policy class
107 struct trace_include_files
108 :   public boost::wave::context_policies::default_preprocessing_hooks
109 {
trace_include_filestrace_include_files110     trace_include_files(set<string> &files_)
111     :   files(files_), include_depth(0)
112     {}
113 
114 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
115     void
opened_include_filetrace_include_files116     opened_include_file(string const &relname, string const &filename,
117         std::size_t /*include_depth*/, bool is_system_include)
118 #else
119     template <typename ContextT>
120     void
121     opened_include_file(ContextT const& ctx, std::string const& relname,
122         std::string const& filename, bool is_system_include)
123 #endif
124     {
125         set<string>::iterator it = files.find(filename);
126         if (it == files.end()) {
127             // print indented filename
128             for (std::size_t i = 0; i < include_depth; ++i)
129                 cout << " ";
130             cout << filename << endl;
131 
132             files.insert(filename);
133         }
134         ++include_depth;
135     }
136 
137 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
returning_from_include_filetrace_include_files138     void returning_from_include_file()
139 #else
140     template <typename ContextT>
141     void returning_from_include_file(ContextT const& ctx)
142 #endif
143     {
144         --include_depth;
145     }
146 
147     set<string> &files;
148     std::size_t include_depth;
149 };
150 
151 ///////////////////////////////////////////////////////////////////////////////
152 //
153 int
do_actual_work(vector<string> const & arguments,po::variables_map const & vm)154 do_actual_work(vector<string> const &arguments, po::variables_map const &vm)
155 {
156 // current file position is saved for exception handling
157 boost::wave::util::file_position_type current_position;
158 
159     try {
160     // list the included files for all arguments given
161         vector<string>::const_iterator lastfile = arguments.end();
162         for (vector<string>::const_iterator file_it = arguments.begin();
163              file_it != lastfile; ++file_it)
164         {
165         ifstream instream((*file_it).c_str());
166         string instring;
167 
168             if (!instream.is_open()) {
169                 cerr << "Could not open input file: " << *file_it << endl;
170                 continue;
171             }
172             instream.unsetf(std::ios::skipws);
173             instring = string(istreambuf_iterator<char>(instream.rdbuf()),
174                               istreambuf_iterator<char>());
175 
176         //  The template boost::wave::cpplexer::lex_token<> is the token type to be
177         //  used by the Wave library.
178             typedef boost::wave::cpplexer::lexertl::lex_iterator<
179                     boost::wave::cpplexer::lex_token<> >
180                 lex_iterator_type;
181             typedef boost::wave::context<
182                     std::string::iterator, lex_iterator_type,
183                     boost::wave::iteration_context_policies::load_file_to_string,
184                     trace_include_files
185                 > context_type;
186 
187         set<string> files;
188         trace_include_files trace(files);
189 
190         // The preprocessor iterator shouldn't be constructed directly. It is
191         // to be generated through a wave::context<> object. This wave:context<>
192         // object is additionally to be used to initialize and define different
193         // parameters of the actual preprocessing.
194         // The preprocessing of the input stream is done on the fly behind the
195         // scenes during iteration over the context_type::iterator_type stream.
196         context_type ctx (instring.begin(), instring.end(), (*file_it).c_str(), trace);
197 
198         // add include directories to the include path
199             if (vm.count("include")) {
200                 vector<string> const &paths =
201                     vm["include"].as<vector<string> >();
202                 vector<string>::const_iterator end = paths.end();
203                 for (vector<string>::const_iterator cit = paths.begin();
204                      cit != end; ++cit)
205                 {
206                     ctx.add_include_path((*cit).c_str());
207                 }
208             }
209 
210         // add system include directories to the include path
211             if (vm.count("sysinclude")) {
212                 vector<string> const &syspaths =
213                     vm["sysinclude"].as<vector<string> >();
214                 vector<string>::const_iterator end = syspaths.end();
215                 for (vector<string>::const_iterator cit = syspaths.begin();
216                      cit != end; ++cit)
217                 {
218                     ctx.add_sysinclude_path((*cit).c_str());
219                 }
220             }
221 
222         // analyze the actual file
223         context_type::iterator_type first = ctx.begin();
224         context_type::iterator_type last = ctx.end();
225 
226             cout << "Printing dependency information for: "
227                  << *file_it << endl;
228 
229             while (first != last) {
230                 current_position = (*first).get_position();
231                 ++first;
232             }
233 
234         // prepend endl before next file
235             cout << endl;
236         }
237     }
238     catch (boost::wave::cpp_exception &e) {
239     // some preprocessing error
240         cerr
241             << e.file_name() << "(" << e.line_no() << "): "
242             << e.description() << endl;
243         return 2;
244     }
245     catch (std::exception &e) {
246     // use last recognized token to retrieve the error position
247         cerr
248             << current_position.get_file()
249             << "(" << current_position.get_line() << "): "
250             << "exception caught: " << e.what()
251             << endl;
252         return 3;
253     }
254     catch (...) {
255     // use last recognized token to retrieve the error position
256         cerr
257             << current_position.get_file()
258             << "(" << current_position.get_line() << "): "
259             << "unexpected exception caught." << endl;
260         return 4;
261     }
262     return 0;
263 }
264 
265 ///////////////////////////////////////////////////////////////////////////////
266 //  here we go!
267 int
main(int argc,char * argv[])268 main (int argc, char *argv[])
269 {
270     try {
271     // analyze the command line options and arguments
272     vector<string> syspathes;
273     po::options_description desc("Usage: list_includes [options] file ...");
274 
275         desc.add_options()
276             ("help,h", "print out program usage (this message)")
277             ("version,v", "print the version number")
278             ("include,I", po::value<vector<string> >(),
279                 "specify additional include directory")
280             ("sysinclude,S", po::value<vector<string> >(),
281                 "specify additional system include directory")
282         ;
283 
284         using namespace boost::program_options::command_line_style;
285 
286     po::parsed_options opts = po::parse_command_line(argc, argv, desc, unix_style);
287     po::variables_map vm;
288 
289         po::store(opts, vm);
290         po::notify(vm);
291 
292         if (vm.count("help")) {
293             cout << desc << endl;
294             return 1;
295         }
296 
297         if (vm.count("version")) {
298             return print_version();
299         }
300 
301     // extract the arguments from the parsed command line
302     vector<po::option> arguments;
303 
304         std::remove_copy_if(opts.options.begin(), opts.options.end(),
305             inserter(arguments, arguments.end()), cmd_line_util::is_argument());
306 
307     // if there is no input file given, then exit
308         if (0 == arguments.size() || 0 == arguments[0].value.size()) {
309             cerr << "list_includes: No input file given. "
310                  << "Use --help to get a hint." << endl;
311             return 5;
312         }
313 
314     // iterate over all given input files
315         return do_actual_work(arguments[0].value , vm);
316     }
317     catch (std::exception &e) {
318         cout << "list_includes: exception caught: " << e.what() << endl;
319         return 6;
320     }
321     catch (...) {
322         cerr << "list_includes: unexpected exception caught." << endl;
323         return 7;
324     }
325 }
326 
327