Skip to content

Commit 1305fe6

Browse files
committed
[libclang][deps] Add initial support for compilation caching
Adds API to allow specifying the CAS + ActionCache to use in a DependencyScannerService, and test that this can be used to get cached module compilations.
1 parent 61c6781 commit 1305fe6

File tree

9 files changed

+327
-5
lines changed

9 files changed

+327
-5
lines changed

clang/include/clang-c/CAS.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*==-- clang-c/CAS.h - CAS Interface ------------------------------*- C -*-===*\
2+
|* *|
3+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
4+
|* Exceptions. *|
5+
|* See https://llvm.org/LICENSE.txt for license information. *|
6+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
7+
|* *|
8+
|*===----------------------------------------------------------------------===*|
9+
|* *|
10+
|* This header provides interfaces for creating and working with CAS and *|
11+
|* ActionCache interfaces. *|
12+
|* *|
13+
|* An example of its usage is available in c-index-test/core_main.cpp. *|
14+
|* *|
15+
|* EXPERIMENTAL: These interfaces are experimental and will change. If you *|
16+
|* use these be prepared for them to change without notice on any commit. *|
17+
|* *|
18+
\*===----------------------------------------------------------------------===*/
19+
20+
#ifndef LLVM_CLANG_C_CAS_H
21+
#define LLVM_CLANG_C_CAS_H
22+
23+
#include "clang-c/CXString.h"
24+
#include "clang-c/Platform.h"
25+
26+
#ifdef __cplusplus
27+
extern "C" {
28+
#endif
29+
30+
/**
31+
* \defgroup CAS CAS and ActionCache interface.
32+
* @{
33+
*/
34+
35+
/**
36+
* Content-addressable storage for objects.
37+
*/
38+
typedef struct CXOpaqueCASObjectStore *CXCASObjectStore;
39+
40+
/**
41+
* A cache from a key describing an action to the result of doing it.
42+
*/
43+
typedef struct CXOpaqueCASActionCache *CXCASActionCache;
44+
45+
/**
46+
* Dispose of a \c CXCASObjectStore object.
47+
*/
48+
CINDEX_LINKAGE void
49+
clang_experimental_cas_ObjectStore_dispose(CXCASObjectStore CAS);
50+
51+
/**
52+
* Dispose of a \c CXCASActionCache object.
53+
*/
54+
CINDEX_LINKAGE void
55+
clang_experimental_cas_ActionCache_dispose(CXCASActionCache Cache);
56+
57+
/**
58+
* Gets or creates a persistent on-disk CAS object store at \p Path.
59+
*
60+
* \param Path The path to locate the object store.
61+
* \param[out] Error The error string to pass back to client (if any).
62+
*
63+
* \returns The resulting object store, or null if there was an error.
64+
*/
65+
CINDEX_LINKAGE CXCASObjectStore
66+
clang_experimental_cas_OnDiskObjectStore_create(
67+
const char *Path, CXString *Error);
68+
69+
/**
70+
* Gets or creates a persistent on-disk action cache at \p Path.
71+
*
72+
* \param Path The path to locate the object store.
73+
* \param[out] Error The error string to pass back to client (if any).
74+
*
75+
* \returns The resulting object store, or null if there was an error.
76+
*/
77+
CINDEX_LINKAGE CXCASActionCache
78+
clang_experimental_cas_OnDiskActionCache_create(
79+
const char *Path, CXString *Error);
80+
81+
/**
82+
* @}
83+
*/
84+
85+
#ifdef __cplusplus
86+
}
87+
#endif
88+
89+
#endif // LLVM_CLANG_C_CAS_H

clang/include/clang-c/Dependencies.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define LLVM_CLANG_C_DEPENDENCIES_H
2222

