diff options
Diffstat (limited to 'libbuild2/version')
-rw-r--r-- | libbuild2/version/init.cxx | 131 | ||||
-rw-r--r-- | libbuild2/version/module.hxx | 1 | ||||
-rw-r--r-- | libbuild2/version/rule.cxx | 71 | ||||
-rw-r--r-- | libbuild2/version/rule.hxx | 11 | ||||
-rw-r--r-- | libbuild2/version/snapshot-git.cxx | 19 | ||||
-rw-r--r-- | libbuild2/version/snapshot.cxx | 4 |
6 files changed, 162 insertions, 75 deletions
diff --git a/libbuild2/version/init.cxx b/libbuild2/version/init.cxx index 05d5fe0..b3657bc 100644 --- a/libbuild2/version/init.cxx +++ b/libbuild2/version/init.cxx @@ -3,6 +3,8 @@ #include <libbuild2/version/init.hxx> +#include <cstring> // strchr() + #include <libbutl/manifest-parser.hxx> #include <libbuild2/scope.hxx> @@ -143,61 +145,98 @@ namespace build2 } 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; + // Parse the dependency and add it to the map (see + // bpkg::dependency_alternatives class for dependency syntax). + // + // Note that currently we only consider simple dependencies: + // singe package without alternatives, clauses, or newlines. + // In the future, if/when we add full support, we will likely + // keep this as a fast path. + // + // Also note that we don't do exhaustive validation here leaving + // it to the package manager. // Get rid of the comment. // + // Note that we can potentially mis-detect the comment + // separator, since ';' can be a part of some of the dependency + // alternative clauses. If that's the case, we will skip the + // dependency later. + // + size_t p; 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. + // Skip the dependency if it is not a simple one. + // + // Note that we will check for the presence of the reflect + // clause later since `=` can also be in the constraint. + // + if (v.find_first_of ("{?|\n") != string::npos) + continue; + + // Find the beginning of the dependency package name, skipping + // the build-time marker, if present. // - if ((p = v.find_last_of ("?*")) != string::npos) - v.erase (0, p + 1); + bool buildtime (v[0] == '*'); + size_t b (buildtime ? v.find_first_not_of (" \t", 1) : 0); - // Parse as |-separated "words". + if (b == string::npos) + fail (l) << "invalid dependency " << v << ": no package name"; + + // Find the end of the dependency package name. + // + p = v.find_first_of (" \t=<>[(~^", b); + + // Dependency name (without leading/trailing white-spaces). // - for (size_t b (0), e (0); next_word (v, b, e, '|'); ) + string n (v, b, p == string::npos ? p : p - b); + + string vc; // Empty if no constraint is specified + + // Position to the first non-whitespace character after the + // dependency name, which, if present, can be a part of the + // version constraint or the reflect clause. + // + if (p != string::npos) + p = v.find_first_not_of (" \t", p); + + if (p != string::npos) + { + // Check if this is definitely not a version constraint and + // drop this dependency if that's the case. + // + if (strchr ("=<>[(~^", v[p]) == nullptr) + continue; + + // Ok, we have a constraint, check that there is no reflect + // clause after it (the only other valid `=` in a constraint + // is in the immediately following character as part of + // `==`, `<=`, or `>=`). + // + if (v.size () > p + 2 && v.find ('=', p + 2) != string::npos) + continue; + + vc.assign (v, p, string::npos); + trim (vc); + } + + // Finally, add the dependency to the map. + // + try + { + package_name pn (move (n)); + string v (pn.variable ()); + + ds.emplace (move (v), + dependency {move (pn), move (vc), buildtime}); + } + catch (const invalid_argument& 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; - } + fail (l) << "invalid dependency package name '" << n << "': " + << e; } } } @@ -246,7 +285,9 @@ namespace build2 { auto i (ds.find ("build2")); - if (i != ds.end () && !i->second.constraint.empty ()) + if (i != ds.end () && + i->second.buildtime && + !i->second.constraint.empty ()) try { check_build_version ( @@ -349,7 +390,7 @@ namespace build2 if (cast_false<bool> (rs["install.booted"])) { rs.insert_rule<manifest> ( - perform_install_id, "version.manifest", manifest_install_rule_); + perform_install_id, "version.install", manifest_install_rule_); } return true; diff --git a/libbuild2/version/module.hxx b/libbuild2/version/module.hxx index e80870e..8549e03 100644 --- a/libbuild2/version/module.hxx +++ b/libbuild2/version/module.hxx @@ -22,6 +22,7 @@ namespace build2 { package_name name; string constraint; + bool buildtime; }; using dependencies = map<string, dependency>; diff --git a/libbuild2/version/rule.cxx b/libbuild2/version/rule.cxx index 919dfcf..65c1117 100644 --- a/libbuild2/version/rule.cxx +++ b/libbuild2/version/rule.cxx @@ -46,12 +46,31 @@ namespace build2 // in_rule // + + // Wrap the in::rule's perform_update recipe into a data-carrying recipe. + // + // To optimize this a bit further (i.e., to avoid the dynamic memory + // allocation) we are going to call in::rule::perform_update() directly + // (after all it's virtual and thus part of the in_rule's interface). + // + struct match_data + { + const module& mod; + const in_rule& rule; + + target_state + operator() (action a, const target& t) + { + return rule.perform_update (a, t); + } + }; + bool in_rule:: - match (action a, target& xt, const string&) const + match (action a, target& xt) const { tracer trace ("version::in_rule::match"); - file& t (static_cast<file&> (xt)); + file& t (xt.as<file> ()); const scope& rs (t.root_scope ()); bool fm (false); // Found manifest. @@ -74,14 +93,26 @@ namespace build2 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 we match, derive the file name early as recommended by the in + // rule. // - if (r && a == perform_update_id) - t.data (rs.find_module<module> (module::name)); + if (fm && fi) + t.derive_path (); - return r; + return fm && fi; + } + + recipe in_rule:: + apply (action a, target& t) const + { + recipe r (rule::apply (a, t)); + + // Lookup and cache the module for the update operation. + // + return a == perform_update_id + ? match_data {*t.root_scope ().find_module<module> (module::name), + *this} + : move (r); } string in_rule:: @@ -89,12 +120,16 @@ namespace build2 action a, const target& t, const string& n, + optional<uint64_t> flags, + const substitution_map* smap, const optional<string>& null) const { + assert (!flags); + // 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*> ()); + const module& m (t.data<match_data> (a).mod); // Split it into the package name and the variable/condition name. // @@ -110,7 +145,7 @@ namespace build2 a, t, p == string::npos ? n : string (n, p + 1), - null); + nullopt, smap, null); } string pn (n, 0, p); @@ -212,13 +247,13 @@ namespace build2 if (mav->snapshot ()) { - r += (p ? "(" : ""); + if (p) r += '('; r += cmp (vm, " < ", mav->version) + " || ("; r += cmp (vm, " == ", mav->version) + " && "; - r += cmp (sm, (mao ? " < " : " <= "), mav->snapshot_sn) + ")"; + r += cmp (sm, (mao ? " < " : " <= "), mav->snapshot_sn) + ')'; - r += (p ? ")" : ""); + if (p) r += ')'; } else r = cmp (vm, (mao ? " < " : " <= "), mav->version); @@ -232,13 +267,13 @@ namespace build2 if (miv->snapshot ()) { - r += (p ? "(" : ""); + if (p) r += '('; r += cmp (vm, " > ", miv->version) + " || ("; r += cmp (vm, " == ", miv->version) + " && "; - r += cmp (sm, (mio ? " > " : " >= "), miv->snapshot_sn) + ")"; + r += cmp (sm, (mio ? " > " : " >= "), miv->snapshot_sn) + ')'; - r += (p ? ")" : ""); + if (p) r += ')'; } else r = cmp (vm, (mio ? " > " : " >= "), miv->version); @@ -298,7 +333,7 @@ namespace build2 // manifest_install_rule // bool manifest_install_rule:: - match (action a, target& t, const string&) const + match (action a, target& t) const { // We only match project's manifest. // @@ -311,7 +346,7 @@ namespace build2 if (s.root_scope () != &s || s.src_path () != t.dir) return false; - return file_rule::match (a, t, ""); + return file_rule::match (a, t); } auto_rmfile manifest_install_rule:: diff --git a/libbuild2/version/rule.hxx b/libbuild2/version/rule.hxx index ddc5e11..0bdc090 100644 --- a/libbuild2/version/rule.hxx +++ b/libbuild2/version/rule.hxx @@ -20,16 +20,21 @@ namespace build2 class in_rule: public in::rule { public: - in_rule (): rule ("version.in 2", "version.in") {} + in_rule (): rule ("version.in 2", "version") {} virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; + + virtual recipe + apply (action, target&) const override; virtual string lookup (const location&, action, const target&, const string&, + optional<uint64_t>, + const substitution_map*, const optional<string>&) const override; }; @@ -41,7 +46,7 @@ namespace build2 manifest_install_rule () {} virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual auto_rmfile install_pre (const file&, const install_dir&) const override; diff --git a/libbuild2/version/snapshot-git.cxx b/libbuild2/version/snapshot-git.cxx index 2ae3f5b..ab0224a 100644 --- a/libbuild2/version/snapshot-git.cxx +++ b/libbuild2/version/snapshot-git.cxx @@ -21,7 +21,7 @@ namespace build2 static global_cache<snapshot, dir_path> cache; snapshot - extract_snapshot_git (dir_path rep_root) + extract_snapshot_git (context& ctx, dir_path rep_root) { if (const snapshot* r = cache.find (rep_root)) return *r; @@ -82,7 +82,11 @@ namespace build2 args[args_i + 1] = "--porcelain"; args[args_i + 2] = nullptr; + // @@ PERF: redo with custom stream reading code (then could also + // get rid of context). + // r.committed = run<string> ( + ctx, 3 /* verbosity */, pp, args, @@ -108,7 +112,8 @@ namespace build2 // (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. + // suppress any diagnostics, and handle non-zero exit code (and so no + // diagnostics buffering is needed, plus we are in the load phase). // string data; @@ -117,12 +122,12 @@ namespace build2 args[args_i + 2] = "HEAD"; args[args_i + 3] = nullptr; - process pr (run_start (3 /* verbosity */, + process pr (run_start (3 /* verbosity */, pp, args, - 0 /* stdin */, - -1 /* stdout */, - false /* error */)); + 0 /* stdin */, + -1 /* stdout */, + 1 /* stderr (to stdout) */)); string l; try @@ -201,7 +206,7 @@ namespace build2 // that. } - if (run_finish_code (args, pr, l)) + if (run_finish_code (args, pr, l, 2 /* verbosity */)) { if (r.sn == 0) fail << "unable to extract git commit id/date for " << rep_root; diff --git a/libbuild2/version/snapshot.cxx b/libbuild2/version/snapshot.cxx index d20e633..000bcba 100644 --- a/libbuild2/version/snapshot.cxx +++ b/libbuild2/version/snapshot.cxx @@ -12,7 +12,7 @@ namespace build2 namespace version { snapshot - extract_snapshot_git (dir_path); + extract_snapshot_git (context&, dir_path); static const path git (".git"); @@ -46,7 +46,7 @@ namespace build2 if (butl::entry_exists (d / git, true /* follow_symlinks */, true /* ignore_errors */)) - return extract_snapshot_git (move (d)); + return extract_snapshot_git (rs.ctx, move (d)); } return snapshot (); |