aboutsummaryrefslogtreecommitdiff
path: root/build2/version/rule.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/version/rule.cxx')
-rw-r--r--build2/version/rule.cxx151
1 files changed, 151 insertions, 0 deletions
diff --git a/build2/version/rule.cxx b/build2/version/rule.cxx
new file mode 100644
index 0000000..1da37bb
--- /dev/null
+++ b/build2/version/rule.cxx
@@ -0,0 +1,151 @@
+// file : build2/version/rule.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/version/rule>
+
+#include <build2/scope>
+#include <build2/target>
+#include <build2/context>
+#include <build2/algorithm>
+#include <build2/filesystem>
+#include <build2/diagnostics>
+
+#include <build2/version/module>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace version
+ {
+ match_result version_doc::
+ match (action a, target& xt, const string&) const
+ {
+ tracer trace ("version::version_file::match");
+
+ doc& t (static_cast<doc&> (xt));
+
+ // We match any doc{} target that is called version (potentially with
+ // some extension and different case) and that has a dependency on a
+ // file called manifest from the same project's src_root.
+ //
+ if (casecmp (t.name, "version") != 0)
+ {
+ l4 ([&]{trace << "name mismatch for target " << t;});
+ return false;
+ }
+
+ const scope& rs (t.root_scope ());
+
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ if (!p.is_a<file> ())
+ continue;
+
+ const target& pt (p.search ());
+
+ if (pt.name != "manifest")
+ continue;
+
+ const scope* prs (pt.base_scope ().root_scope ());
+
+ if (prs == nullptr || prs != &rs || pt.dir != rs.src_path ())
+ continue;
+
+ return true;
+ }
+
+ l4 ([&]{trace << "no manifest prerequisite for target " << t;});
+ return false;
+ }
+
+ recipe version_doc::
+ apply (action a, target& xt) const
+ {
+ doc& t (static_cast<doc&> (xt));
+
+ // Derive file names for the members.
+ //
+ t.derive_path ();
+
+ // Inject dependency on the output directory.
+ //
+ inject_fsdir (a, t);
+
+ // Match prerequisite members.
+ //
+ match_prerequisite_members (a, t);
+
+ switch (a)
+ {
+ case perform_update_id: return &perform_update;
+ case perform_clean_id: return &perform_clean; // Standard clean.
+ default: return noop_recipe; // Configure update.
+ }
+ }
+
+ target_state version_doc::
+ perform_update (action a, const target& xt)
+ {
+ const doc& t (xt.as<const doc&> ());
+ const path& f (t.path ());
+
+ const scope& rs (t.root_scope ());
+ const module& m (*rs.modules.lookup<module> (module::name));
+
+ // Determine if anything needs to be updated.
+ //
+ // While we use the manifest file to decide whether we need to
+ // regenerate the version file, the version itself we get from the
+ // module (we checked above that manifest and version files are in the
+ // same project).
+ //
+ // That is, unless we patched the snapshot information in, in which case
+ // we have to compare the contents.
+ //
+ {
+ auto p (execute_prerequisites (a, t, t.load_mtime ()));
+
+ if (!p.first)
+ {
+ if (!m.version_patched || !exists (f))
+ return p.second;
+
+ try
+ {
+ ifdstream ifs (f, fdopen_mode::in, ifdstream::badbit);
+
+ string s;
+ getline (ifs, s);
+
+ if (s == m.version.string_project ())
+ return p.second;
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to read " << f << ": " << e;
+ }
+ }
+ }
+
+ if (verb >= 2)
+ text << "cat >" << f;
+
+ try
+ {
+ ofdstream ofs (f);
+ ofs << m.version.string_project () << endl;
+ ofs.close ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write " << f << ": " << e;
+ }
+
+ t.mtime (system_clock::now ());
+ return target_state::changed;
+ }
+ }
+}