1 //
2 // Copyright (C) 2006-2007 Maciej Sobczak
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //
7
8 #include "config.h"
9 #include "structures/SourceFiles.h"
10 #include "plugins/Profiles.h"
11 #include "plugins/Rules.h"
12 #include "plugins/Exclusions.h"
13 #include "plugins/Transformations.h"
14 #include "plugins/Parameters.h"
15 #include "plugins/Reports.h"
16 #include "plugins/RootDirectory.h"
17 #include <iostream>
18 #include <fstream>
19 #include <string>
20 #include <vector>
21 #include <cstdlib>
22 #include <sys/stat.h>
23 #include <cstring>
24 #include <cerrno>
25 #include <boost/program_options.hpp>
26 #include <boost/foreach.hpp>
27 #include "get_vera_root_default.h"
28
29 #define foreach BOOST_FOREACH
30
31 #ifdef _MSC_VER
32 // vm.count() used as a bool
33 #pragma warning(disable:4800)
34 #endif
35
36 template<typename Files, typename Options, typename Reporter>
doReports(Files & reports,Options & vm,Reporter * reporter)37 void doReports(Files & reports, Options & vm, Reporter * reporter)
38 {
39 foreach (const std::string & fn, reports)
40 {
41 if (fn == "-")
42 {
43 if (vm.count("warning") || vm.count("error"))
44 {
45 (*reporter)(std::cerr, vm.count("no-duplicate"));
46 }
47 else
48 {
49 (*reporter)(std::cout, vm.count("no-duplicate"));
50 }
51 }
52 else
53 {
54 std::ofstream file(fn.c_str());
55 if (file.is_open() == false)
56 {
57 throw std::runtime_error(
58 "Cannot open " + fn + ": " + strerror(errno));
59 }
60 (*reporter)(file, vm.count("no-duplicate"));
61 if (file.bad())
62 {
63 throw std::runtime_error(
64 "Cannot write to " + fn + ": " + strerror(errno));
65 }
66 file.close();
67 }
68 }
69
70 }
71
boost_main(int argc,char * argv[])72 int boost_main(int argc, char * argv[])
73 {
74 // The directory containing the profile and rule definitions
75 // by default it is (in this order, first has highest precedence):
76 // - VERA_ROOT (if VERA_ROOT is defined)
77 // - HOME/.vera++ (if HOME is defined)
78 // - current directory (if scripts and profile are present)
79 // - /usr/lib/vera++/ default debian directory
80
81 Vera::Plugins::RootDirectory::DirectoryName veraRoot(get_vera_root_default(argv[0]));
82
83 struct stat St;
84 bool isInCurrent = ( (stat( "./scripts", &St ) == 0 || stat( "./rules", &St ) == 0
85 || stat( "./transforms", &St ) == 0)
86 && stat( "./profiles", &St ) == 0 );
87
88 // scripts and profiles folders are inside current directory
89 if (isInCurrent)
90 {
91 // we can override /usr/lib/vera++
92 veraRoot = ".";
93 }
94 char * veraRootEnv = getenv("HOME");
95 if (veraRootEnv != NULL)
96 {
97 Vera::Plugins::RootDirectory::DirectoryName veraRootTmp(veraRootEnv);
98 veraRootTmp += "/.vera++";
99 bool isInHome = ( stat( veraRootTmp.c_str(), &St ) == 0 );
100 if (isInHome)
101 {
102 // We assume that if the user has a .vera++ folder in
103 // their home then we can override the current
104 // directory
105 // We don't want to override the current directory
106 // only because $HOME is defined.
107 veraRoot = veraRootEnv;
108 veraRoot += "/.vera++";
109 }
110 }
111 veraRootEnv = getenv("VERA_ROOT");
112 if (veraRootEnv != NULL)
113 {
114 veraRoot = veraRootEnv;
115 }
116
117 std::string profile = "default";
118 std::string transform;
119 std::vector<std::string> rules;
120 std::vector<std::string> parameters;
121 std::vector<std::string> parameterFiles;
122 std::vector<std::string> inputs;
123 std::vector<std::string> inputFiles;
124 std::vector<std::string> exclusionFiles;
125 // outputs
126 std::vector<std::string> stdreports;
127 std::vector<std::string> vcreports;
128 std::vector<std::string> xmlreports;
129 std::vector<std::string> checkstylereports;
130 /** Define and parse the program options
131 */
132 namespace po = boost::program_options;
133 po::options_description visibleOptions("Options");
134 visibleOptions.add_options()
135 ("profile,p", po::value(&profile), "execute all rules from the given profile")
136 ("rule,R", po::value(&rules), "execute the given rule (note: the .tcl extension is added"
137 " automatically. can be used many times.)")
138 ("transform", po::value(&transform), "execute the given transformation")
139 ("std-report,o", po::value(&stdreports), "write the standard (gcc-like) report to this"
140 "file. Default is standard or error output depending on the options."
141 " (note: may be used many times.)")
142 ("vc-report,v", po::value(&vcreports), "write the Visual C report to this file."
143 " Not used by default. (note: may be used many times.)")
144 ("xml-report,x", po::value(&xmlreports), "write the XML report to this file."
145 " Not used by default. (note: may be used many times.)")
146 ("checkstyle-report,c", po::value(&checkstylereports),
147 "write the checkstyle report to this file."
148 " Not used by default. (note: may be used many times.)")
149 ("show-rule,s", "include rule name in each report")
150 ("no-duplicate,d", "do not duplicate messages if a single rule is violated many times in a"
151 " single line of code")
152 ("warning,w", "reports are marked as warning and generated on error output")
153 ("error,e", "reports are marked as error and generated on error output."
154 " A non zero exit code is used when one or more reports are generated.")
155 ("quiet,q", "don't display the reports")
156 ("summary,S", "display the number of reports and the number of processed files")
157 ("parameters", po::value(¶meterFiles), "read parameters from file"
158 " (note: can be used many times)")
159 ("parameter,P", po::value(¶meters), "provide parameters to the scripts as name=value"
160 " (note: can be used many times)")
161 ("exclusions", po::value(&exclusionFiles), "read exclusions from file"
162 " (note: can be used many times)")
163 ("inputs,i", po::value(&inputFiles), "the inputs are read from that file (note: one file"
164 " per line. can be used many times.)")
165 ("root,r", po::value(&veraRoot), "use the given directory as the vera root directory")
166 ("help,h", "show this help message and exit")
167 ("version", "show vera++'s version and exit");
168
169 // don't call it "input", as is may seems more logical, in order to get an error when
170 // --inputs is used without the "s" at the end
171 po::options_description allOptions;
172 allOptions.add(visibleOptions).add_options()
173 ("__input__", po::value(&inputs), "input files from command line");
174
175 po::positional_options_description positionalOptions;
176 positionalOptions.add("__input__", -1);
177
178 po::variables_map vm;
179 try
180 {
181 po::store(po::command_line_parser(argc, argv).options(allOptions)
182 .positional(positionalOptions).run(), vm);
183 if ( vm.count("help") )
184 {
185 std::cout << "vera++ [options] [list-of-files]" << std::endl
186 << visibleOptions << std::endl;
187 return EXIT_SUCCESS;
188 }
189 po::notify(vm); // throws on error, so do after help in case
190 // there are any problems
191
192 // don't do the profile stuff when we only want the version
193 if (vm.count("version"))
194 {
195 std::cout << VERA_VERSION << '\n';
196 return EXIT_SUCCESS;
197 }
198
199 // we need the root to be able to find the profiles
200 Vera::Plugins::RootDirectory::setRootDirectory(veraRoot);
201
202 if (vm.count("profile") != 0 || (vm.count("rule") == 0 && vm.count("transform") == 0))
203 {
204 try
205 {
206 // backward compatibility stuff
207 Vera::Plugins::Profiles::RuleNameCollection res =
208 Vera::Plugins::Profiles::getListOfScriptNamesTcl(profile);
209 rules.insert(rules.end(), res.begin(), res.end());
210 }
211 catch (...)
212 {
213 std::string profileFile = veraRoot + "/profiles/" + profile;
214 std::ifstream file(profileFile.c_str());
215 if (file.is_open() == false)
216 {
217 throw std::runtime_error(
218 "Cannot open profile description for profile '" + profile + "': "
219 + strerror(errno));
220 }
221 po::store(po::parse_config_file(file, visibleOptions), vm);
222 if (file.bad())
223 {
224 throw std::runtime_error(
225 "Cannot read from profile description '" + profile + "': "
226 + strerror(errno));
227 }
228 po::notify(vm);
229 }
230 }
231 }
232 catch (po::error& e)
233 {
234 std::cerr << "vera++: " << e.what() << std::endl << std::endl;
235 std::cerr << visibleOptions << std::endl;
236 return EXIT_FAILURE;
237 }
238 catch (std::exception& e)
239 {
240 std::cerr << "vera++: " << e.what() << std::endl;
241 return EXIT_FAILURE;
242 }
243
244 try
245 {
246 Vera::Plugins::Reports::setShowRules(vm.count("show-rule"));
247 if (vm.count("warning"))
248 {
249 if (vm.count("error"))
250 {
251 std::cerr << "vera++: --warning and --error can't be used at the same time."
252 << std::endl;
253 std::cerr << visibleOptions << std::endl;
254 return EXIT_FAILURE;
255 }
256 Vera::Plugins::Reports::setPrefix("warning");
257 }
258 if (vm.count("error"))
259 {
260 Vera::Plugins::Reports::setPrefix("error");
261 }
262 foreach (const std::string & f, exclusionFiles)
263 {
264 Vera::Plugins::Exclusions::setExclusions(f);
265 }
266 foreach (const std::string & f, parameterFiles)
267 {
268 Vera::Plugins::Parameters::readFromFile(f);
269 }
270 foreach (const std::string & p, parameters)
271 {
272 Vera::Plugins::Parameters::ParamAssoc assoc(p);
273 Vera::Plugins::Parameters::set(assoc);
274 }
275 if (vm.count("__input__"))
276 {
277 foreach (const std::string & i, inputs)
278 {
279 Vera::Structures::SourceFiles::addFileName(i);
280 }
281 }
282 if (vm.count("__input__") == 0 && vm.count("inputs") == 0)
283 {
284 // list of source files is provided on stdin
285 inputFiles.push_back("-");
286 }
287
288 foreach (const std::string & f, inputFiles)
289 {
290 if (f == "-")
291 {
292 Vera::Structures::SourceFiles::FileName name;
293 while (std::getline(std::cin, name))
294 {
295 Vera::Structures::SourceFiles::addFileName(name);
296 }
297 }
298 else
299 {
300 std::ifstream file(f.c_str());
301 if (file.is_open() == false)
302 {
303 throw std::runtime_error(
304 "Cannot open " + f + ": " + strerror(errno));
305 }
306 std::string name;
307 while (std::getline(file, name))
308 {
309 Vera::Structures::SourceFiles::addFileName(name);
310 }
311 if (file.bad())
312 {
313 throw std::runtime_error(
314 "Cannot read from " + f + ": " + strerror(errno));
315 }
316 file.close();
317 }
318 }
319
320 if (vm.count("std-report") == 0 && vm.count("vc-report") == 0
321 && vm.count("xml-report") == 0 && vm.count("checkstyle-report") == 0
322 && vm.count("quiet") == 0)
323 {
324 // no report set - use std report on std out/err
325 stdreports.push_back("-");
326 }
327
328 if (rules.empty() == false)
329 {
330 if (vm.count("transform"))
331 {
332 std::cerr << "vera++: --transform and --rule can't be used at the same time."
333 << std::endl;
334 std::cerr << visibleOptions << std::endl;
335 return EXIT_FAILURE;
336 }
337 foreach (const std::string & r, rules)
338 {
339 Vera::Plugins::Rules::executeRule(r);
340 }
341 }
342 else if (vm.count("transform"))
343 {
344 Vera::Plugins::Transformations::executeTransformation(transform);
345 }
346
347 doReports(stdreports, vm, Vera::Plugins::Reports::writeStd);
348 doReports(vcreports, vm, Vera::Plugins::Reports::writeVc);
349 doReports(xmlreports, vm, Vera::Plugins::Reports::writeXml);
350 doReports(checkstylereports, vm, Vera::Plugins::Reports::writeCheckStyle);
351
352 if (vm.count("summary"))
353 {
354 std::cerr << Vera::Plugins::Reports::count() << " reports in "
355 << Vera::Structures::SourceFiles::count() << " files." << std::endl;
356 }
357 }
358 catch (const std::exception & e)
359 {
360 std::cerr << "vera++: " << e.what() << std::endl;
361 return EXIT_FAILURE;
362 }
363
364 if (vm.count("error") && Vera::Plugins::Reports::count() !=0)
365 {
366 return EXIT_FAILURE;
367 }
368 return EXIT_SUCCESS;
369 }
370