1 //===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" 10 #include "clang/Frontend/Utils.h" 11 #include <optional> 12 13 using namespace clang; 14 using namespace tooling; 15 using namespace dependencies; 16 17 DependencyScanningTool::DependencyScanningTool( 18 DependencyScanningService &Service, 19 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) 20 : Worker(Service, std::move(FS)) {} 21 22 namespace { 23 /// Prints out all of the gathered dependencies into a string. 24 class MakeDependencyPrinterConsumer : public DependencyConsumer { 25 public: 26 void handleBuildCommand(Command) override {} 27 28 void 29 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { 30 this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 31 } 32 33 void handleFileDependency(StringRef File) override { 34 Dependencies.push_back(std::string(File)); 35 } 36 37 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 38 // Same as `handleModuleDependency`. 39 } 40 41 void handleModuleDependency(ModuleDeps MD) override { 42 // These are ignored for the make format as it can't support the full 43 // set of deps, and handleFileDependency handles enough for implicitly 44 // built modules to work. 45 } 46 47 void handleContextHash(std::string Hash) override {} 48 49 void printDependencies(std::string &S) { 50 assert(Opts && "Handled dependency output options."); 51 52 class DependencyPrinter : public DependencyFileGenerator { 53 public: 54 DependencyPrinter(DependencyOutputOptions &Opts, 55 ArrayRef<std::string> Dependencies) 56 : DependencyFileGenerator(Opts) { 57 for (const auto &Dep : Dependencies) 58 addDependency(Dep); 59 } 60 61 void printDependencies(std::string &S) { 62 llvm::raw_string_ostream OS(S); 63 outputDependencyFile(OS); 64 } 65 }; 66 67 DependencyPrinter Generator(*Opts, Dependencies); 68 Generator.printDependencies(S); 69 } 70 71 protected: 72 std::unique_ptr<DependencyOutputOptions> Opts; 73 std::vector<std::string> Dependencies; 74 }; 75 } // anonymous namespace 76 77 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 78 const std::vector<std::string> &CommandLine, StringRef CWD) { 79 MakeDependencyPrinterConsumer Consumer; 80 CallbackActionController Controller(nullptr); 81 auto Result = 82 Worker.computeDependencies(CWD, CommandLine, Consumer, Controller); 83 if (Result) 84 return std::move(Result); 85 std::string Output; 86 Consumer.printDependencies(Output); 87 return Output; 88 } 89 90 llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile( 91 const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput, 92 std::string &MakeformatOutputPath) { 93 class P1689ModuleDependencyPrinterConsumer 94 : public MakeDependencyPrinterConsumer { 95 public: 96 P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule, 97 const CompileCommand &Command) 98 : Filename(Command.Filename), Rule(Rule) { 99 Rule.PrimaryOutput = Command.Output; 100 } 101 102 void handleProvidedAndRequiredStdCXXModules( 103 std::optional<P1689ModuleInfo> Provided, 104 std::vector<P1689ModuleInfo> Requires) override { 105 Rule.Provides = Provided; 106 if (Rule.Provides) 107 Rule.Provides->SourcePath = Filename.str(); 108 Rule.Requires = Requires; 109 } 110 111 StringRef getMakeFormatDependencyOutputPath() { 112 if (Opts->OutputFormat != DependencyOutputFormat::Make) 113 return {}; 114 return Opts->OutputFile; 115 } 116 117 private: 118 StringRef Filename; 119 P1689Rule &Rule; 120 }; 121 122 class P1689ActionController : public DependencyActionController { 123 public: 124 // The lookupModuleOutput is for clang modules. P1689 format don't need it. 125 std::string lookupModuleOutput(const ModuleID &, 126 ModuleOutputKind Kind) override { 127 return ""; 128 } 129 }; 130 131 P1689Rule Rule; 132 P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); 133 P1689ActionController Controller; 134 auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer, 135 Controller); 136 if (Result) 137 return std::move(Result); 138 139 MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath(); 140 if (!MakeformatOutputPath.empty()) 141 Consumer.printDependencies(MakeformatOutput); 142 return Rule; 143 } 144 145 llvm::Expected<TranslationUnitDeps> 146 DependencyScanningTool::getTranslationUnitDependencies( 147 const std::vector<std::string> &CommandLine, StringRef CWD, 148 const llvm::StringSet<> &AlreadySeen, 149 LookupModuleOutputCallback LookupModuleOutput) { 150 FullDependencyConsumer Consumer(AlreadySeen); 151 CallbackActionController Controller(LookupModuleOutput); 152 llvm::Error Result = 153 Worker.computeDependencies(CWD, CommandLine, Consumer, Controller); 154 if (Result) 155 return std::move(Result); 156 return Consumer.takeTranslationUnitDeps(); 157 } 158 159 llvm::Expected<ModuleDepsGraph> DependencyScanningTool::getModuleDependencies( 160 StringRef ModuleName, const std::vector<std::string> &CommandLine, 161 StringRef CWD, const llvm::StringSet<> &AlreadySeen, 162 LookupModuleOutputCallback LookupModuleOutput) { 163 FullDependencyConsumer Consumer(AlreadySeen); 164 CallbackActionController Controller(LookupModuleOutput); 165 llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, 166 Controller, ModuleName); 167 if (Result) 168 return std::move(Result); 169 return Consumer.takeModuleGraphDeps(); 170 } 171 172 TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() { 173 TranslationUnitDeps TU; 174 175 TU.ID.ContextHash = std::move(ContextHash); 176 TU.FileDeps = std::move(Dependencies); 177 TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); 178 TU.Commands = std::move(Commands); 179 180 for (auto &&M : ClangModuleDeps) { 181 auto &MD = M.second; 182 if (MD.ImportedByMainFile) 183 TU.ClangModuleDeps.push_back(MD.ID); 184 // TODO: Avoid handleModuleDependency even being called for modules 185 // we've already seen. 186 if (AlreadySeen.count(M.first)) 187 continue; 188 TU.ModuleGraph.push_back(std::move(MD)); 189 } 190 191 return TU; 192 } 193 194 ModuleDepsGraph FullDependencyConsumer::takeModuleGraphDeps() { 195 ModuleDepsGraph ModuleGraph; 196 197 for (auto &&M : ClangModuleDeps) { 198 auto &MD = M.second; 199 // TODO: Avoid handleModuleDependency even being called for modules 200 // we've already seen. 201 if (AlreadySeen.count(M.first)) 202 continue; 203 ModuleGraph.push_back(std::move(MD)); 204 } 205 206 return ModuleGraph; 207 } 208 209 CallbackActionController::~CallbackActionController() {} 210