aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/algorithm.cxx27
-rw-r--r--build/buildfile4
-rw-r--r--build/context.cxx4
-rw-r--r--build/cxx/rule.cxx27
-rw-r--r--build/parser.cxx84
-rw-r--r--build/path5
-rw-r--r--build/path.txx28
-rw-r--r--build/prerequisite11
-rw-r--r--build/prerequisite.cxx18
-rw-r--r--build/target25
-rw-r--r--build/target.cxx7
-rw-r--r--build/trace13
-rw-r--r--build/utility13
-rw-r--r--build/utility.cxx12
14 files changed, 244 insertions, 34 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index 419bfaf..1d7f215 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -45,13 +45,34 @@ namespace build
//
auto r (
targets.emplace (
- unique_ptr<target> (p.type.factory (p.name, move (d)))));
+ unique_ptr<target> (p.type.factory (move (d), p.name, p.ext))));
+
+ target& t (**r.first);
trace (4, [&]{
- tr << (r.second ? "new" : "existing") << " target " << **r.first
+ tr << (r.second ? "new" : "existing") << " target " << t
<< " for prerequsite " << p;});
- return (p.target = r.first->get ());
+ // Update extension if the existing target has it unspecified.
+ //
+ if (t.ext != p.ext)
+ {
+ trace (4, [&]{
+ tracer::record r (tr);
+ r << "assuming target " << t << " is the same as the one with ";
+ if (p.ext == nullptr)
+ r << "unspecified extension";
+ else if (p.ext->empty ())
+ r << "no extension";
+ else
+ r << "extension " << *p.ext;
+ });
+
+ if (p.ext != nullptr)
+ t.ext = p.ext;
+ }
+
+ return (p.target = &t);
}
bool
diff --git a/build/buildfile b/build/buildfile
index 3afe19b..73f2e91 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -1,5 +1,6 @@
exe{b1}: obj{b algorithm scope parser lexer trace target prerequisite rule \
- native context diagnostics cxx/target cxx/rule process timestamp path}
+ native context diagnostics cxx/target cxx/rule process timestamp path \
+ utility}
obj{b}: cxx{b}
obj{algorithm}: cxx{algorithm}
@@ -18,3 +19,4 @@ obj{cxx/rule}: cxx{cxx/rule}
obj{process}: cxx{process}
obj{timestamp}: cxx{timestamp}
obj{path}: cxx{path}
+obj{utility}: cxx{utility}
diff --git a/build/context.cxx b/build/context.cxx
index fc4ec1c..2eb76c0 100644
--- a/build/context.cxx
+++ b/build/context.cxx
@@ -2,10 +2,10 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
-#include <ostream>
-
#include <build/context>
+#include <ostream>
+
using namespace std;
namespace build
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index 1d2a2c4..c6ceb1a 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -211,19 +211,40 @@ namespace build
// @@ TODO:
//
- // Split the name into its directory part and the name part.
- // Here we assume the name part is a valid filesystem name.
+ // Split the name into its directory part, the name part, and
+ // extension. Here we can assume the name part is a valid
+ // filesystem name.
//
path d (file.directory ());
string n (file.leaf ().base ().string ());
+ const char* es (file.extension ());
+ const string* e (&extension_pool.find (es != nullptr ? es : ""));
// Find or insert.
//
auto r (ds.prerequisites.emplace (
- hxx::static_type, move (n), move (d), ds));
+ hxx::static_type, move (d), move (n), e, ds));
auto& p (const_cast<prerequisite&> (*r.first));
+ // Update extension if the existing prerequisite has it
+ // unspecified.
+ //
+ if (p.ext != e)
+ {
+ trace (4, [&]{
+ tracer::record r (tr);
+ r << "assuming prerequisite " << p << " is the same as the "
+ << "one with ";
+ if (e->empty ())
+ r << "no extension";
+ else
+ r << "extension " << *e;
+ });
+
+ p.ext = e;
+ }
+
// Resolve to target so that we can assign its path.
//
path_target& t (
diff --git a/build/parser.cxx b/build/parser.cxx
index 289e571..77571fb 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -13,6 +13,7 @@
#include <build/scope>
#include <build/target>
#include <build/prerequisite>
+#include <build/diagnostics>
using namespace std;
@@ -49,6 +50,8 @@ namespace build
void parser::
parse_clause (token& t, token_type& tt)
{
+ tracer tr ("parser::parse_clause");
+
while (tt != type::eos)
{
// We always start with one or more names.
@@ -93,13 +96,14 @@ namespace build
const target_type& ti (i->second);
- // We need to split the name into its directory part (if any)
- // and the name part. We cannot assume the name part is a
- // valid filesystem name so we will have to do the splitting
- // manually.
+ // 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;
string n;
+ const string* e (nullptr);
{
path::size_type i (path::traits::rfind_separator (pn.name));
@@ -112,6 +116,16 @@ namespace build
n.assign (pn.name, i + 1, string::npos);
d.normalize ();
}
+
+ // Extract extension.
+ //
+ string::size_type j (n.rfind ('.'));
+
+ if (j != string::npos)
+ {
+ e = &extension_pool.find (n.c_str () + j + 1);
+ n.resize (j);
+ }
}
//cout << "prerequisite " << tt << " " << n << " " << d << endl;
@@ -119,15 +133,39 @@ namespace build
// Find or insert.
//
auto r (scope_->prerequisites.emplace (
- ti, move (n), move (d), *scope_));
+ ti, move (d), move (n), e, *scope_));
+
+ auto& p (const_cast<prerequisite&> (*r.first));
+
+ // Update extension if the existing prerequisite has it
+ // unspecified.
+ //
+ if (p.ext != e)
+ {
+ trace (4, [&]{
+ tracer::record r (tr);
+ r << "assuming prerequisite " << p << " is the same as the "
+ << "one with ";
+ if (e == nullptr)
+ r << "unspecified extension";
+ else if (e->empty ())
+ r << "no extension";
+ else
+ r << "extension " << *e;
+ });
+
+ if (e != nullptr)
+ p.ext = e;
+ }
- ps.push_back (const_cast<prerequisite&> (*r.first));
+ ps.push_back (p);
}
for (auto& tn: tns)
{
path d;
string n;
+ const string* e (nullptr);
// The same deal as in handling prerequisites above.
//
@@ -149,6 +187,16 @@ namespace build
d.normalize ();
}
+
+ // Extract extension.
+ //
+ string::size_type j (n.rfind ('.'));
+
+ if (j != string::npos)
+ {
+ e = &extension_pool.find (n.c_str () + j + 1);
+ n.resize (j);
+ }
}
// Resolve target type.
@@ -178,11 +226,31 @@ namespace build
//
auto r (
targets.emplace (
- unique_ptr<target> (ti.factory (move (n), move (d)))));
+ unique_ptr<target> (ti.factory (move (d), move (n), e))));
target& t (**r.first);
- t.prerequisites = ps; //@@ TODO: move is last target.
+ // Update extension if the existing target has it unspecified.
+ //
+ if (t.ext != e)
+ {
+ trace (4, [&]{
+ tracer::record r (tr);
+ r << "assuming target " << t << " is the same as the "
+ << "one with ";
+ if (e == nullptr)
+ r << "unspecified extension";
+ else if (e->empty ())
+ r << "no extension";
+ else
+ r << "extension " << *e;
+ });
+
+ if (e != nullptr)
+ t.ext = e;
+ }
+
+ t.prerequisites = ps; //@@ TODO: move if last target.
if (default_target == nullptr)
default_target = &t;
diff --git a/build/path b/build/path
index 92832a1..dbd048d 100644
--- a/build/path
+++ b/build/path
@@ -259,6 +259,11 @@ namespace build
basic_path
base () const;
+ // Return the extension or NULL if not present.
+ //
+ const C*
+ extension () const;
+
// Return a path relative to the specified path that is equivalent
// to *this. Throws invalid_path if a relative path cannot be derived
// (e.g., paths are on different drives on Windows).
diff --git a/build/path.txx b/build/path.txx
index 3e952d8..f576870 100644
--- a/build/path.txx
+++ b/build/path.txx
@@ -62,6 +62,34 @@ namespace build
return *this;
}
+ template <typename C>
+ const C* basic_path<C>::
+ extension () const
+ {
+ size_type i (path_.size ());
+
+ for (; i > 0; --i)
+ {
+ if (path_[i - 1] == '.')
+ break;
+
+ if (traits::is_separator (path_[i - 1]))
+ {
+ i = 0;
+ break;
+ }
+ }
+
+ // Weed out paths like ".txt" and "/.txt"
+ //
+ if (i > 1 && !traits::is_separator (path_[i - 2]))
+ {
+ return path_.c_str () + i;
+ }
+ else
+ return nullptr;
+ }
+
#ifdef _WIN32
template <typename C>
typename basic_path<C>::string_type basic_path<C>::
diff --git a/build/prerequisite b/build/prerequisite
index e58532d..6c9c171 100644
--- a/build/prerequisite
+++ b/build/prerequisite
@@ -12,6 +12,7 @@
#include <typeindex>
#include <build/path>
+#include <build/utility> // extension_pool
namespace build
{
@@ -27,18 +28,20 @@ namespace build
typedef build::scope scope_type;
prerequisite (const target_type_type& t,
- std::string n,
path d,
+ std::string n,
+ const std::string* e,
scope_type& s)
- : type (t), name (std::move (n)), directory (std::move (d)),
+ : type (t), directory (std::move (d)), name (std::move (n)), ext (e),
scope (s), target (0) {}
public:
const target_type_type& type;
+ const path directory; // Normalized absolute or relative (to scope).
const std::string name;
- const path directory; // Normalized absolute or relative (to scope).
+ const std::string* ext; // NULL if unspecified.
scope_type& scope;
- target_type* target; // NULL if not yet resolved.
+ target_type* target; // NULL if not yet resolved.
};
std::ostream&
diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx
index c43827d..370f5d0 100644
--- a/build/prerequisite.cxx
+++ b/build/prerequisite.cxx
@@ -43,7 +43,12 @@ namespace build
os << s << path::traits::directory_separator;
}
- os << p.name << '}';
+ os << p.name;
+
+ if (p.ext != nullptr)
+ os << '.' << *p.ext;
+
+ os << '}';
}
return os;
@@ -52,10 +57,19 @@ namespace build
bool
operator< (const prerequisite& x, const prerequisite& y)
{
+ //@@ TODO: use compare() to compare once.
+
+ // Unspecified and specified extension are assumed equal. The
+ // extension strings are from the pool, so we can just compare
+ // pointers.
+ //
return
(x.type.id < y.type.id) ||
(x.type.id == y.type.id && x.name < y.name) ||
(x.type.id == y.type.id && x.name == y.name &&
- x.directory < y.directory);
+ x.directory < y.directory) ||
+ (x.type.id == y.type.id && x.name == y.name &&
+ x.directory == y.directory &&
+ x.ext != nullptr && y.ext != nullptr && x.ext < y.ext);
}
}
diff --git a/build/target b/build/target
index a381fd0..508aedb 100644
--- a/build/target
+++ b/build/target
@@ -19,7 +19,7 @@
#include <build/path>
#include <build/timestamp>
#include <build/prerequisite>
-#include <build/utility> // compare_c_string, compare_pointer_target
+#include <build/utility> // compare_*, extension_pool
namespace build
{
@@ -33,17 +33,18 @@ namespace build
std::type_index id;
const char* name;
const target_type* base;
- target* (*const factory) (std::string, path);
+ target* (*const factory) (path, std::string, const std::string*);
};
class target
{
public:
- target (std::string n, path d)
- : name (std::move (n)), directory (std::move (d)) {}
+ target (path d, std::string n, const std::string* e)
+ : directory (std::move (d)), name (std::move (n)), ext (e) {}
+ const path directory; // Absolute and normalized.
const std::string name;
- const path directory; // Absolute and normalized.
+ const std::string* ext; // Extension, NULL means unspecified.
public:
typedef
@@ -89,10 +90,18 @@ namespace build
{
std::type_index tx (typeid (x)), ty (typeid (y));
+ //@@ TODO: use compare() to compare once.
+
+ // Unspecified and specified extension are assumed equal. The
+ // extension strings are from the pool, so we can just compare
+ // pointers.
+ //
return
(tx < ty) ||
(tx == ty && x.name < y.name) ||
- (tx == ty && x.name == y.name && x.directory < y.directory);
+ (tx == ty && x.name == y.name && x.directory < y.directory) ||
+ (tx == ty && x.name == y.name && x.directory == y.directory &&
+ x.ext != nullptr && y.ext != nullptr && x.ext < y.ext);
}
typedef std::set<std::unique_ptr<target>, compare_pointer_target> target_set;
@@ -113,9 +122,9 @@ namespace build
template <typename T>
target*
- target_factory (std::string n, path d)
+ target_factory (path d, std::string n, const std::string* e)
{
- return new T (std::move (n), std::move (d));
+ return new T (std::move (d), std::move (n), e);
}
// Modification time-based target.
diff --git a/build/target.cxx b/build/target.cxx
index 4dffe7f..61423db 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -27,7 +27,12 @@ namespace build
os << s << path::traits::directory_separator;
}
- os << t.name << '}';
+ os << t.name;
+
+ if (t.ext != nullptr)
+ os << '.' << *t.ext;
+
+ os << '}';
return os;
}
diff --git a/build/trace b/build/trace
index f1c0567..1764a69 100644
--- a/build/trace
+++ b/build/trace
@@ -34,9 +34,11 @@ namespace build
return std::cerr << x;
}
+ explicit record (tracer& t): empty_ (false) {t.begin ();}
+ explicit record (bool e = true): empty_ (e) {}
+
// Movable-only type.
//
- explicit record (bool e = true): empty_ (e) {}
record (record&& r) {empty_ = r.empty_; r.empty_ = true;}
record& operator= (record&& r) {empty_ = r.empty_; r.empty_ = true;}
@@ -51,10 +53,17 @@ namespace build
record
operator<< (const T& x) const
{
- std::cerr << "trace: " << name_ << ": " << x;
+ begin ();
+ std::cerr << x;
return record (false);
}
+ void
+ begin () const
+ {
+ std::cerr << "trace: " << name_ << ": ";
+ }
+
private:
const char* name_;
};
diff --git a/build/utility b/build/utility
index bef6335..bcbf834 100644
--- a/build/utility
+++ b/build/utility
@@ -5,8 +5,11 @@
#ifndef BUILD_UTILITY
#define BUILD_UTILITY
+#include <string>
+#include <unordered_set>
#include <cstring> // strcmp
+
namespace build
{
struct compare_c_string
@@ -22,6 +25,16 @@ namespace build
template <typename P>
bool operator() (const P& x, const P& y) const {return *x < *y;}
};
+
+ // Pools (@@ perhaps move into a separate header).
+ //
+ struct string_pool: std::unordered_set<std::string>
+ {
+ const std::string&
+ find (const char* s) {return *emplace (s).first;}
+ };
+
+ extern string_pool extension_pool;
}
#endif // BUILD_UTILITY
diff --git a/build/utility.cxx b/build/utility.cxx
new file mode 100644
index 0000000..f3fd51b
--- /dev/null
+++ b/build/utility.cxx
@@ -0,0 +1,12 @@
+// file : build/utility.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/utility>
+
+using namespace std;
+
+namespace build
+{
+ string_pool extension_pool;
+}