11#include < sourcemeta/one/build.h>
22
3+ #include < sourcemeta/core/jsonschema.h>
4+
35#include < cassert> // assert
46#include < chrono> // std::chrono::nanoseconds, std::chrono::duration_cast
57#include < cstdint> // std::int64_t
@@ -18,7 +20,13 @@ auto Build::dependency(std::filesystem::path node)
1820}
1921
2022Build::Build (const std::filesystem::path &output_root)
21- : root{std::filesystem::canonical (output_root)} {
23+ : root{(static_cast <void >(std::filesystem::create_directories (output_root)),
24+ std::filesystem::canonical (output_root))} {
25+ for (const auto &entry :
26+ std::filesystem::recursive_directory_iterator (this ->root )) {
27+ this ->tracker .emplace (entry.path (), false );
28+ }
29+
2230 const auto deps_path{this ->root / DEPENDENCIES_FILE};
2331 if (!std::filesystem::exists (deps_path)) {
2432 return ;
@@ -114,15 +122,14 @@ auto Build::has_dependencies(const std::filesystem::path &path) const -> bool {
114122 return match != this ->dependencies_map .end () && !match->second .empty ();
115123}
116124
117- auto Build::write_dependencies (
118- const std::function<bool (const std::filesystem::path &)> &filter) -> void {
125+ auto Build::finish () -> void {
119126 const auto deps_path{this ->root / DEPENDENCIES_FILE};
120127 std::ofstream stream{deps_path};
121128 assert (!stream.fail ());
122129
123130 std::shared_lock dependencies_lock{this ->dependencies_mutex };
124131 for (const auto &entry : this ->dependencies_map ) {
125- if (! filter (this ->root / entry.first )) {
132+ if (this -> is_untracked_file (this ->root / entry.first )) {
126133 continue ;
127134 }
128135
@@ -156,6 +163,13 @@ auto Build::write_dependencies(
156163
157164 stream.flush ();
158165 stream.close ();
166+ this ->track (deps_path);
167+
168+ for (const auto &entry : this ->tracker ) {
169+ if (!entry.second ) {
170+ std::filesystem::remove_all (entry.first );
171+ }
172+ }
159173}
160174
161175auto Build::refresh (const std::filesystem::path &path) -> void {
@@ -198,4 +212,53 @@ auto Build::mark(const std::filesystem::path &path)
198212 }
199213}
200214
215+ auto Build::track (const std::filesystem::path &path)
216+ -> const std::filesystem::path & {
217+ assert (path.is_absolute ());
218+ assert (std::filesystem::exists (path));
219+ std::unique_lock lock{this ->tracker_mutex };
220+ assert (!this ->tracker .contains (path) || !this ->tracker .at (path));
221+ const auto &result{this ->tracker .insert_or_assign (path, true ).first ->first };
222+ for (auto current = path; !current.empty () && current != this ->root ;
223+ current = current.parent_path ()) {
224+ this ->tracker .insert_or_assign (current, true );
225+ }
226+
227+ return result;
228+ }
229+
230+ auto Build::is_untracked_file (const std::filesystem::path &path) const -> bool {
231+ std::shared_lock lock{this ->tracker_mutex };
232+ const auto match{this ->tracker .find (path)};
233+ return match == this ->tracker .cend () || !match->second ;
234+ }
235+
236+ auto Build::output_write_json (const std::filesystem::path &path,
237+ const sourcemeta::core::JSON &document)
238+ -> const std::filesystem::path & {
239+ assert (path.is_absolute ());
240+ std::filesystem::create_directories (path.parent_path ());
241+ std::ofstream stream{path};
242+ assert (!stream.fail ());
243+ sourcemeta::core::stringify (document, stream);
244+ return this ->track (path);
245+ }
246+
247+ auto Build::write_json_if_different (const std::filesystem::path &path,
248+ const sourcemeta::core::JSON &document)
249+ -> void {
250+ if (std::filesystem::exists (path)) {
251+ const auto current{sourcemeta::core::read_json (path)};
252+ if (current != document) {
253+ this ->output_write_json (path, document);
254+ this ->refresh (path);
255+ } else {
256+ this ->track (path);
257+ }
258+ } else {
259+ this ->output_write_json (path, document);
260+ this ->refresh (path);
261+ }
262+ }
263+
201264} // namespace sourcemeta::one
0 commit comments