1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
|
// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef INCLUDED_SRC_BUILDTOOL_STORAGE_CONFIG_HPP
#define INCLUDED_SRC_BUILDTOOL_STORAGE_CONFIG_HPP
#include <cstddef>
#include <filesystem>
#include <optional>
#include <string>
#include <utility>
#include "fmt/core.h"
#include "gsl/gsl"
#include "src/buildtool/common/protocol_traits.hpp"
#include "src/buildtool/crypto/hash_function.hpp"
#include "src/buildtool/file_system/file_system_manager.hpp"
#include "src/buildtool/storage/backend_description.hpp"
#include "src/utils/cpp/expected.hpp"
#include "src/utils/cpp/gsl.hpp"
#include "src/utils/cpp/tmp_dir.hpp"
struct StorageConfig;
struct GenerationConfig final {
gsl::not_null<StorageConfig const*> const storage_config;
std::filesystem::path const cas_f;
std::filesystem::path const cas_x;
std::filesystem::path const cas_t;
std::filesystem::path const cas_large_f;
std::filesystem::path const cas_large_t;
std::filesystem::path const action_cache;
std::filesystem::path const target_cache;
};
struct StorageConfig final {
class Builder;
static inline auto const kDefaultBuildRoot =
FileSystemManager::GetUserHome() / ".cache" / "just";
// Build root directory. All the storage dirs are subdirs of build_root.
// By default, build_root is set to $HOME/.cache/just.
// If the user uses --local-build-root PATH,
// then build_root will be set to PATH.
std::filesystem::path const build_root = kDefaultBuildRoot;
// Number of total storage generations (default: two generations).
std::size_t const num_generations = 2;
HashFunction const hash_function{HashFunction::Type::GitSHA1};
// Hash of the execution backend description
BackendDescription const backend_description;
/// \brief Root directory of all storage generations.
[[nodiscard]] auto CacheRoot() const noexcept -> std::filesystem::path {
return build_root / "protocol-dependent";
}
/// \brief Root directory of all repository generations.
[[nodiscard]] auto RepositoryRoot() const noexcept
-> std::filesystem::path {
return build_root / "repositories";
}
/// \brief Directory for the given generation of stored repositories
[[nodiscard]] auto RepositoryGenerationRoot(
std::size_t index) const noexcept -> std::filesystem::path {
ExpectsAudit(index < num_generations);
auto generation = std::string{"generation-"} + std::to_string(index);
return RepositoryRoot() / generation;
}
/// \brief Directory for the git repository of the given generation
[[nodiscard]] auto GitGenerationRoot(std::size_t index) const noexcept
-> std::filesystem::path {
return RepositoryGenerationRoot(index) / "git";
}
/// \brief Directory for the git repository storing various roots
[[nodiscard]] auto GitRoot() const noexcept -> std::filesystem::path {
return GitGenerationRoot(0);
}
/// \brief Root directory of specific storage generation
[[nodiscard]] auto GenerationCacheRoot(std::size_t index) const noexcept
-> std::filesystem::path {
ExpectsAudit(index < num_generations);
auto generation = std::string{"generation-"} + std::to_string(index);
return CacheRoot() / generation;
}
/// \brief Root directory for all ephemeral directories, i.e., directories
/// that can (and should) be removed immediately by garbage collection.
[[nodiscard]] auto EphemeralRoot() const noexcept -> std::filesystem::path {
return GenerationCacheRoot(0) / "ephemeral";
}
/// \brief Root directory for local action executions; individual actions
/// create a working directory below this root.
[[nodiscard]] auto ExecutionRoot() const noexcept -> std::filesystem::path {
return EphemeralRoot() / "exec_root";
}
/// \brief Create a tmp directory with controlled lifetime for specific
/// operations (archive, zip, file, distdir checkouts; fetch; update).
[[nodiscard]] auto CreateTypedTmpDir(std::string const& type) const noexcept
-> TmpDir::Ptr {
// try to create parent dir
auto parent_path = EphemeralRoot() / "tmp-workspaces" / type;
return TmpDir::Create(parent_path);
}
[[nodiscard]] auto CreateGenerationConfig(
std::size_t generation) const noexcept -> GenerationConfig {
bool const native = ProtocolTraits::IsNative(hash_function.GetType());
auto const cache_root = GenerationCacheRoot(generation);
auto const cache_dir = UpdatePathForCompatibility(cache_root, native);
return GenerationConfig{
.storage_config = this,
.cas_f = cache_dir / "casf",
.cas_x = cache_dir / "casx",
.cas_t = cache_dir / (native ? "cast" : "casf"),
.cas_large_f = cache_dir / "cas-large-f",
.cas_large_t = cache_dir / (native ? "cas-large-t" : "cas-large-f"),
.action_cache = cache_dir / "ac",
.target_cache = cache_dir / "tc"};
};
private:
// different folder for different caching protocol
[[nodiscard]] static auto UpdatePathForCompatibility(
std::filesystem::path const& dir,
bool is_native) -> std::filesystem::path {
return dir / (is_native ? "git-sha1" : "compatible-sha256");
};
};
class StorageConfig::Builder final {
public:
explicit Builder() = default;
/// \brief Create a configurable builder from an existing config.
/// Useful, for example, to make a copy of an existing config and change
/// the hash type.
[[nodiscard]] static auto Rebuild(StorageConfig const& config) noexcept
-> Builder {
return Builder{}
.SetBuildRoot(config.build_root)
.SetNumGenerations(config.num_generations)
.SetHashType(config.hash_function.GetType())
.SetBackendDescription(config.backend_description);
}
auto SetBuildRoot(std::filesystem::path value) noexcept -> Builder& {
build_root_ = std::move(value);
return *this;
}
/// \brief Specifies the number of storage generations.
auto SetNumGenerations(std::size_t value) noexcept -> Builder& {
num_generations_ = value;
return *this;
}
/// \brief Specify the type of the hash function
auto SetHashType(HashFunction::Type value) noexcept -> Builder& {
hash_type_ = value;
return *this;
}
auto SetBackendDescription(BackendDescription value) noexcept -> Builder& {
backend_description_ = std::move(value);
return *this;
}
[[nodiscard]] auto Build() const noexcept
-> expected<StorageConfig, std::string> {
// To not duplicate default arguments of StorageConfig in builder,
// create a default config and copy default arguments from there.
StorageConfig const default_config;
auto build_root = default_config.build_root;
if (build_root_.has_value()) {
build_root = *build_root_;
if (FileSystemManager::IsRelativePath(build_root)) {
return unexpected(fmt::format(
"Build root must be absolute path but got '{}'.",
build_root.string()));
}
}
auto num_generations = default_config.num_generations;
if (num_generations_.has_value()) {
num_generations = *num_generations_;
if (num_generations == 0) {
return unexpected(std::string{
"The number of generations must be greater than 0."});
}
}
auto const hash_function = hash_type_.has_value()
? HashFunction{*hash_type_}
: default_config.hash_function;
// Hash the execution backend description
auto backend_description = default_config.backend_description;
if (backend_description_) {
backend_description = *backend_description_;
}
return StorageConfig{
.build_root = std::move(build_root),
.num_generations = num_generations,
.hash_function = hash_function,
.backend_description = std::move(backend_description)};
}
private:
std::optional<std::filesystem::path> build_root_;
std::optional<std::size_t> num_generations_;
std::optional<HashFunction::Type> hash_type_;
std::optional<BackendDescription> backend_description_;
};
#endif // INCLUDED_SRC_BUILDTOOL_STORAGE_CONFIG_HPP
|