From 7eed858cac7e8ff78626bdc5d63a7f36ca8f8010 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 4 Mar 2015 16:33:51 +0200 Subject: Move roots and bases to appropriate scopes --- build/b.cxx | 56 ++++++++++++++++++-------- build/context | 22 ++++++----- build/context.cxx | 42 ++++++++++++++++---- build/cxx/rule.cxx | 29 +++++++++++--- build/diagnostics.cxx | 25 ++++++++++-- build/name | 4 ++ build/name.cxx | 2 + build/parser | 3 ++ build/parser.cxx | 102 +++++++++++++++++++++++++++++++----------------- build/path | 6 +++ build/path.ixx | 13 +++++++ build/scope | 10 ++--- build/scope.cxx | 54 +++++++++++++------------ build/target.cxx | 2 +- build/utility | 9 +++++ build/utility.cxx | 3 ++ build/variable | 106 +++++++++++++++++++++++++++++++++++++++++++++++++- build/variable.cxx | 47 ++++++++++++++++++++++ 18 files changed, 424 insertions(+), 111 deletions(-) diff --git a/build/b.cxx b/build/b.cxx index edefbe9..5509cba 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -98,7 +98,7 @@ main (int argc, char* argv[]) target_types.insert (cxx::ixx::static_type); target_types.insert (cxx::txx::static_type); - // Figure out directories: work, home, and {src,out}_{root,base}. + // Figure out work and home directories. // work = path::current (); @@ -117,24 +117,48 @@ main (int argc, char* argv[]) home = path (pw->pw_dir); } + if (verb >= 4) + { + trace << "work dir: " << work.string (); + trace << "home dir: " << home.string (); + } + + // Create root scope. For Win32 we use the empty path since there + // is no such "real" root path. On POSIX, however, this is a real + // path. See the comment in for details. + // +#ifdef _WIN32 + root_scope = &scopes[path ()]; +#else + root_scope = &scopes[path ("/")]; +#endif + + // Figure out {src,out}_{root,base}. Note that all the paths must be + // normalized. + // //@@ Must be normalized. // - out_base = work; - src_base = out_base; + path out_base (work); + path src_base (out_base); //@@ TMP + + path src_root; + path out_root; // The project's root directory is the one that contains the build/ // sub-directory which contains the pre.build file. // for (path d (src_base); !d.root () && d != home; d = d.directory ()) { - path f (d / path ("build/pre.build")); - if (path_mtime (f) != timestamp_nonexistent) + if (path_mtime (d / path ("build/pre.build")) != timestamp_nonexistent) { src_root = d; break; } } + // If there is no such sub-directory, assume this is a simple project + // with src_root being the same as src_base. + // if (src_root.empty ()) { src_root = src_base; @@ -145,23 +169,23 @@ main (int argc, char* argv[]) if (verb >= 4) { - trace << "work dir: " << work.string (); - trace << "home dir: " << home.string (); trace << "out_base: " << out_base.string (); trace << "src_base: " << src_base.string (); trace << "out_root: " << out_root.string (); trace << "src_root: " << src_root.string (); } - // Create root scope. For Win32 we use the empty path since there - // is no such "real" root path. On POSIX, however, this is a real - // path. See the comment in for details. + // Create project root and base scopes, set the corresponding + // variables. // -#ifdef _WIN32 - root_scope = &scopes[path ()]; -#else - root_scope = &scopes[path ("/")]; -#endif + scope& proot_scope (scopes[out_root]); + scope& pbase_scope (scopes[out_base]); + + proot_scope.variables["out_root"] = move (out_root); + proot_scope.variables["src_root"] = move (src_root); + + pbase_scope.variables["out_base"] = out_base; + pbase_scope.variables["src_base"] = src_base; // Parse buildfile. // @@ -176,7 +200,7 @@ main (int argc, char* argv[]) try { - p.parse (ifs, bf, scopes[out_base]); + p.parse (ifs, bf, pbase_scope); } catch (const std::ios_base::failure&) { diff --git a/build/context b/build/context index c177603..7c7421d 100644 --- a/build/context +++ b/build/context @@ -12,26 +12,28 @@ namespace build { + class scope; + extern path work; extern path home; - extern path src_root; - extern path out_root; - - extern path src_base; - extern path out_base; - // Return the src/out directory corresponding to the given out/src. The // passed directory should be a sub-directory of out/src_root. // path - src_out (const path&); + src_out (const path& out, scope&); + + path + src_out (const path& out, const path& out_root, const path& src_root); + + path + out_src (const path& src, scope&); path - out_src (const path&); + out_src (const path& src, const path& out_root, const path& src_root); - // If possible, translate an absolute, normalized path into relative to - // the work directory. + // If possible and beneficial, translate an absolute, normalized path + // into relative to the work directory. // path relative_work (const path&); diff --git a/build/context.cxx b/build/context.cxx index f7c9650..6c37e38 100644 --- a/build/context.cxx +++ b/build/context.cxx @@ -7,6 +7,8 @@ #include #include +#include + using namespace std; namespace build @@ -14,21 +16,31 @@ namespace build path work; path home; - path src_root; - path out_root; + path + src_out (const path& out, scope& s) + { + return src_out (out, + s["out_root"].as (), + s["src_root"].as ()); + } - path src_base; - path out_base; + path + out_src (const path& src, scope& s) + { + return out_src (src, + s["out_root"].as (), + s["src_root"].as ()); + } path - src_out (const path& o) + src_out (const path& o, const path& out_root, const path& src_root) { assert (o.sub (out_root)); return src_root / o.leaf (out_root); } path - out_src (const path& s) + out_src (const path& s, const path& out_root, const path& src_root) { assert (s.sub (src_root)); return out_root / s.leaf (src_root); @@ -41,11 +53,25 @@ namespace build return p.leaf (work); // If work is a sub-path of {src,out}_root and this path is also a - // sub-bath of it, then use '..' to form a relative path. + // sub-path of it, then use '..' to form a relative path. + // + // Don't think this is a good heuristic. For example, why shouldn't + // we display paths from imported projects as relative if they are + // more readable than absolute? // + /* if ((work.sub (src_root) && p.sub (src_root)) || - (work.sub (out_root) && p.sub (out_root))) // @@ cache + (work.sub (out_root) && p.sub (out_root))) return p.relative (work); + */ + + if (p.root_directory () == work.root_directory ()) + { + path r (p.relative (work)); + + if (r.string ().size () < p.string ().size ()) + return r; + } return p; } diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index eb61bd1..e495c27 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -136,7 +136,7 @@ namespace build const char* args[] = { "g++-4.9", "-std=c++14", - "-I", src_root.string ().c_str (), + "-I", ds["src_root"].as ().string ().c_str (), "-MM", //@@ -M "-MG", // Treat missing headers as generated. "-MQ", "*", // Quoted target (older version can't handle empty name). @@ -269,7 +269,7 @@ namespace build "g++-4.9", "-std=c++14", "-g", - "-I", src_root.string ().c_str (), + "-I", o.prerequisites[0].get ().scope["src_root"].as ().string ().c_str (), "-c", "-o", ro.string ().c_str (), rs.string ().c_str (), @@ -382,7 +382,13 @@ namespace build if (e.path ().empty ()) e.path (e.dir / path (e.name)); - // Process prerequisited: do rule chaining for C and C++ source + // We may need the project roots for rule chaining (see below). + // We will resolve them lazily only if needed. + // + const path* out_root (nullptr); + const path* src_root (nullptr); + + // Process prerequisites: do rule chaining for C and C++ source // files as well as search and match. // for (auto& pr: t.prerequisites) @@ -398,6 +404,17 @@ namespace build continue; } + if (out_root == nullptr) + { + // Which scope shall we use to resolve the roots? Unlikely, + // but possible, the prerequisite is from a different project + // altogether. So we are going to use the target's project. + // + scope& s (scopes.find (e.dir)); + out_root = &s["out_root"].as (); + src_root = &s["src_root"].as (); + } + prerequisite& cp (p); // Come up with the obj{} prerequisite. The c(xx){} prerequisite @@ -408,15 +425,15 @@ namespace build // possible it is under out_root (e.g., generated source). // path d; - if (cp.dir.relative () || cp.dir.sub (out_root)) + if (cp.dir.relative () || cp.dir.sub (*out_root)) d = cp.dir; else { - if (!cp.dir.sub (src_root)) + if (!cp.dir.sub (*src_root)) fail << "out of project prerequisite " << cp << info << "specify corresponding obj{} target explicitly"; - d = out_root / cp.dir.leaf (src_root); + d = *out_root / cp.dir.leaf (*src_root); } prerequisite& op ( diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx index 34bbee8..f5802de 100644 --- a/build/diagnostics.cxx +++ b/build/diagnostics.cxx @@ -21,14 +21,31 @@ namespace build if (p == work) return "."; - path rp (relative_work (p)); +#ifndef _WIN32 + if (p == home) + return "~"; +#endif + + path rw (relative_work (p)); #ifndef _WIN32 - if (rp.absolute () && rp.sub (home)) - return "~/" + rp.leaf (home).string (); + if (rw.relative ()) + { + // See if the original path with the ~/ shortcut is better + // that the relative to work. + // + if (p.sub (home)) + { + path rh (p.leaf (home)); + if (rw.string ().size () > rh.string ().size () + 2) // 2 for '~/' + return "~/" + rh.string (); + } + } + else if (rw.sub (home)) + return "~/" + rw.leaf (home).string (); #endif - return rp.string (); + return rw.string (); } return p.string (); diff --git a/build/name b/build/name index db7b61c..00f0c00 100644 --- a/build/name +++ b/build/name @@ -17,12 +17,16 @@ namespace build // A name is what we operate on by default. Depending on the context, // it can be interpreted as a target or prerequisite name. A name // without a type and directory can be used to represent any text. + // A name with directory and empty value represents a directory. // struct name { explicit name (std::string v): value (std::move (v)) {} + explicit + name (path d): dir (std::move (d)) {} + name (std::string t, path d, std::string v) : type (std::move (t)), dir (std::move (d)), value (std::move (v)) {} diff --git a/build/name.cxx b/build/name.cxx index 9de1695..4ffc848 100644 --- a/build/name.cxx +++ b/build/name.cxx @@ -30,6 +30,8 @@ namespace build s.back () != path::traits::directory_separator) os << path::traits::directory_separator; } + else if (n.value.empty () && n.type.empty ()) + os << s; // Otherwise nothing gets printed. } os << n.value; diff --git a/build/parser b/build/parser index e5aeb60..6e0be34 100644 --- a/build/parser +++ b/build/parser @@ -92,6 +92,9 @@ namespace build scope* scope_; target* default_target_; + const path* out_root_; + const path* src_root_; + token peek_ {token_type::eos, false, 0, 0}; bool peeked_ {false}; diff --git a/build/parser.cxx b/build/parser.cxx index 1ff841b..a030a09 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -125,6 +125,9 @@ namespace build scope_ = &s; default_target_ = nullptr; + out_root_ = &s["out_root"].as (); + src_root_ = &s["src_root"].as (); + token t (type::eos, false, 0, 0); type tt; next (t, tt); @@ -394,28 +397,28 @@ namespace build } else { - //@@ TODO: assuming it is a list. - // - list_value* val (dynamic_cast ((*scope_)[var])); - - if (val == nullptr) // Initialization. + if (auto val = (*scope_)[var]) { - list_value_ptr nval (new list_value (*scope_, move (vns))); - scope_->variables.emplace (var, move (nval)); + //@@ TODO: assuming it is a list. + // + list_value* lv (&val.as ()); + + if (&lv->scope != scope_) // Append to value from parent scope? + { + list_value_ptr nval (new list_value (*scope_, lv->data)); + lv = nval.get (); // Append to. + scope_->variables.emplace (var, move (nval)); + } + + lv->data.insert (lv->data.end (), + make_move_iterator (vns.begin ()), + make_move_iterator (vns.end ())); } - else if (&val->scope != scope_) // Append to value from parent scope. + else // Initialization. { - list_value_ptr nval (new list_value (*scope_, val->data)); - val = nval.get (); // Append. + list_value_ptr nval (new list_value (*scope_, move (vns))); scope_->variables.emplace (var, move (nval)); } - - // Append. - // - if (val != nullptr) - val->data.insert (val->data.end (), - make_move_iterator (vns.begin ()), - make_move_iterator (vns.end ())); } if (tt == type::newline) @@ -454,7 +457,7 @@ namespace build // to the current directory scope. // if (p.relative ()) - p = src_out (scope_->path ()) / p; + p = src_out (scope_->path (), *out_root_, *src_root_) / p; ifstream ifs (p.string ()); @@ -532,14 +535,14 @@ namespace build // Make sure the path is in this project. Include is only meant // to be used for intra-project inclusion. // - if (!p.sub (src_root) && !(in_out = p.sub (out_root))) + if (!p.sub (*src_root_) && !(in_out = p.sub (*out_root_))) fail (l) << "out of project include " << p; } else { // Use the src directory corresponding to the current directory scope. // - p = src_out (scope_->path ()) / p; + p = src_out (scope_->path (), *out_root_, *src_root_) / p; p.normalize (); } @@ -549,6 +552,22 @@ namespace build continue; } + // Determine new bases. + // + path out_base; + path src_base; + + if (in_out) + { + out_base = p.directory (); + src_base = src_out (out_base, *out_root_, *src_root_); + } + else + { + src_base = p.directory (); + out_base = out_src (src_base, *out_root_, *src_root_); + } + ifstream ifs (p.string ()); if (!ifs.is_open ()) @@ -567,7 +586,10 @@ namespace build lexer_ = &l; scope* os (scope_); - scope_ = &scopes[(in_out ? p : out_src (p)).directory ()]; + scope_ = &scopes[out_base]; + + scope_->variables["out_base"] = move (out_base); + scope_->variables["src_base"] = move (src_base); target* odt (default_target_); default_target_ = nullptr; @@ -745,14 +767,12 @@ namespace build //@@ TODO: append namespace if any. n = t.name (); - //@@ TODO: assuming it is a list. - // const variable& var (variable_pool.find (move (n))); - list_value* val (dynamic_cast ((*scope_)[var])); + auto val ((*scope_)[var]); // Undefined namespaces variables are not allowed. // - if (val == nullptr && var.name.find ('.') != string::npos) + if (!val && var.name.find ('.') != string::npos) fail (t) << "undefined namespace variable " << var.name; if (paren) @@ -765,7 +785,14 @@ namespace build tt = peek (); - if (val == nullptr || val->data.empty ()) + if (!val) + continue; + + //@@ TODO: assuming it is a list. + // + const list_value& lv (val.as ()); + + if (lv.data.empty ()) continue; // Should we accumulate? If the buffer is not empty, then @@ -778,33 +805,36 @@ namespace build ((tt == type::name || tt == type::dollar) // Start. && !peeked ().separated ())) { - // This should be a simple value. The token still points - // to the name (or closing paren). + // This should be a simple value or a simple directory. The + // token still points to the name (or closing paren). // - if (val->data.size () > 1) + if (lv.data.size () > 1) fail (t) << "concatenating expansion of " << var.name << " contains multiple values"; - const name& n (val->data[0]); + const name& n (lv.data[0]); if (!n.type.empty ()) fail (t) << "concatenating expansion of " << var.name << " contains type"; if (!n.dir.empty ()) - fail (t) << "concatenating expansion of " << var.name - << " contains directory"; - - concat += n.value; + { + if (!n.value.empty ()) + fail (t) << "concatenating expansion of " << var.name + << " contains directory"; - text << "concat: " << concat; + concat += n.dir.string (); + } + else + concat += n.value; } else { // Copy the names from the variable into the resulting name list // while doing sensible things with the types and directories. // - for (const name& n: val->data) + for (const name& n: lv.data) { const path* dp1 (dp); const string* tp1 (tp); diff --git a/build/path b/build/path index 6ce2172..9b68485 100644 --- a/build/path +++ b/build/path @@ -287,6 +287,12 @@ namespace build basic_path directory (basic_path const&) const; + // Return the root directory of the path or empty path if + // the directory is not absolute. + // + basic_path + root_directory () const; + // Return the path without the extension, if any. // basic_path diff --git a/build/path.ixx b/build/path.ixx index 60d3bc3..ef1232b 100644 --- a/build/path.ixx +++ b/build/path.ixx @@ -59,6 +59,19 @@ namespace build template inline basic_path basic_path:: + root_directory () const + { + return absolute () +#ifdef _WIN32 + ? path (path_, 2) +#else + ? path ("/") +#endif + : path (); + } + + template + inline basic_path basic_path:: base () const { size_type p (traits::find_extension (path_)); diff --git a/build/scope b/build/scope index 2746ff8..b4f492d 100644 --- a/build/scope +++ b/build/scope @@ -26,18 +26,18 @@ namespace build // Variable lookup. // public: - value* - operator[] (const variable&); + value_proxy + operator[] (const std::string&); - value* - operator[] (const std::string& name); + value_proxy + operator[] (const variable&); private: friend class scope_map; typedef path_map::const_iterator iterator; - scope () = default; + scope (): variables (*this) {} void init (const iterator& i, scope* p) {i_ = i; parent_ = p;} diff --git a/build/scope.cxx b/build/scope.cxx index 9013e12..b9b576e 100644 --- a/build/scope.cxx +++ b/build/scope.cxx @@ -10,24 +10,24 @@ namespace build { // scope // - value* scope:: + value_proxy scope:: operator[] (const string& name) { const variable& var (variable_pool.find (name)); - return (*this)[var]; + return operator[] (var); } - value* scope:: + value_proxy scope:: operator[] (const variable& var) { for (scope* s (this); s != nullptr; s = s->parent ()) { auto i (s->variables.find (var)); if (i != s->variables.end ()) - return i->second.get (); + return value_proxy (&i->second, s); } - return nullptr; + return value_proxy (nullptr, nullptr); } // scope_map @@ -45,31 +45,37 @@ namespace build { scope* p (nullptr); - // Update scopes of which we are a new parent. + // Update scopes of which we are a new parent (unless this is the + // root scope). // - for (auto r (find_prefix (k)); r.first != r.second; ++r.first) + if (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 (find_prefix (k)); + for (++r.first; r.first != r.second; ++r.first) + { + 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. + // + if (p == nullptr) + p = c.parent (); + else if (p != c.parent ()) // A scope with an intermediate parent. + continue; + + 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 (); - else if (p != c.parent ()) // A scope with an intermediate parent. - continue; - - 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 (unless this is is the - // root scope). - // - if (p == nullptr && size () != 1) - p = &find (k.directory ()); - s.init (er.first, p); } diff --git a/build/target.cxx b/build/target.cxx index 136b912..6fdfb7b 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -141,7 +141,7 @@ namespace build if (p.dir.relative ()) { paths sp; - sp.push_back (src_out (p.scope.path ())); // src_base + sp.push_back (src_out (p.scope.path (), p.scope)); // src_base return search_existing_file (p, sp); } diff --git a/build/utility b/build/utility index f429598..fd7888c 100644 --- a/build/utility +++ b/build/utility @@ -12,8 +12,17 @@ #include #include +#include + namespace build { + // Empty string and path. + // + extern const std::string empty_string; + extern const path empty_path; + + // Comparators. + // struct compare_c_string { bool operator() (const char* x, const char* y) const diff --git a/build/utility.cxx b/build/utility.cxx index cd92740..c66fba2 100644 --- a/build/utility.cxx +++ b/build/utility.cxx @@ -8,6 +8,9 @@ using namespace std; namespace build { + const string empty_string; + const path empty_path; + bool exception_unwinding_dtor = false; string_pool extension_pool; diff --git a/build/variable b/build/variable index 393ad47..ae4c7b3 100644 --- a/build/variable +++ b/build/variable @@ -8,9 +8,11 @@ #include #include // unique_ptr #include // move() +#include #include #include +#include #include #include @@ -62,9 +64,90 @@ namespace build { list_value (scope_type& s, names d): value (s), data (std::move (d)) {} + list_value (scope_type& s, std::string d) + : value (s) + { + data.emplace_back (std::move (d)); + } + + // Note: stored in name as a directory. + // + list_value (scope_type& s, path d) + : value (s) + { + data.emplace_back (std::move (d)); + } + names data; }; typedef std::unique_ptr list_value_ptr; + + // value_proxy + // + struct value_proxy + { + explicit operator bool () const {return p != nullptr && *p != nullptr;} + explicit operator value_ptr& () const {return *p;} + + // Get interface. See available specializations below. + // + template + T + as () const = delete; + + // Set interface. + // + const value_proxy& + operator= (value_ptr v) const + { + assert (v == nullptr || &v->scope == s); + *p = std::move (v); + return *this; + } + + const value_proxy& + operator= (std::string v) const + { + // In most cases this is used to initialize a new variable, so + // don't bother trying to optimize for the case where p is not + // NULL. + // + p->reset (new list_value (*s, std::move (v))); + return *this; + } + + // Note: stored in name as a directory. + // + const value_proxy& + operator= (path v) const + { + p->reset (new list_value (*s, std::move (v))); + return *this; + } + + // Implementation details. + // + explicit + value_proxy (value_ptr* p, scope* s): p (p), s (s) {} + + protected: + value_ptr* p; + scope* s; + }; + + template <> + list_value& value_proxy:: + as () const; + + template <> + const std::string& value_proxy:: + as () const; + + // Note: get the name's directory. + // + template <> + const path& value_proxy:: + as () const; } namespace std @@ -117,7 +200,28 @@ namespace build } }; - typedef prefix_map variable_map; + struct variable_map: prefix_map + { + typedef prefix_map base; + + value_proxy + operator[] (const std::string& v) + { + return operator[] (variable_pool.find (v)); + } + + value_proxy + operator[] (const variable& v) + { + return value_proxy (&base::operator[] (v), &scope_); + } + + explicit + variable_map (scope& s): scope_ (s) {} + + private: + scope& scope_; // Scope to which this map belongs (and all its value). + }; } #endif // BUILD_VARIABLE diff --git a/build/variable.cxx b/build/variable.cxx index 613cada..56d5a08 100644 --- a/build/variable.cxx +++ b/build/variable.cxx @@ -4,9 +4,56 @@ #include +#include + using namespace std; namespace build { variable_set variable_pool; + + // value_proxy + // + template <> + list_value& value_proxy:: + as () const + { + list_value* lv (dynamic_cast (p->get ())); + assert (lv != nullptr); + return *lv; + } + + template <> + const string& value_proxy:: + as () const + { + const list_value& lv (as ()); + assert (lv.data.size () < 2); + + if (lv.data.empty ()) + return empty_string; + + const name& n (lv.data.front ()); + + assert (n.type.empty () && n.dir.empty ()); + return n.value; + } + + // Note: get the name's directory. + // + template <> + const path& value_proxy:: + as () const + { + const list_value& lv (as ()); + assert (lv.data.size () < 2); + + if (lv.data.empty ()) + return empty_path; + + const name& n (lv.data.front ()); + + assert (n.type.empty () && n.value.empty ()); + return n.dir; + } } -- cgit v1.1