aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-15 11:59:58 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-15 11:59:58 +0200
commitad720fabd468974e3909f62a0f4e4e3cf0d03aef (patch)
tree8c6b7d851e42a42118b28488a9a3def8e86cd849
parentace1743f7f78bb13f99553d6e97ad1beecf1ba99 (diff)
Initial library support
-rw-r--r--build/b.cxx7
-rw-r--r--build/cxx/rule.cxx102
-rw-r--r--build/native12
-rw-r--r--build/native.cxx17
-rw-r--r--build/rule.cxx21
-rw-r--r--build/scope9
-rw-r--r--build/scope.cxx7
-rw-r--r--build/target37
-rw-r--r--build/target.cxx37
-rw-r--r--build/variable16
-rw-r--r--build/variable.ixx11
11 files changed, 200 insertions, 76 deletions
diff --git a/build/b.cxx b/build/b.cxx
index e53844c..3fe9d20 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -139,8 +139,9 @@ main (int argc, char* argv[])
target_types.insert (dir::static_type);
target_types.insert (fsdir::static_type);
- target_types.insert (exe::static_type);
target_types.insert (obj::static_type);
+ target_types.insert (exe::static_type);
+ target_types.insert (lib::static_type);
target_types.insert (cxx::h::static_type);
target_types.insert (cxx::c::static_type);
@@ -157,6 +158,10 @@ main (int argc, char* argv[])
rules[update_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
rules[clean_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
+ rules[default_id][typeid (lib)].emplace ("cxx.gnu.link", cxx_link);
+ rules[update_id][typeid (lib)].emplace ("cxx.gnu.link", cxx_link);
+ rules[clean_id][typeid (lib)].emplace ("cxx.gnu.link", cxx_link);
+
cxx::compile cxx_compile;
rules[default_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
rules[update_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index d8a7190..a7ec2a6 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -26,12 +26,15 @@ namespace build
{
namespace cxx
{
+ // T is either target or scope.
+ //
+ template <typename T>
static void
- append_options (vector<const char*>& args, scope& s, const char* var)
+ append_options (vector<const char*>& args, T& s, const char* var)
{
if (auto val = s[var])
{
- for (const name& n: val.as<const list_value&> ())
+ for (const name& n: val.template as<const list_value&> ())
{
if (!n.type.empty () || !n.dir.empty ())
fail << "expected option instead of " << n <<
@@ -43,9 +46,9 @@ namespace build
}
static void
- append_std (vector<const char*>& args, scope& s, string& opt)
+ append_std (vector<const char*>& args, target& t, string& opt)
{
- if (auto val = s["cxx.std"])
+ if (auto val = t["cxx.std"])
{
const string& v (val.as<const string&> ());
@@ -95,7 +98,7 @@ namespace build
obj& o (dynamic_cast<obj&> (t));
if (o.path ().empty ())
- o.path (o.dir / path (o.name + ".o"));
+ o.path (o.derived_path ("o"));
// Search and match all the existing prerequisites. The injection
// code (below) takes care of the ones it is adding.
@@ -182,14 +185,13 @@ namespace build
{
tracer trace ("cxx::compile::inject_prerequisites");
- scope& ts (o.base_scope ());
- scope& rs (*ts.root_scope ()); // Shouldn't have matched if nullptr.
+ scope& rs (*o.root_scope ()); // Shouldn't have matched if nullptr.
const string& cxx (rs["config.cxx"].as<const string&> ());
vector<const char*> args {cxx.c_str ()};
append_options (args, rs, "config.cxx.poptions");
- append_options (args, ts, "cxx.poptions");
+ append_options (args, o, "cxx.poptions");
// @@ Some C++ options (e.g., -std, -m) affect the preprocessor.
// Or maybe they are not C++ options? Common options?
@@ -197,9 +199,9 @@ namespace build
append_options (args, rs, "config.cxx.coptions");
string std; // Storage.
- append_std (args, ts, std);
+ append_std (args, o, std);
- append_options (args, ts, "cxx.coptions");
+ append_options (args, o, "cxx.coptions");
args.push_back ("-MM"); // @@ Change to -M
args.push_back ("-MG"); // Treat missing headers as generated.
@@ -268,7 +270,7 @@ namespace build
//
// Note that if the file has no extension, we record an empty
// extension rather than NULL (which would signify that the
- // extension needs to be added).
+ // default extension needs to be added).
//
dir_path d (f.directory ());
string n (f.leaf ().base ().string ());
@@ -337,21 +339,20 @@ namespace build
path relo (relative (o.path ()));
path rels (relative (s->path ()));
- scope& ts (o.base_scope ());
- scope& rs (*ts.root_scope ()); // Shouldn't have matched if nullptr.
+ scope& rs (*o.root_scope ()); // Shouldn't have matched if nullptr.
const string& cxx (rs["config.cxx"].as<const string&> ());
vector<const char*> args {cxx.c_str ()};
append_options (args, rs, "config.cxx.poptions");
- append_options (args, ts, "cxx.poptions");
+ append_options (args, o, "cxx.poptions");
append_options (args, rs, "config.cxx.coptions");
string std; // Storage.
- append_std (args, ts, std);
+ append_std (args, o, std);
- append_options (args, ts, "cxx.coptions");
+ append_options (args, o, "cxx.coptions");
args.push_back ("-o");
args.push_back (relo.string ().c_str ());
@@ -461,12 +462,23 @@ namespace build
{
tracer trace ("cxx::link::apply");
- // Derive executable file name from target name.
+ // Derive file name from target name.
//
- exe& e (dynamic_cast<exe&> (t));
+ bool so (false);
+ if (exe* e = dynamic_cast<exe*> (&t))
+ {
+ if (e->path ().empty ())
+ e->path (e->derived_path ());
+ }
+ else if (lib* l = dynamic_cast<lib*> (&t))
+ {
+ if (l->path ().empty ())
+ l->path (l->derived_path ("so", "lib"));
- if (e.path ().empty ())
- e.path (e.dir / path (e.name));
+ so = true;
+ }
+ else
+ assert (false);
// We may need the project roots for rule chaining (see below).
// We will resolve them lazily only if needed.
@@ -487,7 +499,7 @@ namespace build
//
target& pt (search (p));
- if (a.operation () == clean_id && !pt.dir.sub (e.dir))
+ if (a.operation () == clean_id && !pt.dir.sub (t.dir))
p.target = nullptr; // Ignore.
else
build::match (a, pt);
@@ -501,7 +513,7 @@ namespace build
// but possible, the prerequisite is from a different project
// altogether. So we are going to use the target's project.
//
- scope* rs (e.root_scope ());
+ scope* rs (t.root_scope ());
assert (rs != nullptr); // Shouldn't have matched.
out_root = &rs->path ();
src_root = &rs->src_path ();
@@ -550,7 +562,7 @@ namespace build
// update will come and finish the rewrite process (it will even
// reuse op that we have created but then ignored). So all is good.
//
- if (a.operation () == clean_id && !ot.dir.sub (e.dir))
+ if (a.operation () == clean_id && !ot.dir.sub (t.dir))
{
// If we shouldn't clean obj{}, then it is fair to assume
// we shouldn't clean cxx{} either (generated source will
@@ -561,6 +573,21 @@ namespace build
continue;
}
+ // Set the -fPIC option if we are building a shared object.
+ //
+ if (so)
+ {
+ auto var (ot.variables["cxx.coptions"]);
+
+ if (!var)
+ {
+ if (auto var1 = ot.base_scope ()["cxx.coptions"])
+ var = var1;
+ }
+
+ var += "-fPIC";
+ }
+
// If this target already exists, then it needs to be "compatible"
// with what we are doing here.
//
@@ -633,42 +660,45 @@ namespace build
}
target_state link::
- perform_update (action a, target& t)
+ perform_update (action a, target& xt)
{
// @@ Q:
//
// - what are we doing with libraries?
//
+ path_target& t (static_cast<path_target&> (xt));
- exe& e (dynamic_cast<exe&> (t));
+ //exe& e (dynamic_cast<exe&> (t));
- if (!execute_prerequisites (a, e, e.mtime ()))
+ if (!execute_prerequisites (a, t, t.mtime ()))
return target_state::unchanged;
// Translate paths to relative (to working directory) ones. This
// results in easier to read diagnostics.
//
- path rele (relative (e.path ()));
+ path relt (relative (t.path ()));
vector<path> relo;
- scope& ts (e.base_scope ());
- scope& rs (*ts.root_scope ()); // Shouldn't have matched if nullptr.
+ scope& rs (*t.root_scope ()); // Shouldn't have matched if nullptr.
const string& cxx (rs["config.cxx"].as<const string&> ());
vector<const char*> args {cxx.c_str ()};
+ if (dynamic_cast<lib*> (&t) != nullptr)
+ args.push_back ("-shared");
+
append_options (args, rs, "config.cxx.coptions");
string std; // Storage.
- append_std (args, ts, std);
+ append_std (args, t, std);
- append_options (args, ts, "cxx.coptions");
+ append_options (args, t, "cxx.coptions");
args.push_back ("-o");
- args.push_back (rele.string ().c_str ());
+ args.push_back (relt.string ().c_str ());
append_options (args, rs, "config.cxx.loptions");
- append_options (args, ts, "cxx.loptions");
+ append_options (args, t, "cxx.loptions");
for (const prerequisite& p: t.prerequisites)
{
@@ -680,14 +710,14 @@ namespace build
}
append_options (args, rs, "config.cxx.libs");
- append_options (args, ts, "cxx.libs");
+ append_options (args, t, "cxx.libs");
args.push_back (nullptr);
if (verb >= 1)
print_process (args);
else
- text << "ld " << e;
+ text << "ld " << t;
try
{
@@ -701,7 +731,7 @@ namespace build
// current clock time. It has the advantage of having the
// subseconds precision.
//
- e.mtime (system_clock::now ());
+ t.mtime (system_clock::now ());
return target_state::changed;
}
catch (const process_error& e)
diff --git a/build/native b/build/native
index 6107705..88f340d 100644
--- a/build/native
+++ b/build/native
@@ -9,6 +9,16 @@
namespace build
{
+ class obj: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
class exe: public file
{
public:
@@ -19,7 +29,7 @@ namespace build
static const target_type static_type;
};
- class obj: public file
+ class lib: public file
{
public:
using file::file;
diff --git a/build/native.cxx b/build/native.cxx
index 8d58e66..d8b0149 100644
--- a/build/native.cxx
+++ b/build/native.cxx
@@ -8,6 +8,15 @@ using namespace std;
namespace build
{
+ const target_type obj::static_type
+ {
+ typeid (obj),
+ "obj",
+ &file::static_type,
+ &target_factory<obj>,
+ file::static_type.search
+ };
+
const target_type exe::static_type
{
typeid (exe),
@@ -17,12 +26,12 @@ namespace build
file::static_type.search
};
- const target_type obj::static_type
+ const target_type lib::static_type
{
- typeid (obj),
- "obj",
+ typeid (lib),
+ "lib",
&file::static_type,
- &target_factory<obj>,
+ &target_factory<lib>,
file::static_type.search
};
}
diff --git a/build/rule.cxx b/build/rule.cxx
index 413676b..884b28d 100644
--- a/build/rule.cxx
+++ b/build/rule.cxx
@@ -55,25 +55,14 @@ namespace build
//
path_target& pt (dynamic_cast<path_target&> (t));
+ // Assign the path. While nromally we shouldn't do this in match(),
+ // no other rule should ever be ambiguous with the fallback one.
+ //
if (pt.path ().empty ())
{
- path p (t.dir / path (pt.name));
-
- // @@ TMP: target name as an extension.
- //
- const string& e (pt.ext != nullptr ? *pt.ext : pt.type ().name);
-
- if (!e.empty ())
- {
- p += '.';
- p += e;
- }
-
- // While strictly speaking we shouldn't do this in match(),
- // no other rule should ever be ambiguous with the fallback
- // one.
+ // @@ TMP: using target name as the default extension.
//
- pt.path (move (p));
+ pt.path (pt.derived_path (pt.type ().name));
}
return pt.mtime () != timestamp_nonexistent ? &t : nullptr;
diff --git a/build/scope b/build/scope
index f9492e6..e4a8a7d 100644
--- a/build/scope
+++ b/build/scope
@@ -46,10 +46,13 @@ namespace build
//
public:
value_proxy
- operator[] (const std::string&);
+ operator[] (const variable&);
value_proxy
- operator[] (const variable&);
+ operator[] (const std::string& name)
+ {
+ return operator[] (variable_pool.find (name));
+ }
const dir_path* src_path_ {nullptr}; // Cached src_{root,base} var value.
@@ -92,7 +95,7 @@ namespace build
typedef dir_path_map<scope>::const_iterator iterator;
- scope (): variables (*this) {}
+ scope (): variables (this) {}
iterator i_;
scope* parent_;
diff --git a/build/scope.cxx b/build/scope.cxx
index aced21b..6fb32b1 100644
--- a/build/scope.cxx
+++ b/build/scope.cxx
@@ -11,13 +11,6 @@ namespace build
// scope
//
value_proxy scope::
- operator[] (const string& name)
- {
- const variable& var (variable_pool.find (name));
- return operator[] (var);
- }
-
- value_proxy scope::
operator[] (const variable& var)
{
for (scope* s (this); s != nullptr; s = s->parent_scope ())
diff --git a/build/target b/build/target
index dd62ec8..5495061 100644
--- a/build/target
+++ b/build/target
@@ -20,6 +20,7 @@
#include <build/map-key> // map_iterator_adapter
#include <build/timestamp>
#include <build/name>
+#include <build/variable>
#include <build/operation>
#include <build/prerequisite>
#include <build/utility> // compare_*, extension_pool
@@ -93,12 +94,13 @@ namespace build
~target () = default;
target (dir_path d, std::string n, const std::string* e)
- : dir (std::move (d)), name (std::move (n)), ext (e) {}
+ : dir (std::move (d)), name (std::move (n)), ext (e),
+ variables (nullptr) {}
const dir_path dir; // Absolute and normalized.
const std::string name;
- const std::string* ext; // Extension, NULL means unspecified.
-
+ const std::string* ext; // Extension, NULL means unspecified,
+ // empty means no extension.
public:
// Most qualified scope that contains this target.
//
@@ -112,6 +114,8 @@ namespace build
scope*
root_scope () const;
+ // Prerequisites.
+ //
public:
typedef
std::vector<std::reference_wrapper<prerequisite>>
@@ -119,6 +123,25 @@ namespace build
prerequisites_type prerequisites;
+ // Target-specific variables.
+ //
+ public:
+ variable_map variables;
+
+ const variable_map&
+ ro_variables () const {return variables;}
+
+ // Variable lookup in this target and all its outer scopes.
+ //
+ value_proxy
+ operator[] (const variable&);
+
+ value_proxy
+ operator[] (const std::string& name)
+ {
+ return operator[] (variable_pool.find (name));
+ }
+
public:
target_state state;
@@ -328,6 +351,14 @@ namespace build
void
path (path_type p) {assert (path_.empty ()); path_ = std::move (p);}
+ // Return a path derived from target's dir, name, and, if specified,
+ // ext. If ext is not specified, then use default_ext. If name_prefix
+ // if not NULL, add it before the name part.
+ //
+ path_type
+ derived_path (const char* default_ext = nullptr,
+ const char* name_prefix = nullptr);
+
protected:
virtual timestamp
load_mtime () const;
diff --git a/build/target.cxx b/build/target.cxx
index db02979..fbba1ed 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -51,6 +51,16 @@ namespace build
return scopes.find (dir).root_scope ();
}
+ value_proxy target::
+ operator[] (const variable& var)
+ {
+ auto i (variables.find (var));
+
+ return i != variables.end ()
+ ? value_proxy (&i->second, nullptr)
+ : base_scope ()[var];
+ }
+
ostream&
operator<< (ostream& os, const target& t)
{
@@ -232,6 +242,33 @@ namespace build
// path_target
//
+ path path_target::
+ derived_path (const char* de, const char* np)
+ {
+ string n;
+
+ if (np != nullptr)
+ n += np;
+
+ n += name;
+
+ if (ext != nullptr)
+ {
+ if (!ext->empty ())
+ {
+ n += '.';
+ n += *ext;
+ }
+ }
+ else if (de != nullptr)
+ {
+ n += '.';
+ n += de;
+ }
+
+ return dir / path_type (move (n));
+ }
+
timestamp path_target::
load_mtime () const
{
diff --git a/build/variable b/build/variable
index f994b58..6c14477 100644
--- a/build/variable
+++ b/build/variable
@@ -100,7 +100,7 @@ namespace build
explicit operator bool () const {return defined () && !null ();}
explicit operator value_ptr& () const {return *p;}
- scope_type* scope;
+ scope_type* scope; // If NULL, then this is a target variable.
// Get interface. See available specializations below.
//
@@ -119,6 +119,11 @@ namespace build
const value_proxy&
operator= (std::string) const;
+ // Append enother simple name to list_value.
+ //
+ const value_proxy&
+ operator+= (std::string) const;
+
const value_proxy&
operator= (dir_path) const;
@@ -218,7 +223,7 @@ namespace build
value_proxy
operator[] (const variable& v)
{
- return value_proxy (&base::operator[] (v), &scope_);
+ return value_proxy (&base::operator[] (v), scope_);
}
value_proxy
@@ -234,7 +239,7 @@ namespace build
return i != end ()
// @@ To do this properly we seem to need ro_value_proxy.
//
- ? value_proxy (&const_cast<value_ptr&> (i->second), &scope_)
+ ? value_proxy (&const_cast<value_ptr&> (i->second), scope_)
: value_proxy (nullptr, nullptr);
}
@@ -251,10 +256,11 @@ namespace build
}
explicit
- variable_map (scope& s): scope_ (s) {}
+ variable_map (scope* s): scope_ (s) {}
private:
- scope& scope_; // Scope to which this map belongs (and all its value).
+ scope* scope_; // Scope to which this map belongs or NULL if this
+ // is a target variable map.
};
}
diff --git a/build/variable.ixx b/build/variable.ixx
index 2d52d9f..c0059c5 100644
--- a/build/variable.ixx
+++ b/build/variable.ixx
@@ -37,6 +37,17 @@ namespace build
}
inline const value_proxy& value_proxy::
+ operator+= (std::string v) const
+ {
+ if (*p == nullptr)
+ *this = v;
+ else
+ as<list_value&> ().emplace_back (std::move (v));
+
+ return *this;
+ }
+
+ inline const value_proxy& value_proxy::
operator= (dir_path v) const
{
p->reset (new list_value (std::move (v)));