list_includes.cpp 11 KB

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