Skip to content

[clang dependency scanning] C APIs for Current Working Directory Optimization #10276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions clang/include/clang-c/Dependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,20 @@ CINDEX_LINKAGE void
clang_experimental_DependencyScannerServiceOptions_setCASOptions(
CXDependencyScannerServiceOptions Opts, CXCASOptions);

/**
* Set the working directory optimization option.
* The dependency scanner service option Opts will indicate to the scanner that
* the current working directory can or cannot be ignored when computing the
* pcms' context hashes. The scanner will then determine if it is safe to
* optimize each module and act accordingly.
*
* \param Value If it is non zero, the option is on. Otherwise the
* option is off.
*/
CINDEX_LINKAGE void
clang_experimental_DependencyScannerServiceOptions_setCWDOptimization(
CXDependencyScannerServiceOptions Opts, int Value);

/**
* Specify a \c CXCASObjectStore in the given options. If an object store and
* action cache are available, the scanner will produce cached commands.
Expand Down Expand Up @@ -578,6 +592,13 @@ CINDEX_LINKAGE const char *
CINDEX_LINKAGE
const char *clang_experimental_DepGraphModule_getCacheKey(CXDepGraphModule);

/**
* \returns 1 if the scanner ignores the current working directory when
* computing the module's context hash. Otherwise returns 0.
*/
CINDEX_LINKAGE
int clang_experimental_DepGraphModule_isCWDIgnored(CXDepGraphModule);

