aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-07-15 14:44:15 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-07-15 14:44:15 +0200
commit243da3993c138d33063f633aa3996a8a710ea396 (patch)
tree6d49a3f964f395773c06e258b6550a4d386fbec3 /build
parent3c2bc8595e9d6cf6ff35079231c3aab474a38130 (diff)
Implement project-qualified names/prerequisites, two-stage import
Diffstat (limited to 'build')
-rw-r--r--build/algorithm.cxx7
-rw-r--r--build/algorithm.ixx9
-rw-r--r--build/b.cxx4
-rw-r--r--build/buildfile2
-rw-r--r--build/context5
-rw-r--r--build/context.cxx7
-rw-r--r--build/file27
-rw-r--r--build/file.cxx95
-rw-r--r--build/name51
-rw-r--r--build/name.cxx3
-rw-r--r--build/parser9
-rw-r--r--build/parser.cxx116
-rw-r--r--build/prerequisite40
-rw-r--r--build/prerequisite.cxx14
-rw-r--r--build/scope.cxx2
-rw-r--r--build/target2
-rw-r--r--build/target-key2
-rw-r--r--build/target.ixx2
-rw-r--r--build/target.txx7
-rw-r--r--build/utility2
-rw-r--r--build/utility.cxx2
-rw-r--r--build/variable3
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>
diff --git a/build/file b/build/file
index b786cf7..8a5f9fa 100644
--- a/build/file
+++ b/build/file
@@ -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.
+ }
}
diff --git a/build/name b/build/name
index fdfe083..111b661 100644
--- a/build/name
+++ b/build/name
@@ -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));}