aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-01 16:10:48 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-01 16:41:08 +0300
commitda9cbf29c403d27c2940f9b31199c4648f8ae4a1 (patch)
tree9732de6f48bdaead2becf32be41b1284e1e83e00 /build2
parent8e69e09b7ec68377758c63092f9b254e95a0d7be (diff)
Move version build system module to separate library
Diffstat (limited to 'build2')
-rw-r--r--build2/b.cxx5
-rw-r--r--build2/bash/init.cxx2
-rw-r--r--build2/buildfile4
-rw-r--r--build2/version/init.cxx392
-rw-r--r--build2/version/init.hxx31
-rw-r--r--build2/version/module.cxx15
-rw-r--r--build2/version/module.hxx64
-rw-r--r--build2/version/rule.cxx334
-rw-r--r--build2/version/rule.hxx52
-rw-r--r--build2/version/snapshot-git.cxx175
-rw-r--r--build2/version/snapshot.cxx39
-rw-r--r--build2/version/snapshot.hxx34
-rw-r--r--build2/version/utility.cxx81
-rw-r--r--build2/version/utility.hxx25
14 files changed, 5 insertions, 1248 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 907784b..a016278 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -52,7 +52,7 @@
#include <libbuild2/install/init.hxx>
#include <libbuild2/in/init.hxx>
-#include <build2/version/init.hxx>
+#include <libbuild2/version/init.hxx>
#include <build2/bin/init.hxx>
#include <build2/c/init.hxx>
@@ -449,8 +449,7 @@ main (int argc, char* argv[])
reg (&test::build2_test_load);
reg (&install::build2_install_load);
- bm["version"] = mf {"version", &version::boot, &version::init};
-
+ reg (&version::build2_version_load);
reg (&in::build2_in_load);
bm["bin.vars"] = mf {"bin.vars", nullptr, &bin::vars_init};
diff --git a/build2/bash/init.cxx b/build2/bash/init.cxx
index a23bc61..146e680 100644
--- a/build2/bash/init.cxx
+++ b/build2/bash/init.cxx
@@ -36,7 +36,7 @@ namespace build2
tracer trace ("bash::init");
l5 ([&]{trace << "for " << bs;});
- // Load in.base (in.* varibales, in{} target type).
+ // Load in.base (in.* variables, in{} target type).
//
if (!cast_false<bool> (rs["in.base.loaded"]))
load_module (rs, rs, "in.base", l);
diff --git a/build2/buildfile b/build2/buildfile
index a940793..ea39e87 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -8,9 +8,9 @@ import libs += libpkgconf%lib{pkgconf}
include ../libbuild2/
libs += ../libbuild2/lib{build2}
-for m: in
+for m: version in
{
- include ../libbuild2/in/
+ include ../libbuild2/$m/
libs += ../libbuild2/$m/lib{build2-$m}
}
diff --git a/build2/version/init.cxx b/build2/version/init.cxx
deleted file mode 100644
index 30f0f45..0000000
--- a/build2/version/init.cxx
+++ /dev/null
@@ -1,392 +0,0 @@
-// file : build2/version/init.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/version/init.hxx>
-
-#include <libbutl/manifest-parser.mxx>
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/context.hxx>
-#include <libbuild2/variable.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <libbuild2/config/utility.hxx>
-
-#include <libbuild2/dist/module.hxx>
-
-#include <build2/version/rule.hxx>
-#include <build2/version/module.hxx>
-#include <build2/version/utility.hxx>
-#include <build2/version/snapshot.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace version
- {
- static const path manifest_file ("manifest");
-
- static const in_rule in_rule_;
- static const manifest_install_rule manifest_install_rule_;
-
- bool
- boot (scope& rs, const location& l, unique_ptr<module_base>& mod)
- {
- tracer trace ("version::boot");
- l5 ([&]{trace << "for " << rs;});
-
- // Extract the version from the manifest file. As well as summary and
- // url while at it.
- //
- // Also, as a sanity check, verify the package name matches the build
- // system project name.
- //
- string sum;
- string url;
-
- standard_version v;
- dependencies ds;
- {
- path f (rs.src_path () / manifest_file);
-
- try
- {
- if (!file_exists (f))
- fail (l) << "no manifest file in " << rs.src_path ();
-
- ifdstream ifs (f);
- manifest_parser p (ifs, f.string ());
-
- manifest_name_value nv (p.next ());
- if (!nv.name.empty () || nv.value != "1")
- fail (l) << "unsupported manifest format in " << f;
-
- for (nv = p.next (); !nv.empty (); nv = p.next ())
- {
- if (nv.name == "name")
- {
- auto& pn (cast<project_name> (rs.vars[var_project]));
-
- if (nv.value != pn.string ())
- {
- path bf (rs.src_path () / rs.root_extra->bootstrap_file);
- location ml (&f, nv.value_line, nv.value_column);
- location bl (&bf);
-
- fail (ml) << "package name " << nv.value << " does not match "
- << "build system project name " << pn <<
- info (bl) << "build system project name specified here";
- }
- }
- if (nv.name == "summary")
- sum = move (nv.value);
- else if (nv.name == "url")
- url = move (nv.value);
- else if (nv.name == "version")
- {
- try
- {
- // Allow the package stub versions in the 0+<revision> form.
- // While not standard, we want to use the version module for
- // packaging stubs.
- //
- v = standard_version (nv.value, standard_version::allow_stub);
- }
- catch (const invalid_argument& e)
- {
- fail << "invalid standard version '" << nv.value << "': " << e;
- }
- }
- else if (nv.name == "depends")
- {
- // According to the package manifest spec, the format of the
- // 'depends' value is as follows:
- //
- // depends: [?][*] <alternatives> [; <comment>]
- //
- // <alternatives> := <dependency> [ '|' <dependency>]*
- // <dependency> := <name> [<constraint>]
- // <constraint> := <comparison> | <range>
- // <comparison> := ('==' | '>' | '<' | '>=' | '<=') <version>
- // <range> := ('(' | '[') <version> <version> (')' | ']')
- //
- // Note that we don't do exhaustive validation here leaving it
- // to the package manager.
- //
- string v (move (nv.value));
-
- size_t p;
-
- // Get rid of the comment.
- //
- if ((p = v.find (';')) != string::npos)
- v.resize (p);
-
- // Get rid of conditional/runtime markers. Note that enither of
- // them is valid in the rest of the value.
- //
- if ((p = v.find_last_of ("?*")) != string::npos)
- v.erase (0, p + 1);
-
- // Parse as |-separated "words".
- //
- for (size_t b (0), e (0); next_word (v, b, e, '|'); )
- {
- string d (v, b, e - b);
- trim (d);
-
- p = d.find_first_of (" \t=<>[(~^");
- string n (d, 0, p);
- string c (p != string::npos ? string (d, p) : string ());
-
- trim (n);
- trim (c);
-
- try
- {
- package_name pn (move (n));
- string v (pn.variable ());
-
- ds.emplace (move (v), dependency {move (pn), move (c)});
- }
- catch (const invalid_argument& e)
- {
- fail (l) << "invalid package name for dependency "
- << d << ": " << e;
- }
- }
- }
- }
- }
- catch (const manifest_parsing& e)
- {
- location l (&f, e.line, e.column);
- fail (l) << e.description;
- }
- catch (const io_error& e)
- {
- fail (l) << "unable to read from " << f << ": " << e;
- }
- catch (const system_error& e) // EACCES, etc.
- {
- fail (l) << "unable to access manifest " << f << ": " << e;
- }
-
- if (v.empty ())
- fail (l) << "no version in " << f;
- }
-
- // If this is the latest snapshot (i.e., the -a.1.z kind), then load the
- // snapshot number and id (e.g., commit date and id from git).
- //
- bool committed (true);
- bool rewritten (false);
- if (v.snapshot () && v.snapshot_sn == standard_version::latest_sn)
- {
- snapshot ss (extract_snapshot (rs));
-
- if (!ss.empty ())
- {
- v.snapshot_sn = ss.sn;
- v.snapshot_id = move (ss.id);
- committed = ss.committed;
- rewritten = true;
- }
- else
- committed = false;
- }
-
- // If there is a dependency on the build system itself, check it (so
- // there is no need for explicit using build@X.Y.Z).
- //
- {
- auto i (ds.find ("build2"));
-
- if (i != ds.end () && !i->second.constraint.empty ())
- try
- {
- check_build_version (
- standard_version_constraint (i->second.constraint, v), l);
- }
- catch (const invalid_argument& e)
- {
- fail (l) << "invalid version constraint for dependency build2 "
- << i->second.constraint << ": " << e;
- }
- }
-
- // Set all the version.* variables.
- //
- auto& vp (var_pool.rw (rs));
-
- auto set = [&vp, &rs] (const char* var, auto val)
- {
- using T = decltype (val);
- auto& v (vp.insert<T> (var, variable_visibility::project));
- rs.assign (v) = move (val);
- };
-
- if (!sum.empty ()) rs.assign (var_project_summary) = move (sum);
- if (!url.empty ()) rs.assign (var_project_url) = move (url);
-
- set ("version", v.string ()); // Project version (var_version).
-
- set ("version.project", v.string_project ());
- set ("version.project_number", v.version);
-
- // Enough of project version for unique identification (can be used in
- // places like soname, etc).
- //
- set ("version.project_id", v.string_project_id ());
-
- set ("version.stub", v.stub ()); // bool
-
- set ("version.epoch", uint64_t (v.epoch));
-
- set ("version.major", uint64_t (v.major ()));
- set ("version.minor", uint64_t (v.minor ()));
- set ("version.patch", uint64_t (v.patch ()));
-
- optional<uint16_t> a (v.alpha ());
- optional<uint16_t> b (v.beta ());
-
- set ("version.alpha", a.has_value ());
- set ("version.beta", b.has_value ());
- set ("version.pre_release", v.pre_release ().has_value ());
- set ("version.pre_release_string", v.string_pre_release ());
- set ("version.pre_release_number", uint64_t (a ? *a : b ? *b : 0));
-
- set ("version.snapshot", v.snapshot ()); // bool
- set ("version.snapshot_sn", v.snapshot_sn); // uint64
- set ("version.snapshot_id", v.snapshot_id); // string
- set ("version.snapshot_string", v.string_snapshot ());
- set ("version.snapshot_committed", committed); // bool
-
- set ("version.revision", uint64_t (v.revision));
-
- // Create the module.
- //
- mod.reset (new module (cast<project_name> (rs.vars[var_project]),
- move (v),
- committed,
- rewritten,
- move (ds)));
-
- return true; // Init first (dist.package, etc).
- }
-
- static void
- dist_callback (const path&, const scope&, void*);
-
- bool
- init (scope& rs,
- scope&,
- const location& l,
- unique_ptr<module_base>& mod,
- bool first,
- bool,
- const variable_map&)
- {
- tracer trace ("version::init");
-
- if (!first)
- fail (l) << "multiple version module initializations";
-
- // Load in.base (in.* varibales, in{} target type).
- //
- if (!cast_false<bool> (rs["in.base.loaded"]))
- load_module (rs, rs, "in.base", l);
-
- module& m (static_cast<module&> (*mod));
- const standard_version& v (m.version);
-
- // If the dist module is used, set its dist.package and register the
- // post-processing callback.
- //
- if (auto* dm = rs.lookup_module<dist::module> (dist::module::name))
- {
- // Make sure dist is init'ed, not just boot'ed.
- //
- if (!cast_false<bool> (rs["dist.loaded"]))
- load_module (rs, rs, "dist", l);
-
- m.dist_uncommitted = cast_false<bool> (rs["config.dist.uncommitted"]);
-
- // Don't touch if dist.package was set by the user.
- //
- value& val (rs.assign (dm->var_dist_package));
-
- if (!val)
- {
- string p (cast<project_name> (rs.vars[var_project]).string ());
- p += '-';
- p += v.string ();
- val = move (p);
-
- // Only register the post-processing callback if this is a rewritten
- // snapshot.
- //
- if (m.rewritten)
- dm->register_callback (dir_path (".") / manifest_file,
- &dist_callback,
- &m);
- }
- }
-
- // Register rules.
- //
- {
- auto& r (rs.rules);
-
- r.insert<file> (perform_update_id, "version.in", in_rule_);
- r.insert<file> (perform_clean_id, "version.in", in_rule_);
- r.insert<file> (configure_update_id, "version.in", in_rule_);
-
- if (cast_false<bool> (rs["install.booted"]))
- {
- r.insert<manifest> (
- perform_install_id, "version.manifest", manifest_install_rule_);
- }
- }
-
- return true;
- }
-
- static void
- dist_callback (const path& f, const scope& rs, void* data)
- {
- module& m (*static_cast<module*> (data));
-
- // Complain if this is an uncommitted snapshot.
- //
- if (!m.committed && !m.dist_uncommitted)
- fail << "distribution of uncommitted project " << rs.src_path () <<
- info << "specify config.dist.uncommitted=true to force";
-
- // The plan is simple: fixing up the version in a temporary file then
- // move it to the original.
- //
- try
- {
- auto_rmfile t (fixup_manifest (f,
- path::temp_path ("manifest"),
- m.version));
-
- mvfile (t.path, f, (cpflags::overwrite_content |
- cpflags::overwrite_permissions));
- t.cancel ();
- }
- catch (const io_error& e)
- {
- fail << "unable to overwrite " << f << ": " << e;
- }
- catch (const system_error& e) // EACCES, etc.
- {
- fail << "unable to overwrite " << f << ": " << e;
- }
- }
- }
-}
diff --git a/build2/version/init.hxx b/build2/version/init.hxx
deleted file mode 100644
index ef3688a..0000000
--- a/build2/version/init.hxx
+++ /dev/null
@@ -1,31 +0,0 @@
-// file : build2/version/init.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_VERSION_INIT_HXX
-#define BUILD2_VERSION_INIT_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/module.hxx>
-
-namespace build2
-{
- namespace version
- {
- bool
- boot (scope&, const location&, unique_ptr<module_base>&);
-
- bool
- init (scope&,
- scope&,
- const location&,
- unique_ptr<module_base>&,
- bool,
- bool,
- const variable_map&);
- }
-}
-
-#endif // BUILD2_VERSION_INIT_HXX
diff --git a/build2/version/module.cxx b/build2/version/module.cxx
deleted file mode 100644
index 1811cfc..0000000
--- a/build2/version/module.cxx
+++ /dev/null
@@ -1,15 +0,0 @@
-// file : build2/version/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/version/module.hxx>
-
-using namespace std;
-
-namespace build2
-{
- namespace version
- {
- const string module::name ("version");
- }
-}
diff --git a/build2/version/module.hxx b/build2/version/module.hxx
deleted file mode 100644
index d2b681c..0000000
--- a/build2/version/module.hxx
+++ /dev/null
@@ -1,64 +0,0 @@
-// file : build2/version/module.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_VERSION_MODULE_HXX
-#define BUILD2_VERSION_MODULE_HXX
-
-#include <map>
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/module.hxx>
-
-namespace build2
-{
- namespace version
- {
- // A map of package names sanitized for use in variable names to the
- // 'depends' values from manifest.
- //
- using package_name = project_name;
-
- struct dependency
- {
- package_name name;
- string constraint;
- };
-
- using dependencies = std::map<string, dependency>;
-
- struct module: module_base
- {
- using dependencies_type = version::dependencies;
-
- static const string name;
-
- // The project variable value sanitized for use in variable names.
- //
- const string project;
-
- butl::standard_version version;
- bool committed; // Whether this is a committed snapshot.
- bool rewritten; // Whether this is a rewritten .z snapshot.
-
- dependencies_type dependencies;
-
- bool dist_uncommitted = false;
-
- module (const project_name& p,
- butl::standard_version v,
- bool c,
- bool r,
- dependencies_type d)
- : project (p.variable ()),
- version (move (v)),
- committed (c),
- rewritten (r),
- dependencies (move (d)) {}
- };
- }
-}
-
-#endif // BUILD2_VERSION_MODULE_HXX
diff --git a/build2/version/rule.cxx b/build2/version/rule.cxx
deleted file mode 100644
index f110e3e..0000000
--- a/build2/version/rule.cxx
+++ /dev/null
@@ -1,334 +0,0 @@
-// file : build2/version/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/version/rule.hxx>
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/target.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <libbuild2/in/target.hxx>
-
-#include <build2/version/module.hxx>
-#include <build2/version/utility.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace version
- {
- using in::in;
-
- // Return true if this prerequisite is a project's manifest file. To be
- // sure we would need to search it into target but that we can't do in
- // match().
- //
- static inline bool
- manifest_prerequisite (const scope& rs, const prerequisite_member& p)
- {
- if (!p.is_a<manifest> () || p.name () != "manifest")
- return false;
-
- const scope& s (p.scope ());
-
- if (s.root_scope () == nullptr) // Out of project prerequisite.
- return false;
-
- dir_path d (p.dir ());
- if (d.relative ())
- d = s.src_path () / d;
- d.normalize ();
-
- return d == rs.src_path ();
- }
-
- // in_rule
- //
- bool in_rule::
- match (action a, target& xt, const string&) const
- {
- tracer trace ("version::in_rule::match");
-
- file& t (static_cast<file&> (xt));
- const scope& rs (t.root_scope ());
-
- bool fm (false); // Found manifest.
- bool fi (false); // Found in.
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- if (include (a, t, p) != include_type::normal) // Excluded/ad hoc.
- continue;
-
- fm = fm || manifest_prerequisite (rs, p);
- fi = fi || p.is_a<in> ();
- }
-
- // Note that while normally we print these at verbosity level 4, these
- // ones get quite noisy since we try this rule any file target.
- //
- if (!fm)
- l5 ([&]{trace << "no manifest prerequisite for target " << t;});
-
- if (!fi)
- l5 ([&]{trace << "no in file prerequisite for target " << t;});
-
- bool r (fm && fi);
-
- // If we match, lookup and cache the module for the update operation.
- //
- if (r && a == perform_update_id)
- t.data (rs.lookup_module<module> (module::name));
-
- return r;
- }
-
- string in_rule::
- lookup (const location& l,
- action a,
- const target& t,
- const string& n) const
- {
- // Note that this code will be executed during up-to-date check for each
- // substitution so let's try not to do anything overly sub-optimal here.
- //
- const module& m (*t.data<const module*> ());
-
- // Split it into the package name and the variable/condition name.
- //
- // We used to bail if there is no package component but now we treat it
- // the same as project. This can be useful when trying to reuse existing
- // .in files (e.g., from autoconf, etc).
- //
- size_t p (n.find ('.'));
-
- if (p == string::npos || n.compare (0, p, m.project) == 0)
- {
- return rule::lookup (l, // Standard lookup.
- a,
- t,
- p == string::npos ? n : string (n, p + 1));
- }
-
- string pn (n, 0, p);
- string vn (n, p + 1);
-
- // Perform substitutions for a dependency. Here we recognize the
- // following substitutions:
- //
- // $libfoo.version$ - textual version constraint.
- // $libfoo.condition(VER[,SNAP])$ - numeric satisfaction condition.
- // $libfoo.check(VER[,SNAP])$ - numeric satisfaction check (#if ...).
- //
- // Where VER is the version number macro and SNAP is the optional
- // snapshot number macro (only needed if you plan to include snapshot
- // informaton in your constraints).
- //
- // Note also that the last two (condition and check) can only be used in
- // the strict substitution mode since in::rule::substitute() will skip
- // them in the lax mode.
-
- // For now we re-parse the constraint every time. Firstly because not
- // all of them are necessarily in the standard form and secondly because
- // of the MT-safety.
- //
- standard_version_constraint dc;
- const package_name* dn;
- {
- auto i (m.dependencies.find (pn));
-
- if (i == m.dependencies.end ())
- fail (l) << "unknown dependency '" << pn << "'";
-
- const dependency& dp (i->second);
-
- if (dp.constraint.empty ())
- fail (l) << "no version constraint for dependency " << dp.name;
-
- try
- {
- dc = standard_version_constraint (dp.constraint, m.version);
- }
- catch (const invalid_argument& e)
- {
- fail (l) << "invalid version constraint for dependency " << dp.name
- << " " << dp.constraint << ": " << e;
- }
-
- dn = &dp.name;
- }
-
- // Now substitute.
- //
- size_t i;
- if (vn == "version")
- {
- return dc.string (); // Use normalized representation.
- }
- if (vn.compare (0, (i = 6), "check(") == 0 ||
- vn.compare (0, (i = 10), "condition(") == 0)
- {
- size_t j (vn.find_first_of (",)", i));
-
- if (j == string::npos || (vn[j] == ',' && vn.back () != ')'))
- fail (l) << "missing closing ')'";
-
- string vm (vn, i, j - i); // VER macro.
- string sm (vn[j] == ',' // SNAP macro.
- ? string (vn, j + 1, vn.size () - j - 2)
- : string ());
-
- trim (vm);
- trim (sm);
-
- auto cond = [&l, &dc, &vm, &sm] () -> string
- {
- auto& miv (dc.min_version);
- auto& mav (dc.max_version);
-
- bool mio (dc.min_open);
- bool mao (dc.max_open);
-
- if (sm.empty () &&
- ((miv && miv->snapshot ()) ||
- (mav && mav->snapshot ())))
- fail (l) << "snapshot macro required for " << dc.string ();
-
- auto cmp = [] (const string& m, const char* o, uint64_t v)
- {
- return m + o + to_string (v) + "ULL";
- };
-
- // Note that version orders everything among pre-releases (that E
- // being 0/1). So the snapshot comparison is only necessary "inside"
- // the same pre-release.
- //
- auto max_cmp = [&vm, &sm, mao, &mav, &cmp] (bool p = false)
- {
- string r;
-
- if (mav->snapshot ())
- {
- r += (p ? "(" : "");
-
- r += cmp (vm, " < ", mav->version) + " || (";
- r += cmp (vm, " == ", mav->version) + " && ";
- r += cmp (sm, (mao ? " < " : " <= "), mav->snapshot_sn) + ")";
-
- r += (p ? ")" : "");
- }
- else
- r = cmp (vm, (mao ? " < " : " <= "), mav->version);
-
- return r;
- };
-
- auto min_cmp = [&vm, &sm, mio, &miv, &cmp] (bool p = false)
- {
- string r;
-
- if (miv->snapshot ())
- {
- r += (p ? "(" : "");
-
- r += cmp (vm, " > ", miv->version) + " || (";
- r += cmp (vm, " == ", miv->version) + " && ";
- r += cmp (sm, (mio ? " > " : " >= "), miv->snapshot_sn) + ")";
-
- r += (p ? ")" : "");
- }
- else
- r = cmp (vm, (mio ? " > " : " >= "), miv->version);
-
- return r;
- };
-
- // < / <=
- //
- if (!miv)
- return max_cmp ();
-
- // > / >=
- //
- if (!mav)
- return min_cmp ();
-
- // ==
- //
- if (*miv == *mav)
- {
- string r (cmp (vm, " == ", miv->version));
-
- if (miv->snapshot ())
- r += " && " + cmp (sm, " == ", miv->snapshot_sn);
-
- return r;
- }
-
- // range
- //
- return min_cmp (true) + " && " + max_cmp (true);
- };
-
- if (vn[1] == 'o') // condition
- return cond ();
-
- string r;
-
- // This is tricky: if the version header hasn't been generated yet,
- // then the check will fail. Maybe a better solution is to disable
- // diagnostics and ignore (some) errors during dependency extraction.
- //
- r += "#ifdef " + vm + "\n";
- r += "# if !(" + cond () + ")\n";
- r += "# error incompatible " + dn->string () + " version, ";
- r += dn->string () + ' ' + dc.string () + " is required\n";
- r += "# endif\n";
- r += "#endif";
-
- return r;
- }
- else
- fail (l) << "unknown dependency substitution '" << vn << "'" << endf;
- }
-
- // manifest_install_rule
- //
- bool manifest_install_rule::
- match (action a, target& t, const string&) const
- {
- // We only match project's manifest.
- //
- if (!t.is_a<manifest> () || t.name != "manifest")
- return false;
-
- // Must be in project's src_root.
- //
- const scope& s (t.base_scope ());
- if (s.root_scope () != &s || s.src_path () != t.dir)
- return false;
-
- return file_rule::match (a, t, "");
- }
-
- auto_rmfile manifest_install_rule::
- install_pre (const file& t, const install_dir&) const
- {
- const path& p (t.path ());
-
- const scope& rs (t.root_scope ());
- const module& m (*rs.lookup_module<module> (module::name));
-
- if (!m.rewritten)
- return auto_rmfile (p, false /* active */);
-
- // Our options are to use path::temp_path() or to create a .t file in
- // the out tree. Somehow the latter feels more appropriate (even though
- // if we crash in between, we won't clean it up).
- //
- return fixup_manifest (p, rs.out_path () / "manifest.t", m.version);
- }
- }
-}
diff --git a/build2/version/rule.hxx b/build2/version/rule.hxx
deleted file mode 100644
index 7bfb783..0000000
--- a/build2/version/rule.hxx
+++ /dev/null
@@ -1,52 +0,0 @@
-// file : build2/version/rule.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_VERSION_RULE_HXX
-#define BUILD2_VERSION_RULE_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/install/rule.hxx>
-
-#include <libbuild2/in/rule.hxx>
-
-namespace build2
-{
- namespace version
- {
- // Preprocess an .in file that depends on manifest.
- //
- class in_rule: public in::rule
- {
- public:
- in_rule (): rule ("version.in 2", "version.in") {}
-
- virtual bool
- match (action, target&, const string&) const override;
-
- virtual string
- lookup (const location&,
- action,
- const target&,
- const string&) const override;
- };
-
- // Pre-process manifest before installation to patch in the version.
- //
- class manifest_install_rule: public install::file_rule
- {
- public:
- manifest_install_rule () {}
-
- virtual bool
- match (action, target&, const string&) const override;
-
- virtual auto_rmfile
- install_pre (const file&, const install_dir&) const override;
- };
- }
-}
-
-#endif // BUILD2_VERSION_RULE_HXX
diff --git a/build2/version/snapshot-git.cxx b/build2/version/snapshot-git.cxx
deleted file mode 100644
index f7993d2..0000000
--- a/build2/version/snapshot-git.cxx
+++ /dev/null
@@ -1,175 +0,0 @@
-// file : build2/version/snapshot-git.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <ctime> // time_t
-
-#include <libbutl/sha1.mxx>
-
-#include <build2/version/snapshot.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace version
- {
- snapshot
- extract_snapshot_git (const dir_path& src_root)
- {
- snapshot r;
- const char* d (src_root.string ().c_str ());
-
- // First check whether the working directory is clean. There doesn't
- // seem to be a way to do everything in a single invocation (the
- // porcelain v2 gives us the commit id but not timestamp).
- //
-
- // If git status --porcelain returns anything, then the working
- // directory is not clean.
- //
- {
- const char* args[] {"git", "-C", d, "status", "--porcelain", nullptr};
- r.committed = run<string> (
- 3 /* verbosity */,
- args,
- [](string& s, bool) {return move (s);}).empty ();
- }
-
- // Now extract the commit id and date. One might think that would be
- // easy... Commit id is a SHA1 hash of the commit object. And commit
- // object looks like this:
- //
- // commit <len>\0
- // <data>
- //
- // Where <len> is the size of <data> and <data> is the output of:
- //
- // git cat-file commit HEAD
- //
- // There is also one annoying special case: new repository without any
- // commits. In this case the above command will fail (with diagnostics
- // and non-zero exit code) because there is no HEAD. Of course, it can
- // also fail for other reason (like broken repository) which would be
- // hard to distinguish. Note, however, that we just ran git status and
- // it would have most likely failed if this were the case. So here we
- // (reluctantly) assume that the only reason git cat-file fails is if
- // there is no HEAD (that we equal with the "new repository" condition
- // which is, strictly speaking, might not be the case either). So we
- // suppress any diagnostics, and handle non-zero exit code.
- //
- string data;
-
- const char* args[] {
- "git", "-C", d, "cat-file", "commit", "HEAD", nullptr};
- process pr (run_start (3 /* verbosity */,
- args,
- 0 /* stdin */,
- -1 /* stdout */,
- false /* error */));
-
- string l;
- try
- {
- ifdstream is (move (pr.in_ofd), ifdstream::badbit);
-
- while (!eof (getline (is, l)))
- {
- data += l;
- data += '\n'; // We assume there is always a newline.
-
- if (r.sn == 0 && l.compare (0, 10, "committer ") == 0)
- try
- {
- // The line format is:
- //
- // committer <noise> <timestamp> <timezone>
- //
- // For example:
- //
- // committer John Doe <john@example.org> 1493117819 +0200
- //
- // The timestamp is in seconds since UNIX epoch. The timezone
- // appears to be always numeric (+0000 for UTC). Note that
- // timestamp appears to be already in UTC with timezone being just
- // for information it seems.
- //
- size_t p1 (l.rfind (' ')); // Can't be npos.
-
- size_t p2 (l.rfind (' ', p1 - 1));
- if (p2 == string::npos)
- throw invalid_argument ("missing timestamp");
-
- string ts (l, p2 + 1, p1 - p2 - 1);
- time_t t (static_cast<time_t> (stoull (ts)));
-
-#if 0
- string tz (l, p1 + 1);
-
- if (tz.size () != 5)
- throw invalid_argument ("invalid timezone");
-
- unsigned long h (stoul (string (tz, 1, 2)));
- unsigned long m (stoul (string (tz, 3, 2)));
- unsigned long s (h * 3600 + m * 60);
-
- // The timezone indicates where the timestamp was generated so to
- // convert to UTC we need to invert the sign.
- //
- switch (tz[0])
- {
- case '+': t -= s; break;
- case '-': t += s; break;
- default: throw invalid_argument ("invalid timezone sign");
- }
-#endif
- // Represent as YYYYMMDDhhmmss.
- //
- r.sn = stoull (to_string (system_clock::from_time_t (t),
- "%Y%m%d%H%M%S",
- false /* special */,
- false /* local (already in UTC) */));
- }
- catch (const invalid_argument& e)
- {
- fail << "unable to extract git commit date from '" << l << "': "
- << e;
- }
- }
-
- is.close ();
- }
- catch (const io_error&)
- {
- // Presumably the child process failed. Let run_finish() deal with
- // that.
- }
-
- if (!run_finish (args, pr, false /* error */, l))
- {
- // Presumably new repository without HEAD. Return uncommitted snapshot
- // with UNIX epoch as timestamp.
- //
- r.sn = 19700101000000ULL;
- r.committed = false;
- return r;
- }
-
- if (r.sn == 0)
- fail << "unable to extract git commit id/date for " << src_root;
-
- if (r.committed)
- {
- sha1 cs;
- cs.append ("commit " + to_string (data.size ())); // Includes '\0'.
- cs.append (data.c_str (), data.size ());
- r.id.assign (cs.string (), 12); // 12-characters abbreviated commit id.
- }
- else
- r.sn++; // Add a second.
-
- return r;
- }
- }
-}
diff --git a/build2/version/snapshot.cxx b/build2/version/snapshot.cxx
deleted file mode 100644
index b43e083..0000000
--- a/build2/version/snapshot.cxx
+++ /dev/null
@@ -1,39 +0,0 @@
-// file : build2/version/snapshot.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/version/snapshot.hxx>
-
-#include <libbuild2/filesystem.hxx>
-
-using namespace std;
-
-namespace build2
-{
- namespace version
- {
- snapshot
- extract_snapshot_git (const dir_path&);
-
- static const path git (".git");
-
- snapshot
- extract_snapshot (const scope& rs)
- {
- // Ignore errors when checking for existence since we may be iterating
- // over directories past any reasonable project boundaries.
- //
- for (dir_path d (rs.src_path ()); !d.empty (); d = d.directory ())
- {
- // .git can be either a directory or a file in case of a submodule.
- //
- if (butl::entry_exists (d / git,
- true /* follow_symlinks */,
- true /* ignore_errors */))
- return extract_snapshot_git (d);
- }
-
- return snapshot ();
- }
- }
-}
diff --git a/build2/version/snapshot.hxx b/build2/version/snapshot.hxx
deleted file mode 100644
index 824ec89..0000000
--- a/build2/version/snapshot.hxx
+++ /dev/null
@@ -1,34 +0,0 @@
-// file : build2/version/snapshot.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_VERSION_SNAPSHOT_HXX
-#define BUILD2_VERSION_SNAPSHOT_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/scope.hxx>
-
-namespace build2
-{
- namespace version
- {
- struct snapshot
- {
- uint64_t sn = 0;
- string id;
- bool committed = false;
-
- bool
- empty () const {return sn == 0;}
- };
-
- // Return empty snapshot if unknown scm or uncommitted.
- //
- snapshot
- extract_snapshot (const scope& rs);
- }
-}
-
-#endif // BUILD2_VERSION_SNAPSHOT_HXX
diff --git a/build2/version/utility.cxx b/build2/version/utility.cxx
deleted file mode 100644
index 8286ff8..0000000
--- a/build2/version/utility.cxx
+++ /dev/null
@@ -1,81 +0,0 @@
-// file : build2/version/utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/version/utility.hxx>
-
-#include <libbutl/manifest-parser.mxx>
-#include <libbutl/manifest-serializer.mxx>
-
-#include <libbuild2/diagnostics.hxx>
-
-using namespace butl;
-
-namespace build2
-{
- namespace version
- {
- auto_rmfile
- fixup_manifest (const path& in, path out, const standard_version& v)
- {
- auto_rmfile r (move (out), !dry_run /* active */);
-
- if (!dry_run)
- {
- try
- {
- permissions perm (path_permissions (in));
-
- ifdstream ifs (in);
- manifest_parser p (ifs, in.string ());
-
- auto_fd ofd (fdopen (r.path,
- fdopen_mode::out |
- fdopen_mode::create |
- fdopen_mode::exclusive |
- fdopen_mode::binary,
- perm));
-
- ofdstream ofs (move (ofd));
- manifest_serializer s (ofs, r.path.string ());
-
- manifest_name_value nv (p.next ());
- assert (nv.name.empty () && nv.value == "1"); // We just loaded it.
- s.next (nv.name, nv.value);
-
- for (nv = p.next (); !nv.empty (); nv = p.next ())
- {
- if (nv.name == "version")
- nv.value = v.string ();
-
- s.next (nv.name, nv.value);
- }
-
- s.next (nv.name, nv.value); // End of manifest.
- s.next (nv.name, nv.value); // End of stream.
-
- ofs.close ();
- ifs.close ();
- }
- catch (const manifest_parsing& e)
- {
- location l (&in, e.line, e.column);
- fail (l) << e.description;
- }
- catch (const manifest_serialization& e)
- {
- location l (&r.path);
- fail (l) << e.description;
- }
- catch (const io_error& e)
- {
- fail << "io error: " << e <<
- info << "while reading " << in <<
- info << "while writing " << r.path;
- }
- }
-
- return r;
- }
- }
-}
diff --git a/build2/version/utility.hxx b/build2/version/utility.hxx
deleted file mode 100644
index 83bb91c..0000000
--- a/build2/version/utility.hxx
+++ /dev/null
@@ -1,25 +0,0 @@
-// file : build2/version/utility.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_VERSION_UTILITY_HXX
-#define BUILD2_VERSION_UTILITY_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/filesystem.hxx>
-
-namespace build2
-{
- namespace version
- {
- // Re-serialize the manifest fixing up the version. Note that this will
- // not preserve comments. Probably acceptable for snapshots.
- //
- auto_rmfile
- fixup_manifest (const path& in, path out, const standard_version&);
- }
-}
-
-#endif // BUILD2_VERSION_UTILITY_HXX