From a94dcda7f00b10cb22b5f2138b1c29bcfbe7de37 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 24 Mar 2015 08:53:06 +0200 Subject: Make meta-operations control build loop; add disfigure skeleton --- build/b.cxx | 289 ++++++++++++--------------------------------- build/bootstrap.build | 2 +- build/buildfile | 2 +- build/config/module | 2 +- build/config/module.cxx | 12 +- build/config/operation.cxx | 56 ++++++++- build/file | 36 ++++++ build/file.cxx | 68 +++++++++++ build/file.ixx | 12 ++ build/module | 4 +- build/operation | 67 ++++++++++- build/operation.cxx | 140 +++++++++++++++++++++- build/parser | 2 +- build/parser.cxx | 11 +- build/scope | 7 +- build/string-table | 3 + build/target | 70 +++++------ build/target.cxx | 10 +- 18 files changed, 522 insertions(+), 271 deletions(-) create mode 100644 build/file create mode 100644 build/file.cxx create mode 100644 build/file.ixx (limited to 'build') 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 // uid_t #include // struct passwd, getpwuid() -#include -#include -#include #include -#include // make_move_iterator() +#include #include //@@ TMP, for dump() #include #include @@ -27,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -34,9 +32,6 @@ #include #include #include -#include - -#include #include 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 ("", 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> 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 + // 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 - // 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 (); } // 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 (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 (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 (oid);}); + } + act = action (mid, oid); current_mode = oif->mode; - current_rules = &rules[o]; - - level4 ([&]{trace << "start operation batch " << oif->name - << ", id " << static_cast (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> 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 (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 (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 +#include + 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 + +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 + +#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 + +#include + +#include +#include +#include +#include + +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; + using module_init_function = void (scope& root, scope& base, const location&); + using module_map = std::unordered_map; 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 #include +#include #include #include // reference_wrapper +#include #include 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 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 #include +#include +#include // reference_wrapper + +#include +#include +#include +#include +#include +#include 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 (); + + // 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> psp; + + for (void* v: ts) + { + target& t (*static_cast (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_ {"", 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 (); 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 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 (vec_.size ());} + bool + empty () const {return vec_.empty ();} + private: using key_type = map_key; using value_type = string_table_element; 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> 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> map; typedef map_iterator_adapter 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 (**i, false); unique_ptr 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 (**i, true); } ostream& - operator<< (ostream& os, const target_set::key& k) + operator<< (ostream& os, const target_key& k) { os << k.type->name << '{'; -- cgit v1.1