aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-24 08:53:06 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-24 08:53:06 +0200
commita94dcda7f00b10cb22b5f2138b1c29bcfbe7de37 (patch)
treec4ca2c4b2ea08285774569283120233a03aa2cb3 /build
parenteaaa82bd9c1e24a83dcea3857f5fd75d0dfb6de5 (diff)
Make meta-operations control build loop; add disfigure skeleton
Diffstat (limited to 'build')
-rw-r--r--build/b.cxx289
-rw-r--r--build/bootstrap.build2
-rw-r--r--build/buildfile2
-rw-r--r--build/config/module2
-rw-r--r--build/config/module.cxx12
-rw-r--r--build/config/operation.cxx56
-rw-r--r--build/file36
-rw-r--r--build/file.cxx68
-rw-r--r--build/file.ixx12
-rw-r--r--build/module4
-rw-r--r--build/operation67
-rw-r--r--build/operation.cxx140
-rw-r--r--build/parser2
-rw-r--r--build/parser.cxx11
-rw-r--r--build/scope7
-rw-r--r--build/string-table3
-rw-r--r--build/target70
-rw-r--r--build/target.cxx10
18 files changed, 522 insertions, 271 deletions
diff --git a/build/b.cxx b/build/b.cxx
index 93a199e..96f6106 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -10,11 +10,8 @@
#include <sys/types.h> // uid_t
#include <pwd.h> // struct passwd, getpwuid()
-#include <vector>
-#include <cassert>
-#include <fstream>
#include <sstream>
-#include <iterator> // make_move_iterator()
+#include <cassert>
#include <iostream> //@@ TMP, for dump()
#include <typeinfo>
#include <system_error>
@@ -27,6 +24,7 @@
#include <build/target>
#include <build/prerequisite>
#include <build/rule>
+#include <build/file>
#include <build/module>
#include <build/algorithm>
#include <build/process>
@@ -34,9 +32,6 @@
#include <build/context>
#include <build/utility>
#include <build/filesystem>
-#include <build/dump>
-
-#include <build/lexer>
#include <build/parser>
using namespace std;
@@ -110,31 +105,9 @@ namespace build
return path ();
}
- void
- load (const path& buildfile, scope& root, scope& base)
- {
- ifstream ifs (buildfile.string ());
- if (!ifs.is_open ())
- fail << "unable to open " << buildfile;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
- parser p;
-
- try
- {
- p.parse_buildfile (ifs, buildfile, root, base);
- }
- catch (const std::ios_base::failure&)
- {
- fail << "failed to read from " << buildfile;
- }
- }
-
- void
+ static void
bootstrap_out (scope& root)
{
- tracer trace ("bootstrap_out");
-
path bf (root.path () / path ("build/bootstrap/src-root.build"));
if (!file_exists (bf))
@@ -142,39 +115,28 @@ namespace build
//@@ TODO: if bootstrap files can source other bootstrap files
// (the way to express dependecies), then we need a way to
- // prevent multiple sourcing.
+ // prevent multiple sourcing. We handle it here but we still
+ // need something like source_once (once [scope] source).
//
-
- level4 ([&]{trace << "loading " << bf;});
- load (bf, root, root);
+ source_once (bf, root, root);
}
- void
- bootstrap_src (scope& root, const path& src_root)
+ static void
+ bootstrap_src (scope& root)
{
tracer trace ("bootstrap_src");
- path bf (src_root / path ("build/bootstrap.build"));
+ path bf (root.src_path () / path ("build/bootstrap.build"));
if (!file_exists (bf))
return;
- level4 ([&]{trace << "loading " << bf;});
- load (bf, root, root);
- }
-
- void
- root_pre (scope& root, const path& src_root)
- {
- tracer trace ("root_pre");
-
- path bf (src_root / path ("build/root.build"));
-
- if (!file_exists (bf))
- return;
-
- level4 ([&]{trace << "loading " << bf;});
- load (bf, root, root);
+ // 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.
+ //
+ source_once (bf, root, root);
}
}
@@ -204,7 +166,7 @@ main (int argc, char* argv[])
// Register modules.
//
- modules["config"] = &config::load;
+ modules["config"] = &config::init;
// Register target types.
//
@@ -303,7 +265,7 @@ main (int argc, char* argv[])
}
istringstream is (s);
- is.exceptions (ifstream::failbit | ifstream::badbit);
+ is.exceptions (istringstream::failbit | istringstream::badbit);
parser p;
try
@@ -318,8 +280,6 @@ main (int argc, char* argv[])
level4 ([&]{trace << "buildspec: " << bspec;});
- // Load all the buildfiles.
- //
if (bspec.empty ())
bspec.push_back (metaopspec ()); // Default meta-operation.
@@ -335,9 +295,7 @@ main (int argc, char* argv[])
{
const location l ("<buildspec>", 1, 0); //@@ TODO
- if (os.empty ())
- // Default target: dir{}.
- //
+ if (os.empty ()) // Default target: dir{}.
os.push_back (targetspec (name ("dir", path (), string ())));
operation_id oid (0); // Not yet translated.
@@ -349,7 +307,7 @@ main (int argc, char* argv[])
// parallelism). But multiple targets in an operation batch
// can be done in parallel.
//
- vector<reference_wrapper<target>> tgs;
+ action_targets tgs;
tgs.reserve (os.size ());
for (targetspec& ts: os)
@@ -442,27 +400,26 @@ main (int argc, char* argv[])
// might already have done this as a result of one of the
// preceding target processing.
//
- auto rsp (scopes.insert (out_root));
- scope& rs (rsp.first);
+ scope& rs (scopes[out_root]);
- if (rsp.second)
+ // Enter built-in meta-operation and operation names. Note that
+ // the order of registration should match the id constants; see
+ // <operation> for details. Loading of modules (via the src_root
+ // bootstrap; see below) can result in additional names being
+ // added.
+ //
+ if (rs.meta_operations.empty ())
{
- // Enter built-in meta-operation and operation names. Note that
- // the order of registration should match the id constants; see
- // <operation> for details. Loading of modules (via the src_root
- // bootstrap; see below) can result in additional names being
- // added.
- //
- rs.meta_operations.insert (perform);
-
- rs.operations.insert (default_);
- rs.operations.insert (update);
- rs.operations.insert (clean);
+ assert (rs.meta_operations.insert (perform) == perform_id);
- rs.variables["out_root"] = out_root;
- bootstrap_out (rs);
+ assert (rs.operations.insert (default_) == default_id);
+ assert (rs.operations.insert (update) == update_id);
+ assert (rs.operations.insert (clean) == clean_id);
}
+ rs.variables["out_root"] = out_root;
+ bootstrap_out (rs);
+
// See if the bootstrap process set src_root.
//
{
@@ -507,6 +464,8 @@ main (int argc, char* argv[])
v = src_root;
}
+
+ rs.src_path_ = &v.as<const path&> ();
}
// At this stage we should have both roots and out_base figured
@@ -516,10 +475,9 @@ main (int argc, char* argv[])
src_base = src_root / out_base.leaf (out_root);
// Now that we have src_root, load the src_root bootstrap file,
- // if there is one. Again, we might have already done that.
+ // if there is one.
//
- if (rsp.second)
- bootstrap_src (rs, src_root);
+ bootstrap_src (rs);
// The src bootstrap should have loaded all the modules that
// may add new meta/operations. So at this stage they should
@@ -581,14 +539,14 @@ main (int argc, char* argv[])
//
if (mid == 0)
{
- //@@ meta-operation batch_pre
- //
-
mid = m;
- mif = &rs.meta_operations[m].get ();
+ mif = &rs.meta_operations[mid].get ();
level4 ([&]{trace << "start meta-operation batch " << mif->name
<< ", id " << static_cast<uint16_t> (mid);});
+
+ if (mif->meta_operation_pre != nullptr)
+ mif->meta_operation_pre ();
}
//
// Otherwise, check that all the targets in a meta-operation
@@ -607,22 +565,32 @@ main (int argc, char* argv[])
//
if (oid == 0)
{
- //@@ operation batch_pre; translate operation (pass
- // default_id for 0).
-
if (o == 0)
- o = update_id; // @@ TMP; if no batch_pre
+ o = default_id;
- oid = o;
oif = &rs.operations[o].get ();
+ level4 ([&]{trace << "start operation batch " << oif->name
+ << ", id " << static_cast<uint16_t> (o);});
+
+ // Allow the meta-operation to translate the operation.
+ //
+ if (mif->operation_pre != nullptr)
+ oid = mif->operation_pre (o);
+ else // Otherwise translate default to update.
+ oid = (o == default_id ? update_id : o);
+
+ if (o != oid)
+ {
+ oif = &rs.operations[oid].get ();
+ level4 ([&]{trace << "operation translated to " << oif->name
+ << ", id " << static_cast<uint16_t> (oid);});
+ }
+
act = action (mid, oid);
current_mode = oif->mode;
- current_rules = &rules[o];
-
- level4 ([&]{trace << "start operation batch " << oif->name
- << ", id " << static_cast<uint16_t> (oid);});
+ current_rules = &rules[oid];
}
//
// Similar to meta-operations, check that all the targets in
@@ -637,8 +605,6 @@ main (int argc, char* argv[])
}
}
- //@@ target pre_load; may request skipping loading
-
if (verb >= 4)
{
trace << "target " << tn << ':';
@@ -648,56 +614,19 @@ main (int argc, char* argv[])
trace << " src_root: " << src_root.string ();
}
- // Load project's root[-pre].build.
- //
- if (rsp.second)
- root_pre (rs, src_root);
-
- // Create the base scope. Note that its existence doesn't
- // mean it was already processed as a base scope; it can
- // be the same as root.
- //
- scope& bs (scopes[out_base]);
-
- bs.variables["out_base"] = out_base;
- bs.variables["src_base"] = src_base;
-
- // Parse the buildfile.
- //
path bf (src_base / path ("buildfile"));
- // Check if this buildfile has already been loaded.
+ // If we were guessing src_base, check that the buildfile
+ // exists and if not, issue more detailed diagnostics.
//
- if (rs.buildfiles.insert (bf).second)
- {
- level4 ([&]{trace << "loading " << bf;});
-
- ifstream ifs (bf.string ());
- if (!ifs.is_open ())
- {
- diag_record dr;
- dr << fail << "unable to open " << bf;
- if (guessing)
- dr << info << "consider explicitly specifying src_base "
- << "for " << tn;
- }
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
- parser p;
+ if (guessing && !file_exists (bf))
+ fail << bf << " does not exist"
+ << info << "consider explicitly specifying src_base "
+ << "for " << tn;
- try
- {
- p.parse_buildfile (ifs, bf, rs, bs);
- }
- catch (const std::ios_base::failure&)
- {
- fail << "failed to read from " << bf;
- }
- }
- else
- level4 ([&]{trace << "skipping already loaded " << bf;});
-
- //@@ target post_load
+ // Load the buildfile.
+ //
+ mif->load (bf, rs, out_base, src_base, l);
// Next resolve and match the target. We don't want to start
// building before we know how to for all the targets in this
@@ -720,79 +649,16 @@ main (int argc, char* argv[])
d.normalize ();
- target_set::key tk {ti, &d, &tn.value, &e};
- auto i (targets.find (tk, trace));
- if (i == targets.end ())
- fail (l) << "unknown target " << tk;
-
- target& t (**i);
-
- //@@ dump
-
- level4 ([&]{trace << "matching target " << t;});
- match (act, t);
-
- //@@ dump
-
- tgs.push_back (t);
+ mif->match (act, target_key {ti, &d, &tn.value, &e}, l, tgs);
}
}
- // Now build collecting postponed targets (to be re-examined).
+ // Now execute the action on the list of targets.
//
- vector<reference_wrapper<target>> psp;
-
- for (target& t: tgs)
- {
- level4 ([&]{trace << "executing target " << t;});
+ mif->execute (act, tgs);
- switch (execute (act, t))
- {
- case target_state::postponed:
- {
- info << "target " << t << " is postponed";
- psp.push_back (t);
- break;
- }
- case target_state::unchanged:
- {
- info << "target " << t << " is unchanged";
- break;
- }
- case target_state::changed:
- break;
- case target_state::failed:
- //@@ This could probably happen in a parallel build.
- default:
- assert (false);
- }
- }
-
- // Re-examine postponed targets.
- //
- for (target& t: psp)
- {
- switch (t.state)
- {
- case target_state::postponed:
- {
- info << "unable to execute target " << t << " at this time";
- break;
- }
- case target_state::unchanged:
- {
- info << "target " << t << " is unchanged";
- break;
- }
- case target_state::unknown: // Assume something was done to it.
- case target_state::changed:
- break;
- case target_state::failed:
- //@@ This could probably happen in a parallel build.
- default:
- assert (false);
- }
- }
+ if (mif->operation_post != nullptr)
+ mif->operation_post (oid);
level4 ([&]{trace << "end operation batch " << oif->name
<< ", id " << static_cast<uint16_t> (oid);});
@@ -800,10 +666,11 @@ main (int argc, char* argv[])
//@@ operation batch_post
}
+ if (mif->meta_operation_post != nullptr)
+ mif->meta_operation_post ();
+
level4 ([&]{trace << "end meta-operation batch " << mif->name
<< ", id " << static_cast<uint16_t> (mid);});
-
- //@@ meta-operation batch_post
}
}
catch (const failed&)
diff --git a/build/bootstrap.build b/build/bootstrap.build
index 2e849c0..492b6c0 100644
--- a/build/bootstrap.build
+++ b/build/bootstrap.build
@@ -2,4 +2,4 @@ print bootstrap.build
project_name = build2
-load config
+using config
diff --git a/build/buildfile b/build/buildfile
index 6752a6e..93f9179 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -2,5 +2,5 @@ cxx = cxx/{target rule}
config = config/{operation module}
exe{b1}: cxx{b algorithm parser lexer name operation spec scope variable \
- target prerequisite rule module native context search diagnostics \
+ target prerequisite rule file module native context search diagnostics \
process timestamp path utility filesystem dump $config $cxx}
diff --git a/build/config/module b/build/config/module
index 5a9d362..c7921df 100644
--- a/build/config/module
+++ b/build/config/module
@@ -12,7 +12,7 @@ namespace build
namespace config
{
void
- load (scope&, scope&, const location&);
+ init (scope&, scope&, const location&);
}
}
diff --git a/build/config/module.cxx b/build/config/module.cxx
index b184286..dca6ede 100644
--- a/build/config/module.cxx
+++ b/build/config/module.cxx
@@ -26,24 +26,24 @@ namespace build
}
void
- load (scope& root, scope& base, const location& l)
+ init (scope& root, scope& base, const location& l)
{
- tracer trace ("config::load");
+ tracer trace ("config::init");
- //@@ TODO: avoid multiple loads (generally, for modules).
+ //@@ TODO: avoid multiple inits (generally, for modules).
//
level4 ([&]{trace << "for " << root.path () << '/';});
if (&root != &base)
- fail (l) << "config module must be loaded in project root scope";
+ fail (l) << "config module must be initialized in project root scope";
// Register meta-operations.
//
if (root.meta_operations.insert (configure) != configure_id ||
root.meta_operations.insert (disfigure) != disfigure_id)
- fail (l) << "config module must be loaded before other modules";
+ fail (l) << "config module must be initialized before other modules";
- // Register the build/config.build loading trigger.
+ // Register the build/config.build sourcing trigger.
//
root.triggers[path ("build/config.build")] = &trigger;
}
diff --git a/build/config/operation.cxx b/build/config/operation.cxx
index c004a0d..1411ce4 100644
--- a/build/config/operation.cxx
+++ b/build/config/operation.cxx
@@ -4,6 +4,8 @@
#include <build/config/operation>
+#include <build/diagnostics>
+
using namespace std;
namespace build
@@ -11,6 +13,58 @@ namespace build
namespace config
{
meta_operation_info configure {"configure"};
- meta_operation_info disfigure {"disfigure"};
+
+ // disfigure
+ //
+ static operation_id
+ disfigure_operation_pre (operation_id o)
+ {
+ return o; // Don't translate default to update.
+ }
+
+ static void
+ disfigure_load (const path& bf,
+ scope&,
+ const path&,
+ const path&,
+ const location&)
+ {
+ tracer trace ("disfigure_load");
+ level4 ([&]{trace << "skipping " << bf;});
+ }
+
+ static void
+ disfigure_match (action a,
+ const target_key& tk,
+ const location& l,
+ action_targets& ts)
+ {
+ tracer trace ("disfigure_match");
+ //level4 ([&]{trace << "matching " << t;});
+ //ts.push_back (&t);
+ }
+
+ static void
+ disfigure_execute (action a, const action_targets& ts)
+ {
+ tracer trace ("execute");
+
+
+ for (void* v: ts)
+ {
+ //level4 ([&]{trace << "disfiguring target " << t;});
+ }
+ }
+
+ meta_operation_info disfigure {
+ "disfigure",
+ nullptr, // meta-operation pre
+ &disfigure_operation_pre,
+ &disfigure_load,
+ &disfigure_match,
+ &disfigure_execute,
+ nullptr, // operation post
+ nullptr // meta-operation post
+ };
}
}
diff --git a/build/file b/build/file
new file mode 100644
index 0000000..669d040
--- /dev/null
+++ b/build/file
@@ -0,0 +1,36 @@
+// file : build/file -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_FILE
+#define BUILD_FILE
+
+#include <build/path>
+
+namespace build
+{
+ class scope;
+
+ void
+ source (const path& buildfile, scope& root, scope& base);
+
+ // As above but first check if this buildfile has already been
+ // sourced for the base scope.
+ //
+ void
+ source_once (const path& buildfile, scope& root, scope& base);
+
+ // As above but checks against the specified scope rather than base.
+ //
+ void
+ source_once (const path& buildfile, scope& root, scope& base, scope& once);
+
+ // Load project's root[-pre].build unless already loaded.
+ //
+ void
+ root_pre (scope& root);
+}
+
+#include <build/file.ixx>
+
+#endif // BUILD_FILE
diff --git a/build/file.cxx b/build/file.cxx
new file mode 100644
index 0000000..7bb8bb6
--- /dev/null
+++ b/build/file.cxx
@@ -0,0 +1,68 @@
+// file : build/file.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/file>
+
+#include <fstream>
+
+#include <build/scope>
+#include <build/parser>
+#include <build/filesystem>
+#include <build/diagnostics>
+
+using namespace std;
+
+namespace build
+{
+ void
+ source (const path& bf, scope& root, scope& base)
+ {
+ tracer trace ("source");
+
+ ifstream ifs (bf.string ());
+ if (!ifs.is_open ())
+ fail << "unable to open " << bf;
+
+ level4 ([&]{trace << "sourcing " << bf;});
+
+ ifs.exceptions (ifstream::failbit | ifstream::badbit);
+ parser p;
+
+ try
+ {
+ p.parse_buildfile (ifs, bf, root, base);
+ }
+ catch (const std::ios_base::failure&)
+ {
+ fail << "failed to read from " << bf;
+ }
+ }
+
+ void
+ source_once (const path& bf, scope& root, scope& base, scope& once)
+ {
+ tracer trace ("source_once");
+
+ if (!once.buildfiles.insert (bf).second)
+ {
+ level4 ([&]{trace << "skipping already sourced " << bf;});
+ return;
+ }
+
+ source (bf, root, base);
+ }
+
+ void
+ root_pre (scope& root)
+ {
+ tracer trace ("root_pre");
+
+ path bf (root.src_path () / path ("build/root.build"));
+
+ if (!file_exists (bf))
+ return;
+
+ source_once (bf, root, root);
+ }
+}
diff --git a/build/file.ixx b/build/file.ixx
new file mode 100644
index 0000000..e3c5185
--- /dev/null
+++ b/build/file.ixx
@@ -0,0 +1,12 @@
+// file : build/file.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+namespace build
+{
+ inline void
+ source_once (const path& bf, scope& root, scope& base)
+ {
+ return source_once (bf, root, base, base);
+ }
+}
diff --git a/build/module b/build/module
index 81595d3..6a7a8a7 100644
--- a/build/module
+++ b/build/module
@@ -13,8 +13,8 @@ namespace build
class scope;
class location;
- using module_load_function = void (scope& root, scope& base, const location&);
- using module_map = std::unordered_map<std::string, module_load_function*>;
+ using module_init_function = void (scope& root, scope& base, const location&);
+ using module_map = std::unordered_map<std::string, module_init_function*>;
extern module_map modules;
}
diff --git a/build/operation b/build/operation
index 3469f21..c759d59 100644
--- a/build/operation
+++ b/build/operation
@@ -7,13 +7,19 @@
#include <string>
#include <iosfwd>
+#include <vector>
#include <cstdint>
#include <functional> // reference_wrapper
+#include <build/path>
#include <build/string-table>
namespace build
{
+ struct location;
+ class scope;
+ struct target_key;
+
// While we are using uint8_t for the meta/operation ids, we assume
// that each is limited to 4 bits (max 128 entries) so that we can
// store the combined action id in uint8_t as well. This makes our
@@ -104,17 +110,76 @@ namespace build
//
enum class execution_mode {first, last};
- // Meta/operation info.
+ // Meta-operation info.
+ //
+
+ // Normally a list of resolved and matched targets to execute. But
+ // can be something else, depending on the meta-operation.
//
+ typedef std::vector<void*> action_targets;
+
struct meta_operation_info
{
const std::string name;
+
+ void (*meta_operation_pre) (); // Start of meta-operation batch.
+ operation_id (*operation_pre) (operation_id); // Start of operation batch.
+
+ // Meta-operation-specific logic to load the buildfile, resolve and match
+ // the targets, and execute the action on the targets.
+ //
+ void (*load) (const path& buildfile,
+ scope& root,
+ const path& out_base,
+ const path& src_base,
+ const location&);
+
+ void (*match) (action,
+ const target_key&,
+ const location&,
+ action_targets&);
+
+ void (*execute) (action, const action_targets&);
+
+ void (*operation_post) (operation_id); // End of operation batch.
+ void (*meta_operation_post) (); // End of meta-operation batch.
};
// Built-in meta-operations.
//
+
+ // perform
+ //
+
+ // Load the buildfile. This is the default implementation that first
+ // calls root_pre(), then creates the scope for out_base, and, finally,
+ // loads the buildfile unless it has already been loaded for the root
+ // scope.
+ //
+ void
+ load (const path& buildfile,
+ scope& root,
+ const path& out_base,
+ const path& src_base,
+ const location&);
+
+ // Resolve and match the target. This is the default implementation
+ // that does just that and adds a pointer to the target to the list.
+ //
+ void
+ match (action, const target_key&, const location&, action_targets&);
+
+ // Execute the action on the list of targets. This is the default
+ // implementation that does just that while issuing appropriate
+ // diagnostics.
+ //
+ void
+ execute (action, const action_targets&);
+
extern meta_operation_info perform;
+ // Operation info.
+ //
struct operation_info
{
const std::string name;
diff --git a/build/operation.cxx b/build/operation.cxx
index 28cb13d..bbd474f 100644
--- a/build/operation.cxx
+++ b/build/operation.cxx
@@ -5,11 +5,22 @@
#include <build/operation>
#include <ostream>
+#include <cassert>
+#include <functional> // reference_wrapper
+
+#include <build/scope>
+#include <build/target>
+#include <build/file>
+#include <build/algorithm>
+#include <build/diagnostics>
+#include <build/dump>
using namespace std;
namespace build
{
+ // action
+ //
ostream&
operator<< (ostream& os, action a)
{
@@ -19,8 +30,135 @@ namespace build
<< ')';
}
- meta_operation_info perform {"perform"};
+ // perform
+ //
+ void
+ load (const path& bf,
+ scope& root,
+ const path& out_base,
+ const path& src_base,
+ const location&)
+ {
+ // Load project's root[-pre].build.
+ //
+ root_pre (root);
+
+ // Create the base scope. Note that its existence doesn't
+ // mean it was already processed as a base scope; it can
+ // be the same as root.
+ //
+ scope& base (scopes[out_base]);
+
+ base.variables["out_base"] = out_base;
+ auto v (base.variables["src_base"] = src_base);
+ base.src_path_ = &v.as<const path&> ();
+
+ // Load the buildfile unless it has already been loaded.
+ //
+ source_once (bf, root, base, root);
+ }
+
+ void
+ match (action a,
+ const target_key& tk,
+ const location& l,
+ action_targets& ts)
+ {
+ tracer trace ("match");
+
+ auto i (targets.find (tk, trace));
+ if (i == targets.end ())
+ fail (l) << "unknown target " << tk;
+
+ target& t (**i);
+
+ //@@ dump
+
+ level4 ([&]{trace << "matching " << t;});
+ match (a, t);
+
+ //@@ dump
+
+ ts.push_back (&t);
+ }
+
+ void
+ execute (action a, const action_targets& ts)
+ {
+ tracer trace ("execute");
+
+ // Build collecting postponed targets (to be re-examined later).
+ //
+ vector<reference_wrapper<target>> psp;
+
+ for (void* v: ts)
+ {
+ target& t (*static_cast<target*> (v));
+
+ level4 ([&]{trace << "executing target " << t;});
+
+ switch (execute (a, t))
+ {
+ case target_state::postponed:
+ {
+ info << "target " << t << " is postponed";
+ psp.push_back (t);
+ break;
+ }
+ case target_state::unchanged:
+ {
+ info << "target " << t << " is unchanged";
+ break;
+ }
+ case target_state::changed:
+ break;
+ case target_state::failed:
+ //@@ This could probably happen in a parallel build.
+ default:
+ assert (false);
+ }
+ }
+
+ // Re-examine postponed targets.
+ //
+ for (target& t: psp)
+ {
+ switch (t.state)
+ {
+ case target_state::postponed:
+ {
+ info << "unable to execute target " << t << " at this time";
+ break;
+ }
+ case target_state::unchanged:
+ {
+ info << "target " << t << " is unchanged";
+ break;
+ }
+ case target_state::unknown: // Assume something was done to it.
+ case target_state::changed:
+ break;
+ case target_state::failed:
+ //@@ This could probably happen in a parallel build.
+ default:
+ assert (false);
+ }
+ }
+ }
+
+ meta_operation_info perform {
+ "perform",
+ nullptr, // meta-operation pre
+ nullptr, // operation pre
+ &load,
+ &match,
+ &execute,
+ nullptr, // operation post
+ nullptr // meta-operation post
+ };
+ // operations
+ //
operation_info default_ {"<default>", execution_mode::first};
operation_info update {"update", execution_mode::first};
operation_info clean {"clean", execution_mode::last};
diff --git a/build/parser b/build/parser
index 164cf10..94a5bdf 100644
--- a/build/parser
+++ b/build/parser
@@ -51,7 +51,7 @@ namespace build
include (token&, token_type&);
void
- load (token&, token_type&);
+ using_ (token&, token_type&);
names_type
names (token& t, token_type& tt)
diff --git a/build/parser.cxx b/build/parser.cxx
index 4e8987e..c87a04a 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -112,10 +112,10 @@ namespace build
include (t, tt);
continue;
}
- else if (n == "load")
+ else if (n == "using")
{
next (t, tt);
- load (t, tt);
+ using_ (t, tt);
continue;
}
}
@@ -567,7 +567,8 @@ namespace build
scope_ = &scopes[out_base];
scope_->variables["out_base"] = move (out_base);
- scope_->variables["src_base"] = move (src_base);
+ auto v (scope_->variables["src_base"] = move (src_base));
+ scope_->src_path_ = &v.as<const path&> ();
target* odt (default_target_);
default_target_ = nullptr;
@@ -597,9 +598,9 @@ namespace build
}
void parser::
- load (token& t, token_type& tt)
+ using_ (token& t, token_type& tt)
{
- tracer trace ("parser::load", &path_);
+ tracer trace ("parser::using", &path_);
// The rest should be a list of module names. Parse them as names
// to get variable expansion, etc.
diff --git a/build/scope b/build/scope
index e74c7bc..26a4e03 100644
--- a/build/scope
+++ b/build/scope
@@ -25,6 +25,9 @@ namespace build
const path_type&
path () const {return i_->first;} // Absolute and normalized.
+ const path_type&
+ src_path () const {return *src_path_;} // Corresponding src path.
+
scope*
parent () const {return parent_;}
@@ -37,6 +40,8 @@ namespace build
value_proxy
operator[] (const variable&);
+ const path_type* src_path_ {nullptr}; // Cached src_{root,base} var value.
+
private:
friend class scope_map;
@@ -61,7 +66,7 @@ namespace build
operation_table operations;
// Set of buildfiles already loaded for this scope. The included
- // buildfiles are checked against project root scope while
+ // buildfiles are checked against the project's root scope while
// imported -- against the overall root scope (root_scope).
//
std::unordered_set<path_type> buildfiles;
diff --git a/build/string-table b/build/string-table
index 39c8cff..7a74024 100644
--- a/build/string-table
+++ b/build/string-table
@@ -67,6 +67,9 @@ namespace build
I
size () const {return static_cast<I> (vec_.size ());}
+ bool
+ empty () const {return vec_.empty ();}
+
private:
using key_type = map_key<std::string>;
using value_type = string_table_element<I, D>;
diff --git a/build/target b/build/target
index c961315..050696c 100644
--- a/build/target
+++ b/build/target
@@ -165,41 +165,46 @@ namespace build
std::ostream&
operator<< (std::ostream&, const target&);
- struct target_set
+ // Light-weight (by being shallow-pointing) target key.
+ //
+ struct target_key
{
- struct key
+ mutable const target_type* type;
+ mutable const path* dir;
+ mutable const std::string* name;
+ mutable const std::string* const* ext;
+
+ friend bool
+ operator< (const target_key& x, const target_key& y)
{
- mutable const target_type* type;
- mutable const path* dir;
- mutable const std::string* name;
- mutable const std::string* const* ext;
-
- friend bool
- operator< (const key& x, const key& y)
- {
- const std::type_index& xt (x.type->id);
- const std::type_index& yt (y.type->id);
-
- //@@ TODO: use compare() to compare once.
-
- // Unspecified and specified extension are assumed equal. The
- // extension strings are from the pool, so we can just compare
- // pointers.
- //
- return
- (xt < yt) ||
- (xt == yt && *x.name < *y.name) ||
- (xt == yt && *x.name == *y.name && *x.dir < *y.dir) ||
- (xt == yt && *x.name == *y.name && *x.dir == *y.dir &&
- *x.ext != nullptr && *y.ext != nullptr && **x.ext < **y.ext);
- }
- };
-
- typedef std::map<key, std::unique_ptr<target>> map;
+ const std::type_index& xt (x.type->id);
+ const std::type_index& yt (y.type->id);
+
+ //@@ TODO: use compare() to compare once.
+
+ // Unspecified and specified extension are assumed equal. The
+ // extension strings are from the pool, so we can just compare
+ // pointers.
+ //
+ return
+ (xt < yt) ||
+ (xt == yt && *x.name < *y.name) ||
+ (xt == yt && *x.name == *y.name && *x.dir < *y.dir) ||
+ (xt == yt && *x.name == *y.name && *x.dir == *y.dir &&
+ *x.ext != nullptr && *y.ext != nullptr && **x.ext < **y.ext);
+ }
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, const target_key&);
+
+ struct target_set
+ {
+ typedef std::map<target_key, std::unique_ptr<target>> map;
typedef map_iterator_adapter<map::const_iterator> iterator;
iterator
- find (const key& k, tracer& trace) const;
+ find (const target_key& k, tracer& trace) const;
iterator
find (const target_type& type,
@@ -209,7 +214,7 @@ namespace build
tracer& trace) const
{
const std::string* e (ext);
- return find (key {&type, &dir, &name, &e}, trace);
+ return find (target_key {&type, &dir, &name, &e}, trace);
}
iterator begin () const {return map_.begin ();}
@@ -226,9 +231,6 @@ namespace build
map map_;
};
- std::ostream&
- operator<< (std::ostream&, const target_set::key&);
-
extern target_set targets;
class target_type_map: public std::map<
diff --git a/build/target.cxx b/build/target.cxx
index 347a382..1a8fbf7 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -40,7 +40,7 @@ namespace build
ostream&
operator<< (ostream& os, const target& t)
{
- return os << target_set::key {&t.type (), &t.dir, &t.name, &t.ext};
+ return os << target_key {&t.type (), &t.dir, &t.name, &t.ext};
}
static target*
@@ -57,7 +57,7 @@ namespace build
target_set targets;
auto target_set::
- find (const key& k, tracer& trace) const -> iterator
+ find (const target_key& k, tracer& trace) const -> iterator
{
iterator i (map_.find (k));
@@ -96,21 +96,21 @@ namespace build
const std::string* ext,
tracer& trace)
{
- iterator i (find (key {&tt, &dir, &name, &ext}, trace));
+ iterator i (find (target_key {&tt, &dir, &name, &ext}, trace));
if (i != end ())
return pair<target&, bool> (**i, false);
unique_ptr<target> t (tt.factory (move (dir), move (name), ext));
i = map_.emplace (
- make_pair (key {&tt, &t->dir, &t->name, &t->ext},
+ make_pair (target_key {&tt, &t->dir, &t->name, &t->ext},
move (t))).first;
return pair<target&, bool> (**i, true);
}
ostream&
- operator<< (ostream& os, const target_set::key& k)
+ operator<< (ostream& os, const target_key& k)
{
os << k.type->name << '{';