aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/version
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/version')
-rw-r--r--libbuild2/version/init.cxx131
-rw-r--r--libbuild2/version/module.hxx1
-rw-r--r--libbuild2/version/rule.cxx71
-rw-r--r--libbuild2/version/rule.hxx11
-rw-r--r--libbuild2/version/snapshot-git.cxx19
-rw-r--r--libbuild2/version/snapshot.cxx4
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 ();