diff options
Diffstat (limited to 'libbuild2/bash')
-rw-r--r-- | libbuild2/bash/init.cxx | 15 | ||||
-rw-r--r-- | libbuild2/bash/rule.cxx | 174 | ||||
-rw-r--r-- | libbuild2/bash/rule.hxx | 29 | ||||
-rw-r--r-- | libbuild2/bash/target.cxx | 2 | ||||
-rw-r--r-- | libbuild2/bash/target.hxx | 7 | ||||
-rw-r--r-- | libbuild2/bash/utility.hxx | 20 |
6 files changed, 151 insertions, 96 deletions
diff --git a/libbuild2/bash/init.cxx b/libbuild2/bash/init.cxx index cf5307f..88c88ba 100644 --- a/libbuild2/bash/init.cxx +++ b/libbuild2/bash/init.cxx @@ -20,7 +20,7 @@ namespace build2 namespace bash { static const in_rule in_rule_; - static const install_rule install_rule_ (in_rule_); + static const install_rule install_rule_ (in_rule_, "bash.in"); bool init (scope& rs, @@ -48,14 +48,13 @@ namespace build2 { using namespace install; - // Install into bin/<project>/ by default stripping the .bash - // extension from <project> if present. + // Install bash{} into bin/<project>.bash/ by default. // const project_name& p (project (rs)); if (!p.empty ()) { - install_path<bash> (bs, dir_path ("bin") /= project_base (p)); + install_path<bash> (bs, dir_path ("bin") /= modules_install_dir (p)); install_mode<bash> (bs, "644"); } } @@ -72,11 +71,11 @@ namespace build2 if (install_loaded) { - bs.insert_rule<exe> (perform_install_id, "bash.install", install_rule_); - bs.insert_rule<exe> (perform_uninstall_id, "bash.uninstall", install_rule_); + bs.insert_rule<exe> (perform_install_id, "bash.install", install_rule_); + bs.insert_rule<exe> (perform_uninstall_id, "bash.install", install_rule_); - bs.insert_rule<bash> (perform_install_id, "bash.install", install_rule_); - bs.insert_rule<bash> (perform_uninstall_id, "bash.uninstall", install_rule_); + bs.insert_rule<bash> (perform_install_id, "bash.install", install_rule_); + bs.insert_rule<bash> (perform_uninstall_id, "bash.install", install_rule_); } return true; diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx index d378227..6e96b34 100644 --- a/libbuild2/bash/rule.cxx +++ b/libbuild2/bash/rule.cxx @@ -26,6 +26,9 @@ namespace build2 struct match_data { + explicit + match_data (const in_rule& r): rule (r) {} + // The "for install" condition is signalled to us by install_rule when // it is matched for the update operation. It also verifies that if we // have already been executed, then it was for install. @@ -33,24 +36,45 @@ namespace build2 // See cc::link_rule for a discussion of some subtleties in this logic. // optional<bool> for_install; + + const in_rule& rule; + + target_state + operator() (action a, const target& t) + { + // Unless the outer install rule signalled that this is update for + // install, signal back that we've performed plain update. + // + if (!for_install) + for_install = false; + + //@@ TODO: need to verify all the modules we depend on are compatible + // with our for_install value, similar to cc::link_rule's + // append_libraries() (and which is the other half of the check + // in install_rule). + + return rule.perform_update (a, t); + } }; - static_assert (sizeof (match_data) <= target::data_size, - "insufficient space"); + static_assert (sizeof (match_data) <= target::small_data_size, + "match data requires dynamic allocation"); // in_rule // bool in_rule:: - match (action a, target& t, const string&) const + match (action a, target& xt, const string& hint, match_extra&) const { tracer trace ("bash::in_rule::match"); - // Note that for bash{} we match even if the target does not depend on - // any modules (while it could have been handled by the in module, that - // would require loading it). + file& t (xt.as<file> ()); // Only registered for exe{} and bash{}. + + // Note that for bash{} and for exe{} with hint we match even if the + // target does not depend on any modules (while it could have been + // handled by the in module, that would require loading it). // - bool fi (false); // Found in. - bool fm (t.is_a<bash> ()); // Found module. + bool fi (false); // Found in. + bool fm (!hint.empty () || t.is_a<bash> ()); // Found module. for (prerequisite_member p: group_prerequisite_members (a, t)) { if (include (a, t, p) != include_type::normal) // Excluded/ad hoc. @@ -64,34 +88,32 @@ namespace build2 l4 ([&]{trace << "no in file prerequisite for target " << t;}); if (!fm) - l4 ([&]{trace << "no bash module prerequisite for target " << t;}); + l4 ([&]{trace << "no bash module prerequisite or hint for target " + << t;}); - return (fi && fm); - } - - recipe in_rule:: - apply (action a, target& t) const - { - // Note that for-install is signalled by install_rule and therefore - // can only be relied upon during execute. + // If we match, derive the file name early as recommended by the in + // rule. // - t.data (match_data ()); + if (fi && fm) + t.derive_path (); - return rule::apply (a, t); + return fi && fm; } - target_state in_rule:: - perform_update (action a, const target& t) const + recipe in_rule:: + apply (action a, target& t) const { - // Unless the outer install rule signalled that this is update for - // install, signal back that we've performed plain update. - // - match_data& md (t.data<match_data> ()); + recipe r (rule::apply (a, t)); - if (!md.for_install) - md.for_install = false; + if (a == perform_update_id) + { + // Note that for-install is signalled by install_rule and therefore + // can only be relied upon during execute. + // + return match_data (*this); + } - return rule::perform_update (a, t); + return r; } prerequisite_target in_rule:: @@ -121,7 +143,7 @@ namespace build2 // apply). // string ext (p.ext ? *p.ext : "bash"); - path ip (dir_path (project_base (*p.proj)) / p.dir / p.name); + path ip (dir_path (modules_install_dir (*p.proj)) / p.dir / p.name); if (!ext.empty ()) { @@ -156,6 +178,9 @@ namespace build2 if (mt != timestamp_nonexistent) { + // @@ Do we actually need _locked(), isn't path_mtime() + // atomic? + // auto rp (t.ctx.targets.insert_locked (bash::static_type, ap.directory (), dir_path () /* out */, @@ -195,12 +220,16 @@ namespace build2 action a, const target& t, const string& n, + optional<uint64_t> flags, bool strict, + const substitution_map* smap, const optional<string>& null) const { + assert (!flags); + return n.compare (0, 6, "import") == 0 && (n[6] == ' ' || n[6] == '\t') ? substitute_import (l, a, t, trim (string (n, 7))) - : rule::substitute (l, a, t, n, strict, null); + : rule::substitute (l, a, t, n, nullopt, strict, smap, null); } string in_rule:: @@ -209,21 +238,45 @@ namespace build2 const target& t, const string& n) const { - // Derive (relative) import path from the import name. + // Derive (relative) import path from the import name. And derive import + // installed path from that by adding the .bash extension to the first + // component. // - path ip; + path ip, iip; + project_name pn; try { ip = path (n); - if (ip.empty () || ip.absolute ()) + if (ip.empty () || ip.simple () || ip.absolute ()) throw invalid_path (n); if (ip.extension_cstring () == nullptr) ip += ".bash"; ip.normalize (); + + auto b (ip.begin ()), e (ip.end ()); + + try + { + pn = project_name (*b); + } + catch (const invalid_argument& e) + { + fail (l) << "invalid import path '" << n << "': " << e.what (); + } + + char s (b++.separator ()); + + iip = path (modules_install_dir (pn) + s) / path (b, e); + + // Strip the .bash extension from the project name in this path to + // be able to compare it to paths inside the project (see below). + // + if (pn.extension () == "bash") + ip = path (pn.base ("bash") + s) / path (b, e); } catch (const invalid_path&) { @@ -235,7 +288,7 @@ namespace build2 const path* ap (nullptr); for (const prerequisite_target& pt: t.prerequisite_targets[a]) { - if (pt.adhoc || pt.target == nullptr) + if (pt.target == nullptr || pt.adhoc ()) continue; if (const bash* b = pt.target->is_a<bash> ()) @@ -253,19 +306,19 @@ namespace build2 // // But we still do a simple match first since it can quickly weed // out candidates that cannot possibly match. - // - if (!pp.sup (ip)) - continue; - // See if this is import-installed target (refer to search() for - // details). + // See if this is import-installed target (refer to search() above + // for details). // if (size_t n = pt.data) { + if (!pp.sup (iip)) + continue; + // Both are normalized so we can compare the "tails". // const string& ps (pp.string ()); - const string& is (ip.string ()); + const string& is (iip.string ()); if (path::traits_type::compare ( ps.c_str () + ps.size () - n, n, @@ -280,6 +333,9 @@ namespace build2 if (const scope* rs = b->base_scope ().root_scope ()) { + if (!pp.sup (ip) || project (*rs) != pn) + continue; + const dir_path& d (pp.sub (rs->src_path ()) ? rs->src_path () : rs->out_path ()); @@ -300,7 +356,7 @@ namespace build2 if (ap == nullptr) fail (l) << "unable to resolve import path " << ip; - match_data& md (t.data<match_data> ()); + match_data& md (t.data<match_data> (a)); assert (md.for_install); if (*md.for_install) @@ -358,7 +414,7 @@ namespace build2 "source \"$(dirname" " \"$(readlink -f" " \"${BASH_SOURCE[0]}\")\")/" - + ip.string () + "\""; + + iip.string () + '"'; } else { @@ -379,7 +435,7 @@ namespace build2 return "source \"$(dirname" " \"${BASH_SOURCE[0]}\")/" - + o + ip.string () + "\""; + + o + iip.string () + '"'; } } else @@ -389,30 +445,35 @@ namespace build2 // install_rule // bool install_rule:: - match (action a, target& t, const string& hint) const + match (action a, target& t) const { // We only want to handle installation if we are also the ones building // this target. So first run in's match(). // - return in_.match (a, t, hint) && file_rule::match (a, t, ""); + return in_.sub_match (in_name_, update_id, a, t) && + file_rule::match (a, t); } recipe install_rule:: - apply (action a, target& t) const + apply (action a, target& t, match_extra& me) const { - recipe r (file_rule::apply (a, t)); + recipe r (file_rule::apply_impl (a, t, me)); + + if (r == nullptr) + return noop_recipe; if (a.operation () == update_id) { // Signal to the in rule that this is update for install. And if the // update has already been executed, verify it was done for install. // - auto& md (t.data<match_data> ()); + auto& md (t.data<match_data> (a.inner_action ())); if (md.for_install) { if (!*md.for_install) - fail << "target " << t << " already updated but not for install"; + fail << "incompatible " << t << " build" << + info << "target already built not for install"; } else md.for_install = true; @@ -420,20 +481,5 @@ namespace build2 return r; } - - const target* install_rule:: - filter (action a, const target& t, const prerequisite& p) const - { - // If this is a module prerequisite, install it as long as it is in the - // same amalgamation as we are. - // - if (p.is_a<bash> ()) - { - const target& pt (search (t, p)); - return pt.in (t.weak_scope ()) ? &pt : nullptr; - } - else - return file_rule::filter (a, t, p); - } } } diff --git a/libbuild2/bash/rule.hxx b/libbuild2/bash/rule.hxx index 81c4604..3f9618f 100644 --- a/libbuild2/bash/rule.hxx +++ b/libbuild2/bash/rule.hxx @@ -29,17 +29,16 @@ namespace build2 class LIBBUILD2_BASH_SYMEXPORT in_rule: public in::rule { public: - in_rule (): rule ("bash.in 1", "bash.in", '@', false /* strict */) {} + in_rule (): rule ("bash.in 1", "bash", '@', false /* strict */) {} virtual bool - match (action, target&, const string&) const override; + match (action, target&, const string&, match_extra&) const override; + + using in::rule::match; // Make Clang happy. virtual recipe apply (action, target&) const override; - virtual target_state - perform_update (action, const target&) const override; - virtual prerequisite_target search (action, const target&, @@ -51,7 +50,9 @@ namespace build2 action a, const target&, const string&, + optional<uint64_t>, bool, + const substitution_map*, const optional<string>&) const override; string @@ -61,29 +62,23 @@ namespace build2 const string&) const; }; - // Installation rule for bash scripts (exe{}) and modules (bash{}). Here - // we do: - // - // 1. Signal to in_rule that this is update for install. - // - // 2. Custom filtering of prerequisites. + // Installation rule for bash scripts (exe{}) and modules (bash{}) that + // signals to in_rule that this is update for install. // class LIBBUILD2_BASH_SYMEXPORT install_rule: public install::file_rule { public: - install_rule (const in_rule& in): in_ (in) {} + install_rule (const in_rule& r, const char* n): in_ (r), in_name_ (n) {} virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe - apply (action, target&) const override; - - virtual const target* - filter (action, const target&, const prerequisite&) const override; + apply (action, target&, match_extra&) const override; protected: const in_rule& in_; + const string in_name_; }; } } diff --git a/libbuild2/bash/target.cxx b/libbuild2/bash/target.cxx index 6fa7cf4..5240fed 100644 --- a/libbuild2/bash/target.cxx +++ b/libbuild2/bash/target.cxx @@ -23,7 +23,7 @@ namespace build2 &target_pattern_var<bash_ext_def>, nullptr, &file_search, - false + target_type::flag::none }; } } diff --git a/libbuild2/bash/target.hxx b/libbuild2/bash/target.hxx index f0af967..ad926bd 100644 --- a/libbuild2/bash/target.hxx +++ b/libbuild2/bash/target.hxx @@ -21,11 +21,14 @@ namespace build2 class LIBBUILD2_BASH_SYMEXPORT bash: public file { public: - using file::file; + bash (context& c, dir_path d, dir_path o, string n) + : file (c, move (d), move (o), move (n)) + { + dynamic_type = &static_type; + } public: static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} }; } } diff --git a/libbuild2/bash/utility.hxx b/libbuild2/bash/utility.hxx index 087fc38..e5e4377 100644 --- a/libbuild2/bash/utility.hxx +++ b/libbuild2/bash/utility.hxx @@ -11,14 +11,26 @@ namespace build2 { namespace bash { - // Strip the .bash extension from the project name. + // Return the bash{} modules installation directory under bin/. // - // Note that the result may not be a valid project name. + // Note that we used to install into bin/<project>/ but that has a good + // chance of clashing with the project's executable. Adding the .bash + // extension feels like a good idea since in our model the executables + // should not use the .bash extension (only modules) and therefore are + // unlikely to clash with this name. + // + // One drawback of this approach is that in case of a project like + // libbutl.bash we now have different module directories inside the + // project (libbutl/) and when installed (libbutl.bash/). Also, the + // installation directory will be shared with the libbutl project but + // that's probably ok (and we had the same issue before). // inline string - project_base (const project_name& pn) + modules_install_dir (const project_name& pn) { - return pn.base ("bash"); + // Strip the .bash extension if present not to duplicate it. + // + return pn.base ("bash") + ".bash"; } } } |