Skip to content

Commit 4fc6247

Browse files
authored
Revamp deps.txt as an mmaped state.bin (#721)
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent ebc7f66 commit 4fc6247

30 files changed

+236
-341
lines changed

src/build/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
sourcemeta_library(NAMESPACE sourcemeta PROJECT one NAME build
2-
SOURCES build.cc)
2+
SOURCES build.cc build_state.h)
33
target_link_libraries(sourcemeta_one_build
44
PUBLIC sourcemeta::core::json
5-
PRIVATE sourcemeta::core::jsonschema)
5+
PRIVATE sourcemeta::core::jsonschema sourcemeta::core::io)

src/build/build.cc

Lines changed: 20 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
#include <sourcemeta/one/build.h>
22

3+
#include "build_state.h"
4+
35
#include <sourcemeta/core/jsonschema.h>
46

57
#include <algorithm> // std::ranges::none_of
68
#include <cassert> // assert
7-
#include <chrono> // std::chrono::nanoseconds, std::chrono::duration_cast
8-
#include <cstdint> // std::int64_t
9-
#include <fstream> // std::ofstream, std::ifstream
10-
11-
#include <mutex> // std::unique_lock
12-
#include <string> // std::string
13-
#include <string_view> // std::string_view
9+
#include <fstream> // std::ofstream
1410

15-
static constexpr std::string_view DEPENDENCIES_FILE{"deps.txt"};
11+
#include <mutex> // std::unique_lock
12+
#include <string> // std::string
1613

