]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
20effc67 TL |
39 | #include <iostream> |
40 | ||
7c673cae FG |
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 { | |
68 | 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 | ||
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 | { | |
110 | 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 | |
116 | 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 | |
138 | 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 | |
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 | |
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 |