clang API Documentation
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