aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-20 13:21:18 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-20 13:21:18 +0200
commiteaaa82bd9c1e24a83dcea3857f5fd75d0dfb6de5 (patch)
tree9d849682e5c8fb971382843064ea0c286d753cba /build
parentb6e72877a1a26a6ae16961728ee57e45f657f717 (diff)
New consolidated load/match/build loop
Diffstat (limited to 'build')
-rw-r--r--build/b.cxx545
-rw-r--r--build/bootstrap.build5
-rw-r--r--build/buildfile6
-rw-r--r--build/config/module.cxx12
-rw-r--r--build/config/operation5
-rw-r--r--build/config/operation.cxx2
-rw-r--r--build/filesystem13
-rw-r--r--build/filesystem.cxx35
-rw-r--r--build/operation58
-rw-r--r--build/operation.cxx7
-rw-r--r--build/parser2
-rw-r--r--build/parser.cxx2
-rw-r--r--build/root.build3
-rw-r--r--build/rule2
-rw-r--r--build/rule.cxx2
-rw-r--r--build/scope8
-rw-r--r--build/string-table9
17 files changed, 418 insertions, 298 deletions
diff --git a/build/b.cxx b/build/b.cxx
index a97235c..93a199e 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -33,6 +33,7 @@
#include <build/diagnostics>
#include <build/context>
#include <build/utility>
+#include <build/filesystem>
#include <build/dump>
#include <build/lexer>
@@ -67,15 +68,14 @@ namespace build
inline bool
is_src_root (const path& d)
{
- return path_mtime (d / path ("build/root.build")) !=
- timestamp_nonexistent;
+ return file_exists (d / path ("build/bootstrap.build")) ||
+ file_exists (d / path ("build/root.build"));
}
inline bool
is_out_root (const path& d)
{
- return path_mtime (d / path ("build/bootstrap/src-root.build")) !=
- timestamp_nonexistent;
+ return file_exists (d / path ("build/bootstrap/src-root.build"));
}
// Given an src_base directory, look for the project's src_root
@@ -111,13 +111,33 @@ namespace build
}
void
- bootstrap (scope& rs)
+ load (const path& buildfile, scope& root, scope& base)
{
- tracer trace ("bootstrap");
+ 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
+ bootstrap_out (scope& root)
+ {
+ tracer trace ("bootstrap_out");
- path bf (rs.path () / path ("build/bootstrap/src-root.build"));
+ path bf (root.path () / path ("build/bootstrap/src-root.build"));
- if (path_mtime (bf) == timestamp_nonexistent)
+ if (!file_exists (bf))
return;
//@@ TODO: if bootstrap files can source other bootstrap files
@@ -126,51 +146,35 @@ namespace build
//
level4 ([&]{trace << "loading " << bf;});
+ load (bf, root, root);
+ }
- ifstream ifs (bf.string ());
- if (!ifs.is_open ())
- fail << "unable to open " << bf;
+ void
+ bootstrap_src (scope& root, const path& src_root)
+ {
+ tracer trace ("bootstrap_src");
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
- parser p;
+ path bf (src_root / path ("build/bootstrap.build"));
- try
- {
- p.parse_buildfile (ifs, bf, rs, rs);
- }
- catch (const std::ios_base::failure&)
- {
- fail << "failed to read from " << bf;
- }
+ if (!file_exists (bf))
+ return;
+
+ level4 ([&]{trace << "loading " << bf;});
+ load (bf, root, root);
}
void
- root_pre (scope& rs, const path& src_root)
+ root_pre (scope& root, const path& src_root)
{
tracer trace ("root_pre");
path bf (src_root / path ("build/root.build"));
- if (path_mtime (bf) == timestamp_nonexistent)
+ if (!file_exists (bf))
return;
level4 ([&]{trace << "loading " << bf;});
-
- ifstream ifs (bf.string ());
- if (!ifs.is_open ())
- fail << "unable to open " << bf;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
- parser p;
-
- try
- {
- p.parse_buildfile (ifs, bf, rs, rs);
- }
- catch (const std::ios_base::failure&)
- {
- fail << "failed to read from " << bf;
- }
+ load (bf, root, root);
}
}
@@ -219,17 +223,27 @@ main (int argc, char* argv[])
target_types.insert (cxx::ixx::static_type);
target_types.insert (cxx::txx::static_type);
- // Enter built-in meta-operation and operation names into tables.
- // Note that the order of registration should match the id constants;
- // see <operation> for details. Loading of the buildfiles can result
- // in additional names being added (via module loading).
+ // Register rules.
//
- meta_operations.insert (meta_operation_info {"perform"});
- meta_operations.insert (meta_operation_info {"configure"});
- meta_operations.insert (meta_operation_info {"disfigure"});
+ cxx::link cxx_link;
+ rules[update_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
+ rules[clean_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
+
+ cxx::compile cxx_compile;
+ rules[update_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[clean_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
+
+ dir_rule dir_r;
+ rules[update_id][typeid (dir)].emplace ("dir", dir_r);
+ rules[clean_id][typeid (dir)].emplace ("dir", dir_r);
- operations.insert (operation_info {"update", execution_mode::first});
- operations.insert (operation_info {"clean", execution_mode::last});
+ fsdir_rule fsdir_r;
+ rules[update_id][typeid (fsdir)].emplace ("fsdir", fsdir_r);
+ rules[clean_id][typeid (fsdir)].emplace ("fsdir", fsdir_r);
+
+ path_rule path_r;
+ rules[update_id][typeid (path_target)].emplace ("path", path_r);
+ rules[clean_id][typeid (path_target)].emplace ("path", path_r);
// Figure out work and home directories.
//
@@ -314,13 +328,30 @@ main (int argc, char* argv[])
if (ms.empty ())
ms.push_back (opspec ()); // Default operation.
+ meta_operation_id mid (0); // Not yet translated.
+ const meta_operation_info* mif (nullptr);
+
for (opspec& os: ms)
{
+ const location l ("<buildspec>", 1, 0); //@@ TODO
+
if (os.empty ())
// Default target: dir{}.
//
os.push_back (targetspec (name ("dir", path (), string ())));
+ operation_id oid (0); // Not yet translated.
+ const operation_info* oif (nullptr);
+
+ action act (0, 0); // Not yet initialized.
+
+ // We do meta-operation and operation batches sequentially (no
+ // parallelism). But multiple targets in an operation batch
+ // can be done in parallel.
+ //
+ vector<reference_wrapper<target>> tgs;
+ tgs.reserve (os.size ());
+
for (targetspec& ts: os)
{
name& tn (ts.name);
@@ -407,17 +438,29 @@ main (int argc, char* argv[])
// Now we know out_root and, if it was explicitly specified,
// src_root. The next step is to create the root scope and
- // load the bootstrap files, if any. Note that we might already
- // have done this as a result of one of the preceding target
- // processing.
+ // load the out_root bootstrap files, if any. Note that we
+ // 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);
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.
+ //
+ rs.meta_operations.insert (perform);
+
+ rs.operations.insert (default_);
+ rs.operations.insert (update);
+ rs.operations.insert (clean);
+
rs.variables["out_root"] = out_root;
- bootstrap (rs);
+ bootstrap_out (rs);
}
// See if the bootstrap process set src_root.
@@ -472,9 +515,133 @@ main (int argc, char* argv[])
if (src_base.empty ())
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 (rsp.second)
+ bootstrap_src (rs, src_root);
+
+ // 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;
+ // see <operation> for details.
+ //
+ assert (rs.operations.size () <= 128);
+ assert (rs.meta_operations.size () <= 128);
+
+ // Since we now know all the names of meta-operations and
+ // operations, "lift" names that we assumed (from buildspec
+ // syntax) were operations but are actually meta-operations.
+ // Also convert empty names (which means they weren't explicitly
+ // specified) to the defaults and verify that all the names are
+ // known.
+ //
+ {
+ const auto& mn (ms.name);
+ const auto& on (os.name);
+
+ meta_operation_id m (0);
+ operation_id o (0);
+
+ if (!on.empty ())
+ {
+ m = rs.meta_operations.find (on);
+
+ if (m != 0)
+ {
+ if (!mn.empty ())
+ fail (l) << "nested meta-operation " << mn
+ << '(' << on << ')';
+ }
+ else
+ {
+ o = rs.operations.find (on);
+
+ if (o == 0)
+ fail (l) << "unknown operation " << on;
+ }
+ }
+
+ if (!mn.empty ())
+ {
+ m = rs.meta_operations.find (mn);
+
+ if (m == 0)
+ fail (l) << "unknown meta-operation " << mn;
+ }
+
+ // The default meta-operation is perform. The default
+ // operation is assigned by the meta-operation below.
+ //
+ if (m == 0)
+ m = perform_id;
+
+ // If this is the first target in the meta-operation batch,
+ // then set the batch meta-operation id.
+ //
+ if (mid == 0)
+ {
+ //@@ meta-operation batch_pre
+ //
+
+ mid = m;
+ mif = &rs.meta_operations[m].get ();
+
+ level4 ([&]{trace << "start meta-operation batch " << mif->name
+ << ", id " << static_cast<uint16_t> (mid);});
+ }
+ //
+ // Otherwise, check that all the targets in a meta-operation
+ // batch have the same meta-operation implementation.
+ //
+ else
+ {
+ if (mid > rs.meta_operations.size () || // Not a valid index.
+ mif != &rs.meta_operations[mid].get ()) // Not the same impl.
+ fail (l) << "different meta-operation implementations "
+ << "in a meta-operation batch";
+ }
+
+ // If this is the first target in the operation batch, then set
+ // the batch operation id.
+ //
+ if (oid == 0)
+ {
+ //@@ operation batch_pre; translate operation (pass
+ // default_id for 0).
+
+ if (o == 0)
+ o = update_id; // @@ TMP; if no batch_pre
+
+ oid = o;
+ oif = &rs.operations[o].get ();
+
+ 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);});
+ }
+ //
+ // Similar to meta-operations, check that all the targets in
+ // an operation batch have the same operation implementation.
+ //
+ else
+ {
+ if (oid > rs.operations.size () || // Not a valid index.
+ oif != &rs.operations[oid].get ()) // Not the same impl.
+ fail (l) << "different operation implementations "
+ << "in an operation batch";
+ }
+ }
+
+ //@@ target pre_load; may request skipping loading
+
if (verb >= 4)
{
- trace << tn << ':';
+ trace << "target " << tn << ':';
trace << " out_base: " << out_base.string ();
trace << " src_base: " << src_base.string ();
trace << " out_root: " << out_root.string ();
@@ -483,7 +650,8 @@ main (int argc, char* argv[])
// Load project's root[-pre].build.
//
- root_pre (rs, src_root);
+ 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
@@ -500,232 +668,83 @@ main (int argc, char* argv[])
// Check if this buildfile has already been loaded.
//
- if (!rs.buildfiles.insert (bf).second)
+ if (rs.buildfiles.insert (bf).second)
{
- level4 ([&]{trace << "skipping already loaded " << bf;});
- continue;
- }
-
- level4 ([&]{trace << "loading " << bf;});
+ 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;
- }
+ 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;
+ ifs.exceptions (ifstream::failbit | ifstream::badbit);
+ parser p;
- try
- {
- p.parse_buildfile (ifs, bf, bs, rs);
- }
- catch (const std::ios_base::failure&)
- {
- fail << "failed to read from " << bf;
+ try
+ {
+ p.parse_buildfile (ifs, bf, rs, bs);
+ }
+ catch (const std::ios_base::failure&)
+ {
+ fail << "failed to read from " << bf;
+ }
}
- }
- }
- }
-
- // We store the combined action id in uint8_t; see <operation> for
- // details.
- //
- assert (operations.size () <= 128);
- assert (meta_operations.size () <= 128);
-
- dump_scopes ();
- dump ();
-
- // At this stage we know all the names of meta-operations and
- // operations so "lift" names that we assumed (from buildspec
- // syntax) were operations but are actually meta-operations.
- // Also convert empty names (which means they weren't explicitly
- // specified) to the defaults and verify that all the names are
- // known.
- //
- for (auto mi (bspec.begin ()); mi != bspec.end (); ++mi)
- {
- metaopspec& ms (*mi);
- const location l ("<buildspec>", 1, 0); //@@ TODO
-
- for (auto oi (ms.begin ()); oi != ms.end (); ++oi)
- {
- opspec& os (*oi);
- const location l ("<buildspec>", 1, 0); //@@ TODO
-
- if (os.name.empty ())
- {
- os.name = "update";
- continue;
- }
+ else
+ level4 ([&]{trace << "skipping already loaded " << bf;});
- if (meta_operations.find (os.name) != 0)
- {
- if (!ms.name.empty ())
- fail (l) << "nested meta-operation " << os.name;
+ //@@ target post_load
- // The easy case is when the metaopspec contains a
- // single opspec (us). In this case we can just move
- // the name.
- //
- if (ms.size () == 1)
- {
- ms.name = move (os.name);
- os.name = "update";
- continue;
- }
- // The hard case is when there are other operations that
- // need to keep their original meta-operation. In this
- // case we have to "split" the metaopspec, in the worst
- // case scenario, into three parts: prefix, us, and suffix.
+ // Next resolve and match the target. We don't want to start
+ // building before we know how to for all the targets in this
+ // operation batch.
//
- else
{
- if (oi != ms.begin ()) // We have a prefix of opspec's.
- {
- // Keep the prefix in the original metaopspec and move
- // the suffix into a new one that is inserted after the
- // prefix. Then simply let the loop finish with the prefix
- // and naturally move to the suffix (in other words, we
- // are reducing this case to the one without a prefix).
- //
- metaopspec suffix;
- suffix.insert (suffix.end (),
- make_move_iterator (oi),
- make_move_iterator (ms.end ()));
- ms.resize (oi - ms.begin ());
-
- mi = bspec.insert (++mi, move (suffix)); // Insert after prefix.
- --mi; // Move back to prefix.
- break;
- }
+ const string* e;
+ const target_type* ti (target_types.find (tn, e));
- // We are the first element and have a suffix of opspec's
- // (otherwise one of the previous cases would have matched).
- //
- assert (oi == ms.begin () && (oi + 1) != ms.end ());
+ if (ti == nullptr)
+ fail (l) << "unknown target type " << tn.type;
- // Move this opspec into a new metaopspec and insert it before
- // the current one. Then continue with the next opspec.
+ // If the directory is relative, assume it is relative to work
+ // (must be consistent with how we derived out_base above).
//
- metaopspec prefix (move (os.name));
- os.name = "update";
- prefix.push_back (move (os));
- ms.erase (oi);
-
- mi = bspec.insert (mi, move (prefix)); // Insert before suffix.
- break; // Restart inner loop: outer loop ++ moves back to suffix.
- }
- }
-
- if (operations.find (os.name) == 0)
- fail (l) << "unknown operation " << os.name;
- }
-
- // Note: using mi rather than ms since ms is invalidated by above
- // insert()'s.
- //
- if (mi->name.empty ())
- mi->name = "perform";
- else if (meta_operations.find (mi->name) == 0)
- fail (l) << "unknown meta-operation " << mi->name;
- }
-
- level4 ([&]{trace << "buildspec: " << bspec;});
-
- // Register rules.
- //
- cxx::link cxx_link;
- rules["update"][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
- rules["clean"][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
-
- cxx::compile cxx_compile;
- rules["update"][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
- rules["clean"][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
-
- dir_rule dir_r;
- rules["update"][typeid (dir)].emplace ("dir", dir_r);
- rules["clean"][typeid (dir)].emplace ("dir", dir_r);
-
- fsdir_rule fsdir_r;
- rules["update"][typeid (fsdir)].emplace ("fsdir", fsdir_r);
- rules["clean"][typeid (fsdir)].emplace ("fsdir", fsdir_r);
-
- path_rule path_r;
- rules["update"][typeid (path_target)].emplace ("path", path_r);
- rules["clean"][typeid (path_target)].emplace ("path", path_r);
-
+ path& d (tn.dir);
- // Do the operations. We do meta-operations and operations sequentially
- // (no parallelism).
- //
- for (metaopspec& ms: bspec)
- {
- for (opspec& os: ms)
- {
- action act (meta_operations.find (ms.name), operations.find (os.name));
+ if (d.relative ())
+ d = work / d;
- current_mode = operations[act.operation ()].mode;
- current_rules = &rules[os.name];
+ d.normalize ();
- level4 ([&]{trace << ms.name << " " << os.name << " " << act;});
+ target_set::key tk {ti, &d, &tn.value, &e};
+ auto i (targets.find (tk, trace));
+ if (i == targets.end ())
+ fail (l) << "unknown target " << tk;
- // Multiple targets in the same operation can be done in parallel.
- //
- vector<reference_wrapper<target>> tgs, psp;
- tgs.reserve (os.size ());
+ target& t (**i);
- // First resolve and match all the targets. We don't want to
- // start building before we know how for all the targets in
- // this operation.
- //
- for (targetspec& ts: os)
- {
- name& tn (ts.name);
- const location l ("<buildspec>", 1, 0); //@@ TODO
-
- const string* e;
- const target_type* ti (target_types.find (tn, e));
+ //@@ dump
- if (ti == nullptr)
- fail (l) << "unknown target type " << tn.type;
+ level4 ([&]{trace << "matching target " << t;});
+ match (act, t);
- // If the directory is relative, assume it is relative to work
- // (must be consistent with how we derive out_base).
- //
- path& d (tn.dir);
+ //@@ dump
- if (d.relative ())
- d = work / d;
-
- 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);
-
- level4 ([&]{trace << "matching target " << t;});
- match (act, t);
-
- tgs.push_back (t);
+ tgs.push_back (t);
+ }
}
- dump ();
-
- // Now build.
+ // Now build collecting postponed targets (to be re-examined).
//
+ vector<reference_wrapper<target>> psp;
+
for (target& t: tgs)
{
- level4 ([&]{trace << "updating target " << t;});
+ level4 ([&]{trace << "executing target " << t;});
switch (execute (act, t))
{
@@ -737,7 +756,7 @@ main (int argc, char* argv[])
}
case target_state::unchanged:
{
- info << "target " << t << " is up to date";
+ info << "target " << t << " is unchanged";
break;
}
case target_state::changed:
@@ -757,12 +776,12 @@ main (int argc, char* argv[])
{
case target_state::postponed:
{
- info << "target " << t << " unable to do at this time";
+ info << "unable to execute target " << t << " at this time";
break;
}
case target_state::unchanged:
{
- info << "target " << t << " is up to date";
+ info << "target " << t << " is unchanged";
break;
}
case target_state::unknown: // Assume something was done to it.
@@ -774,7 +793,17 @@ main (int argc, char* argv[])
assert (false);
}
}
+
+ level4 ([&]{trace << "end operation batch " << oif->name
+ << ", id " << static_cast<uint16_t> (oid);});
+
+ //@@ operation batch_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
new file mode 100644
index 0000000..2e849c0
--- /dev/null
+++ b/build/bootstrap.build
@@ -0,0 +1,5 @@
+print bootstrap.build
+
+project_name = build2
+
+load config
diff --git a/build/buildfile b/build/buildfile
index 246e2f6..6752a6e 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -1,4 +1,6 @@
+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 \
- config/module cxx/target cxx/rule process timestamp path utility \
- filesystem dump}
+ process timestamp path utility filesystem dump $config $cxx}
diff --git a/build/config/module.cxx b/build/config/module.cxx
index bbaccdc..b184286 100644
--- a/build/config/module.cxx
+++ b/build/config/module.cxx
@@ -8,6 +8,8 @@
#include <build/scope>
#include <build/diagnostics>
+#include <build/config/operation>
+
using namespace std;
namespace build
@@ -28,12 +30,18 @@ namespace build
{
tracer trace ("config::load");
+ //@@ TODO: avoid multiple loads (generally, for modules).
+ //
+ level4 ([&]{trace << "for " << root.path () << '/';});
+
if (&root != &base)
fail (l) << "config module must be loaded in project root scope";
- //@@ TODO: avoid multiple loads (generally, for modules).
+ // Register meta-operations.
//
- level4 ([&]{trace << "for " << root.path () << '/';});
+ 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";
// Register the build/config.build loading trigger.
//
diff --git a/build/config/operation b/build/config/operation
index a233e63..cfd04c3 100644
--- a/build/config/operation
+++ b/build/config/operation
@@ -11,6 +11,11 @@ namespace build
{
namespace config
{
+ const meta_operation_id configure_id = 2;
+ const meta_operation_id disfigure_id = 3;
+
+ extern meta_operation_info configure;
+ extern meta_operation_info disfigure;
}
}
diff --git a/build/config/operation.cxx b/build/config/operation.cxx
index cc04929..c004a0d 100644
--- a/build/config/operation.cxx
+++ b/build/config/operation.cxx
@@ -10,5 +10,7 @@ namespace build
{
namespace config
{
+ meta_operation_info configure {"configure"};
+ meta_operation_info disfigure {"disfigure"};
}
}
diff --git a/build/filesystem b/build/filesystem
index 8fa76c2..c96e669 100644
--- a/build/filesystem
+++ b/build/filesystem
@@ -11,6 +11,19 @@
namespace build
{
+ // Return true if the path is to an existing directory. Note that
+ // this function resolves symlinks.
+ //
+ bool
+ dir_exists (const path&);
+
+ // Return true if the path is to an existing regular file. Note that
+ // this function resolves symlinks.
+ //
+ bool
+ file_exists (const path&);
+
+
// Note that you should probably use the default mode 0777 and let
// the umask mechanism adjust it to the user's preferences. Errors
// are reported by throwing std::system_error.
diff --git a/build/filesystem.cxx b/build/filesystem.cxx
index 75d0283..ee17fba 100644
--- a/build/filesystem.cxx
+++ b/build/filesystem.cxx
@@ -4,8 +4,9 @@
#include <build/filesystem>
-#include <unistd.h> // rmdir(), unlink()
-#include <sys/stat.h> // mkdir()
+#include <unistd.h> // rmdir(), unlink()
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat, lstat(), S_IS*, mkdir()
#include <system_error>
@@ -13,6 +14,36 @@ using namespace std;
namespace build
{
+ bool
+ dir_exists (const path& p)
+ {
+ struct stat s;
+ if (::lstat (p.string ().c_str (), &s) != 0)
+ {
+ if (errno == ENOENT || errno == ENOTDIR)
+ return false;
+ else
+ throw system_error (errno, system_category ());
+ }
+
+ return S_ISDIR (s.st_mode);
+ }
+
+ bool
+ file_exists (const path& p)
+ {
+ struct stat s;
+ if (::lstat (p.string ().c_str (), &s) != 0)
+ {
+ if (errno == ENOENT || errno == ENOTDIR)
+ return false;
+ else
+ throw system_error (errno, system_category ());
+ }
+
+ return S_ISREG (s.st_mode);
+ }
+
void
mkdir (const path& p, mode_t m)
{
diff --git a/build/operation b/build/operation
index 3d9c7e0..3469f21 100644
--- a/build/operation
+++ b/build/operation
@@ -6,8 +6,9 @@
#define BUILD_OPERATION
#include <string>
-#include <cstdint>
#include <iosfwd>
+#include <cstdint>
+#include <functional> // reference_wrapper
#include <build/string-table>
@@ -49,11 +50,13 @@ namespace build
// Id constants for build-in operations.
//
const meta_operation_id perform_id = 1;
- const meta_operation_id configure_id = 2;
- const meta_operation_id disfigure_id = 3;
- const operation_id update_id = 1;
- const operation_id clean_id = 2;
+ // The default operation is a special marker that can be used to
+ // indicate that no operation was explicitly specified by the user.
+ //
+ const operation_id default_id = 1;
+ const operation_id update_id = 2;
+ const operation_id clean_id = 3;
const action_id perform_update_id = (perform_id << 4) | update_id;
const action_id perform_clean_id = (perform_id << 4) | clean_id;
@@ -101,31 +104,52 @@ namespace build
//
enum class execution_mode {first, last};
- // Meta/operation tables.
+ // Meta/operation info.
//
struct meta_operation_info
{
const std::string name;
-
- const std::string&
- key () const {return name;} // string_table interface.
};
+ // Built-in meta-operations.
+ //
+ extern meta_operation_info perform;
+
struct operation_info
{
const std::string name;
const execution_mode mode;
-
- const std::string&
- key () const {return name;} // string_table interface.
};
- using meta_operation_table = string_table<meta_operation_id,
- meta_operation_info>;
- using operation_table = string_table<operation_id, operation_info>;
+ // Build-in operations.
+ //
+ extern operation_info default_;
+ extern operation_info update;
+ extern operation_info clean;
+
+ // Meta/operation tables.
+ //
+ using meta_operation_table = string_table<
+ meta_operation_id,
+ std::reference_wrapper<const meta_operation_info>>;
- extern meta_operation_table meta_operations;
- extern operation_table operations;
+ using operation_table = string_table<
+ operation_id,
+ std::reference_wrapper<const operation_info>>;
+
+ template <>
+ struct string_table_traits<std::reference_wrapper<const meta_operation_info>>
+ {
+ static const std::string&
+ key (const meta_operation_info& x) {return x.name;}
+ };
+
+ template <>
+ struct string_table_traits<std::reference_wrapper<const operation_info>>
+ {
+ static const std::string&
+ key (const operation_info& x) {return x.name;}
+ };
}
#endif // BUILD_OPERATION
diff --git a/build/operation.cxx b/build/operation.cxx
index 0d90c95..28cb13d 100644
--- a/build/operation.cxx
+++ b/build/operation.cxx
@@ -19,6 +19,9 @@ namespace build
<< ')';
}
- meta_operation_table meta_operations;
- operation_table operations;
+ meta_operation_info perform {"perform"};
+
+ 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 0d42d73..164cf10 100644
--- a/build/parser
+++ b/build/parser
@@ -28,7 +28,7 @@ namespace build
// Issue diagnostics and throw failed in case of an error.
//
void
- parse_buildfile (std::istream&, const path&, scope& base, scope& root);
+ parse_buildfile (std::istream&, const path&, scope& root, scope& base);
buildspec
parse_buildspec (std::istream&, const std::string& name);
diff --git a/build/parser.cxx b/build/parser.cxx
index c7687d3..4e8987e 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -38,7 +38,7 @@ namespace build
typedef token_type type;
void parser::
- parse_buildfile (istream& is, const path& p, scope& base, scope& root)
+ parse_buildfile (istream& is, const path& p, scope& root, scope& base)
{
string rw (diag_relative_work (p));
path_ = &rw;
diff --git a/build/root.build b/build/root.build
index d10a80c..8412356 100644
--- a/build/root.build
+++ b/build/root.build
@@ -1,4 +1,3 @@
print root.build
-load config
-source build/config.build
+source build/config.build
diff --git a/build/rule b/build/rule
index 8eb6b26..e6dbd5a 100644
--- a/build/rule
+++ b/build/rule
@@ -30,7 +30,7 @@ namespace build
std::type_index,
prefix_multimap<std::string, std::reference_wrapper<rule>, '.'>>;
- using operation_rule_map = std::unordered_map<std::string, target_rule_map>;
+ using operation_rule_map = std::unordered_map<operation_id, target_rule_map>;
extern operation_rule_map rules;
diff --git a/build/rule.cxx b/build/rule.cxx
index c6bfe7a..aa0300a 100644
--- a/build/rule.cxx
+++ b/build/rule.cxx
@@ -220,7 +220,7 @@ namespace build
const path& d (t.dir); // Everything is in t.dir.
- if (path_mtime (d) == timestamp_nonexistent)
+ if (!dir_exists (d))
{
if (verb >= 1)
text << "mkdir " << d.string ();
diff --git a/build/scope b/build/scope
index f86d0ed..e74c7bc 100644
--- a/build/scope
+++ b/build/scope
@@ -13,6 +13,7 @@
#include <build/path-map>
#include <build/variable>
#include <build/prerequisite>
+#include <build/operation>
namespace build
{
@@ -53,13 +54,18 @@ namespace build
variable_map variables;
prerequisite_set prerequisites;
+ // Meta/operations supported by this project (set on the project
+ // root scope only).
+ //
+ meta_operation_table meta_operations;
+ operation_table operations;
+
// Set of buildfiles already loaded for this scope. The included
// buildfiles are checked against project root scope while
// imported -- against the overall root scope (root_scope).
//
std::unordered_set<path_type> buildfiles;
-
// A map of buildfiles to trigger functions that are executed when
// such files are sourced. The path is is assumed to be relative to
// the src directory corresponding to this scope.
diff --git a/build/string-table b/build/string-table
index 522476f..39c8cff 100644
--- a/build/string-table
+++ b/build/string-table
@@ -33,14 +33,7 @@ namespace build
};
template <typename D>
- struct string_table_traits
- {
- // By default, look for the key() function in D. But you can
- // also specialize this class template.
- //
- static const std::string&
- key (const D& d) {return d.key ();}
- };
+ struct string_table_traits;
template <>
struct string_table_traits<std::string>