aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/bash
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/bash')
-rw-r--r--libbuild2/bash/init.cxx15
-rw-r--r--libbuild2/bash/rule.cxx158
-rw-r--r--libbuild2/bash/rule.hxx18
-rw-r--r--libbuild2/bash/target.cxx2
-rw-r--r--libbuild2/bash/target.hxx7
-rw-r--r--libbuild2/bash/utility.hxx20
6 files changed, 144 insertions, 76 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 e0391e3..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,39 +88,32 @@ namespace build2
l4 ([&]{trace << "no in file prerequisite for target " << t;});
if (!fm)
- l4 ([&]{trace << "no bash module prerequisite for target " << t;});
-
- return (fi && fm);
- }
+ l4 ([&]{trace << "no bash module prerequisite or hint for target "
+ << t;});
- 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> ());
-
- if (!md.for_install)
- md.for_install = false;
+ recipe r (rule::apply (a, t));
- //@@ 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).
+ 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::
@@ -126,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 ())
{
@@ -161,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 */,
@@ -200,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::
@@ -214,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&)
{
@@ -240,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> ())
@@ -258,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,
@@ -285,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 ());
@@ -305,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)
@@ -363,7 +414,7 @@ namespace build2
"source \"$(dirname"
" \"$(readlink -f"
" \"${BASH_SOURCE[0]}\")\")/"
- + ip.string () + "\"";
+ + iip.string () + '"';
}
else
{
@@ -384,7 +435,7 @@ namespace build2
return
"source \"$(dirname"
" \"${BASH_SOURCE[0]}\")/"
- + o + ip.string () + "\"";
+ + o + iip.string () + '"';
}
}
else
@@ -394,18 +445,19 @@ 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_impl (a, t));
+ recipe r (file_rule::apply_impl (a, t, me));
if (r == nullptr)
return noop_recipe;
@@ -415,7 +467,7 @@ namespace build2
// 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)
{
diff --git a/libbuild2/bash/rule.hxx b/libbuild2/bash/rule.hxx
index c54b07c..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
@@ -67,16 +68,17 @@ namespace build2
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;
+ 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";
}
}
}