diff options
Diffstat (limited to 'libbuild2/version/init.cxx')
-rw-r--r-- | libbuild2/version/init.cxx | 216 |
1 files changed, 128 insertions, 88 deletions
diff --git a/libbuild2/version/init.cxx b/libbuild2/version/init.cxx index d11b2f3..b3657bc 100644 --- a/libbuild2/version/init.cxx +++ b/libbuild2/version/init.cxx @@ -3,7 +3,9 @@ #include <libbuild2/version/init.hxx> -#include <libbutl/manifest-parser.mxx> +#include <cstring> // strchr() + +#include <libbutl/manifest-parser.hxx> #include <libbuild2/scope.hxx> #include <libbuild2/variable.hxx> @@ -30,6 +32,44 @@ namespace build2 static const in_rule in_rule_; static const manifest_install_rule manifest_install_rule_; + static void + dist_callback (const path&, const scope&, void*); + + void + boot_post (scope& rs, const location&, module_boot_post_extra& extra) + { + // If the dist module is used, set its dist.package and register the + // post-processing callback. + // + if (auto* dm = rs.find_module<dist::module> (dist::module::name)) + { + // Don't touch if dist.package was set by the user. + // + value& val (rs.assign (dm->var_dist_package)); + + if (!val) + { + auto& m (extra.module_as<module> ()); + const standard_version& v (m.version); + + // We've already verified in boot() it is named. + // + string p (project (rs).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); + } + } + } + void boot (scope& rs, const location& l, module_boot_extra& extra) { @@ -105,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 ((p = v.find_last_of ("?*")) != string::npos) - v.erase (0, p + 1); + if (v.find_first_of ("{?|\n") != string::npos) + continue; - // Parse as |-separated "words". + // Find the beginning of the dependency package name, skipping + // the build-time marker, if present. // - for (size_t b (0), e (0); next_word (v, b, e, '|'); ) + bool buildtime (v[0] == '*'); + size_t b (buildtime ? v.find_first_not_of (" \t", 1) : 0); + + 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). + // + 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; } } } @@ -208,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 ( @@ -281,19 +360,17 @@ namespace build2 // Initialize second (dist.package, etc). // + extra.post = &boot_post; extra.init = module_boot_init::before_second; } - static void - dist_callback (const path&, const scope&, void*); - bool init (scope& rs, scope&, const location& l, bool first, bool, - module_init_extra& extra) + module_init_extra&) { tracer trace ("version::init"); @@ -304,43 +381,6 @@ namespace build2 // load_module (rs, rs, "in.base", l); - auto& m (extra.module_as<module> ()); - 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.find_module<dist::module> (dist::module::name)) - { - // Make sure dist is init'ed, not just boot'ed. - // - 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) - { - // We've already verified in boot() it is named. - // - string p (project (rs).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. // rs.insert_rule<file> (perform_update_id, "version.in", in_rule_); @@ -350,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; @@ -363,7 +403,7 @@ namespace build2 // Complain if this is an uncommitted snapshot. // - if (!m.committed && !m.dist_uncommitted) + if (!m.committed && !cast_false<bool> (rs["config.dist.uncommitted"])) fail << "distribution of uncommitted project " << rs.src_path () << info << "specify config.dist.uncommitted=true to force"; |