aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-24 12:29:20 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-24 12:29:20 +0200
commit2a0f9e035f673f1ee387924501a31990de37f18d (patch)
treeb8e55ab74bc88b788e99d8649219b931b80432d5 /build
parent4c44c914d898af53152addad5530504548175e85 (diff)
Implement lib/liba/libso{} target group, shared/static library build
Diffstat (limited to 'build')
-rw-r--r--build/algorithm29
-rw-r--r--build/algorithm.cxx115
-rw-r--r--build/algorithm.ixx10
-rw-r--r--build/algorithm.txx139
-rw-r--r--build/b.cxx69
-rw-r--r--build/bin/rule39
-rw-r--r--build/bin/rule.cxx126
-rw-r--r--build/bin/target96
-rw-r--r--build/bin/target.cxx156
-rw-r--r--build/buildfile9
-rw-r--r--build/config/utility45
-rw-r--r--build/config/utility.cxx41
-rw-r--r--build/config/utility.txx61
-rw-r--r--build/cxx/module.cxx144
-rw-r--r--build/cxx/rule7
-rw-r--r--build/cxx/rule.cxx213
-rw-r--r--build/file.cxx18
-rw-r--r--build/native70
-rw-r--r--build/native.cxx72
-rw-r--r--build/prerequisite2
-rw-r--r--build/rule.cxx6
-rw-r--r--build/search.cxx4
-rw-r--r--build/target169
-rw-r--r--build/target.cxx22
-rw-r--r--build/target.ixx13
-rw-r--r--build/utility11
26 files changed, 1162 insertions, 524 deletions
diff --git a/build/algorithm b/build/algorithm
index 81ad8d8..7d82285 100644
--- a/build/algorithm
+++ b/build/algorithm
@@ -54,8 +54,13 @@ namespace build
void
inject_parent_fsdir (action, target&);
+ // Executor function type.
+ //
+ using executor_function = target_state (action, target&);
+
// Execute the action on target, assuming a rule has been matched
- // and the recipe for this action has been set.
+ // and the recipe for this action has been set. This is the default
+ // executor implementation.
//
target_state
execute (action, target&);
@@ -68,17 +73,20 @@ namespace build
// with postponed execution the same as ignored. Note that this
// function can be used as a recipe.
//
+ template <executor_function* E = execute>
target_state
execute_prerequisites (action, target&);
// As above but iterates over the prerequisites in reverse.
//
+ template <executor_function* E = execute>
target_state
reverse_execute_prerequisites (action, target&);
// A version of the above that also determines whether the action
// needs to be executed on the target based on the passed timestamp.
//
+ template <executor_function* E = execute>
bool
execute_prerequisites (action, target&, const timestamp&);
@@ -89,15 +97,28 @@ namespace build
// there are multiple prerequisites of this type, then the last
// one returned.
//
- template <typename T>
+ template <typename T, executor_function* E = execute>
T*
- execute_prerequisites (action, target&, const timestamp&);
+ execute_find_prerequisites (action, target&, const timestamp&);
+
+ // Return noop_recipe instead of using this function directly.
+ //
+ target_state
+ noop_action (action, target&);
+
+ // Default action implementation which forwards to the prerequsites.
+ // Use default_recipe for the standard executor.
+ //
+ template <executor_function* E = execute>
+ target_state
+ default_action (action, target&);
// Standard perform(clean) action implementation for the file target
// or derived.
//
+ template <executor_function* E = execute>
target_state
- perform_clean_file (action, target&);
+ perform_clean (action, target&);
}
#include <build/algorithm.ixx>
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index 82286f5..9f428b3 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -250,118 +250,9 @@ namespace build
}
target_state
- execute_prerequisites (action a, target& t)
+ noop_action (action, target&)
{
- target_state ts (target_state::unchanged);
-
- if (t.group != nullptr)
- ts = execute_prerequisites (a, *t.group);
-
- for (target* pt: t.prerequisites)
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- if (execute (a, *pt) == target_state::changed)
- ts = target_state::changed;
- }
-
- return ts;
- }
-
- target_state
- reverse_execute_prerequisites (action a, target& t)
- {
- target_state ts (target_state::unchanged);
-
- for (target* pt: reverse_iterate (t.prerequisites))
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- if (execute (a, *pt) == target_state::changed)
- ts = target_state::changed;
- }
-
- if (t.group != nullptr)
- {
- if (reverse_execute_prerequisites (a, *t.group) == target_state::changed)
- ts = target_state::changed;
- }
-
- return ts;
- }
-
- bool
- execute_prerequisites (action a, target& t, const timestamp& mt)
- {
- bool e (mt == timestamp_nonexistent);
-
- if (t.group != nullptr)
- {
- if (execute_prerequisites (a, *t.group, mt))
- e = true;
- }
-
- for (target* pt: t.prerequisites)
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- target_state ts (execute (a, *pt));
-
- if (!e)
- {
- // If this is an mtime-based target, then compare timestamps.
- //
- if (auto mpt = dynamic_cast<const mtime_target*> (pt))
- {
- timestamp mp (mpt->mtime ());
-
- // What do we do if timestamps are equal? This can happen, for
- // example, on filesystems that don't have subsecond resolution.
- // There is not much we can do here except detect the case where
- // the prerequisite was changed in this run which means the
- // action must be executed on the target as well.
- //
- if (mt < mp || (mt == mp && ts == target_state::changed))
- e = true;
- }
- else
- {
- // Otherwise we assume the prerequisite is newer if it was changed.
- //
- if (ts == target_state::changed)
- e = true;
- }
- }
- }
-
- return e;
- }
-
- target_state
- perform_clean_file (action a, target& t)
- {
- // The reverse order of update: first delete the file, then clean
- // prerequisites.
- //
- file& ft (dynamic_cast<file&> (t));
-
- bool r (rmfile (ft.path (), ft));
-
- // Update timestamp in case there are operations after us that
- // could use the information.
- //
- ft.mtime (timestamp_nonexistent);
-
- // Clean prerequisites.
- //
- target_state ts (target_state::unchanged);
-
- if (!t.prerequisites.empty ())
- ts = reverse_execute_prerequisites (a, t);
-
- return r ? target_state::changed : ts;
+ assert (false); // We shouldn't be called, see target::recipe().
+ return target_state::unchanged;
}
}
diff --git a/build/algorithm.ixx b/build/algorithm.ixx
index 5d87338..76f9a0f 100644
--- a/build/algorithm.ixx
+++ b/build/algorithm.ixx
@@ -53,16 +53,16 @@ namespace build
}
}
- template <typename T>
+ template <typename T, executor_function* E>
T*
- execute_prerequisites (action, target&, const timestamp&, bool&);
+ execute_find_prerequisites (action, target&, const timestamp&, bool&);
- template <typename T>
+ template <typename T, executor_function* E>
inline T*
- execute_prerequisites (action a, target& t, const timestamp& mt)
+ execute_find_prerequisites (action a, target& t, const timestamp& mt)
{
bool e (mt == timestamp_nonexistent);
- T* r (execute_prerequisites<T> (a, t, mt, e));
+ T* r (execute_find_prerequisites<T, E> (a, t, mt, e));
assert (r != nullptr);
return e ? r : nullptr;
}
diff --git a/build/algorithm.txx b/build/algorithm.txx
index 2592b5e..4f5f9f3 100644
--- a/build/algorithm.txx
+++ b/build/algorithm.txx
@@ -2,11 +2,110 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
+#include <build/context>
+#include <build/utility> // reverse_iterate
+
namespace build
{
- template <typename T>
+ template <executor_function* E>
+ target_state
+ execute_prerequisites (action a, target& t)
+ {
+ target_state ts (target_state::unchanged);
+
+ if (t.group != nullptr)
+ ts = execute_prerequisites<E> (a, *t.group);
+
+ for (target* pt: t.prerequisites)
+ {
+ if (pt == nullptr) // Skip ignored.
+ continue;
+
+ if (E (a, *pt) == target_state::changed)
+ ts = target_state::changed;
+ }
+
+ return ts;
+ }
+
+ template <executor_function* E>
+ target_state
+ reverse_execute_prerequisites (action a, target& t)
+ {
+ target_state ts (target_state::unchanged);
+
+ for (target* pt: reverse_iterate (t.prerequisites))
+ {
+ if (pt == nullptr) // Skip ignored.
+ continue;
+
+ if (E (a, *pt) == target_state::changed)
+ ts = target_state::changed;
+ }
+
+ if (t.group != nullptr)
+ {
+ if (reverse_execute_prerequisites<E> (a, *t.group) ==
+ target_state::changed)
+ ts = target_state::changed;
+ }
+
+ return ts;
+ }
+
+ template <executor_function* E>
+ bool
+ execute_prerequisites (action a, target& t, const timestamp& mt)
+ {
+ bool e (mt == timestamp_nonexistent);
+
+ if (t.group != nullptr)
+ {
+ if (execute_prerequisites<E> (a, *t.group, mt))
+ e = true;
+ }
+
+ for (target* pt: t.prerequisites)
+ {
+ if (pt == nullptr) // Skip ignored.
+ continue;
+
+ target_state ts (E (a, *pt));
+
+ if (!e)
+ {
+ // If this is an mtime-based target, then compare timestamps.
+ //
+ if (auto mpt = dynamic_cast<const mtime_target*> (pt))
+ {
+ timestamp mp (mpt->mtime ());
+
+ // What do we do if timestamps are equal? This can happen, for
+ // example, on filesystems that don't have subsecond resolution.
+ // There is not much we can do here except detect the case where
+ // the prerequisite was changed in this run which means the
+ // action must be executed on the target as well.
+ //
+ if (mt < mp || (mt == mp && ts == target_state::changed))
+ e = true;
+ }
+ else
+ {
+ // Otherwise we assume the prerequisite is newer if it was changed.
+ //
+ if (ts == target_state::changed)
+ e = true;
+ }
+ }
+ }
+
+ return e;
+ }
+
+ template <typename T, executor_function* E>
T*
- execute_prerequisites (action a, target& t, const timestamp& mt, bool& e)
+ execute_find_prerequisites (
+ action a, target& t, const timestamp& mt, bool& e)
{
//@@ Can factor the bulk of it into a non-template code. Can
// either do a function template that will do dynamic_cast check
@@ -15,14 +114,14 @@ namespace build
T* r (nullptr);
if (t.group != nullptr)
- r = execute_prerequisites<T> (a, *t.group, mt, e);
+ r = execute_find_prerequisites<T, E> (a, *t.group, mt, e);
for (target* pt: t.prerequisites)
{
if (pt == nullptr) // Skip ignored.
continue;
- target_state ts (execute (a, *pt));
+ target_state ts (E (a, *pt));
if (!e)
{
@@ -56,4 +155,36 @@ namespace build
return r;
}
+
+ template <executor_function* E = execute>
+ target_state
+ default_action (action a, target& t)
+ {
+ return current_mode == execution_mode::first
+ ? execute_prerequisites<E> (a, t)
+ : reverse_execute_prerequisites<E> (a, t);
+ }
+
+ template <executor_function* E>
+ target_state
+ perform_clean (action a, target& t)
+ {
+ // The reverse order of update: first delete the file, then clean
+ // prerequisites.
+ //
+ file& ft (dynamic_cast<file&> (t));
+
+ bool r (rmfile (ft.path (), ft));
+
+ // Update timestamp in case there are operations after us that
+ // could use the information.
+ //
+ ft.mtime (timestamp_nonexistent);
+
+ // Clean prerequisites.
+ //
+ target_state ts (reverse_execute_prerequisites<E> (a, t));
+
+ return r ? target_state::changed : ts;
+ }
}
diff --git a/build/b.cxx b/build/b.cxx
index 01ca248..eac0768 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -77,14 +77,15 @@ namespace build
}
}
-#include <build/native>
+#include <build/config/module>
+
+#include <build/bin/target>
+#include <build/bin/rule>
#include <build/cxx/target>
#include <build/cxx/rule>
#include <build/cxx/module>
-#include <build/config/module>
-
using namespace build;
int
@@ -139,11 +140,13 @@ main (int argc, char* argv[])
target_types.insert (dir::static_type);
target_types.insert (fsdir::static_type);
- target_types.insert (obja::static_type);
- target_types.insert (objso::static_type);
- target_types.insert (obj::static_type);
- target_types.insert (exe::static_type);
- target_types.insert (lib::static_type);
+ target_types.insert (bin::obja::static_type);
+ target_types.insert (bin::objso::static_type);
+ target_types.insert (bin::obj::static_type);
+ target_types.insert (bin::exe::static_type);
+ target_types.insert (bin::liba::static_type);
+ target_types.insert (bin::libso::static_type);
+ target_types.insert (bin::lib::static_type);
target_types.insert (cxx::h::static_type);
target_types.insert (cxx::c::static_type);
@@ -155,27 +158,45 @@ main (int argc, char* argv[])
// Register rules.
//
- cxx::link cxx_link;
- rules[default_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
- rules[update_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
- rules[clean_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
+ bin::obj_rule obj_rule;
+ bin::lib_rule lib_rule;
+ {
+ using namespace bin;
+
+ rules[default_id][typeid (obj)].emplace ("bin.obj", obj_rule);
+ rules[update_id][typeid (obj)].emplace ("bin.obj", obj_rule);
+ rules[clean_id][typeid (obj)].emplace ("bin.obj", obj_rule);
- 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);
+ rules[default_id][typeid (lib)].emplace ("bin.lib", lib_rule);
+ rules[update_id][typeid (lib)].emplace ("bin.lib", lib_rule);
+ rules[clean_id][typeid (lib)].emplace ("bin.lib", lib_rule);
+ }
cxx::compile cxx_compile;
- rules[default_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
- rules[update_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
- rules[clean_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
+ cxx::link cxx_link;
+ {
+ using namespace bin;
+
+ rules[default_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[update_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[clean_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
- rules[default_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
- rules[update_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
- rules[clean_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[default_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[update_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[clean_id][typeid (objso)].emplace ("cxx.gnu.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);
- rules[clean_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[default_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
+ 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 (liba)].emplace ("cxx.gnu.link", cxx_link);
+ rules[update_id][typeid (liba)].emplace ("cxx.gnu.link", cxx_link);
+ rules[clean_id][typeid (liba)].emplace ("cxx.gnu.link", cxx_link);
+
+ rules[default_id][typeid (libso)].emplace ("cxx.gnu.link", cxx_link);
+ rules[update_id][typeid (libso)].emplace ("cxx.gnu.link", cxx_link);
+ rules[clean_id][typeid (libso)].emplace ("cxx.gnu.link", cxx_link);
+ }
dir_rule dir_r;
rules[default_id][typeid (dir)].emplace ("dir", dir_r);
diff --git a/build/bin/rule b/build/bin/rule
new file mode 100644
index 0000000..007f6f7
--- /dev/null
+++ b/build/bin/rule
@@ -0,0 +1,39 @@
+// file : build/bin/rule -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_BIN_RULE
+#define BUILD_BIN_RULE
+
+#include <build/rule>
+
+namespace build
+{
+ namespace bin
+ {
+ class obj_rule: public rule
+ {
+ public:
+ virtual void*
+ match (action, target&, const std::string& hint) const;
+
+ virtual recipe
+ apply (action, target&, void*) const;
+ };
+
+ class lib_rule: public rule
+ {
+ public:
+ virtual void*
+ match (action, target&, const std::string& hint) const;
+
+ virtual recipe
+ apply (action, target&, void*) const;
+
+ static target_state
+ perform (action, target&);
+ };
+ }
+}
+
+#endif // BUILD_BIN_RULE
diff --git a/build/bin/rule.cxx b/build/bin/rule.cxx
new file mode 100644
index 0000000..1b887bf
--- /dev/null
+++ b/build/bin/rule.cxx
@@ -0,0 +1,126 @@
+// file : build/bin/rule.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/bin/rule>
+
+#include <build/scope>
+#include <build/target>
+#include <build/algorithm>
+#include <build/diagnostics>
+
+#include <build/config/utility>
+
+#include <build/bin/target>
+
+using namespace std;
+
+namespace build
+{
+ namespace bin
+ {
+ // obj
+ //
+ void* obj_rule::
+ match (action a, target& t, const std::string&) const
+ {
+ fail << diag_doing (a, t) << " target group" <<
+ info << "explicitly select either obja{} or objso{} member";
+ }
+
+ recipe obj_rule::
+ apply (action, target&, void*) const {return empty_recipe;}
+
+ // lib
+ //
+ // The whole logic is pretty much as if we had our two group
+ // members as prerequisites.
+ //
+ void* lib_rule::
+ match (action, target& t, const std::string&) const
+ {
+ return &t;
+ }
+
+ recipe lib_rule::
+ apply (action a, target& xt, void*) const
+ {
+ lib& t (static_cast<lib&> (xt));
+
+ // Configure.
+ //
+ // The logic is as follows: if this library somehow knowns what
+ // it wants to be (i.e., the bin.lib is defined), then don't
+ // bother configuring the project-wide value.
+ //
+ const string* type (nullptr);
+
+ if (auto v = t["bin.lib"])
+ type = &v.as<const string&> ();
+ else
+ {
+ scope& root (*t.root_scope ());
+ type = &config::required (root, "config.bin.lib", "shared").first;
+ root.assign ("bin.lib") = *type;
+ }
+
+ bool ar (*type == "static" || *type == "both");
+ bool so (*type == "shared" || *type == "both");
+
+ if (!ar && !so)
+ fail << "unknown library type: " << *type <<
+ info << "'static', 'shared', or 'both' expected";
+
+ if (ar)
+ {
+ if (t.a == nullptr)
+ t.a = &static_cast<liba&> (search (prerequisite_key {
+ &liba::static_type, &t.dir, &t.name, &t.ext, nullptr}));
+
+ build::match (a, *t.a);
+ }
+
+ if (so)
+ {
+ if (t.so == nullptr)
+ t.so = &static_cast<libso&> (search (prerequisite_key {
+ &libso::static_type, &t.dir, &t.name, &t.ext, nullptr}));
+
+ build::match (a, *t.so);
+ }
+
+ return &perform;
+ }
+
+ target_state lib_rule::
+ perform (action a, target& xt)
+ {
+ lib& t (static_cast<lib&> (xt));
+
+ //@@ Not cool we have to do this again. Looks like we need
+ // some kind of a cache vs resolved pointer, like in
+ // prerequisite vs prerequisite_target.
+ //
+ //
+ const string& type (t["bin.lib"].as<const string&> ());
+ bool ar (type == "static" || type == "both");
+ bool so (type == "shared" || type == "both");
+
+ target* m1 (ar ? t.a : nullptr);
+ target* m2 (so ? t.so : nullptr);
+
+ if (current_mode == execution_mode::last)
+ swap (m1, m2);
+
+ target_state ts (target_state::unchanged);
+
+ if (m1 != nullptr && execute (a, *m1) == target_state::changed)
+ ts = target_state::changed;
+
+ if (m2 != nullptr && execute (a, *m2) == target_state::changed)
+ ts = target_state::changed;
+
+ return ts;
+ }
+ }
+}
diff --git a/build/bin/target b/build/bin/target
new file mode 100644
index 0000000..b26a890
--- /dev/null
+++ b/build/bin/target
@@ -0,0 +1,96 @@
+// file : build/bin/target -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_BIN_TARGET
+#define BUILD_BIN_TARGET
+
+#include <build/target>
+
+namespace build
+{
+ namespace bin
+ {
+ // The obj{} target group.
+ //
+ class obja: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
+ class objso: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
+ class obj: public target
+ {
+ public:
+ using target::target;
+
+ obja* a {nullptr};
+ objso* so {nullptr};
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
+ class exe: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
+ // The lib{} target group.
+ //
+ class liba: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
+ class libso: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
+ class lib: public target
+ {
+ public:
+ using target::target;
+
+ liba* a {nullptr};
+ libso* so {nullptr};
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+ }
+}
+
+#endif // BUILD_BIN_TARGET
diff --git a/build/bin/target.cxx b/build/bin/target.cxx
new file mode 100644
index 0000000..1849533
--- /dev/null
+++ b/build/bin/target.cxx
@@ -0,0 +1,156 @@
+// file : build/bin/target.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/bin/target>
+
+using namespace std;
+
+namespace build
+{
+ namespace bin
+ {
+ static target*
+ obja_factory (dir_path d, std::string n, const std::string* e)
+ {
+ obj* o (targets.find<obj> (d, n));
+ obja* a (new obja (std::move (d), std::move (n), e));
+
+ if ((a->group = o))
+ o->a = a;
+
+ return a;
+ }
+
+ const target_type obja::static_type
+ {
+ typeid (obja),
+ "obja",
+ &file::static_type,
+ &obja_factory,
+ file::static_type.search
+ };
+
+ static target*
+ objso_factory (dir_path d, std::string n, const std::string* e)
+ {
+ obj* o (targets.find<obj> (d, n));
+ objso* so (new objso (std::move (d), std::move (n), e));
+
+ if ((so->group = o))
+ o->so = so;
+
+ return so;
+ }
+
+ const target_type objso::static_type
+ {
+ typeid (objso),
+ "objso",
+ &file::static_type,
+ &objso_factory,
+ file::static_type.search
+ };
+
+ static target*
+ obj_factory (dir_path d, string n, const string* e)
+ {
+ obja* a (targets.find<obja> (d, n));
+ objso* so (targets.find<objso> (d, n));
+ obj* o (new obj (move (d), move (n), e));
+
+ if ((o->a = a))
+ a->group = o;
+
+ if ((o->so = so))
+ so->group = o;
+
+ return o;
+ }
+
+ const target_type obj::static_type
+ {
+ typeid (obj),
+ "obj",
+ &target::static_type,
+ &obj_factory,
+ target::static_type.search
+ };
+
+ const target_type exe::static_type
+ {
+ typeid (exe),
+ "exe",
+ &file::static_type,
+ &target_factory<exe>,
+ file::static_type.search
+ };
+
+ static target*
+ liba_factory (dir_path d, std::string n, const std::string* e)
+ {
+ lib* l (targets.find<lib> (d, n));
+ liba* a (new liba (std::move (d), std::move (n), e));
+
+ if ((a->group = l))
+ l->a = a;
+
+ return a;
+ }
+
+ const target_type liba::static_type
+ {
+ typeid (liba),
+ "liba",
+ &file::static_type,
+ &liba_factory,
+ file::static_type.search
+ };
+
+ static target*
+ libso_factory (dir_path d, std::string n, const std::string* e)
+ {
+ lib* l (targets.find<lib> (d, n));
+ libso* so (new libso (std::move (d), std::move (n), e));
+
+ if ((so->group = l))
+ l->so = so;
+
+ return so;
+ }
+
+ const target_type libso::static_type
+ {
+ typeid (libso),
+ "libso",
+ &file::static_type,
+ &libso_factory,
+ file::static_type.search
+ };
+
+ static target*
+ lib_factory (dir_path d, string n, const string* e)
+ {
+ liba* a (targets.find<liba> (d, n));
+ libso* so (targets.find<libso> (d, n));
+ lib* l (new lib (move (d), move (n), e));
+
+ if ((l->a = a))
+ a->group = l;
+
+ if ((l->so = so))
+ so->group = l;
+
+ return l;
+ }
+
+ const target_type lib::static_type
+ {
+ typeid (lib),
+ "lib",
+ &target::static_type,
+ &lib_factory,
+ target::static_type.search
+ };
+ }
+}
diff --git a/build/buildfile b/build/buildfile
index b3dfb4a..dd6fcaa 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -1,10 +1,11 @@
+config = config/{operation module utility}
+bin = bin/{target rule}
cxx = cxx/{target rule module}
-config = config/{operation module}
exe{b b-prev}: cxx{b algorithm name operation spec scope variable target \
- prerequisite rule file module native context search diagnostics token \
- lexer parser process timestamp path path-io utility filesystem dump \
- options $config $cxx}
+ prerequisite rule file module context search diagnostics token lexer \
+ parser process timestamp path path-io utility filesystem dump options \
+ $config $bin $cxx}
.: exe{b b-prev}
diff --git a/build/config/utility b/build/config/utility
new file mode 100644
index 0000000..2973516
--- /dev/null
+++ b/build/config/utility
@@ -0,0 +1,45 @@
+// file : build/config/utility -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_CONFIG_UTILITY
+#define BUILD_CONFIG_UTILITY
+
+#include <string>
+#include <utility> // pair
+
+namespace build
+{
+ class scope;
+ class list_value;
+
+ namespace config
+ {
+ // Set, if necessary, a required config.* variable.
+ //
+ // Return the reference to the value as well as the indication of
+ // whether the variable has actually been set.
+ //
+ template <typename T>
+ std::pair<const T&, bool>
+ required (scope& root, const char* name, const T& default_value);
+
+ std::pair<const std::string&, bool>
+ required (scope& root, const char* name, const char* default_value);
+
+ // Set, if necessary, an optional config.* variable. In particular,
+ // an unspecified variable is set to NULL which is used to to
+ // distinguish between the "configured as unspecified" and "not
+ // yet configured" cases.
+ //
+ // Return the pointer to the value, which can be NULL.
+ //
+ template <typename T>
+ const T*
+ optional (scope& root, const char* name);
+ }
+}
+
+#include <build/config/utility.txx>
+
+#endif // BUILD_CONFIG_UTILITY
diff --git a/build/config/utility.cxx b/build/config/utility.cxx
new file mode 100644
index 0000000..8b26239
--- /dev/null
+++ b/build/config/utility.cxx
@@ -0,0 +1,41 @@
+// file : build/config/utility.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/config/utility>
+
+using namespace std;
+
+namespace build
+{
+ namespace config
+ {
+ // The same as the template except it is a bit more efficient
+ // when it comes to not creating the default value string
+ // unnecessarily.
+ //
+ pair<const string&, bool>
+ required (scope& root, const char* name, const char* def_value)
+ {
+ string r;
+ const variable& var (variable_pool.find (name));
+
+ if (auto v = root[var])
+ {
+ const string& s (v.as<const string&> ());
+
+ if (!v.belongs (*global_scope)) // A value from (some) config.build.
+ return pair<const string&, bool> (s, false);
+
+ r = s;
+ }
+ else
+ r = def_value;
+
+ auto v (root.assign (var));
+ v = move (r);
+
+ return pair<const string&, bool> (v.as<const string&> (), true);
+ }
+ }
+}
diff --git a/build/config/utility.txx b/build/config/utility.txx
new file mode 100644
index 0000000..e37c2b5
--- /dev/null
+++ b/build/config/utility.txx
@@ -0,0 +1,61 @@
+// file : build/config/utility.txx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <build/scope>
+#include <build/variable>
+
+namespace build
+{
+ namespace config
+ {
+ template <typename T>
+ std::pair<const T&, bool>
+ required (scope& root, const char* name, const T& def_value)
+ {
+ T r;
+ const variable& var (variable_pool.find (name));
+
+ if (auto v = root[var])
+ {
+ const T& s (v.as<const T&> ());
+
+ if (!v.belongs (*global_scope)) // A value from (some) config.build.
+ return std::pair<const T&, bool> (s, false);
+
+ r = s;
+ }
+ else
+ r = def_value;
+
+ auto v (root.assign (var));
+ v = std::move (r);
+
+ return std::pair<const T&, bool> (v.as<const T&> (), true);
+ }
+
+ template <typename T>
+ const T*
+ optional (scope& root, const char* name)
+ {
+ const T* r (nullptr);
+ const variable& var (variable_pool.find (name));
+
+ auto v (root[var]);
+
+ if (v.defined ())
+ {
+ if (v.belongs (*global_scope))
+ root.assign (var) = v;
+
+ r = v.null () ? nullptr : &v.as<const T&> ();
+ }
+ else
+ root.assign (var) = nullptr;
+
+ return r;
+ }
+ }
+}
diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx
index 3e0101f..45f8271 100644
--- a/build/cxx/module.cxx
+++ b/build/cxx/module.cxx
@@ -12,6 +12,8 @@
#include <build/process>
#include <build/diagnostics>
+#include <build/config/utility>
+
using namespace std;
namespace build
@@ -41,130 +43,68 @@ namespace build
// config.cxx
//
- for (bool f (true); f; f = false)
{
- auto val (root["config.cxx"]);
-
- string v;
+ auto r (config::required (root, "config.cxx", "g++"));
- if (val)
+ // If we actually set a new value, test it by trying to execute.
+ //
+ if (r.second)
{
- if (!val.belongs (*global_scope))
- break; // A value from (some) config.build.
-
- v = val.as<const string&> ();
- }
- else
- v = "g++"; // Default.
+ const string& cxx (r.first);
+ const char* args[] = {cxx.c_str (), "-dumpversion", nullptr};
- // Test it by trying to execute.
- //
- const char* args[] = {v.c_str (), "-dumpversion", nullptr};
+ if (verb)
+ print_process (args);
+ else
+ text << "test " << cxx;
- if (verb)
- print_process (args);
- else
- text << "test " << v;
+ string ver;
+ try
+ {
+ process pr (args, false, false, true);
- string ver;
- try
- {
- process pr (args, false, false, true);
+ __gnu_cxx::stdio_filebuf<char> fb (pr.in_ofd, ios_base::in);
+ istream is (&fb);
- __gnu_cxx::stdio_filebuf<char> fb (pr.in_ofd, ios_base::in);
- istream is (&fb);
+ bool r (getline (is, ver));
- bool r (getline (is, ver));
+ if (!pr.wait ())
+ throw failed ();
- if (!pr.wait ())
- throw failed ();
+ if (!r)
+ fail << "unexpected output from " << cxx;
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << cxx << ": " << e.what ();
- if (!r)
- fail << "unexpected output from " << v;
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << v << ": " << e.what ();
+ if (e.child ())
+ exit (1);
- if (e.child ())
- exit (1);
+ throw failed ();
+ }
- throw failed ();
+ //text << "toolchain version " << ver;
}
-
- //text << "toolchain version " << ver;
-
- // Set on the project root.
- //
- root.assign ("config.cxx") = move (v);
}
// config.cxx.{p,c,l}options
// config.cxx.libs
//
- // These are optional so all we need to do is "import" them
- // into the root scope if they were specified on the command
- // line and set them to NULL if unspecified (the last part
- // is important to distinguish between the "configured as
- // unspecified" and "not configured" cases).
- //
- // We also merge them into the corresponding cxx.* variables.
+ // These are optional. We also merge them into the corresponding
+ // cxx.* variables.
//
- {
- auto v (root["config.cxx.poptions"]);
+ if (auto* v = config::optional<list_value> (root, "config.cxx.poptions"))
+ root.append ("cxx.poptions") += *v;
- if (v.defined ())
- {
- if (v.belongs (*global_scope))
- root.assign ("config.cxx.poptions") = v;
+ if (auto* v = config::optional<list_value> (root, "config.cxx.coptions"))
+ root.append ("cxx.coptions") += *v;
- root.append ("cxx.poptions") += v;
- }
- else
- root.assign ("config.cxx.poptions") = nullptr;
- }
+ if (auto* v = config::optional<list_value> (root, "config.cxx.loptions"))
+ root.append ("cxx.loptions") += *v;
- {
- auto v (root["config.cxx.coptions"]);
-
- if (v.defined ())
- {
- if (v.belongs (*global_scope))
- root.assign ("config.cxx.coptions") = v;
-
- root.append ("cxx.coptions") += v;
- }
- else
- root.assign ("config.cxx.coptions") = nullptr;
- }
-
- {
- auto v (root["config.cxx.loptions"]);
-
- if (v.defined ())
- {
- if (v.belongs (*global_scope))
- root.assign ("config.cxx.loptions") = v;
-
- root.append ("cxx.loptions") += v;
- }
- else
- root.assign ("config.cxx.loptions") = nullptr;
- }
-
- {
- auto v (root["config.cxx.libs"]);
-
- if (v.defined ())
- {
- if (v.belongs (*global_scope))
- root.assign ("config.cxx.libs") = v;
-
- root.append ("cxx.libs") += v;
- }
- else
- root.assign ("config.cxx.libs") = nullptr;
- }
+ if (auto* v = config::optional<list_value> (root, "config.cxx.libs"))
+ root.append ("cxx.libs") += *v;
}
}
}
diff --git a/build/cxx/rule b/build/cxx/rule
index 24879d4..d139cc7 100644
--- a/build/cxx/rule
+++ b/build/cxx/rule
@@ -6,9 +6,6 @@
#define BUILD_CXX_RULE
#include <build/rule>
-#include <build/native>
-
-#include <build/cxx/target>
namespace build
{
@@ -16,6 +13,8 @@ namespace build
namespace cxx
{
+ class cxx;
+
// @@ Can't we do match(obj&) and then registration code extracts
// that. And no virtuals?
//
@@ -39,6 +38,8 @@ namespace build
class link: public rule
{
public:
+ enum class type {exe, liba, libso};
+
virtual void*
match (action, target&, const std::string& hint) const;
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index 65616f8..9d25143 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -20,12 +20,18 @@
#include <build/diagnostics>
#include <build/context>
+#include <build/bin/target>
+
+#include <build/cxx/target>
+
using namespace std;
namespace build
{
namespace cxx
{
+ using namespace bin;
+
// T is either target or scope.
//
template <typename T>
@@ -73,27 +79,16 @@ namespace build
// - if path already assigned, verify extension?
//
- if (t.is_a<obj> ())
- fail << diag_doing (a, t) << " target group" <<
- info << "explicitly select either obja{} or objso{} member";
-
- // See if we have a C++ source file.
+ // See if we have a C++ source file. Iterate in reverse so that
+ // a source file specified for an obj member overrides the one
+ // specified for the group.
//
- for (prerequisite_target& pe: t.prerequisites)
+ for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t)))
{
if (pe.prereq->type.id == typeid (cxx))
return &pe;
}
- if (t.group != nullptr)
- {
- for (prerequisite_target& pe: t.group->prerequisites)
- {
- if (pe.prereq->type.id == typeid (cxx))
- return &pe;
- }
- }
-
level3 ([&]{trace << "no c++ source file for target " << t;});
return nullptr;
}
@@ -146,7 +141,7 @@ namespace build
switch (a)
{
case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean_file;
+ case perform_clean_id: return &perform_clean;
default: return default_recipe; // Forward to prerequisites.
}
}
@@ -273,9 +268,13 @@ namespace build
path f (next (l, pos));
f.normalize ();
- assert (f.absolute ()); // Logic below depends on this.
+ if (!f.absolute ())
+ {
+ level5 ([&]{trace << "skipping generated/non-existent " << f;});
+ continue;
+ }
- level5 ([&]{trace << "prerequisite path: " << f;});
+ level5 ([&]{trace << "injecting " << f;});
// Split the name into its directory part, the name part, and
// extension. Here we can assume the name part is a valid
@@ -343,7 +342,7 @@ namespace build
perform_update (action a, target& xt)
{
path_target& t (static_cast<path_target&> (xt));
- cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ()));
+ cxx* s (execute_find_prerequisites<cxx> (a, t, t.mtime ()));
if (s == nullptr)
return target_state::unchanged;
@@ -431,13 +430,13 @@ namespace build
// (i.e., a utility library).
//
- bool so (t.is_a<lib> ());
+ bool so (t.is_a<libso> ());
// Scan prerequisites and see if we can work with what we've got.
//
bool seen_cxx (false), seen_c (false), seen_obj (false);
- for (prerequisite& p: t.prerequisites)
+ for (prerequisite& p: group_prerequisites (t))
{
if (p.type.id == typeid (cxx)) // @@ Should use is_a (add to p.type).
{
@@ -478,6 +477,20 @@ namespace build
return seen_cxx || seen_c || seen_obj ? &t : nullptr;
}
+ static inline target_state
+ select_a (action a, target& t)
+ {
+ obj* o (t.is_a<obj> ());
+ return execute (a, o != nullptr ? *o->a : t);
+ }
+
+ static inline target_state
+ select_so (action a, target& t)
+ {
+ obj* o (t.is_a<obj> ());
+ return execute (a, o != nullptr ? *o->so : t);
+ }
+
recipe link::
apply (action a, target& xt, void*) const
{
@@ -485,16 +498,22 @@ namespace build
path_target& t (static_cast<path_target&> (xt));
- bool so (t.is_a<lib> ());
+ type tt (t.is_a<exe> ()
+ ? type::exe
+ : (t.is_a<liba> () ? type::liba : type::libso));
+
+ bool so (tt == type::libso);
// Derive file name from target name.
//
if (t.path ().empty ())
{
- if (so)
- t.path (t.derived_path ("so", "lib"));
- else
- t.path (t.derived_path ()); // exe
+ switch (tt)
+ {
+ case type::exe: t.path (t.derived_path ( )); break;
+ case type::liba: t.path (t.derived_path ("a", "lib")); break;
+ case type::libso: t.path (t.derived_path ("so", "lib")); break;
+ }
}
// We may need the project roots for rule chaining (see below).
@@ -506,8 +525,9 @@ namespace build
// Process prerequisites: do rule chaining for C and C++ source
// files as well as search and match.
//
- for (prerequisite_target& pe: t.prerequisites)
+ for (prerequisite_target& pe: group_prerequisites (t))
{
+ bool group (!pe.belongs (t)); // Target group's prerequisite.
prerequisite& p (pe);
if (!p.is_a<c> () && !p.is_a<cxx> ())
@@ -525,21 +545,37 @@ namespace build
// If this is the obj{} target group, then pick the appropriate
// member and make sure it is searched and matched.
//
+ target* pt;
+
if (obj* o = pe.target->is_a<obj> ())
{
- pe.target = so ? static_cast<target*> (o->so) : o->a;
+ pt = so ? static_cast<target*> (o->so) : o->a;
- if (pe.target == nullptr)
+ if (pt == nullptr)
{
const target_type& type (
so ? objso::static_type : obja::static_type);
- pe.target = &search (
+ pt = &search (
prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope});
}
+
+ // This is a bit tricky: if this is a group's prerequisite,
+ // then we have to keep pe.target pointing to the obj{} group
+ // since there could be another match that uses a different
+ // member of this prerequisite. In this case we specify the
+ // "executor" (see below) which will pick the right member
+ // for one of common algorithms. However, if this is our own
+ // prerequisite, then we are free to hard-wire the member
+ // we need directly in pe.target.
+ //
+ if (!group)
+ pe.target = pt;
}
+ else
+ pt = pe.target;
- build::match (a, *pe.target);
+ build::match (a, *pt);
continue;
}
@@ -557,7 +593,9 @@ namespace build
prerequisite& cp (p);
const target_type& o_type (
- so ? objso::static_type : obja::static_type);
+ group
+ ? obj::static_type
+ : (so ? objso::static_type : obja::static_type));
// Come up with the obj*{} prerequisite. The c(xx){} prerequisite
// directory can be relative (to the scope) or absolute. If it is
@@ -590,7 +628,7 @@ namespace build
// Resolve this prerequisite to target.
//
- target& ot (search (op));
+ target* ot (&search (op));
// If we are cleaning, check that this target is in the same or
// a subdirectory of ours.
@@ -601,7 +639,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 (t.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
@@ -612,6 +650,26 @@ namespace build
continue;
}
+ pe.target = ot;
+
+ // If we have created the obj{} target group, pick one of its
+ // members; the rest would be primarily concerned with it.
+ //
+ if (group)
+ {
+ obj& o (static_cast<obj&> (*ot));
+ ot = so ? static_cast<target*> (o.so) : o.a;
+
+ if (ot == nullptr)
+ {
+ const target_type& type (
+ so ? objso::static_type : obja::static_type);
+
+ ot = &search (
+ prerequisite_key {&type, &o.dir, &o.name, &o.ext, nullptr});
+ }
+ }
+
// If this target already exists, then it needs to be "compatible"
// with what we are doing here.
//
@@ -625,7 +683,7 @@ namespace build
// speculatively doesn't really hurt.
//
prerequisite* cp1 (nullptr);
- for (prerequisite& p: ot.prerequisites)
+ for (prerequisite& p: reverse_iterate (group_prerequisites (*ot)))
{
// Ignore some known target types (fsdir, headers).
//
@@ -643,19 +701,19 @@ namespace build
}
fail << "synthesized target for prerequisite " << cp
- << " would be incompatible with existing target " << ot <<
+ << " would be incompatible with existing target " << *ot <<
info << "unknown existing prerequsite type " << p <<
info << "specify corresponding obj{} target explicitly";
}
if (cp1 != nullptr)
{
- build::match (a, ot); // Now cp1 should be resolved.
- search (cp); // Our own prerequisite, so this is ok.
+ build::match (a, *ot); // Now cp1 should be resolved.
+ search (cp); // Our own prerequisite, so this is ok.
if (cp.target != cp1->target)
fail << "synthesized target for prerequisite " << cp
- << " would be incompatible with existing target " << ot <<
+ << " would be incompatible with existing target " << *ot <<
info << "existing prerequsite " << *cp1 << " does not "
<< "match " << cp <<
info << "specify corresponding " << o_type.name << "{} "
@@ -663,40 +721,55 @@ namespace build
}
else
{
- ot.prerequisites.emplace_back (cp);
- build::match (a, ot);
+ // Note: add the source to the group, not member.
+ //
+ pe.target->prerequisites.emplace_back (cp);
+ build::match (a, *ot);
}
// Change the exe{} target's prerequsite ref from cxx{} to obj*{}.
//
pe.prereq = &op;
- pe.target = &ot;
}
// Inject dependency on the output directory.
//
inject_parent_fsdir (a, t);
- switch (a)
+ if (so)
{
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean_file;
- default: return default_recipe; // Forward to prerequisites.
+ switch (a)
+ {
+ case perform_update_id: return &perform_update;
+ case perform_clean_id: return &perform_clean<select_so>;
+ default: return default_action<select_so>; // Forward to prerequisites.
+ }
+ }
+ else
+ {
+ switch (a)
+ {
+ case perform_update_id: return &perform_update;
+ case perform_clean_id: return &perform_clean<select_a>;
+ default: return default_action<select_a>; // Forward to prerequisites.
+ }
}
}
target_state link::
perform_update (action a, target& xt)
{
- // @@ Q:
- //
- // - what are we doing with libraries?
- //
path_target& t (static_cast<path_target&> (xt));
- bool so (t.is_a<lib> ());
+ type tt (t.is_a<exe> ()
+ ? type::exe
+ : (t.is_a<liba> () ? type::liba : type::libso));
+
+ bool so (tt == type::libso);
- if (!execute_prerequisites (a, t, t.mtime ()))
+ if (so
+ ? !execute_prerequisites<select_so> (a, t, t.mtime ())
+ : !execute_prerequisites<select_a> (a, t, t.mtime ()))
return target_state::unchanged;
// Translate paths to relative (to working directory) ones. This
@@ -706,24 +779,35 @@ namespace build
vector<path> relo;
scope& rs (*t.root_scope ()); // Shouldn't have matched if nullptr.
- const string& cxx (rs["config.cxx"].as<const string&> ());
+ vector<const char*> args;
+ string storage1;
- vector<const char*> args {cxx.c_str ()};
+ if (tt == type::liba)
+ {
+ //@@ ranlib
+ //
+ args.push_back ("ar");
+ args.push_back ("-rc");
+ args.push_back (relt.string ().c_str ());
+ }
+ else
+ {
+ args.push_back (rs["config.cxx"].as<const string&> ().c_str ());
- append_options (args, t, "cxx.coptions");
+ append_options (args, t, "cxx.coptions");
- string std; // Storage.
- append_std (args, t, std);
+ append_std (args, t, storage1);
- if (so)
- args.push_back ("-shared");
+ if (so)
+ args.push_back ("-shared");
- args.push_back ("-o");
- args.push_back (relt.string ().c_str ());
+ args.push_back ("-o");
+ args.push_back (relt.string ().c_str ());
- append_options (args, t, "cxx.loptions");
+ append_options (args, t, "cxx.loptions");
+ }
- for (target* pt: t.prerequisites)
+ for (target* pt: group_prerequisites (t))
{
if (pt == nullptr)
continue; // Skipped.
@@ -743,7 +827,8 @@ namespace build
args.push_back (relo.back ().string ().c_str ());
}
- append_options (args, t, "cxx.libs");
+ if (tt != type::liba)
+ append_options (args, t, "cxx.libs");
args.push_back (nullptr);
diff --git a/build/file.cxx b/build/file.cxx
index 72b8d37..c130663 100644
--- a/build/file.cxx
+++ b/build/file.cxx
@@ -11,6 +11,8 @@
#include <build/filesystem>
#include <build/diagnostics>
+#include <build/config/utility>
+
using namespace std;
namespace build
@@ -257,20 +259,14 @@ namespace build
// Figure out this project's out_root.
//
- const variable& var (variable_pool.find ("config." + n.value));
- auto val (iroot[var]);
+ string var ("config." + n.value);
+ const dir_path& out_root (
+ config::required (iroot, var.c_str (), dir_path ()).first);
- if (val)
- {
- if (val.belongs (*global_scope))
- iroot.assign (var) = val; // Copy into root scope.
- }
- else
+ if (out_root.empty ())
fail (l) << "unable to find out_root for imported " << n <<
info << "consider explicitly configuring its out_root via the "
- << var.name << " command line variable";
-
- const dir_path& out_root (val.as<const dir_path&> ());
+ << var << " command line variable";
// Bootstrap the imported root scope. This is pretty similar to
// what we do in main() except that here we don't try to guess
diff --git a/build/native b/build/native
deleted file mode 100644
index 3c1e6f2..0000000
--- a/build/native
+++ /dev/null
@@ -1,70 +0,0 @@
-// file : build/native -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_NATIVE
-#define BUILD_NATIVE
-
-#include <build/target>
-
-namespace build
-{
- // The obj{} target group members.
- //
- class obja: public file
- {
- public:
- using file::file;
-
- public:
- virtual const target_type& type () const {return static_type;}
- static const target_type static_type;
- };
-
- class objso: public file
- {
- public:
- using file::file;
-
- public:
- virtual const target_type& type () const {return static_type;}
- static const target_type static_type;
- };
-
- // Target group.
- //
- class obj: public target
- {
- public:
- using target::target;
-
- obja* a {nullptr};
- objso* so {nullptr};
-
- public:
- virtual const target_type& type () const {return static_type;}
- static const target_type static_type;
- };
-
- class exe: public file
- {
- public:
- using file::file;
-
- public:
- virtual const target_type& type () const {return static_type;}
- static const target_type static_type;
- };
-
- class lib: public file
- {
- public:
- using file::file;
-
- public:
- virtual const target_type& type () const {return static_type;}
- static const target_type static_type;
- };
-}
-
-#endif // BUILD_NATIVE
diff --git a/build/native.cxx b/build/native.cxx
deleted file mode 100644
index 42b6cf3..0000000
--- a/build/native.cxx
+++ /dev/null
@@ -1,72 +0,0 @@
-// file : build/native.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
-// license : MIT; see accompanying LICENSE file
-
-#include <build/native>
-
-using namespace std;
-
-namespace build
-{
- const target_type obja::static_type
- {
- typeid (obja),
- "obja",
- &file::static_type,
- &member_target_factory<obja, obj>,
- file::static_type.search
- };
-
- const target_type objso::static_type
- {
- typeid (objso),
- "objso",
- &file::static_type,
- &member_target_factory<objso, obj>,
- file::static_type.search
- };
-
- static target*
- obj_factory (dir_path d, string n, const string* e)
- {
- target* a (targets.find (obja::static_type, d, n));
- target* so (targets.find (objso::static_type, d, n));
-
- obj* t (new obj (move (d), move (n), e));
-
- if ((t->a = static_cast<obja*> (a)))
- a->group = t;
-
- if ((t->so = static_cast<objso*> (so)))
- so->group = t;
-
- return t;
- }
-
- const target_type obj::static_type
- {
- typeid (obj),
- "obj",
- &target::static_type,
- &obj_factory,
- target::static_type.search
- };
-
- const target_type exe::static_type
- {
- typeid (exe),
- "exe",
- &file::static_type,
- &target_factory<exe>,
- file::static_type.search
- };
-
- const target_type lib::static_type
- {
- typeid (lib),
- "lib",
- &file::static_type,
- &target_factory<lib>,
- file::static_type.search
- };
-}
diff --git a/build/prerequisite b/build/prerequisite
index b845f03..8a601fb 100644
--- a/build/prerequisite
+++ b/build/prerequisite
@@ -32,7 +32,7 @@ namespace build
typedef build::scope scope_type;
target_key tk;
- mutable scope_type* scope;
+ mutable scope_type* scope; // Can be NULL if tk.dir is absolute.
};
inline bool
diff --git a/build/rule.cxx b/build/rule.cxx
index 03dd056..e912b2e 100644
--- a/build/rule.cxx
+++ b/build/rule.cxx
@@ -92,7 +92,7 @@ namespace build
return a == perform_update_id
? &perform_update
- : t.prerequisites.empty () ? noop_recipe : default_recipe;
+ : t.has_prerequisites () ? default_recipe : noop_recipe;
}
target_state path_rule::
@@ -211,7 +211,7 @@ namespace build
// First update prerequisites (e.g. create parent directories)
// then create this directory.
//
- if (!t.prerequisites.empty ())
+ if (t.has_prerequisites ())
ts = execute_prerequisites (a, t);
const path& d (t.dir); // Everything is in t.dir.
@@ -253,7 +253,7 @@ namespace build
target_state ts (target_state::unchanged);
- if (!t.prerequisites.empty ())
+ if (t.has_prerequisites ())
ts = reverse_execute_prerequisites (a, t);
// If we couldn't remove the directory, return postponed meaning
diff --git a/build/search.cxx b/build/search.cxx
index 7fa9720..b1d5ec6 100644
--- a/build/search.cxx
+++ b/build/search.cxx
@@ -100,7 +100,9 @@ namespace build
level4 ([&]{trace << (r.second ? "new" : "existing") << " target "
<< t << " for prerequsite " << pk;});
- t.path (move (f));
+ if (t.path ().empty ())
+ t.path (move (f));
+
t.mtime (mt);
return &t;
}
diff --git a/build/target b/build/target
index 49c135f..cca8f8d 100644
--- a/build/target
+++ b/build/target
@@ -14,6 +14,7 @@
#include <ostream>
#include <cassert>
#include <utility> // move()
+#include <iterator>
#include <build/path>
#include <build/map-key> // map_iterator_adapter
@@ -69,7 +70,7 @@ namespace build
extern const recipe default_recipe;
target_state
- noop_recipe_function (action, target&);
+ noop_action (action, target&); // Defined in <algorithm>
// Prerequisite target. It consist of a reference to the prerequisite
// plus a pointer to target to which it resolves in this context.
@@ -101,6 +102,16 @@ namespace build
prerequisite_target (prerequisite& p, target_type& t)
: prereq (&p), target (&t) {}
+
+ // Return true if this object belongs to the target's prerequisite
+ // list. Note that this test only works if you use references to
+ // the container elements and the container hasn't been resized
+ // since such a reference was obtained. Normally this function is
+ // used when iterating over combined prerequisites range (see
+ // group_prerequisites below).
+ //
+ bool
+ belongs (const target_type&) const;
};
// Target.
@@ -126,7 +137,9 @@ namespace build
// if any. Note that we assume that the group
// and all its members are in the same scope
// (see, for example, variable lookup).
- public:
+ // We also currently assume that there are
+ // no multi-level groups.
+ public:
// Most qualified scope that contains this target.
//
scope&
@@ -145,6 +158,16 @@ namespace build
typedef std::vector<prerequisite_target> prerequisites_type;
prerequisites_type prerequisites;
+ // Check if there are any prerequisites, taking into account
+ // group prerequisites.
+ //
+ bool
+ has_prerequisites () const
+ {
+ return !prerequisites.empty () ||
+ (group != nullptr && !group->prerequisites.empty ());
+ }
+
// Target-specific variables.
//
public:
@@ -226,7 +249,7 @@ namespace build
// the recipe.
//
recipe_function** f (recipe_.target<recipe_function*> ());
- state = (f == nullptr || *f != &noop_recipe_function)
+ state = (f == nullptr || *f != &noop_action)
? target_state::unknown
: target_state::unchanged;
@@ -255,6 +278,119 @@ namespace build
std::ostream&
operator<< (std::ostream&, const target&);
+ // A "range" that presents the prerequisites of a group and one of
+ // its members as one continuous sequence, or, in other words, as
+ // if they were in a single container. The group's prerequisites
+ // come first followed by the member's. If you need to see them
+ // in the other direction, iterate in reverse, for example:
+ //
+ // for (prerequisite_target& pe: group_prerequisites (t))
+ //
+ // for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t))
+ //
+ // Note that in this case the individual elements of each list will
+ // also be traversed in reverse, but that's what you usually want,
+ // anyway.
+ //
+ class group_prerequisites
+ {
+ public:
+ typedef target::prerequisites_type prerequisites_type;
+
+ explicit
+ group_prerequisites (target& t): t_ (t) {}
+
+ struct iterator
+ {
+ typedef prerequisites_type::iterator base_iterator;
+
+ typedef base_iterator::value_type value_type;
+ typedef base_iterator::pointer pointer;
+ typedef base_iterator::reference reference;
+ typedef base_iterator::difference_type difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+ iterator () {}
+ iterator (target* t, prerequisites_type* c, base_iterator i)
+ : t_ (t), c_ (c), i_ (i) {}
+
+ iterator&
+ operator++ ()
+ {
+ if (++i_ == c_->end () && c_ != &t_->prerequisites)
+ {
+ c_ = &t_->prerequisites;
+ i_ = c_->begin ();
+ }
+ return *this;
+ }
+
+ iterator
+ operator++ (int) {iterator r (*this); return ++r;}
+
+ iterator&
+ operator-- ()
+ {
+ if (i_ == c_->begin () && c_ == &t_->prerequisites)
+ {
+ c_ = &t_->group->prerequisites;
+ i_ = c_->end ();
+ }
+
+ --i_;
+ return *this;
+ }
+
+ iterator
+ operator-- (int) {iterator r (*this); return --r;}
+
+ friend bool
+ operator== (const iterator& x, const iterator& y)
+ {
+ return x.t_ == y.t_ && x.c_ == y.c_ && x.i_ == y.i_;
+ }
+
+ reference operator* () const {return *i_;}
+ pointer operator-> () const {return i_.operator -> ();}
+
+ friend bool
+ operator!= (const iterator& x, const iterator& y) {return !(x == y);}
+
+ private:
+ target* t_ {nullptr};
+ prerequisites_type* c_ {nullptr};
+ prerequisites_type::iterator i_;
+ };
+
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+
+ iterator
+ begin () const
+ {
+ auto& c ((t_.group != nullptr && !t_.group->prerequisites.empty ()
+ ? *t_.group : t_).prerequisites);
+ return iterator (&t_, &c, c.begin ());
+ }
+
+ iterator
+ end () const
+ {
+ auto& c (t_.prerequisites);
+ return iterator (&t_, &c, c.end ());
+ }
+
+ reverse_iterator
+ rbegin () const {return reverse_iterator (end ());}
+
+ reverse_iterator
+ rend () const {return reverse_iterator (begin ());}
+
+ private:
+ target& t_;
+ };
+
+ //
+ //
struct target_set
{
typedef std::map<target_key, std::unique_ptr<target>> map;
@@ -277,14 +413,13 @@ namespace build
// As above but ignore the extension and return the target or
// nullptr instead of the iterator.
//
- target*
- find (const target_type& type,
- const dir_path& dir,
- const std::string& name) const
+ template <typename T>
+ T*
+ find (const dir_path& dir, const std::string& name) const
{
const std::string* e (nullptr);
- auto i (map_.find (target_key {&type, &dir, &name, &e}));
- return i != map_.end () ? i->second.get () : nullptr;
+ auto i (map_.find (target_key {&T::static_type, &dir, &name, &e}));
+ return i != map_.end () ? static_cast<T*> (i->second.get ()) : nullptr;
}
iterator begin () const {return map_.begin ();}
@@ -340,20 +475,6 @@ namespace build
return new T (std::move (d), std::move (n), e);
}
- // Default implementation for a target that is a member of a
- // target group. Besides creating the target as above this
- // version also tries to "link up" with the group.
- //
- template <typename T, typename G>
- target*
- member_target_factory (dir_path d, std::string n, const std::string* e)
- {
- target* g (targets.find (G::static_type, d, n));
- target* t (new T (std::move (d), std::move (n), e));
- t->group = g;
- return t;
- }
-
// Modification time-based target.
//
class mtime_target: public target
@@ -465,4 +586,6 @@ namespace build
};
}
+#include <build/target.ixx>
+
#endif // BUILD_TARGET
diff --git a/build/target.cxx b/build/target.cxx
index 4f06596..5e421c3 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -6,8 +6,7 @@
#include <build/scope>
#include <build/search>
-#include <build/context>
-#include <build/algorithm> // execute_prerequisites()
+#include <build/algorithm>
#include <build/diagnostics>
using namespace std;
@@ -16,24 +15,9 @@ namespace build
{
// recipe
//
- target_state
- noop_recipe_function (action, target&)
- {
- assert (false); // We shouldn't be called, see target::recipe().
- return target_state::unchanged;
- }
-
- static target_state
- default_recipe_function (action a, target& t)
- {
- return current_mode == execution_mode::first
- ? execute_prerequisites (a, t)
- : reverse_execute_prerequisites (a, t);
- }
-
const recipe empty_recipe;
- const recipe noop_recipe (&noop_recipe_function);
- const recipe default_recipe (&default_recipe_function);
+ const recipe noop_recipe (&noop_action);
+ const recipe default_recipe (&default_action);
// target
//
diff --git a/build/target.ixx b/build/target.ixx
new file mode 100644
index 0000000..02bdc69
--- /dev/null
+++ b/build/target.ixx
@@ -0,0 +1,13 @@
+// file : build/target.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+namespace build
+{
+ inline bool prerequisite_target::
+ belongs (const target_type& t) const
+ {
+ const auto& p (t.prerequisites);
+ return !(p.empty () || this < &p.front () || this > &p.back ());
+ }
+}
diff --git a/build/utility b/build/utility
index 6584c0a..01ecd95 100644
--- a/build/utility
+++ b/build/utility
@@ -50,12 +50,12 @@ namespace build
public:
reverse_range (T& x): x_ (x) {}
- auto begin () const -> decltype (this->x_.rbegin ())
+ auto begin () -> decltype (this->x_.rbegin ())
{
return x_.rbegin ();
}
- auto end () const -> decltype (this->x_.rend ())
+ auto end () -> decltype (this->x_.rend ())
{
return x_.rend ();
}
@@ -68,6 +68,13 @@ namespace build
return reverse_range<T> (x);
}
+ template <typename T>
+ inline reverse_range<const T>
+ reverse_iterate (const T& x)
+ {
+ return reverse_range<const T> (x);
+ }
+
// Call a function if there is an exception.
//