Index: clang/test/ClangScanDeps/P1689.cppm =================================================================== --- clang/test/ClangScanDeps/P1689.cppm +++ clang/test/ClangScanDeps/P1689.cppm @@ -3,8 +3,25 @@ // RUN: split-file %s %t // // RUN: sed "s|DIR|%/t|g" %t/P1689.json.in > %t/P1689.json -// RUN: clang-scan-deps -compilation-database %t/P1689.json -format=p1689 | FileCheck %t/Checks.cpp -DPREFIX=%/t -// RUN: clang-scan-deps --mode=preprocess-dependency-directives -compilation-database %t/P1689.json -format=p1689 | FileCheck %t/Checks.cpp -DPREFIX=%/t +// RUN: clang-scan-deps --compilation-database %t/P1689.json -format=p1689 | FileCheck %t/Checks.cpp -DPREFIX=%/t +// RUN: clang-scan-deps --mode=preprocess-dependency-directives --compilation-database %t/P1689.json -format=p1689 | FileCheck %t/Checks.cpp -DPREFIX=%/t +// +// Check the seperated dependency format. +// RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/M.cppm --p1689-targeted-output=%t/M.o \ +// RUN: --p1689-targeted-directory=%t --p1689-targeted-command="clang++ -std=c++20 %t/M.cppm -c -o %t/M.o" \ +// RUN: | FileCheck %t/M.cppm -DPREFIX=%/t +// RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/Impl.cpp --p1689-targeted-output=%t/Impl.o \ +// RUN: --p1689-targeted-directory=%t --p1689-targeted-command="clang++ -std=c++20 %t/Impl.cpp -c -o %t/Impl.o" \ +// RUN: | FileCheck %t/Impl.cpp -DPREFIX=%/t +// RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/impl_part.cppm --p1689-targeted-output=%t/impl_part.o \ +// RUN: --p1689-targeted-directory=%t --p1689-targeted-command="clang++ -std=c++20 %t/impl_part.cppm -c -o %t/impl_part.o" \ +// RUN: | FileCheck %t/impl_part.cppm -DPREFIX=%/t +// RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/interface_part.cppm --p1689-targeted-output=%t/interface_part.o \ +// RUN: --p1689-targeted-directory=%t --p1689-targeted-command="clang++ -std=c++20 %t/interface_part.cppm -c -o %t/interface_part.o" \ +// RUN: | FileCheck %t/interface_part.cppm -DPREFIX=%/t +// RUN: clang-scan-deps -format=p1689 --p1689-targeted-file-name=%t/User.cpp --p1689-targeted-output=%t/User.o \ +// RUN: --p1689-targeted-directory=%t --p1689-targeted-command="clang++ -std=c++20 %t/User.cpp -c -o %t/User.o" \ +// RUN: | FileCheck %t/User.cpp -DPREFIX=%/t //--- P1689.json.in [ @@ -47,6 +64,31 @@ import :impl_part; export void Hello(); +// CHECK: { +// CHECK-NEXT: "revision": 0, +// CHECK-NEXT: "rules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "primary-output": "[[PREFIX]]/M.o", +// CHECK-NEXT: "provides": [ +// CHECK-NEXT: { +// CHECK-NEXT: "is-interface": true, +// CHECK-NEXT: "logical-name": "M", +// CHECK-NEXT: "source-path": "[[PREFIX]]/M.cppm" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "requires": [ +// CHECK-NEXT: { +// CHECK-NEXT: "logical-name": "M:interface_part" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "logical-name": "M:impl_part" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "version": 1 +// CHECK-NEXT: } + //--- Impl.cpp module; #include "header.mock" @@ -55,6 +97,21 @@ std::cout << "Hello "; } +// CHECK: { +// CHECK-NEXT: "revision": 0, +// CHECK-NEXT: "rules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "primary-output": "[[PREFIX]]/Impl.o", +// CHECK-NEXT: "requires": [ +// CHECK-NEXT: { +// CHECK-NEXT: "logical-name": "M" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "version": 1 +// CHECK-NEXT: } + //--- impl_part.cppm module; #include "header.mock" @@ -66,10 +123,49 @@ std::cout << W << std::endl; } +// CHECK: { +// CHECK-NEXT: "revision": 0, +// CHECK-NEXT: "rules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "primary-output": "[[PREFIX]]/impl_part.o", +// CHECK-NEXT: "provides": [ +// CHECK-NEXT: { +// CHECK-NEXT: "is-interface": false, +// CHECK-NEXT: "logical-name": "M:impl_part", +// CHECK-NEXT: "source-path": "[[PREFIX]]/impl_part.cppm" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "requires": [ +// CHECK-NEXT: { +// CHECK-NEXT: "logical-name": "M:interface_part" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "version": 1 +// CHECK-NEXT: } + //--- interface_part.cppm export module M:interface_part; export void World(); +// CHECK: { +// CHECK-NEXT: "revision": 0, +// CHECK-NEXT: "rules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "primary-output": "[[PREFIX]]/interface_part.o", +// CHECK-NEXT: "provides": [ +// CHECK-NEXT: { +// CHECK-NEXT: "is-interface": true, +// CHECK-NEXT: "logical-name": "M:interface_part", +// CHECK-NEXT: "source-path": "[[PREFIX]]/interface_part.cppm" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "version": 1 +// CHECK-NEXT: } + //--- User.cpp import M; import third_party_module; @@ -79,6 +175,24 @@ return 0; } +// CHECK: { +// CHECK-NEXT: "revision": 0, +// CHECK-NEXT: "rules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "primary-output": "[[PREFIX]]/User.o", +// CHECK-NEXT: "requires": [ +// CHECK-NEXT: { +// CHECK-NEXT: "logical-name": "M" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "logical-name": "third_party_module" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "version": 1 +// CHECK-NEXT: } + //--- Checks.cpp // CHECK: { // CHECK-NEXT: "revision": 0, Index: clang/tools/clang-scan-deps/ClangScanDeps.cpp =================================================================== --- clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -167,7 +167,7 @@ llvm::cl::opt CompilationDB("compilation-database", - llvm::cl::desc("Compilation database"), llvm::cl::Required, + llvm::cl::desc("Compilation database"), llvm::cl::Optional, llvm::cl::cat(DependencyScannerCategory)); llvm::cl::opt ModuleName( @@ -175,6 +175,30 @@ llvm::cl::desc("the module of which the dependencies are to be computed"), llvm::cl::cat(DependencyScannerCategory)); +llvm::cl::opt P1689TargetedFileName( + "p1689-targeted-file-name", llvm::cl::Optional, + llvm::cl::desc("Only supported for P1689, the targeted file name of which " + "the dependencies are to be computed."), + llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt P1689TargetedOutput( + "p1689-targeted-output", llvm::cl::Optional, + llvm::cl::desc("Only supported for P1689, the targeted output of which the " + "dependencies are to be computed."), + llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt P1689TargetedDirectory( + "p1689-targeted-directory", llvm::cl::Optional, + llvm::cl::desc("Only supported for P1689, the targeted directory of which " + "the dependencies are to be computed."), + llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt P1689TargetedCommand( + "p1689-targeted-command", llvm::cl::Optional, + llvm::cl::desc("Only supported for P1689, the targeted command of which " + "the dependencies are to be computed."), + llvm::cl::cat(DependencyScannerCategory)); + llvm::cl::list ModuleDepTargets( "dependency-target", llvm::cl::desc("The names of dependency targets for the dependency file"), @@ -522,6 +546,46 @@ return std::string(Path); } +// getCompilationDataBase - If -compilation-database is set, load the +// compilation database from the specified file. Otherwise if the we're +// generating P1689 format, trying to generate the compilation database +// form the p1689 related command lines. Otherwise, the invocation is +// ill-formed. +static std::unique_ptr +getCompilationDataBase(std::string &ErrorMessage) { + if (!CompilationDB.empty()) + return tooling::JSONCompilationDatabase::loadFromFile( + CompilationDB, ErrorMessage, + tooling::JSONCommandLineSyntax::AutoDetect); + + if (Format != ScanningOutputFormat::P1689) { + llvm::errs() << "the --compilation-database option: must be specified at " + "least once!"; + return nullptr; + } + + if (P1689TargetedFileName.empty() || P1689TargetedOutput.empty() || + P1689TargetedDirectory.empty() || P1689TargetedCommand.empty()) { + llvm::errs() << "missing compilation database and failed to contruct " + "one by --p1689-targeted-file-name, --p1689-targeted-output" + "--p1689-targeted-directory and --p1689-targeted-command"; + return nullptr; + } + + llvm::raw_string_ostream OS(CompilationDB); + + using namespace llvm::json; + Array Entries; + Object Output{{"directory", P1689TargetedDirectory}, + {"command", P1689TargetedCommand}, + {"file", P1689TargetedFileName}, + {"output", P1689TargetedOutput}}; + Entries.push_back(std::move(Output)); + OS << Value(std::move(Entries)); + return tooling::JSONCompilationDatabase::loadFromBuffer( + CompilationDB, ErrorMessage, tooling::JSONCommandLineSyntax::AutoDetect); +} + int main(int argc, const char **argv) { llvm::InitLLVM X(argc, argv); llvm::cl::HideUnrelatedOptions(DependencyScannerCategory); @@ -530,9 +594,7 @@ std::string ErrorMessage; std::unique_ptr Compilations = - tooling::JSONCompilationDatabase::loadFromFile( - CompilationDB, ErrorMessage, - tooling::JSONCommandLineSyntax::AutoDetect); + getCompilationDataBase(ErrorMessage); if (!Compilations) { llvm::errs() << "error: " << ErrorMessage << "\n"; return 1;