aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/target.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/target.cxx')
-rw-r--r--libbuild2/target.cxx625
1 files changed, 486 insertions, 139 deletions
diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx
index a686fc2..2a134a4 100644
--- a/libbuild2/target.cxx
+++ b/libbuild2/target.cxx
@@ -22,26 +22,13 @@ namespace build2
bool target_type::
is_a (const char* n) const
{
- if (strcmp (name, n) == 0)
- return true;
-
- for (const target_type* b (base); b != nullptr; b = b->base)
+ for (const target_type* b (this); b != nullptr; b = b->base)
if (strcmp (b->name, n) == 0)
return true;
return false;
}
- bool target_type::
- is_a_base (const target_type& tt) const
- {
- for (const target_type* b (base); b != nullptr; b = b->base)
- if (*b == tt)
- return true;
-
- return false;
- }
-
// target_key
//
void target_key::
@@ -51,7 +38,9 @@ namespace build2
if (!name->empty ())
{
v = *name;
- target::combine_name (v, ext, false /* @@ TODO: what to do? */);
+ // @@ TMP: see also other calls to combine_name() -- need to fix.
+ //
+ target::combine_name (v, ext, false /* @@ TMP: what to do? */);
}
else
assert (!ext || ext->empty ()); // Unspecified or none.
@@ -69,6 +58,7 @@ namespace build2
//
static const char* const target_state_[] =
{
+ "<invalid>", // Absent/invalid (see target_state for details).
"unknown",
"unchanged",
"postponed",
@@ -78,10 +68,10 @@ namespace build2
"group"
};
- ostream&
- operator<< (ostream& os, target_state ts)
+ string
+ to_string (target_state ts)
{
- return os << target_state_[static_cast<uint8_t> (ts)];
+ return target_state_[static_cast<uint8_t> (ts)];
}
// target
@@ -91,7 +81,6 @@ namespace build2
target::
~target ()
{
- clear_data ();
}
const string& target::
@@ -122,31 +111,40 @@ namespace build2
group_view target::
group_members (action) const
{
- assert (false); // Not a group or doesn't expose its members.
+ // Not a group or doesn't expose its members.
+ //
return group_view {nullptr, 0};
}
const scope& target::
- base_scope () const
+ base_scope_impl () const
{
// If this target is from the src tree, use its out directory to find
// the scope.
//
- return ctx.scopes.find_out (out_dir ());
- }
+ const scope& s (ctx.scopes.find_out (out_dir ()));
- const scope& target::
- root_scope () const
- {
- // This is tricky to cache so we do the lookup for now.
+ // Cache unless we are in the load phase.
//
- const scope* r (base_scope ().root_scope ());
- assert (r != nullptr);
- return *r;
+ if (ctx.phase != run_phase::load)
+ {
+ const scope* e (nullptr);
+ if (!base_scope_.compare_exchange_strong (
+ e,
+ &s,
+ memory_order_release,
+ memory_order_consume))
+ assert (e == &s);
+ }
+
+ return s;
}
pair<lookup, size_t> target::
- lookup_original (const variable& var, bool target_only) const
+ lookup_original (const variable& var,
+ bool target_only,
+ const scope* bs,
+ bool locked) const
{
pair<lookup_type, size_t> r (lookup_type (), 0);
@@ -157,23 +155,48 @@ namespace build2
r.first = lookup_type (*p.first, p.second, vars);
}
- const target* g (nullptr);
+ const target* g1 (nullptr);
+ const target* g2 (nullptr);
if (!r.first)
{
++r.second;
+ // While we went back to not treating the first member as a group for
+ // variable lookup, let's keep this logic in case one day we end up with
+ // a separate ad hoc group target.
+ //
+#if 0
+ // In case of an ad hoc group, we may have to look in two groups.
+ //
+ if ((g1 = group) != nullptr)
+ {
+ auto p (g1->vars.lookup (var));
+ if (p.first != nullptr)
+ r.first = lookup_type (*p.first, p.second, g1->vars);
+ else
+ {
+ if ((g2 = g1->group) != nullptr)
+ {
+ auto p (g2->vars.lookup (var));
+ if (p.first != nullptr)
+ r.first = lookup_type (*p.first, p.second, g2->vars);
+ }
+ }
+ }
+#else
// Skip looking up in the ad hoc group, which is semantically the
// first/primary member.
//
- if ((g = group == nullptr
+ if ((g1 = group == nullptr
? nullptr
: group->adhoc_group () ? group->group : group))
{
- auto p (g->vars.lookup (var));
+ auto p (g1->vars.lookup (var));
if (p.first != nullptr)
- r.first = lookup_type (*p.first, p.second, g->vars);
+ r.first = lookup_type (*p.first, p.second, g1->vars);
}
+#endif
}
// Delegate to scope's lookup_original().
@@ -182,13 +205,22 @@ namespace build2
{
if (!target_only)
{
- target_key tk (key ());
- target_key gk (g != nullptr ? g->key () : target_key {});
+ auto key = [locked] (const target* t)
+ {
+ return locked ? t->key_locked () : t->key ();
+ };
+
+ target_key tk (key (this));
+ target_key g1k (g1 != nullptr ? key (g1) : target_key {});
+ target_key g2k (g2 != nullptr ? key (g2) : target_key {});
- auto p (base_scope ().lookup_original (
- var,
- &tk,
- g != nullptr ? &gk : nullptr));
+ if (bs == nullptr)
+ bs = &base_scope ();
+
+ auto p (bs->lookup_original (var,
+ &tk,
+ g1 != nullptr ? &g1k : nullptr,
+ g2 != nullptr ? &g2k : nullptr));
r.first = move (p.first);
r.second = r.first ? r.second + p.second : p.second;
@@ -201,14 +233,30 @@ namespace build2
}
value& target::
- append (const variable& var)
+ append (const variable& var, const scope* bs)
{
// Note: see also prerequisite::append() if changing anything here.
// Note that here we want the original value without any overrides
// applied.
//
- auto l (lookup_original (var).first);
+ auto l (lookup_original (var, false, bs).first);
+
+ if (l.defined () && l.belongs (*this)) // Existing var in this target.
+ return vars.modify (l); // Ok since this is original.
+
+ value& r (assign (var)); // NULL.
+
+ if (l.defined ())
+ r = *l; // Copy value (and type) from the outer scope.
+
+ return r;
+ }
+
+ value& target::
+ append_locked (const variable& var, const scope* bs)
+ {
+ auto l (lookup_original (var, false, bs, true /* locked */).first);
if (l.defined () && l.belongs (*this)) // Existing var in this target.
return vars.modify (l); // Ok since this is original.
@@ -528,28 +576,128 @@ namespace build2
// include()
//
+ // See var_include documentation for details on what's going on here.
+ //
include_type
include_impl (action a,
const target& t,
- const string& v,
const prerequisite& p,
- const target* m)
+ const target* m,
+ lookup* rl)
{
context& ctx (t.ctx);
- include_type r (false);
+ include_type r (include_type::normal);
+ {
+ lookup l (p.vars[ctx.var_include]);
- if (v == "false") r = include_type::excluded;
- else if (v == "adhoc") r = include_type::adhoc;
- else if (v == "true") r = include_type::normal;
- else
- fail << "invalid " << ctx.var_include->name << " variable value "
- << "'" << v << "' specified for prerequisite " << p;
+ if (l.defined ())
+ {
+ if (l->null)
+ {
+ // @@ TMP (added in 0.16.0).
+ //
+ warn << "null " << *ctx.var_include << " variable value specified "
+ << "for prerequisite " << p <<
+ info << "treated as undefined for backwards compatibility" <<
+ info << "this warning will become error in the future";
+ }
+ else
+ {
+ const string& v (cast<string> (*l));
+
+ if (v == "false") r = include_type::excluded;
+ else if (v == "true") r = include_type::normal;
+ else if (v == "adhoc") r = include_type::adhoc;
+ else if (v == "posthoc") r = include_type::posthoc;
+ else
+ fail << "invalid " << *ctx.var_include << " variable value '"
+ << v << "' specified for prerequisite " << p;
+ }
+ }
+ }
+
+ // Handle operation-specific override (see var_include documentation
+ // for details).
+ //
+ lookup l;
+ optional<bool> r1; // Absent means something other than true|false.
+
+ names storage;
+ names_view ns;
+ const variable* ovar (nullptr);
+
+ if (r != include_type::excluded)
+ {
+ // Instead of going via potentially expensive target::base_scope(), use
+ // the prerequisite's scope; while it may not be the same as the
+ // targets's base scope, they must have the same root scope.
+ //
+ const scope& rs (*p.scope.root_scope ());
+
+ ovar = rs.root_extra->operations[
+ (a.outer ()
+ ? ctx.current_outer_oif
+ : ctx.current_inner_oif)->id].ovar;
+
+ if (ovar != nullptr)
+ {
+ l = p.vars[*ovar];
+
+ if (l.defined ())
+ {
+ if (l->null)
+ fail << "null " << *ovar << " variable value specified for "
+ << "prerequisite " << p;
+
+ // Maybe we should optimize this for the common cases (bool, path,
+ // name)? But then again we don't expect many such overrides. Plus
+ // will complicate the diagnostics below.
+ //
+ ns = reverse (*l, storage, true /* reduce */);
+
+ if (ns.size () == 1)
+ {
+ const name& n (ns[0]);
+
+ if (n.simple ())
+ {
+ const string& v (n.value);
+
+ if (v == "false")
+ r1 = false;
+ else if (v == "true")
+ r1 = true;
+ }
+ }
+
+ if (r1 && !*r1)
+ r = include_type::excluded;
+ }
+ }
+ }
// Call the meta-operation override, if any (currently used by dist).
//
- if (auto f = ctx.current_mif->include)
- r = f (a, t, prerequisite_member {p, m}, r);
+ if (r != include_type::normal || l)
+ {
+ if (auto f = ctx.current_mif->include)
+ r = f (a, t, prerequisite_member {p, m}, r, l);
+ }
+
+ if (l)
+ {
+ if (rl != nullptr)
+ *rl = l;
+ else if (!r1)
+ {
+ // Note: we have to delay this until the meta-operation callback above
+ // had a chance to override it.
+ //
+ fail << "unrecognized " << *ovar << " variable value '" << ns
+ << "' specified for prerequisite " << p;
+ }
+ }
return r;
}
@@ -559,7 +707,9 @@ namespace build2
const target* target_set::
find (const target_key& k, tracer& trace) const
{
- slock sl (mutex_);
+ bool load (ctx.phase == run_phase::load);
+
+ slock sl (mutex_, defer_lock); if (!load) sl.lock ();
map_type::const_iterator i (map_.find (k));
if (i == map_.end ())
@@ -578,14 +728,18 @@ namespace build2
// Between us releasing the shared lock and acquiring unique the
// extension could change and possibly a new target that matches the
// key could be inserted. In this case we simply re-run find ().
+ // Naturally, can't happen during load.
//
- sl.unlock ();
- ul = ulock (mutex_);
-
- if (ext) // Someone set the extension.
+ if (!load)
{
- ul.unlock ();
- return find (k, trace);
+ sl.unlock ();
+ ul = ulock (mutex_);
+
+ if (ext) // Someone set the extension.
+ {
+ ul.unlock ();
+ return find (k, trace);
+ }
}
}
@@ -619,10 +773,12 @@ namespace build2
string name,
optional<string> ext,
target_decl decl,
- tracer& trace)
+ tracer& trace,
+ bool skip_find,
+ bool need_lock)
{
target_key tk {&tt, &dir, &out, &name, move (ext)};
- target* t (const_cast<target*> (find (tk, trace)));
+ target* t (skip_find ? nullptr : const_cast<target*> (find (tk, trace)));
if (t == nullptr)
{
@@ -643,7 +799,9 @@ namespace build2
// case we proceed pretty much like find() except already under the
// exclusive lock.
//
- ulock ul (mutex_);
+ ulock ul (mutex_, defer_lock);
+ if (ctx.phase != run_phase::load || need_lock)
+ ul.lock ();
auto p (map_.emplace (target_key {&tt, &t->dir, &t->out, &t->name, e},
unique_ptr<target> (t)));
@@ -652,10 +810,28 @@ namespace build2
if (p.second)
{
+#if 0
+ {
+ size_t n (map_.bucket_count ());
+ if (n > buckets_)
+ {
+ text << "target_set buckets: " << buckets_ << " -> " << n
+ << " (" << map_.size () << ")";
+ buckets_ = n;
+ }
+ }
+#endif
+
t->ext_ = &i->first.ext;
t->decl = decl;
t->state.inner.target_ = t;
t->state.outer.target_ = t;
+ t->state.inner.vars.target_ = t;
+ t->state.outer.vars.target_ = t;
+
+ if (ctx.phase != run_phase::load && !need_lock)
+ ul.unlock ();
+
return pair<target&, ulock> (*t, move (ul));
}
@@ -707,9 +883,14 @@ namespace build2
static const optional<string> unknown_ext ("?");
- ostream&
- to_stream (ostream& os, const target_key& k, optional<stream_verbosity> osv)
+ bool
+ to_stream (ostream& os,
+ const target_key& k,
+ optional<stream_verbosity> osv,
+ bool name_only)
{
+ // Note: similar code in print_diag_impl(vector<target_key>).
+
stream_verbosity sv (osv ? *osv : stream_verb (os));
uint16_t dv (sv.path);
uint16_t ev (sv.extension);
@@ -719,22 +900,29 @@ namespace build2
//
bool n (!k.name->empty ());
- // Note: relative() returns empty for './'.
- //
- const dir_path& rd (dv < 1 ? relative (*k.dir) : *k.dir); // Relative.
- const dir_path& pd (n ? rd : rd.directory ()); // Parent.
+ const target_type& tt (*k.type);
- if (!pd.empty ())
+ dir_path rds; // Storage.
+ if (!name_only)
{
+ // Note: relative() returns empty for './'.
+ //
if (dv < 1)
- os << diag_relative (pd);
- else
- to_stream (os, pd, true /* representation */);
- }
+ rds = relative (*k.dir);
- const target_type& tt (*k.type);
+ const dir_path& rd (dv < 1 ? rds : *k.dir); // Relative.
+ const dir_path& pd (n ? rd : rd.directory ()); // Parent.
- os << tt.name << '{';
+ if (!pd.empty ())
+ {
+ if (dv < 1)
+ os << diag_relative (pd);
+ else
+ to_stream (os, pd, true /* representation */);
+ }
+
+ os << tt.name << '{';
+ }
if (n)
{
@@ -777,37 +965,47 @@ namespace build2
}
}
else
+ {
+ if (name_only && dv < 1) // Already done if !name_only.
+ rds = relative (*k.dir);
+
+ const dir_path& rd (dv < 1 ? rds : *k.dir);
+
to_stream (os,
rd.empty () ? dir_path (".") : rd.leaf (),
true /* representation */);
+ }
- os << '}';
-
- // If this target is from src, print its out.
- //
- if (!k.out->empty ())
+ if (!name_only)
{
- if (dv < 1)
+ os << '}';
+
+ // If this target is from src, print its out.
+ //
+ if (!k.out->empty ())
{
- // Don't print '@./'.
- //
- const string& o (diag_relative (*k.out, false));
+ if (dv < 1)
+ {
+ // Don't print '@./'.
+ //
+ const string& o (diag_relative (*k.out, false));
- if (!o.empty ())
- os << '@' << o;
+ if (!o.empty ())
+ os << '@' << o;
+ }
+ else
+ os << '@' << *k.out;
}
- else
- os << '@' << *k.out;
}
- return os;
+ return n; // Regular if we had the name.
}
ostream&
operator<< (ostream& os, const target_key& k)
{
if (auto p = k.type->print)
- p (os, k);
+ p (os, k, false /* name_only */);
else
to_stream (os, k, stream_verb (os));
@@ -828,14 +1026,19 @@ namespace build2
case run_phase::load: break;
case run_phase::match:
{
- // Similar logic to matched_state_impl().
+ // Similar logic to target::matched().
//
const opstate& s (state[action () /* inner */]);
- // Note: already synchronized.
- size_t o (s.task_count.load (memory_order_relaxed) - ctx.count_base ());
+ // Note: use acquire for group_state().
+ //
+ size_t c (s.task_count.load (memory_order_acquire));
+ size_t b (ctx.count_base ()); // Note: cannot do (c - b)!
- if (o != offset_applied && o != offset_executed)
+ if (!(c == (b + offset_applied) ||
+ c == (b + offset_executed) ||
+ (c >= (b + offset_busy) &&
+ s.match_extra.cur_options_.load (memory_order_relaxed) != 0)))
break;
}
// Fall through.
@@ -958,25 +1161,27 @@ namespace build2
//
const target*
- target_search (const target& t, const prerequisite_key& pk)
+ target_search (context& ctx, const target*, const prerequisite_key& pk)
{
// The default behavior is to look for an existing target in the
// prerequisite's directory scope.
//
- return search_existing_target (t.ctx, pk);
+ return search_existing_target (ctx, pk, true /* out_only */);
}
const target*
- file_search (const target& t, const prerequisite_key& pk)
+ file_search (context& ctx, const target* t, const prerequisite_key& pk)
{
- // First see if there is an existing target.
+ // First see if there is an existing target in the out or src tree.
//
- if (const target* e = search_existing_target (t.ctx, pk))
+ if (const target* e = search_existing_target (ctx,
+ pk,
+ false /* out_only */))
return e;
// Then look for an existing file in the src tree.
//
- return search_existing_file (t.ctx, pk);
+ return t != nullptr ? search_existing_file (ctx, pk) : nullptr;
}
extern const char target_extension_none_[] = "";
@@ -996,20 +1201,20 @@ namespace build2
return tk.ext->c_str ();
}
- void
- target_print_0_ext_verb (ostream& os, const target_key& k)
+ bool
+ target_print_0_ext_verb (ostream& os, const target_key& k, bool no)
{
stream_verbosity sv (stream_verb (os));
if (sv.extension == 1) sv.extension = 0; // Remap 1 to 0.
- to_stream (os, k, sv);
+ return to_stream (os, k, sv, no);
}
- void
- target_print_1_ext_verb (ostream& os, const target_key& k)
+ bool
+ target_print_1_ext_verb (ostream& os, const target_key& k, bool no)
{
stream_verbosity sv (stream_verb (os));
if (sv.extension == 0) sv.extension = 1; // Remap 0 to 1.
- to_stream (os, k, sv);
+ return to_stream (os, k, sv, no);
}
// type info
@@ -1025,7 +1230,7 @@ namespace build2
nullptr,
nullptr,
&target_search,
- false
+ target_type::flag::none,
};
const target_type mtime_target::static_type
@@ -1038,7 +1243,7 @@ namespace build2
nullptr,
nullptr,
&target_search,
- false
+ target_type::flag::none
};
const target_type path_target::static_type
@@ -1051,7 +1256,7 @@ namespace build2
nullptr,
nullptr,
&target_search,
- false
+ target_type::flag::none
};
const target_type file::static_type
@@ -1064,18 +1269,82 @@ namespace build2
nullptr, /* pattern */
&target_print_1_ext_verb, // Print extension even at verbosity level 0.
&file_search,
- false
+ target_type::flag::none
};
+ // group
+ //
+ group_view group::
+ group_members (action a) const
+ {
+ if (members_on == 0) // Not yet discovered.
+ return group_view {nullptr, 0};
+
+ // Members discovered during anything other than perform_update are only
+ // good for that operation. For example, we only return the static members
+ // ("representative sample") for perform_configure.
+ //
+ // We also re-discover the members on each update and clean not to
+ // overcomplicate the already twisted adhoc_buildscript_rule::apply()
+ // logic.
+ //
+ if (members_on != ctx.current_on)
+ {
+ if (members_action != perform_update_id ||
+ a == perform_update_id ||
+ a == perform_clean_id)
+ return group_view {nullptr, 0};
+ }
+
+ // Note that we may have no members (e.g., perform_configure and there are
+ // no static members). However, whether std::vector returns a non-NULL
+ // pointer in this case is undefined.
+ //
+ size_t n (members.size ());
+ return group_view {
+ n != 0
+ ? members.data ()
+ : reinterpret_cast<const target* const*> (this),
+ n};
+ }
+
+ const target_type group::static_type
+ {
+ "group",
+ &mtime_target::static_type,
+ &target_factory<group>,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ //
+ // Note that the dyn_members semantics is used not only to handle
+ // depdb-dyndep --dyn-target, but also pattern rule-static members.
+ //
+ target_type::flag::group | target_type::flag::dyn_members
+ };
+
+ // alias
+ //
static const target*
- alias_search (const target& t, const prerequisite_key& pk)
+ alias_search (context& ctx, const target* t, const prerequisite_key& pk)
{
// For an alias we don't want to silently create a target since it will do
- // nothing and it most likely not what the user intended.
+ // nothing and it most likely not what the user intended (but omit this
+ // check when searching for an existing target since presumably a new one
+ // won't be created in this case).
+ //
+ // But, allowing implied aliases seems harmless since all the alias does
+ // is pull its prerequisites. And they are handy to use as metadata
+ // carriers.
//
- const target* e (search_existing_target (t.ctx, pk));
+ // Doesn't feel like an alias in the src tree makes much sense.
+ //
+ const target* e (search_existing_target (ctx, pk, true /* out_only */));
- if (e == nullptr || e->decl != target_decl::real)
+ if ((e == nullptr ||
+ !(operator>= (e->decl, target_decl::implied))) && t != nullptr)
fail << "no explicit target for " << pk;
return e;
@@ -1091,7 +1360,7 @@ namespace build2
nullptr,
nullptr,
&alias_search,
- false
+ target_type::flag::none
};
// dir
@@ -1101,7 +1370,7 @@ namespace build2
{
try
{
- for (const dir_entry& e: dir_iterator (d, true /* ignore_dangling */))
+ for (const dir_entry& e: dir_iterator (d, dir_iterator::detect_dangling))
{
switch (e.type ())
{
@@ -1119,6 +1388,16 @@ namespace build2
break;
}
+ case entry_type::unknown:
+ {
+ bool sl (e.ltype () == entry_type::symlink);
+
+ warn << "skipping "
+ << (sl ? "dangling symlink" : "inaccessible entry") << ' '
+ << d / e.path ();
+
+ break;
+ }
default:
break;
}
@@ -1140,17 +1419,26 @@ namespace build2
try
{
- for (const dir_entry& e: dir_iterator (d, true /* ignore_dangling */))
+ for (const dir_entry& e: dir_iterator (d, dir_iterator::detect_dangling))
{
if (e.type () == entry_type::directory)
+ {
r.push_back (
- prerequisite (nullopt,
- dir::static_type,
+ prerequisite (dir::static_type,
dir_path (e.path ().representation ()), // Relative.
dir_path (), // In the out tree.
string (),
nullopt,
bs));
+ }
+ else if (e.type () == entry_type::unknown)
+ {
+ bool sl (e.ltype () == entry_type::symlink);
+
+ warn << "skipping "
+ << (sl ? "dangling symlink" : "inaccessible entry") << ' '
+ << d / e.path ();
+ }
}
}
catch (const system_error& e)
@@ -1162,17 +1450,27 @@ namespace build2
}
static const target*
- dir_search (const target& t, const prerequisite_key& pk)
+ dir_search (context& ctx, const target* t, const prerequisite_key& pk)
{
tracer trace ("dir_search");
- // The first step is like in search_alias(): looks for an existing target.
+ // The first step is like in alias_search(): looks for an existing target
+ // (but unlike alias, no implied, think `test/: install=false`).
//
- const target* e (search_existing_target (t.ctx, pk));
+ // Likewise, dir{} in the src tree doesn't make much sense.
+ //
+ const target* e (search_existing_target (ctx, pk, true /* out_only */));
if (e != nullptr && e->decl == target_decl::real)
return e;
+ // The search for an existing target can also be done during execute so
+ // none of the below code applied. Note: return implied instead of NULL
+ // (to be consistent with search_new(), for example).
+ //
+ if (t == nullptr)
+ return e;
+
// If not found (or is implied), then try to load the corresponding
// buildfile (which would normally define this target). Failed that, see
// if we can assume an implied buildfile which would be equivalent to:
@@ -1206,18 +1504,18 @@ namespace build2
//
bool retest (false);
- assert (t.ctx.phase == run_phase::match);
+ assert (ctx.phase == run_phase::match);
{
// Switch the phase to load.
//
- phase_switch ps (t.ctx, run_phase::load);
+ phase_switch ps (ctx, run_phase::load);
// This is subtle: while we were fussing around another thread may have
// loaded the buildfile. So re-test now that we are in an exclusive
// phase.
//
if (e == nullptr)
- e = search_existing_target (t.ctx, pk);
+ e = search_existing_target (ctx, pk, true);
if (e != nullptr && e->decl == target_decl::real)
retest = true;
@@ -1255,14 +1553,14 @@ namespace build2
}
}
- assert (t.ctx.phase == run_phase::match);
+ assert (ctx.phase == run_phase::match);
// If we loaded/implied the buildfile, examine the target again.
//
if (retest)
{
if (e == nullptr)
- e = search_existing_target (t.ctx, pk);
+ e = search_existing_target (ctx, pk, true);
if (e != nullptr && e->decl == target_decl::real)
return e;
@@ -1307,7 +1605,7 @@ namespace build2
&dir_pattern,
nullptr,
&dir_search,
- false
+ target_type::flag::none
};
const target_type fsdir::static_type
@@ -1320,7 +1618,7 @@ namespace build2
&dir_pattern,
nullptr,
&target_search,
- false
+ target_type::flag::none
};
static optional<string>
@@ -1387,8 +1685,8 @@ namespace build2
nullptr,
#endif
nullptr,
- &file_search,
- false
+ &file_search, // Note: can also be a script in src.
+ target_type::flag::none
};
static const char*
@@ -1474,7 +1772,56 @@ namespace build2
&buildfile_target_pattern,
nullptr,
&file_search,
- false
+ target_type::flag::none
+ };
+
+ static const char*
+ buildscript_target_extension (const target_key& tk, const scope*)
+ {
+ // If the name is special 'buildscript', then there is no extension,
+ // otherwise it is .buildscript.
+ //
+ return *tk.name == "buildscript" ? "" : "buildscript";
+ }
+
+ static bool
+ buildscript_target_pattern (const target_type&,
+ const scope&,
+ string& v,
+ optional<string>& e,
+ const location& l,
+ bool r)
+ {
+ if (r)
+ {
+ assert (e);
+ e = nullopt;
+ }
+ else
+ {
+ e = target::split_name (v, l);
+
+ if (!e && v != "buildscript")
+ {
+ e = "buildscript";
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const target_type buildscript::static_type
+ {
+ "buildscript",
+ &file::static_type,
+ &target_factory<buildscript>,
+ &buildscript_target_extension,
+ nullptr, /* default_extension */
+ &buildscript_target_pattern,
+ nullptr,
+ &file_search,
+ target_type::flag::none
};
const target_type doc::static_type
@@ -1487,7 +1834,7 @@ namespace build2
nullptr, /* pattern */ // Same as file.
&target_print_1_ext_verb, // Same as file.
&file_search,
- false
+ target_type::flag::none
};
const target_type legal::static_type
@@ -1500,7 +1847,7 @@ namespace build2
nullptr, /* pattern */ // Same as file.
&target_print_1_ext_verb, // Same as file.
&file_search,
- false
+ target_type::flag::none
};
const target_type man::static_type
@@ -1513,7 +1860,7 @@ namespace build2
nullptr,
&target_print_1_ext_verb, // Print extension even at verbosity level 0.
&file_search,
- false
+ target_type::flag::none
};
extern const char man1_ext[] = "1"; // VC14 rejects constexpr.
@@ -1528,7 +1875,7 @@ namespace build2
&target_pattern_fix<man1_ext>,
&target_print_0_ext_verb, // Fixed extension, no use printing.
&file_search,
- false
+ target_type::flag::none
};
static const char*
@@ -1577,6 +1924,6 @@ namespace build2
&manifest_target_pattern,
nullptr,
&file_search,
- false
+ target_type::flag::none
};
}