aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-04-27 12:01:09 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-04-27 12:01:09 +0200
commit3813b05824fa2616b8ab9c18ed158c0c02265337 (patch)
tree35dc74b54ae0b6096e93fd76471faba140facb46
parentbff415fd8a787a63bcda2d9f95c8e086e40c1368 (diff)
Add support for build hooks
The following buildfiles are loaded (if present) at appropriate times from the out_root subdirectories of a project: build/bootstrap/pre-*.build # before loading bootstrap.build build/bootstrap/post-*.build # after loading bootstrap.build build/root/pre-*.build # before loading root.build build/root/post-*.build # after loading root.build
-rw-r--r--build2/b.cxx32
-rw-r--r--build2/config/operation.cxx5
-rw-r--r--build2/file.cxx178
-rw-r--r--build2/file.hxx34
-rw-r--r--build2/file.ixx15
-rw-r--r--build2/operation.cxx4
-rw-r--r--tests/hooks/buildfile5
-rw-r--r--tests/hooks/testscript30
8 files changed, 233 insertions, 70 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index e427237..6921a97 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -822,9 +822,9 @@ main (int argc, char* argv[])
scope& rs (
create_root (*scope::global_, out_root, src_root)->second);
- bool bootstrapped (build2::bootstrapped (rs));
+ bool bstrapped (bootstrapped (rs));
- if (!bootstrapped)
+ if (!bstrapped)
{
bootstrap_out (rs);
@@ -860,22 +860,27 @@ main (int argc, char* argv[])
v = src_root;
}
- setup_root (rs);
+ setup_root (rs, forwarded);
// Now that we have src_root, load the src_root bootstrap file,
// if there is one.
//
- bootstrapped = bootstrap_src (rs);
+ bootstrap_pre (rs);
+ bootstrap_src (rs);
+ // bootstrap_post() delayed until after create_bootstrap_outer().
}
- else if (src_root.empty ())
- src_root = rs.src_path ();
+ else
+ {
+ if (src_root.empty ())
+ src_root = rs.src_path ();
- // Note that we only "upgrade" the forwarded value since the same
- // project root can be arrived at via multiple paths (think command
- // line and import).
- //
- if (forwarded)
- rs.assign (var_forwarded) = true;
+ // Note that we only "upgrade" the forwarded value since the same
+ // project root can be arrived at via multiple paths (think
+ // command line and import).
+ //
+ if (forwarded)
+ rs.assign (var_forwarded) = true;
+ }
// At this stage we should have both roots and out_base figured
// out. If src_base is still undetermined, calculate it.
@@ -917,6 +922,9 @@ main (int argc, char* argv[])
//
create_bootstrap_outer (rs);
+ if (!bstrapped)
+ bootstrap_post (rs);
+
// The src bootstrap should have loaded all the modules that
// may add new meta/operations. So at this stage they should
// all be known. We store the combined action id in uint8_t;
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
index 1bd5d4d..cdf2f9a 100644
--- a/build2/config/operation.cxx
+++ b/build2/config/operation.cxx
@@ -641,8 +641,11 @@ namespace build2
// Clean up the directories.
//
+ // Note: try to remove the root/ hooks directory if it is empty.
+ //
+ r = rmdir (out_root / root_dir, 2) || r;
r = rmdir (out_root / bootstrap_dir, 2) || r;
- r = rmdir (out_root / build_dir, 2) || r;
+ r = rmdir (out_root / build_dir, 2) || r;
switch (rmdir (out_root))
{
diff --git a/build2/file.cxx b/build2/file.cxx
index ae24b77..7cc6b5e 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -25,6 +25,7 @@ using namespace butl;
namespace build2
{
const dir_path build_dir ("build");
+ const dir_path root_dir (dir_path (build_dir) /= "root");
const dir_path bootstrap_dir (dir_path (build_dir) /= "bootstrap");
const path root_file (build_dir / "root.build");
@@ -145,6 +146,50 @@ namespace build2
return true;
}
+ // Source (once) pre-*.build (pre is true) or post-*.build (otherwise) hooks
+ // from the specified subdirectory (build/bootstrap/ or build/root/) of
+ // out_root/.
+ //
+ void
+ source_hooks (scope& root, const dir_path& sd, bool pre)
+ {
+ dir_path d (root.out_path () / sd);
+
+ if (!exists (d))
+ return;
+
+ // While we could have used the wildcard pattern matching functionality,
+ // our needs are pretty basic and performance is quite important, so let's
+ // handle this ourselves.
+ //
+ for (const dir_entry& de: dir_iterator (d))
+ {
+ // If this is a link, then type() will try to stat() it. And if the link
+ // is dangling or points to something inaccessible, it will fail. So
+ // let's first check that the name matches and only then check the type.
+ //
+ const path& n (de.path ());
+
+ if (n.string ().compare (0, pre ? 4 : 5, pre ? "pre-" : "post-") != 0 ||
+ n.extension () != "build")
+ continue;
+
+ path f (d / n);
+
+ try
+ {
+ if (de.type () != entry_type::regular)
+ continue;
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to read buildfile " << f << ": " << e;
+ }
+
+ source_once (root, root, f);
+ }
+ }
+
scope_map::iterator
create_root (scope& l, const dir_path& out_root, const dir_path& src_root)
{
@@ -215,7 +260,7 @@ namespace build2
}
void
- setup_root (scope& s)
+ setup_root (scope& s, bool forwarded)
{
// The caller must have made sure src_root is set on this scope.
//
@@ -227,6 +272,8 @@ namespace build2
s.src_path_ = &d;
else
assert (s.src_path_ == &d);
+
+ s.assign (var_forwarded) = forwarded;
}
scope&
@@ -298,7 +345,7 @@ namespace build2
// Switch to the new root scope.
//
if (rs != &root)
- load_root_pre (*rs); // Load new root(s) recursively.
+ load_root (*rs); // Load new root(s) recursively.
// Now we can figure out src_base and finish setting the scope.
//
@@ -346,7 +393,8 @@ namespace build2
//@@ TODO: if bootstrap files can source other bootstrap files
// (the way to express dependecies), then we need a way to
// prevent multiple sourcing. We handle it here but we still
- // need something like source_once (once [scope] source).
+ // need something like source_once (once [scope] source) in
+ // buildfiles.
//
source_once (root, root, bf);
}
@@ -760,10 +808,10 @@ namespace build2
}
// Return true if the inner/outer project (identified by out/src_root) of
- // the 'origin' project (identified by root) should be forwarded.
+ // the 'origin' project (identified by orig) should be forwarded.
//
static inline bool
- forwarded (const scope& root,
+ forwarded (const scope& orig,
const dir_path& out_root,
const dir_path& src_root)
{
@@ -776,7 +824,7 @@ namespace build2
// 3. Inner/outer out-root.build exists in src_root and refers out_root.
//
return (out_root != src_root &&
- cast_false<bool> (root.vars[var_forwarded]) &&
+ cast_false<bool> (orig.vars[var_forwarded]) &&
bootstrap_fwd (src_root) == out_root);
}
@@ -804,7 +852,9 @@ namespace build2
//
scope& rs (create_root (root, out_root, dir_path ())->second);
- if (!bootstrapped (rs))
+ bool bstrapped (bootstrapped (rs));
+
+ if (!bstrapped)
{
bootstrap_out (rs); // #3 happens here (or it can be #1).
@@ -822,15 +872,22 @@ namespace build2
}
}
- setup_root (rs);
+ setup_root (rs, forwarded (root, out_root, v.as<dir_path> ()));
+ bootstrap_pre (rs);
bootstrap_src (rs);
+ // bootstrap_post() delayed until after create_bootstrap_outer().
+ }
+ else
+ {
+ if (forwarded (root, rs.out_path (), rs.src_path ()))
+ rs.assign (var_forwarded) = true; // Only upgrade (see main()).
}
-
- if (forwarded (root, rs.out_path (), rs.src_path ()))
- rs.assign (var_forwarded) = true;
create_bootstrap_outer (rs);
+ if (!bstrapped)
+ bootstrap_post (rs);
+
// Check if we are strongly amalgamated by this outer root scope.
//
if (root.src_path ().sub (rs.src_path ()))
@@ -864,8 +921,15 @@ namespace build2
? out_root
: (root.src_path () / p.second);
- setup_root (rs);
+ setup_root (rs, forwarded (root, out_root, v.as<dir_path> ()));
+ bootstrap_pre (rs);
bootstrap_src (rs);
+ bootstrap_post (rs);
+ }
+ else
+ {
+ if (forwarded (root, rs.out_path (), rs.src_path ()))
+ rs.assign (var_forwarded) = true; // Only upgrade (see main()).
}
// Check if we strongly amalgamated this inner root scope.
@@ -873,9 +937,6 @@ namespace build2
if (rs.src_path ().sub (root.src_path ()))
rs.strong_ = root.strong_scope (); // Itself or some outer scope.
- if (forwarded (root, rs.out_path (), rs.src_path ()))
- rs.assign (var_forwarded) = true;
-
// See if there are more inner roots.
//
return create_bootstrap_inner (rs, out_base);
@@ -886,14 +947,22 @@ namespace build2
}
void
- load_root_pre (scope& root)
+ load_root (scope& root)
{
- tracer trace ("root_pre");
+ tracer trace ("load_root");
+
+ // As an optimization, check if we have already loaded root.build. If
+ // that's the case, then we have already been called for this project.
+ //
+ path bf (root.src_path () / root_file);
+
+ if (root.buildfiles.find (bf) != root.buildfiles.end ())
+ return;
// First load outer roots, if any.
//
if (scope* rs = root.parent_scope ()->root_scope ())
- load_root_pre (*rs);
+ load_root (*rs);
// Finish off loading bootstrapped modules.
//
@@ -913,12 +982,17 @@ namespace build2
load_module (root, root, p.first, s.loc);
}
- // Load root.build.
+ // Load hooks and root.build.
//
- path bf (root.src_path () / root_file);
-
- if (exists (bf))
- source_once (root, root, bf);
+ // We can load the pre hooks before finishing off loading the bootstrapped
+ // modules (which, in case of config would load config.build) or after and
+ // one can come up with a plausible use-case for either approach. Note,
+ // however, that one can probably achieve adequate pre-modules behavior
+ // with a post-bootstrap hook.
+ //
+ source_hooks (root, root_dir, true /* pre */);
+ if (exists (bf)) source_once (root, root, bf);
+ source_hooks (root, root_dir, false /* pre */);
}
scope&
@@ -936,16 +1010,20 @@ namespace build2
if (!bootstrapped (rs))
{
bootstrap_out (rs);
- setup_root (rs);
+ setup_root (rs, forwarded);
+ bootstrap_pre (rs);
bootstrap_src (rs);
+ bootstrap_post (rs);
+ }
+ else
+ {
+ if (forwarded)
+ rs.assign (var_forwarded) = true; // Only upgrade (see main()).
}
-
- if (forwarded)
- rs.assign (var_forwarded) = true;
if (load)
{
- load_root_pre (rs);
+ load_root (rs);
setup_base (i, out_root, src_root); // Setup as base.
}
@@ -1141,15 +1219,20 @@ namespace build2
for (const scope* proot (nullptr); ; proot = root)
{
+ bool top (proot == nullptr);
+
root = &create_root (iroot, out_root, src_root)->second;
- if (!bootstrapped (*root))
+ bool bstrapped (bootstrapped (*root));
+
+ if (!bstrapped)
{
bootstrap_out (*root);
// Check that the bootstrap process set src_root.
//
- if (auto l = root->vars[*var_src_root])
+ auto l (root->vars[*var_src_root]);
+ if (l)
{
const dir_path& p (cast<dir_path> (l));
@@ -1161,16 +1244,30 @@ namespace build2
fail (loc) << "unable to determine src_root for imported " << proj <<
info << "consider configuring " << out_root;
- setup_root (*root);
+ setup_root (
+ *root,
+ top ? fwd : forwarded (*proot, out_root, l->as<dir_path> ()));
+ bootstrap_pre (*root);
bootstrap_src (*root);
+ if (!top)
+ bootstrap_post (*root);
}
- else if (src_root.empty ())
- src_root = root->src_path ();
+ else
+ {
+ if (src_root.empty ())
+ src_root = root->src_path ();
- if (proot == nullptr
- ? fwd
- : forwarded (*proot, root->out_path (), root->src_path ()))
- root->assign (var_forwarded) = true;
+ if (top ? fwd : forwarded (*proot, out_root, src_root))
+ root->assign (var_forwarded) = true; // Only upgrade (see main()).
+ }
+
+ if (top)
+ {
+ create_bootstrap_outer (*root);
+
+ if (!bstrapped)
+ bootstrap_post (*root);
+ }
// Now we know this project's name as well as all its subprojects.
//
@@ -1194,14 +1291,9 @@ namespace build2
fail (loc) << out_root << " is not out_root for " << proj;
}
- // Bootstrap outer roots if any. Loading will be done by load_root_pre()
- // below.
- //
- create_bootstrap_outer (*root);
-
// Load the imported root scope.
//
- load_root_pre (*root);
+ load_root (*root);
// Create a temporary scope so that the export stub does not mess
// up any of our variables.
diff --git a/build2/file.hxx b/build2/file.hxx
index 74f2f64..23362c8 100644
--- a/build2/file.hxx
+++ b/build2/file.hxx
@@ -24,8 +24,9 @@ namespace build2
ostream&
operator<< (ostream&, const subprojects&); // Print as name@dir sequence.
- extern const dir_path build_dir; // build
- extern const dir_path bootstrap_dir; // build/bootstrap
+ extern const dir_path build_dir; // build/
+ extern const dir_path root_dir; // build/root/
+ extern const dir_path bootstrap_dir; // build/bootstrap/
extern const path root_file; // build/root.build
extern const path bootstrap_file; // build/bootstrap.build
@@ -79,11 +80,11 @@ namespace build2
scope_map::iterator
create_root (scope&, const dir_path& out_root, const dir_path& src_root);
- // Setup root scope. Note that it assumes the src_root variable
- // has already been set.
+ // Setup root scope. Note that it assumes the src_root variable has already
+ // been set.
//
void
- setup_root (scope&);
+ setup_root (scope&, bool forwarded);
// Setup the base scope (set *_base variables, etc).
//
@@ -128,8 +129,8 @@ namespace build2
void
bootstrap_out (scope& root);
- // Bootstrap the project's root scope, the src part. Return true if
- // we loaded anything (which confirms the src_root is not bogus).
+ // Bootstrap the project's root scope, the src part. Return true if we
+ // loaded anything (which confirms the src_root is not bogus).
//
bool
bootstrap_src (scope& root);
@@ -144,8 +145,17 @@ namespace build2
bool
bootstrapped (scope& root);
+ // Execute pre/post-bootstrap hooks. Similar to bootstrap_out/sr(), should
+ // only be called once per project bootstrap.
+ //
+ void
+ bootstrap_pre (scope& root);
+
+ void
+ bootstrap_post (scope& root);
+
// Create and bootstrap outer root scopes, if any. Loading is done by
- // load_root_pre().
+ // load_root().
//
void
create_bootstrap_outer (scope& root);
@@ -153,17 +163,17 @@ namespace build2
// Create and bootstrap inner root scopes between root and base, if any. If
// out_base is empty, then bootstrap all the way in. Return the innermost
// created root scope or root if none were created. Note: loading is done by
- // load_root_pre().
+ // load_root().
//
scope&
create_bootstrap_inner (scope& root, const dir_path& out_base = dir_path ());
- // Load project's root[-pre].build unless already loaded. Also
- // make sure all outer root scopes are loaded prior to loading
+ // Load project's root.build (and root pre/post hooks) unless already
+ // loaded. Also make sure all outer root scopes are loaded prior to loading
// this root scope.
//
void
- load_root_pre (scope& root);
+ 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
diff --git a/build2/file.ixx b/build2/file.ixx
index 15fa8dc..ef944cf 100644
--- a/build2/file.ixx
+++ b/build2/file.ixx
@@ -26,4 +26,19 @@ namespace build2
assert (phase == run_phase::match || phase == run_phase::execute);
return import (pk, true);
}
+
+ void
+ source_hooks (scope&, const dir_path&, bool);
+
+ inline void
+ bootstrap_pre (scope& root)
+ {
+ source_hooks (root, bootstrap_dir, true /* pre */);
+ }
+
+ inline void
+ bootstrap_post (scope& root)
+ {
+ source_hooks (root, bootstrap_dir, false /* pre */);
+ }
}
diff --git a/build2/operation.cxx b/build2/operation.cxx
index dabebcc..10ae6e7 100644
--- a/build2/operation.cxx
+++ b/build2/operation.cxx
@@ -72,9 +72,9 @@ namespace build2
const dir_path& src_base,
const location&)
{
- // Load project's root[-pre].build.
+ // Load project's root.build.
//
- load_root_pre (root);
+ load_root (root);
// Create the base scope. Note that its existence doesn't mean it was
// already setup as a base scope; it can be the same as root.
diff --git a/tests/hooks/buildfile b/tests/hooks/buildfile
new file mode 100644
index 0000000..c951d56
--- /dev/null
+++ b/tests/hooks/buildfile
@@ -0,0 +1,5 @@
+# file : tests/hooks/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: test{testscript} $b
diff --git a/tests/hooks/testscript b/tests/hooks/testscript
new file mode 100644
index 0000000..7ad8028
--- /dev/null
+++ b/tests/hooks/testscript
@@ -0,0 +1,30 @@
+# file : tests/hooks/testscript
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+.include ../common.test
+
++cat <'print bootstrap' >+build/bootstrap.build
++cat <'print root' >=build/root.build
+
++mkdir build/bootstrap
++cat <'print pre-bootstrap' >=build/bootstrap/pre-bootstrap.build
++cat <'print post-bootstrap' >=build/bootstrap/post-bootstrap.build
+
++mkdir build/root
++cat <'print pre-root' >=build/root/pre-root.build
++cat <'print post-root' >=build/root/post-root.build
+
+: basics
+:
+$* <<EOI >>EOO
+print buildfile
+EOI
+pre-bootstrap
+bootstrap
+post-bootstrap
+pre-root
+root
+post-root
+buildfile
+EOO