/**
* \returns the number \c CXDepGraphTUCommand objects in the graph.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ enum class ScanningOptimizations {
IgnoreCWD = (1 << 4),

DSS_LAST_BITMASK_ENUM(IgnoreCWD),
Default = All,

// The build system needs to be aware that the current working
// directory is ignored. Without a good way of notifying the build
// system, it is less risky to default to off.
Default = All & (~IgnoreCWD),
FullIncludeTreeIrrelevant = HeaderSearch | VFS,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ struct ModuleDeps {
/// Whether this is a "system" module.
bool IsSystem;

/// Whether current working directory is ignored.
bool IgnoreCWD;

/// The path to the modulemap file which defines this module.
///
/// This can be used to explicitly build this module. This file will
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,7 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MD.ModuleCacheKey = Key->toString();
}

MD.IgnoreCWD = IgnoreCWD;
MDC.associateWithContextHash(CI, IgnoreCWD, MD);

// Finish the compiler invocation. Requires dependencies and the context hash.
Expand Down
42 changes: 42 additions & 0 deletions clang/test/ClangScanDeps/cwd-c-api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Test the current working directory C APIs.

// RUN: rm -rf %t
// RUN: split-file %s %t

// RUN: c-index-test core -scan-deps -working-dir %S -- %clang \
// RUN: -c %t/main.c -fmodules -fmodules-cache-path=%t/module-cache \
// RUN: 2>&1 > %t/no_cwd_opt.txt
// RUN: cat %t/no_cwd_opt.txt | FileCheck %s --check-prefix=NO-CWD-OPT


// RUN: c-index-test core -scan-deps -working-dir %S -optimize-cwd -- \
// RUN: %clang \
// RUN: -c %t/main.c -fmodules -fmodules-cache-path=%t/module-cache \
// RUN: 2>&1 > %t/cwd_opt.txt
// RUN: cat %t/cwd_opt.txt | FileCheck %s --check-prefix=CWD-OPT

//--- module.modulemap
module Mod { header "Mod.h" }

//--- Mod.h
int foo();

//--- main.c
#include "Mod.h"

int main() {
return foo();
}

// NO-CWD-OPT: modules:
// NO-CWD-OPT-NEXT: module:
// NO-CWD-OPT-NEXT: name: Mod
// NO-CWD-OPT-NEXT: context-hash:{{.*}}
// NO-CWD-OPT-NEXT: cwd-ignored: 0


// CWD-OPT: modules:
// CWD-OPT-NEXT: module:
// CWD-OPT-NEXT: name: Mod
// CWD-OPT-NEXT: context-hash:{{.*}}
// CWD-OPT-NEXT: cwd-ignored: 1
10 changes: 5 additions & 5 deletions clang/test/ClangScanDeps/modules-context-hash-cwd.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
// RUN: sed -e "s|DIR|%/t|g" %t/cdb3.json.in > %t/cdb3.json
// RUN: sed -e "s|DIR|%/t|g" %t/cdb4.json.in > %t/cdb4.json
// RUN: sed -e "s|DIR|%/t|g" %t/cdb5.json.in > %t/cdb5.json
// RUN: clang-scan-deps -compilation-database %t/cdb0.json -format experimental-full > %t/result0.json
// RUN: clang-scan-deps -compilation-database %t/cdb1.json -format experimental-full > %t/result1.json
// RUN: clang-scan-deps -compilation-database %t/cdb0.json -format experimental-full -optimize-args=all > %t/result0.json
// RUN: clang-scan-deps -compilation-database %t/cdb1.json -format experimental-full -optimize-args=all > %t/result1.json
// It is not a typo to use cdb1.json for result2. We intend to use the same
// compilation database, but different clang-scan-deps optimize-args options.
// RUN: clang-scan-deps -compilation-database %t/cdb1.json -format experimental-full -optimize-args=header-search,system-warnings,vfs,canonicalize-macros > %t/result2.json
// RUN: clang-scan-deps -compilation-database %t/cdb3.json -format experimental-full > %t/result3.json
// RUN: clang-scan-deps -compilation-database %t/cdb4.json -format experimental-full > %t/result4.json
// RUN: clang-scan-deps -compilation-database %t/cdb5.json -format experimental-full > %t/result5.json
// RUN: clang-scan-deps -compilation-database %t/cdb3.json -format experimental-full -optimize-args=all > %t/result3.json
// RUN: clang-scan-deps -compilation-database %t/cdb4.json -format experimental-full -optimize-args=all > %t/result4.json
// RUN: clang-scan-deps -compilation-database %t/cdb5.json -format experimental-full -optimize-args=all > %t/result5.json
// RUN: cat %t/result0.json %t/result1.json | FileCheck %s
// RUN: cat %t/result0.json %t/result2.json | FileCheck %s -check-prefix=SKIPOPT
// RUN: cat %t/result3.json %t/result4.json | FileCheck %s -check-prefix=RELPATH
Expand Down
2 changes: 1 addition & 1 deletion clang/test/ClangScanDeps/modules-debug-dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// RUN: split-file %s %t
// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json
// RUN: clang-scan-deps -compilation-database %t/cdb.json -format \
// RUN: experimental-full > %t/result.json
// RUN: experimental-full -optimize-args=all > %t/result.json
// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s

//--- cdb.json.in
Expand Down
1 change: 1 addition & 0 deletions clang/test/Index/Core/scan-deps-by-mod-name.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// CHECK-NEXT: module:
// CHECK-NEXT: name: ModA
// CHECK-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]]
// CHECK-NEXT: cwd-ignored: 0
// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap
// CHECK-NEXT: module-deps:
// CHECK-NEXT: file-deps:
Expand Down
2 changes: 2 additions & 0 deletions clang/test/Index/Core/scan-deps-cas.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
// CHECK-NEXT: module:
// CHECK-NEXT: name: ModA
// CHECK-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]]
// CHECK-NEXT: cwd-ignored: 0
// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap
// CHECK-NEXT: cache-key: [[CASFS_MODA_CACHE_KEY:llvmcas://[[:xdigit:]]+]]
// CHECK-NEXT: module-deps:
Expand Down Expand Up @@ -70,6 +71,7 @@
// INCLUDE_TREE-NEXT: module:
// INCLUDE_TREE-NEXT: name: ModA
// INCLUDE_TREE-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]]
// INCLUDE_TREE-NEXT: cwd-ignored: 0
// INCLUDE_TREE-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap
// INCLUDE_TREE-NEXT: include-tree-id: [[ModA_INCLUDE_TREE_ID:llvmcas://[[:xdigit:]]+]]
// INCLUDE_TREE-NEXT: cache-key: [[ModA_CACHE_KEY:llvmcas://[[:xdigit:]]+]]
Expand Down
1 change: 1 addition & 0 deletions clang/test/Index/Core/scan-deps.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// CHECK-NEXT: module:
// CHECK-NEXT: name: ModA
// CHECK-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]]
// CHECK-NEXT: cwd-ignored: 0
// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap
// CHECK-NEXT: module-deps:
// CHECK-NEXT: file-deps:
Expand Down
12 changes: 11 additions & 1 deletion clang/tools/c-index-test/core_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,11 @@ static cl::opt<bool> TestCASCancellation(
"test-cas-cancellation",
cl::desc(
"perform extra CAS API invocation and cancel it for testing purposes"));
}
static cl::opt<bool> OptimizeCWD(
"optimize-cwd",
cl::desc(
"instruct the scanner to ignore current working directory if safe."));
} // namespace options
} // anonymous namespace

static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS);
Expand Down Expand Up @@ -714,6 +718,10 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
clang_experimental_DependencyScannerServiceOptions_setDependencyMode(
Opts, CXDependencyMode_Full);

if (options::OptimizeCWD)
clang_experimental_DependencyScannerServiceOptions_setCWDOptimization(Opts,
1);

if (DBs)
clang_experimental_DependencyScannerServiceOptions_setCASDatabases(Opts,
DBs);
Expand Down Expand Up @@ -806,6 +814,7 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
const char *Name = clang_experimental_DepGraphModule_getName(Mod);
const char *ContextHash =
clang_experimental_DepGraphModule_getContextHash(Mod);
int CwdIgnored = clang_experimental_DepGraphModule_isCWDIgnored(Mod);
const char *ModuleMapPath =
clang_experimental_DepGraphModule_getModuleMapPath(Mod);
const char *ModuleIncludeTreeID =
Expand All @@ -823,6 +832,7 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
llvm::outs() << " module:\n"
<< " name: " << Name << "\n"
<< " context-hash: " << ContextHash << "\n"
<< " cwd-ignored: " << CwdIgnored << "\n"
<< " module-map-path: "
<< (ModuleMapPath ? ModuleMapPath : "<none>") << "\n";
if (ModuleIncludeTreeID)
Expand Down
17 changes: 16 additions & 1 deletion clang/tools/libclang/CDependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace {
struct DependencyScannerServiceOptions {
ScanningOutputFormat ConfiguredFormat = ScanningOutputFormat::Full;
CASOptions CASOpts;
ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default;
std::shared_ptr<cas::ObjectStore> CAS;
std::shared_ptr<cas::ActionCache> Cache;

Expand Down Expand Up @@ -115,6 +116,15 @@ void clang_experimental_DependencyScannerServiceOptions_setCASOptions(
unwrap(Opts)->CASOpts = *cas::unwrap(CASOpts);
}

void clang_experimental_DependencyScannerServiceOptions_setCWDOptimization(
CXDependencyScannerServiceOptions Opts, int Value) {
auto Mask =
Value != 0 ? ScanningOptimizations::All : ScanningOptimizations::None;
auto OptArgs = unwrap(Opts)->OptimizeArgs;
unwrap(Opts)->OptimizeArgs = (OptArgs & ~ScanningOptimizations::IgnoreCWD) |
(ScanningOptimizations::IgnoreCWD & Mask);
}

void clang_experimental_DependencyScannerServiceOptions_setObjectStore(
CXDependencyScannerServiceOptions Opts, CXCASObjectStore CAS) {
unwrap(Opts)->CAS = cas::unwrap(CAS)->CAS;
Expand Down Expand Up @@ -171,7 +181,8 @@ clang_experimental_DependencyScannerService_create_v1(
}
return wrap(new DependencyScanningService(
ScanningMode::DependencyDirectivesScan, Format, unwrap(Opts)->CASOpts,
std::move(CAS), std::move(Cache), std::move(FS)));
std::move(CAS), std::move(Cache), std::move(FS),
unwrap(Opts)->OptimizeArgs));
}

void clang_experimental_DependencyScannerService_dispose_v0(
Expand Down Expand Up @@ -630,6 +641,10 @@ clang_experimental_DepGraphModule_getCacheKey(CXDepGraphModule CXDepMod) {
return nullptr;
}

int clang_experimental_DepGraphModule_isCWDIgnored(CXDepGraphModule CXDepMod) {
return unwrap(CXDepMod)->ModDeps->IgnoreCWD;
}

size_t clang_experimental_DepGraph_getNumTUCommands(CXDepGraph Graph) {
TranslationUnitDeps &TUDeps = unwrap(Graph)->TUDeps;
return TUDeps.Commands.size();
Expand Down
6 changes: 6 additions & 0 deletions clang/tools/libclang/libclang.map
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,12 @@ LLVM_19 {
clang_Cursor_getBinaryOpcodeStr;
};

LLVM_21 {
global:
clang_experimental_DependencyScannerServiceOptions_setCWDOptimization;
clang_experimental_DepGraphModule_isCWDIgnored;
};

# Example of how to add a new symbol version entry. If you do add a new symbol
# version, please update the example to depend on the version you added.
# LLVM_X {
Expand Down