summaryrefslogtreecommitdiffstats
path: root/clang/tools
diff options
context:
space:
mode:
Diffstat (limited to 'clang/tools')
-rw-r--r--clang/tools/clang-scan-deps/ClangScanDeps.cpp215
1 files changed, 208 insertions, 7 deletions
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 1294e668284..70e546b3416 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -15,6 +15,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/JSON.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/Threading.h"
@@ -129,6 +130,11 @@ static llvm::cl::opt<ScanningOutputFormat> Format(
llvm::cl::init(ScanningOutputFormat::Make),
llvm::cl::cat(DependencyScannerCategory));
+static llvm::cl::opt<bool> FullCommandLine(
+ "full-command-line",
+ llvm::cl::desc("Include the full command lines to use to build modules"),
+ llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory));
+
llvm::cl::opt<unsigned>
NumThreads("j", llvm::cl::Optional,
llvm::cl::desc("Number of worker threads to use (default: use "
@@ -189,9 +195,10 @@ private:
/// based on the result.
///
/// \returns True on error.
-static bool handleDependencyToolResult(const std::string &Input,
- llvm::Expected<std::string> &MaybeFile,
- SharedStream &OS, SharedStream &Errs) {
+static bool
+handleMakeDependencyToolResult(const std::string &Input,
+ llvm::Expected<std::string> &MaybeFile,
+ SharedStream &OS, SharedStream &Errs) {
if (!MaybeFile) {
llvm::handleAllErrors(
MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
@@ -206,6 +213,184 @@ static bool handleDependencyToolResult(const std::string &Input,
return false;
}
+static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
+ std::vector<llvm::StringRef> Strings;
+ for (auto &&I : Set)
+ Strings.push_back(I.getKey());
+ std::sort(Strings.begin(), Strings.end());
+ return llvm::json::Array(Strings);
+}
+
+static llvm::json::Array toJSONSorted(std::vector<ClangModuleDep> V) {
+ std::sort(V.begin(), V.end(),
+ [](const ClangModuleDep &A, const ClangModuleDep &B) {
+ return std::tie(A.ModuleName, A.ContextHash) <
+ std::tie(B.ModuleName, B.ContextHash);
+ });
+
+ llvm::json::Array Ret;
+ for (const ClangModuleDep &CMD : V)
+ Ret.push_back(llvm::json::Object(
+ {{"module-name", CMD.ModuleName}, {"context-hash", CMD.ContextHash}}));
+ return Ret;
+}
+
+// Thread safe.
+class FullDeps {
+public:
+ void mergeDeps(StringRef Input, FullDependenciesResult FDR,
+ size_t InputIndex) {
+ const FullDependencies &FD = FDR.FullDeps;
+
+ InputDeps ID;
+ ID.FileName = Input;
+ ID.ContextHash = std::move(FD.ContextHash);
+ ID.FileDeps = std::move(FD.FileDeps);
+ ID.ModuleDeps = std::move(FD.ClangModuleDeps);
+
+ std::unique_lock<std::mutex> ul(Lock);
+ for (const ModuleDeps &MD : FDR.DiscoveredModules) {
+ auto I = Modules.find({MD.ContextHash, MD.ModuleName, 0});
+ if (I != Modules.end()) {
+ I->first.InputIndex = std::min(I->first.InputIndex, InputIndex);
+ continue;
+ }
+ Modules.insert(
+ I, {{MD.ContextHash, MD.ModuleName, InputIndex}, std::move(MD)});
+ }
+
+ if (FullCommandLine)
+ ID.AdditonalCommandLine = FD.getAdditionalCommandLine(
+ [&](ClangModuleDep CMD) { return lookupPCMPath(CMD); },
+ [&](ClangModuleDep CMD) -> const ModuleDeps & {
+ return lookupModuleDeps(CMD);
+ });
+
+ Inputs.push_back(std::move(ID));
+ }
+
+ void printFullOutput(raw_ostream &OS) {
+ // Sort the modules by name to get a deterministic order.
+ std::vector<ContextModulePair> ModuleNames;
+ for (auto &&M : Modules)
+ ModuleNames.push_back(M.first);
+ std::sort(ModuleNames.begin(), ModuleNames.end(),
+ [](const ContextModulePair &A, const ContextModulePair &B) {
+ return std::tie(A.ModuleName, A.InputIndex) <
+ std::tie(B.ModuleName, B.InputIndex);
+ });
+
+ std::sort(Inputs.begin(), Inputs.end(),
+ [](const InputDeps &A, const InputDeps &B) {
+ return A.FileName < B.FileName;
+ });
+
+ using namespace llvm::json;
+
+ Array OutModules;
+ for (auto &&ModName : ModuleNames) {
+ auto &MD = Modules[ModName];
+ Object O{
+ {"name", MD.ModuleName},
+ {"context-hash", MD.ContextHash},
+ {"file-deps", toJSONSorted(MD.FileDeps)},
+ {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
+ {"clang-modulemap-file", MD.ClangModuleMapFile},
+ {"command-line",
+ FullCommandLine
+ ? MD.getFullCommandLine(
+ [&](ClangModuleDep CMD) { return lookupPCMPath(CMD); },
+ [&](ClangModuleDep CMD) -> const ModuleDeps & {
+ return lookupModuleDeps(CMD);
+ })
+ : MD.NonPathCommandLine},
+ };
+ OutModules.push_back(std::move(O));
+ }
+
+ Array TUs;
+ for (auto &&I : Inputs) {
+ Object O{
+ {"input-file", I.FileName},
+ {"clang-context-hash", I.ContextHash},
+ {"file-deps", I.FileDeps},
+ {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
+ {"command-line", I.AdditonalCommandLine},
+ };
+ TUs.push_back(std::move(O));
+ }
+
+ Object Output{
+ {"modules", std::move(OutModules)},
+ {"translation-units", std::move(TUs)},
+ };
+
+ OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
+ }
+
+private:
+ StringRef lookupPCMPath(ClangModuleDep CMD) {
+ return Modules[ContextModulePair{CMD.ContextHash, CMD.ModuleName, 0}]
+ .ImplicitModulePCMPath;
+ }
+
+ const ModuleDeps &lookupModuleDeps(ClangModuleDep CMD) {
+ auto I =
+ Modules.find(ContextModulePair{CMD.ContextHash, CMD.ModuleName, 0});
+ assert(I != Modules.end());
+ return I->second;
+ };
+
+ struct ContextModulePair {
+ std::string ContextHash;
+ std::string ModuleName;
+ mutable size_t InputIndex;
+
+ bool operator==(const ContextModulePair &Other) const {
+ return ContextHash == Other.ContextHash && ModuleName == Other.ModuleName;
+ }
+ };
+
+ struct ContextModulePairHasher {
+ std::size_t operator()(const ContextModulePair &CMP) const {
+ using llvm::hash_combine;
+
+ return hash_combine(CMP.ContextHash, CMP.ModuleName);
+ }
+ };
+
+ struct InputDeps {
+ std::string FileName;
+ std::string ContextHash;
+ std::vector<std::string> FileDeps;
+ std::vector<ClangModuleDep> ModuleDeps;
+ std::vector<std::string> AdditonalCommandLine;
+ };
+
+ std::mutex Lock;
+ std::unordered_map<ContextModulePair, ModuleDeps, ContextModulePairHasher>
+ Modules;
+ std::vector<InputDeps> Inputs;
+};
+
+static bool handleFullDependencyToolResult(
+ const std::string &Input,
+ llvm::Expected<FullDependenciesResult> &MaybeFullDeps, FullDeps &FD,
+ size_t InputIndex, SharedStream &OS, SharedStream &Errs) {
+ if (!MaybeFullDeps) {
+ llvm::handleAllErrors(
+ MaybeFullDeps.takeError(), [&Input, &Errs](llvm::StringError &Err) {
+ Errs.applyLocked([&](raw_ostream &OS) {
+ OS << "Error while scanning dependencies for " << Input << ":\n";
+ OS << Err.getMessage();
+ });
+ });
+ return true;
+ }
+ FD.mergeDeps(Input, std::move(*MaybeFullDeps), InputIndex);
+ return false;
+}
+
int main(int argc, const char **argv) {
llvm::InitLLVM X(argc, argv);
llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
@@ -316,6 +501,7 @@ int main(int argc, const char **argv) {
std::vector<std::thread> WorkerThreads;
std::atomic<bool> HadErrors(false);
+ FullDeps FD;
std::mutex Lock;
size_t Index = 0;
@@ -324,26 +510,38 @@ int main(int argc, const char **argv) {
<< " files using " << NumWorkers << " workers\n";
}
for (unsigned I = 0; I < NumWorkers; ++I) {
- auto Worker = [I, &Lock, &Index, &Inputs, &HadErrors, &WorkerTools,
+ auto Worker = [I, &Lock, &Index, &Inputs, &HadErrors, &FD, &WorkerTools,
&DependencyOS, &Errs]() {
+ llvm::StringSet<> AlreadySeenModules;
while (true) {
const SingleCommandCompilationDatabase *Input;
std::string Filename;
std::string CWD;
+ size_t LocalIndex;
// Take the next input.
{
std::unique_lock<std::mutex> LockGuard(Lock);
if (Index >= Inputs.size())
return;
+ LocalIndex = Index;
Input = &Inputs[Index++];
tooling::CompileCommand Cmd = Input->getAllCompileCommands()[0];
Filename = std::move(Cmd.Filename);
CWD = std::move(Cmd.Directory);
}
// Run the tool on it.
- auto MaybeFile = WorkerTools[I]->getDependencyFile(*Input, CWD);
- if (handleDependencyToolResult(Filename, MaybeFile, DependencyOS, Errs))
- HadErrors = true;
+ if (Format == ScanningOutputFormat::Make) {
+ auto MaybeFile = WorkerTools[I]->getDependencyFile(*Input, CWD);
+ if (handleMakeDependencyToolResult(Filename, MaybeFile, DependencyOS,
+ Errs))
+ HadErrors = true;
+ } else {
+ auto MaybeFullDeps = WorkerTools[I]->getFullDependencies(
+ *Input, CWD, AlreadySeenModules);
+ if (handleFullDependencyToolResult(Filename, MaybeFullDeps, FD,
+ LocalIndex, DependencyOS, Errs))
+ HadErrors = true;
+ }
}
};
#if LLVM_ENABLE_THREADS
@@ -356,5 +554,8 @@ int main(int argc, const char **argv) {
for (auto &W : WorkerThreads)
W.join();
+ if (Format == ScanningOutputFormat::Full)
+ FD.printFullOutput(llvm::outs());
+
return HadErrors;
}
OpenPOWER on IntegriCloud