diff options
41 files changed, 600 insertions, 248 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. diff --git a/tests/import/installed/build/bootstrap.build b/tests/import/installed/build/bootstrap.build index 97fd11f..2af1e60 100644 --- a/tests/import/installed/build/bootstrap.build +++ b/tests/import/installed/build/bootstrap.build @@ -1,3 +1,4 @@ project = import-installed amalgamation = # Disabled. using config +using test diff --git a/tests/import/installed/buildfile b/tests/import/installed/buildfile index 276de36..abdc151 100644 --- a/tests/import/installed/buildfile +++ b/tests/import/installed/buildfile @@ -5,5 +5,9 @@ cxx.ext = cxx import libs += lib{z} -#exe{driver}: cxx{driver} $libs -lib{driver}: cxx{driver} $libs +#lib{driver}: cxx{driver} $libs + +exe{driver}: cxx{driver} $libs +exe{driver}: test = true + + diff --git a/tests/scope/amalgamation/build/bootstrap.build b/tests/scope/amalgamation/build/bootstrap.build new file mode 100644 index 0000000..ee73365 --- /dev/null +++ b/tests/scope/amalgamation/build/bootstrap.build @@ -0,0 +1,3 @@ +project = scope-amalgamation +amalgamation = # Disabled. +using config diff --git a/tests/scope/amalgamation/buildfile b/tests/scope/amalgamation/buildfile new file mode 100644 index 0000000..9f80de9 --- /dev/null +++ b/tests/scope/amalgamation/buildfile @@ -0,0 +1 @@ +./: diff --git a/tests/scope/amalgamation/l1/build/bootstrap.build b/tests/scope/amalgamation/l1/build/bootstrap.build new file mode 100644 index 0000000..6bde838 --- /dev/null +++ b/tests/scope/amalgamation/l1/build/bootstrap.build @@ -0,0 +1,16 @@ +project = scope-amalgamation-l1 +using config + +# At this stage we don't know ../ is a project. This +# tests an out-of-project scope that will later be +# replaced with an in-project scope. Note that the +# replacement will only occur if src_root != out_root. +# If they are the same, then this scope will simply +# be "upgraded". +# +$src_root/../: +{ + print 0: $project + print 0: $src_base + print 0: $out_base +} diff --git a/tests/scope/amalgamation/l1/buildfile b/tests/scope/amalgamation/l1/buildfile new file mode 100644 index 0000000..55d8c64 --- /dev/null +++ b/tests/scope/amalgamation/l1/buildfile @@ -0,0 +1,37 @@ +# Out of amalgamation. +# +../../: +{ + print -1: $project + print -1: $src_base + print -1: $out_base +} + +# In amalgamation. +# +../s/: +{ + print 0: $project + print 0: $src_base + print 0: $out_base +} + +# In project. +# +s/: +{ + print 1: $project + print 1: $src_base + print 1: $out_base +} + +# In sub-project. +# +l2/s/: +{ + print 2: $project + print 2: $src_base + print 2: $out_base +} + +./: diff --git a/tests/scope/amalgamation/l1/l2/build/bootstrap.build b/tests/scope/amalgamation/l1/l2/build/bootstrap.build new file mode 100644 index 0000000..0262763 --- /dev/null +++ b/tests/scope/amalgamation/l1/l2/build/bootstrap.build @@ -0,0 +1,2 @@ +project = scope-amalgamation-l2 +using config diff --git a/tests/scope/test-1.out b/tests/scope/test-1.out new file mode 100644 index 0000000..0273bf1 --- /dev/null +++ b/tests/scope/test-1.out @@ -0,0 +1,15 @@ +0: +0: +0: +-1: +-1: +-1: +0: scope-amalgamation +0: /home/boris/work/build2/build2/tests/scope/amalgamation/s/ +0: /home/boris/work/build2/build2/tests/scope/amalgamation/s/ +1: scope-amalgamation-l1 +1: /home/boris/work/build2/build2/tests/scope/amalgamation/l1/s/ +1: /home/boris/work/build2/build2/tests/scope/amalgamation/l1/s/ +2: scope-amalgamation-l2 +2: /home/boris/work/build2/build2/tests/scope/amalgamation/l1/l2/s/ +2: /home/boris/work/build2/build2/tests/scope/amalgamation/l1/l2/s/ diff --git a/tests/scope/test-2.out b/tests/scope/test-2.out new file mode 100644 index 0000000..88f9620 --- /dev/null +++ b/tests/scope/test-2.out @@ -0,0 +1,15 @@ +0: +0: +0: +-1: +-1: +-1: +0: scope-amalgamation +0: /home/boris/work/build2/build2/tests/scope/amalgamation/s/ +0: /home/boris/work/build2/build2/tests/scope/a-out/s/ +1: scope-amalgamation-l1 +1: /home/boris/work/build2/build2/tests/scope/amalgamation/l1/s/ +1: /home/boris/work/build2/build2/tests/scope/a-out/l1/s/ +2: scope-amalgamation-l2 +2: /home/boris/work/build2/build2/tests/scope/amalgamation/l1/l2/s/ +2: /home/boris/work/build2/build2/tests/scope/a-out/l1/l2/s/ diff --git a/tests/scope/test.sh b/tests/scope/test.sh new file mode 100755 index 0000000..5bbf0d8 --- /dev/null +++ b/tests/scope/test.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# In-tree. +# +valgrind b amalgamation/l1/ 2>/dev/null | diff -u test-1.out - + +# Out-of-tree. +# +rm -rf a-out/ +b 'configure(amalgamation/@a-out/)' 2>/dev/null +valgrind b amalgamation/l1/@a-out/l1/ 2>/dev/null | diff -u test-2.out - +rm -rf a-out/ diff --git a/tests/simple/build/bootstrap.build b/tests/simple/build/bootstrap.build new file mode 100644 index 0000000..7ee30cb --- /dev/null +++ b/tests/simple/build/bootstrap.build @@ -0,0 +1,3 @@ +project = simple +amalgamation = # Disabled. +using config diff --git a/tests/simple/buildfile b/tests/simple/buildfile new file mode 100644 index 0000000..986f391 --- /dev/null +++ b/tests/simple/buildfile @@ -0,0 +1,6 @@ +using cxx + +hxx.ext = hxx +cxx.ext = cxx + +exe{driver}: cxx{driver} diff --git a/tests/simple/driver.cxx b/tests/simple/driver.cxx new file mode 100644 index 0000000..5b076c7 --- /dev/null +++ b/tests/simple/driver.cxx @@ -0,0 +1,8 @@ +#include <iostream> + +using namespace std; + +int +main () +{ +} |