clang API Documentation

CompilationDatabase.cpp
Go to the documentation of this file.
00001 //===--- CompilationDatabase.cpp - ----------------------------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 //  This file contains implementations of the CompilationDatabase base class
00011 //  and the FixedCompilationDatabase.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "clang/Tooling/CompilationDatabase.h"
00016 #include "clang/Basic/Diagnostic.h"
00017 #include "clang/Basic/DiagnosticOptions.h"
00018 #include "clang/Driver/Action.h"
00019 #include "clang/Driver/Compilation.h"
00020 #include "clang/Driver/Driver.h"
00021 #include "clang/Driver/DriverDiagnostic.h"
00022 #include "clang/Driver/Job.h"
00023 #include "clang/Frontend/TextDiagnosticPrinter.h"
00024 #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
00025 #include "clang/Tooling/Tooling.h"
00026 #include "llvm/ADT/SmallString.h"
00027 #include "llvm/Option/Arg.h"
00028 #include "llvm/Support/Host.h"
00029 #include "llvm/Support/Path.h"
00030 #include <sstream>
00031 #include <system_error>
00032 
00033 namespace clang {
00034 namespace tooling {
00035 
00036 CompilationDatabase::~CompilationDatabase() {}
00037 
00038 std::unique_ptr<CompilationDatabase>
00039 CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
00040                                        std::string &ErrorMessage) {
00041   std::stringstream ErrorStream;
00042   for (CompilationDatabasePluginRegistry::iterator
00043        It = CompilationDatabasePluginRegistry::begin(),
00044        Ie = CompilationDatabasePluginRegistry::end();
00045        It != Ie; ++It) {
00046     std::string DatabaseErrorMessage;
00047     std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate());
00048     if (std::unique_ptr<CompilationDatabase> DB =
00049             Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
00050       return DB;
00051     ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
00052   }
00053   ErrorMessage = ErrorStream.str();
00054   return nullptr;
00055 }
00056 
00057 static std::unique_ptr<CompilationDatabase>
00058 findCompilationDatabaseFromDirectory(StringRef Directory,
00059                                      std::string &ErrorMessage) {
00060   std::stringstream ErrorStream;
00061   bool HasErrorMessage = false;
00062   while (!Directory.empty()) {
00063     std::string LoadErrorMessage;
00064 
00065     if (std::unique_ptr<CompilationDatabase> DB =
00066             CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
00067       return DB;
00068 
00069     if (!HasErrorMessage) {
00070       ErrorStream << "No compilation database found in " << Directory.str()
00071                   << " or any parent directory\n" << LoadErrorMessage;
00072       HasErrorMessage = true;
00073     }
00074 
00075     Directory = llvm::sys::path::parent_path(Directory);
00076   }
00077   ErrorMessage = ErrorStream.str();
00078   return nullptr;
00079 }
00080 
00081 std::unique_ptr<CompilationDatabase>
00082 CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
00083                                           std::string &ErrorMessage) {
00084   SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
00085   StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
00086 
00087   std::unique_ptr<CompilationDatabase> DB =
00088       findCompilationDatabaseFromDirectory(Directory, ErrorMessage);
00089 
00090   if (!DB)
00091     ErrorMessage = ("Could not auto-detect compilation database for file \"" +
00092                    SourceFile + "\"\n" + ErrorMessage).str();
00093   return DB;
00094 }
00095 
00096 std::unique_ptr<CompilationDatabase>
00097 CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
00098                                              std::string &ErrorMessage) {
00099   SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
00100 
00101   std::unique_ptr<CompilationDatabase> DB =
00102       findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage);
00103 
00104   if (!DB)
00105     ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
00106                    SourceDir + "\"\n" + ErrorMessage).str();
00107   return DB;
00108 }
00109 
00110 CompilationDatabasePlugin::~CompilationDatabasePlugin() {}
00111 
00112 // Helper for recursively searching through a chain of actions and collecting
00113 // all inputs, direct and indirect, of compile jobs.
00114 struct CompileJobAnalyzer {
00115   void run(const driver::Action *A) {
00116     runImpl(A, false);
00117   }
00118 
00119   SmallVector<std::string, 2> Inputs;
00120 
00121 private:
00122 
00123   void runImpl(const driver::Action *A, bool Collect) {
00124     bool CollectChildren = Collect;
00125     switch (A->getKind()) {
00126     case driver::Action::CompileJobClass:
00127       CollectChildren = true;
00128       break;
00129 
00130     case driver::Action::InputClass: {
00131       if (Collect) {
00132         const driver::InputAction *IA = cast<driver::InputAction>(A);
00133         Inputs.push_back(IA->getInputArg().getSpelling());
00134       }
00135     } break;
00136 
00137     default:
00138       // Don't care about others
00139       ;
00140     }
00141 
00142     for (driver::ActionList::const_iterator I = A->begin(), E = A->end();
00143          I != E; ++I)
00144       runImpl(*I, CollectChildren);
00145   }
00146 };
00147 
00148 // Special DiagnosticConsumer that looks for warn_drv_input_file_unused
00149 // diagnostics from the driver and collects the option strings for those unused
00150 // options.
00151 class UnusedInputDiagConsumer : public DiagnosticConsumer {
00152 public:
00153   UnusedInputDiagConsumer() : Other(nullptr) {}
00154 
00155   // Useful for debugging, chain diagnostics to another consumer after
00156   // recording for our own purposes.
00157   UnusedInputDiagConsumer(DiagnosticConsumer *Other) : Other(Other) {}
00158 
00159   virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
00160                                 const Diagnostic &Info) override {
00161     if (Info.getID() == clang::diag::warn_drv_input_file_unused) {
00162       // Arg 1 for this diagnostic is the option that didn't get used.
00163       UnusedInputs.push_back(Info.getArgStdStr(0));
00164     }
00165     if (Other)
00166       Other->HandleDiagnostic(DiagLevel, Info);
00167   }
00168 
00169   DiagnosticConsumer *Other;
00170   SmallVector<std::string, 2> UnusedInputs;
00171 };
00172 
00173 // Unary functor for asking "Given a StringRef S1, does there exist a string
00174 // S2 in Arr where S1 == S2?"
00175 struct MatchesAny {
00176   MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {}
00177   bool operator() (StringRef S) {
00178     for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; ++I)
00179       if (*I == S)
00180         return true;
00181     return false;
00182   }
00183 private:
00184   ArrayRef<std::string> Arr;
00185 };
00186 
00187 /// \brief Strips any positional args and possible argv[0] from a command-line
00188 /// provided by the user to construct a FixedCompilationDatabase.
00189 ///
00190 /// FixedCompilationDatabase requires a command line to be in this format as it
00191 /// constructs the command line for each file by appending the name of the file
00192 /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
00193 /// start of the command line although its value is not important as it's just
00194 /// ignored by the Driver invoked by the ClangTool using the
00195 /// FixedCompilationDatabase.
00196 ///
00197 /// FIXME: This functionality should probably be made available by
00198 /// clang::driver::Driver although what the interface should look like is not
00199 /// clear.
00200 ///
00201 /// \param[in] Args Args as provided by the user.
00202 /// \return Resulting stripped command line.
00203 ///          \li true if successful.
00204 ///          \li false if \c Args cannot be used for compilation jobs (e.g.
00205 ///          contains an option like -E or -version).
00206 static bool stripPositionalArgs(std::vector<const char *> Args,
00207                                 std::vector<std::string> &Result) {
00208   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
00209   UnusedInputDiagConsumer DiagClient;
00210   DiagnosticsEngine Diagnostics(
00211       IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()),
00212       &*DiagOpts, &DiagClient, false);
00213 
00214   // The clang executable path isn't required since the jobs the driver builds
00215   // will not be executed.
00216   std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
00217       /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
00218       Diagnostics));
00219   NewDriver->setCheckInputsExist(false);
00220 
00221   // This becomes the new argv[0]. The value is actually not important as it
00222   // isn't used for invoking Tools.
00223   Args.insert(Args.begin(), "clang-tool");
00224 
00225   // By adding -c, we force the driver to treat compilation as the last phase.
00226   // It will then issue warnings via Diagnostics about un-used options that
00227   // would have been used for linking. If the user provided a compiler name as
00228   // the original argv[0], this will be treated as a linker input thanks to
00229   // insertng a new argv[0] above. All un-used options get collected by
00230   // UnusedInputdiagConsumer and get stripped out later.
00231   Args.push_back("-c");
00232 
00233   // Put a dummy C++ file on to ensure there's at least one compile job for the
00234   // driver to construct. If the user specified some other argument that
00235   // prevents compilation, e.g. -E or something like -version, we may still end
00236   // up with no jobs but then this is the user's fault.
00237   Args.push_back("placeholder.cpp");
00238 
00239   // Remove -no-integrated-as; it's not used for syntax checking,
00240   // and it confuses targets which don't support this option.
00241   Args.erase(std::remove_if(Args.begin(), Args.end(),
00242                             MatchesAny(std::string("-no-integrated-as"))),
00243              Args.end());
00244 
00245   const std::unique_ptr<driver::Compilation> Compilation(
00246       NewDriver->BuildCompilation(Args));
00247 
00248   const driver::JobList &Jobs = Compilation->getJobs();
00249 
00250   CompileJobAnalyzer CompileAnalyzer;
00251 
00252   for (const auto &Job : Jobs) {
00253     if (Job.getKind() == driver::Job::CommandClass) {
00254       const driver::Command &Cmd = cast<driver::Command>(Job);
00255       // Collect only for Assemble jobs. If we do all jobs we get duplicates
00256       // since Link jobs point to Assemble jobs as inputs.
00257       if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass)
00258         CompileAnalyzer.run(&Cmd.getSource());
00259     }
00260   }
00261 
00262   if (CompileAnalyzer.Inputs.empty()) {
00263     // No compile jobs found.
00264     // FIXME: Emit a warning of some kind?
00265     return false;
00266   }
00267 
00268   // Remove all compilation input files from the command line. This is
00269   // necessary so that getCompileCommands() can construct a command line for
00270   // each file.
00271   std::vector<const char *>::iterator End = std::remove_if(
00272       Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs));
00273 
00274   // Remove all inputs deemed unused for compilation.
00275   End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs));
00276 
00277   // Remove the -c add above as well. It will be at the end right now.
00278   assert(strcmp(*(End - 1), "-c") == 0);
00279   --End;
00280 
00281   Result = std::vector<std::string>(Args.begin() + 1, End);
00282   return true;
00283 }
00284 
00285 FixedCompilationDatabase *
00286 FixedCompilationDatabase::loadFromCommandLine(int &Argc,
00287                                               const char **Argv,
00288                                               Twine Directory) {
00289   const char **DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
00290   if (DoubleDash == Argv + Argc)
00291     return nullptr;
00292   std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
00293   Argc = DoubleDash - Argv;
00294 
00295   std::vector<std::string> StrippedArgs;
00296   if (!stripPositionalArgs(CommandLine, StrippedArgs))
00297     return nullptr;
00298   return new FixedCompilationDatabase(Directory, StrippedArgs);
00299 }
00300 
00301 FixedCompilationDatabase::
00302 FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
00303   std::vector<std::string> ToolCommandLine(1, "clang-tool");
00304   ToolCommandLine.insert(ToolCommandLine.end(),
00305                          CommandLine.begin(), CommandLine.end());
00306   CompileCommands.push_back(
00307       CompileCommand(Directory, std::move(ToolCommandLine)));
00308 }
00309 
00310 std::vector<CompileCommand>
00311 FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
00312   std::vector<CompileCommand> Result(CompileCommands);
00313   Result[0].CommandLine.push_back(FilePath);
00314   return Result;
00315 }
00316 
00317 std::vector<std::string>
00318 FixedCompilationDatabase::getAllFiles() const {
00319   return std::vector<std::string>();
00320 }
00321 
00322 std::vector<CompileCommand>
00323 FixedCompilationDatabase::getAllCompileCommands() const {
00324   return std::vector<CompileCommand>();
00325 }
00326 
00327 // This anchor is used to force the linker to link in the generated object file
00328 // and thus register the JSONCompilationDatabasePlugin.
00329 extern volatile int JSONAnchorSource;
00330 static int JSONAnchorDest = JSONAnchorSource;
00331 
00332 } // end namespace tooling
00333 } // end namespace clang