diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-04-24 12:29:20 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-04-24 12:29:20 +0200 |
commit | 2a0f9e035f673f1ee387924501a31990de37f18d (patch) | |
tree | b8e55ab74bc88b788e99d8649219b931b80432d5 /build | |
parent | 4c44c914d898af53152addad5530504548175e85 (diff) |
Implement lib/liba/libso{} target group, shared/static library build
Diffstat (limited to 'build')
-rw-r--r-- | build/algorithm | 29 | ||||
-rw-r--r-- | build/algorithm.cxx | 115 | ||||
-rw-r--r-- | build/algorithm.ixx | 10 | ||||
-rw-r--r-- | build/algorithm.txx | 139 | ||||
-rw-r--r-- | build/b.cxx | 69 | ||||
-rw-r--r-- | build/bin/rule | 39 | ||||
-rw-r--r-- | build/bin/rule.cxx | 126 | ||||
-rw-r--r-- | build/bin/target | 96 | ||||
-rw-r--r-- | build/bin/target.cxx | 156 | ||||
-rw-r--r-- | build/buildfile | 9 | ||||
-rw-r--r-- | build/config/utility | 45 | ||||
-rw-r--r-- | build/config/utility.cxx | 41 | ||||
-rw-r--r-- | build/config/utility.txx | 61 | ||||
-rw-r--r-- | build/cxx/module.cxx | 144 | ||||
-rw-r--r-- | build/cxx/rule | 7 | ||||
-rw-r--r-- | build/cxx/rule.cxx | 213 | ||||
-rw-r--r-- | build/file.cxx | 18 | ||||
-rw-r--r-- | build/native | 70 | ||||
-rw-r--r-- | build/native.cxx | 72 | ||||
-rw-r--r-- | build/prerequisite | 2 | ||||
-rw-r--r-- | build/rule.cxx | 6 | ||||
-rw-r--r-- | build/search.cxx | 4 | ||||
-rw-r--r-- | build/target | 169 | ||||
-rw-r--r-- | build/target.cxx | 22 | ||||
-rw-r--r-- | build/target.ixx | 13 | ||||
-rw-r--r-- | build/utility | 11 |
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. // |