aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-01-15 08:49:57 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-01-15 08:53:30 +0200
commit8a0ebb2b607c92b8c1bdab7e525da2b711b31565 (patch)
tree4556f7d33dd9fe22fe299b5466b3cb6a930ddfe1
parent05ee8c20d83c2f108aa71a65e19b7adff8ff9aa0 (diff)
Automatically alias unknown target types of imported targets
-rw-r--r--libbuild2/file.cxx91
-rw-r--r--libbuild2/file.hxx31
-rw-r--r--libbuild2/parser.cxx33
-rw-r--r--libbuild2/scope.cxx12
-rw-r--r--libbuild2/scope.hxx14
-rw-r--r--libbuild2/scope.ixx6
6 files changed, 140 insertions, 47 deletions
diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx
index 81a9dac..33f380d 100644
--- a/libbuild2/file.cxx
+++ b/libbuild2/file.cxx
@@ -2808,7 +2808,8 @@ namespace build2
l5 ([&]{trace << "importing " << es;});
// @@ Should we verify these are all unqualified names? Or maybe there
- // is a use-case for the export stub to return a qualified name?
+ // is a use-case for the export stub to return a qualified name? E.g.,
+ // re-export?
//
names v;
{
@@ -2893,6 +2894,27 @@ namespace build2
}
}
+ const target_type&
+ import_target_type (scope& root,
+ const scope& iroot, const string& n,
+ const location& l)
+ {
+ // NOTE: see similar code in parser::parse_define().
+
+ const target_type* tt (iroot.find_target_type (n));
+ if (tt == nullptr)
+ fail (l) << "unknown imported target type " << n << " in project "
+ << iroot;
+
+ auto p (root.root_extra->target_types.insert (*tt));
+
+ if (!p.second && &p.first.get () != tt)
+ fail (l) << "imported target type " << n << " already defined in project "
+ << root;
+
+ return *tt;
+ }
+
static names
import2_buildfile (context&, names&&, bool, const location&);
@@ -2901,7 +2923,7 @@ namespace build2
const string&, bool, const optional<string>&, bool,
const location&);
- pair<names, import_kind>
+ import_result<scope>
import (scope& base,
name tgt,
const optional<string>& ph2,
@@ -2931,7 +2953,10 @@ namespace build2
import_result<target> r (
import_direct (base, move (tgt), ph2, opt, metadata, loc));
- return make_pair (move (r.name), r.kind);
+ return import_result<scope> {
+ r.target != nullptr ? r.target->base_scope ().root_scope () : nullptr,
+ move (r.name),
+ r.kind};
}
pair<name, optional<dir_path>> r (
@@ -2947,6 +2972,7 @@ namespace build2
if (!r.second || r.second->empty ())
{
names ns;
+ const target* t (nullptr);
if (r.first.empty ())
{
@@ -2972,13 +2998,15 @@ namespace build2
// This is tricky: we only want the optional semantics for the
// fallback case.
//
- if (const target* t = import2 (ctx,
- base, ns,
- *ph2,
- opt && !r.second /* optional */,
- nullopt /* metadata */,
- false /* existing */,
- loc))
+ t = import2 (ctx,
+ base, ns,
+ *ph2,
+ opt && !r.second /* optional */,
+ nullopt /* metadata */,
+ false /* existing */,
+ loc);
+
+ if (t != nullptr)
{
// Note that here r.first was still project-qualified and we
// have no choice but to call as_name(). This shouldn't cause
@@ -2994,18 +3022,20 @@ namespace build2
}
}
- return make_pair (
+ return import_result<scope> {
+ t != nullptr ? t->base_scope ().root_scope () : nullptr,
move (ns),
- r.second.has_value () ? import_kind::adhoc : import_kind::fallback);
+ r.second.has_value () ? import_kind::adhoc : import_kind::fallback};
}
import_kind k (r.first.absolute ()
? import_kind::adhoc
: import_kind::normal);
- return make_pair (
- import_load (base.ctx, move (r), false /* metadata */, loc).first,
- k);
+ pair<names, const scope&> p (
+ import_load (base.ctx, move (r), false /* metadata */, loc));
+
+ return import_result<scope> {&p.second, move (p.first), k};
}
const target*
@@ -3339,6 +3369,8 @@ namespace build2
context& ctx (base.ctx);
assert (ctx.phase == run_phase::load);
+ scope& root (*base.root_scope ());
+
// Use the original target name as metadata key.
//
auto meta (metadata ? optional<string> (tgt.value) : nullopt);
@@ -3346,6 +3378,7 @@ namespace build2
names ns, rns;
import_kind k;
const target* pt (nullptr);
+ const scope* iroot (nullptr); // Imported root scope.
pair<name, optional<dir_path>> r (
import_search (new_value,
@@ -3400,6 +3433,8 @@ namespace build2
// It's a bit fuzzy in which cases we end up here. So for now we keep
// the original if it's absolute and call as_name() otherwise.
//
+ // @@ TODO: resolve iroot or assume target type should be known?
+ //
if (r.first.absolute ())
rns.push_back (r.first);
@@ -3409,14 +3444,30 @@ namespace build2
else
{
k = r.first.absolute () ? import_kind::adhoc : import_kind::normal;
- rns = ns = import_load (base.ctx, move (r), metadata, loc).first;
+
+ pair<names, const scope&> p (
+ import_load (base.ctx, move (r), metadata, loc));
+
+ rns = ns = move (p.first);
+ iroot = &p.second;
}
if (pt == nullptr)
{
+ // Import (more precisely, alias) the target type into this project
+ // if not known.
+ //
+ const target_type* tt (nullptr);
+ if (iroot != nullptr && !ns.empty ())
+ {
+ const name& n (ns.front ());
+ if (n.typed ())
+ tt = &import_target_type (root, *iroot, n.type, loc);
+ }
+
// Similar logic to perform's search(). Note: modifies ns.
//
- target_key tk (base.find_target_key (ns, loc));
+ target_key tk (base.find_target_key (ns, loc, tt));
pt = ctx.targets.find (tk, trace);
if (pt == nullptr)
fail (loc) << "unknown imported target " << tk;
@@ -3491,10 +3542,8 @@ namespace build2
//
if (const auto* e = cast_null<strings> (t.vars[pfx + ".environment"]))
{
- scope& rs (*base.root_scope ());
-
for (const string& v: *e)
- config::save_environment (rs, v);
+ config::save_environment (root, v);
}
}
else
@@ -3512,7 +3561,7 @@ namespace build2
string () /* phase2 */,
opt,
false /* metadata */,
- loc).first);
+ loc).name);
path p;
if (!r.empty ()) // Optional not found.
diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx
index 68c284b..36e4c00 100644
--- a/libbuild2/file.hxx
+++ b/libbuild2/file.hxx
@@ -371,7 +371,18 @@ namespace build2
//
enum class import_kind {adhoc, normal, fallback};
- LIBBUILD2_SYMEXPORT pair<names, import_kind>
+ template <typename T>
+ struct import_result
+ {
+ const T* target; // Note: T can be imported target or imported scope.
+ names name;
+ import_kind kind;
+ };
+
+ // Note that import_result<scope>::target may be NULL even if name is not
+ // empty (e.g, out of project target imported via phase 2).
+ //
+ LIBBUILD2_SYMEXPORT import_result<scope>
import (scope& base,
name,
const optional<string>& phase2,
@@ -409,14 +420,6 @@ namespace build2
//
// Note: cannot be used to import buildfile targets (use import_buildfile()
// instead).
- //
- template <typename T>
- struct import_result
- {
- const T* target;
- names name;
- import_kind kind;
- };
// Print import_direct<exe>() result either as a target for a normal import
// or as a process path for ad hoc and fallback imports. Normally used in
@@ -518,6 +521,16 @@ namespace build2
bool metadata,
const location&);
+ // Import (more precisely, alias as if using the `define =` syntax) the
+ // target type from imported project (iroot) into this project (root). If
+ // the target type with this name is already defined in this project, then
+ // make sure it is the same as in the imported project.
+ //
+ LIBBUILD2_SYMEXPORT const target_type&
+ import_target_type (scope& root,
+ const scope& iroot, const string&,
+ const location&);
+
// Suggest appropriate ways to import the specified target (as type and
// name) from the specified project.
//
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index baf404a..ebfb698 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -4226,12 +4226,15 @@ namespace build2
// import() will check the name, if required.
//
- names r (import (*scope_,
- move (n),
- ph2 ? ph2 : bf ? optional<string> (string ()) : nullopt,
- opt,
- meta,
- loc).first);
+ import_result<scope> ir (
+ import (*scope_,
+ move (n),
+ ph2 ? ph2 : bf ? optional<string> (string ()) : nullopt,
+ opt,
+ meta,
+ loc));
+
+ names& r (ir.name);
if (val != nullptr)
{
@@ -4242,6 +4245,19 @@ namespace build2
}
else
{
+ // Import (more precisely, alias) the target type into this project
+ // if not known.
+ //
+ // Note that if the result is ignored (val is NULL), then it's fair
+ // to assume this is not necessary.
+ //
+ if (const scope* iroot = ir.target)
+ {
+ const name& n (r.front ());
+ if (n.typed ())
+ import_target_type (*root_, *iroot, n.type, loc);
+ }
+
if (atype == type::assign) val->assign (move (r), var);
else if (atype == type::prepend) val->prepend (move (r), var);
else val->append (move (r), var);
@@ -4518,7 +4534,8 @@ namespace build2
<< "group-related attribute";
if (!root_->derive_target_type (move (n), *bt, fs).second)
- fail (nl) << "target type " << n << " already defined in this project";
+ fail (nl) << "target type " << n << " already defined in project "
+ << *root_;
next (t, tt); // Get newline.
}
@@ -4565,6 +4582,8 @@ namespace build2
// If we got here, then tn->dir is the scope and tn->value is the target
// type.
//
+ // NOTE: see similar code in import_target_type().
+ //
const target_type* tt (nullptr);
if (const scope* rs = ctx->scopes.find_out (tn->dir).root_scope ())
{
diff --git a/libbuild2/scope.cxx b/libbuild2/scope.cxx
index 4c9f301..2b47dd0 100644
--- a/libbuild2/scope.cxx
+++ b/libbuild2/scope.cxx
@@ -792,9 +792,11 @@ namespace build2
}
pair<const target_type&, optional<string>> scope::
- find_target_type (name& n, name& o, const location& loc) const
+ find_target_type (name& n, name& o,
+ const location& loc,
+ const target_type* tt) const
{
- auto r (find_target_type (n, loc));
+ auto r (find_target_type (n, loc, tt));
if (r.first == nullptr)
fail (loc) << "unknown target type " << n.type << " in " << n;
@@ -878,14 +880,16 @@ namespace build2
}
target_key scope::
- find_target_key (names& ns, const location& loc) const
+ find_target_key (names& ns,
+ const location& loc,
+ const target_type* tt) const
{
if (size_t n = ns.size ())
{
if (n == (ns[0].pair ? 2 : 1))
{
name dummy;
- return find_target_key (ns[0], n == 1 ? dummy : ns[1], loc);
+ return find_target_key (ns[0], n == 1 ? dummy : ns[1], loc, tt);
}
}
diff --git a/libbuild2/scope.hxx b/libbuild2/scope.hxx
index 3f6b0af..968727b 100644
--- a/libbuild2/scope.hxx
+++ b/libbuild2/scope.hxx
@@ -358,19 +358,25 @@ namespace build2
// the out directory.
//
pair<const target_type&, optional<string>>
- find_target_type (name&, name&, const location&) const;
+ find_target_type (name&, name&,
+ const location&,
+ const target_type* = nullptr) const;
// As above, but return the result as a target key (with its members
// shallow-pointing to processed parts in the two names).
//
target_key
- find_target_key (name&, name&, const location&) const;
+ find_target_key (name&, name&,
+ const location&,
+ const target_type* = nullptr) const;
// As above, but the names are passed as a vector. Issue appropriate
// diagnostics if the wrong number of names is passed.
//
target_key
- find_target_key (names&, const location&) const;
+ find_target_key (names&,
+ const location&,
+ const target_type* = nullptr) const;
// Similar to the find_target_type() but does not complete relative
// directories.
@@ -378,7 +384,7 @@ namespace build2
pair<const target_type&, optional<string>>
find_prerequisite_type (name&, name&,
const location&,
- const target_type* tt = nullptr) const;
+ const target_type* = nullptr) const;
// As above, but return a prerequisite key.
//
diff --git a/libbuild2/scope.ixx b/libbuild2/scope.ixx
index 4543f2c..5975c76 100644
--- a/libbuild2/scope.ixx
+++ b/libbuild2/scope.ixx
@@ -146,9 +146,11 @@ namespace build2
}
inline target_key scope::
- find_target_key (name& n, name& o, const location& loc) const
+ find_target_key (name& n, name& o,
+ const location& loc,
+ const target_type* tt) const
{
- auto p (find_target_type (n, o, loc));
+ auto p (find_target_type (n, o, loc, tt));
return target_key {
&p.first,
&n.dir,