2323
#include "clang-c/BuildSystem.h"
24+
#include "clang-c/CAS.h"
2425
#include "clang-c/CXDiagnostic.h"
2526
#include "clang-c/CXErrorCode.h"
2627
#include "clang-c/CXString.h"
@@ -201,6 +202,22 @@ CINDEX_LINKAGE void
201202
clang_experimental_DependencyScannerServiceOptions_setDependencyMode(
202203
CXDependencyScannerServiceOptions Opts, CXDependencyMode Mode);
203204

205+
/**
206+
* Specify a \c CXCASObjectStore in the given options. If an object store and
207+
* action cache are available, the scanner will produce cached commands.
208+
*/
209+
CINDEX_LINKAGE void
210+
clang_experimental_DependencyScannerServiceOptions_setObjectStore(
211+
CXDependencyScannerServiceOptions Opts, CXCASObjectStore CAS);
212+
213+
/**
214+
* Specify a \c CXCASActionCache in the given options. If an object store and
215+
* action cache are available, the scanner will produce cached commands.
216+
*/
217+
CINDEX_LINKAGE void
218+
clang_experimental_DependencyScannerServiceOptions_setActionCache(
219+
CXDependencyScannerServiceOptions Opts, CXCASActionCache Cache);
220+
204221
/**
205222
* See \c clang_experimental_DependencyScannerService_create_v1.
206223
*/

clang/test/Index/Core/scan-deps-cas.m

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: rm -rf %t.mcp %t
2+
// RUN: echo %S > %t.result
3+
//
4+
// RUN: c-index-test core --scan-deps %S -output-dir=%t \
5+
// RUN: -cas-path %t/cas -action-cache-path %t/cache \
6+
// RUN: -- %clang -c -I %S/Inputs/module \
7+
// RUN: -fmodules -fmodules-cache-path=%t.mcp \
8+
// RUN: -o FoE.o -x objective-c %s >> %t.result
9+
// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck %s -DOUTPUTS=%/t
10+
11+
// RUN: c-index-test core --scan-deps %S -output-dir=%t \
12+
// RUN: -- %clang -c -I %S/Inputs/module \
13+
// RUN: -fmodules -fmodules-cache-path=%t.mcp \
14+
// RUN: -o FoE.o -x objective-c %s | FileCheck %s -check-prefix=NO_CAS
15+
// NO_CAS-NOT: fcas
16+
// NO_CAS-NOT: faction-cache
17+
// NO_CAS-NOT: fcache-compile-job
18+
19+
@import ModA;
20+
21+
// CHECK: [[PREFIX:.*]]
22+
// CHECK-NEXT: modules:
23+
// CHECK-NEXT: module:
24+
// CHECK-NEXT: name: ModA
25+
// CHECK-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]]
26+
// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap
27+
// CHECK-NEXT: module-deps:
28+
// CHECK-NEXT: file-deps:
29+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/ModA.h
30+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubModA.h
31+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubSubModA.h
32+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/module.modulemap
33+
// CHECK-NEXT: build-args:
34+
// CHECK-SAME: -cc1
35+
// CHECK-SAME: -fcas-path
36+
// CHECK-SAME: -faction-cache-path
37+
// CHECK-SAME: -fcas-fs llvmcas://{{[[:xdigit:]]+}}
38+
// CHECK-SAME: -fcache-compile-job
39+
// CHECK-SAME: -emit-module
40+
// CHECK-SAME: -fmodule-name=ModA
41+
// CHECK-SAME: -fno-implicit-modules
42+
43+
// CHECK-NEXT: dependencies:
44+
// CHECK-NEXT: command 0:
45+
// CHECK-NEXT: context-hash: [[HASH_TU:[A-Z0-9]+]]
46+
// CHECK-NEXT: module-deps:
47+
// CHECK-NEXT: ModA:[[HASH_MOD_A]]
48+
// CHECK-NEXT: file-deps:
49+
// CHECK-NEXT: [[PREFIX]]/scan-deps-cas.m
50+
// CHECK-NEXT: build-args:
51+
// CHECK-SAME: -cc1
52+
// CHECK-SAME: -fcas-path
53+
// CHECK-SAME: -faction-cache-path
54+
// CHECK-SAME: -fcas-fs llvmcas://{{[[:xdigit:]]+}}
55+
// CHECK-SAME: -fcache-compile-job
56+
// CHECK-SAME: -fmodule-file-cache-key=[[PCM:.*ModA_.*pcm]]=llvmcas://{{[[:xdigit:]]+}}
57+
// CHECK-SAME: -fmodule-file={{(ModA=)?}}[[PCM]]

