1357095Sdim//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===// 2353942Sdim// 3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353942Sdim// See https://llvm.org/LICENSE.txt for license information. 5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6353942Sdim// 7353942Sdim//===----------------------------------------------------------------------===// 8353942Sdim 9353942Sdim#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" 10353942Sdim#include "clang/Frontend/Utils.h" 11357095Sdim#include "llvm/Support/JSON.h" 12353942Sdim 13357095Sdimstatic llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) { 14357095Sdim std::vector<llvm::StringRef> Strings; 15357095Sdim for (auto &&I : Set) 16357095Sdim Strings.push_back(I.getKey()); 17357095Sdim std::sort(Strings.begin(), Strings.end()); 18357095Sdim return llvm::json::Array(Strings); 19357095Sdim} 20357095Sdim 21353942Sdimnamespace clang{ 22353942Sdimnamespace tooling{ 23353942Sdimnamespace dependencies{ 24353942Sdim 25357095SdimDependencyScanningTool::DependencyScanningTool( 26357095Sdim DependencyScanningService &Service) 27357095Sdim : Format(Service.getFormat()), Worker(Service) { 28357095Sdim} 29353942Sdim 30357095Sdimllvm::Expected<std::string> DependencyScanningTool::getDependencyFile( 31357095Sdim const tooling::CompilationDatabase &Compilations, StringRef CWD) { 32353942Sdim /// Prints out all of the gathered dependencies into a string. 33357095Sdim class MakeDependencyPrinterConsumer : public DependencyConsumer { 34353942Sdim public: 35353942Sdim void handleFileDependency(const DependencyOutputOptions &Opts, 36353942Sdim StringRef File) override { 37353942Sdim if (!this->Opts) 38353942Sdim this->Opts = std::make_unique<DependencyOutputOptions>(Opts); 39353942Sdim Dependencies.push_back(File); 40353942Sdim } 41353942Sdim 42357095Sdim void handleModuleDependency(ModuleDeps MD) override { 43357095Sdim // These are ignored for the make format as it can't support the full 44357095Sdim // set of deps, and handleFileDependency handles enough for implicitly 45357095Sdim // built modules to work. 46357095Sdim } 47357095Sdim 48357095Sdim void handleContextHash(std::string Hash) override {} 49357095Sdim 50353942Sdim void printDependencies(std::string &S) { 51353942Sdim if (!Opts) 52353942Sdim return; 53353942Sdim 54353942Sdim class DependencyPrinter : public DependencyFileGenerator { 55353942Sdim public: 56353942Sdim DependencyPrinter(DependencyOutputOptions &Opts, 57353942Sdim ArrayRef<std::string> Dependencies) 58353942Sdim : DependencyFileGenerator(Opts) { 59353942Sdim for (const auto &Dep : Dependencies) 60353942Sdim addDependency(Dep); 61353942Sdim } 62353942Sdim 63353942Sdim void printDependencies(std::string &S) { 64353942Sdim llvm::raw_string_ostream OS(S); 65353942Sdim outputDependencyFile(OS); 66353942Sdim } 67353942Sdim }; 68353942Sdim 69353942Sdim DependencyPrinter Generator(*Opts, Dependencies); 70353942Sdim Generator.printDependencies(S); 71353942Sdim } 72353942Sdim 73353942Sdim private: 74353942Sdim std::unique_ptr<DependencyOutputOptions> Opts; 75353942Sdim std::vector<std::string> Dependencies; 76353942Sdim }; 77353942Sdim 78357095Sdim class FullDependencyPrinterConsumer : public DependencyConsumer { 79357095Sdim public: 80357095Sdim void handleFileDependency(const DependencyOutputOptions &Opts, 81357095Sdim StringRef File) override { 82357095Sdim Dependencies.push_back(File); 83357095Sdim } 84357095Sdim 85357095Sdim void handleModuleDependency(ModuleDeps MD) override { 86357095Sdim ClangModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD); 87357095Sdim } 88357095Sdim 89357095Sdim void handleContextHash(std::string Hash) override { 90357095Sdim ContextHash = std::move(Hash); 91357095Sdim } 92357095Sdim 93357095Sdim void printDependencies(std::string &S, StringRef MainFile) { 94357095Sdim // Sort the modules by name to get a deterministic order. 95357095Sdim std::vector<StringRef> Modules; 96357095Sdim for (auto &&Dep : ClangModuleDeps) 97357095Sdim Modules.push_back(Dep.first); 98357095Sdim std::sort(Modules.begin(), Modules.end()); 99357095Sdim 100357095Sdim llvm::raw_string_ostream OS(S); 101357095Sdim 102357095Sdim using namespace llvm::json; 103357095Sdim 104357095Sdim Array Imports; 105357095Sdim for (auto &&ModName : Modules) { 106357095Sdim auto &MD = ClangModuleDeps[ModName]; 107357095Sdim if (MD.ImportedByMainFile) 108357095Sdim Imports.push_back(MD.ModuleName); 109357095Sdim } 110357095Sdim 111357095Sdim Array Mods; 112357095Sdim for (auto &&ModName : Modules) { 113357095Sdim auto &MD = ClangModuleDeps[ModName]; 114357095Sdim Object Mod{ 115357095Sdim {"name", MD.ModuleName}, 116357095Sdim {"file-deps", toJSONSorted(MD.FileDeps)}, 117357095Sdim {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, 118357095Sdim {"clang-modulemap-file", MD.ClangModuleMapFile}, 119357095Sdim }; 120357095Sdim Mods.push_back(std::move(Mod)); 121357095Sdim } 122357095Sdim 123357095Sdim Object O{ 124357095Sdim {"input-file", MainFile}, 125357095Sdim {"clang-context-hash", ContextHash}, 126357095Sdim {"file-deps", Dependencies}, 127357095Sdim {"clang-module-deps", std::move(Imports)}, 128357095Sdim {"clang-modules", std::move(Mods)}, 129357095Sdim }; 130357095Sdim 131357095Sdim S = llvm::formatv("{0:2},\n", Value(std::move(O))).str(); 132357095Sdim return; 133357095Sdim } 134357095Sdim 135357095Sdim private: 136357095Sdim std::vector<std::string> Dependencies; 137357095Sdim std::unordered_map<std::string, ModuleDeps> ClangModuleDeps; 138357095Sdim std::string ContextHash; 139357095Sdim }; 140357095Sdim 141357095Sdim 142357095Sdim // We expect a single command here because if a source file occurs multiple 143357095Sdim // times in the original CDB, then `computeDependencies` would run the 144357095Sdim // `DependencyScanningAction` once for every time the input occured in the 145357095Sdim // CDB. Instead we split up the CDB into single command chunks to avoid this 146357095Sdim // behavior. 147357095Sdim assert(Compilations.getAllCompileCommands().size() == 1 && 148357095Sdim "Expected a compilation database with a single command!"); 149357095Sdim std::string Input = Compilations.getAllCompileCommands().front().Filename; 150357095Sdim 151357095Sdim if (Format == ScanningOutputFormat::Make) { 152357095Sdim MakeDependencyPrinterConsumer Consumer; 153357095Sdim auto Result = 154357095Sdim Worker.computeDependencies(Input, CWD, Compilations, Consumer); 155357095Sdim if (Result) 156357095Sdim return std::move(Result); 157357095Sdim std::string Output; 158357095Sdim Consumer.printDependencies(Output); 159357095Sdim return Output; 160357095Sdim } else { 161357095Sdim FullDependencyPrinterConsumer Consumer; 162357095Sdim auto Result = 163357095Sdim Worker.computeDependencies(Input, CWD, Compilations, Consumer); 164357095Sdim if (Result) 165357095Sdim return std::move(Result); 166357095Sdim std::string Output; 167357095Sdim Consumer.printDependencies(Output, Input); 168357095Sdim return Output; 169357095Sdim } 170353942Sdim} 171353942Sdim 172353942Sdim} // end namespace dependencies 173353942Sdim} // end namespace tooling 174353942Sdim} // end namespace clang 175