aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-09 08:43:58 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-09 08:43:58 +0200
commit2e98d3ec3aa57c7b1776d3bf5e7e219a9a3cb3af (patch)
tree9a7fd8701853c5df17587be0ef00c61a32d6fc40 /build
parent7de6f6f275d840e8d9523c72d8f4309c51b4dcd3 (diff)
Build according to buildspec
At this stage operations are still ignored.
Diffstat (limited to 'build')
-rw-r--r--build/b.cxx305
-rw-r--r--build/name.cxx19
-rw-r--r--build/parser.cxx136
-rw-r--r--build/path2
-rw-r--r--build/search.cxx2
-rw-r--r--build/spec10
-rw-r--r--build/spec.cxx4
-rw-r--r--build/target36
-rw-r--r--build/target.cxx140
9 files changed, 406 insertions, 248 deletions
diff --git a/build/b.cxx b/build/b.cxx
index de76071..0a1d8d2 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -19,6 +19,7 @@
#include <system_error>
#include <build/spec>
+#include <build/name>
#include <build/scope>
#include <build/target>
#include <build/prerequisite>
@@ -171,83 +172,146 @@ main (int argc, char* argv[])
}
}
- if (verb >= 4)
- trace << "buildspec: " << bspec;
+ level4 ([&]{trace << "buildspec: " << bspec;});
- // Figure out {src,out}_{root,base}. Note that all the paths must be
- // normalized.
- //
- //@@ Must be normalized.
+ // Load all the buildfiles.
//
- path out_base (work);
- path src_base (out_base); //@@ TMP
+ if (bspec.empty ())
+ bspec.push_back (metaopspec ()); // Default meta-operation.
- 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 ())
+ for (metaopspec& ms: bspec)
{
- if (path_mtime (d / path ("build/pre.build")) != timestamp_nonexistent)
+ if (ms.empty ())
+ ms.push_back (opspec ()); // Default operation.
+
+ for (opspec& os: ms)
{
- src_root = d;
- break;
+ if (os.empty ())
+ // Default target: dir{}.
+ //
+ os.push_back (targetspec (name ("dir", path (), string ())));
+
+ for (targetspec& ts: os)
+ {
+ name& tn (ts.target);
+
+ // First figure out the out_base of this target. The logic
+ // is as follows: if a directory was specified in any form,
+ // then that's the out_base. Otherwise, we check if the name
+ // value has a directory prefix. This has a good balance of
+ // control and the expected result in most cases.
+ //
+ path out_base (tn.dir);
+ if (out_base.empty ())
+ {
+ // See if there is a directory part in value. We cannot
+ // assume it is a valid filesystem name so we will have
+ // to do the splitting manually.
+ //
+ path::size_type i (path::traits::rfind_separator (tn.value));
+
+ if (i != string::npos)
+ out_base = path (tn.value, i != 0 ? i : 1); // Special case: "/".
+ }
+
+ if (out_base.relative ())
+ out_base = work / out_base;
+
+ out_base.normalize ();
+
+ path& src_base (ts.src_base);
+ if (src_base.empty ())
+ {
+ //@@ TODO: Configured case: find out_root (looking for
+ // "build/bootstrap.build" or some such), then src_root
+ // (stored in this file). Need to also detect the in-tree
+ // build.
+ //
+
+ // If that doesn't work out (e.g., the first build), then
+ // default to the working directory as src_base.
+ //
+ src_base = work;
+ }
+
+ if (src_base.relative ())
+ src_base = work / src_base;
+
+ src_base.normalize ();
+
+ 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), f ("build/pre.build");
+ !d.root () && d != home;
+ d = d.directory ())
+ {
+ if (path_mtime (d / f) != 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;
+ out_root = out_base;
+ }
+ else
+ out_root = out_base.directory (src_base.leaf (src_root));
+
+ if (verb >= 4)
+ {
+ trace << tn;
+ 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 project root and base scopes, set the corresponding
+ // variables. Note that we might already have all of this set
+ // up as a result of one of the preceding target processing.
+ //
+ 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 the buildfile.
+ //
+ path bf (src_base / path ("buildfile"));
+
+ ifstream ifs (bf.string ());
+ if (!ifs.is_open ())
+ fail << "unable to open " << bf;
+
+ ifs.exceptions (ifstream::failbit | ifstream::badbit);
+ parser p;
+
+ try
+ {
+ p.parse_buildfile (ifs, bf, pbase_scope);
+ }
+ catch (const std::ios_base::failure&)
+ {
+ fail << "failed to read from " << bf;
+ }
+ }
}
}
- // 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;
- out_root = out_base;
- }
- else
- out_root = out_base.directory (src_base.leaf (src_root));
-
- if (verb >= 4)
- {
- 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 project root and base scopes, set the corresponding
- // variables.
- //
- 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.
- //
- path bf ("buildfile");
-
- ifstream ifs (bf.string ());
- if (!ifs.is_open ())
- fail << "unable to open " << bf;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
- parser p;
-
- try
- {
- p.parse_buildfile (ifs, bf, pbase_scope);
- }
- catch (const std::ios_base::failure&)
- {
- fail << "failed to read from " << bf;
- }
-
dump_scopes ();
dump ();
@@ -268,30 +332,93 @@ main (int argc, char* argv[])
path_rule path_r;
rules[typeid (path_target)].emplace ("path", path_r);
- // Build.
+ // Do the operations. We do meta-operations and operations sequentially
+ // (no parallelism).
//
- auto i (targets.find (dir::static_type.id, out_base, "", nullptr, trace));
- if (i == targets.end ())
- fail << "no targets in " << bf;
-
- target& t (**i);
-
- match (t);
-
- dump ();
-
- switch (update (t))
+ for (metaopspec& ms: bspec)
{
- case target_state::uptodate:
+ for (opspec& os: ms)
{
- info << "target " << t << " is up to date";
- break;
+ // But multiple targets in the same operation can be done in
+ // parallel.
+ //
+ vector<reference_wrapper<target>> tgs;
+ tgs.reserve (os.size ());
+
+ // First resolve and match all the targets. We don't want to
+ // start building before we know how for all the targets in
+ // this operation.
+ //
+ for (targetspec& ts: os)
+ {
+ name& tn (ts.target);
+ const location l ("<buildspec>", 1, 0); //@@ TODO
+
+ const string* e;
+ const target_type* ti (target_types.find (tn, e));
+
+ if (ti == nullptr)
+ fail (l) << "unknown target type " << tn.type;
+
+ // If the directory is relative, assume it is relative to work
+ // (must be consistent with how we derive out_base).
+ //
+ path& d (tn.dir);
+
+ if (d.relative ())
+ d = work / d;
+
+ d.normalize ();
+
+ target_set::key tk {ti, &d, &tn.value, &e};
+ auto i (targets.find (tk, trace));
+ if (i == targets.end ())
+ fail (l) << "unknown target " << tk;
+
+ target& t (**i);
+
+ if (!t.recipe ())
+ {
+ level4 ([&]{trace << "matching target " << t;});
+ match (t);
+ }
+
+ tgs.push_back (t);
+ }
+
+ dump ();
+
+ // Now build.
+ //
+ for (target& t: tgs)
+ {
+ // The target might have already been updated indirectly. We
+ // still want to inform the user about its status since they
+ // requested its update explicitly.
+ //
+ target_state s (t.state ());
+ if (s == target_state::unknown)
+ {
+ level4 ([&]{trace << "updating target " << t;});
+ s = update (t);
+ }
+
+ switch (s)
+ {
+ case target_state::uptodate:
+ {
+ info << "target " << t << " is up to date";
+ break;
+ }
+ case target_state::updated:
+ break;
+ case target_state::failed:
+ //@@ This could probably happen in a parallel build.
+ case target_state::unknown:
+ assert (false);
+ }
+ }
}
- case target_state::updated:
- break;
- case target_state::failed:
- case target_state::unknown:
- assert (false);
}
}
catch (const failed&)
diff --git a/build/name.cxx b/build/name.cxx
index 7f300f4..46e2440 100644
--- a/build/name.cxx
+++ b/build/name.cxx
@@ -19,8 +19,9 @@ namespace build
bool hv (!n.value.empty ());
bool hd (false);
- // Print the directory before type.
- //
+ if (ht)
+ os << n.type << '{';
+
if (!n.dir.empty ())
{
string s (diag_relative_work (n.dir));
@@ -31,24 +32,20 @@ namespace build
{
os << s;
- // Add the directory separator unless it is already there.
+ // Add the directory separator unless it is already there
+ // or we have type but no value. The idea is to print foo/
+ // or dir{foo}.
//
- if (s.back () != path::traits::directory_separator)
+ if (s.back () != path::traits::directory_separator && (hv || !ht))
os << path::traits::directory_separator;
hd = true;
}
}
- if (ht)
- os << n.type;
-
- if (ht || (hd && hv))
- os << '{';
-
os << n.value;
- if (ht || (hd && hv))
+ if (ht)
os << '}';
if (!ht && !hv && !hd)
diff --git a/build/parser.cxx b/build/parser.cxx
index 5cdd627..2c629d6 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -36,84 +36,6 @@ namespace build
typedef token_type type;
- // Given a target or prerequisite name, figure out its type, taking
- // into account extensions, special names (e.g., '.' and '..'), or
- // anything else that might be relevant. Also process the name (in
- // place) by extracting the extension, adjusting dir/value, etc.
- //
- const target_type&
- find_target_type (name& n, const location& l, const string*& ext)
- {
- string& v (n.value);
-
- // First determine the target type.
- //
- const char* tt;
- if (n.type.empty ())
- {
- // Empty name or '.' and '..' signify a directory.
- //
- if (v.empty () || v == "." || v == "..")
- tt = "dir";
- else
- //@@ TODO: derive type from extension.
- //
- tt = "file";
- }
- else
- tt = n.type.c_str ();
-
- auto i (target_types.find (tt));
- if (i == target_types.end ())
- fail (l) << "unknown target type " << tt;
-
- const target_type& ti (i->second);
-
- ext = nullptr;
-
- // Directories require special name processing. If we find that more
- // targets deviate, then we should make this target-type-specific.
- //
- if (ti.id == dir::static_type.id || ti.id == fsdir::static_type.id)
- {
- // The canonical representation of a directory name is with empty
- // value.
- //
- if (!v.empty ())
- {
- n.dir /= path (v); // Move name value to dir.
- v.clear ();
- }
- }
- else
- {
- // Split the path into its directory part (if any) the name part,
- // and the extension (if any). We cannot assume the name part is
- // a valid filesystem name so we will have to do the splitting
- // manually.
- //
- path::size_type i (path::traits::rfind_separator (v));
-
- if (i != string::npos)
- {
- n.dir /= path (v, i != 0 ? i : 1); // Special case: "/".
- v = string (v, i + 1, string::npos);
- }
-
- // Extract the extension.
- //
- string::size_type j (path::traits::find_extension (v));
-
- if (j != string::npos)
- {
- ext = &extension_pool.find (v.c_str () + j);
- v.resize (j - 1);
- }
- }
-
- return ti;
- }
-
void parser::
parse_buildfile (istream& is, const path& p, scope& s)
{
@@ -299,7 +221,10 @@ namespace build
for (auto& pn: pns)
{
const string* e;
- const target_type& ti (find_target_type (pn, ploc, e));
+ const target_type* ti (target_types.find (pn, e));
+
+ if (ti == nullptr)
+ fail (ploc) << "unknown target type " << pn.type;
pn.dir.normalize ();
@@ -307,7 +232,7 @@ namespace build
//
prerequisite& p (
scope_->prerequisites.insert (
- ti, move (pn.dir), move (pn.value), e, *scope_, trace).first);
+ *ti, move (pn.dir), move (pn.value), e, *scope_, trace).first);
ps.push_back (p);
}
@@ -315,7 +240,11 @@ namespace build
for (auto& tn: ns)
{
const string* e;
- const target_type& ti (find_target_type (tn, nloc, e));
+ const target_type* ti (target_types.find (tn, e));
+
+ if (ti == nullptr)
+ fail (nloc) << "unknown target type " << tn.type;
+
path& d (tn.dir);
if (d.empty ())
@@ -332,7 +261,7 @@ namespace build
//
target& t (
targets.insert (
- ti, move (tn.dir), move (tn.value), e, trace).first);
+ *ti, move (tn.dir), move (tn.value), e, trace).first);
t.prerequisites = ps; //@@ OPT: move if last target.
@@ -764,7 +693,8 @@ namespace build
// Note that at this stage we don't treat '.' and '..' as special
// (unless they are specified with a directory separator) because
// then we would have ended up treating '.: ...' as a directory
- // scope. Instead, this is handled higher up, in find_target_type().
+ // scope. Instead, this is handled higher up the processing chain,
+ // in target_types::find().
//
// @@ TODO: and not quoted
//
@@ -1097,36 +1027,36 @@ namespace build
if (bs.empty () || !bs.back ().meta_operation.empty ())
bs.push_back (metaopspec ()); // Empty (default) meta operation.
- metaopspec& mo (bs.back ());
+ metaopspec& ms (bs.back ());
for (auto i (ns.begin ()), e (i + targets); i != e; ++i)
{
if (opname (*i))
- mo.push_back (opspec (move (i->value)));
+ ms.push_back (opspec (move (i->value)));
else
{
- // Do we have the src_root?
+ // Do we have the src_base?
//
- path src_root;
+ path src_base;
if (i->pair)
{
if (!i->type.empty ())
- fail (l) << "expected target src_root instead of " << *i;
+ fail (l) << "expected target src_base instead of " << *i;
- src_root = move (i->dir);
+ src_base = move (i->dir);
if (!i->value.empty ())
- src_root /= path (move (i->value));
+ src_base /= path (move (i->value));
++i;
assert (i != e);
}
- if (mo.empty () || !mo.back ().operation.empty ())
- mo.push_back (opspec ()); // Empty (default) operation.
+ if (ms.empty () || !ms.back ().operation.empty ())
+ ms.push_back (opspec ()); // Empty (default) operation.
- opspec& os (mo.back ());
- os.emplace_back (move (src_root), move (*i));
+ opspec& os (ms.back ());
+ os.emplace_back (move (src_base), move (*i));
}
}
}
@@ -1146,16 +1076,16 @@ namespace build
// checks.
//
bool meta (false);
- for (const metaopspec& mo: nbs)
+ for (const metaopspec& nms: nbs)
{
- if (!mo.meta_operation.empty ())
- fail (l) << "nested meta-operation " << mo.meta_operation;
+ if (!nms.meta_operation.empty ())
+ fail (l) << "nested meta-operation " << nms.meta_operation;
if (!meta)
{
- for (const opspec& o: mo)
+ for (const opspec& nos: nms)
{
- if (!o.operation.empty ())
+ if (!nos.operation.empty ())
{
meta = true;
break;
@@ -1181,13 +1111,13 @@ namespace build
// should be just a bunch of targets.
//
assert (nmo.size () == 1);
- opspec& no (nmo.back ());
+ opspec& nos (nmo.back ());
if (bs.empty () || !bs.back ().meta_operation.empty ())
bs.push_back (metaopspec ()); // Empty (default) meta operation.
- no.operation = move (ns.back ().value);
- bs.back ().push_back (move (no));
+ nos.operation = move (ns.back ().value);
+ bs.back ().push_back (move (nos));
}
next (t, tt); // Done with ')'.
@@ -1210,7 +1140,7 @@ namespace build
// then we don't do anything.
//
if (default_target_ == nullptr || // No targets in this buildfile.
- targets.find (dir::static_type.id, // Explicit current dir target.
+ targets.find (dir::static_type, // Explicit current dir target.
scope_->path (),
"",
nullptr,
diff --git a/build/path b/build/path
index 9b68485..550af80 100644
--- a/build/path
+++ b/build/path
@@ -74,6 +74,8 @@ namespace build
return string_type::npos;
}
+ // Return the position of '.' or npos if there is no extension.
+ //
static size_type
find_extension (string_type const& s)
{
diff --git a/build/search.cxx b/build/search.cxx
index e80f7e7..5f21c64 100644
--- a/build/search.cxx
+++ b/build/search.cxx
@@ -41,7 +41,7 @@ namespace build
}
}
- auto i (targets.find (p.type.id, d, p.name, p.ext, trace));
+ auto i (targets.find (p.type, d, p.name, p.ext, trace));
if (i == targets.end ())
return 0;
diff --git a/build/spec b/build/spec
index 9ca8c4d..fda4186 100644
--- a/build/spec
+++ b/build/spec
@@ -17,11 +17,13 @@ namespace build
{
struct targetspec
{
- targetspec (path sr, name t)
- : src_root (std::move (sr)), target (std::move (t)) {}
+ explicit
+ targetspec (name t): target (std::move (t)) {}
+ targetspec (path sb, name t)
+ : src_base (std::move (sb)), target (std::move (t)) {}
- path src_root;
- name target; // target.dir is out_root.
+ path src_base;
+ name target;
};
struct opspec: std::vector<targetspec>
diff --git a/build/spec.cxx b/build/spec.cxx
index fb83b31..31bd4f1 100644
--- a/build/spec.cxx
+++ b/build/spec.cxx
@@ -15,9 +15,9 @@ namespace build
ostream&
operator<< (ostream& os, const targetspec& s)
{
- if (!s.src_root.empty ())
+ if (!s.src_base.empty ())
{
- string d (diag_relative_work (s.src_root));
+ string d (diag_relative_work (s.src_base));
if (d != ".")
{
diff --git a/build/target b/build/target
index 31a590d..9c4640e 100644
--- a/build/target
+++ b/build/target
@@ -18,6 +18,7 @@
#include <build/path>
#include <build/key-set>
#include <build/timestamp>
+#include <build/name>
#include <build/prerequisite>
#include <build/utility> // compare_*, extension_pool
@@ -102,14 +103,17 @@ namespace build
{
struct key
{
- mutable const std::type_index* type;
+ mutable const target_type* type;
mutable const path* dir;
mutable const std::string* name;
- mutable const std::string** ext;
+ mutable const std::string* const* ext;
friend bool
operator< (const key& x, const key& y)
{
+ const std::type_index& xt (x.type->id);
+ const std::type_index& yt (y.type->id);
+
//@@ TODO: use compare() to compare once.
// Unspecified and specified extension are assumed equal. The
@@ -117,10 +121,10 @@ namespace build
// pointers.
//
return
- (*x.type < *y.type) ||
- (*x.type == *y.type && *x.name < *y.name) ||
- (*x.type == *y.type && *x.name == *y.name && *x.dir < *y.dir) ||
- (*x.type == *y.type && *x.name == *y.name && *x.dir == *y.dir &&
+ (xt < yt) ||
+ (xt == yt && *x.name < *y.name) ||
+ (xt == yt && *x.name == *y.name && *x.dir < *y.dir) ||
+ (xt == yt && *x.name == *y.name && *x.dir == *y.dir &&
*x.ext != nullptr && *y.ext != nullptr && **x.ext < **y.ext);
}
};
@@ -132,7 +136,7 @@ namespace build
find (const key& k, tracer& trace) const;
iterator
- find (const std::type_index& type,
+ find (const target_type& type,
const path& dir,
const std::string& name,
const std::string* ext,
@@ -156,6 +160,9 @@ namespace build
map map_;
};
+ std::ostream&
+ operator<< (std::ostream&, const target_set::key&);
+
extern target_set targets;
class target_type_map: public std::map<
@@ -164,8 +171,23 @@ namespace build
compare_c_string>
{
public:
+ typedef std::map<const char*,
+ std::reference_wrapper<const target_type>,
+ compare_c_string> base;
+
void
insert (const target_type& tt) {emplace (tt.name, tt);}
+
+ using base::find;
+
+ // Given a name, figure out its type, taking into account extensions,
+ // special names (e.g., '.' and '..'), or anything else that might be
+ // relevant. Also process the name (in place) by extracting the
+ // extension, adjusting dir/value, etc (note that the dir is not
+ // necessarily normalized). Return NULL if not found.
+ //
+ const target_type*
+ find (name&, const std::string*& ext) const;
};
extern target_type_map target_types;
diff --git a/build/target.cxx b/build/target.cxx
index 6fdfb7b..f09a858 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -13,39 +13,12 @@ using namespace std;
namespace build
{
- // target_type
- //
- const target_type*
- resolve_target_type (const std::string name);
-
// target
//
ostream&
operator<< (ostream& os, const target& t)
{
- os << t.type ().name << '{';
-
- if (!t.dir.empty ())
- {
- string s (diag_relative_work (t.dir));
-
- if (s != ".")
- {
- os << s;
-
- if (!t.name.empty () && s.back () != path::traits::directory_separator)
- os << path::traits::directory_separator;
- }
- }
-
- os << t.name;
-
- if (t.ext != nullptr && !t.ext->empty ())
- os << '.' << *t.ext;
-
- os << '}';
-
- return os;
+ return os << target_set::key {&t.type (), &t.dir, &t.name, &t.ext};
}
static target*
@@ -59,6 +32,8 @@ namespace build
// target_set
//
+ target_set targets;
+
auto target_set::
find (const key& k, tracer& trace) const -> iterator
{
@@ -99,22 +74,125 @@ namespace build
const std::string* ext,
tracer& trace)
{
- iterator i (find (key {&tt.id, &dir, &name, &ext}, trace));
+ iterator i (find (key {&tt, &dir, &name, &ext}, trace));
if (i != end ())
return pair<target&, bool> (**i, false);
unique_ptr<target> t (tt.factory (move (dir), move (name), ext));
i = map_.emplace (
- make_pair (key {&tt.id, &t->dir, &t->name, &t->ext},
+ make_pair (key {&tt, &t->dir, &t->name, &t->ext},
move (t))).first;
return pair<target&, bool> (**i, true);
}
- target_set targets;
+ ostream&
+ operator<< (ostream& os, const target_set::key& k)
+ {
+ os << k.type->name << '{';
+
+ if (!k.dir->empty ())
+ {
+ string s (diag_relative_work (*k.dir));
+
+ if (s != ".")
+ {
+ os << s;
+
+ if (!k.name->empty () &&
+ s.back () != path::traits::directory_separator)
+ os << path::traits::directory_separator;
+ }
+ }
+
+ os << *k.name;
+
+ if (*k.ext != nullptr && !(*k.ext)->empty ())
+ os << '.' << **k.ext;
+
+ os << '}';
+
+ return os;
+ }
+
+ //
+ //
target_type_map target_types;
+ const target_type* target_type_map::
+ find (name& n, const string*& ext) const
+ {
+ ext = nullptr;
+
+ string& v (n.value);
+
+ // First determine the target type.
+ //
+ const char* tt;
+ if (n.type.empty ())
+ {
+ // Empty name or '.' and '..' signify a directory.
+ //
+ if (v.empty () || v == "." || v == "..")
+ tt = "dir";
+ else
+ //@@ TODO: derive type from extension.
+ //
+ tt = "file";
+ }
+ else
+ tt = n.type.c_str ();
+
+ auto i (find (tt));
+ if (i == end ())
+ return nullptr;
+
+ const target_type& ti (i->second);
+
+ // Directories require special name processing. If we find that more
+ // targets deviate, then we should make this target-type-specific.
+ //
+ if (ti.id == dir::static_type.id || ti.id == fsdir::static_type.id)
+ {
+ // The canonical representation of a directory name is with empty
+ // value.
+ //
+ if (!v.empty ())
+ {
+ n.dir /= path (v); // Move name value to dir.
+ v.clear ();
+ }
+ }
+ else
+ {
+ // Split the path into its directory part (if any) the name part,
+ // and the extension (if any). We cannot assume the name part is
+ // a valid filesystem name so we will have to do the splitting
+ // manually.
+ //
+ path::size_type i (path::traits::rfind_separator (v));
+
+ if (i != string::npos)
+ {
+ n.dir /= path (v, i != 0 ? i : 1); // Special case: "/".
+ v = string (v, i + 1, string::npos);
+ }
+
+ // Extract the extension.
+ //
+ string::size_type j (path::traits::find_extension (v));
+
+ if (j != string::npos)
+ {
+ ext = &extension_pool.find (v.c_str () + j + 1);
+ v.resize (j);
+ }
+ }
+
+ return &ti;
+ }
+
// path_target
//
timestamp path_target::