Skip to content

Commit e135476

Browse files
committed
[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
1 parent 311ff22 commit e135476

File tree

4 files changed

+157
-77
lines changed

4 files changed

+157
-77
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,22 @@ class DependencyScanningTool {
9292
llvm::Expected<std::string>
9393
getDependencyFile(const std::vector<std::string> &CommandLine, StringRef CWD);
9494

95+
/// Collect the module dependency in P1689 format for C++20 named modules.
96+
///
97+
/// \param MakeformatOutput The output parameter for dependency information
98+
/// in make format if the command line requires to generate make-format
99+
/// dependency information by `-MD -MF <dep_file>`.
100+
///
101+
/// \param MakeformatOutputPath The output parameter for the path to
102+
/// \param MakeformatOutput.
103+
///
104+
/// \returns A \c StringError with the diagnostic output if clang errors
105+
/// occurred, P1689 dependency format rules otherwise.
95106
llvm::Expected<P1689Rule>
96-
getP1689ModuleDependencyFile(const CompileCommand &Command,
97-
StringRef CWD);
107+
getP1689ModuleDependencyFile(
108+
const clang::tooling::CompileCommand &Command, StringRef CWD,
109+
std::string &MakeformatOutput, std::string &MakeformatOutputPath,
110+
std::optional<StringRef> ModuleName = std::nullopt);
98111

99112
/// Given a Clang driver command-line for a translation unit, gather the
100113
/// modular dependencies and return the information needed for explicit build.

clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp

Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -19,66 +19,68 @@ DependencyScanningTool::DependencyScanningTool(
1919
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
2020
: Worker(Service, std::move(FS)) {}
2121

22-
llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
23-
const std::vector<std::string> &CommandLine, StringRef CWD) {
24-
/// Prints out all of the gathered dependencies into a string.
25-
class MakeDependencyPrinterConsumer : public DependencyConsumer {
26-
public:
27-
void handleBuildCommand(Command) override {}
28-
29-
void
30-
handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
31-
this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
32-
}
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+
}
3332

34-
void handleFileDependency(StringRef File) override {
35-
Dependencies.push_back(std::string(File));
36-
}
33+
void handleFileDependency(StringRef File) override {
34+
Dependencies.push_back(std::string(File));
35+
}
3736

38-
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
39-
// Same as `handleModuleDependency`.
40-
}
37+
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
38+
// Same as `handleModuleDependency`.
39+
}
4140

42-
void handleModuleDependency(ModuleDeps MD) override {
43-
// These are ignored for the make format as it can't support the full
44-
// set of deps, and handleFileDependency handles enough for implicitly
45-
// built modules to work.
46-
}
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+
}
4746

48-
void handleContextHash(std::string Hash) override {}
47+
void handleContextHash(std::string Hash) override {}
4948

50-
std::string lookupModuleOutput(const ModuleID &ID,
51-
ModuleOutputKind Kind) override {
52-
llvm::report_fatal_error("unexpected call to lookupModuleOutput");
53-
}
49+
std::string lookupModuleOutput(const ModuleID &ID,
50+
ModuleOutputKind Kind) override {
51+
llvm::report_fatal_error("unexpected call to lookupModuleOutput");
52+
}
5453

55-
void printDependencies(std::string &S) {
56-
assert(Opts && "Handled dependency output options.");
57-
58-
class DependencyPrinter : public DependencyFileGenerator {
59-
public:
60-
DependencyPrinter(DependencyOutputOptions &Opts,
61-
ArrayRef<std::string> Dependencies)
62-
: DependencyFileGenerator(Opts) {
63-
for (const auto &Dep : Dependencies)
64-
addDependency(Dep);
65-
}
66-
67-
void printDependencies(std::string &S) {
68-
llvm::raw_string_ostream OS(S);
69-
outputDependencyFile(OS);
70-
}
71-
};
72-
73-
DependencyPrinter Generator(*Opts, Dependencies);
74-
Generator.printDependencies(S);
75-
}
54+
void printDependencies(std::string &S) {
55+
assert(Opts && "Handled dependency output options.");
56+
57+
class DependencyPrinter : public DependencyFileGenerator {
58+
public:
59+
DependencyPrinter(DependencyOutputOptions &Opts,
60+
ArrayRef<std::string> Dependencies)
61+
: DependencyFileGenerator(Opts) {
62+
for (const auto &Dep : Dependencies)
63+
addDependency(Dep);
64+
}
65+
66+
void printDependencies(std::string &S) {
67+
llvm::raw_string_ostream OS(S);
68+
outputDependencyFile(OS);
69+
}
70+
};
71+
72+
DependencyPrinter Generator(*Opts, Dependencies);
73+
Generator.printDependencies(S);
74+
}
7675

