diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-07-15 14:44:15 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-07-15 14:44:15 +0200 |
commit | 243da3993c138d33063f633aa3996a8a710ea396 (patch) | |
tree | 6d49a3f964f395773c06e258b6550a4d386fbec3 | |
parent | 3c2bc8595e9d6cf6ff35079231c3aab474a38130 (diff) |
Implement project-qualified names/prerequisites, two-stage import
-rw-r--r-- | build/algorithm.cxx | 7 | ||||
-rw-r--r-- | build/algorithm.ixx | 9 | ||||
-rw-r--r-- | build/b.cxx | 4 | ||||
-rw-r--r-- | build/buildfile | 2 | ||||
-rw-r--r-- | build/context | 5 | ||||
-rw-r--r-- | build/context.cxx | 7 | ||||
-rw-r--r-- | build/file | 27 | ||||
-rw-r--r-- | build/file.cxx | 95 | ||||
-rw-r--r-- | build/name | 51 | ||||
-rw-r--r-- | build/name.cxx | 3 | ||||
-rw-r--r-- | build/parser | 9 | ||||
-rw-r--r-- | build/parser.cxx | 116 | ||||
-rw-r--r-- | build/prerequisite | 40 | ||||
-rw-r--r-- | build/prerequisite.cxx | 14 | ||||
-rw-r--r-- | build/scope.cxx | 2 | ||||
-rw-r--r-- | build/target | 2 | ||||
-rw-r--r-- | build/target-key | 2 | ||||
-rw-r--r-- | build/target.ixx | 2 | ||||
-rw-r--r-- | build/target.txx | 7 | ||||
-rw-r--r-- | build/utility | 2 | ||||
-rw-r--r-- | build/utility.cxx | 2 | ||||
-rw-r--r-- | build/variable | 3 |
22 files changed, 304 insertions, 107 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx index 488bbb7..83a6510 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -15,6 +15,7 @@ #include <build/target> #include <build/prerequisite> #include <build/rule> +#include <build/file> // import() #include <build/search> #include <build/context> #include <build/utility> @@ -28,6 +29,12 @@ namespace build target& search (const prerequisite_key& pk) { + // If this is a project-qualified prerequisite, then this + // is import's business. + // + if (*pk.proj != nullptr) + return import (pk); + if (target* t = pk.tk.type->search (pk)) return *t; diff --git a/build/algorithm.ixx b/build/algorithm.ixx index c4f5815..a1e2129 100644 --- a/build/algorithm.ixx +++ b/build/algorithm.ixx @@ -4,6 +4,7 @@ #include <utility> // pair +#include <build/rule> #include <build/prerequisite> #include <build/context> @@ -22,7 +23,8 @@ namespace build search (const target_type& t, const prerequisite_key& k) { return search ( - prerequisite_key {{&t, k.tk.dir, k.tk.name, k.tk.ext}, k.scope}); + prerequisite_key + {k.proj, {&t, k.tk.dir, k.tk.name, k.tk.ext}, k.scope}); } inline target& @@ -32,7 +34,10 @@ namespace build const std::string* ext, scope* scope) { - return search (prerequisite_key {{&type, &dir, &name, &ext}, scope}); + const std::string* proj (nullptr); + return search ( + prerequisite_key + {&proj, {&type, &dir, &name, &ext}, scope}); } template <typename T> diff --git a/build/b.cxx b/build/b.cxx index 36d0b21..7280ecb 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -211,7 +211,7 @@ main (int argc, char* argv[]) const location l ("<buildspec>", 1, 0); //@@ TODO if (os.empty ()) // Default target: dir{}. - os.push_back (targetspec (name ("dir", dir_path (), string ()))); + os.push_back (targetspec (name ("dir", string ()))); operation_id oid (0); // Not yet translated. const operation_info* oif (nullptr); @@ -265,7 +265,7 @@ main (int argc, char* argv[]) // Otherwise, if this is a simple name, see if there is a // directory part in value. // - else if (tn.type.empty ()) + else if (tn.untyped ()) { // We cannot assume it is a valid filesystem name so we // will have to do the splitting manually. diff --git a/build/buildfile b/build/buildfile index 7885c1d..5a5f9fe 100644 --- a/build/buildfile +++ b/build/buildfile @@ -2,7 +2,7 @@ # copyright : Copyright (c) 2014-2015 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -import libs += libbutl +import libs += libbutl%lib{butl} config = config/{operation module utility} bin = bin/{target rule module} diff --git a/build/context b/build/context index 8cf42dc..ad9c091 100644 --- a/build/context +++ b/build/context @@ -11,7 +11,7 @@ #include <butl/filesystem> #include <build/types> -#include <build/rule> +#include <build/utility> #include <build/operation> namespace build @@ -22,6 +22,9 @@ namespace build extern dir_path work; extern dir_path home; + extern string_pool extension_pool; + extern string_pool project_name_pool; + // Current action (meta/operation). // extern const meta_operation_info* current_mif; diff --git a/build/context.cxx b/build/context.cxx index 32c9e9b..bd6143a 100644 --- a/build/context.cxx +++ b/build/context.cxx @@ -21,6 +21,9 @@ namespace build dir_path work; dir_path home; + string_pool extension_pool; + string_pool project_name_pool; + const meta_operation_info* current_mif; const operation_info* current_oif; execution_mode current_mode; @@ -34,6 +37,9 @@ namespace build void reset () { + extension_pool.clear (); + project_name_pool.clear (); + targets.clear (); scopes.clear (); variable_pool.clear (); @@ -82,7 +88,6 @@ namespace build rs.insert<file> (update_id, "file", file_); rs.insert<file> (clean_id, "file", file_); } - } fs_status<mkdir_status> @@ -11,7 +11,9 @@ namespace build { class scope; + class target; class location; + class prerequisite_key; extern const dir_path build_dir; // build extern const dir_path bootstrap_dir; // build/bootstrap @@ -92,8 +94,31 @@ namespace build void load_root_pre (scope& root); + // Import has two phases: the first is triggered by the import + // directive in the buildfile. It will try to find and load the + // project. Failed that, it will return the project-qualified + // name of the target which will be used to create a project- + // qualified prerequisite. This gives the rule that will be + // searching this prerequisite a chance to do some target-type + // specific search. For example, a C++ link rule can search + // for lib{} prerequisites in the C++ compiler default library + // search paths (so that we end up with functionality identical + // to -lfoo). If, however, the rule didn't do any of that (or + // failed to find anything usable), it calls the standard + // prerequisite search() function which sees this is a project- + // qualified prerequisite and goes straight to the second phase + // of import. Here, currently, we simply fail but in the future + // this will be the place where we can call custom "last resort" + // import hooks. For example, we can hook a package manager that + // will say, "Hey, I see you are trying to import foo and I see + // there is a package foo available in repository bar. Wanna + // download and use it?" + // list_value - import (scope& base, const name&, const location&); + import (scope& base, name, const location&); + + target& + import (const prerequisite_key&); } #include <build/file.ixx> diff --git a/build/file.cxx b/build/file.cxx index b137c32..ef7ef78 100644 --- a/build/file.cxx +++ b/build/file.cxx @@ -13,6 +13,7 @@ #include <build/scope> #include <build/context> #include <build/parser> +#include <build/prerequisite> #include <build/diagnostics> #include <build/token> @@ -648,44 +649,26 @@ namespace build } list_value - import (scope& ibase, const name& n, const location& l) + import (scope& ibase, name target, const location& l) { tracer trace ("import"); - // Split the name into the project and target. + // If there is no project specified for this target, then our + // run will be short and sweet: we simply return it as empty- + // project-qualified and let someone else (e.g., a rule) take + // a stab at it. // - string project; - name target; - - // @@ This is probably all wrong. - // - if (n.dir.empty ()) + if (target.unqualified ()) { - if (!n.simple ()) - fail << "project name expected before imported target " << n; - - // Note that value can be foo/bar/baz; in this case probably - // means sub-projects? Or only to a certain point, then - // (untyped) target? Looks like I will need to scan anything - // that looks like a directory checking if this is a subproject. - // If not, then that's part of the target. No, no, nooooo! - // - project = n.value; + target.proj = &project_name_pool.find (""); + return list_value (move (target)); } - else - { - //@@ This can be a path inside a sub-project. So, eventually, - // we should find the innermost sub-project and load the - // export stub from there (will probably still have to - // resolve root from the top-level project). For now we - // assume the project is always top-level. - // - project = *n.dir.begin (); - target.dir = n.dir.leaf (dir_path (project)); - target.type = n.type; - target.value = n.value; - } + // Otherwise, get the project name and convert the target to + // unqualified. + // + const string& project (*target.proj); + target.proj = nullptr; scope& iroot (*ibase.root_scope ()); @@ -715,7 +698,6 @@ namespace build break; } - // Then try the config.import.* mechanism. // if (out_root.empty ()) @@ -734,7 +716,7 @@ namespace build // list_value& lv (v.as<list_value&> ()); - if (lv.size () != 1 || lv[0].empty () || !lv[0].type.empty ()) + if (lv.size () != 1 || lv[0].empty () || lv[0].typed ()) fail (l) << "invalid " << var << " value " << lv; name& n (lv[0]); @@ -759,9 +741,15 @@ namespace build } } else - fail (l) << "unable to find out_root for imported " << project << - info << "consider explicitly configuring its out_root via the " - << var << " command line variable"; + { + // If we can't find the project, convert it back into qualified + // target and return to let someone else (e.g., a rule) to take + // a stab at it. + // + target.proj = &project; + level4 ([&]{trace << "postponing " << target;}); + return list_value (move (target)); + } } // Bootstrap the imported root scope. This is pretty similar to @@ -780,8 +768,8 @@ namespace build const dir_path& p (v.as<const dir_path&> ()); if (!src_root.empty () && p != src_root) - fail << "bootstrapped src_root " << p << " does not match " - << "discovered " << src_root; + fail (l) << "bootstrapped src_root " << p << " does not match " + << "discovered " << src_root; root.src_path_ = &p; } @@ -835,7 +823,7 @@ namespace build path es (root.src_path () / path ("build/export.build")); ifstream ifs (es.string ()); if (!ifs.is_open ()) - fail << "unable to open " << es; + fail (l) << "unable to open " << es; level4 ([&]{trace << "importing " << es;}); @@ -848,9 +836,36 @@ namespace build } catch (const std::ios_base::failure&) { - fail << "failed to read from " << es; + fail (l) << "failed to read from " << es; } + // @@ Should we verify these are all unqualified names? Or maybe + // there is a use-case for the export stub to return a qualified + // name? + // return p.export_value (); } + + target& + import (const prerequisite_key& pk) + { + assert (*pk.proj != nullptr); + const string& p (**pk.proj); + + // @@ We no longer have location. This is especially bad for the + // empty case, i.e., where do I need to specify the project + // name)? Looks like the only way to do this is to keep location + // in name and then in prerequisite. Perhaps one day... + // + if (!p.empty ()) + fail << "unable to import target " << pk << + info << "consider explicitly specifying its project out_root via the " + << "config.import." << p << " command line variable"; + else + fail << "unable to import target " << pk << + info << "consider adding its installation location" << + info << "or explicitly specifying its project name"; + + throw failed (); // No return. + } } @@ -22,6 +22,9 @@ namespace build // 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. + // A name may also be project-qualified. If the project name is + // empty, then it means the name is in a project other than our + // own (e.g., it is installed). // // If pair is not '\0', then this name and the next in the list // form a pair. @@ -36,21 +39,52 @@ namespace build explicit name (dir_path d): dir (std::move (d)) {} - name (std::string t, dir_path d, std::string v) - : type (std::move (t)), dir (std::move (d)), value (std::move (v)) {} + name (std::string t, std::string v) + : type (std::move (t)), value (std::move (v)) {} + + name (dir_path d, std::string t, std::string v) + : dir (std::move (d)), type (std::move (t)), value (std::move (v)) {} + + // The first argument should be from project_name_pool. + // + name (const std::string* p, dir_path d, std::string t, std::string v) + : proj (p), + dir (std::move (d)), + type (std::move (t)), + value (std::move (v)) {} + + bool + qualified () const {return proj != nullptr;} + + bool + unqualified () const {return proj == nullptr;} bool - empty () const {return type.empty () && dir.empty () && value.empty ();} + typed () const {return !type.empty ();} bool - simple () const {return type.empty () && dir.empty ();} + untyped () const {return type.empty ();} + + bool + empty () const {return dir.empty () && value.empty ();} + + // Note that strictly speaking the following tests should be + // orthogonal to qualification. However, the vast majority of + // cases where we expect a simple or directory name, we also + // expect it to be unqualified. + // + // Note also that empty name is simple but not a directory. + // + bool + simple () const {return unqualified () && untyped () && dir.empty ();} bool directory () const - {return type.empty () && !dir.empty () && value.empty ();} + {return unqualified () && untyped () && !dir.empty () && value.empty ();} - std::string type; + const std::string* proj = nullptr; // Points to project_name_pool. dir_path dir; + std::string type; std::string value; char pair = '\0'; // Pair symbol, if any. }; @@ -58,7 +92,10 @@ namespace build inline bool operator== (const name& x, const name& y) { - return x.type == y.type && x.dir == y.dir && x.value == y.value; + return x.proj == y.proj && // Pooled, so can just compare pointers. + x.type == y.type && + x.dir == y.dir && + x.value == y.value; } inline bool diff --git a/build/name.cxx b/build/name.cxx index 89236ee..4061c78 100644 --- a/build/name.cxx +++ b/build/name.cxx @@ -15,6 +15,9 @@ namespace build ostream& operator<< (ostream& os, const name& n) { + if (n.proj != nullptr) + os << *n.proj << '%'; + // If the value is empty, then we want to print the directory // inside {}, e.g., dir{bar/}, not bar/dir{}. We also want to // print {} for an empty name. diff --git a/build/parser b/build/parser index 403fbaf..6eefccd 100644 --- a/build/parser +++ b/build/parser @@ -81,14 +81,17 @@ namespace build names (token& t, token_type& tt) { names_type ns; - names (t, tt, ns, 0, nullptr, nullptr); + names (t, tt, ns, 0, nullptr, nullptr, nullptr); return ns; } void names (token&, token_type&, - names_type&, std::size_t pair, - const dir_path* dir, const std::string* type); + names_type&, + std::size_t pair, + const std::string* prj, + const dir_path* dir, + const std::string* type); // Buildspec. // diff --git a/build/parser.cxx b/build/parser.cxx index 0238adc..29ee02b 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -140,7 +140,7 @@ namespace build const location nloc (get_location (t, &path_)); names_type ns (tt != type::colon ? names (t, tt) - : names_type ({name ("dir", dir_path (), string ())})); + : names_type ({name ("dir", string ())})); if (tt == type::colon) { @@ -173,11 +173,11 @@ namespace build { // A name represents directory as an empty value. // - if (n.type.empty () && n.value.empty ()) + if (n.directory ()) { if (ns.size () != 1) { - // @@ TODO: point to name. + // @@ TODO: point to name (and above). // fail (nloc) << "multiple names in directory scope"; } @@ -296,6 +296,9 @@ namespace build name& n (ns[0]); + if (n.qualified ()) + fail (nloc) << "project name in scope/target " << n; + target* ot (target_); scope* os (scope_); @@ -342,6 +345,7 @@ namespace build // prerequisite& p ( scope_->prerequisites.insert ( + pn.proj, *ti, move (pn.dir), move (pn.value), @@ -354,6 +358,9 @@ namespace build for (auto& tn: ns) { + if (tn.qualified ()) + fail (nloc) << "project name in target " << tn; + target& t (enter_target (move (tn))); //@@ OPT: move if last/single target (common cases). @@ -414,6 +421,9 @@ namespace build for (name& n: ns) { + if (n.qualified () || n.empty () || n.value.empty ()) + fail (l) << "expected buildfile instead of " << n; + // Construct the buildfile path. // path p (move (n.dir)); @@ -497,6 +507,9 @@ namespace build for (name& n: ns) { + if (n.qualified () || n.empty ()) + fail (l) << "expected buildfile instead of " << n; + // Construct the buildfile path. If it is a directory, then append // 'buildfile'. // @@ -654,7 +667,9 @@ namespace build for (name& n: ns) { - list_value r (build::import (*scope_, n, l)); + // build::import() will check the name, if required. + // + list_value r (build::import (*scope_, move (n), l)); if (val.defined ()) { @@ -684,6 +699,7 @@ namespace build 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. // export_value_ = (tt != type::newline && tt != type::eos ? names (t, tt) @@ -767,7 +783,6 @@ namespace build names_type vns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); - if (assign) { auto v (target_ != nullptr @@ -800,6 +815,7 @@ namespace build type& tt, names_type& ns, size_t pair, + const std::string* pp, const dir_path* dp, const string* tp) { @@ -857,11 +873,45 @@ namespace build continue; } - string::size_type p (name.rfind ('/')); + string::size_type p (name.find_last_of ("/%")); + + // First take care of project. A project-qualified name is + // not very common, so we can afford some copying for the + // sake of simplicity. + // + const string* pp1 (pp); + + if (p != string::npos) + { + bool last (name[p] == '%'); + string::size_type p1 (last ? p : name.rfind ('%', p - 1)); + + if (p1 != string::npos) + { + string proj; + proj.swap (name); + + // First fix the rest of the name. + // + name.assign (proj, p1 + 1, string::npos); + p = last ? string::npos : p - (p1 + 1); + + // Now process the project name. + // @@ Validate it. + // + proj.resize (p1); + + if (pp != nullptr) + fail (t) << "nested project name " << proj; + + pp1 = &project_name_pool.find (proj); + } + } + string::size_type n (name.size () - 1); - // See if this is a type name, directory prefix, or both. That is, - // it is followed by '{'. + // See if this is a type name, directory prefix, or both. That + // is, it is followed by '{'. // if (tt == type::lcbrace) { @@ -907,7 +957,7 @@ namespace build (pair != 0 ? pair : (ns.empty () || ns.back ().pair == '\0' ? 0 : ns.size ())), - dp1, tp1); + pp1, dp1, tp1); count = ns.size () - count; if (tt != type::rcbrace) @@ -945,13 +995,15 @@ namespace build if (dp != nullptr) dir = *dp / dir; - ns.emplace_back ((tp != nullptr ? *tp : string ()), + ns.emplace_back (pp1, move (dir), + (tp != nullptr ? *tp : string ()), string ()); } else - ns.emplace_back ((tp != nullptr ? *tp : string ()), + ns.emplace_back (pp1, (dp != nullptr ? *dp : dir_path ()), + (tp != nullptr ? *tp : string ()), move (name)); count = 1; @@ -1025,6 +1077,10 @@ namespace build const name& n (lv[0]); + if (n.proj != nullptr) + fail (t) << "concatenating expansion of " << var.name + << " contains project name"; + if (!n.type.empty ()) fail (t) << "concatenating expansion of " << var.name << " contains type"; @@ -1047,9 +1103,19 @@ namespace build // for (const name& n: lv) { + const string* pp1 (pp); const dir_path* dp1 (dp); const string* tp1 (tp); + if (n.proj != 0) + { + if (pp == nullptr) + pp1 = n.proj; + else + fail (t) << "nested project name " << *n.proj << " in " + << "variable expansion"; + } + dir_path d1; if (!n.dir.empty ()) { @@ -1090,8 +1156,9 @@ namespace build ns.push_back (ns[pair - 1]); } - ns.emplace_back ((tp1 != nullptr ? *tp1 : string ()), + ns.emplace_back (pp1, (dp1 != nullptr ? *dp1 : dir_path ()), + (tp1 != nullptr ? *tp1 : string ()), n.value); } @@ -1112,7 +1179,7 @@ namespace build (pair != 0 ? pair : (ns.empty () || ns.back ().pair == '\0' ? 0 : ns.size ())), - dp, tp); + pp, dp, tp); count = ns.size () - count; if (tt != type::rcbrace) @@ -1136,9 +1203,10 @@ namespace build { // Empty LHS, (e.g., {=y}), create an empty name. // - ns.emplace_back ((tp != nullptr ? *tp : string ()), + ns.emplace_back (pp, (dp != nullptr ? *dp : dir_path ()), - ""); + (tp != nullptr ? *tp : string ()), + string ()); count = 1; } @@ -1160,9 +1228,10 @@ namespace build if (pair != 0 && pair != ns.size ()) ns.push_back (ns[pair - 1]); - ns.emplace_back ((tp != nullptr ? *tp : string ()), + ns.emplace_back (pp, (dp != nullptr ? *dp : dir_path ()), - ""); + (tp != nullptr ? *tp : string ()), + string ()); break; } else @@ -1173,9 +1242,10 @@ namespace build // if (!ns.empty () && ns.back ().pair != '\0') { - ns.emplace_back ((tp != nullptr ? *tp : string ()), + ns.emplace_back (pp, (dp != nullptr ? *dp : dir_path ()), - ""); + (tp != nullptr ? *tp : string ()), + string ()); } } @@ -1267,6 +1337,11 @@ namespace build for (auto i (ns.begin ()), e (i + targets); i != e; ++i) { + // @@ We may actually want to support this at some point. + // + if (i->qualified ()) + fail (l) << "target name expected instead of " << *i; + if (opname (*i)) ms.push_back (opspec (move (i->value))); else @@ -1276,7 +1351,7 @@ namespace build dir_path src_base; if (i->pair != '\0') { - if (!i->type.empty ()) + if (i->typed ()) fail (l) << "expected target src_base instead of " << *i; src_base = move (i->dir); @@ -1416,6 +1491,7 @@ namespace build prerequisite& p ( scope_->prerequisites.insert ( + nullptr, dt.type (), dt.dir, dt.name, diff --git a/build/prerequisite b/build/prerequisite index e33feb6..f91a199 100644 --- a/build/prerequisite +++ b/build/prerequisite @@ -31,6 +31,8 @@ namespace build public: typedef build::scope scope_type; + mutable const std::string* const* proj; // Only *proj can be NULL, points + // to project_name_pool. target_key tk; mutable scope_type* scope; // Can be NULL if tk.dir is absolute. }; @@ -39,7 +41,10 @@ namespace build operator< (const prerequisite_key& x, const prerequisite_key& y) { assert (x.scope == y.scope); - return x.tk < y.tk; + + // Can compare project name pointers since they are from project_name_pool. + // + return *x.proj < *y.proj || (*x.proj == *y.proj && x.tk < y.tk); } std::ostream& @@ -52,28 +57,34 @@ namespace build typedef build::target_type target_type_type; typedef build::scope scope_type; - prerequisite (const target_type_type& t, + prerequisite (const std::string* p, + const target_type_type& t, dir_path d, std::string n, const std::string* e, scope_type& s) - : type (t), dir (std::move (d)), name (std::move (n)), ext (e), - scope (s), target (nullptr) {} + : proj (p), + type (t), + dir (std::move (d)), + name (std::move (n)), + ext (e), + scope (s), + target (nullptr) {} public: + const std::string* proj; // NULL if not project-qualified. const target_type_type& type; - const dir_path dir; // Normalized absolute or relative (to scope). + const dir_path dir; // Normalized absolute or relative (to scope). const std::string name; - const std::string* ext; // NULL if unspecified. + const std::string* ext; // NULL if unspecified. scope_type& scope; - target_type* target; // NULL if not yet resolved. Note that this should - // always be the "primary target", not a member of - // a target group. - + target_type* target; // NULL if not yet resolved. Note that this should + // always be the "primary target", not a member of + // a target group. prerequisite_key key () const { - return prerequisite_key {{&type, &dir, &name, &ext}, &scope}; + return prerequisite_key {&proj, {&type, &dir, &name, &ext}, &scope}; } public: @@ -101,7 +112,8 @@ namespace build struct prerequisite_set: std::set<prerequisite> { std::pair<prerequisite&, bool> - insert (const target_type&, + insert (const std::string* proj, + const target_type&, dir_path dir, std::string name, const std::string* ext, @@ -109,9 +121,9 @@ namespace build tracer&); std::pair<prerequisite&, bool> - insert (const target_key& tk, scope& s, tracer& t) + insert (const std::string* proj, const target_key& tk, scope& s, tracer& t) { - return insert (*tk.type, *tk.dir, *tk.name, *tk.ext, s, t); + return insert (proj, *tk.type, *tk.dir, *tk.name, *tk.ext, s, t); } }; } diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx index d4c8a9c..2437826 100644 --- a/build/prerequisite.cxx +++ b/build/prerequisite.cxx @@ -20,9 +20,14 @@ namespace build ostream& operator<< (ostream& os, const prerequisite_key& pk) { - // Print scope unless the prerequisite's directory is absolute. + if (*pk.proj != nullptr) + os << **pk.proj << '%'; // - if (!pk.tk.dir->absolute ()) + // Don't print scope if we are project-qualified or the + // prerequisite's directory is absolute. In both these + // cases the scope is not used to resolve it to target. + // + else if (!pk.tk.dir->absolute ()) { string s (diag_relative (pk.scope->path (), false)); @@ -36,7 +41,8 @@ namespace build // prerequisite_set // auto prerequisite_set:: - insert (const target_type& tt, + insert (const std::string* proj, + const target_type& tt, dir_path dir, std::string name, const std::string* ext, @@ -48,7 +54,7 @@ namespace build // Find or insert. // - auto r (emplace (tt, move (dir), move (name), ext, s)); + auto r (emplace (proj, tt, move (dir), move (name), ext, s)); prerequisite& p (const_cast<prerequisite&> (*r.first)); // Update extension if the existing prerequisite has it unspecified. diff --git a/build/scope.cxx b/build/scope.cxx index 4645979..600c46e 100644 --- a/build/scope.cxx +++ b/build/scope.cxx @@ -78,7 +78,7 @@ namespace build // First determine the target type. // const char* tt; - if (n.type.empty ()) + if (n.untyped ()) { // Empty name or '.' and '..' signify a directory. // diff --git a/build/target b/build/target index 85eed1a..a1adbbd 100644 --- a/build/target +++ b/build/target @@ -498,7 +498,7 @@ namespace build key () const { return target != nullptr - ? prerequisite_key {target->key (), nullptr} + ? prerequisite_key {&prerequisite.get ().proj, target->key (), nullptr} : prerequisite.get ().key (); } diff --git a/build/target-key b/build/target-key index 37fa6ab..f49efb2 100644 --- a/build/target-key +++ b/build/target-key @@ -26,7 +26,7 @@ namespace build mutable const target_type* type; mutable const dir_path* dir; mutable const std::string* name; - mutable const std::string* const* ext; // Note only *ext can be NULL. + mutable const std::string* const* ext; // Note: only *ext can be NULL. friend bool operator< (const target_key& x, const target_key& y) diff --git a/build/target.ixx b/build/target.ixx index 8cf9e77..3ae9660 100644 --- a/build/target.ixx +++ b/build/target.ixx @@ -24,7 +24,7 @@ namespace build // The use of the group's prerequisite scope is debatable. // scope& s (prerequisite.get ().scope); - return s.prerequisites.insert (key ().tk, s, trace).first; + return s.prerequisites.insert (nullptr, key ().tk, s, trace).first; } // prerequisite_members diff --git a/build/target.txx b/build/target.txx index 6fe3f33..bded149 100644 --- a/build/target.txx +++ b/build/target.txx @@ -3,7 +3,7 @@ // license : MIT; see accompanying LICENSE file #include <build/scope> -#include <build/utility> // extension_pool +#include <build/context> // extension_pool #include <build/diagnostics> #include <build/prerequisite> @@ -36,7 +36,10 @@ namespace build if (tk.dir->absolute ()) dr << "target " << tk; else - dr << "prerequisite " << prerequisite_key {tk, &s}; + { + const std::string* proj (nullptr); // Used for local prerequisites. + dr << "prerequisite " << prerequisite_key {&proj, tk, &s}; + } } return extension_pool.find (val.as<const std::string&> ()); diff --git a/build/utility b/build/utility index 90d1694..42d7f0f 100644 --- a/build/utility +++ b/build/utility @@ -79,8 +79,6 @@ namespace build const std::string& find (const std::string& s) {return *emplace (s).first;} }; - - extern string_pool extension_pool; } #endif // BUILD_UTILITY diff --git a/build/utility.cxx b/build/utility.cxx index bc2c559..d394186 100644 --- a/build/utility.cxx +++ b/build/utility.cxx @@ -13,6 +13,4 @@ namespace build const dir_path empty_dir_path; bool exception_unwinding_dtor = false; - - string_pool extension_pool; } diff --git a/build/variable b/build/variable index 835fde1..bb29138 100644 --- a/build/variable +++ b/build/variable @@ -72,8 +72,9 @@ namespace build list_value () = default; list_value (names d): names (std::move (d)) {} - list_value (std::string d) {emplace_back (std::move (d));} + list_value (name n) {emplace_back (std::move (n));} list_value (dir_path d) {emplace_back (std::move (d));} + list_value (std::string d) {emplace_back (std::move (d));} virtual value_ptr clone () const {return value_ptr (new list_value (*this));} |