From eaaa82bd9c1e24a83dcea3857f5fd75d0dfb6de5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 20 Mar 2015 13:21:18 +0200 Subject: New consolidated load/match/build loop --- build/b.cxx | 545 ++++++++++++++++++++++++--------------------- build/bootstrap.build | 5 + build/buildfile | 6 +- build/config/module.cxx | 12 +- build/config/operation | 5 + build/config/operation.cxx | 2 + build/filesystem | 13 ++ build/filesystem.cxx | 35 ++- build/operation | 58 +++-- build/operation.cxx | 7 +- build/parser | 2 +- build/parser.cxx | 2 +- build/root.build | 3 +- build/rule | 2 +- build/rule.cxx | 2 +- build/scope | 8 +- build/string-table | 9 +- 17 files changed, 418 insertions(+), 298 deletions(-) create mode 100644 build/bootstrap.build 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 #include #include +#include #include #include @@ -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 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 ("", 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> 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 + // 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 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 (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 (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 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 ("", 1, 0); //@@ TODO - - for (auto oi (ms.begin ()); oi != ms.end (); ++oi) - { - opspec& os (*oi); - const location l ("", 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> 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 ("", 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> 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 (oid);}); + + //@@ operation batch_post } + + level4 ([&]{trace << "end meta-operation batch " << mif->name + << ", id " << static_cast (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 #include +#include + 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 -#include // rmdir(), unlink() -#include // mkdir() +#include // rmdir(), unlink() +#include // stat +#include // stat, lstat(), S_IS*, mkdir() #include @@ -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 -#include #include +#include +#include // reference_wrapper #include @@ -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; - using operation_table = string_table; + // 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>; - extern meta_operation_table meta_operations; - extern operation_table operations; + using operation_table = string_table< + operation_id, + std::reference_wrapper>; + + template <> + struct string_table_traits> + { + static const std::string& + key (const meta_operation_info& x) {return x.name;} + }; + + template <> + struct string_table_traits> + { + 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_ {"", 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, '.'>>; - using operation_rule_map = std::unordered_map; + using operation_rule_map = std::unordered_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 #include #include +#include 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 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 - 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 -- cgit v1.1