aboutsummaryrefslogtreecommitdiff
path: root/build/parser.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-03 11:43:03 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-03 11:43:03 +0200
commit17287b34a8090d381278c02b7bc6676669968099 (patch)
tree276fb40c1a06b0b3683b0dcad24e92f59cecce9f /build/parser.cxx
parentd4a6fb02ab5741aa41251653f0be3feb4594e553 (diff)
Implement new default target logic, canonical directory name (empty value)
The logic is as follows: if we have an explicit current directory target, then that's the default target. Otherwise, we take the first target and use it as a prerequisite to create an implicit current directory target, effectively making it the default target via an alias. If there are no targets in this buildfile, then we don't do anything.
Diffstat (limited to 'build/parser.cxx')
-rw-r--r--build/parser.cxx274
1 files changed, 139 insertions, 135 deletions
diff --git a/build/parser.cxx b/build/parser.cxx
index 20918bf..d5b07b1 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -36,20 +36,82 @@ namespace build
// Given a target or prerequisite name, figure out its type, taking
// into account extensions, trailing '/', or anything else that might
- // be relevant.
+ // be relevant. Also process the name (in place) by extracting the
+ // extension, adjusting dir/value, etc.
//
- static const char*
- find_target_type (const string& n, const string* e)
+ const target_type&
+ find_target_type (name& n, const location& l, const string*& ext)
{
- // Empty name or a name ending with a directory separator
- // signifies a directory.
+ string& v (n.value);
+
+ // First determine the target type.
//
- if (n.empty () || path::traits::is_separator (n.back ()))
- return "dir";
+ const char* tt;
+ if (n.type.empty ())
+ {
+ // Empty name, '.' and '..', or a name ending with a directory
+ // separator signifies a directory.
+ //
+ if (v.empty () || v == "." || v == ".." ||
+ path::traits::is_separator (v.back ()))
+ 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);
- //@@ TODO: derive type from extension.
+ ext = nullptr;
+
+ // Directories require special name processing. If we find that more
+ // targets deviate, then we should make this target-type-specific.
//
- return "file";
+ 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::
@@ -61,6 +123,7 @@ namespace build
lexer l (is, p.string ());
lexer_ = &l;
scope_ = &s;
+ default_target_ = nullptr;
token t (type::eos, false, 0, 0);
type tt;
@@ -70,6 +133,8 @@ namespace build
if (tt != type::eos)
fail (t) << "unexpected " << t;
+
+ process_default_target (t);
}
void parser::
@@ -118,6 +183,7 @@ namespace build
// ': foo' is equvalent to '{}: foo' and to 'dir{}: foo'.
//
+ location nloc (get_location (t, &path_));
names_type ns (tt != type::colon
? names (t, tt)
: names_type ({name ("")}));
@@ -215,6 +281,7 @@ namespace build
tt == type::newline ||
tt == type::eos)
{
+ location ploc (get_location (t, &path_));
names_type pns (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
@@ -226,156 +293,46 @@ namespace build
for (auto& pn: pns)
{
- // We need to 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 d (pn.dir);
- string n;
- const string* e (nullptr);
-
- {
- string& v (pn.value);
-
- path::size_type i (path::traits::rfind_separator (v));
-
- if (i == string::npos)
- n = move (v); // NOTE: steal!
- else
- {
- d /= path (v, i != 0 ? i : 1); // Special case: "/".
- n.assign (v, i + 1, string::npos);
- }
-
- // Handle '.' and '..'.
- //
- if (n == ".")
- n.clear ();
- else if (n == "..")
- {
- d /= path (n);
- n.clear ();
- }
-
- d.normalize ();
-
- // Extract extension.
- //
- string::size_type j (path::traits::find_extension (n));
-
- if (j != string::npos)
- {
- e = &extension_pool.find (n.c_str () + j);
- n.resize (j - 1);
- }
- }
-
- // Resolve prerequisite type.
- //
- const char* tt (pn.type.empty ()
- ? find_target_type (n, e)
- : pn.type.c_str ());
-
- auto i (target_types.find (tt));
+ const string* e;
+ const target_type& ti (find_target_type (pn, ploc, e));
- if (i == target_types.end ())
- {
- //@@ TODO name (or better yet, type) location
-
- fail (t) << "unknown prerequisite type " << tt;
- }
-
- const target_type& ti (i->second);
+ pn.dir.normalize ();
// Find or insert.
//
prerequisite& p (
scope_->prerequisites.insert (
- ti, move (d), move (n), e, *scope_, trace).first);
+ ti, move (pn.dir), move (pn.value), e, *scope_, trace).first);
ps.push_back (p);
}
for (auto& tn: ns)
{
- path d (tn.dir);
- string n;
- const string* e (nullptr);
-
- // The same deal as in handling prerequisites above.
- //
- {
- string& v (tn.value);
-
- path::size_type i (path::traits::rfind_separator (v));
-
- if (i == string::npos)
- n = move (v); // NOTE: steal!
- else
- {
- d /= path (v, i != 0 ? i : 1); // Special case: "/".
- n.assign (v, i + 1, string::npos);
- }
-
- // Handle '.' and '..'.
- //
- if (n == ".")
- n.clear ();
- else if (n == "..")
- {
- d /= path (n);
- n.clear ();
- }
-
- if (d.empty ())
- d = scope_->path (); // Already normalized.
- else
- {
- if (d.relative ())
- d = scope_->path () / d;
-
- d.normalize ();
- }
-
- // Extract extension.
- //
- string::size_type j (path::traits::find_extension (n));
-
- if (j != string::npos)
- {
- e = &extension_pool.find (n.c_str () + j);
- n.resize (j - 1);
- }
- }
-
- // Resolve target type.
- //
- const char* tt (tn.type.empty ()
- ? find_target_type (n, e)
- : tn.type.c_str ());
+ const string* e;
+ const target_type& ti (find_target_type (tn, nloc, e));
+ path& d (tn.dir);
- auto i (target_types.find (tt));
-
- if (i == target_types.end ())
+ if (d.empty ())
+ d = scope_->path (); // Already normalized.
+ else
{
- //@@ TODO name (or better yet, type) location
+ if (d.relative ())
+ d = scope_->path () / d;
- fail (t) << "unknown target type " << tt;
+ d.normalize ();
}
- const target_type& ti (i->second);
-
// Find or insert.
//
target& t (
targets.insert (
- ti, move (d), move (n), e, trace).first);
+ ti, move (tn.dir), move (tn.value), e, trace).first);
t.prerequisites = ps; //@@ OPT: move if last target.
- if (default_target == nullptr)
- default_target = &t;
+ if (default_target_ == nullptr)
+ default_target_ = &t;
}
if (tt == type::newline)
@@ -585,14 +542,20 @@ namespace build
scope* os (scope_);
scope_ = &scopes[(in_out ? p : out_src (p)).directory ()];
+ target* odt (default_target_);
+ default_target_ = nullptr;
+
next (t, tt);
clause (t, tt);
if (tt != type::eos)
fail (t) << "unexpected " << t;
+ process_default_target (t);
+
level4 ([&]{trace (t) << "leaving " << p;});
+ default_target_ = odt;
scope_ = os;
lexer_ = ol;
path_ = op;
@@ -864,6 +827,47 @@ namespace build
}
}
+ void parser::
+ process_default_target (token& t)
+ {
+ tracer trace ("parser::process_default_target", &path_);
+
+ // The logic is as follows: if we have an explicit current directory
+ // target, then that's the default target. Otherwise, we take the
+ // first target and use it as a prerequisite to create an implicit
+ // current directory target, effectively making it the default
+ // target via an alias. If there are no targets in this buildfile,
+ // 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.
+ scope_->path (),
+ "",
+ nullptr,
+ trace) != targets.end ())
+ return;
+
+ target& dt (*default_target_);
+
+ level4 ([&]{trace (t) << "creating current directory alias for " << dt;});
+
+ target& ct (
+ targets.insert (
+ dir::static_type, scope_->path (), "", nullptr, trace).first);
+
+ prerequisite& p (
+ scope_->prerequisites.insert (
+ dt.type (),
+ dt.dir,
+ dt.name,
+ dt.ext,
+ *scope_, // Doesn't matter which scope since dir is absolute.
+ trace).first);
+
+ p.target = &dt;
+ ct.prerequisites.push_back (p);
+ }
+
token_type parser::
next (token& t, token_type& tt)
{