1 /*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
4 Sample: List include dependencies of a given source file
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
9 To get a hint which commandline options are supported, call it with the
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 =============================================================================*/
19 #include "list_includes.hpp" // config data
21 ///////////////////////////////////////////////////////////////////////////////
22 // include required boost libraries
23 #include <boost/assert.hpp>
24 #include <boost/program_options.hpp>
26 ///////////////////////////////////////////////////////////////////////////////
27 // Include Wave itself
28 #include <boost/wave.hpp>
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
35 ///////////////////////////////////////////////////////////////////////////////
36 // Include the default context trace policies
37 #include <boost/wave/preprocessing_hooks.hpp>
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>
47 ///////////////////////////////////////////////////////////////////////////////
48 // import required names
49 using namespace boost::spirit::classic
;
59 using std::istreambuf_iterator
;
61 namespace po
= boost::program_options
;
63 ///////////////////////////////////////////////////////////////////////////////
64 namespace cmd_line_util
{
66 // predicate to extract all positional arguments from the command line
68 bool operator()(po::option
const &opt
)
70 return (opt
.position_key
== -1) ? true : false;
74 ///////////////////////////////////////////////////////////////////////////////
77 ///////////////////////////////////////////////////////////////////////////////
78 // print the current version
82 // get time of last compilation of this file
83 boost::wave::util::time_conversion_helper
compilation_time(__DATE__
" " __TIME__
);
85 // calculate the number of days since Jan 29 2003
86 // (the day the list_includes project was started)
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
94 long seconds
= long(std::difftime(compilation_time
.get_time(),
95 std::mktime(&first_day
)));
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
105 ///////////////////////////////////////////////////////////////////////////////
107 struct trace_include_files
108 : public boost::wave::context_policies::default_preprocessing_hooks
110 trace_include_files(set
<string
> &files_
)
111 : files(files_
), include_depth(0)
114 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
116 opened_include_file(string
const &relname
, string
const &filename
,
117 std::size_t /*include_depth*/, bool is_system_include
)
119 template <typename ContextT
>
121 opened_include_file(ContextT
const& ctx
, std::string
const& relname
,
122 std::string
const& filename
, bool is_system_include
)
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
)
130 cout
<< filename
<< endl
;
132 files
.insert(filename
);
137 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
138 void returning_from_include_file()
140 template <typename ContextT
>
141 void returning_from_include_file(ContextT
const& ctx
)
148 std::size_t include_depth
;
151 ///////////////////////////////////////////////////////////////////////////////
154 do_actual_work(vector
<string
> const &arguments
, po::variables_map
const &vm
)
156 // current file position is saved for exception handling
157 boost::wave::util::file_position_type current_position
;
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
)
165 ifstream
instream((*file_it
).c_str());
168 if (!instream
.is_open()) {
169 cerr
<< "Could not open input file: " << *file_it
<< endl
;
172 instream
.unsetf(std::ios::skipws
);
173 instring
= string(istreambuf_iterator
<char>(instream
.rdbuf()),
174 istreambuf_iterator
<char>());
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
<> >
181 typedef boost::wave::context
<
182 std::string::iterator
, lex_iterator_type
,
183 boost::wave::iteration_context_policies::load_file_to_string
,
188 trace_include_files
trace(files
);
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
);
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();
206 ctx
.add_include_path((*cit
).c_str());
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();
218 ctx
.add_sysinclude_path((*cit
).c_str());
222 // analyze the actual file
223 context_type::iterator_type first
= ctx
.begin();
224 context_type::iterator_type last
= ctx
.end();
226 cout
<< "Printing dependency information for: "
229 while (first
!= last
) {
230 current_position
= (*first
).get_position();
234 // prepend endl before next file
238 catch (boost::wave::cpp_exception
&e
) {
239 // some preprocessing error
241 << e
.file_name() << "(" << e
.line_no() << "): "
242 << e
.description() << endl
;
245 catch (std::exception
&e
) {
246 // use last recognized token to retrieve the error position
248 << current_position
.get_file()
249 << "(" << current_position
.get_line() << "): "
250 << "exception caught: " << e
.what()
255 // use last recognized token to retrieve the error position
257 << current_position
.get_file()
258 << "(" << current_position
.get_line() << "): "
259 << "unexpected exception caught." << endl
;
265 ///////////////////////////////////////////////////////////////////////////////
268 main (int argc
, char *argv
[])
271 // analyze the command line options and arguments
272 vector
<string
> syspathes
;
273 po::options_description
desc("Usage: list_includes [options] file ...");
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")
284 using namespace boost::program_options::command_line_style
;
286 po::parsed_options opts
= po::parse_command_line(argc
, argv
, desc
, unix_style
);
287 po::variables_map vm
;
292 if (vm
.count("help")) {
293 cout
<< desc
<< endl
;
297 if (vm
.count("version")) {
298 return print_version();
301 // extract the arguments from the parsed command line
302 vector
<po::option
> arguments
;
304 std::remove_copy_if(opts
.options
.begin(), opts
.options
.end(),
305 inserter(arguments
, arguments
.end()), cmd_line_util::is_argument());
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
;
314 // iterate over all given input files
315 return do_actual_work(arguments
[0].value
, vm
);
317 catch (std::exception
&e
) {
318 cout
<< "list_includes: exception caught: " << e
.what() << endl
;
322 cerr
<< "list_includes: unexpected exception caught." << endl
;