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
1714using mark_type = sourcemeta::one::Build::mark_type;
1815using 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
15168auto 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