aboutsummaryrefslogtreecommitdiff
path: root/build
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
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')
-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