Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h =================================================================== --- clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -92,9 +92,17 @@ getDependencyFile(const std::vector &CommandLine, StringRef CWD, llvm::Optional ModuleName = None); + /// Collect the module dependency in P1689 format for C++20 named modules. + /// + /// \param MakeformatOutput The output parameter for dependency information + /// in make format if \p MakeformatOutput isn't none. + /// + /// \returns A \c StringError with the diagnostic output if clang errors + /// occurred, P1689 dependency format rules otherwise. llvm::Expected getP1689ModuleDependencyFile(const clang::tooling::CompileCommand &Command, StringRef CWD, + llvm::Optional &MakeformatOutput, llvm::Optional ModuleName = None); /// Collect the full module dependency graph for the input, ignoring any Index: clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp =================================================================== --- clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -39,67 +39,69 @@ llvm::IntrusiveRefCntPtr FS) : Worker(Service, std::move(FS)) {} -llvm::Expected DependencyScanningTool::getDependencyFile( - const std::vector &CommandLine, StringRef CWD, - llvm::Optional ModuleName) { - /// Prints out all of the gathered dependencies into a string. - class MakeDependencyPrinterConsumer : public DependencyConsumer { - public: - void handleBuildCommand(Command) override {} +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(Opts); + } - void - handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { - this->Opts = std::make_unique(Opts); - } + void handleFileDependency(StringRef File) override { + Dependencies.push_back(std::string(File)); + } - void handleFileDependency(StringRef File) override { - Dependencies.push_back(std::string(File)); - } + void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override { + // Same as `handleModuleDependency`. + } - 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 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 {} - void handleContextHash(std::string Hash) override {} + std::string lookupModuleOutput(const ModuleID &ID, + ModuleOutputKind Kind) override { + llvm::report_fatal_error("unexpected call to lookupModuleOutput"); + } - 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."); - void printDependencies(std::string &S) { - assert(Opts && "Handled dependency output options."); - - class DependencyPrinter : public DependencyFileGenerator { - public: - DependencyPrinter(DependencyOutputOptions &Opts, - ArrayRef 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); - } + class DependencyPrinter : public DependencyFileGenerator { + public: + DependencyPrinter(DependencyOutputOptions &Opts, + ArrayRef Dependencies) + : DependencyFileGenerator(Opts) { + for (const auto &Dep : Dependencies) + addDependency(Dep); + } - private: - std::unique_ptr Opts; - std::vector Dependencies; - }; + void printDependencies(std::string &S) { + llvm::raw_string_ostream OS(S); + outputDependencyFile(OS); + } + }; + DependencyPrinter Generator(*Opts, Dependencies); + Generator.printDependencies(S); + } + +protected: + std::unique_ptr Opts; + std::vector Dependencies; +}; +} // anonymous namespace + +llvm::Expected DependencyScanningTool::getDependencyFile( + const std::vector &CommandLine, StringRef CWD, + llvm::Optional ModuleName) { MakeDependencyPrinterConsumer Consumer; auto Result = Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName); @@ -112,8 +114,10 @@ llvm::Expected DependencyScanningTool::getP1689ModuleDependencyFile( const clang::tooling::CompileCommand &Command, StringRef CWD, + llvm::Optional &MakeformatOutput, llvm::Optional ModuleName) { - class P1689ModuleDependencyPrinterConsumer : public DependencyConsumer { + class P1689ModuleDependencyPrinterConsumer + : public MakeDependencyPrinterConsumer { public: P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule, const CompileCommand &Command) @@ -122,14 +126,13 @@ } 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"); + handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { + MakeDependencyPrinterConsumer::handleDependencyOutputOpts(Opts); + /// NOTE: Currently, the compiler will generate module unit (.cppm) files + /// into module file (.pcm) first. Then it compiles the module file (.pcm) + /// into object files. It results that the Targets[0] here will be .pcm + /// file instead of the expected output file. + this->Opts->Targets[0] = Rule.PrimaryOutput; } void handleProvidedAndRequiredStdCXXModules( @@ -152,6 +155,8 @@ ModuleName); if (Result) return std::move(Result); + if (MakeformatOutput) + Consumer.printDependencies(*MakeformatOutput); return Rule; } Index: clang/test/ClangScanDeps/P1689.cppm =================================================================== --- clang/test/ClangScanDeps/P1689.cppm +++ clang/test/ClangScanDeps/P1689.cppm @@ -22,6 +22,14 @@ // RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/User.cpp --p1689-targeted-output=%t/User.o \ // RUN: -- -std=c++20 -c \ // RUN: | FileCheck %t/User.cpp -DPREFIX=%/t +// +// Check we can generate the make-style dependencies as expected. +// RUN: clang-scan-deps --compilation-database %t/P1689.json -format=p1689 --p1689-makeformat-output=%t/P1689.dep +// RUN: cat %t/P1689.dep | FileCheck %t/Checks.cpp -DPREFIX=%/t --check-prefix=CHECK-MAKE +// +// RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/impl_part.cppm --p1689-targeted-output=%t/impl_part.o \ +// RUN: --p1689-makeformat-output=%t/impl_part.dep -- -std=c++20 -c +// RUN: cat %t/impl_part.dep | FileCheck %t/impl_part.cppm -DPREFIX=%/t --check-prefix=CHECK-MAKE //--- P1689.json.in [ @@ -57,7 +65,6 @@ } ] - //--- M.cppm export module M; export import :interface_part; @@ -145,6 +152,10 @@ // CHECK-NEXT: "version": 1 // CHECK-NEXT: } +// CHECK-MAKE: [[PREFIX]]/impl_part.o: +// CHECK-MAKE: [[PREFIX]]/impl_part.cppm +// CHECK-MAKE: [[PREFIX]]/header.mock + //--- interface_part.cppm export module M:interface_part; export void World(); @@ -268,4 +279,17 @@ // CHECK-NEXT: "version": 1 // CHECK-NEXT: } +// CHECK-MAKE-DAG: [[PREFIX]]/User.o: +// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/User.cpp +// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/Impl.o: +// CHECK-MAKE-DAG: [[PREFIX]]/Impl.cpp +// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/header.mock +// CHECK-MAKE-DAG: [[PREFIX]]/M.o: +// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/M.cppm +// CHECK-MAKE-DAG: [[PREFIX]]/interface_part.o: +// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/interface_part.cppm +// CHECK-MAKE-DAG: [[PREFIX]]/impl_part.o: +// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/impl_part.cppm +// CHECK-MAKE-DAG-NEXT: [[PREFIX]]/header.mock + //--- header.mock Index: clang/tools/clang-scan-deps/ClangScanDeps.cpp =================================================================== --- clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -187,6 +187,13 @@ "dependencies are to be computed."), llvm::cl::cat(DependencyScannerCategory)); +llvm::cl::opt P1689MakeformatOutputpath( + "p1689-makeformat-output", llvm::cl::Optional, + llvm::cl::desc("Only supported for P1689. Print the make-style dependency " + "output to the specified output. This is a helper for build " + "systems to do duplicate scanning."), + llvm::cl::cat(DependencyScannerCategory)); + llvm::cl::opt P1689TargettedCommand( llvm::cl::Positional, llvm::cl::ZeroOrMore, llvm::cl::desc("The command line flags for the target of which " @@ -584,6 +591,19 @@ return std::move(FixedCompilationDatabase); } +static raw_ostream &getDependencyOS() { + if (!P1689MakeformatOutputpath.empty()) { + std::error_code EC; + static llvm::raw_fd_ostream OS(P1689MakeformatOutputpath, EC); + if (EC) + llvm::errs() << "Failed to open P1689 make format output file \"" + << P1689MakeformatOutputpath << "\" for " << EC.message() + << "\n"; + return OS; + } + return llvm::outs(); +} + int main(int argc, const char **argv) { std::string ErrorMessage; std::unique_ptr Compilations = @@ -662,7 +682,7 @@ SharedStream Errs(llvm::errs()); // Print out the dependency results to STDOUT by default. - SharedStream DependencyOS(llvm::outs()); + SharedStream DependencyOS(getDependencyOS()); DependencyScanningService Service(ScanMode, Format, OptimizeArgs, EagerLoadModules); @@ -722,10 +742,20 @@ Errs)) HadErrors = true; } else if (Format == ScanningOutputFormat::P1689) { + llvm::Optional MakeformatOutput; + if (!P1689MakeformatOutputpath.empty()) + MakeformatOutput.emplace(); + auto MaybeRule = WorkerTools[I]->getP1689ModuleDependencyFile( - *Input, CWD, MaybeModuleName); - if (handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs)) - HadErrors = true; + *Input, CWD, MakeformatOutput, MaybeModuleName); + HadErrors = + handleP1689DependencyToolResult(Filename, MaybeRule, PD, Errs); + + if (MakeformatOutput && !MakeformatOutput->empty() && !HadErrors) { + llvm::Expected MaybeOutput(*MakeformatOutput); + HadErrors = handleMakeDependencyToolResult(Filename, MaybeOutput, + DependencyOS, Errs); + } } else if (DeprecatedDriverCommand) { auto MaybeFullDeps = WorkerTools[I]->getFullDependenciesLegacyDriverCommand(