From da9cbf29c403d27c2940f9b31199c4648f8ae4a1 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 1 Aug 2019 16:10:48 +0300 Subject: Move version build system module to separate library --- build2/b.cxx | 5 +- build2/bash/init.cxx | 2 +- build2/buildfile | 4 +- build2/version/init.cxx | 392 ---------------------------------------- build2/version/init.hxx | 31 ---- build2/version/module.cxx | 15 -- build2/version/module.hxx | 64 ------- build2/version/rule.cxx | 334 ---------------------------------- build2/version/rule.hxx | 52 ------ build2/version/snapshot-git.cxx | 175 ------------------ build2/version/snapshot.cxx | 39 ---- build2/version/snapshot.hxx | 34 ---- build2/version/utility.cxx | 81 --------- build2/version/utility.hxx | 25 --- 14 files changed, 5 insertions(+), 1248 deletions(-) delete mode 100644 build2/version/init.cxx delete mode 100644 build2/version/init.hxx delete mode 100644 build2/version/module.cxx delete mode 100644 build2/version/module.hxx delete mode 100644 build2/version/rule.cxx delete mode 100644 build2/version/rule.hxx delete mode 100644 build2/version/snapshot-git.cxx delete mode 100644 build2/version/snapshot.cxx delete mode 100644 build2/version/snapshot.hxx delete mode 100644 build2/version/utility.cxx delete mode 100644 build2/version/utility.hxx (limited to 'build2') 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 #include -#include +#include #include #include @@ -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 (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 - -#include - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include - -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& 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 (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+ 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: [?][*] [; ] - // - // := [ '|' ]* - // := [] - // := | - // := ('==' | '>' | '<' | '>=' | '<=') - // := ('(' | '[') (')' | ']') - // - // 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 (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 a (v.alpha ()); - optional 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 (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& 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 (rs["in.base.loaded"])) - load_module (rs, rs, "in.base", l); - - module& m (static_cast (*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::name)) - { - // Make sure dist is init'ed, not just boot'ed. - // - if (!cast_false (rs["dist.loaded"])) - load_module (rs, rs, "dist", l); - - m.dist_uncommitted = cast_false (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 (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 (perform_update_id, "version.in", in_rule_); - r.insert (perform_clean_id, "version.in", in_rule_); - r.insert (configure_update_id, "version.in", in_rule_); - - if (cast_false (rs["install.booted"])) - { - r.insert ( - 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 (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 -#include - -#include - -namespace build2 -{ - namespace version - { - bool - boot (scope&, const location&, unique_ptr&); - - bool - init (scope&, - scope&, - const location&, - unique_ptr&, - 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 - -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 - -#include -#include - -#include - -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; - - 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 - -#include -#include -#include - -#include - -#include -#include - -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 () || 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 (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 (); - } - - // 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::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 ()); - - // 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 () || 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::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 -#include - -#include - -#include - -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 // time_t - -#include - -#include - -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 ( - 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 \0 - // - // - // Where is the size of and 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 - // - // For example: - // - // committer John Doe 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 (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 - -#include - -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 -#include - -#include - -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 - -#include -#include - -#include - -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 -#include - -#include - -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 -- cgit v1.1