From 435b3e43dd054a716b3fc67fc34b43267f8e9809 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 8 Feb 2021 11:02:25 +0200 Subject: Enter scope src directories into scope map --- libbuild2/cc/compile-rule.cxx | 20 +++++--------- libbuild2/config/operation.cxx | 2 +- libbuild2/context.cxx | 4 +-- libbuild2/dist/operation.cxx | 2 +- libbuild2/dump.cxx | 17 ++++++------ libbuild2/file.cxx | 48 ++++++++++++++++++++++------------ libbuild2/file.hxx | 2 +- libbuild2/parser.hxx | 2 +- libbuild2/scope.cxx | 59 ++++++++++++++++++++++++++++++++---------- libbuild2/scope.hxx | 57 +++++++++++++++++++++++++++++++++------- libbuild2/search.cxx | 2 +- 11 files changed, 148 insertions(+), 67 deletions(-) (limited to 'libbuild2') diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 8354bca..c9d2652 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -2151,8 +2151,7 @@ namespace build2 if (j != je) { - // The groups are ordered from the most to least - // specific. + // The groups are ordered from the most to least specific. // for (const string& g: j->second) if ((i = ths.find (g)) != ie) @@ -2812,18 +2811,11 @@ namespace build2 if (!e.empty ()) n.resize (n.size () - e.size () - 1); // One for the dot. - // See if this directory is part of any project out_root hierarchy and - // if so determine the target type. + // See if this directory is part of any project and if so determine + // the target type. // - // Note that this will miss all the headers that come from src_root - // (so they will be treated as generic C headers below). Generally, we - // don't have the ability to determine that some file belongs to - // src_root of some project. But that's not a problem for our - // purposes: it is only important for us to accurately determine - // target types for headers that could be auto-generated. - // - // While at it also try to determine if this target is from the src or - // out tree of said project. + // While at it also determine if this target is from the src or out + // tree of said project. // dir_path out; @@ -2839,7 +2831,7 @@ namespace build2 { tts = map_extension (bs, n, e); - if (bs.out_path () != bs.src_path () && d.sub (bs.src_path ())) + if (!bs.out_eq_src () && d.sub (bs.src_path ())) out = out_src (d, *rs); } diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index f9d83da..7490bc3 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -720,7 +720,7 @@ namespace build2 // create_bootstrap_inner (rs); - if (rs.out_path () == rs.src_path ()) + if (rs.out_eq_src ()) fail (l) << "forwarding to source directory " << rs.src_path (); } else diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx index 3224dc1..df06aa8 100644 --- a/libbuild2/context.cxx +++ b/libbuild2/context.cxx @@ -35,7 +35,7 @@ namespace build2 create_global_scope (scope_map& m) { auto i (m.insert (dir_path ())); - scope& r (i->second); + scope& r (*i->second.scope); r.out_path_ = &i->first; return r; }; @@ -488,7 +488,7 @@ namespace build2 // if (c == '!' || (dir && dir->absolute ())) { - scope& s (c == '!' ? gs : sm.insert (*dir)->second); + scope& s (c == '!' ? gs : *sm.insert (*dir)->second.scope); auto p (s.vars.insert (*o)); assert (p.second); // Variable name is unique. diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx index 07f3b42..ac76a62 100644 --- a/libbuild2/dist/operation.cxx +++ b/libbuild2/dist/operation.cxx @@ -597,7 +597,7 @@ namespace build2 (rs->out_path () != t.dir && rs->src_path () != t.dir)) fail << "dist meta-operation target must be project root directory"; - if (rs->out_path () == rs->src_path ()) + if (rs->out_eq_src ()) fail << "in-tree distribution of target " << t << info << "distribution requires out-of-tree build"; diff --git a/libbuild2/dump.cxx b/libbuild2/dump.cxx index 95c7cae..7cd95dd 100644 --- a/libbuild2/dump.cxx +++ b/libbuild2/dump.cxx @@ -438,7 +438,7 @@ namespace build2 scope_map::const_iterator& i, bool rel) { - const scope& p (i->second); + const scope& p (*i->second.scope); const dir_path& d (i->first); ++i; @@ -484,7 +484,8 @@ namespace build2 vb = true; } - // Nested scopes of which we are an immediate parent. + // Nested scopes of which we are an immediate parent. Only consider the + // out hierarchy. // // Note that because we use the logical (rather than physical) parent, we // will be printing the logical scope hierarchy (i.e., a project with @@ -492,7 +493,7 @@ namespace build2 // scope). // for (auto e (p.ctx.scopes.end ()); - i != e && i->second.parent_scope () == &p; ) + i != e && i->second.out && i->second.scope->parent_scope () == &p; ) { if (vb) { @@ -541,8 +542,8 @@ namespace build2 void dump (const context& c, optional a) { - auto i (c.scopes.cbegin ()); - assert (&i->second == &c.global_scope); + auto i (c.scopes.begin ()); + assert (i->second.scope == &c.global_scope); // We don't lock diag_stream here as dump() is supposed to be called from // the main thread prior/after to any other threads being spawned. @@ -556,9 +557,9 @@ namespace build2 void dump (const scope& s, const char* cind) { - const scope_map_base& m (s.ctx.scopes); // Iterator interface. - auto i (m.find (s.out_path ())); - assert (i != m.end () && &i->second == &s); + const scope_map& m (s.ctx.scopes); + auto i (m.find_exact (s.out_path ())); + assert (i != m.end () && i->second.scope == &s); string ind (cind); ostream& os (*diag_stream); diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index 5d1487f..87e21a6 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -399,7 +399,7 @@ namespace build2 const dir_path& src_root) { auto i (ctx.scopes.rw ().insert (out_root, true /* root */)); - scope& rs (i->second); + scope& rs (*i->second.scope); // Set out_path. Note that src_path is set in setup_root() below. // @@ -457,9 +457,17 @@ namespace build2 const dir_path& d (cast (v)); if (s.src_path_ == nullptr) - s.src_path_ = &d; + { + if (*s.out_path_ != d) + { + auto i (ctx.scopes.rw (s).insert (s, d)); + s.src_path_ = &i->first; + } + else + s.src_path_ = s.out_path_; + } else - assert (s.src_path_ == &d); + assert (*s.src_path_ == d); s.assign (ctx.var_forwarded) = forwarded; } @@ -469,7 +477,7 @@ namespace build2 const dir_path& out_base, const dir_path& src_base) { - scope& s (i->second); + scope& s (*i->second.scope); context& ctx (s.ctx); // Set src/out_base variables. @@ -496,7 +504,15 @@ namespace build2 assert (*s.out_path_ == out_base); if (s.src_path_ == nullptr) - s.src_path_ = &cast (sv); + { + if (out_base != src_base) + { + auto i (ctx.scopes.rw (s).insert (s, src_base)); + s.src_path_ = &i->first; + } + else + s.src_path_ = s.out_path_; + } else assert (*s.src_path_ == src_base); @@ -504,21 +520,22 @@ namespace build2 } pair - switch_scope (scope& root, const dir_path& p, bool proj) + switch_scope (scope& root, const dir_path& out_base, bool proj) { // 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 (root.ctx.scopes.rw (root).insert (p)); - scope& base (i->second); + auto i (root.ctx.scopes.rw (root).insert (out_base)); + scope& base (*i->second.scope); scope* rs (nullptr); if (proj && (rs = base.root_scope ()) != nullptr) { - // Path p can be src_base or out_base. Figure out which one it is. + // The path must be in the out (since we've inserted it as out into the + // scope map). // - dir_path out_base (p.sub (rs->out_path ()) ? p : out_src (p, *rs)); + assert (out_base.sub (rs->out_path ())); // 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 @@ -534,8 +551,7 @@ namespace build2 // 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)); + setup_base (i, out_base, src_out (out_base, *rs)); } return pair (base, rs); @@ -1316,7 +1332,7 @@ namespace build2 // probably be tried first since that src_root was explicitly configured // by the user. After that, #2 followed by #1 seems reasonable. // - scope& rs (create_root (ctx, out_root, dir_path ())->second); + scope& rs (*create_root (ctx, out_root, dir_path ())->second.scope); bool bstrapped (bootstrapped (rs)); @@ -1383,7 +1399,7 @@ namespace build2 // The same logic to src_root as in create_bootstrap_outer(). // - scope& rs (create_root (ctx, out_root, dir_path ())->second); + scope& rs (*create_root (ctx, out_root, dir_path ())->second.scope); optional altn; if (!bootstrapped (rs)) @@ -1618,7 +1634,7 @@ namespace build2 assert (!forwarded || out_root != src_root); auto i (create_root (ctx, out_root, src_root)); - scope& rs (i->second); + scope& rs (*i->second.scope); if (!bootstrapped (rs)) { @@ -2327,7 +2343,7 @@ namespace build2 { bool top (proot == nullptr); - root = &create_root (ctx, out_root, src_root)->second; + root = create_root (ctx, out_root, src_root)->second.scope; bool bstrapped (bootstrapped (*root)); diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx index e0291fe..0c4ad62 100644 --- a/libbuild2/file.hxx +++ b/libbuild2/file.hxx @@ -146,7 +146,7 @@ namespace build2 // second. // LIBBUILD2_SYMEXPORT pair - switch_scope (scope& root, const dir_path&, bool project = true); + switch_scope (scope& root, const dir_path& out_base, bool project = true); // Bootstrap and optionally load an ad hoc (sub)project (i.e., the kind that // is not discovered and loaded automatically by bootstrap/load functions diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 1e924f0..3efb94a 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -531,7 +531,7 @@ namespace build2 // project. So both must be saved and restored. // void - switch_scope (const dir_path&); + switch_scope (const dir_path& out_base); void process_default_target (token&); diff --git a/libbuild2/scope.cxx b/libbuild2/scope.cxx index 53c859f..29e0c55 100644 --- a/libbuild2/scope.cxx +++ b/libbuild2/scope.cxx @@ -944,10 +944,21 @@ namespace build2 auto scope_map:: insert (const dir_path& k, bool root) -> iterator { - scope_map_base& m (*this); + auto er (map_.emplace (k, true /* out */)); - auto er (m.emplace (k, scope (ctx, true /* global */))); - scope& s (er.first->second); + if (er.second) + { + er.first->second.scope = new scope (ctx, true /* global */); + } + else if (!er.first->second.out) + { + // This can potentially be triggered if we use the same directory as one + // project's out and another's src. + // + fail << "directory " << k << " is used as both src and out scope"; + } + + scope& s (*er.first->second.scope); // If this is a new scope, update the parent chain. // @@ -958,14 +969,14 @@ namespace build2 // Update scopes of which we are a new parent/root (unless this is the // global scope). Also find our parent while at it. // - if (m.size () > 1) + if (map_.size () > 1) { // The first entry is ourselves. // - auto r (m.find_sub (k)); + auto r (map_.find_sub (k)); for (++r.first; r.first != r.second; ++r.first) { - scope& c (r.first->second); + scope& c (*r.first->second.scope); // 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 @@ -995,10 +1006,10 @@ namespace build2 { // Upgrade to root scope. // - auto r (m.find_sub (k)); + auto r (map_.find_sub (k)); for (++r.first; r.first != r.second; ++r.first) { - scope& c (r.first->second); + scope& c (*r.first->second.scope); if (c.root_ == s.root_) // No intermediate root. c.root_ = &s; @@ -1010,14 +1021,36 @@ namespace build2 return er.first; } + auto scope_map:: + insert (scope& s, const dir_path& k) -> iterator + { + auto er (map_.emplace (k, false /* out */)); + + if (er.second) + { + er.first->second.scope = &s; + } + else if (!er.first->second.out) + { + assert (er.first->second.scope == &s); + } + else + { + // This can be triggered, for example, by specifying a variable override + // with src instead of out directory. + // + fail << "directory " << k << " is used as both src and out scope"; + } + + return er.first; + } + scope& scope_map:: find (const dir_path& k) { assert (k.normalized (false)); // Allow non-canonical dir separators. - - scope_map_base& m (*this); - auto i (m.find_sup (k)); - assert (i != m.end ()); // Should have global scope. - return i->second; + auto i (map_.find_sup (k)); + assert (i != map_.end ()); // Should have global scope. + return *i->second.scope; } } diff --git a/libbuild2/scope.hxx b/libbuild2/scope.hxx index bc04bae..11fdfe4 100644 --- a/libbuild2/scope.hxx +++ b/libbuild2/scope.hxx @@ -43,8 +43,10 @@ namespace build2 const dir_path& out_path () const {return *out_path_;} const dir_path& src_path () const {return *src_path_;} - // The first is a pointer to the key in scope_map. The second is a pointer - // to the src_root/base variable value, if any (i.e., it can be NULL). + bool out_eq_src () const {return out_path_ == src_path_;} + + // These are pointers to the keys in scope_map. The second can be NULL + // during bootstrap until initialized. // const dir_path* out_path_ = nullptr; const dir_path* src_path_ = nullptr; @@ -627,21 +629,53 @@ namespace build2 } }; - // Scope map. + // Scope map. Protected by the phase mutex. // - // Protected by the phase mutex. Note that the scope map is only for paths - // from the out tree. + // While it contains both out and src paths, the latter is not available + // during bootstrap (see setup_root() and setup_base() for details). // - using scope_map_base = dir_path_map; - - class scope_map: public scope_map_base + class scope_map { public: + struct scope_ptr + { + using scope_type = build2::scope; + + scope_type* scope; + bool out; + + scope_ptr (bool o): scope (nullptr), out (o) {} + ~scope_ptr () {if (out) delete scope;} + + scope_ptr (scope_ptr&& x) // For GCC 4.9 + : scope (x.scope), out (x.out) + { + x.scope = nullptr; + } + + scope_ptr& operator= (scope_ptr&&) = delete; + + scope_ptr (const scope_ptr&) = delete; + scope_ptr& operator= (const scope_ptr&) = delete; + }; + + using map_type = dir_path_map; + + using iterator = map_type::iterator; + using const_iterator = map_type::const_iterator; + + // Insert a scope given its out path. + // // Note that we assume the first insertion into the map is always the // global scope with empty key. // LIBBUILD2_SYMEXPORT iterator - insert (const dir_path&, bool root = false); + insert (const dir_path& our_path, bool root = false); + + // Insert a shallow reference to the scope for its src path. + // + LIBBUILD2_SYMEXPORT iterator + insert (scope&, const dir_path& src_path); // Find the most qualified scope that encompasses this path. // @@ -667,6 +701,10 @@ namespace build2 return find (path_cast (p)); } + const_iterator begin () const {return map_.begin ();} + const_iterator end () const {return map_.end ();} + const_iterator find_exact (const dir_path& d) const {return map_.find (d);} + // RW access. // public: @@ -691,6 +729,7 @@ namespace build2 private: context& ctx; + map_type map_; }; } diff --git a/libbuild2/search.cxx b/libbuild2/search.cxx index 25a4199..fca19ea 100644 --- a/libbuild2/search.cxx +++ b/libbuild2/search.cxx @@ -175,7 +175,7 @@ namespace build2 if (tk.out->empty ()) { - if (s->out_path () != s->src_path ()) + if (!s->out_eq_src ()) out = out_src (d, *s->root_scope ()); } else -- cgit v1.1