77-
private:
78-
std::unique_ptr<DependencyOutputOptions> Opts;
79-
std::vector<std::string> Dependencies;
80-
};
76+
protected:
77+
std::unique_ptr<DependencyOutputOptions> Opts;
78+
std::vector<std::string> Dependencies;
79+
};
80+
} // anonymous namespace
8181

82+
llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
83+
const std::vector<std::string> &CommandLine, StringRef CWD) {
8284
MakeDependencyPrinterConsumer Consumer;
8385
auto Result = Worker.computeDependencies(CWD, CommandLine, Consumer);
8486
if (Result)
@@ -89,26 +91,18 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
8991
}
9092

9193
llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
92-
const CompileCommand &Command, StringRef CWD) {
93-
class P1689ModuleDependencyPrinterConsumer : public DependencyConsumer {
94+
const CompileCommand &Command, StringRef CWD,
95+
std::string &MakeformatOutput, std::string &MakeformatOutputPath,
96+
std::optional<StringRef> ModuleName) {
97+
class P1689ModuleDependencyPrinterConsumer
98+
: public MakeDependencyPrinterConsumer {
9499
public:
95100
P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
96101
const CompileCommand &Command)
97102
: Filename(Command.Filename), Rule(Rule) {
98103
Rule.PrimaryOutput = Command.Output;
99104
}
100105

101-
void
102-
handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
103-
void handleFileDependency(StringRef File) override {}
104-
void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
105-
void handleModuleDependency(ModuleDeps MD) override {}
106-
void handleContextHash(std::string Hash) override {}
107-
std::string lookupModuleOutput(const ModuleID &ID,
108-
ModuleOutputKind Kind) override {
109-
llvm::report_fatal_error("unexpected call to lookupModuleOutput");
110-
}
111-
112106
void handleProvidedAndRequiredStdCXXModules(
113107
std::optional<P1689ModuleInfo> Provided,
114108
std::vector<P1689ModuleInfo> Requires) override {
@@ -118,6 +112,12 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
118112
Rule.Requires = Requires;
119113
}
120114

115+
StringRef getMakeFormatDependencyOutputPath() {
116+
if (Opts->OutputFormat != DependencyOutputFormat::Make)
117+
return {};
118+
return Opts->OutputFile;
119+
}
120+
121121
private:
122122
StringRef Filename;
123123
P1689Rule &Rule;
@@ -128,6 +128,10 @@ llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
128128
auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer);
129129
if (Result)
130130
return std::move(Result);
131+
132+
MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
133+
if (!MakeformatOutputPath.empty())
134+
Consumer.printDependencies(MakeformatOutput);
131135
return Rule;
132136
}
133137

clang/test/ClangScanDeps/P1689.cppm

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// It is annoying to handle different slash direction
2+
// in the filesystem of Windows and Linux.
3+
// So we disable the test on Windows here.
4+
// REQUIRES: !system-windows
5+
//
16
// RUN: rm -fr %t
27
// RUN: mkdir -p %t
38
// RUN: split-file %s %t
@@ -25,42 +30,50 @@
2530
// RUN: clang-scan-deps -format=p1689 \
2631
// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/User.cpp -o %t/User.o \
2732
// RUN: | FileCheck %t/User.cpp -DPREFIX=%/t
33+
//
34+
// Check we can generate the make-style dependencies as expected.
35+
// RUN: clang-scan-deps -format=p1689 \
36+
// RUN: -- %clang++ -std=c++20 -c -fprebuilt-module-path=%t %t/impl_part.cppm -o %t/impl_part.o \
37+
// RUN: -MT %t/impl_part.o.ddi -MD -MF %t/impl_part.dep
38+
// RUN: cat %t/impl_part.dep | FileCheck %t/impl_part.cppm -DPREFIX=%/t --check-prefix=CHECK-MAKE
39+
//
40+
// Check that we can generate multiple make-style dependency information with compilation database.
41+
// RUN: cat %t/P1689.dep | FileCheck %t/Checks.cpp -DPREFIX=%/t --check-prefix=CHECK-MAKE
2842