clang/tools/c-index-test/core_main.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ static cl::list<std::string> DependencyTargets(
140140
static cl::opt<bool> DeprecatedDriverCommand(
141141
"deprecated-driver-command",
142142
cl::desc("use a single driver command to build the tu (deprecated)"));
143+
static llvm::cl::opt<std::string>
144+
CASPath("cas-path", llvm::cl::desc("Path for on-disk CAS."));
145+
static llvm::cl::opt<std::string>
146+
CachePath("action-cache-path",
147+
llvm::cl::desc("Path for on-disk action cache."));
143148
}
144149
} // anonymous namespace
145150

@@ -681,6 +686,8 @@ static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) {
681686
static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
682687
bool SerializeDiags, bool DependencyFile,
683688
ArrayRef<std::string> DepTargets, std::string OutputPath,
689+
Optional<std::string> CASPath,
690+
Optional<std::string> CachePath,
684691
Optional<std::string> ModuleName = None) {
685692
CXDependencyScannerServiceOptions Opts =
686693
clang_experimental_DependencyScannerServiceOptions_create();
@@ -690,6 +697,36 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
690697
clang_experimental_DependencyScannerServiceOptions_setDependencyMode(
691698
Opts, CXDependencyMode_Full);
692699

700+
CXString Error;
701+
if (CASPath) {
702+
CXCASObjectStore CAS = clang_experimental_cas_OnDiskObjectStore_create(
703+
CASPath->c_str(), &Error);
704+
auto CleanupCache = llvm::make_scope_exit(
705+
[&] { clang_experimental_cas_ObjectStore_dispose(CAS); });
706+
if (!CAS) {
707+
llvm::errs() << "error: failed to create ObjectStore\n";
708+
llvm::errs() << clang_getCString(Error) << "\n";
709+
clang_disposeString(Error);
710+
return 1;
711+
}
712+
clang_experimental_DependencyScannerServiceOptions_setObjectStore(Opts,
713+
CAS);
714+
if (CachePath) {
715+
CXCASActionCache Cache = clang_experimental_cas_OnDiskActionCache_create(
716+
CachePath->c_str(), &Error);
717+
auto CleanupCache = llvm::make_scope_exit(
718+
[&] { clang_experimental_cas_ActionCache_dispose(Cache); });
719+
if (!Cache) {
720+
llvm::errs() << "error: failed to create ActionCache\n";
721+
llvm::errs() << clang_getCString(Error) << "\n";
722+
clang_disposeString(Error);
723+
return 1;
724+
}
725+
clang_experimental_DependencyScannerServiceOptions_setActionCache(Opts,
726+
Cache);
727+
}
728+
}
729+
693730
CXDependencyScannerService Service =
694731
clang_experimental_DependencyScannerService_create_v1(Opts);
695732
CXDependencyScannerWorker Worker =
@@ -1133,15 +1170,21 @@ int indextest_core_main(int argc, const char **argv) {
11331170
}
11341171
return aggregateDataAsJSON(storePath, PathRemapper, OS);
11351172
}
1136-
1173+
1174+
Optional<std::string> CASPath =
1175+
options::CASPath.empty() ? None : Optional<std::string>(options::CASPath);
1176+
Optional<std::string> CachePath =
1177+
options::CachePath.empty() ? None
1178+
: Optional<std::string>(options::CachePath);
1179+
11371180
if (options::Action == ActionType::ScanDeps) {
11381181
if (options::InputFiles.empty()) {
11391182
errs() << "error: missing working directory\n";
11401183
return 1;
11411184
}
11421185
return scanDeps(CompArgs, options::InputFiles[0], options::SerializeDiags,
11431186
options::DependencyFile, options::DependencyTargets,
1144-
options::OutputDir);
1187+
options::OutputDir, CASPath, CachePath);
11451188
}
11461189

