[C++20] [Modules] [ClangScanDeps] Enable to print make-style dependency file within P1689 format (4/4)
Required in https://reviews.llvm.org/D137534. The build systems needs the information to know that "header X changed, scanning may have changed, so please rerun scanning". Although it is possible to get the information by running clang-scan-deps for the second time with make format, it is not user friendly clearly. Reviewed By: jansvoboda11 Differential Revision: https://reviews.llvm.org/D139168
This commit is contained in:
1 parent
311ff227b4
commit
e1354763b6
4 files changed
+163
-83
No files matched your search
@@ -92,9 +92,22 @@ public:
|
||||
llvm::Expected<std::string>
|
||||
getDependencyFile(const std::vector<std::string> &CommandLine, StringRef CWD);
|
||||
|
||||
/// Collect the module dependency in P1689 format for C++20 named modules.
|
||||
///
|
||||
/// \param MakeformatOutput The output parameter for dependency information
|
||||
/// in make format if the command line requires to generate make-format
|
||||
/// dependency information by `-MD -MF <dep_file>`.
|
||||
///
|
||||
/// \param MakeformatOutputPath The output parameter for the path to
|
||||
/// \param MakeformatOutput.
|
||||
///
|
||||
/// \returns A \c StringError with the diagnostic output if clang errors
|
||||
/// occurred, P1689 dependency format rules otherwise.
|
||||
llvm::Expected<P1689Rule>
|
||||
getP1689ModuleDependencyFile(const CompileCommand &Command,
|
||||
StringRef CWD);
|
||||
getP1689ModuleDependencyFile(
|
||||
const clang::tooling::CompileCommand &Command, StringRef CWD,
|
||||
std::string &MakeformatOutput, std::string &MakeformatOutputPath,
|
||||
std::optional<StringRef> ModuleName = std::nullopt);
|
||||
|
||||
/// Given a Clang driver command-line for a translation unit, gather the
|
||||
/// modular dependencies and return the information needed for explicit build.
|
||||
|
||||
@@ -19,66 +19,68 @@ DependencyScanningTool::DependencyScanningTool(
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
|
||||
: Worker(Service, std::move(FS)) {}
|
||||
|
||||
namespace {
|
||||
/// Prints out all of the gathered dependencies into a string.
|
||||
class MakeDependencyPrinterConsumer : public DependencyConsumer {
|
||||
public:
|
||||
void handleBuildCommand(Command) override {}
|
||||
|
||||
void
|
||||
handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
|
||||
this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
|
||||
}
|
||||
|
||||
void handleFileDependency(StringRef File) override {
|
||||
Dependencies.push_back(std::string(File));
|
||||
}
|
||||
|
||||
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
|
||||
// Same as `handleModuleDependency`.
|
||||
}
|
||||
|
||||
void handleModuleDependency(ModuleDeps MD) override {
|
||||
// These are ignored for the make format as it can't support the full
|
||||
// set of deps, and handleFileDependency handles enough for implicitly
|
||||
// built modules to work.
|
||||
}
|
||||
|
||||
void handleContextHash(std::string Hash) override {}
|
||||
|
||||
std::string lookupModuleOutput(const ModuleID &ID,
|
||||
ModuleOutputKind Kind) override {
|
||||
llvm::report_fatal_error("unexpected call to lookupModuleOutput");
|
||||
}
|
||||
|
||||
void printDependencies(std::string &S) {
|
||||
assert(Opts && "Handled dependency output options.");
|
||||
|
||||
class DependencyPrinter : public DependencyFileGenerator {
|
||||
public:
|
||||
DependencyPrinter(DependencyOutputOptions &Opts,
|
||||
ArrayRef<std::string> Dependencies)
|
||||
: DependencyFileGenerator(Opts) {
|
||||
for (const auto &Dep : Dependencies)
|
||||
addDependency(Dep);
|
||||
}
|
||||
|
||||
void printDependencies(std::string &S) {
|
||||
llvm::raw_string_ostream OS(S);
|
||||
outputDependencyFile(OS);
|
||||
}
|
||||
};
|
||||
|
||||
DependencyPrinter Generator(*Opts, Dependencies);
|
||||
Generator.printDependencies(S);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<DependencyOutputOptions> Opts;
|
||||
std::vector<std::string> Dependencies;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
|
||||
const std::vector<std::string> &CommandLine, StringRef CWD) {
|
||||
/// Prints out all of the gathered dependencies into a string.
|
||||
class MakeDependencyPrinterConsumer : public DependencyConsumer {
|
||||
public:
|
||||
void handleBuildCommand(Command) override {}
|
||||
|
||||
void
|
||||
handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
|
||||
this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
|
||||
}
|
||||
|
||||
void handleFileDependency(StringRef File) override {
|
||||
Dependencies.push_back(std::string(File));
|
||||
}
|
||||
|
||||
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
|
||||
// Same as `handleModuleDependency`.
|
||||
}
|
||||
|
||||
void handleModuleDependency(ModuleDeps MD) override {
|
||||
// These are ignored for the make format as it can't support the full
|
||||
// set of deps, and handleFileDependency handles enough for implicitly
|
||||
// built modules to work.
|
||||
}
|
||||
|
||||
void handleContextHash(std::string Hash) override {}
|
||||
|
||||
std::string lookupModuleOutput(const ModuleID &ID,
|
||||
ModuleOutputKind Kind) override {
|
||||
llvm::report_fatal_error("unexpected call to lookupModuleOutput");
|
||||
}
|
||||
|
||||
void printDependencies(std::string &S) {
|
||||
assert(Opts && "Handled dependency output options.");
|
||||
|
||||
class DependencyPrinter : public DependencyFileGenerator {
|
||||
public:
|
||||
DependencyPrinter(DependencyOutputOptions &Opts,
|
||||
ArrayRef<std::string> Dependencies)
|
||||
: DependencyFileGenerator(Opts) {
|
||||
for (const auto &Dep : Dependencies)
|
||||
addDependency(Dep);
|
||||
}
|
||||
|
||||
void printDependencies(std::string &S) {
|
||||
llvm::raw_string_ostream OS(S);
|
||||
outputDependencyFile(OS);
|
||||
}
|
||||
};
|
||||
|
||||
DependencyPrinter Generator(*Opts, Dependencies);
|
||||
Generator.printDependencies(S);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<DependencyOutputOptions> Opts;
|
||||
std::vector<std::string> Dependencies;
|
||||
};
|
||||
|
||||
MakeDependencyPrinterConsumer Consumer;
|
||||
auto Result = Worker.computeDependencies(CWD, CommandLine, Consumer);
|
||||
if (Result)
|
||||
@@ -89,8 +91,11 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
|
||||
}
|
||||
|
||||
llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
|
||||
const CompileCommand &Command, StringRef CWD) {
|
||||
class P1689ModuleDependencyPrinterConsumer : public DependencyConsumer {
|
||||
const CompileCommand &Command, StringRef CWD,
|
||||
std::string &MakeformatOutput, std::string &MakeformatOutputPath,
|
||||
std::optional<StringRef> ModuleName) {
|
||||
class P1689ModuleDependencyPrinterConsumer
|
||||
: public MakeDependencyPrinterConsumer {
|
||||
public:
|
||||
P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
|
||||
const CompileCommand &Command)
|
||||
@@ -98,17 +103,6 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
|
||||
Rule.PrimaryOutput = Command.Output;
|
||||
}
|
||||
|
||||
void
|
||||
handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
|
||||
void handleFileDependency(StringRef File) override {}
|
||||
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
|
||||
void handleModuleDependency(ModuleDeps MD) override {}
|
||||
void handleContextHash(std::string Hash) override {}
|
||||
std::string lookupModuleOutput(const ModuleID &ID,
|
||||
ModuleOutputKind Kind) override {
|
||||
llvm::report_fatal_error("unexpected call to lookupModuleOutput");
|
||||
}
|
||||
|
||||
void handleProvidedAndRequiredStdCXXModules(
|
||||
std::optional<P1689ModuleInfo> Provided,
|
||||
std::vector<P1689ModuleInfo> Requires) override {
|
||||
@@ -118,6 +112,12 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
|
||||
Rule.Requires = Requires;
|
||||
}
|
||||
|
||||
StringRef getMakeFormatDependencyOutputPath() {
|
||||
if (Opts->OutputFormat != DependencyOutputFormat::Make)
|
||||
return {};
|
||||
return Opts->OutputFile;
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef Filename;
|
||||
P1689Rule &Rule;
|
||||
@@ -128,6 +128,10 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
|
||||
auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer);
|
||||
if (Result)
|
||||
return std::move(Result);
|
||||
|
||||
MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
|
||||
if (!MakeformatOutputPath.empty())
|
||||
Consumer.printDependencies(MakeformatOutput);
|
||||
return Rule;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// It is annoying to handle different slash direction
|
||||
// in the filesystem of Windows and Linux.
|
||||
// So we disable the test on Windows here.
|
||||
// REQUIRES: !system-windows
|
||||
//
|
||||
// RUN: rm -fr %t
|
||||
// RUN: mkdir -p %t
|
||||
// RUN: split-file %s %t
|
||||
@@ -25,42 +30,50 @@
|
||||
// RUN: clang-scan-deps -format=p1689 \
|
||||
// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/User.cpp -o %t/User.o \
|
||||
// RUN: | FileCheck %t/User.cpp -DPREFIX=%/t
|
||||
//
|
||||
// Check we can generate the make-style dependencies as expected.
|
||||
// RUN: clang-scan-deps -format=p1689 \
|
||||
// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/impl_part.cppm -o %t/impl_part.o \
|
||||
// RUN: -MT %t/impl_part.o.ddi -MD -MF %t/impl_part.dep
|
||||
// RUN: cat %t/impl_part.dep | FileCheck %t/impl_part.cppm -DPREFIX=%/t --check-prefix=CHECK-MAKE
|
||||
//
|
||||
// Check that we can generate multiple make-style dependency information with compilation database.
|
||||
// RUN: cat %t/P1689.dep | FileCheck %t/Checks.cpp -DPREFIX=%/t --check-prefix=CHECK-MAKE
|
||||
|
||||
//--- P1689.json.in
|
||||
[
|
||||
{
|
||||
"directory": "DIR",
|
||||
"command": "clang++ -std=c++20 DIR/M.cppm -c -o DIR/M.o",
|
||||
"command": "clang++ -std=c++20 DIR/M.cppm -c -o DIR/M.o -MT DIR/M.o.ddi -MD -MF DIR/P1689.dep",
|
||||
"file": "DIR/M.cppm",
|
||||
"output": "DIR/M.o"
|
||||
},
|
||||
{
|
||||
"directory": "DIR",
|
||||
"command": "clang++ -std=c++20 DIR/Impl.cpp -c -o DIR/Impl.o",
|
||||
"command": "clang++ -std=c++20 DIR/Impl.cpp -c -o DIR/Impl.o -MT DIR/Impl.o.ddi -MD -MF DIR/P1689.dep",
|
||||
"file": "DIR/Impl.cpp",
|
||||
"output": "DIR/Impl.o"
|
||||
},
|
||||
{
|
||||
"directory": "DIR",
|
||||
"command": "clang++ -std=c++20 DIR/impl_part.cppm -c -o DIR/impl_part.o",
|
||||
"command": "clang++ -std=c++20 DIR/impl_part.cppm -c -o DIR/impl_part.o -MT DIR/impl_part.o.ddi -MD -MF DIR/P1689.dep",
|
||||
"file": "DIR/impl_part.cppm",
|
||||
"output": "DIR/impl_part.o"
|
||||
},
|
||||
{
|
||||
"directory": "DIR",
|
||||
"command": "clang++ -std=c++20 DIR/interface_part.cppm -c -o DIR/interface_part.o",
|
||||
"command": "clang++ -std=c++20 DIR/interface_part.cppm -c -o DIR/interface_part.o -MT DIR/interface_part.o.ddi -MD -MF DIR/P1689.dep",
|
||||
"file": "DIR/interface_part.cppm",
|
||||
"output": "DIR/interface_part.o"
|
||||
},
|
||||
{
|
||||
"directory": "DIR",
|
||||
"command": "clang++ -std=c++20 DIR/User.cpp -c -o DIR/User.o",
|
||||
"command": "clang++ -std=c++20 DIR/User.cpp -c -o DIR/User.o -MT DIR/User.o.ddi -MD -MF DIR/P1689.dep",
|
||||
"file": "DIR/User.cpp",
|
||||
"output": "DIR/User.o"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
//--- M.cppm
|
||||
export module M;
|
||||
export import :interface_part;
|
||||
@@ -275,4 +288,17 @@ int main() {
|
||||
// CHECK-NEXT: "version": 1
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK-MAKE-DAG: [[PREFIX]]/impl_part.o.ddi: \
|
||||
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/impl_part.cppm \
|
||||
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/header.mock
|
||||
// CHECK-MAKE-DAG: [[PREFIX]]/interface_part.o.ddi: \
|
||||
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/interface_part.cppm
|
||||
// CHECK-MAKE-DAG: [[PREFIX]]/M.o.ddi: \
|
||||
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/M.cppm
|
||||
// CHECK-MAKE-DAG: [[PREFIX]]/User.o.ddi: \
|
||||
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/User.cpp
|
||||
// CHECK-MAKE-DAG: [[PREFIX]]/Impl.o.ddi: \
|
||||
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/Impl.cpp \
|
||||
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/header.mock
|
||||
|
||||
//--- header.mock
|
||||
@@ -825,10 +825,47 @@ int main(int argc, const char **argv) {
|
||||
Errs))
|
||||
HadErrors = true;
|
||||
} else if (Format == ScanningOutputFormat::P1689) {
|
||||
auto MaybeRule =
|
||||
WorkerTools[I]->getP1689ModuleDependencyFile(*Input, CWD);
|
||||
if (handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs))
|
||||
HadErrors = true;
|
||||
// It is useful to generate the make-format dependency output during
|
||||
// the scanning for P1689. Otherwise the users need to scan again for
|
||||
// it. We will generate the make-format dependency output if we find
|
||||
// `-MF` in the command lines.
|
||||
std::string MakeformatOutputPath;
|
||||
std::string MakeformatOutput;
|
||||
|
||||
auto MaybeRule = WorkerTools[I]->getP1689ModuleDependencyFile(
|
||||
*Input, CWD, MakeformatOutput, MakeformatOutputPath,
|
||||
MaybeModuleName);
|
||||
HadErrors =
|
||||
handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs);
|
||||
|
||||
if (!MakeformatOutputPath.empty() && !MakeformatOutput.empty() &&
|
||||
!HadErrors) {
|
||||
static std::mutex Lock;
|
||||
// With compilation database, we may open different files
|
||||
// concurrently or we may write the same file concurrently. So we
|
||||
// use a map here to allow multiple compile commands to write to the
|
||||
// same file. Also we need a lock here to avoid data race.
|
||||
static llvm::StringMap<llvm::raw_fd_ostream> OSs;
|
||||
std::unique_lock<std::mutex> LockGuard(Lock);
|
||||
|
||||
auto OSIter = OSs.find(MakeformatOutputPath);
|
||||
if (OSIter == OSs.end()) {
|
||||
std::error_code EC;
|
||||
OSIter = OSs.try_emplace(MakeformatOutputPath,
|
||||
MakeformatOutputPath, EC)
|
||||
.first;
|
||||
if (EC)
|
||||
llvm::errs()
|
||||
<< "Failed to open P1689 make format output file \""
|
||||
<< MakeformatOutputPath << "\" for " << EC.message()
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
SharedStream MakeformatOS(OSIter->second);
|
||||
llvm::Expected<std::string> MaybeOutput(MakeformatOutput);
|
||||
HadErrors = handleMakeDependencyToolResult(Filename, MaybeOutput,
|
||||
MakeformatOS, Errs);
|
||||
}
|
||||
} else if (MaybeModuleName) {
|
||||
auto MaybeModuleDepsGraph = WorkerTools[I]->getModuleDependencies(
|
||||
*MaybeModuleName, Input->CommandLine, CWD, AlreadySeenModules,
|
||||
|
||||
Reference in new issue
Block a user