aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--build/b.cxx13
-rw-r--r--build/parser8
-rw-r--r--build/parser.cxx274
-rw-r--r--build/search.cxx1
-rw-r--r--build/target6
-rw-r--r--build/target.cxx1
6 files changed, 158 insertions, 145 deletions
diff --git a/build/b.cxx b/build/b.cxx
index 659eb40..edefbe9 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -205,20 +205,21 @@ main (int argc, char* argv[])
// Build.
//
- if (default_target == nullptr)
- fail << "no default target";
+ auto i (targets.find (dir::static_type.id, out_base, "", nullptr, trace));
+ if (i == targets.end ())
+ fail << "no targets in " << bf;
- target& d (*default_target);
+ target& t (**i);
- match (d);
+ match (t);
dump ();
- switch (update (d))
+ switch (update (t))
{
case target_state::uptodate:
{
- info << "target " << d << " is up to date";
+ info << "target " << t << " is up to date";
break;
}
case target_state::updated:
diff --git a/build/parser b/build/parser
index 2e399c9..e5aeb60 100644
--- a/build/parser
+++ b/build/parser
@@ -17,6 +17,7 @@
namespace build
{
class scope;
+ class target;
class lexer;
class parser
@@ -61,6 +62,12 @@ namespace build
// Utilities.
//
private:
+ void
+ process_default_target (token&);
+
+ // Lexer.
+ //
+ private:
token_type
next (token&, token_type&);
@@ -83,6 +90,7 @@ namespace build
const std::string* path_; // Path processed by diagnostic_string().
lexer* lexer_;
scope* scope_;
+ target* default_target_;
token peek_ {token_type::eos, false, 0, 0};
bool peeked_ {false};
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)
{
diff --git a/build/search.cxx b/build/search.cxx
index 966f5f1..e80f7e7 100644
--- a/build/search.cxx
+++ b/build/search.cxx
@@ -67,6 +67,7 @@ namespace build
for (const path& d: sp)
{
path f (d / p.dir / path (p.name));
+ f.normalize ();
// @@ TMP: use target name as an extension.
//
diff --git a/build/target b/build/target
index 0f5de4f..31a590d 100644
--- a/build/target
+++ b/build/target
@@ -135,10 +135,11 @@ namespace build
find (const std::type_index& type,
const path& dir,
const std::string& name,
- const std::string*& ext,
+ const std::string* ext,
tracer& trace) const
{
- return find (key {&type, &dir, &name, &ext}, trace);
+ const std::string* e (ext);
+ return find (key {&type, &dir, &name, &e}, trace);
}
iterator begin () const {return map_.begin ();}
@@ -156,7 +157,6 @@ namespace build
};
extern target_set targets;
- extern target* default_target;
class target_type_map: public std::map<
const char*,
diff --git a/build/target.cxx b/build/target.cxx
index 4168991..136b912 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -113,7 +113,6 @@ namespace build
}
target_set targets;
- target* default_target = nullptr;
target_type_map target_types;
// path_target