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