aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-05-15 12:11:30 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-05-27 08:28:34 +0200
commit02d902cb8e5e69b123fcdf170e5eeb9ca5605304 (patch)
tree29ed61127744a3fc1554bee96230342cae8f5972
parentba1fb800d20e7757cd8523a0793f63cff137c7cf (diff)
Amalgamation cutoff support
Now a project that disables amalgamation will not logically "see" an outer project even if it's physically inside its scope.
-rw-r--r--build2/b.cxx4
-rw-r--r--libbuild2/config/operation.cxx6
-rw-r--r--libbuild2/dump.cxx5
-rw-r--r--libbuild2/file.cxx150
-rw-r--r--libbuild2/file.hxx17
-rw-r--r--libbuild2/operation.cxx2
-rw-r--r--libbuild2/scope.hxx26
-rw-r--r--libbuild2/scope.ixx56
8 files changed, 200 insertions, 66 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 0ff4e5c..cdcfd59 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -1395,9 +1395,9 @@ main (int argc, char* argv[])
trace << " out_root: " << out_root;
trace << " src_root: " << src_root;
trace << " forwarded: " << (forwarded ? "true" : "false");
- if (auto l = rs.vars[ctx->var_amalgamation])
+ if (const dir_path* a = *rs.root_extra->amalgamation)
{
- trace << " amalgamation: " << cast<dir_path> (l);
+ trace << " amalgamation: " << *a;
trace << " strong scope: " << *rs.strong_scope ();
trace << " weak scope: " << *rs.weak_scope ();
}
diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx
index 37ff1a3..17eb99a 100644
--- a/libbuild2/config/operation.cxx
+++ b/libbuild2/config/operation.cxx
@@ -202,12 +202,10 @@ namespace build2
if (inherit)
{
- if (auto l = rs.vars[ctx.var_amalgamation])
+ if (const dir_path* a = *rs.root_extra->amalgamation)
{
- const dir_path& d (cast<dir_path> (l));
-
os << endl
- << "# Base configuration inherited from " << d << endl
+ << "# Base configuration inherited from " << *a << endl
<< "#" << endl;
}
}
diff --git a/libbuild2/dump.cxx b/libbuild2/dump.cxx
index e253988..8ee68b7 100644
--- a/libbuild2/dump.cxx
+++ b/libbuild2/dump.cxx
@@ -400,6 +400,11 @@ namespace build2
// Nested scopes of which we are an immediate parent.
//
+ // Note that because we use the logical (rather than physical) parent, we
+ // will be printing the logical scope hierarchy (i.e., a project with
+ // disabled amalgamation will be printed directly inside the global
+ // scope).
+ //
for (auto e (p.ctx.scopes.end ());
i != e && i->second.parent_scope () == &p; )
{
diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx
index 3e0d1c1..0bcb198 100644
--- a/libbuild2/file.cxx
+++ b/libbuild2/file.cxx
@@ -494,6 +494,7 @@ namespace build2
root.root_extra.reset (
new scope::root_extra_type {
+ nullopt /* amalgamation */,
a,
a ? alt_build_ext : std_build_ext,
a ? alt_build_dir : std_build_dir,
@@ -567,15 +568,29 @@ namespace build2
}
pair<value, bool>
- extract_variable (context& ctx, lexer& l, const variable& var)
+ extract_variable (context& ctx, lexer& l, const variable& var, size_t n)
{
const path_name& fn (l.name ());
try
{
token t (l.next ());
- token_type tt;
+ // Skip the requested number of lines.
+ //
+ while (--n != 0)
+ {
+ for (; t.type != token_type::eos; t = l.next ())
+ {
+ if (t.type == token_type::newline)
+ {
+ t = l.next ();
+ break;
+ }
+ }
+ }
+
+ token_type tt;
if (t.type != token_type::word || t.value != var.name ||
((tt = l.next ().type) != token_type::assign &&
tt != token_type::prepend &&
@@ -603,22 +618,23 @@ namespace build2
pair<value, bool>
extract_variable (context& ctx,
- istream& is,
- const path& bf,
- const variable& var)
+ istream& is, const path& bf,
+ const variable& var, size_t n)
{
path_name in (bf);
lexer l (is, in);
- return extract_variable (ctx, l, var);
+ return extract_variable (ctx, l, var, n);
}
pair<value, bool>
- extract_variable (context& ctx, const path& bf, const variable& var)
+ extract_variable (context& ctx,
+ const path& bf,
+ const variable& var, size_t n)
{
try
{
ifdstream ifs (bf);
- return extract_variable (ctx, ifs, bf, var);
+ return extract_variable (ctx, ifs, bf, var, n);
}
catch (const io_error& e)
{
@@ -715,8 +731,8 @@ namespace build2
auto p (extract_variable (ctx, f, *ctx.var_project));
if (!p.second)
- fail << "variable " << ctx.var_project->name << " expected "
- << "as a first line in " << f;
+ fail << "variable " << *ctx.var_project << " expected as a first "
+ << "line in " << f;
name = cast<project_name> (move (p.first));
}
@@ -817,44 +833,68 @@ namespace build2
context& ctx (rs.ctx);
- bool r (false);
-
const dir_path& out_root (rs.out_path ());
const dir_path& src_root (rs.src_path ());
+ path bf (exists (src_root, std_bootstrap_file, alt_bootstrap_file, altn));
+
+ if (rs.root_extra == nullptr)
{
- path f (exists (src_root, std_bootstrap_file, alt_bootstrap_file, altn));
+ // If nothing so far has indicated the naming, assume standard.
+ //
+ if (!altn)
+ altn = false;
- if (rs.root_extra == nullptr)
- {
- // If nothing so far has indicated the naming, assume standard.
- //
- if (!altn)
- altn = false;
+ setup_root_extra (rs, altn);
+ }
- setup_root_extra (rs, altn);
- }
+ bool r (true);
+ if (bf.empty ())
+ {
+ r = false;
+ }
+ // We assume that bootstrap out cannot load this file explicitly. It
+ // feels wrong to allow this since that makes the whole bootstrap
+ // process hard to reason about. But we may try to bootstrap the same
+ // root scope multiple time.
+ //
+ else if (rs.buildfiles.insert (bf).second)
+ {
+ // Deal with the empty amalgamation variable (which indicates that
+ // amalgamating this project is disabled). We go through all this
+ // trouble of extracting its value manually (and thus requiring its
+ // assignment, if any, to be the second line in bootstrap.build, after
+ // project assignment) in order to have the logical amalgamation view
+ // during bootstrap (note that the bootstrap pre hooks will still see
+ // physical amalgamation).
+ //
+ auto ap (extract_variable (ctx, bf, *ctx.var_amalgamation, 2));
+
+ if (ap.second && (ap.first.null || ap.first.empty ()))
+ rs.root_extra->amalgamation = nullptr;
- if (!f.empty ())
{
- // We assume that bootstrap out cannot load this file explicitly. It
- // feels wrong to allow this since that makes the whole bootstrap
- // process hard to reason about. But we may try to bootstrap the same
- // root scope multiple time.
- //
- if (rs.buildfiles.insert (f).second)
- {
- parser p (rs.ctx, load_stage::boot);
- source (p, rs, rs, f);
- }
- else
- l5 ([&]{trace << "skipping already sourced " << f;});
+ parser p (rs.ctx, load_stage::boot);
+ source (p, rs, rs, bf);
+ }
- r = true;
+ // Detect and diagnose the case where the amalgamation variable is not
+ // the second line.
+ //
+ if (!ap.second && rs.vars[ctx.var_amalgamation].defined ())
+ {
+ fail << "variable " << *ctx.var_amalgamation << " expected as a "
+ << "second line in " << bf;
}
}
+ else
+ {
+ // Here we assume amalgamation has been dealt with.
+ //
+ l5 ([&]{trace << "skipping already sourced " << bf;});
+ }
- // See if we are a part of an amalgamation. There are two key players: the
+ // Finish dealing with the amalgamation. There are two key players: the
// outer root scope which may already be present (i.e., we were loaded as
// part of an amalgamation) and the amalgamation variable that may or may
// not be set by the user (in bootstrap.build) or by an earlier call to
@@ -877,17 +917,20 @@ namespace build2
const dir_path& ad (ars->out_path ());
dir_path rd (ad.relative (out_root));
- // If we already have the amalgamation variable set, verify
- // that aroot matches its value.
+ // If we already have the amalgamation variable set, verify that aroot
+ // matches its value.
//
if (!rp.second)
{
+ /* @@ TMP
if (!v)
{
fail << out_root << " cannot be amalgamated" <<
info << "amalgamated by " << ad;
}
else
+ */
+ if (v)
{
const dir_path& vd (cast<dir_path> (v));
@@ -909,10 +952,9 @@ namespace build2
}
else if (rp.second)
{
- // If there is no outer root and the amalgamation variable
- // hasn't been set, then we need to check if any of the
- // outer directories is a project's out_root. If so, then
- // that's our amalgamation.
+ // If there is no outer root and the amalgamation variable hasn't been
+ // set, then we need to check if any of the outer directories is a
+ // project's out_root. If so, then that's our amalgamation.
//
optional<bool> altn;
const dir_path& ad (find_out_root (out_root.directory (), altn).first);
@@ -923,7 +965,18 @@ namespace build2
l5 ([&]{trace << out_root << " amalgamated as " << rd;});
v = move (rd);
}
+ //@@ else: the value will be NULL and amalgamation will be disabled.
+ // We could omit setting it in root_extra... But maybe this is
+ // correct: we don't want to load half of the project as
+ // amalgamated and the other half as not, would we now?
+
}
+ // @@ else if (v): shouldn't we try to bootstrap a project in the
+ // user-specified directory? Though this case is not
+ // used outside of some controlled cases (like module
+ // sidebuilds).
+
+ rs.root_extra->amalgamation = cast_null<dir_path> (v);
}
// See if we have any subprojects. In a sense, this is the other
@@ -1198,6 +1251,8 @@ namespace build2
// Check if we are strongly amalgamated by this outer root scope.
//
+ // Note that we won't end up here if we are not amalgamatable.
+ //
if (root.src_path ().sub (rs.src_path ()))
root.strong_ = rs.strong_scope (); // Itself or some outer scope.
}
@@ -1248,10 +1303,17 @@ namespace build2
rs.assign (ctx.var_forwarded) = true; // Only upgrade (see main()).
}
+ //@@ TODO: what if subproject has amalgamation disabled? Can we have a
+ // subproject that disables our attempt to amalgamate it (see
+ // amalgamatable() call below).
+
// Check if we strongly amalgamated this inner root scope.
//
- if (rs.src_path ().sub (root.src_path ()))
- rs.strong_ = root.strong_scope (); // Itself or some outer scope.
+ if (rs.amalgamatable ())
+ {
+ if (rs.src_path ().sub (root.src_path ()))
+ rs.strong_ = root.strong_scope (); // Itself or some outer scope.
+ }
// See if there are more inner roots.
//
diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx
index 8af408e..0123591 100644
--- a/libbuild2/file.hxx
+++ b/libbuild2/file.hxx
@@ -213,24 +213,27 @@ namespace build2
load_root (scope& root);
// Extract the specified variable value from a buildfile. It is expected to
- // be the first non-comment line and not to rely on any variable expansion
- // other than those from the global scope or any variable overrides. Return
- // an indication of whether the variable was found.
+ // be the n'th non-blank/comment line and not to rely on any variable
+ // expansions other than those from the global scope or any variable
+ // overrides. Return in second an indication of whether the variable was
+ // found.
//
LIBBUILD2_SYMEXPORT pair<value, bool>
- extract_variable (context&, const path&, const variable&);
+ extract_variable (context&, const path&, const variable&, size_t n = 1);
- // As above, but extract from a stream. The name argument is used for
+ // As above, but extract from a stream. The path argument is used for
// diagnostics.
//
LIBBUILD2_SYMEXPORT pair<value, bool>
- extract_variable (context&, istream&, const path& name, const variable&);
+ extract_variable (context&,
+ istream&, const path&,
+ const variable&, size_t = 1);
// As above, but extract from a lexer (this could be useful for extracting
// from stdin).
//
LIBBUILD2_SYMEXPORT pair<value, bool>
- extract_variable (context&, lexer&, const variable&);
+ extract_variable (context&, lexer&, const variable&, size_t = 1);
// Import has two phases: the first is triggered by the import directive in
// the buildfile. It will try to find and load the project. Failed that, it
diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx
index 5b549a4..d56e416 100644
--- a/libbuild2/operation.cxx
+++ b/libbuild2/operation.cxx
@@ -549,7 +549,7 @@ namespace build2
<< "url: " << cast_empty<string> (rs[ctx.var_project_url]) << endl
<< "src_root: " << cast<dir_path> (rs[ctx.var_src_root]) << endl
<< "out_root: " << cast<dir_path> (rs[ctx.var_out_root]) << endl
- << "amalgamation: " << cast_empty<dir_path> (rs[ctx.var_amalgamation]) << endl
+ << "amalgamation: " << (*rs.root_extra->amalgamation != nullptr ? **rs.root_extra->amalgamation : empty_dir_path) << endl
<< "subprojects: " << cast_empty<subprojects> (rs[ctx.var_subprojects]) << endl
<< "operations:"; print_ops (rs.root_extra->operations, ctx.operation_table); cout << endl
<< "meta-operations:"; print_ops (rs.root_extra->meta_operations, ctx.meta_operation_table); cout << endl;
diff --git a/libbuild2/scope.hxx b/libbuild2/scope.hxx
index e7c2db7..473dde8 100644
--- a/libbuild2/scope.hxx
+++ b/libbuild2/scope.hxx
@@ -46,18 +46,21 @@ namespace build2
const dir_path* src_path_ = nullptr;
bool
- root () const {return root_ == this;}
+ root () const;
- scope* parent_scope () {return parent_;}
- const scope* parent_scope () const {return parent_;}
+ // Note that the *_scope() functions reaturn "logical" parent/root/etc
+ // scopes, taking into account the project's var_amalgamation value.
+
+ scope* parent_scope ();
+ const scope* parent_scope () const;
// Root scope of this scope or NULL if this scope is not (yet) in any
// (known) project. Note that if the scope itself is root, then this
// function return this. To get to the outer root, query the root scope of
// the parent.
//
- scope* root_scope () {return root_;}
- const scope* root_scope () const {return root_;}
+ scope* root_scope ();
+ const scope* root_scope () const;
// Root scope of the outermost "strong" (source-based) amalgamation of
// this scope or NULL if this scope is not (yet) in any (known) project.
@@ -413,6 +416,11 @@ namespace build2
public:
struct root_extra_type
{
+ // This project's amalgamation (var_amalgamation value). Absent means it
+ // is not yet determined. NULL means amalgamation is disabled.
+ //
+ optional<const dir_path*> amalgamation;
+
bool altn; // True if using alternative build file/directory naming.
// Build file/directory naming scheme used by this project.
@@ -506,6 +514,14 @@ namespace build2
scope (context& c, bool global)
: ctx (c), vars (c, global), target_vars (c, global) {}
+ // Return true if this root scope can be amalgamated.
+ //
+ bool
+ amalgamatable () const;
+
+ // Note that these values represent "physical" scoping relationships not
+ // taking into account the project's var_amalgamation value.
+ //
scope* parent_;
scope* root_;
scope* strong_ = nullptr; // Only set on root scopes.
diff --git a/libbuild2/scope.ixx b/libbuild2/scope.ixx
index 9aecd48..a3a417f 100644
--- a/libbuild2/scope.ixx
+++ b/libbuild2/scope.ixx
@@ -5,9 +5,52 @@ namespace build2
{
// scope
//
+ inline bool scope::
+ root () const
+ {
+ return root_ == this;
+ }
+
+ inline bool scope::
+ amalgamatable () const
+ {
+ return (root_extra == nullptr ||
+ !root_extra->amalgamation ||
+ *root_extra->amalgamation != nullptr);
+ }
+
+ inline scope* scope::
+ parent_scope ()
+ {
+ // If this is a root scope and amalgamation is disabled, "jump" straight
+ // to the global scope.
+ //
+ return root () && !amalgamatable () ? &global_scope () : parent_;
+ }
+
+ inline const scope* scope::
+ parent_scope () const
+ {
+ return root () && !amalgamatable () ? &global_scope () : parent_;
+ }
+
+ inline scope* scope::
+ root_scope ()
+ {
+ return root_;
+ }
+
+ inline const scope* scope::
+ root_scope () const
+ {
+ return root_;
+ }
+
inline scope* scope::
strong_scope ()
{
+ // We naturally assume strong_ is not set for non-amalgamatable projects.
+ //
return root_ != nullptr
? root_->strong_ != nullptr ? root_->strong_ : root_
: nullptr;
@@ -26,7 +69,9 @@ namespace build2
{
scope* r (root_);
if (r != nullptr)
- for (; r->parent_->root_ != nullptr; r = r->parent_->root_) ;
+ for (;
+ r->amalgamatable () && r->parent_->root_ != nullptr;
+ r = r->parent_->root_) ;
return r;
}
@@ -35,7 +80,9 @@ namespace build2
{
const scope* r (root_);
if (r != nullptr)
- for (; r->parent_->root_ != nullptr; r = r->parent_->root_) ;
+ for (;
+ r->amalgamatable () && r->parent_->root_ != nullptr;
+ r = r->parent_->root_) ;
return r;
}
@@ -44,9 +91,12 @@ namespace build2
{
// Scan the parent root scope chain looking for this scope.
//
- for (const scope* pr (&r); (pr = pr->parent_->root_) != nullptr; )
+ for (const scope* pr (&r);
+ pr->amalgamatable () && (pr = pr->parent_->root_) != nullptr; )
+ {
if (pr == this)
return true;
+ }
return false;
}