Skip to content

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

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 8 commits into from
Mar 11, 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 @@ -125,6 +125,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 @@ -420,6 +434,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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there precedent for "CWD" in the C API layer? Maybe we should spell it out as "WorkingDirectory".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried doing this but clang_experimental_DependencyScannerServiceOptions_setCurrentWorkingDirOptimization got too long (83 chars), and I don't want to rename one API but not the other. In this case I think I slightly prefer keeping the names as they are. But I am all ears for better/consistent names.


/**
* \returns the number \c CXDepGraphTUCommand objects in the graph.
*/
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 @@ -869,6 +869,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
1 change: 1 addition & 0 deletions clang/test/ClangScanDeps/cas-fs-multiple-commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
// CHECK-LIBCLANG-NEXT: module:
// CHECK-LIBCLANG-NEXT: name: Mod
// CHECK-LIBCLANG-NEXT: context-hash: {{.*}}
// CHECK-LIBCLANG-NEXT: cwd-ignored: 0
// CHECK-LIBCLANG-NEXT: module-map-path: [[PREFIX]]/module.modulemap
// CHECK-LIBCLANG-NEXT: casfs-root-id: [[M_ROOT_ID:llvmcas://[[:xdigit:]]+]]
// CHECK-LIBCLANG-NEXT: cache-key: [[M_CACHE_KEY:llvmcas://[[:xdigit:]]+]]
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
1 change: 1 addition & 0 deletions clang/test/ClangScanDeps/include-tree-multiple-commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
// CHECK-LIBCLANG-NEXT: module:
// CHECK-LIBCLANG-NEXT: name: Mod
// CHECK-LIBCLANG-NEXT: context-hash: {{.*}}
// CHECK-LIBCLANG-NEXT: cwd-ignored: 0
// CHECK-LIBCLANG-NEXT: module-map-path: [[PREFIX]]/include/module.modulemap
// CHECK-LIBCLANG-NEXT: include-tree-id: [[M_INCLUDE_TREE:llvmcas://[[:xdigit:]]+]]
// CHECK-LIBCLANG-NEXT: cache-key: [[M_CACHE_KEY:llvmcas://[[:xdigit:]]+]]
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: casfs-root-id: [[CASFS_MODA_ROOT_ID:llvmcas://[[:xdigit:]]+]]
// CHECK-NEXT: cache-key: [[CASFS_MODA_CACHE_KEY:llvmcas://[[:xdigit:]]+]]
Expand Down Expand Up @@ -72,6 +73,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 @@ -162,7 +162,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 @@ -717,6 +721,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 @@ -813,6 +821,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 *ModuleFilesystemRootID =
Expand All @@ -832,6 +841,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 (ModuleFilesystemRootID)
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 @@ -100,6 +101,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 @@ -156,7 +166,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 @@ -452,6 +463,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 @@ -570,6 +570,12 @@ LLVM_20 {
clang_visitCXXMethods;
};

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