Skip to content

Commit 512458b

Browse files
[CAS] Move CASConfiguration into LLVM
Move CASConfiguration into LLVM and add a JSON based configuration file to help constructing CAS from a init file `.cas-config`.
1 parent d4f430d commit 512458b

File tree

7 files changed

+289
-87
lines changed

7 files changed

+289
-87
lines changed

clang/include/clang/CAS/CASOptions.h

Lines changed: 16 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_CLANG_CAS_CASOPTIONS_H
1616

1717
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/CAS/CASConfiguration.h"
1819
#include "llvm/Support/Error.h"
1920
#include <string>
2021
#include <vector>
@@ -30,52 +31,6 @@ namespace clang {
3031

3132
class DiagnosticsEngine;
3233

33-
/// Base class for options configuring which CAS to use. Separated for the
34-
/// fields where we don't need special move/copy logic.
35-
///
36-
/// TODO: Add appropriate options once we support plugins.
37-
class CASConfiguration {
38-
public:
39-
enum CASKind {
40-
UnknownCAS,
41-
InMemoryCAS,
42-
OnDiskCAS,
43-
};
44-
45-
/// Kind of CAS to use.
46-
CASKind getKind() const {
47-
return IsFrozen ? UnknownCAS : CASPath.empty() ? InMemoryCAS : OnDiskCAS;
48-
}
49-
50-
/// Path to a persistent backing store on-disk. This is optional, although \a
51-
/// CASFileSystemRootID is unlikely to work without it.
52-
///
53-
/// - "" means there is none; falls back to in-memory.
54-
/// - "auto" is an alias for an automatically chosen location in the user's
55-
/// system cache.
56-
std::string CASPath;
57-
58-
std::string PluginPath;
59-
/// Each entry is a (<option-name>, <value>) pair.
60-
std::vector<std::pair<std::string, std::string>> PluginOptions;
61-
62-
friend bool operator==(const CASConfiguration &LHS,
63-
const CASConfiguration &RHS) {
64-
return LHS.CASPath == RHS.CASPath && LHS.PluginPath == RHS.PluginPath &&
65-
LHS.PluginOptions == RHS.PluginOptions;
66-
}
67-
friend bool operator!=(const CASConfiguration &LHS,
68-
const CASConfiguration &RHS) {
69-
return !(LHS == RHS);
70-
}
71-
72-
private:
73-
/// Whether the configuration has been "frozen", in order to hide the kind of
74-
/// CAS that's in use.
75-
bool IsFrozen = false;
76-
friend class CASOptions;
77-
};
78-
7934
/// Options configuring which CAS to use. User-accessible fields should be
8035
/// defined in CASConfiguration to enable caching a CAS instance.
8136
///
@@ -87,8 +42,18 @@ class CASConfiguration {
8742
/// clang::createVFSFromCompilerInvocation() uses the same CAS instance that
8843
/// the rest of the compiler job does, without updating all callers. Probably
8944
/// it would be better to update all callers and remove it from here.
90-
class CASOptions : public CASConfiguration {
45+
class CASOptions : public llvm::cas::CASConfiguration {
9146
public:
47+
enum CASKind {
48+
UnknownCAS,
49+
InMemoryCAS,
50+
OnDiskCAS,
51+
};
52+
53+
/// Kind of CAS to use.
54+
CASKind getKind() const {
55+
return IsFrozen ? UnknownCAS : CASPath.empty() ? InMemoryCAS : OnDiskCAS;
56+
}
9257
/// Get a CAS & ActionCache defined by the options above. Future calls will
9358
/// return the same instances... unless the configuration has changed, in
9459
/// which case new ones will be created.
@@ -117,8 +82,6 @@ class CASOptions : public CASConfiguration {
11782
/// default on-disk CAS, otherwise this is a noop.
11883
void ensurePersistentCAS();
11984

120-
void getResolvedCASPath(llvm::SmallVectorImpl<char> &Result) const;
121-
12285
private:
12386
/// Initialize Cached CAS and ActionCache.
12487
llvm::Error initCache() const;
@@ -133,6 +96,10 @@ class CASOptions : public CASConfiguration {
13396
CASConfiguration Config;
13497
};
13598
mutable CachedCAS Cache;
99+
100+
/// Whether the configuration has been "frozen", in order to hide the kind of
101+
/// CAS that's in use.
102+
bool IsFrozen = false;
136103
};
137104

138105
} // end namespace clang

clang/lib/CAS/CASOptions.cpp

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ std::pair<std::shared_ptr<llvm::cas::ObjectStore>,
2222
std::shared_ptr<llvm::cas::ActionCache>>
2323
CASOptions::getOrCreateDatabases(DiagnosticsEngine &Diags,
2424
bool CreateEmptyDBsOnFailure) const {
25-
if (Cache.Config.IsFrozen)
25+
if (IsFrozen)
2626
return {Cache.CAS, Cache.AC};
2727

2828
if (auto E = initCache())
@@ -44,7 +44,7 @@ CASOptions::getOrCreateDatabases() const {
4444
}
4545

4646
void CASOptions::freezeConfig(DiagnosticsEngine &Diags) {
47-
if (Cache.Config.IsFrozen)
47+
if (IsFrozen)
4848
return;
4949

5050
// Make sure the cache is initialized.
@@ -57,7 +57,7 @@ void CASOptions::freezeConfig(DiagnosticsEngine &Diags) {
5757
// scheduled/executed at a level that has access to the configuration.
5858
auto &CurrentConfig = static_cast<CASConfiguration &>(*this);
5959
CurrentConfig = CASConfiguration();
60-
CurrentConfig.IsFrozen = Cache.Config.IsFrozen = true;
60+
IsFrozen = true;
6161

6262
if (Cache.CAS) {
6363
// Set the CASPath to the hash schema, since that leaks through CASContext's
@@ -90,41 +90,10 @@ llvm::Error CASOptions::initCache() const {
9090
Cache.Config = CurrentConfig;
9191
StringRef CASPath = Cache.Config.CASPath;
9292

93-
if (!PluginPath.empty()) {
94-
std::pair<std::shared_ptr<ObjectStore>, std::shared_ptr<ActionCache>> DBs;
95-
if (llvm::Error E =
96-
createPluginCASDatabases(PluginPath, CASPath, PluginOptions)
97-
.moveInto(DBs)) {
98-
return E;
99-
}
100-
std::tie(Cache.CAS, Cache.AC) = std::move(DBs);
101-
return llvm::Error::success();
102-
}
103-
104-
if (CASPath.empty()) {
105-
Cache.CAS = llvm::cas::createInMemoryCAS();
106-
Cache.AC = llvm::cas::createInMemoryActionCache();
107-
return llvm::Error::success();
108-
}
109-
110-
SmallString<256> PathBuf;
111-
getResolvedCASPath(PathBuf);
112-
if (CASPath == "auto") {
113-
getDefaultOnDiskCASPath(PathBuf);
114-
CASPath = PathBuf;
115-
}
116-
std::pair<std::unique_ptr<ObjectStore>, std::unique_ptr<ActionCache>> DBs;
117-
if (llvm::Error E = createOnDiskUnifiedCASDatabases(CASPath).moveInto(DBs))
118-
return E;
93+
auto DBs = Cache.Config.createDatabases();
94+
if (!DBs)
95+
return DBs.takeError();
11996

120-
std::tie(Cache.CAS, Cache.AC) = std::move(DBs);
97+
std::tie(Cache.CAS, Cache.AC) = std::move(*DBs);
12198
return llvm::Error::success();
12299
}
123-
124-
void CASOptions::getResolvedCASPath(SmallVectorImpl<char> &Result) const {
125-
if (CASPath == "auto") {
126-
getDefaultOnDiskCASPath(Result);
127-
} else {
128-
Result.assign(CASPath.begin(), CASPath.end());
129-
}
130-
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===- CASOptions.h - Options for configuring the CAS -----------*- C++ -*-===//
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+
/// \file
10+
/// Defines the llvm::cas::CASConfiguration interface.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_CAS_CASCONFIGURATION_H
15+
#define LLVM_CAS_CASCONFIGURATION_H
16+
17+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
18+
#include "llvm/Support/Error.h"
19+
#include "llvm/Support/VirtualFileSystem.h"
20+
#include <string>
21+
#include <vector>
22+
23+
namespace llvm::cas {
24+
25+
class ActionCache;
26+
class ObjectStore;
27+
28+
/// Base class for options configuring which CAS to use.
29+
class CASConfiguration {
30+
public:
31+
/// Path to a persistent backing store on-disk.
32+
///
33+
/// - "" means there is none; falls back to in-memory.
34+
/// - "auto" is an alias for an automatically chosen location in the user's
35+
/// system cache.
36+
std::string CASPath;
37+
/// Path to the CAS plugin library.
38+
std::string PluginPath;
39+
/// Each entry is a (<option-name>, <value>) pair.
40+
std::vector<std::pair<std::string, std::string>> PluginOptions;
41+
42+
friend bool operator==(const CASConfiguration &LHS,
43+
const CASConfiguration &RHS) {
44+
return LHS.CASPath == RHS.CASPath && LHS.PluginPath == RHS.PluginPath &&
45+
LHS.PluginOptions == RHS.PluginOptions;
46+
}
47+
friend bool operator!=(const CASConfiguration &LHS,
48+
const CASConfiguration &RHS) {
49+
return !(LHS == RHS);
50+
}
51+
52+
// Get resolved CASPath.
53+
void getResolvedCASPath(llvm::SmallVectorImpl<char> &Result) const;
54+
55+
// Create CASDatabase from the CASConfiguration.
56+
llvm::Expected<std::pair<std::shared_ptr<llvm::cas::ObjectStore>,
57+
std::shared_ptr<llvm::cas::ActionCache>>>
58+
createDatabases() const;
59+
60+
/// Write CAS configuration file.
61+
void writeConfigurationFile(raw_ostream &OS) const;
62+
63+
/// Create CASConfiguration from config file content.
64+
static llvm::Expected<CASConfiguration>
65+
createFromConfig(llvm::StringRef Content);
66+
67+
/// Create CASConfiguration from recurively search config file from a path.
68+
///
69+
/// Returns the path to configuration file and its corresponding
70+
/// CASConfiguration.
71+
static std::optional<std::pair<std::string, CASConfiguration>>
72+
createFromSearchConfigFile(
73+
StringRef Path,
74+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = nullptr);
75+
};
76+
77+
} // namespace llvm::cas
78+
79+
#endif

llvm/lib/CAS/CASConfiguration.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//===- CASConfiguration.cpp -------------------------------------*- C++ -*-===//
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 "llvm/CAS/CASConfiguration.h"
10+
#include "llvm/CAS/ActionCache.h"
11+
#include "llvm/CAS/BuiltinUnifiedCASDatabases.h"
12+
#include "llvm/CAS/ObjectStore.h"
13+
#include "llvm/Support/JSON.h"
14+
15+
using namespace llvm;
16+
using namespace llvm::cas;
17+
18+
void CASConfiguration::getResolvedCASPath(
19+
llvm::SmallVectorImpl<char> &Result) const {
20+
if (CASPath == "auto") {
21+
getDefaultOnDiskCASPath(Result);
22+
} else {
23+
Result.assign(CASPath.begin(), CASPath.end());
24+
}
25+
}
26+
27+
Expected<std::pair<std::shared_ptr<ObjectStore>, std::shared_ptr<ActionCache>>>
28+
CASConfiguration::createDatabases() const {
29+
if (!PluginPath.empty())
30+
return createPluginCASDatabases(PluginPath, CASPath, PluginOptions);
31+
32+
if (CASPath.empty()) {
33+
return std::pair(createInMemoryCAS(), createInMemoryActionCache());
34+
}
35+
36+
SmallString<128> PathBuf;
37+
getResolvedCASPath(PathBuf);
38+
39+
std::pair<std::unique_ptr<ObjectStore>, std::unique_ptr<ActionCache>> DBs;
40+
return createOnDiskUnifiedCASDatabases(PathBuf);
41+
}
42+
43+
void CASConfiguration::writeConfigurationFile(raw_ostream &OS) const {
44+
using namespace llvm::json;
45+
Object Root;
46+
Root["CASPath"] = CASPath;
47+
Root["PluginPath"] = PluginPath;
48+
49+
Array PlugOpts;
50+
for (const auto &Opt : PluginOptions) {
51+
Object Entry;
52+
Entry[Opt.first] = Opt.second;
53+
PlugOpts.emplace_back(std::move(Entry));
54+
}
55+
Root["PluginOptions"] = std::move(PlugOpts);
56+
57+
OS << formatv("{0:2}", Value(std::move(Root)));
58+
}
59+
60+
Expected<CASConfiguration>
61+
CASConfiguration::createFromConfig(StringRef Content) {
62+
auto Parsed = json::parse(Content);
63+
if (!Parsed)
64+
return Parsed.takeError();
65+
66+
CASConfiguration Config;
67+
auto *Root = Parsed->getAsObject();
68+
if (!Root)
69+
return createStringError(
70+
"CASConfiguration file error: top level object missing");
71+
72+
if (auto CASPath = Root->getString("CASPath"))
73+
Config.CASPath = *CASPath;
74+
75+
if (auto PluginPath = Root->getString("PluginPath"))
76+
Config.PluginPath = *PluginPath;
77+
78+
if (auto *Opts = Root->getArray("PluginOptions")) {
79+
for (auto &Opt : *Opts) {
80+
if (auto *Arg = Opt.getAsObject()) {
81+
for (auto &Entry : *Arg) {
82+
if (auto V = Entry.second.getAsString())
83+
Config.PluginOptions.emplace_back(Entry.first.str(), *V);
84+
}
85+
}
86+
}
87+
}
88+
89+
return Config;
90+
}
91+
92+
std::optional<std::pair<std::string, CASConfiguration>>
93+
CASConfiguration::createFromSearchConfigFile(
94+
StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
95+
if (!VFS)
96+
VFS = vfs::getRealFileSystem();
97+
98+
while (!Path.empty()) {
99+
SmallString<256> ConfigPath(Path);
100+
sys::path::append(ConfigPath, ".cas-config");
101+
auto File = VFS->openFileForRead(ConfigPath);
102+
if (!File || !*File) {
103+
Path = sys::path::parent_path(Path);
104+
continue;
105+
}
106+
107+
auto Buffer = (*File)->getBuffer(ConfigPath);
108+
if (!Buffer || !*Buffer) {
109+
Path = sys::path::parent_path(Path);
110+
continue;
111+
}
112+
113+
auto Config = createFromConfig((*Buffer)->getBuffer());
114+
if (!Config) {
115+
consumeError(Config.takeError());
116+
Path = sys::path::parent_path(Path);
117+
continue;
118+
}
119+
return std::pair{ConfigPath.str().str(), *Config};
120+
}
121+
return std::nullopt;
122+
}

llvm/lib/CAS/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMCAS
77
ActionCaches.cpp
88
BuiltinCAS.cpp
99
BuiltinUnifiedCASDatabases.cpp
10+
CASConfiguration.cpp
1011
CASFileSystem.cpp
1112
CASNodeSchema.cpp
1213
CASOutputBackend.cpp

0 commit comments

Comments
 (0)