2943
//--- P1689.json.in
3044
[
3145
{
3246
"directory": "DIR",
33-
"command": "clang++ -std=c++20 DIR/M.cppm -c -o DIR/M.o",
47+
"command": "clang++ -std=c++20 DIR/M.cppm -c -o DIR/M.o -MT DIR/M.o.ddi -MD -MF DIR/P1689.dep",
3448
"file": "DIR/M.cppm",
3549
"output": "DIR/M.o"
3650
},
3751
{
3852
"directory": "DIR",
39-
"command": "clang++ -std=c++20 DIR/Impl.cpp -c -o DIR/Impl.o",
53+
"command": "clang++ -std=c++20 DIR/Impl.cpp -c -o DIR/Impl.o -MT DIR/Impl.o.ddi -MD -MF DIR/P1689.dep",
4054
"file": "DIR/Impl.cpp",
4155
"output": "DIR/Impl.o"
4256
},
4357
{
4458
"directory": "DIR",
45-
"command": "clang++ -std=c++20 DIR/impl_part.cppm -c -o DIR/impl_part.o",
59+
"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",
4660
"file": "DIR/impl_part.cppm",
4761
"output": "DIR/impl_part.o"
4862
},
4963
{
5064
"directory": "DIR",
51-
"command": "clang++ -std=c++20 DIR/interface_part.cppm -c -o DIR/interface_part.o",
65+
"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",
5266
"file": "DIR/interface_part.cppm",
5367
"output": "DIR/interface_part.o"
5468
},
5569
{
5670
"directory": "DIR",
57-
"command": "clang++ -std=c++20 DIR/User.cpp -c -o DIR/User.o",
71+
"command": "clang++ -std=c++20 DIR/User.cpp -c -o DIR/User.o -MT DIR/User.o.ddi -MD -MF DIR/P1689.dep",
5872
"file": "DIR/User.cpp",
5973
"output": "DIR/User.o"
6074
}
6175
]
6276

63-
6477
//--- M.cppm
6578
export module M;
6679
export import :interface_part;
@@ -275,4 +288,17 @@ int main() {
275288
// CHECK-NEXT: "version": 1
276289
// CHECK-NEXT: }
277290

291+
// CHECK-MAKE-DAG: [[PREFIX]]/impl_part.o.ddi: \
292+
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/impl_part.cppm \
293+
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/header.mock
294+
// CHECK-MAKE-DAG: [[PREFIX]]/interface_part.o.ddi: \
295+
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/interface_part.cppm
296+
// CHECK-MAKE-DAG: [[PREFIX]]/M.o.ddi: \
297+
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/M.cppm
298+
// CHECK-MAKE-DAG: [[PREFIX]]/User.o.ddi: \
299+
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/User.cpp
300+
// CHECK-MAKE-DAG: [[PREFIX]]/Impl.o.ddi: \
301+
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/Impl.cpp \
302+
// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/header.mock
303+
278304
//--- header.mock

clang/tools/clang-scan-deps/ClangScanDeps.cpp

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -825,10 +825,47 @@ int main(int argc, const char **argv) {
825825
Errs))
826826
HadErrors = true;
827827
} else if (Format == ScanningOutputFormat::P1689) {
828-
auto MaybeRule =
829-
WorkerTools[I]->getP1689ModuleDependencyFile(*Input, CWD);
830-
if (handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs))
831-
HadErrors = true;
828+
// It is useful to generate the make-format dependency output during
829+
// the scanning for P1689. Otherwise the users need to scan again for
830+
// it. We will generate the make-format dependency output if we find
831+
// `-MF` in the command lines.
832+
std::string MakeformatOutputPath;
833+
std::string MakeformatOutput;
834+
835+
auto MaybeRule = WorkerTools[I]->getP1689ModuleDependencyFile(
836+
*Input, CWD, MakeformatOutput, MakeformatOutputPath,
837+
MaybeModuleName);
838+
HadErrors =
839+
handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs);
840+
841+
if (!MakeformatOutputPath.empty() && !MakeformatOutput.empty() &&
842+
!HadErrors) {
843+
static std::mutex Lock;
844+
// With compilation database, we may open different files
845+
// concurrently or we may write the same file concurrently. So we
846+
// use a map here to allow multiple compile commands to write to the
847+
// same file. Also we need a lock here to avoid data race.
848+
static llvm::StringMap<llvm::raw_fd_ostream> OSs;
849+
std::unique_lock<std::mutex> LockGuard(Lock);
850+
851+
auto OSIter = OSs.find(MakeformatOutputPath);
852+
if (OSIter == OSs.end()) {
853+
std::error_code EC;
854+
OSIter = OSs.try_emplace(MakeformatOutputPath,
855+
MakeformatOutputPath, EC)
856+
.first;
857+
if (EC)
858+
llvm::errs()
859+
<< "Failed to open P1689 make format output file \""
860+
<< MakeformatOutputPath << "\" for " << EC.message()
861+
<< "\n";
862+
}
863+
864+
SharedStream MakeformatOS(OSIter->second);
865+
llvm::Expected<std::string> MaybeOutput(MakeformatOutput);
866+
HadErrors = handleMakeDependencyToolResult(Filename, MaybeOutput,
867+
MakeformatOS, Errs);
868+
}
832869
} else if (MaybeModuleName) {
833870
auto MaybeModuleDepsGraph = WorkerTools[I]->getModuleDependencies(
834871
*MaybeModuleName, Input->CommandLine, CWD, AlreadySeenModules,

0 commit comments

Comments
 (0)