1714
using mark_type = sourcemeta::one::Build::mark_type;
1815
using Entry = sourcemeta::one::Build::Entry;
@@ -39,101 +36,21 @@ Build::Build(const std::filesystem::path &output_root)
3936
: root{(static_cast<void>(std::filesystem::create_directories(output_root)),
4037
std::filesystem::canonical(output_root))},
4138
root_string{this->root.string()} {
42-
for (const auto &entry :
43-
std::filesystem::recursive_directory_iterator(this->root)) {
44-
auto &map_entry{this->entries_[entry.path().native()]};
45-
map_entry.tracked = false;
46-
map_entry.is_directory = entry.is_directory();
47-
}
39+
const auto state_path{this->root / STATE_FILENAME};
40+
if (!std::filesystem::exists(state_path)) {
41+
// First run or crash recovery: scan directory for orphaned files
42+
for (const auto &entry :
43+
std::filesystem::recursive_directory_iterator(this->root)) {
44+
auto &map_entry{this->entries_[entry.path().native()]};
45+
map_entry.tracked = false;
46+
map_entry.is_directory = entry.is_directory();
47+
}
4848

49-
const auto dependencies_path{this->root / DEPENDENCIES_FILE};
50-
if (!std::filesystem::exists(dependencies_path)) {
5149
return;
5250
}
5351

5452
try {
55-
std::ifstream stream{dependencies_path};
56-
if (!stream.is_open()) {
57-
return;
58-
}
59-
60-
std::string contents{std::istreambuf_iterator<char>(stream),
61-
std::istreambuf_iterator<char>()};
62-
63-
std::string current_key;
64-
std::vector<std::filesystem::path> current_static_dependencies;
65-
std::vector<std::filesystem::path> current_dynamic_dependencies;
66-
std::size_t position{0};
67-
68-
while (position < contents.size()) {
69-
auto newline{contents.find('\n', position)};
70-
if (newline == std::string::npos) {
71-
newline = contents.size();
72-
}
73-
74-
if (newline <= position + 2 || contents[position + 1] != ' ') {
75-
position = newline + 1;
76-
continue;
77-
}
78-
79-
const char tag{contents[position]};
80-
const std::string_view value{contents.data() + position + 2,
81-
newline - position - 2};
82-
83-
switch (tag) {
84-
case 't':
85-
if (!current_key.empty()) {
86-
auto &previous_entry{this->entries_[current_key]};
87-
previous_entry.static_dependencies =
88-
std::move(current_static_dependencies);
89-
previous_entry.dynamic_dependencies =
90-
std::move(current_dynamic_dependencies);
91-
current_static_dependencies = {};
92-
current_dynamic_dependencies = {};
93-
}
94-
95-
current_key = this->root_string;
96-
current_key += '/';
97-
current_key += value;
98-
break;
99-
case 's':
100-
current_static_dependencies.emplace_back(
101-
(this->root / std::string{value}).lexically_normal());
102-
break;
103-
case 'd':
104-
current_dynamic_dependencies.emplace_back(
105-
(this->root / std::string{value}).lexically_normal());
106-
break;
107-
case 'm': {
108-
const auto space{value.find(' ')};
109-
if (space != std::string_view::npos) {
110-
const auto path_part{value.substr(0, space)};
111-
const auto nanoseconds_part{value.substr(space + 1)};
112-
const std::chrono::nanoseconds nanoseconds{
113-
std::stoll(std::string{nanoseconds_part})};
114-
const auto mark_value{mark_type{
115-
std::chrono::duration_cast<mark_type::duration>(nanoseconds)}};
116-
std::string absolute_key{this->root_string};
117-
absolute_key += '/';
118-
absolute_key += path_part;
119-
this->entries_[absolute_key].file_mark = mark_value;
120-
}
121-
122-
break;
123-
}
124-
default:
125-
break;
126-
}
127-
128-
position = newline + 1;
129-
}
130-
131-
if (!current_key.empty()) {
132-
auto &last_entry{this->entries_[current_key]};
133-
last_entry.static_dependencies = std::move(current_static_dependencies);
134-
last_entry.dynamic_dependencies = std::move(current_dynamic_dependencies);
135-
}
136-
this->has_previous_run = true;
53+
this->has_previous_run = load_state(state_path, this->entries_);
13754
} catch (...) {
13855
this->entries_.clear();
13956
}
@@ -149,69 +66,14 @@ auto Build::has_dependencies(const std::filesystem::path &path) const -> bool {
14966
}
15067

15168
auto Build::finish() -> void {
152-
const auto dependencies_path{this->root / DEPENDENCIES_FILE};
153-
std::ofstream stream{dependencies_path};
154-
assert(!stream.fail());
155-
156-
const auto root_prefix_size{this->root_string.size() + 1};
157-
std::shared_lock lock{this->mutex};
158-
for (const auto &entry : this->entries_) {
159-
if (!entry.second.tracked) {
160-
continue;
161-
}
162-
163-
if (!entry.second.static_dependencies.empty() ||
164-
!entry.second.dynamic_dependencies.empty()) {
165-
if (entry.first.size() > root_prefix_size &&
166-
entry.first.starts_with(this->root_string)) {
167-
stream << "t " << std::string_view{entry.first}.substr(root_prefix_size)
168-
<< '\n';
169-
} else {
170-
stream << "t " << entry.first << '\n';
171-
}
172-
173-
for (const auto &dependency : entry.second.static_dependencies) {
174-
const auto &dependency_string{dependency.native()};
175-
if (dependency_string.size() > root_prefix_size &&
176-
dependency_string.starts_with(this->root_string)) {
177-
stream << "s "
178-
<< std::string_view{dependency_string}.substr(root_prefix_size)
179-
<< '\n';
180-
} else {
181-
stream << "s " << dependency_string << '\n';
182-
}
183-
}
69+
const auto state_path{this->root / STATE_FILENAME};
18470

185-
for (const auto &dependency : entry.second.dynamic_dependencies) {
186-
const auto &dependency_string{dependency.native()};
187-
if (dependency_string.size() > root_prefix_size &&
188-
dependency_string.starts_with(this->root_string)) {
189-
stream << "d "
190-
<< std::string_view{dependency_string}.substr(root_prefix_size)
191-
<< '\n';
192-
} else {
193-
stream << "d " << dependency_string << '\n';
194-
}
195-
}
196-
}
197-
198-
if (entry.second.file_mark.has_value()) {
199-
if (entry.first.size() > root_prefix_size &&
200-
entry.first.starts_with(this->root_string)) {
201-
const auto nanoseconds{
202-
std::chrono::duration_cast<std::chrono::nanoseconds>(
203-
entry.second.file_mark.value().time_since_epoch())
204-
.count()};
205-
stream << "m " << std::string_view{entry.first}.substr(root_prefix_size)
206-
<< ' ' << static_cast<std::int64_t>(nanoseconds) << '\n';
207-
}
208-
}
71+
{
72+
std::shared_lock lock{this->mutex};
73+
save_state(state_path, this->entries_);
20974
}
21075

211-
stream.flush();
212-
stream.close();
213-
lock.unlock();
214-
this->track(dependencies_path);
76+
this->track(state_path);
21577

21678
// Remove untracked files inside the output directory
21779
std::shared_lock read_lock{this->mutex};

0 commit comments

Comments
 (0)