From 5925c11a1fe8b2e02b790dd40b031ae005d5b68f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Mar 2015 15:42:04 +0200 Subject: Further operation implementation --- build/algorithm | 34 ++++++++++++---------- build/algorithm.cxx | 81 +++++++++++++++++++++++++++++++---------------------- build/algorithm.ixx | 8 +++--- build/algorithm.txx | 24 ++++++++-------- build/b.cxx | 66 +++++++++++++++++++++++++++---------------- build/buildfile | 6 ++-- build/context | 4 --- build/context.cxx | 3 -- build/cxx/rule | 14 ++++----- build/cxx/rule.cxx | 38 ++++++++++++------------- build/operation | 70 +++++++++++++++++++++++++++++++++++++++++++++ build/operation.cxx | 24 ++++++++++++++++ build/rule | 32 ++++++++++++--------- build/rule.cxx | 43 ++++++++++++++-------------- build/target | 20 ++++++++++--- build/target.cxx | 2 ++ build/utility | 53 ++++++++++++++++++++++++++++++++++- 17 files changed, 358 insertions(+), 164 deletions(-) create mode 100644 build/operation create mode 100644 build/operation.cxx diff --git a/build/algorithm b/build/algorithm index 82913ad..8ce35f8 100644 --- a/build/algorithm +++ b/build/algorithm @@ -6,6 +6,7 @@ #define BUILD_ALGORITHM #include +#include #include namespace build @@ -19,41 +20,44 @@ namespace build target& search (prerequisite&); - // Match a rule to the target with ambiguity detection. + // Match a rule to the action/target with ambiguity detection. // void - match (target&); + match (action, target&); // The default prerequisite search and match implementation. It calls // search() and then match() for each prerequisite in a loop. // void - search_and_match (target&); + search_and_match (action, target&); + // Execute the action on target, assuming a rule has been matched + // and the recipe for this action has been set. + // target_state - update (target&); + execute (action, target&); - // The default prerequisite update implementation. It calls update() - // for each prerequisite in a loop. Returns target_state::updated - // if any of them were updated and target_state::uptodate otherwise. + // The default prerequisite execute implementation. It calls execute() + // for each prerequisite in a loop. Returns target_state::changed + // if any of them were changed and target_state::unchanged otherwise. // target_state - update_prerequisites (target&); + execute_prerequisites (action, target&); - // A version of the above that also determines whether the target - // needs updating based on the passed timestamp. + // A version of the above that also determines whether the action + // needs to be executed on the target based on the passed timestamp. // bool - update_prerequisites (target&, const timestamp&); + execute_prerequisites (action, target&, const timestamp&); // Another version of the above that does two extra things for the - // caller: it determines whether the target needs updating based - // on the passed timestamp and, if so, finds a prerequisite of the - // specified type. + // caller: it determines whether the action needs to be executed on + // the target based on the passed timestamp and, if so, finds a + // prerequisite of the specified type (e.g., source file). // template T* - update_prerequisites (target&, const timestamp&); + execute_prerequisites (action, target&, const timestamp&); } #include diff --git a/build/algorithm.cxx b/build/algorithm.cxx index 5be5cad..46387b3 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -33,21 +33,36 @@ namespace build } void - match_impl (target& t) + match_impl (action a, target& t) { for (auto tt (&t.type ()); - tt != nullptr && !t.recipe (); + tt != nullptr && !t.recipe (a); tt = tt->base) { - auto i (rules.find (tt->id)); + auto i (current_rules->find (tt->id)); - if (i == rules.end ()) // No rules registered for this target type. - continue; + if (i == current_rules->end () || i->second.empty ()) + continue; // No rules registered for this target type, try base. const auto& rules (i->second); // Hint map. - string hint; // @@ TODO - auto rs (rules.find_prefix (hint)); + // @@ TODO + // + // Different rules can be used for different operations (update + // vs test is a good example). So, at some point, we will probably + // have to support a list of hints or even an operation-hint map + // (e.g., 'hint=cxx test=foo' if cxx supports the test operation + // but we want the foo rule instead). This is also the place where + // the '{build clean}=cxx' construct (which we currently do not + // support) can come handy. + // + // Also, ignore the hint (that is most likely ment for a different + // operation) if this is a unique match. + // + string hint; + auto rs (rules.size () == 1 + ? make_pair (rules.begin (), rules.end ()) + : rules.find_prefix (hint)); for (auto i (rs.first); i != rs.second; ++i) { @@ -64,7 +79,7 @@ namespace build }, t, n)); - m = ru.match (t, hint); + m = ru.match (a, t, hint); } if (m != nullptr) @@ -91,7 +106,7 @@ namespace build }, t, n1)); - m1 = ru1.match (t, hint); + m1 = ru1.match (a, t, hint); } if (m1 != nullptr) @@ -117,7 +132,7 @@ namespace build }, t, n)); - t.recipe (ru.apply (t, m)); + t.recipe (a, ru.apply (a, t, m)); break; } else @@ -126,24 +141,24 @@ namespace build } } - if (!t.recipe ()) + if (!t.recipe (a)) fail << "no rule to update target " << t; } void - search_and_match (target& t) + search_and_match (action a, target& t) { for (prerequisite& p: t.prerequisites) { if (p.target == nullptr) search (p); - match (*p.target); + match (a, *p.target); } } target_state - update (target& t) + execute (action a, target& t) { // Implementation with some multi-threading ideas in mind. // @@ -158,13 +173,13 @@ namespace build [](target& t){info << "while updating target " << t;}, t)); - ts = t.recipe () (t); + ts = t.recipe (a) (a, t); assert (ts != target_state::unknown && ts != target_state::failed); t.state (ts); return ts; } - case target_state::uptodate: - case target_state::updated: + case target_state::unchanged: + case target_state::changed: return ts; case target_state::failed: throw failed (); @@ -172,34 +187,34 @@ namespace build } target_state - update_prerequisites (target& t) + execute_prerequisites (action a, target& t) { - target_state ts (target_state::uptodate); + target_state ts (target_state::unchanged); for (const prerequisite& p: t.prerequisites) { assert (p.target != nullptr); - if (update (*p.target) != target_state::uptodate) - ts = target_state::updated; + if (execute (a, *p.target) != target_state::unchanged) + ts = target_state::changed; } return ts; } bool - update_prerequisites (target& t, const timestamp& mt) + execute_prerequisites (action a, target& t, const timestamp& mt) { - bool u (mt == timestamp_nonexistent); + bool e (mt == timestamp_nonexistent); for (const prerequisite& p: t.prerequisites) { assert (p.target != nullptr); target& pt (*p.target); - target_state ts (update (pt)); + target_state ts (execute (a, pt)); - if (!u) + if (!e) { // If this is an mtime-based target, then compare timestamps. // @@ -210,22 +225,22 @@ namespace build // 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 updated in this run which means the - // target must be out of date. + // 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::updated)) - u = true; + if (mt < mp || (mt == mp && ts == target_state::changed)) + e = true; } else { - // Otherwise we assume the prerequisite is newer if it was updated. + // Otherwise we assume the prerequisite is newer if it was changed. // - if (ts == target_state::updated) - u = true; + if (ts == target_state::changed) + e = true; } } } - return u; + return e; } } diff --git a/build/algorithm.ixx b/build/algorithm.ixx index 02c223c..e33566b 100644 --- a/build/algorithm.ixx +++ b/build/algorithm.ixx @@ -5,12 +5,12 @@ namespace build { void - match_impl (target&); + match_impl (action, target&); inline void - match (target& t) + match (action a, target& t) { - if (!t.recipe ()) - match_impl (t); + if (!t.recipe (a)) + match_impl (a, t); } } diff --git a/build/algorithm.txx b/build/algorithm.txx index 20817d2..4c9a673 100644 --- a/build/algorithm.txx +++ b/build/algorithm.txx @@ -6,7 +6,7 @@ namespace build { template T* - update_prerequisites (target& t, const timestamp& mt) + execute_prerequisites (action a, target& t, const timestamp& mt) { //@@ Can factor the bulk of it into a non-template code. Can // either do a function template that will do dynamic_cast check @@ -14,16 +14,16 @@ namespace build // T* r (nullptr); - bool u (mt == timestamp_nonexistent); + bool e (mt == timestamp_nonexistent); for (const prerequisite& p: t.prerequisites) { assert (p.target != nullptr); target& pt (*p.target); - target_state ts (update (pt)); + target_state ts (execute (a, pt)); - if (!u) + if (!e) { // If this is an mtime-based target, then compare timestamps. // @@ -34,18 +34,18 @@ namespace build // 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 updated in this run which means the - // target must be out of date. + // 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::updated)) - u = true; + if (mt < mp || (mt == mp && ts == target_state::changed)) + e = true; } else { - // Otherwise we assume the prerequisite is newer if it was updated. + // Otherwise we assume the prerequisite is newer if it was changed. // - if (ts == target_state::updated) - u = true; + if (ts == target_state::changed) + e = true; } } @@ -54,6 +54,6 @@ namespace build } assert (r != nullptr); - return u ? r : nullptr; + return e ? r : nullptr; } } diff --git a/build/b.cxx b/build/b.cxx index 4779a57..499ac10 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -19,8 +19,10 @@ #include #include -#include +#include #include +#include +#include #include #include #include @@ -102,16 +104,17 @@ main (int argc, char* argv[]) target_types.insert (cxx::ixx::static_type); target_types.insert (cxx::txx::static_type); - // Enter built-in meta-operation and operation names. Loading of - // the buildfiles can result in additional names being added (via - // module loading). + // Enter built-in meta-operation and operation names into tables. + // Note that the order of registration should match the id constants; + // see for details. Loading of the buildfiles can result + // in additional names being added (via module loading). // - meta_operations.emplace ("perform"); // Default. - meta_operations.emplace ("configure"); - meta_operations.emplace ("disfigure"); + meta_operations.insert ("perform"); // Default. + meta_operations.insert ("configure"); + meta_operations.insert ("disfigure"); - operations.emplace ("update"); // Default. - operations.emplace ("clean"); + operations.insert ("update"); // Default. + operations.insert ("clean"); // Figure out work and home directories. // @@ -334,6 +337,12 @@ main (int argc, char* argv[]) } } + // We store the combined action id in uint8_t; see for + // details. + // + assert (operations.size () <= 128); + assert (meta_operations.size () <= 128); + dump_scopes (); dump (); @@ -360,7 +369,7 @@ main (int argc, char* argv[]) continue; } - if (meta_operations.count (os.name)) + if (meta_operations.find (os.name) != 0) { if (!ms.name.empty ()) fail (l) << "nested meta-operation " << os.name; @@ -419,7 +428,7 @@ main (int argc, char* argv[]) } } - if (!operations.count (os.name)) + if (operations.find (os.name) == 0) fail (l) << "unknown operation " << os.name; } @@ -428,7 +437,7 @@ main (int argc, char* argv[]) // if (mi->name.empty ()) mi->name = "perform"; - else if (!meta_operations.count (mi->name)) + else if (meta_operations.find (mi->name) == 0) fail (l) << "unknown meta-operation " << mi->name; } @@ -437,19 +446,24 @@ main (int argc, char* argv[]) // Register rules. // cxx::link cxx_link; - rules[typeid (exe)].emplace ("cxx.gnu.link", cxx_link); + rules["update"][typeid (exe)].emplace ("cxx.gnu.link", cxx_link); + rules["clean"][typeid (exe)].emplace ("cxx.gnu.link", cxx_link); cxx::compile cxx_compile; - rules[typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile); + rules["update"][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile); + rules["clean"][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile); dir_rule dir_r; - rules[typeid (dir)].emplace ("dir", dir_r); + rules["update"][typeid (dir)].emplace ("dir", dir_r); + rules["clean"][typeid (dir)].emplace ("dir", dir_r); fsdir_rule fsdir_r; - rules[typeid (fsdir)].emplace ("fsdir", fsdir_r); + rules["update"][typeid (fsdir)].emplace ("fsdir", fsdir_r); + rules["clean"][typeid (fsdir)].emplace ("fsdir", fsdir_r); path_rule path_r; - rules[typeid (path_target)].emplace ("path", path_r); + rules["update"][typeid (path_target)].emplace ("path", path_r); + rules["clean"][typeid (path_target)].emplace ("path", path_r); // Do the operations. We do meta-operations and operations sequentially @@ -459,8 +473,12 @@ main (int argc, char* argv[]) { for (opspec& os: ms) { - // But multiple targets in the same operation can be done in - // parallel. + current_rules = &rules[os.name]; + action act (meta_operations.find (ms.name), operations.find (os.name)); + + level4 ([&]{trace << ms.name << " " << os.name << " " << act;}); + + // Multiple targets in the same operation can be done in parallel. // vector> tgs; tgs.reserve (os.size ()); @@ -497,10 +515,10 @@ main (int argc, char* argv[]) target& t (**i); - if (!t.recipe ()) + if (!t.recipe (act)) { level4 ([&]{trace << "matching target " << t;}); - match (t); + match (act, t); } tgs.push_back (t); @@ -520,17 +538,17 @@ main (int argc, char* argv[]) if (s == target_state::unknown) { level4 ([&]{trace << "updating target " << t;}); - s = update (t); + s = execute (act, t); } switch (s) { - case target_state::uptodate: + case target_state::unchanged: { info << "target " << t << " is up to date"; break; } - case target_state::updated: + case target_state::changed: break; case target_state::failed: //@@ This could probably happen in a parallel build. diff --git a/build/buildfile b/build/buildfile index 5c1225d..8f11823 100644 --- a/build/buildfile +++ b/build/buildfile @@ -1,3 +1,3 @@ -exe{b1}: cxx{b algorithm parser lexer name spec scope variable target \ - prerequisite rule native context search diagnostics cxx/target cxx/rule \ - process timestamp path utility mkdir dump} +exe{b1}: cxx{b algorithm parser lexer name operation spec scope variable \ + target prerequisite rule native context search diagnostics cxx/target \ + cxx/rule process timestamp path utility mkdir dump} diff --git a/build/context b/build/context index 1967a83..7c7421d 100644 --- a/build/context +++ b/build/context @@ -7,7 +7,6 @@ #include #include -#include #include @@ -18,9 +17,6 @@ namespace build extern path work; extern path home; - extern std::unordered_set meta_operations; - extern std::unordered_set operations; - // Return the src/out directory corresponding to the given out/src. The // passed directory should be a sub-directory of out/src_root. // diff --git a/build/context.cxx b/build/context.cxx index d58f9c7..6c37e38 100644 --- a/build/context.cxx +++ b/build/context.cxx @@ -16,9 +16,6 @@ namespace build path work; path home; - unordered_set meta_operations; - unordered_set operations; - path src_out (const path& out, scope& s) { diff --git a/build/cxx/rule b/build/cxx/rule index 80a917e..1dfb8b5 100644 --- a/build/cxx/rule +++ b/build/cxx/rule @@ -23,30 +23,30 @@ namespace build { public: virtual void* - match (target&, const std::string& hint) const; + match (action a, target&, const std::string& hint) const; virtual recipe - apply (target&, void*) const; + apply (action a, target&, void*) const; static target_state - update (target&); + update (action a, target&); private: void - inject_prerequisites (obj&, const cxx&, scope&) const; + inject_prerequisites (action a, obj&, const cxx&, scope&) const; }; class link: public rule { public: virtual void* - match (target&, const std::string& hint) const; + match (action a, target&, const std::string& hint) const; virtual recipe - apply (target&, void*) const; + apply (action a, target&, void*) const; static target_state - update (target&); + update (action a, target&); }; } } diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index e495c27..4c93a1a 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -28,7 +28,7 @@ namespace build // compile // void* compile:: - match (target& t, const string&) const + match (action a, target& t, const string&) const { tracer trace ("cxx::compile::match"); @@ -57,7 +57,7 @@ namespace build } recipe compile:: - apply (target& t, void* v) const + apply (action a, target& t, void* v) const { // Derive object file name from target name. // @@ -69,7 +69,7 @@ namespace build // Search and match all the existing prerequisites. The injection // code (below) takes care of the ones it is adding. // - search_and_match (t); + search_and_match (a, t); // Inject additional prerequisites. // @@ -77,7 +77,7 @@ namespace build auto& st (dynamic_cast (*sp.target)); if (st.mtime () != timestamp_nonexistent) - inject_prerequisites (o, st, sp.scope); + inject_prerequisites (a, o, st, sp.scope); return &update; } @@ -125,7 +125,7 @@ namespace build } void compile:: - inject_prerequisites (obj& o, const cxx& s, scope& ds) const + inject_prerequisites (action a, obj& o, const cxx& s, scope& ds) const { tracer trace ("cxx::compile::inject_prerequisites"); @@ -226,7 +226,7 @@ namespace build // Match to a rule. // - build::match (t); + build::match (a, t); } } @@ -251,13 +251,13 @@ namespace build } target_state compile:: - update (target& t) + update (action a, target& t) { obj& o (dynamic_cast (t)); - cxx* s (update_prerequisites (o, o.mtime ())); + cxx* s (execute_prerequisites (a, o, o.mtime ())); if (s == nullptr) - return target_state::uptodate; + return target_state::unchanged; // Translate paths to relative (to working directory) ones. This // results in easier to read diagnostics. @@ -293,7 +293,7 @@ namespace build // subseconds precision. // o.mtime (system_clock::now ()); - return target_state::updated; + return target_state::changed; } catch (const process_error& e) { @@ -313,7 +313,7 @@ namespace build // link // void* link:: - match (target& t, const string& hint) const + match (action a, target& t, const string& hint) const { tracer trace ("cxx::link::match"); @@ -371,7 +371,7 @@ namespace build } recipe link:: - apply (target& t, void*) const + apply (action a, target& t, void*) const { tracer trace ("cxx::link::apply"); @@ -400,7 +400,7 @@ namespace build if (p.target == nullptr) search (p); - build::match (*p.target); + build::match (a, *p.target); continue; } @@ -487,7 +487,7 @@ namespace build if (cp1 != nullptr) { - build::match (ot); // Now cp1 should be resolved. + build::match (a, ot); // Now cp1 should be resolved. if (cp.target == nullptr) search (cp); // Our own prerequisite, so this is ok. @@ -502,7 +502,7 @@ namespace build else { ot.prerequisites.push_back (cp); - build::match (ot); + build::match (a, ot); } // Change the exe{} target's prerequsite from cxx{} to obj{}. @@ -514,7 +514,7 @@ namespace build } target_state link:: - update (target& t) + update (action a, target& t) { // @@ Q: // @@ -523,8 +523,8 @@ namespace build exe& e (dynamic_cast (t)); - if (!update_prerequisites (e, e.mtime ())) - return target_state::uptodate; + if (!execute_prerequisites (a, e, e.mtime ())) + return target_state::unchanged; // Translate paths to relative (to working directory) ones. This // results in easier to read diagnostics. @@ -563,7 +563,7 @@ namespace build // subseconds precision. // e.mtime (system_clock::now ()); - return target_state::updated; + return target_state::changed; } catch (const process_error& e) { diff --git a/build/operation b/build/operation new file mode 100644 index 0000000..3398f32 --- /dev/null +++ b/build/operation @@ -0,0 +1,70 @@ +// file : build/operation -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD_OPERATION +#define BUILD_OPERATION + +#include +#include +#include + +#include // string_table + +namespace build +{ + // While we are using uint8_t for the meta/operation ids, we assume + // that each is limited to 4 bits (max 128 entries) so that we can + // store the combined action id in uint8_t as well. This makes our + // life easier when it comes to defining switch labels for action + // ids (no need to mess with endian-ness). + // + // Note that 0 is not a valid meta/operation/action id. + // + using meta_operation_id = std::uint8_t; + using operation_id = std::uint8_t; + using action_id = std::uint8_t; + + struct action + { + action (meta_operation_id m, operation_id o): id ((m << 4) | o) {} + + meta_operation_id + meta_operation () const {return id >> 4;} + + operation_id + operation () const {return id & 0xF;} + + // Implicit conversion operator to action_id for the switch() + // statement, etc. + // + operator action_id () const {return id;} + + action_id id; + }; + + std::ostream& + operator<< (std::ostream&, action); + + // Id constants for build-in operations. + // + const meta_operation_id perform_id = 1; + const meta_operation_id configure_id = 2; + const meta_operation_id disfigure_id = 3; + + const operation_id update_id = 1; + const operation_id clean_id = 2; + + const action_id perform_update_id = (perform_id << 4) | update_id; + const action_id perform_clean_id = (perform_id << 4) | clean_id; + + // Meta/operation id tables. + // + using meta_operation_table = string_table; + using operation_table = string_table; + + extern meta_operation_table meta_operations; + extern operation_table operations; +} + +#endif // BUILD_OPERATION diff --git a/build/operation.cxx b/build/operation.cxx new file mode 100644 index 0000000..0d90c95 --- /dev/null +++ b/build/operation.cxx @@ -0,0 +1,24 @@ +// file : build/operation.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include + +#include + +using namespace std; + +namespace build +{ + ostream& + operator<< (ostream& os, action a) + { + return os << '(' + << static_cast (a.meta_operation ()) << ',' + << static_cast (a.operation ()) + << ')'; + } + + meta_operation_table meta_operations; + operation_table operations; +} diff --git a/build/rule b/build/rule index 4336c8f..2e11191 100644 --- a/build/rule +++ b/build/rule @@ -11,6 +11,7 @@ #include #include +#include #include namespace build @@ -19,17 +20,20 @@ namespace build { public: virtual void* - match (target&, const std::string& hint) const = 0; + match (action, target&, const std::string& hint) const = 0; virtual recipe - apply (target&, void*) const = 0; + apply (action, target&, void*) const = 0; }; - typedef std::unordered_map< + using target_rule_map = std::unordered_map< std::type_index, - prefix_multimap, '.'>> rule_map; + prefix_multimap, '.'>>; - extern rule_map rules; + using operation_rule_map = std::unordered_map; + + extern operation_rule_map rules; + extern const target_rule_map* current_rules; // Rules for current operation. // Fallback rule that check that the path exists. // @@ -37,39 +41,39 @@ namespace build { public: virtual void* - match (target&, const std::string& hint) const; + match (action, target&, const std::string& hint) const; virtual recipe - apply (target&, void*) const; + apply (action, target&, void*) const; static target_state - update (target&); + update (action, target&); }; class dir_rule: public rule { public: virtual void* - match (target&, const std::string& hint) const; + match (action, target&, const std::string& hint) const; virtual recipe - apply (target&, void*) const; + apply (action, target&, void*) const; static target_state - update (target&); + update (action, target&); }; class fsdir_rule: public rule { public: virtual void* - match (target&, const std::string& hint) const; + match (action, target&, const std::string& hint) const; virtual recipe - apply (target&, void*) const; + apply (action, target&, void*) const; static target_state - update (target&); + update (action, target&); }; } diff --git a/build/rule.cxx b/build/rule.cxx index 41296f0..f193760 100644 --- a/build/rule.cxx +++ b/build/rule.cxx @@ -16,12 +16,13 @@ using namespace std; namespace build { - rule_map rules; + operation_rule_map rules; + const target_rule_map* current_rules; // path_rule // void* path_rule:: - match (target& t, const string&) const + match (action a, target& t, const string&) const { // @@ TODO: // @@ -55,17 +56,17 @@ namespace build } recipe path_rule:: - apply (target& t, void*) const + apply (action a, target& t, void*) const { // Search and match all the prerequisites. // - search_and_match (t); + search_and_match (a, t); return &update; } target_state path_rule:: - update (target& t) + update (action a, target& t) { // Make sure the target is not older than any of its prerequisites. // @@ -74,7 +75,7 @@ namespace build for (const prerequisite& p: t.prerequisites) { target& pt (*p.target); - target_state ts (update (pt)); + target_state ts (update (a, pt)); // If this is an mtime-based target, then compare timestamps. // @@ -89,52 +90,52 @@ namespace build } else { - // Otherwise we assume the prerequisite is newer if it was updated. + // Otherwise we assume the prerequisite is newer if it was changed. // - if (ts == target_state::updated) + if (ts == target_state::changed) fail << "no recipe to update target " << t << info << "prerequisite " << pt << " is ahead of " << t << " because it was updated"; } } - return target_state::uptodate; + return target_state::unchanged; } // dir_rule // void* dir_rule:: - match (target& t, const string&) const + match (action a, target& t, const string&) const { return &t; } recipe dir_rule:: - apply (target& t, void*) const + apply (action a, target& t, void*) const { - search_and_match (t); + search_and_match (a, t); return &update; } target_state dir_rule:: - update (target& t) + update (action a, target& t) { - // Return updated if any of our prerequsites were updated and - // uptodate otherwise. + // Return changed if any of our prerequsites were updated and + // unchanged otherwise. // - return update_prerequisites (t); + return execute_prerequisites (a, t); } // fsdir_rule // void* fsdir_rule:: - match (target& t, const string&) const + match (action a, target& t, const string&) const { return &t; } recipe fsdir_rule:: - apply (target& t, void*) const + apply (action a, target& t, void*) const { // Let's not allow any prerequisites for this target since it // doesn't make much sense. The sole purpose of this target type @@ -147,7 +148,7 @@ namespace build } target_state fsdir_rule:: - update (target& t) + update (action a, target& t) { path d (t.dir / path (t.name)); @@ -160,7 +161,7 @@ namespace build } if (path_mtime (d) != timestamp_nonexistent) - return target_state::uptodate; + return target_state::unchanged; if (verb >= 1) text << "mkdir " << d.string (); @@ -177,6 +178,6 @@ namespace build << e.what (); } - return target_state::updated; + return target_state::changed; } } diff --git a/build/target b/build/target index 9c4640e..a49c57f 100644 --- a/build/target +++ b/build/target @@ -19,6 +19,7 @@ #include #include #include +#include #include #include // compare_*, extension_pool @@ -26,11 +27,11 @@ namespace build { class target; - enum class target_state {unknown, uptodate, updated, failed}; + enum class target_state {unknown, unchanged, changed, failed}; // Note: should throw rather than returning target_state::failed. // - typedef std::function recipe; + typedef std::function recipe; struct target_type { @@ -71,10 +72,19 @@ namespace build typedef build::recipe recipe_type; const recipe_type& - recipe () const {return recipe_;} + recipe (action_id a) const {return action_ == a ? recipe_ : empty_recipe_;} void - recipe (recipe_type r) {assert (!recipe_); recipe_ = r;} + recipe (action_id a, recipe_type r) + { + assert (action_ != a || !recipe_); + action_ = a; + recipe_ = r; + + // Also reset the target state. + // + state_ = target_state::unknown; + } public: target_state @@ -92,7 +102,9 @@ namespace build static const target_type static_type; private: + action_id action_ {0}; // Action id of this target recipe. recipe_type recipe_; + static const recipe_type empty_recipe_; target_state state_ {target_state::unknown}; }; diff --git a/build/target.cxx b/build/target.cxx index f09a858..5cf131f 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -15,6 +15,8 @@ namespace build { // target // + const recipe target::empty_recipe_; + ostream& operator<< (ostream& os, const target& t) { diff --git a/build/utility b/build/utility index fd7888c..93fb441 100644 --- a/build/utility +++ b/build/utility @@ -6,11 +6,15 @@ #define BUILD_UTILITY #include +#include // numeric_limits #include +#include #include -#include // strcmp +#include // strcmp() +#include #include #include +#include #include @@ -94,6 +98,53 @@ namespace build }; extern string_pool extension_pool; + + // A pool of strings in which each string is assigned an individual + // index (or id) of type I (e.g., uint8_t, uint16_t, etc., depending + // on how many entries are expected). Index value 0 is reserved to + // indicate the no entry condition. + // + template + struct string_table + { + // Find existing or insert new. + // + I + insert (const std::string& s) + { + std::size_t i (vec_.size () + 1); + auto r (map_.emplace (s, static_cast (i))); + + if (r.second) + { + assert (i <= std::numeric_limits::max ()); + vec_.push_back (&r.first->first); + } + + return r.first->second; + } + + // Find existing. + // + I + find (const std::string& s) const + { + auto i (map_.find (s)); + return i != map_.end () ? i->second : 0; + } + + // Reverse lookup. + // + const std::string& + operator[] (I i) const {assert (i > 0); return *vec_[i - 1];} + + I + size () const {return static_cast (vec_.size ());} + + private: + std::unordered_map map_; + std::vector vec_; + }; } #endif // BUILD_UTILITY -- cgit v1.1