11471190
if (options::Action == ActionType::ScanDepsByModuleName) {
@@ -1156,7 +1199,8 @@ int indextest_core_main(int argc, const char **argv) {
11561199
}
11571200
return scanDeps(CompArgs, options::InputFiles[0], options::SerializeDiags,
11581201
options::DependencyFile, options::DependencyTargets,
1159-
options::OutputDir, options::ModuleName);
1202+
options::OutputDir, CASPath, CachePath,
1203+
options::ModuleName);
11601204
}
11611205

11621206
if (options::Action == ActionType::WatchDir) {

clang/tools/libclang/CASUtils.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===- CASUtils.h - libclang CAS utilities --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CASUTILS_H
10+
#define LLVM_CLANG_TOOLS_LIBCLANG_CASUTILS_H
11+
12+
#include "clang-c/CAS.h"
13+
#include "clang/Basic/LLVM.h"
14+
#include "llvm/CAS/ActionCache.h"
15+
#include "llvm/CAS/ObjectStore.h"
16+
#include "llvm/Support/CBindingWrapping.h"
17+
#include <string>
18+
19+
namespace clang {
20+
namespace cas {
21+
22+
struct WrappedObjectStore {
23+
std::shared_ptr<ObjectStore> CAS;
24+
std::string CASPath;
25+
};
26+
27+
struct WrappedActionCache {
28+
std::shared_ptr<ActionCache> Cache;
29+
std::string CachePath;
30+
};
31+
32+
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(WrappedObjectStore, CXCASObjectStore)
33+
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(WrappedActionCache, CXCASActionCache)
34+
35+
} // namespace cas
36+
} // namespace clang
37+
38+
#endif // LLVM_CLANG_TOOLS_LIBCLANG_CASUTILS_H

clang/tools/libclang/CCAS.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===- CCAS.cpp -----------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang-c/CAS.h"
10+
11+
#include "CASUtils.h"
12+
#include "CXString.h"
13+
14+
#include "clang/Basic/LLVM.h"
15+
#include "clang/CAS/CASOptions.h"
16+
#include "llvm/CAS/ActionCache.h"
17+
#include "llvm/CAS/ObjectStore.h"
18+
19+
using namespace clang;
20+
using namespace clang::cas;
21+
22+
void clang_experimental_cas_ObjectStore_dispose(CXCASObjectStore CAS) {
23+
delete unwrap(CAS);
24+
}
25+
void clang_experimental_cas_ActionCache_dispose(CXCASActionCache Cache) {
26+
delete unwrap(Cache);
27+
}
28+
29+
CXCASObjectStore
30+
clang_experimental_cas_OnDiskObjectStore_create(const char *Path,
31+
CXString *Error) {
32+
auto CAS = llvm::cas::createOnDiskCAS(Path);
33+
if (!CAS) {
34+
if (Error)
35+
*Error = cxstring::createDup(llvm::toString(CAS.takeError()));
36+
return nullptr;
37+
}
38+
return wrap(new WrappedObjectStore{std::move(*CAS), Path});
39+
}
40+
41+
CXCASActionCache
42+
clang_experimental_cas_OnDiskActionCache_create(const char *Path,
43+
CXString *Error) {
44+
auto Cache = llvm::cas::createOnDiskActionCache(Path);
45+
if (!Cache) {
46+
if (Error)
47+
*Error = cxstring::createDup(llvm::toString(Cache.takeError()));
48+
return nullptr;
49+
}
50+
return wrap(new WrappedActionCache{std::move(*Cache), Path});
51+
}

0 commit comments

Comments
 (0)