diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-08-31 13:45:57 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-08-31 13:45:57 +0200 |
commit | 5974cab56148a18628bfb423189e016ade2d40f9 (patch) | |
tree | 472a7966d0e1c5725f0736c73812cbdeaab827dc /build | |
parent | 2a9d673f298b623db061ee85d397563d644c8268 (diff) |
Rework scoping logic
Now the src directory is entered into the scope map and points to
the same scope as out. This means that targets that are in src,
not out (e.g., source files) will "see" rules, variables, etc.
This becomes important when we try, for example, to install a
source file (say, a header) from src: we need the rule as well
as the install.* variables.
Diffstat (limited to 'build')
-rw-r--r-- | build/algorithm.cxx | 2 | ||||
-rw-r--r-- | build/algorithm.ixx | 4 | ||||
-rw-r--r-- | build/b.cxx | 66 | ||||
-rw-r--r-- | build/bin/module.cxx | 2 | ||||
-rw-r--r-- | build/cli/module.cxx | 2 | ||||
-rw-r--r-- | build/config/module.cxx | 6 | ||||
-rw-r--r-- | build/config/operation.cxx | 14 | ||||
-rw-r--r-- | build/context.cxx | 7 | ||||
-rw-r--r-- | build/cxx/compile.cxx | 4 | ||||
-rw-r--r-- | build/cxx/link.cxx | 4 | ||||
-rw-r--r-- | build/cxx/module.cxx | 2 | ||||
-rw-r--r-- | build/dist/module.cxx | 2 | ||||
-rw-r--r-- | build/dist/operation.cxx | 2 | ||||
-rw-r--r-- | build/dist/rule.cxx | 2 | ||||
-rw-r--r-- | build/dump.cxx | 72 | ||||
-rw-r--r-- | build/file | 15 | ||||
-rw-r--r-- | build/file.cxx | 93 | ||||
-rw-r--r-- | build/install/module.cxx | 2 | ||||
-rw-r--r-- | build/operation.cxx | 10 | ||||
-rw-r--r-- | build/parser | 9 | ||||
-rw-r--r-- | build/parser.cxx | 160 | ||||
-rw-r--r-- | build/prerequisite.cxx | 2 | ||||
-rw-r--r-- | build/rule-map | 6 | ||||
-rw-r--r-- | build/scope | 63 | ||||
-rw-r--r-- | build/scope.cxx | 162 | ||||
-rw-r--r-- | build/search.cxx | 4 | ||||
-rw-r--r-- | build/target.cxx | 2 | ||||
-rw-r--r-- | build/test/module.cxx | 2 |
28 files changed, 475 insertions, 246 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx index f462bb8..9a2f9fb 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -323,7 +323,7 @@ namespace build if (rs == nullptr) // Could be outside any project. return; - const dir_path& out_root (rs->path ()); + const dir_path& out_root (rs->out_path ()); // If t is a directory (name is empty), say foo/bar/, then // t is bar and its parent directory is foo/. diff --git a/build/algorithm.ixx b/build/algorithm.ixx index ac2b1fc..72a04fe 100644 --- a/build/algorithm.ixx +++ b/build/algorithm.ixx @@ -109,7 +109,7 @@ namespace build t, a.operation () != clean_id ? dir_path () - : t.strong_scope ().path ()); + : t.strong_scope ().out_path ()); } inline void @@ -122,7 +122,7 @@ namespace build // through groups since the group target should clean eveything // up. A bit of an optimization. // - search_and_match_prerequisites (a, t, t.strong_scope ().path ()); + search_and_match_prerequisites (a, t, t.strong_scope ().out_path ()); } target_state diff --git a/build/b.cxx b/build/b.cxx index 38a944c..d2d813a 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -401,7 +401,7 @@ main (int argc, char* argv[]) // as a result of one of the preceding target processing. // // If we know src_root, set that variable as well. This could - // be of use to the bootstrap file (other than src-root.build, + // be of use to the bootstrap files (other than src-root.build, // which, BTW, doesn't need to exist if src_root == out_root). // scope& rs (create_root (out_root, src_root)); @@ -410,50 +410,48 @@ main (int argc, char* argv[]) // See if the bootstrap process set/changed src_root. // - { - value& v (rs.assign ("src_root")); + value& v (rs.assign ("src_root")); - if (v) - { - // If we also have src_root specified by the user, make - // sure they match. - // - const dir_path& p (as<dir_path> (v)); + if (v) + { + // If we also have src_root specified by the user, make + // sure they match. + // + const dir_path& p (as<dir_path> (v)); - if (src_root.empty ()) - src_root = p; - else if (src_root != p) - fail << "bootstrapped src_root " << p << " does not match " - << "specified " << src_root; - } - else + if (src_root.empty ()) + src_root = p; + else if (src_root != p) + fail << "bootstrapped src_root " << p << " does not match " + << "specified " << src_root; + } + else + { + // Neither bootstrap nor the user produced src_root. + // + if (src_root.empty ()) { - // Bootstrap didn't produce src_root. + // If it also wasn't explicitly specified, see if it is + // the same as out_root. // - if (src_root.empty ()) + if (is_src_root (out_root)) + src_root = out_root; + else { - // If it also wasn't explicitly specified, see if it is - // the same as out_root. + // If not, then assume we are running from src_base + // and calculate src_root based on out_root/out_base. // - if (is_src_root (out_root)) - src_root = out_root; - else - { - // If not, then assume we are running from src_base - // and calculate src_root based on out_root/out_base. - // - src_base = work; - src_root = src_base.directory (out_base.leaf (out_root)); - guessing = true; - } + src_base = work; + src_root = src_base.directory (out_base.leaf (out_root)); + guessing = true; } - - v = src_root; } - rs.src_path_ = &as<dir_path> (v); + v = src_root; } + setup_root (rs); + // At this stage we should have both roots and out_base figured // out. If src_base is still undetermined, calculate it. // diff --git a/build/bin/module.cxx b/build/bin/module.cxx index 760c90d..ba10181 100644 --- a/build/bin/module.cxx +++ b/build/bin/module.cxx @@ -37,7 +37,7 @@ namespace build bool first) { tracer trace ("bin::init"); - level4 ([&]{trace << "for " << b.path ();}); + level4 ([&]{trace << "for " << b.out_path ();}); // Register target types. // diff --git a/build/cli/module.cxx b/build/cli/module.cxx index 0e81db3..f8d1522 100644 --- a/build/cli/module.cxx +++ b/build/cli/module.cxx @@ -36,7 +36,7 @@ namespace build bool first) { tracer trace ("cli::init"); - level4 ([&]{trace << "for " << base.path ();}); + level4 ([&]{trace << "for " << base.out_path ();}); // Make sure the cxx module has been loaded since we need its // targets types (?xx{}). Note that we don't try to load it diff --git a/build/config/module.cxx b/build/config/module.cxx index 5ecf2d3..74bcec4 100644 --- a/build/config/module.cxx +++ b/build/config/module.cxx @@ -42,7 +42,7 @@ namespace build return; } - const dir_path& out_root (r.path ()); + const dir_path& out_root (r.out_path ()); level4 ([&]{trace << "for " << out_root;}); // Register meta-operations. @@ -52,7 +52,11 @@ namespace build // Register alias and fallback rule for the configure meta-operation. // + global_scope->rules.insert<file> ( + configure_id, 0, "file", file_rule::instance); + r.rules.insert<alias> (configure_id, 0, "alias", alias_rule::instance); + r.rules.insert<file> (configure_id, 0, "", fallback_rule::instance); r.rules.insert<target> (configure_id, 0, "", fallback_rule::instance); // Load config.build if one exists. diff --git a/build/config/operation.cxx b/build/config/operation.cxx index b1c6239..424fd46 100644 --- a/build/config/operation.cxx +++ b/build/config/operation.cxx @@ -65,7 +65,7 @@ namespace build static void save_config (scope& root) { - const dir_path& out_root (root.path ()); + const dir_path& out_root (root.out_path ()); path f (out_root / config_file); text << (verb ? "config::save_config " : "save ") << f; @@ -134,7 +134,7 @@ namespace build { tracer trace ("configure_project"); - const dir_path& out_root (root.path ()); + const dir_path& out_root (root.out_path ()); const dir_path& src_root (root.src_path ()); // Make sure the directories exist. @@ -179,7 +179,7 @@ namespace build // @@ Strictly speaking we need to check whether the config // module was loaded for this subproject. // - if (nroot.path () != out_nroot) // This subproject was not loaded. + if (nroot.out_path () != out_nroot) // This subproject not loaded. continue; configure_project (a, nroot); @@ -273,7 +273,7 @@ namespace build action_targets& ts) { tracer trace ("disfigure_search"); - level5 ([&]{trace << "collecting " << root.path ();}); + level5 ([&]{trace << "collecting " << root.out_path ();}); ts.push_back (&root); } @@ -287,7 +287,7 @@ namespace build bool m (false); // Keep track of whether we actually did anything. - const dir_path& out_root (root.path ()); + const dir_path& out_root (root.out_path ()); const dir_path& src_root (root.src_path ()); // Disfigure subprojects. Since we don't load buildfiles during @@ -313,7 +313,7 @@ namespace build if (!val) val = is_src_root (out_nroot) ? out_nroot : (src_root / pd); - nroot.src_path_ = &as<dir_path> (val); + setup_root (nroot); bootstrap_src (nroot); @@ -398,7 +398,7 @@ namespace build // target& t ( targets.insert ( - dir::static_type, root.path (), "", nullptr, trace).first); + dir::static_type, root.out_path (), "", nullptr, trace).first); if (!quiet) info << diag_done (a, t); diff --git a/build/context.cxx b/build/context.cxx index aaeb426..fa1ea8f 100644 --- a/build/context.cxx +++ b/build/context.cxx @@ -77,7 +77,8 @@ namespace build // On POSIX, however, this is a real path. See the comment in // <build/path-map> for details. // - global_scope = &scopes[dir_path ("/")]; + global_scope = scopes.insert ( + dir_path ("/"), nullptr, true, false)->second; global_scope->assign ("work") = work; global_scope->assign ("home") = home; @@ -188,14 +189,14 @@ namespace build src_out (const dir_path& out, scope& s) { scope& rs (*s.root_scope ()); - return src_out (out, rs.path (), rs.src_path ()); + return src_out (out, rs.out_path (), rs.src_path ()); } dir_path out_src (const dir_path& src, scope& s) { scope& rs (*s.root_scope ()); - return out_src (src, rs.path (), rs.src_path ()); + return out_src (src, rs.out_path (), rs.src_path ()); } dir_path diff --git a/build/cxx/compile.cxx b/build/cxx/compile.cxx index 514c57a..f9351f2 100644 --- a/build/cxx/compile.cxx +++ b/build/cxx/compile.cxx @@ -88,7 +88,7 @@ namespace build const dir_path* amlg ( a.operation () != clean_id ? nullptr - : &t.strong_scope ().path ()); + : &t.strong_scope ().out_path ()); link::search_paths_cache lib_paths; // Extract lazily. @@ -213,7 +213,7 @@ namespace build return; const dir_path& out_base (t.dir); - const dir_path& out_root (rs->path ()); + const dir_path& out_root (rs->out_path ()); if (auto l = t[var]) { diff --git a/build/cxx/link.cxx b/build/cxx/link.cxx index 3b628b5..2554930 100644 --- a/build/cxx/link.cxx +++ b/build/cxx/link.cxx @@ -492,7 +492,7 @@ namespace build const dir_path* amlg ( a.operation () != clean_id ? nullptr - : &t.strong_scope ().path ()); + : &t.strong_scope ().out_path ()); for (prerequisite_member p: group_prerequisite_members (a, t)) { @@ -573,7 +573,7 @@ namespace build // altogether. So we are going to use the target's project. // root = &t.root_scope (); - out_root = &root->path (); + out_root = &root->out_path (); src_root = &root->src_path (); } diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx index 7171738..6b7da80 100644 --- a/build/cxx/module.cxx +++ b/build/cxx/module.cxx @@ -35,7 +35,7 @@ namespace build bool first) { tracer trace ("cxx::init"); - level4 ([&]{trace << "for " << b.path ();}); + level4 ([&]{trace << "for " << b.out_path ();}); // Initialize the bin module. Only do this if it hasn't already // been loaded so that we don't overwrite user's bin.* settings. diff --git a/build/dist/module.cxx b/build/dist/module.cxx index ab27df7..48c1fd0 100644 --- a/build/dist/module.cxx +++ b/build/dist/module.cxx @@ -40,7 +40,7 @@ namespace build return; } - const dir_path& out_root (r.path ()); + const dir_path& out_root (r.out_path ()); level4 ([&]{trace << "for " << out_root;}); // Register meta-operation. diff --git a/build/dist/operation.cxx b/build/dist/operation.cxx index 3c6ec98..c4d022b 100644 --- a/build/dist/operation.cxx +++ b/build/dist/operation.cxx @@ -79,7 +79,7 @@ namespace build if (rs == nullptr) fail << "out of project target " << t; - const dir_path& out_root (rs->path ()); + const dir_path& out_root (rs->out_path ()); const dir_path& src_root (rs->src_path ()); if (out_root == src_root) diff --git a/build/dist/rule.cxx b/build/dist/rule.cxx index bda2ef7..6977676 100644 --- a/build/dist/rule.cxx +++ b/build/dist/rule.cxx @@ -24,7 +24,7 @@ namespace build recipe rule:: apply (action a, target& t, const match_result&) const { - const dir_path& out_root (t.root_scope ().path ()); + const dir_path& out_root (t.root_scope ().out_path ()); for (prerequisite_member p: group_prerequisite_members (a, t)) { diff --git a/build/dump.cxx b/build/dump.cxx index f3b8c4b..7f4502e 100644 --- a/build/dump.cxx +++ b/build/dump.cxx @@ -4,7 +4,6 @@ #include <build/dump> -#include <set> #include <string> #include <cassert> @@ -142,19 +141,21 @@ namespace build dump_scope (ostream& os, string& ind, action a, - scope& p, - scope_map::iterator& i, - set<const target*>& rts) + scope_map::const_iterator& i) { + scope& p (*i->second); + const dir_path& d (i->first); + ++i; + // We don't want the extra notations (e.g., ~/) provided by // diag_relative() since we want the path to be relative to - // the global scope. + // the outer scope. // - os << ind << relative (p.path ()) << ":" << endl + os << ind << relative (d) << ":" << endl << ind << '{'; const dir_path* orb (relative_base); - relative_base = &p.path (); + relative_base = &d; ind += " "; @@ -179,10 +180,25 @@ namespace build vb = true; } - // Nested scopes of which we are a parent. + // Nested scopes of which we are an immediate parent. // - for (auto e (scopes.end ()); i != e && i->second.parent_scope () == &p; ) + for (auto e (scopes.end ()); i != e && i->second->parent_scope () == &p;) { + // See what kind of scope entry this is. It can be: + // + // 1. Out-of-project scope. + // 2. In-project out entry. + // 3. In-project src entry. + // + // We want to print #2 and #3 as a single, unified scope. + // + scope& s (*i->second); + if (s.src_path_ != s.out_path_ && s.src_path_ == &i->first) + { + ++i; + continue; + } + if (vb) { os << endl; @@ -193,9 +209,7 @@ namespace build os << endl; // Extra newline between scope blocks. os << endl; - scope& s (i->second); - dump_scope (os, ind, a, s, ++i, rts); - + dump_scope (os, ind, a, i); sb = true; } @@ -204,33 +218,8 @@ namespace build for (const auto& pt: targets) { const target& t (*pt); - const scope* ts (&t.base_scope ()); - - bool f (false); - if (ts == &p) - { - // If this is the global scope, check that this target hasn't - // been handled by the src logic below. - // - f = (ts != global_scope || rts.find (&t) == rts.end ()); - } - // If this target is in the global scope and we have a corresponding - // src directory (i.e., we are a scope inside a project), check - // whether this target is in our src. - // - else if (ts == global_scope && p.src_path_ != nullptr) - { - if (t.dir.sub (p.src_path ())) - { - // Check that it hasn't already been handled by a more qualified - // scope. - // - f = rts.insert (&t).second; - } - } - - if (!f) + if (&p != &t.base_scope ()) continue; if (vb || sb) @@ -254,14 +243,11 @@ namespace build dump (action a) { auto i (scopes.begin ()); - scope& g (i->second); // Global scope. - assert (&g == global_scope); + assert (i->second == global_scope); string ind; - set<const target*> rts; - ostream& os (*diag_stream); - dump_scope (os, ind, a, g, ++i, rts); + dump_scope (os, ind, a, i); os << endl; } } @@ -9,11 +9,11 @@ #include <string> #include <build/types> +#include <build/scope> #include <build/variable> // list_value namespace build { - class scope; class target; class location; class prerequisite_key; @@ -68,6 +68,19 @@ namespace build scope& create_root (const dir_path& out_root, const dir_path& src_root); + // Setup root scope. Note that it assume the src_root variable + // has already been set. + // + void + setup_root (scope&); + + // Setup the base scope (set *_base variables, etc). + // + scope& + setup_base (scope_map::iterator, + const dir_path& out_base, + const dir_path& src_base); + // Bootstrap the project's root scope, the out part. // void diff --git a/build/file.cxx b/build/file.cxx index b303740..dd9b294 100644 --- a/build/file.cxx +++ b/build/file.cxx @@ -115,7 +115,16 @@ namespace build scope& create_root (const dir_path& out_root, const dir_path& src_root) { - scope& rs (scopes.insert (out_root, true).first); + auto i (scopes.insert (out_root, nullptr, true, true)); + scope& rs (*i->second); + + // Set out_path. src_path is set in setup_root() below. + // + if (rs.out_path_ != &i->first) + { + assert (rs.out_path_ == nullptr); + rs.out_path_ = &i->first; + } // Enter built-in meta-operation and operation names. Loading of // modules (via the src bootstrap; see below) can result in @@ -168,9 +177,70 @@ namespace build } void + setup_root (scope& s) + { + value& v (s.assign ("src_root")); + assert (v); + + // Register and set src_path. + // + if (s.src_path_ == nullptr) + s.src_path_ = &scopes.insert (as<dir_path> (v), &s, false, true)->first; + } + + scope& + setup_base (scope_map::iterator i, + const dir_path& out_base, + const dir_path& src_base) + { + scope& s (*i->second); + + // Set src/out_path. The key (i->first) can be either out_base + // or src_base. + // + if (s.out_path_ == nullptr) + { + s.out_path_ = + i->first == out_base + ? &i->first + : &scopes.insert (out_base, &s, true, false)->first; + } + + if (s.src_path_ == nullptr) + { + s.src_path_ = + i->first == src_base + ? &i->first + : &scopes.insert (src_base, &s, false, false)->first; + } + + // Set src/out_base variables. + // + { + value& v (s.assign ("out_base")); + + if (!v) + v = out_base; + else + assert (as<dir_path> (v) == out_base); + } + + { + value& v (s.assign ("src_base")); + + if (!v) + v = src_base; + else + assert (as<dir_path> (v) == src_base); + } + + return s; + } + + void bootstrap_out (scope& root) { - path bf (root.path () / path ("build/bootstrap/src-root.build")); + path bf (root.out_path () / path ("build/bootstrap/src-root.build")); if (!file_exists (bf)) return; @@ -334,7 +404,7 @@ namespace build bool r (false); - const dir_path& out_root (root.path ()); + const dir_path& out_root (root.out_path ()); const dir_path& src_root (root.src_path ()); path bf (src_root / path ("build/bootstrap.build")); @@ -370,7 +440,7 @@ namespace build if (scope* aroot = root.parent_scope ()->root_scope ()) { - const dir_path& ad (aroot->path ()); + const dir_path& ad (aroot->out_path ()); dir_path rd (ad.relative (out_root)); // If we already have the amalgamation variable set, verify @@ -526,7 +596,7 @@ namespace build return; const dir_path& d (as<dir_path> (*l)); - dir_path out_root (root.path () / d); + dir_path out_root (root.out_path () / d); out_root.normalize (); // src_root is a bit more complicated. Here we have three cases: @@ -557,7 +627,7 @@ namespace build } } - rs.src_path_ = &as<dir_path> (v); + setup_root (rs); bootstrap_src (rs); create_bootstrap_outer (rs); @@ -578,7 +648,7 @@ namespace build if (n.pair != '\0') continue; // Skip project names. - dir_path out_root (root.path () / n.dir); + dir_path out_root (root.out_path () / n.dir); if (!out_base.sub (out_root)) continue; @@ -595,7 +665,7 @@ namespace build ? out_root : (root.src_path () / n.dir); - rs.src_path_ = &as<dir_path> (v); + setup_root (rs); bootstrap_src (rs); @@ -671,7 +741,7 @@ namespace build if (i != m.end ()) { const dir_path& d ((*i).second); - out_root = r->path () / d; + out_root = r->out_path () / d; fallback_src_root = r->src_path () / d; break; } @@ -745,8 +815,6 @@ namespace build if (!src_root.empty () && p != src_root) fail (loc) << "bootstrapped src_root " << p << " does not match " << "discovered " << src_root; - - root.src_path_ = &p; } // Otherwise, use fallback if available. // @@ -754,12 +822,13 @@ namespace build { value& v (root.assign ("src_root")); v = move (fallback_src_root); - root.src_path_ = &as<dir_path> (v); } else fail (loc) << "unable to determine src_root for imported " << project << info << "consider configuring " << out_root; + setup_root (root); + bootstrap_src (root); // Bootstrap outer roots if any. Loading will be done by diff --git a/build/install/module.cxx b/build/install/module.cxx index 5bf2876..454ff3e 100644 --- a/build/install/module.cxx +++ b/build/install/module.cxx @@ -109,7 +109,7 @@ namespace build return; } - const dir_path& out_root (r.path ()); + const dir_path& out_root (r.out_path ()); level4 ([&]{trace << "for " << out_root;}); // Register the install operation. diff --git a/build/operation.cxx b/build/operation.cxx index 55a926a..9cc8025 100644 --- a/build/operation.cxx +++ b/build/operation.cxx @@ -62,13 +62,11 @@ namespace build load_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. + // mean it was already setup as a base scope; it can be the + // same as root. // - scope& base (scopes[out_base]); - - base.assign ("out_base") = out_base; - base.src_path_ = &as<dir_path> (base.assign ("src_base") = src_base); + auto i (scopes.insert (out_base, nullptr, true, false)); + scope& base (setup_base (i, out_base, src_base)); // Load the buildfile unless it has already been loaded. // diff --git a/build/parser b/build/parser index 3374ac0..c5c33a1 100644 --- a/build/parser +++ b/build/parser @@ -100,6 +100,15 @@ namespace build // Utilities. // private: + + // Switch to a new current scope. Note that this function might + // also have to switch to a new root scope if the new current + // scope is in another project. So both must be saved and + // restored. + // + void + switch_scope (const dir_path&); + // Switch to new root scope and return the previous one. // scope* diff --git a/build/parser.cxx b/build/parser.cxx index 9c50d17..470efdb 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -101,37 +101,31 @@ namespace build // @@ Is this the only place where it is valid? Probably also // in var namespace. // - next (t, tt); print (t, tt); continue; } else if (n == "source") { - next (t, tt); source (t, tt); continue; } else if (n == "include") { - next (t, tt); include (t, tt); continue; } else if (n == "import") { - next (t, tt); import (t, tt); continue; } else if (n == "export") { - next (t, tt); export_ (t, tt); continue; } else if (n == "using") { - next (t, tt); using_ (t, tt); continue; } @@ -198,26 +192,23 @@ namespace build dir_path p (move (ns[0].dir)); // Steal. + // Relative scopes are opened relative to out, not src. + // if (p.relative ()) - p = prev.path () / p; + p = scope_->out_path () / p; p.normalize (); - scope_ = &scopes[p]; - // If this is a known project root scope, switch the - // parser state to use it. - // - scope* ors (switch_root (scope_->root () ? scope_ : root_)); - - if (ors != root_) - level4 ([&]{trace (nloc) << "switching to root scope " << p;}); + scope* ors (root_); + scope* ocs (scope_); + switch_scope (p); // A directory scope can contain anything that a top level can. // clause (t, tt); + scope_ = ocs; switch_root (ors); - scope_ = &prev; } else { @@ -269,11 +260,11 @@ namespace build path& d (tn.dir); if (d.empty ()) - d = scope_->path (); // Already normalized. + d = scope_->out_path (); // Already normalized. else { if (d.relative ()) - d = scope_->path () / d; + d = scope_->out_path () / d; d.normalize (); } @@ -301,9 +292,6 @@ namespace build if (n.qualified ()) fail (nloc) << "project name in scope/target " << n; - target* ot (target_); - scope* os (scope_); - if (n.directory ()) { // The same code as in directory scope handling code above. @@ -311,18 +299,29 @@ namespace build dir_path p (move (n.dir)); if (p.relative ()) - p = scope_->path () / p; + p = scope_->out_path () / p; p.normalize (); - scope_ = &scopes[move (p)]; + + scope* ors (root_); + scope* ocs (scope_); + switch_scope (p); + + variable (t, tt, move (var), tt); + + scope_ = ocs; + switch_root (ors); + } else + { + target* ot (target_); target_ = &enter_target (move (n)); - variable (t, tt, move (var), tt); + variable (t, tt, move (var), tt); - scope_ = os; - target_ = ot; + target_ = ot; + } } // Dependency declaration. // @@ -416,6 +415,7 @@ namespace build // The rest should be a list of buildfiles. Parse them as names // to get variable expansion and directory prefixes. // + next (t, tt); const location l (get_location (t, &path_)); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) @@ -435,7 +435,7 @@ namespace build // to the current directory scope. // if (src_root_ != nullptr && p.relative ()) - p = src_out (scope_->path (), *out_root_, *src_root_) / p; + p = src_out (scope_->out_path (), *out_root_, *src_root_) / p; p.normalize (); @@ -504,6 +504,7 @@ namespace build // The rest should be a list of buildfiles. Parse them as names // to get variable expansion and directory prefixes. // + next (t, tt); const location l (get_location (t, &path_)); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) @@ -536,7 +537,7 @@ namespace build if (p.relative ()) { - out_base = scope_->path () / p.directory (); + out_base = scope_->out_path () / p.directory (); out_base.normalize (); } else @@ -555,28 +556,25 @@ namespace build : out_src (p.directory (), *out_root_, *src_root_); } - // Create and bootstrap root scope(s) of subproject(s) that - // this out_base belongs to. If any were created, load them - // and update parser state. Note that we need to do this - // before figuring out absolute buildfile path since we may - // switch the project root (i.e., include into a sub-project). + // Switch the scope. Note that we need to do this before figuring + // out the absolute buildfile path since we may switch the project + // root and src_root with it (i.e., include into a sub-project). // - scope* ors (switch_root (&create_bootstrap_inner (*root_, out_base))); - - if (root_ != ors) - load_root_pre (*root_); // Loads outer roots recursively. + scope* ors (root_); + scope* ocs (scope_); + switch_scope (out_base); - // Determine src_base and buildfile, if relative. + // Use the new scope's src_base to get absolute buildfile path + // if it is relative. // - dir_path src_base (src_out (out_base, *out_root_, *src_root_)); - if (p.relative ()) - p = src_base / p.leaf (); + p = scope_->src_path () / p.leaf (); if (!root_->buildfiles.insert (p).second) // Note: may be "new" root. { level4 ([&]{trace (l) << "skipping already included " << p;}); - switch_root (ors); // Restore old root. + scope_ = ocs; + switch_root (ors); continue; } @@ -599,13 +597,6 @@ namespace build lexer* ol (lexer_); lexer_ = &l; - scope* os (scope_); - scope_ = &scopes[out_base]; - - scope_->assign ("out_base") = move (out_base); - scope_->src_path_ = &as<dir_path> ( - scope_->assign ("src_base") = move (src_base)); - target* odt (default_target_); default_target_ = nullptr; @@ -622,10 +613,10 @@ namespace build level4 ([&]{trace (t) << "leaving " << p;}); default_target_ = odt; - scope_ = os; lexer_ = ol; path_ = op; + scope_ = ocs; switch_root (ors); } @@ -643,6 +634,8 @@ namespace build if (src_root_ == nullptr) fail (t) << "import during bootstrap"; + next (t, tt); + // General import format: // // import [<var>=](<project>|<project>/<target>])+ @@ -704,12 +697,15 @@ namespace build // This should be temp_scope. // - if (ps == nullptr || ps->path () != scope_->path ()) + if (ps == nullptr || ps->out_path () != scope_->out_path ()) fail (t) << "export outside export stub"; // The rest is a value. Parse it as names to get variable expansion. // build::import() will check the names, if required. // + lexer_->mode (lexer_mode::value); + next (t, tt); + if (tt != type::newline && tt != type::eos) export_value_ = names (t, tt); @@ -727,6 +723,7 @@ namespace build // The rest should be a list of module names. Parse them as names // to get variable expansion, etc. // + next (t, tt); const location l (get_location (t, &path_)); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) @@ -751,6 +748,13 @@ namespace build void parser:: print (token& t, token_type& tt) { + // Parse the rest as names to get variable expansion, etc. Switch + // to the variable value lexing mode so that we don't treat special + // characters (e.g., ':') as the end of the names. + // + lexer_->mode (lexer_mode::value); + + next (t, tt); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); @@ -786,6 +790,8 @@ namespace build if (var.pairs != '\0') lexer_->mode (lexer_mode::pairs, var.pairs); + else + lexer_->mode (lexer_mode::value); next (t, tt); names_type vns (tt != type::newline && tt != type::eos @@ -1452,6 +1458,54 @@ namespace build return bs; } + void parser:: + switch_scope (const dir_path& p) + { + tracer trace ("parser::switch_scope", &path_); + + // First, enter the scope into the map and see if it is in any + // project. If it is not, then there is nothing else to do. + // + auto i (scopes.insert (p, nullptr, true, false)); + scope_ = i->second; + scope* rs (scope_->root_scope ()); + + if (rs == nullptr) + return; + + // Path p can be src_base or out_base. Figure out which one it is. + // + dir_path out_base (p.sub (rs->out_path ()) ? p : src_out (p, *rs)); + + // Create and bootstrap root scope(s) of subproject(s) that this + // scope may belong to. If any were created, load them. Note that + // we need to do this before figuring out src_base since we may + // switch the root project (and src_root with it). + // + { + scope* nrs (&create_bootstrap_inner (*rs, out_base)); + + if (rs != nrs) + { + load_root_pre (*nrs); // Load outer roots recursively. + rs = nrs; + } + } + + // Switch to the new root scope. + // + if (rs != root_) + { + level4 ([&]{trace << "switching to root scope " << rs->out_path ();}); + switch_root (rs); + } + + // Now we can figure out src_base and finish setting the scope. + // + dir_path src_base (src_out (out_base, *rs)); + setup_base (i, move (out_base), move (src_base)); + } + scope* parser:: switch_root (scope* nr) { @@ -1489,7 +1543,7 @@ namespace build // if (default_target_ == nullptr || // No targets in this buildfile. targets.find (dir::static_type, // Explicit current dir target. - scope_->path (), + scope_->out_path (), "", nullptr, trace) != targets.end ()) @@ -1501,7 +1555,7 @@ namespace build target& ct ( targets.insert ( - dir::static_type, scope_->path (), "", nullptr, trace).first); + dir::static_type, scope_->out_path (), "", nullptr, trace).first); prerequisite& p ( scope_->prerequisites.insert ( diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx index 2437826..1f9f68b 100644 --- a/build/prerequisite.cxx +++ b/build/prerequisite.cxx @@ -29,7 +29,7 @@ namespace build // else if (!pk.tk.dir->absolute ()) { - string s (diag_relative (pk.scope->path (), false)); + string s (diag_relative (pk.scope->out_path (), false)); if (!s.empty ()) os << s << ':'; diff --git a/build/rule-map b/build/rule-map index 0ef0036..0ce49e7 100644 --- a/build/rule-map +++ b/build/rule-map @@ -52,6 +52,9 @@ namespace build return map_.size () > oid ? &map_[oid] : nullptr; } + bool + empty () const {return map_.empty ();} + private: std::vector<target_type_rule_map> map_; }; @@ -92,6 +95,9 @@ namespace build explicit rule_map (meta_operation_id mid = perform_id): mid_ (mid) {} + bool + empty () const {return map_.empty () && next_ == nullptr;} + private: meta_operation_id mid_; operation_rule_map map_; diff --git a/build/scope b/build/scope index 50c4f6b..2afc9f4 100644 --- a/build/scope +++ b/build/scope @@ -24,13 +24,18 @@ namespace build class scope { public: + // Absolute and normalized. + // const dir_path& - path () const {return *path_;} // Absolute and normalized. + out_path () const {return *out_path_;} const dir_path& - src_path () const {return *src_path_;} // Corresponding src path. + src_path () const {return *src_path_;} - const dir_path* src_path_ {nullptr}; // Cached src_{root,base} var value. + // These are pointers to the keys in scope_map. + // + const dir_path* out_path_ {nullptr}; + const dir_path* src_path_ {nullptr}; scope* parent_scope () const {return parent_;} @@ -154,6 +159,22 @@ namespace build public: loaded_module_map modules; // Only on root scope. + public: + bool + empty () const + { + return + vars.empty () && + target_vars.empty () && + prerequisites.empty () && + meta_operations.empty () && + operations.empty () && + buildfiles.empty () && + target_types.empty () && + rules.empty () && + modules.empty (); + } + private: friend class scope_map; friend class temp_scope; @@ -165,7 +186,6 @@ namespace build scope () = default; - const dir_path* path_; // Pointer to the key in scope_map. scope* parent_; scope* root_; scope* strong_ = nullptr; // Only set on root sopes. @@ -185,33 +205,35 @@ namespace build public: temp_scope (scope& p) { - path_ = p.path_; + out_path_ = p.out_path_; + src_path_ = p.src_path_; parent_ = &p; root_ = p.root_; // No need to copy strong_ since we are never root scope. } }; - using scope_map_base = butl::dir_path_map<scope>; - class scope_map: public scope_map_base + class scope_map { public: + using map_type = butl::dir_path_map<scope*>; + using iterator = map_type::iterator; + using const_iterator = map_type::const_iterator; + // Note that we assume the first insertion into the map is that - // of the global scope. + // of the global scope. If the passed scope pointer is not NULL, + // then insert this scope instead of a new one. // - std::pair<scope&, bool> - insert (const dir_path&, bool root); - - scope& - operator[] (const dir_path& p) {return insert (p, false).first;} + iterator + insert (const dir_path&, scope*, bool parent, bool root); // Find the most qualified scope that encompasses this path. // scope& - find (const dir_path&); + find (const dir_path&) const; scope& - find (const path& p) + find (const path& p) const { // Natural thing to do here would be to call find (p.directory ()). // However, there could be a situation where the passed path is a @@ -220,6 +242,17 @@ namespace build // return find (dir_path (p.string ())); } + + const_iterator begin () const {return map_.begin ();} + const_iterator end () const {return map_.end ();} + + void + clear (); + + ~scope_map () {clear ();} + + private: + map_type map_; }; extern scope_map scopes; diff --git a/build/scope.cxx b/build/scope.cxx index e6d165e..53e3a53 100644 --- a/build/scope.cxx +++ b/build/scope.cxx @@ -146,93 +146,151 @@ namespace build scope_map scopes; scope* global_scope; - pair<scope&, bool> scope_map:: - insert (const dir_path& k, bool root) + auto scope_map:: + insert (const dir_path& k, scope* ns, bool parent, bool root) -> iterator { - auto er (emplace (k, scope ())); - scope& s (er.first->second); + auto er (map_.emplace (k, nullptr)); + scope*& ps (er.first->second); if (er.second) + ps = ns == nullptr ? new scope : ns; + else if (ns != nullptr && ps != ns) { - scope* p (nullptr); + assert (ps->out_path_ == nullptr || ps->src_path_ == nullptr); - // Update scopes of which we are a new parent/root (unless this - // is the global scope). + if (!ps->empty ()) + fail << "attempt to replace non-empty scope " << k; + + // Un-parent ourselves. We will becomes a new parent below, + // if requested by the caller. // - if (size () > 1) + auto r (map_.find_prefix (k)); // The first entry is ourselves. + for (++r.first; r.first != r.second; ++r.first) + { + scope& c (*r.first->second); + + if (c.parent_ == ps) // No intermediate parent. + c.parent_ = ps->parent_; + } + + delete ps; + ps = ns; + er.second = true; + } + + scope& s (*ps); + + if (parent) + { + if (er.second) { - // The first entry is ourselves. + scope* p (nullptr); + + // Update scopes of which we are a new parent/root (unless this + // is the global scope). Also find our parent while at it. // - auto r (find_prefix (k)); - for (++r.first; r.first != r.second; ++r.first) + if (map_.size () > 1) { - scope& c (r.first->second); - - // The first scope of which we are a parent is the least - // (shortest) one which means there is no other scope - // between it and our parent. + // The first entry is ourselves. + // + auto r (map_.find_prefix (k)); + for (++r.first; r.first != r.second; ++r.first) + { + scope& c (*r.first->second); + + // The child-parent relationship is based on the out hierarchy, + // thus the extra check. + // + if (c.out_path_ != nullptr && !c.out_path_->sub (k)) + continue; + + // The first scope of which we are a parent is the least + // (shortest) one which means there is no other scope + // between it and our parent. + // + if (p == nullptr) + p = c.parent_; + + if (root && c.root_ == p->root_) // No intermediate root. + c.root_ = &s; + + if (p == c.parent_) // No intermediate parent. + c.parent_ = &s; + } + + // We couldn't get the parent from one of its old children + // so we have to find it ourselves. // if (p == nullptr) - p = c.parent_; - - if (root && c.root_ == p->root_) // No intermediate root. - c.root_ = &s; - - if (p == c.parent_) // No intermediate parent. - c.parent_ = &s; + p = &find (k.directory ()); } - // We couldn't get the parent from one of its old children - // so we have to find it ourselves. - // - if (p == nullptr) - p = &find (k.directory ()); + s.parent_ = p; + s.root_ = root ? &s : (p != nullptr ? p->root_ : nullptr); } - - s.path_ = &er.first->first; - s.parent_ = p; - s.root_ = root ? &s : (p != nullptr ? p->root_ : nullptr); - } - else if (root && !s.root ()) - { - // Upgrade to root scope. - // - auto r (find_prefix (k)); - for (++r.first; r.first != r.second; ++r.first) + else if (root && !s.root ()) { - scope& c (r.first->second); + // Upgrade to root scope. + // + auto r (map_.find_prefix (k)); + for (++r.first; r.first != r.second; ++r.first) + { + scope& c (*r.first->second); - if (c.root_ == s.root_) // No intermediate root. - c.root_ = &s; - } + if (c.root_ == s.root_) // No intermediate root. + c.root_ = &s; + } - s.root_ = &s; + s.root_ = &s; + } } + else + assert (s.parent_ != nullptr); - return pair<scope&, bool> (s, er.second); + return er.first; } // Find the most qualified scope that encompasses this path. // scope& scope_map:: - find (const dir_path& k) + find (const dir_path& k) const { // Normally we would have a scope for the full path so try // that before making any copies. // - auto i (scope_map_base::find (k)); + auto i (map_.find (k)), e (map_.end ()); - if (i != end ()) - return i->second; + if (i != e) + return *i->second; for (dir_path d (k.directory ());; d = d.directory ()) { - auto i (scope_map_base::find (d)); + auto i (map_.find (d)); - if (i != end ()) - return i->second; + if (i != e) + return *i->second; assert (!d.empty ()); // We should have the global scope. } } + + void scope_map:: + clear () + { + for (auto& p: map_) + { + scope* s (p.second); + + if (s->out_path_ == &p.first) + s->out_path_ = nullptr; + + if (s->src_path_ == &p.first) + s->src_path_ = nullptr; + + if (s->out_path_ == nullptr && s->src_path_ == nullptr) + delete s; + } + + map_.clear (); + } } diff --git a/build/search.cxx b/build/search.cxx index dfb603d..32302d6 100644 --- a/build/search.cxx +++ b/build/search.cxx @@ -33,7 +33,7 @@ namespace build d = *tk.dir; // Already normalized. else { - d = pk.scope->path (); + d = pk.scope->out_path (); if (!tk.dir->empty ()) { @@ -148,7 +148,7 @@ namespace build d = *tk.dir; // Already normalized. else { - d = pk.scope->path (); + d = pk.scope->out_path (); if (!tk.dir->empty ()) { diff --git a/build/target.cxx b/build/target.cxx index 669ff7a..9e0bea7 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -378,7 +378,7 @@ namespace build if (pk.tk.dir->relative ()) { dir_paths sp; - sp.push_back (src_out (pk.scope->path (), *pk.scope)); // src_base + sp.push_back (pk.scope->src_path ()); // src_base return search_existing_file (pk, sp); } else diff --git a/build/test/module.cxx b/build/test/module.cxx index 8c0df38..b3378ba 100644 --- a/build/test/module.cxx +++ b/build/test/module.cxx @@ -39,7 +39,7 @@ namespace build return; } - const dir_path& out_root (r.path ()); + const dir_path& out_root (r.out_path ()); level4 ([&]{trace << "for " << out_root;}); // Register the test operation. |