1//===- DependencyScanningTool.h - 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#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGTOOL_H 10#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGTOOL_H 11 12#include "clang/Tooling/DependencyScanning/DependencyScanningService.h" 13#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" 14#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" 15#include "clang/Tooling/JSONCompilationDatabase.h" 16#include "llvm/ADT/DenseSet.h" 17#include "llvm/ADT/MapVector.h" 18#include <optional> 19#include <string> 20#include <vector> 21 22namespace clang { 23namespace tooling { 24namespace dependencies { 25 26/// A callback to lookup module outputs for "-fmodule-file=", "-o" etc. 27using LookupModuleOutputCallback = 28 llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)>; 29 30/// Graph of modular dependencies. 31using ModuleDepsGraph = std::vector<ModuleDeps>; 32 33/// The full dependencies and module graph for a specific input. 34struct TranslationUnitDeps { 35 /// The graph of direct and transitive modular dependencies. 36 ModuleDepsGraph ModuleGraph; 37 38 /// The identifier of the C++20 module this translation unit exports. 39 /// 40 /// If the translation unit is not a module then \c ID.ModuleName is empty. 41 ModuleID ID; 42 43 /// A collection of absolute paths to files that this translation unit 44 /// directly depends on, not including transitive dependencies. 45 std::vector<std::string> FileDeps; 46 47 /// A collection of prebuilt modules this translation unit directly depends 48 /// on, not including transitive dependencies. 49 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps; 50 51 /// A list of modules this translation unit directly depends on, not including 52 /// transitive dependencies. 53 /// 54 /// This may include modules with a different context hash when it can be 55 /// determined that the differences are benign for this compilation. 56 std::vector<ModuleID> ClangModuleDeps; 57 58 /// The sequence of commands required to build the translation unit. Commands 59 /// should be executed in order. 60 /// 61 /// FIXME: If we add support for multi-arch builds in clang-scan-deps, we 62 /// should make the dependencies between commands explicit to enable parallel 63 /// builds of each architecture. 64 std::vector<Command> Commands; 65 66 /// Deprecated driver command-line. This will be removed in a future version. 67 std::vector<std::string> DriverCommandLine; 68}; 69 70struct P1689Rule { 71 std::string PrimaryOutput; 72 std::optional<P1689ModuleInfo> Provides; 73 std::vector<P1689ModuleInfo> Requires; 74}; 75 76/// The high-level implementation of the dependency discovery tool that runs on 77/// an individual worker thread. 78class DependencyScanningTool { 79public: 80 /// Construct a dependency scanning tool. 81 DependencyScanningTool(DependencyScanningService &Service, 82 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = 83 llvm::vfs::createPhysicalFileSystem()); 84 85 /// Print out the dependency information into a string using the dependency 86 /// file format that is specified in the options (-MD is the default) and 87 /// return it. 88 /// 89 /// \returns A \c StringError with the diagnostic output if clang errors 90 /// occurred, dependency file contents otherwise. 91 llvm::Expected<std::string> 92 getDependencyFile(const std::vector<std::string> &CommandLine, StringRef CWD); 93 94 /// Collect the module dependency in P1689 format for C++20 named modules. 95 /// 96 /// \param MakeformatOutput The output parameter for dependency information 97 /// in make format if the command line requires to generate make-format 98 /// dependency information by `-MD -MF <dep_file>`. 99 /// 100 /// \param MakeformatOutputPath The output parameter for the path to 101 /// \param MakeformatOutput. 102 /// 103 /// \returns A \c StringError with the diagnostic output if clang errors 104 /// occurred, P1689 dependency format rules otherwise. 105 llvm::Expected<P1689Rule> 106 getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command, 107 StringRef CWD, std::string &MakeformatOutput, 108 std::string &MakeformatOutputPath); 109 llvm::Expected<P1689Rule> 110 getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command, 111 StringRef CWD) { 112 std::string MakeformatOutput; 113 std::string MakeformatOutputPath; 114 115 return getP1689ModuleDependencyFile(Command, CWD, MakeformatOutput, 116 MakeformatOutputPath); 117 } 118 119 /// Given a Clang driver command-line for a translation unit, gather the 120 /// modular dependencies and return the information needed for explicit build. 121 /// 122 /// \param AlreadySeen This stores modules which have previously been 123 /// reported. Use the same instance for all calls to this 124 /// function for a single \c DependencyScanningTool in a 125 /// single build. Use a different one for different tools, 126 /// and clear it between builds. 127 /// \param LookupModuleOutput This function is called to fill in 128 /// "-fmodule-file=", "-o" and other output 129 /// arguments for dependencies. 130 /// 131 /// \returns a \c StringError with the diagnostic output if clang errors 132 /// occurred, \c TranslationUnitDeps otherwise. 133 llvm::Expected<TranslationUnitDeps> 134 getTranslationUnitDependencies(const std::vector<std::string> &CommandLine, 135 StringRef CWD, 136 const llvm::DenseSet<ModuleID> &AlreadySeen, 137 LookupModuleOutputCallback LookupModuleOutput); 138 139 /// Given a compilation context specified via the Clang driver command-line, 140 /// gather modular dependencies of module with the given name, and return the 141 /// information needed for explicit build. 142 llvm::Expected<ModuleDepsGraph> getModuleDependencies( 143 StringRef ModuleName, const std::vector<std::string> &CommandLine, 144 StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen, 145 LookupModuleOutputCallback LookupModuleOutput); 146 147private: 148 DependencyScanningWorker Worker; 149}; 150 151class FullDependencyConsumer : public DependencyConsumer { 152public: 153 FullDependencyConsumer(const llvm::DenseSet<ModuleID> &AlreadySeen) 154 : AlreadySeen(AlreadySeen) {} 155 156 void handleBuildCommand(Command Cmd) override { 157 Commands.push_back(std::move(Cmd)); 158 } 159 160 void handleDependencyOutputOpts(const DependencyOutputOptions &) override {} 161 162 void handleFileDependency(StringRef File) override { 163 Dependencies.push_back(std::string(File)); 164 } 165 166 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { 167 PrebuiltModuleDeps.emplace_back(std::move(PMD)); 168 } 169 170 void handleModuleDependency(ModuleDeps MD) override { 171 ClangModuleDeps[MD.ID] = std::move(MD); 172 } 173 174 void handleDirectModuleDependency(ModuleID ID) override { 175 DirectModuleDeps.push_back(ID); 176 } 177 178 void handleContextHash(std::string Hash) override { 179 ContextHash = std::move(Hash); 180 } 181 182 TranslationUnitDeps takeTranslationUnitDeps(); 183 ModuleDepsGraph takeModuleGraphDeps(); 184 185private: 186 std::vector<std::string> Dependencies; 187 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps; 188 llvm::MapVector<ModuleID, ModuleDeps> ClangModuleDeps; 189 std::vector<ModuleID> DirectModuleDeps; 190 std::vector<Command> Commands; 191 std::string ContextHash; 192 std::vector<std::string> OutputPaths; 193 const llvm::DenseSet<ModuleID> &AlreadySeen; 194}; 195 196/// A simple dependency action controller that uses a callback. If no callback 197/// is provided, it is assumed that looking up module outputs is unreachable. 198class CallbackActionController : public DependencyActionController { 199public: 200 virtual ~CallbackActionController(); 201 202 CallbackActionController(LookupModuleOutputCallback LMO) 203 : LookupModuleOutput(std::move(LMO)) { 204 if (!LookupModuleOutput) { 205 LookupModuleOutput = [](const ModuleID &, 206 ModuleOutputKind) -> std::string { 207 llvm::report_fatal_error("unexpected call to lookupModuleOutput"); 208 }; 209 } 210 } 211 212 std::string lookupModuleOutput(const ModuleID &ID, 213 ModuleOutputKind Kind) override { 214 return LookupModuleOutput(ID, Kind); 215 } 216 217private: 218 LookupModuleOutputCallback LookupModuleOutput; 219}; 220 221} // end namespace dependencies 222} // end namespace tooling 223} // end namespace clang 224 225#endif // LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGTOOL_H 226