clang  6.0.0svn
CompilationDatabase.cpp
Go to the documentation of this file.
1 //===--- CompilationDatabase.cpp - ----------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file contains implementations of the CompilationDatabase base class
11 // and the FixedCompilationDatabase.
12 //
13 // FIXME: Various functions that take a string &ErrorMessage should be upgraded
14 // to Expected.
15 //
16 //===----------------------------------------------------------------------===//
17 
19 #include "clang/Basic/Diagnostic.h"
21 #include "clang/Driver/Action.h"
23 #include "clang/Driver/Driver.h"
25 #include "clang/Driver/Job.h"
28 #include "clang/Tooling/Tooling.h"
29 #include "llvm/ADT/SmallString.h"
30 #include "llvm/Option/Arg.h"
31 #include "llvm/Support/Host.h"
32 #include "llvm/Support/LineIterator.h"
33 #include "llvm/Support/Path.h"
34 #include "llvm/Support/raw_ostream.h"
35 #include <sstream>
36 #include <system_error>
37 using namespace clang;
38 using namespace tooling;
39 
40 LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
41 
43 
44 std::unique_ptr<CompilationDatabase>
45 CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
46  std::string &ErrorMessage) {
47  llvm::raw_string_ostream ErrorStream(ErrorMessage);
48  for (CompilationDatabasePluginRegistry::iterator
49  It = CompilationDatabasePluginRegistry::begin(),
50  Ie = CompilationDatabasePluginRegistry::end();
51  It != Ie; ++It) {
52  std::string DatabaseErrorMessage;
53  std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate());
54  if (std::unique_ptr<CompilationDatabase> DB =
55  Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
56  return DB;
57  ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
58  }
59  return nullptr;
60 }
61 
62 static std::unique_ptr<CompilationDatabase>
64  std::string &ErrorMessage) {
65  std::stringstream ErrorStream;
66  bool HasErrorMessage = false;
67  while (!Directory.empty()) {
68  std::string LoadErrorMessage;
69 
70  if (std::unique_ptr<CompilationDatabase> DB =
71  CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
72  return DB;
73 
74  if (!HasErrorMessage) {
75  ErrorStream << "No compilation database found in " << Directory.str()
76  << " or any parent directory\n" << LoadErrorMessage;
77  HasErrorMessage = true;
78  }
79 
80  Directory = llvm::sys::path::parent_path(Directory);
81  }
82  ErrorMessage = ErrorStream.str();
83  return nullptr;
84 }
85 
86 std::unique_ptr<CompilationDatabase>
88  std::string &ErrorMessage) {
89  SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
90  StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
91 
92  std::unique_ptr<CompilationDatabase> DB =
93  findCompilationDatabaseFromDirectory(Directory, ErrorMessage);
94 
95  if (!DB)
96  ErrorMessage = ("Could not auto-detect compilation database for file \"" +
97  SourceFile + "\"\n" + ErrorMessage).str();
98  return DB;
99 }
100 
101 std::unique_ptr<CompilationDatabase>
103  std::string &ErrorMessage) {
104  SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
105 
106  std::unique_ptr<CompilationDatabase> DB =
107  findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage);
108 
109  if (!DB)
110  ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
111  SourceDir + "\"\n" + ErrorMessage).str();
112  return DB;
113 }
114 
115 std::vector<CompileCommand> CompilationDatabase::getAllCompileCommands() const {
116  std::vector<CompileCommand> Result;
117  for (const auto &File : getAllFiles()) {
118  auto C = getCompileCommands(File);
119  std::move(C.begin(), C.end(), std::back_inserter(Result));
120  }
121  return Result;
122 }
123 
125 
126 namespace {
127 // Helper for recursively searching through a chain of actions and collecting
128 // all inputs, direct and indirect, of compile jobs.
129 struct CompileJobAnalyzer {
130  void run(const driver::Action *A) {
131  runImpl(A, false);
132  }
133 
135 
136 private:
137 
138  void runImpl(const driver::Action *A, bool Collect) {
139  bool CollectChildren = Collect;
140  switch (A->getKind()) {
142  CollectChildren = true;
143  break;
144 
146  if (Collect) {
147  const driver::InputAction *IA = cast<driver::InputAction>(A);
148  Inputs.push_back(IA->getInputArg().getSpelling());
149  }
150  } break;
151 
152  default:
153  // Don't care about others
154  ;
155  }
156 
157  for (const driver::Action *AI : A->inputs())
158  runImpl(AI, CollectChildren);
159  }
160 };
161 
162 // Special DiagnosticConsumer that looks for warn_drv_input_file_unused
163 // diagnostics from the driver and collects the option strings for those unused
164 // options.
165 class UnusedInputDiagConsumer : public DiagnosticConsumer {
166 public:
167  UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {}
168 
169  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
170  const Diagnostic &Info) override {
171  if (Info.getID() == clang::diag::warn_drv_input_file_unused) {
172  // Arg 1 for this diagnostic is the option that didn't get used.
173  UnusedInputs.push_back(Info.getArgStdStr(0));
174  } else if (DiagLevel >= DiagnosticsEngine::Error) {
175  // If driver failed to create compilation object, show the diagnostics
176  // to user.
177  Other.HandleDiagnostic(DiagLevel, Info);
178  }
179  }
180 
181  DiagnosticConsumer &Other;
182  SmallVector<std::string, 2> UnusedInputs;
183 };
184 
185 // Unary functor for asking "Given a StringRef S1, does there exist a string
186 // S2 in Arr where S1 == S2?"
187 struct MatchesAny {
188  MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {}
189  bool operator() (StringRef S) {
190  for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; ++I)
191  if (*I == S)
192  return true;
193  return false;
194  }
195 private:
197 };
198 } // namespace
199 
200 /// \brief Strips any positional args and possible argv[0] from a command-line
201 /// provided by the user to construct a FixedCompilationDatabase.
202 ///
203 /// FixedCompilationDatabase requires a command line to be in this format as it
204 /// constructs the command line for each file by appending the name of the file
205 /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
206 /// start of the command line although its value is not important as it's just
207 /// ignored by the Driver invoked by the ClangTool using the
208 /// FixedCompilationDatabase.
209 ///
210 /// FIXME: This functionality should probably be made available by
211 /// clang::driver::Driver although what the interface should look like is not
212 /// clear.
213 ///
214 /// \param[in] Args Args as provided by the user.
215 /// \return Resulting stripped command line.
216 /// \li true if successful.
217 /// \li false if \c Args cannot be used for compilation jobs (e.g.
218 /// contains an option like -E or -version).
219 static bool stripPositionalArgs(std::vector<const char *> Args,
220  std::vector<std::string> &Result,
221  std::string &ErrorMsg) {
223  llvm::raw_string_ostream Output(ErrorMsg);
224  TextDiagnosticPrinter DiagnosticPrinter(Output, &*DiagOpts);
225  UnusedInputDiagConsumer DiagClient(DiagnosticPrinter);
226  DiagnosticsEngine Diagnostics(
228  &*DiagOpts, &DiagClient, false);
229 
230  // The clang executable path isn't required since the jobs the driver builds
231  // will not be executed.
232  std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
233  /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
234  Diagnostics));
235  NewDriver->setCheckInputsExist(false);
236 
237  // This becomes the new argv[0]. The value is actually not important as it
238  // isn't used for invoking Tools.
239  Args.insert(Args.begin(), "clang-tool");
240 
241  // By adding -c, we force the driver to treat compilation as the last phase.
242  // It will then issue warnings via Diagnostics about un-used options that
243  // would have been used for linking. If the user provided a compiler name as
244  // the original argv[0], this will be treated as a linker input thanks to
245  // insertng a new argv[0] above. All un-used options get collected by
246  // UnusedInputdiagConsumer and get stripped out later.
247  Args.push_back("-c");
248 
249  // Put a dummy C++ file on to ensure there's at least one compile job for the
250  // driver to construct. If the user specified some other argument that
251  // prevents compilation, e.g. -E or something like -version, we may still end
252  // up with no jobs but then this is the user's fault.
253  Args.push_back("placeholder.cpp");
254 
255  // Remove -no-integrated-as; it's not used for syntax checking,
256  // and it confuses targets which don't support this option.
257  Args.erase(std::remove_if(Args.begin(), Args.end(),
258  MatchesAny(std::string("-no-integrated-as"))),
259  Args.end());
260 
261  const std::unique_ptr<driver::Compilation> Compilation(
262  NewDriver->BuildCompilation(Args));
263  if (!Compilation)
264  return false;
265 
266  const driver::JobList &Jobs = Compilation->getJobs();
267 
268  CompileJobAnalyzer CompileAnalyzer;
269 
270  for (const auto &Cmd : Jobs) {
271  // Collect only for Assemble and Compile jobs. If we do all jobs we get
272  // duplicates since Link jobs point to Assemble jobs as inputs.
273  if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass ||
274  Cmd.getSource().getKind() == driver::Action::CompileJobClass) {
275  CompileAnalyzer.run(&Cmd.getSource());
276  }
277  }
278 
279  if (CompileAnalyzer.Inputs.empty()) {
280  ErrorMsg = "warning: no compile jobs found\n";
281  return false;
282  }
283 
284  // Remove all compilation input files from the command line. This is
285  // necessary so that getCompileCommands() can construct a command line for
286  // each file.
287  std::vector<const char *>::iterator End = std::remove_if(
288  Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs));
289 
290  // Remove all inputs deemed unused for compilation.
291  End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs));
292 
293  // Remove the -c add above as well. It will be at the end right now.
294  assert(strcmp(*(End - 1), "-c") == 0);
295  --End;
296 
297  Result = std::vector<std::string>(Args.begin() + 1, End);
298  return true;
299 }
300 
301 std::unique_ptr<FixedCompilationDatabase>
303  const char *const *Argv,
304  std::string &ErrorMsg,
305  Twine Directory) {
306  ErrorMsg.clear();
307  if (Argc == 0)
308  return nullptr;
309  const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
310  if (DoubleDash == Argv + Argc)
311  return nullptr;
312  std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
313  Argc = DoubleDash - Argv;
314 
315  std::vector<std::string> StrippedArgs;
316  if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg))
317  return nullptr;
318  return llvm::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs);
319 }
320 
321 std::unique_ptr<FixedCompilationDatabase>
322 FixedCompilationDatabase::loadFromFile(StringRef Path, std::string &ErrorMsg) {
323  ErrorMsg.clear();
324  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
325  llvm::MemoryBuffer::getFile(Path);
326  if (std::error_code Result = File.getError()) {
327  ErrorMsg = "Error while opening fixed database: " + Result.message();
328  return nullptr;
329  }
330  std::vector<std::string> Args{llvm::line_iterator(**File),
331  llvm::line_iterator()};
332  return llvm::make_unique<FixedCompilationDatabase>(
333  llvm::sys::path::parent_path(Path), std::move(Args));
334 }
335 
337 FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
338  std::vector<std::string> ToolCommandLine(1, "clang-tool");
339  ToolCommandLine.insert(ToolCommandLine.end(),
340  CommandLine.begin(), CommandLine.end());
341  CompileCommands.emplace_back(Directory, StringRef(),
342  std::move(ToolCommandLine),
343  StringRef());
344 }
345 
346 std::vector<CompileCommand>
348  std::vector<CompileCommand> Result(CompileCommands);
349  Result[0].CommandLine.push_back(FilePath);
350  Result[0].Filename = FilePath;
351  return Result;
352 }
353 
354 namespace {
355 
356 class FixedCompilationDatabasePlugin : public CompilationDatabasePlugin {
357  std::unique_ptr<CompilationDatabase>
358  loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
359  SmallString<1024> DatabasePath(Directory);
360  llvm::sys::path::append(DatabasePath, "compile_flags.txt");
361  return FixedCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage);
362  }
363 };
364 
365 static CompilationDatabasePluginRegistry::Add<FixedCompilationDatabasePlugin>
366 X("fixed-compilation-database", "Reads plain-text flags file");
367 
368 } // namespace
369 
370 namespace clang {
371 namespace tooling {
372 
373 // This anchor is used to force the linker to link in the generated object file
374 // and thus register the JSONCompilationDatabasePlugin.
375 extern volatile int JSONAnchorSource;
376 static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource;
377 
378 } // end namespace tooling
379 } // end namespace clang
static std::unique_ptr< CompilationDatabase > autoDetectFromDirectory(StringRef SourceDir, std::string &ErrorMessage)
Tries to detect a compilation database location and load it.
static std::unique_ptr< CompilationDatabase > findCompilationDatabaseFromDirectory(StringRef Directory, std::string &ErrorMessage)
input_range inputs()
Definition: Action.h:141
std::vector< CompileCommand > getCompileCommands(StringRef FilePath) const override
Returns the given compile command.
Interface for compilation database plugins.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1397
llvm::Registry< CompilationDatabasePlugin > CompilationDatabasePluginRegistry
const llvm::opt::Arg & getInputArg() const
Definition: Action.h:205
Represents the diagnostic with the level of severity and possible fixes to be applied.
Definition: Diagnostic.h:50
virtual std::vector< std::string > getAllFiles() const
Returns the list of all files available in the compilation database.
Action - Represent an abstract compilation step to perform.
Definition: Action.h:45
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:147
Defines the Diagnostic-related interfaces.
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition: Driver.h:65
ActionClass getKind() const
Definition: Action.h:131
SourceLocation End
std::string getAbsolutePath(StringRef File)
Returns the absolute path of File, by prepending it with the current directory if File is not absolut...
Definition: Tooling.cpp:163
JobList - A sequence of jobs to perform.
Definition: Job.h:166
static bool stripPositionalArgs(std::vector< const char *> Args, std::vector< std::string > &Result, std::string &ErrorMsg)
Strips any positional args and possible argv[0] from a command-line provided by the user to construct...
Interface for compilation databases.
static std::unique_ptr< FixedCompilationDatabase > loadFromCommandLine(int &Argc, const char *const *Argv, std::string &ErrorMsg, Twine Directory=".")
Creates a FixedCompilationDatabase from the arguments after "--".
Options for controlling the compiler diagnostics engine.
static CompilationDatabasePluginRegistry::Add< JSONCompilationDatabasePlugin > X("json-compilation-database", "Reads JSON formatted compilation databases")
static std::unique_ptr< CompilationDatabase > autoDetectFromSource(StringRef SourceFile, std::string &ErrorMessage)
Tries to detect a compilation database location and load it.
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info)
Handle this diagnostic, reporting it to the user or capturing it to a log as needed.
Definition: Diagnostic.cpp:435
Dataflow Directional Tag Classes.
Used for handling and querying diagnostic IDs.
FixedCompilationDatabase(Twine Directory, ArrayRef< std::string > CommandLine)
Constructs a compilation data base from a specified directory and command line.
Level
The level of the diagnostic, after it has been through mapping.
Definition: Diagnostic.h:150
static std::unique_ptr< CompilationDatabase > loadFromDirectory(StringRef BuildDirectory, std::string &ErrorMessage)
Loads a compilation database from a build directory.
static std::unique_ptr< FixedCompilationDatabase > loadFromFile(StringRef Path, std::string &ErrorMsg)
Reads flags from the given file, one-per line.
virtual std::vector< CompileCommand > getAllCompileCommands() const
Returns all compile commands for all the files in the compilation database.
static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest
const list_type & getJobs() const
Definition: Job.h:186
virtual std::vector< CompileCommand > getCompileCommands(StringRef FilePath) const =0
Returns all compile commands in which the specified file was compiled.