diff options
37 files changed, 465 insertions, 356 deletions
diff --git a/build2/algorithm b/build2/algorithm index 09e0c4d..5bfa1e0 100644 --- a/build2/algorithm +++ b/build2/algorithm @@ -71,7 +71,7 @@ namespace build2 // Note that currently we return NULL for project-qualified names and // unknown target types. // - target* + const target* search_existing (const name&, const scope&, const dir_path& out = dir_path ()); @@ -152,7 +152,7 @@ namespace build2 // executor implementation. Decrements the dependents count. // target_state - execute (action, target&); + execute (action, const target&); // Execute the recipe obtained with match_delegate(). Note that // the target's state is neither checked nor updated by this @@ -161,7 +161,7 @@ namespace build2 // state into the one returned. // target_state - execute_delegate (const recipe&, action, target&); + execute_delegate (const recipe&, action, const target&); // A special version of the above that should be used for "direct" // and "now" execution, that is, side-stepping the normal target- @@ -171,7 +171,7 @@ namespace build2 // is busy. // target_state - execute_direct (action, target&); + execute_direct (action, const target&); // The default prerequisite execute implementation. It calls execute() // on each non-ignored (non-NULL) prerequisite target in a loop. If this @@ -181,12 +181,12 @@ namespace build2 // function can be used as a recipe. // target_state - execute_prerequisites (action, target&); + execute_prerequisites (action, const target&); // As above but iterates over the prerequisites in reverse. // target_state - reverse_execute_prerequisites (action, target&); + reverse_execute_prerequisites (action, const target&); // A version of the above that also determines whether the action needs to // be executed on the target based on the passed timestamp and filter. @@ -207,10 +207,10 @@ namespace build2 // Note that because we use mtime, this function should normally only be // used in the perform_update action. // - using prerequisite_filter = function<bool (target&)>; + using prerequisite_filter = function<bool (const target&)>; pair<bool, target_state> - execute_prerequisites (action, target&, + execute_prerequisites (action, const target&, const timestamp&, const prerequisite_filter& = nullptr); @@ -222,45 +222,45 @@ namespace build2 // prerequisites of the same type may get injected). // template <typename T> - pair<T*, target_state> - execute_prerequisites (action, target&, + pair<const T*, target_state> + execute_prerequisites (action, const target&, const timestamp&, const prerequisite_filter& = nullptr); - pair<target*, target_state> + pair<const target*, target_state> execute_prerequisites (const target_type&, - action, target&, + action, const target&, const timestamp&, const prerequisite_filter& = nullptr); template <typename T> - pair<T*, target_state> + pair<const T*, target_state> execute_prerequisites (const target_type&, - action, target&, + action, const target&, const timestamp&, const prerequisite_filter& = nullptr); // Return noop_recipe instead of using this function directly. // target_state - noop_action (action, target&); + noop_action (action, const target&); // Default action implementation which forwards to the prerequisites. // Use default_recipe instead of using this function directly. // target_state - default_action (action, target&); + default_action (action, const target&); // Standard perform(clean) action implementation for the file target // (or derived). // target_state - perform_clean (action, target&); + perform_clean (action, const target&); // As above, but also removes the auxiliary dependency database (.d file). // target_state - perform_clean_depdb (action, target&); + perform_clean_depdb (action, const target&); // Helper for custom perform(clean) implementations that cleans extra files // and directories (recursively) specified as a list of either absolute @@ -281,11 +281,11 @@ namespace build2 // You can also clean extra files derived from adhoc group members. // target_state - clean_extra (action, file&, + clean_extra (action, const file&, initializer_list<initializer_list<const char*>> extra); inline target_state - clean_extra (action a, file& f, initializer_list<const char*> extra) + clean_extra (action a, const file& f, initializer_list<const char*> extra) { return clean_extra (a, f, {extra}); } diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx index f173af6..74e7d7d 100644 --- a/build2/algorithm.cxx +++ b/build2/algorithm.cxx @@ -63,7 +63,7 @@ namespace build2 n.proj); } - target* + const target* search_existing (const name& cn, const scope& s, const dir_path& out) { assert (phase == run_phase::search_match || phase == run_phase::execute); @@ -422,8 +422,10 @@ namespace build2 } target_state - execute (action a, target& t) + execute (action a, const target& ct) { + target& t (const_cast<target&> (ct)); // MT-aware. + // text << "E " << t << ": " << t.dependents << " " << dependency_count; size_t d (0); @@ -494,8 +496,10 @@ namespace build2 } target_state - execute_direct (action a, target& t) + execute_direct (action a, const target& ct) { + target& t (const_cast<target&> (ct)); // MT-aware. + size_t tc (target::count_unexecuted); if (t.task_count.compare_exchange_strong (tc, target::count_executing)) return execute_impl (a, t); @@ -515,11 +519,11 @@ namespace build2 } target_state - execute_prerequisites (action a, target& t) + execute_prerequisites (action a, const target& t) { target_state r (target_state::unchanged); - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { if (pt == nullptr) // Skipped. continue; @@ -531,11 +535,11 @@ namespace build2 } target_state - reverse_execute_prerequisites (action a, target& t) + reverse_execute_prerequisites (action a, const target& t) { target_state r (target_state::unchanged); - for (target* pt: reverse_iterate (t.prerequisite_targets)) + for (const target* pt: reverse_iterate (t.prerequisite_targets)) { if (pt == nullptr) // Skipped. continue; @@ -546,17 +550,17 @@ namespace build2 return r; } - pair<target*, target_state> + pair<const target*, target_state> execute_prerequisites (const target_type* tt, - action a, target& t, + action a, const target& t, const timestamp& mt, const prerequisite_filter& pf) { bool e (mt == timestamp_nonexistent); - target* rt (tt != nullptr ? nullptr : &t); + const target* rt (tt != nullptr ? nullptr : &t); target_state rs (target_state::unchanged); - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { if (pt == nullptr) // Skip ignored. continue; @@ -598,7 +602,7 @@ namespace build2 } target_state - noop_action (action a, target& t) + noop_action (action a, const target& t) { text << "noop action triggered for " << diag_doing (a, t); assert (false); // We shouldn't be called, see target::recipe(). @@ -606,12 +610,13 @@ namespace build2 } target_state - group_action (action a, target& t) + group_action (action a, const target& t) { // If the group is busy, we wait, similar to prerequisites. // - if (execute (a, *t.group) == target_state::busy) - sched.wait (target::count_executed, t.group->task_count); + const target& g (*t.group); + if (execute (a, g) == target_state::busy) + sched.wait (target::count_executed, g.task_count); // Indicate to execute() that this target's state comes from the group. // @@ -619,7 +624,7 @@ namespace build2 } target_state - default_action (action a, target& t) + default_action (action a, const target& t) { return current_mode == execution_mode::first ? execute_prerequisites (a, t) @@ -628,7 +633,7 @@ namespace build2 target_state clean_extra (action a, - file& ft, + const file& ft, initializer_list<initializer_list<const char*>> extra) { // Clean the extras first and don't print the commands at verbosity level @@ -639,7 +644,8 @@ namespace build2 bool ed (false); path ep; - auto clean = [&er, &ed, &ep] (file& f, initializer_list<const char*> es) + auto clean = [&er, &ed, &ep] (const file& f, + initializer_list<const char*> es) { for (const char* e: es) { @@ -713,9 +719,9 @@ namespace build2 // Now clean the ad hoc group file members, if any. // - for (target* m (ft.member); m != nullptr; m = m->member) + for (const target* m (ft.member); m != nullptr; m = m->member) { - file* fm (dynamic_cast<file*> (m)); + const file* fm (dynamic_cast<const file*> (m)); if (fm == nullptr || fm->path ().empty ()) continue; @@ -776,14 +782,14 @@ namespace build2 } target_state - perform_clean (action a, target& t) + perform_clean (action a, const target& t) { - return clean_extra (a, dynamic_cast<file&> (t), {nullptr}); + return clean_extra (a, dynamic_cast<const file&> (t), {nullptr}); } target_state - perform_clean_depdb (action a, target& t) + perform_clean_depdb (action a, const target& t) { - return clean_extra (a, dynamic_cast<file&> (t), {".d"}); + return clean_extra (a, dynamic_cast<const file&> (t), {".d"}); } } diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx index cf234d8..0c171fd 100644 --- a/build2/algorithm.ixx +++ b/build2/algorithm.ixx @@ -173,7 +173,7 @@ namespace build2 } inline target_state - execute_delegate (const recipe& r, action a, target& t) + execute_delegate (const recipe& r, action a, const target& t) { return r (a, t); } @@ -181,13 +181,13 @@ namespace build2 // If the first argument is NULL, then the result is treated as a boolean // value. // - pair<target*, target_state> + pair<const target*, target_state> execute_prerequisites (const target_type*, - action, target&, + action, const target&, const timestamp&, const prerequisite_filter&); inline pair<bool, target_state> - execute_prerequisites (action a, target& t, + execute_prerequisites (action a, const target& t, const timestamp& mt, const prerequisite_filter& pf) { auto p (execute_prerequisites (nullptr, a, t, mt, pf)); @@ -195,29 +195,29 @@ namespace build2 } template <typename T> - inline pair<T*, target_state> - execute_prerequisites (action a, target& t, + inline pair<const T*, target_state> + execute_prerequisites (action a, const target& t, const timestamp& mt, const prerequisite_filter& pf) { auto p (execute_prerequisites (T::static_type, a, t, mt, pf)); - return make_pair (static_cast<T*> (p.first), p.second); + return make_pair (static_cast<const T*> (p.first), p.second); } - inline pair<target*, target_state> + inline pair<const target*, target_state> execute_prerequisites (const target_type& tt, - action a, target& t, + action a, const target& t, const timestamp& mt, const prerequisite_filter& pf) { return execute_prerequisites (&tt, a, t, mt, pf); } template <typename T> - inline pair<T*, target_state> + inline pair<const T*, target_state> execute_prerequisites (const target_type& tt, - action a, target& t, + action a, const target& t, const timestamp& mt, const prerequisite_filter& pf) { auto p (execute_prerequisites (tt, a, t, mt, pf)); - return make_pair (static_cast<T*> (p.first), p.second); + return make_pair (static_cast<const T*> (p.first), p.second); } } diff --git a/build2/bin/rule b/build2/bin/rule index c903990..5fe9069 100644 --- a/build2/bin/rule +++ b/build2/bin/rule @@ -38,7 +38,7 @@ namespace build2 apply (slock&, action, target&) const override; static target_state - perform (action, target&); + perform (action, const target&); }; } } diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx index c9e8b3e..a73ad3d 100644 --- a/build2/bin/rule.cxx +++ b/build2/bin/rule.cxx @@ -124,9 +124,9 @@ namespace build2 } target_state lib_rule:: - perform (action act, target& xt) + perform (action act, const target& xt) { - lib& t (static_cast<lib&> (xt)); + const lib& t (static_cast<const lib&> (xt)); const match_data& md (t.data<match_data> ()); const string& type (md.type); diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx index ba15c7d..caef8e0 100644 --- a/build2/bin/target.cxx +++ b/build2/bin/target.cxx @@ -22,7 +22,7 @@ namespace build2 obj* o (targets.find<obj> (dir, out, n)); obje* e (new obje (move (dir), move (out), move (n))); - if ((e->group = o)) + if ((e->group = o) != nullptr) o->e = e; return make_pair (e, move (ext)); @@ -49,7 +49,7 @@ namespace build2 obj* o (targets.find<obj> (dir, out, n)); obja* a (new obja (move (dir), move (out), move (n))); - if ((a->group = o)) + if ((a->group = o) != nullptr) o->a = a; return make_pair (a, move (ext)); @@ -76,7 +76,7 @@ namespace build2 obj* o (targets.find<obj> (dir, out, n)); objs* s (new objs (move (dir), move (out), move (n))); - if ((s->group = o)) + if ((s->group = o) != nullptr) o->s = s; return make_pair (s, move (ext)); @@ -106,13 +106,13 @@ namespace build2 obj* o (new obj (move (dir), move (out), move (n))); - if ((o->e = e)) + if ((o->e = e) != nullptr) e->group = o; - if ((o->a = a)) + if ((o->a = a)!= nullptr) a->group = o; - if ((o->s = s)) + if ((o->s = s)!= nullptr) s->group = o; return make_pair (o, move (ext)); @@ -141,7 +141,7 @@ namespace build2 lib* l (t == liba::static_type ? targets.find<lib> (d, o, n) : nullptr); liba* a (new liba (move (d), move (o), move (n))); - if ((a->group = l)) + if ((a->group = l) != nullptr) l->a = a; return make_pair (a, move (ext)); @@ -182,7 +182,7 @@ namespace build2 lib* l (t == libs::static_type ? targets.find<lib> (d, o, n) : nullptr); libs* s (new libs (move (d), move (o), move (n))); - if ((s->group = l)) + if ((s->group = l) != nullptr) l->s = s; return make_pair (s, move (ext)); @@ -222,10 +222,10 @@ namespace build2 lib* l (new lib (move (d), move (o), move (n))); - if ((l->a = a)) + if ((l->a = a) != nullptr) a->group = l; - if ((l->s = s)) + if ((l->s = s) != nullptr) s->group = l; return make_pair (l, move (ext)); diff --git a/build2/cc/common b/build2/cc/common index 06df4c3..860913e 100644 --- a/build2/cc/common +++ b/build2/cc/common @@ -184,18 +184,16 @@ namespace build2 // public: void - process_libraries (const scope&, - lorder, - const dir_paths&, - file&, - bool, - const function<bool (file&, bool)>&, - const function<void (file*, const string&, bool)>&, - const function<void (file&, - const string&, - bool, - bool)>&, - bool = false) const; + process_libraries ( + const scope&, + lorder, + const dir_paths&, + const file&, + bool, + const function<bool (const file&, bool)>&, + const function<void (const file*, const string&, bool)>&, + const function<void (const file&, const string&, bool, bool)>&, + bool = false) const; target* search_library (const dir_paths& sysd, @@ -209,7 +207,7 @@ namespace build2 } private: - file& + const file& resolve_library (const scope&, name, lorder, diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx index e7b26a1..62a4ab2 100644 --- a/build2/cc/common.cxx +++ b/build2/cc/common.cxx @@ -49,14 +49,14 @@ namespace build2 const scope& top_bs, lorder top_lo, const dir_paths& top_sysd, - file& l, + const file& l, bool la, - const function<bool (file&, + const function<bool (const file&, bool la)>& proc_impl, // Implementation? - const function<void (file*, // Can be NULL. + const function<void (const file*, // Can be NULL. const string& path, // Library path. bool sys)>& proc_lib, // True if system library. - const function<void (file&, + const function<void (const file&, const string& type, // cc.type bool com, // cc. or x. bool exp)>& proc_opt, // *.export. @@ -213,10 +213,10 @@ namespace build2 // if (impl && !c_e_libs.defined () && !x_e_libs.defined ()) { - for (target* p: l.prerequisite_targets) + for (const target* p: l.prerequisite_targets) { bool a; - file* f; + const file* f; if ((a = (f = p->is_a<liba> ()) != nullptr) || (f = p->is_a<libs> ()) != nullptr) @@ -287,7 +287,7 @@ namespace build2 if (sysd == nullptr) find_sysd (); if (!lo) find_lo (); - file& t (resolve_library (bs, n, *lo, *sysd, usrd)); + const file& t (resolve_library (bs, n, *lo, *sysd, usrd)); if (proc_lib) { @@ -385,7 +385,7 @@ namespace build2 // will select exactly the same target as the library's matched rule and // that's the only way to guarantee it will be up-to-date. // - file& common:: + const file& common:: resolve_library (const scope& s, name n, lorder lo, @@ -395,7 +395,7 @@ namespace build2 if (n.type != "lib" && n.type != "liba" && n.type != "libs") fail << "target name " << n << " is not a library"; - target* xt (nullptr); + const target* xt (nullptr); if (n.dir.absolute () && !n.qualified ()) { @@ -422,12 +422,12 @@ namespace build2 // dir_path out; prerequisite_key pk {n.proj, {tt, &n.dir, &out, &n.value, ext}, &s}; - xt = search_library (sysd, usrd, pk); + xt = search_library (sysd, usrd, pk); //@@ TM const if (xt == nullptr) { if (n.qualified ()) - xt = &import (pk); + xt = &import (pk); //@@ TM const else fail << "unable to find library " << pk; } @@ -435,10 +435,10 @@ namespace build2 // If this is lib{}, pick appropriate member. // - if (lib* l = xt->is_a<lib> ()) + if (const lib* l = xt->is_a<lib> ()) xt = &link_member (*l, lo); // Pick liba{} or libs{}. - return static_cast<file&> (*xt); + return static_cast<const file&> (*xt); } // Note that pk's scope should not be NULL (even if dir is absolute). If diff --git a/build2/cc/compile b/build2/cc/compile index b6fc7d6..63ce286 100644 --- a/build2/cc/compile +++ b/build2/cc/compile @@ -33,17 +33,23 @@ namespace build2 apply (slock&, action, target&) const override; target_state - perform_update (action, target&) const; + perform_update (action, const target&) const; target_state - perform_clean (action, target&) const; + perform_clean (action, const target&) const; private: void - append_lib_options (const scope&, cstrings&, target&, lorder) const; + append_lib_options (const scope&, + cstrings&, + const target&, + lorder) const; void - hash_lib_options (const scope&, sha256&, target&, lorder) const; + hash_lib_options (const scope&, + sha256&, + const target&, + lorder) const; // Mapping of include prefixes (e.g., foo in <foo/bar>) for auto- // generated headers to directories where they will be generated. @@ -58,7 +64,7 @@ namespace build2 using prefix_map = butl::dir_path_map<dir_path>; void - append_prefixes (prefix_map&, target&, const variable&) const; + append_prefixes (prefix_map&, const target&, const variable&) const; void append_lib_prefixes (const scope&, prefix_map&, target&, lorder) const; diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx index 66ea9bd..98d1218 100644 --- a/build2/cc/compile.cxx +++ b/build2/cc/compile.cxx @@ -78,10 +78,11 @@ namespace build2 void compile:: append_lib_options (const scope& bs, cstrings& args, - target& t, + const target& t, lorder lo) const { - auto opt = [&args, this] (file& l, const string& t, bool com, bool exp) + auto opt = [&args, this] ( + const file& l, const string& t, bool com, bool exp) { // Note that in our model *.export.poptions are always "interface", // even if set on liba{}/libs{}, unlike loptions. @@ -98,31 +99,35 @@ namespace build2 // In case we don't have the "small function object" optimization. // - const function<void (file&, const string&, bool, bool)> optf (opt); + const function<void (const file&, const string&, bool, bool)> optf (opt); // Note that here we don't need to see group members (see apply()). // - for (prerequisite& p: group_prerequisites (t)) + for (const prerequisite& p: const_group_prerequisites (t)) { - target* pt (p.target); // Already searched and matched. + const target* pt (p.target); // Already searched and matched. bool a; - if (lib* l = pt->is_a<lib> ()) + if (const lib* l = pt->is_a<lib> ()) a = (pt = &link_member (*l, lo))->is_a<liba> (); else if (!(a = pt->is_a<liba> ()) && !pt->is_a<libs> ()) continue; process_libraries (bs, lo, sys_lib_dirs, - static_cast<file&> (*pt), a, + static_cast<const file&> (*pt), a, nullptr, nullptr, optf); } } void compile:: - hash_lib_options (const scope& bs, sha256& cs, target& t, lorder lo) const + hash_lib_options (const scope& bs, + sha256& cs, + const target& t, + lorder lo) const { - auto opt = [&cs, this] (file& l, const string& t, bool com, bool exp) + auto opt = [&cs, this] ( + const file& l, const string& t, bool com, bool exp) { assert (exp); @@ -136,21 +141,21 @@ namespace build2 // In case we don't have the "small function object" optimization. // - const function<void (file&, const string&, bool, bool)> optf (opt); + const function<void (const file&, const string&, bool, bool)> optf (opt); - for (prerequisite& p: group_prerequisites (t)) + for (const prerequisite& p: const_group_prerequisites (t)) { - target* pt (p.target); // Already searched and matched. + const target* pt (p.target); // Already searched and matched. bool a; - if (lib* l = pt->is_a<lib> ()) + if (const lib* l = pt->is_a<lib> ()) a = (pt = &link_member (*l, lo))->is_a<liba> (); else if (!(a = pt->is_a<liba> ()) && !pt->is_a<libs> ()) continue; process_libraries (bs, lo, sys_lib_dirs, - static_cast<file&> (*pt), a, + static_cast<const file&> (*pt), a, nullptr, nullptr, optf); } } @@ -164,7 +169,8 @@ namespace build2 target& t, lorder lo) const { - auto opt = [&m, this] (file& l, const string& t, bool com, bool exp) + auto opt = [&m, this] ( + const file& l, const string& t, bool com, bool exp) { assert (exp); @@ -178,7 +184,7 @@ namespace build2 // In case we don't have the "small function object" optimization. // - const function<void (file&, const string&, bool, bool)> optf (opt); + const function<void (const file&, const string&, bool, bool)> optf (opt); for (prerequisite& p: group_prerequisites (t)) { @@ -192,7 +198,7 @@ namespace build2 continue; process_libraries (bs, lo, sys_lib_dirs, - static_cast<file&> (*pt), a, + static_cast<const file&> (*pt), a, nullptr, nullptr, optf); } } @@ -399,12 +405,15 @@ namespace build2 switch (a) { - case perform_update_id: - return [this] (action a, target& t) {return perform_update (a, t);}; - case perform_clean_id: - return [this] (action a, target& t) {return perform_clean (a, t);}; - default: - return noop_recipe; // Configure update. + case perform_update_id: return [this] (action a, const target& t) + { + return perform_update (a, t); + }; + case perform_clean_id: return [this] (action a, const target& t) + { + return perform_clean (a, t); + }; + default: return noop_recipe; // Configure update. } } @@ -440,7 +449,7 @@ namespace build2 } void compile:: - append_prefixes (prefix_map& m, target& t, const variable& var) const + append_prefixes (prefix_map& m, const target& t, const variable& var) const { tracer trace (x, "append_prefixes"); @@ -1394,15 +1403,15 @@ namespace build2 msvc_filter_cl (ifdstream&, const path& src); target_state compile:: - perform_update (action a, target& xt) const + perform_update (action a, const target& xt) const { - file& t (static_cast<file&> (xt)); + const file& t (static_cast<const file&> (xt)); // Update prerequisites and determine if any relevant ones render us // out-of-date. Note that currently we treat all the prerequisites // as potentially affecting the result (for simplicity/performance). // - file* s; + const file* s; { auto p (execute_prerequisites<file> (x_src, a, t, t.mtime ())); @@ -1621,9 +1630,9 @@ namespace build2 } target_state compile:: - perform_clean (action a, target& xt) const + perform_clean (action a, const target& xt) const { - file& t (static_cast<file&> (xt)); + const file& t (static_cast<const file&> (xt)); if (cid == "msvc") return clean_extra (a, t, {".d", ".idb", ".pdb"}); diff --git a/build2/cc/install b/build2/cc/install index ff7af4d..f676d72 100644 --- a/build2/cc/install +++ b/build2/cc/install @@ -30,11 +30,14 @@ namespace build2 virtual match_result match (slock&, action, target&, const string&) const override; + virtual recipe + apply (slock&, action, target&) const override; + virtual void - install_extra (file&, const install_dir&) const override; + install_extra (const file&, const install_dir&) const override; virtual bool - uninstall_extra (file&, const install_dir&) const override; + uninstall_extra (const file&, const install_dir&) const override; private: const link& link_; diff --git a/build2/cc/install.cxx b/build2/cc/install.cxx index 074654b..f022a92 100644 --- a/build2/cc/install.cxx +++ b/build2/cc/install.cxx @@ -66,17 +66,35 @@ namespace build2 // ones building this target. So first run link's match(). // match_result r (link_.match (ml, a, t, hint)); - return r ? install::file_rule::match (ml, a, t, "") : r; + return r ? file_rule::match (ml, a, t, "") : r; + } + + recipe install:: + apply (slock& s, action a, target& t) const + { + recipe r (file_rule::apply (s, a, t)); + + // Derive shared library paths and cache them in the target's aux + // storage if we are (un)installing (used in *_extra() functions below). + // + if (a.operation () == install_id || a.operation () == uninstall_id) + { + file* f; + if ((f = t.is_a<libs> ()) != nullptr && tclass != "windows") + t.data (link_.derive_libs_paths (*f)); + } + + return r; } void install:: - install_extra (file& t, const install_dir& id) const + install_extra (const file& t, const install_dir& id) const { if (t.is_a<libs> () && tclass != "windows") { // Here we may have a bunch of symlinks that we need to install. // - link::libs_paths lp (link_.derive_libs_paths (t)); + auto& lp (t.data<link::libs_paths> ()); auto ln = [&id, this] (const path& f, const path& l) { @@ -87,7 +105,7 @@ namespace build2 const path& so (lp.soname); const path& in (lp.interm); - const path* f (lp.real); + const path* f (&lp.real); if (!in.empty ()) {ln (*f, in); f = ∈} if (!so.empty ()) {ln (*f, so); f = &so;} @@ -96,7 +114,7 @@ namespace build2 } bool install:: - uninstall_extra (file& t, const install_dir& id) const + uninstall_extra (const file& t, const install_dir& id) const { bool r (false); @@ -104,7 +122,7 @@ namespace build2 { // Here we may have a bunch of symlinks that we need to uninstall. // - link::libs_paths lp (link_.derive_libs_paths (t)); + auto& lp (t.data<link::libs_paths> ()); auto rm = [&id, this] (const path& l) { diff --git a/build2/cc/link b/build2/cc/link index d1b0e7a..c787015 100644 --- a/build2/cc/link +++ b/build2/cc/link @@ -31,10 +31,10 @@ namespace build2 apply (slock&, action, target&) const override; target_state - perform_update (action, target&) const; + perform_update (action, const target&) const; target_state - perform_clean (action, target&) const; + perform_clean (action, const target&) const; private: friend class install; @@ -50,19 +50,16 @@ namespace build2 // The libs{} path is always the real path. On Windows the link path // is the import library. // - // @@ TODO: change real to reference, make other const once cache the - // object. - // - path link; // What we link: libfoo.so - path soname; // SONAME: libfoo-1.so, libfoo.so.1 - path interm; // Intermediate: libfoo.so.1.2 - const path* real; // Real: libfoo.so.1.2.3 + const path link; // What we link: libfoo.so + const path soname; // SONAME: libfoo-1.so, libfoo.so.1 + const path interm; // Intermediate: libfoo.so.1.2 + const path& real; // Real: libfoo.so.1.2.3 inline const path& effect_link () const {return link.empty () ? effect_soname () : link;} inline const path& - effect_soname () const {return soname.empty () ? *real : soname;} + effect_soname () const {return soname.empty () ? real : soname;} }; libs_paths @@ -71,13 +68,21 @@ namespace build2 // Library handling. // void - append_libraries (strings&, file&, bool, const scope&, lorder) const; + append_libraries (strings&, + const file&, bool, + const scope&, lorder) const; void - hash_libraries (sha256&, file&, bool, const scope&, lorder) const; + hash_libraries (sha256&, + const file&, bool, + const scope&, lorder) const; void - rpath_libraries (strings&, target&, const scope&, lorder, bool) const; + rpath_libraries (strings&, + const target&, + const scope&, + lorder, + bool) const; // Windows rpath emulation (windows-rpath.cxx). // @@ -93,13 +98,13 @@ namespace build2 using windows_dlls = std::set<windows_dll>; timestamp - windows_rpath_timestamp (file&, const scope&, lorder) const; + windows_rpath_timestamp (const file&, const scope&, lorder) const; windows_dlls - windows_rpath_dlls (file&, const scope&, lorder) const; + windows_rpath_dlls (const file&, const scope&, lorder) const; void - windows_rpath_assembly (file&, const scope&, lorder, + windows_rpath_assembly (const file&, const scope&, lorder, const string&, timestamp, bool) const; @@ -107,7 +112,7 @@ namespace build2 // Windows-specific (windows-manifest.cxx). // path - windows_manifest (file&, bool rpath_assembly) const; + windows_manifest (const file&, bool rpath_assembly) const; private: const string rule_id; diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index 83a7c0d..7c946aa 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -266,7 +266,6 @@ namespace build2 // Now determine the paths. // path lk, so, in; - const path* re (nullptr); // We start with the basic path. // @@ -310,7 +309,7 @@ namespace build2 if (!v.empty ()) b += v; - re = &ls.derive_path (move (b)); + const path& re (ls.derive_path (move (b))); return libs_paths {move (lk), move (so), move (in), re}; } @@ -318,6 +317,9 @@ namespace build2 recipe link:: apply (slock& ml, action a, target& xt) const { + static_assert (sizeof (link::libs_paths) <= target::data_size, + "insufficient space"); + tracer trace (x, "link::apply"); file& t (static_cast<file&> (xt)); @@ -388,7 +390,7 @@ namespace build2 if (tclass == "windows") add_adhoc (t, "libi"); - derive_libs_paths (t); + t.data (derive_libs_paths (t)); // Cache in target. break; } } @@ -645,18 +647,21 @@ namespace build2 switch (a) { - case perform_update_id: - return [this] (action a, target& t) {return perform_update (a, t);}; - case perform_clean_id: - return [this] (action a, target& t) {return perform_clean (a, t);}; - default: - return noop_recipe; // Configure update. + case perform_update_id: return [this] (action a, const target& t) + { + return perform_update (a, t); + }; + case perform_clean_id: return [this] (action a, const target& t) + { + return perform_clean (a, t); + }; + default: return noop_recipe; // Configure update. } } void link:: append_libraries (strings& args, - file& l, bool la, + const file& l, bool la, const scope& bs, lorder lo) const { // Note: lack of the "small function object" optimization will really @@ -664,9 +669,9 @@ namespace build2 // bool win (tclass == "windows"); - auto imp = [] (file&, bool la) {return la;}; + auto imp = [] (const file&, bool la) {return la;}; - auto lib = [&args, win] (file* f, const string& p, bool) + auto lib = [&args, win] (const file* f, const string& p, bool) { if (f != nullptr) { @@ -675,7 +680,7 @@ namespace build2 // (see search_library() for details). // if (win && f->member != nullptr && f->is_a<libs> ()) - f = static_cast<file*> (f->member); + f = static_cast<const file*> (f->member.get ()); args.push_back (relative (f->path ()).string ()); } @@ -683,11 +688,12 @@ namespace build2 args.push_back (p); }; - auto opt = [&args, this] (file& l, const string& t, bool com, bool exp) + auto opt = [&args, this] ( + const file& l, const string& t, bool com, bool exp) { // If we need an interface value, then use the group (lib{}). // - if (target* g = exp && l.is_a<libs> () ? l.group : &l) + if (const target* g = exp && l.is_a<libs> () ? l.group : &l) { const variable& var ( com @@ -705,16 +711,14 @@ namespace build2 void link:: hash_libraries (sha256& cs, - file& l, - bool la, - const scope& bs, - lorder lo) const + const file& l, bool la, + const scope& bs, lorder lo) const { bool win (tclass == "windows"); - auto imp = [] (file&, bool la) {return la;}; + auto imp = [] (const file&, bool la) {return la;}; - auto lib = [&cs, win] (file* f, const string& p, bool) + auto lib = [&cs, win] (const file* f, const string& p, bool) { if (f != nullptr) { @@ -723,7 +727,7 @@ namespace build2 // (see search_library() for details). // if (win && f->member != nullptr && f->is_a<libs> ()) - f = static_cast<file*> (f->member); + f = static_cast<const file*> (f->member.get ()); cs.append (f->path ().string ()); } @@ -731,9 +735,10 @@ namespace build2 cs.append (p); }; - auto opt = [&cs, this] (file& l, const string& t, bool com, bool exp) + auto opt = [&cs, this] ( + const file& l, const string& t, bool com, bool exp) { - if (target* g = exp && l.is_a<libs> () ? l.group : &l) + if (const target* g = exp && l.is_a<libs> () ? l.group : &l) { const variable& var ( com @@ -751,7 +756,7 @@ namespace build2 void link:: rpath_libraries (strings& args, - target& t, + const target& t, const scope& bs, lorder lo, bool for_install) const @@ -765,7 +770,7 @@ namespace build2 return; } - auto imp = [for_install] (file&, bool la) + auto imp = [for_install] (const file&, bool la) { // If we are not installing, then we only need to rpath interface // libraries (they will include rpath's for their implementations). @@ -791,7 +796,7 @@ namespace build2 bool for_install; } d {args, for_install}; - auto lib = [&d, this] (file* l, const string& f, bool sys) + auto lib = [&d, this] (const file* l, const string& f, bool sys) { // We don't rpath system libraries. Why, you may ask? There are many // good reasons and I have them written on an napkin somewhere... @@ -845,13 +850,13 @@ namespace build2 // In case we don't have the "small function object" optimization. // - const function<bool (file&, bool)> impf (imp); - const function<void (file*, const string&, bool)> libf (lib); + const function<bool (const file&, bool)> impf (imp); + const function<void (const file*, const string&, bool)> libf (lib); - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { - file* f; - liba* a; + const file* f; + const liba* a; if ((f = a = pt->is_a<liba> ()) || (f = pt->is_a<libs> ())) @@ -884,14 +889,14 @@ namespace build2 msvc_machine (const string& cpu); // msvc.cxx target_state link:: - perform_update (action a, target& xt) const + perform_update (action a, const target& xt) const { tracer trace (x, "link::perform_update"); auto oop (a.outer_operation ()); bool for_install (oop == install_id || oop == uninstall_id); - file& t (static_cast<file&> (xt)); + const file& t (static_cast<const file&> (xt)); const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); @@ -1071,10 +1076,6 @@ namespace build2 // cstrings args {nullptr}; // Reserve one for config.bin.ar/config.x. - libs_paths paths; - if (lt == otype::s) - paths = derive_libs_paths (t); - // Storage. // string soname1, soname2; @@ -1127,6 +1128,7 @@ namespace build2 // if (lt == otype::s) { + const libs_paths& paths (t.data<libs_paths> ()); const string& leaf (paths.effect_soname ().leaf ().string ()); if (tclass == "macosx") @@ -1195,11 +1197,11 @@ namespace build2 { sha256 cs; - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { - file* f; - liba* a (nullptr); - libs* s (nullptr); + const file* f; + const liba* a (nullptr); + const libs* s (nullptr); if ((f = pt->is_a<obje> ()) || (f = pt->is_a<obja> ()) || @@ -1367,8 +1369,8 @@ namespace build2 // derived from the import library by changing the extension. // Lucky for us -- there is no option to name it. // - auto imp (static_cast<file*> (t.member)); - out2 = "/IMPLIB:" + relative (imp->path ()).string (); + auto& imp (static_cast<const file&> (*t.member)); + out2 = "/IMPLIB:" + relative (imp.path ()).string (); args.push_back (out2.c_str ()); } @@ -1377,9 +1379,10 @@ namespace build2 // if (find_option ("/DEBUG", args, true)) { - auto pdb (static_cast<file*> ( - lt == otype::e ? t.member : t.member->member)); - out1 = "/PDB:" + relative (pdb->path ()).string (); + auto& pdb ( + static_cast<const file&> ( + lt == otype::e ? *t.member : *t.member->member)); + out1 = "/PDB:" + relative (pdb.path ()).string (); args.push_back (out1.c_str ()); } @@ -1412,8 +1415,8 @@ namespace build2 // On Windows libs{} is the DLL and its first ad hoc group // member is the import library. // - auto imp (static_cast<file*> (t.member)); - out = "-Wl,--out-implib=" + relative (imp->path ()).string (); + auto& imp (static_cast<const file&> (*t.member)); + out = "-Wl,--out-implib=" + relative (imp.path ()).string (); args.push_back (out.c_str ()); } } @@ -1428,11 +1431,11 @@ namespace build2 args[0] = ld->recall_string (); - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { - file* f; - liba* a (nullptr); - libs* s (nullptr); + const file* f; + const liba* a (nullptr); + const libs* s (nullptr); if ((f = pt->is_a<obje> ()) || (f = pt->is_a<obja> ()) || @@ -1600,11 +1603,13 @@ namespace build2 } }; + const libs_paths& paths (t.data<libs_paths> ()); + const path& lk (paths.link); const path& so (paths.soname); const path& in (paths.interm); - const path* f (paths.real); + const path* f (&paths.real); if (!in.empty ()) {ln (f->leaf (), in); f = ∈} if (!so.empty ()) {ln (f->leaf (), so); f = &so;} @@ -1622,11 +1627,9 @@ namespace build2 } target_state link:: - perform_clean (action a, target& xt) const + perform_clean (action a, const target& xt) const { - file& t (static_cast<file&> (xt)); - - libs_paths paths; + const file& t (static_cast<const file&> (xt)); switch (link_type (t)) { @@ -1666,7 +1669,8 @@ namespace build2 // Here we can have a bunch of symlinks that we need to remove. If // the paths are empty, then they will be ignored. // - paths = derive_libs_paths (t); + const libs_paths& paths (t.data<libs_paths> ()); + return clean_extra (a, t, {".d", paths.link.string ().c_str (), paths.soname.string ().c_str (), diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx index 4ac82a0..fc2979c 100644 --- a/build2/cc/msvc.cxx +++ b/build2/cc/msvc.cxx @@ -69,7 +69,7 @@ namespace build2 // if (lt == otype::s && l.compare (0, 3, " ") == 0) { - path imp (static_cast<file*> (t.member)->path ().leaf ()); + path imp (static_cast<const file&> (*t.member).path ().leaf ()); if (l.find (imp.string ()) != string::npos && l.find (imp.base ().string () + ".exp") != string::npos) diff --git a/build2/cc/utility b/build2/cc/utility index 050b645..bb7ed34 100644 --- a/build2/cc/utility +++ b/build2/cc/utility @@ -22,10 +22,10 @@ namespace build2 // Compile/link output type. // otype - compile_type (target&); + compile_type (const target&); otype - link_type (target&); + link_type (const target&); // Library link order. // @@ -41,8 +41,13 @@ namespace build2 // Given the link order return the library member (liba or libs) to link. // + // Note that the const version assumes you have already called non-const. + // target& link_member (bin::lib&, lorder); + + const target& + link_member (const bin::lib&, lorder); } } diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx index 5c5d1fe..4ccdcaf 100644 --- a/build2/cc/utility.cxx +++ b/build2/cc/utility.cxx @@ -38,6 +38,37 @@ namespace build2 : v.size () > 1 && v[1] == "shared" ? lorder::a_s : lorder::a; } + const target& + link_member (const bin::lib& l, lorder lo) + { + bool ls (true); + const string& at (cast<string> (l["bin.lib"])); // Available members. + + switch (lo) + { + case lorder::a: + case lorder::a_s: + ls = false; // Fall through. + case lorder::s: + case lorder::s_a: + { + if (ls ? at == "static" : at == "shared") + { + if (lo == lorder::a_s || lo == lorder::s_a) + ls = !ls; + else + assert (false); + } + } + } + + target* r (ls ? static_cast<target*> (l.s) : l.a); + + assert (r != nullptr); + + return *r; + } + target& link_member (bin::lib& l, lorder lo) { diff --git a/build2/cc/utility.ixx b/build2/cc/utility.ixx index 8695a8e..bc9cd05 100644 --- a/build2/cc/utility.ixx +++ b/build2/cc/utility.ixx @@ -7,7 +7,7 @@ namespace build2 namespace cc { inline otype - compile_type (target& t) + compile_type (const target& t) { return t.is_a<bin::obje> () ? otype::e : @@ -16,7 +16,7 @@ namespace build2 } inline otype - link_type (target& t) + link_type (const target& t) { return t.is_a<exe> () ? otype::e : diff --git a/build2/cc/windows-manifest.cxx b/build2/cc/windows-manifest.cxx index b0b0feb..0e38e7d 100644 --- a/build2/cc/windows-manifest.cxx +++ b/build2/cc/windows-manifest.cxx @@ -39,7 +39,7 @@ namespace build2 // file corresponding to the exe{} target. Return the manifest file path. // path link:: - windows_manifest (file& t, bool rpath_assembly) const + windows_manifest (const file& t, bool rpath_assembly) const { tracer trace (x, "windows_manifest"); diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx index 00911d4..94b1145 100644 --- a/build2/cc/windows-rpath.cxx +++ b/build2/cc/windows-rpath.cxx @@ -46,16 +46,16 @@ namespace build2 // adding to the assembly or timestamp_nonexistent if there aren't any. // timestamp link:: - windows_rpath_timestamp (file& t, const scope& bs, lorder lo) const + windows_rpath_timestamp (const file& t, const scope& bs, lorder lo) const { timestamp r (timestamp_nonexistent); // We need to collect all the DLLs, so go into implementation of both // shared and static (in case they depend on shared). // - auto imp = [] (file&, bool) {return true;}; + auto imp = [] (const file&, bool) {return true;}; - auto lib = [&r] (file* l, const string& f, bool sys) + auto lib = [&r] (const file* l, const string& f, bool sys) { // We don't rpath system libraries. // @@ -97,10 +97,10 @@ namespace build2 r = t; }; - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { - file* f; - liba* a; + const file* f; + const liba* a; if ((f = a = pt->is_a<liba> ()) || (f = pt->is_a<libs> ())) @@ -116,15 +116,15 @@ namespace build2 // duplicates). // auto link:: - windows_rpath_dlls (file& t, + windows_rpath_dlls (const file& t, const scope& bs, lorder lo) const -> windows_dlls { windows_dlls r; - auto imp = [] (file&, bool) {return true;}; + auto imp = [] (const file&, bool) {return true;}; - auto lib = [&r] (file* l, const string& f, bool sys) + auto lib = [&r] (const file* l, const string& f, bool sys) { if (sys) return; @@ -137,7 +137,8 @@ namespace build2 // const string* pdb ( l->member != nullptr && l->member->member != nullptr - ? &static_cast<file&> (*l->member->member).path ().string () + ? &static_cast<const file&> ( + *l->member->member).path ().string () : nullptr); r.insert (windows_dll {f, pdb, string ()}); @@ -178,10 +179,10 @@ namespace build2 } }; - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { - file* f; - liba* a; + const file* f; + const liba* a; if ((f = a = pt->is_a<liba> ()) || (f = pt->is_a<libs> ())) @@ -205,7 +206,7 @@ namespace build2 // manifest file. // void link:: - windows_rpath_assembly (file& t, + windows_rpath_assembly (const file& t, const scope& bs, lorder lo, const string& tcpu, diff --git a/build2/cli/rule b/build2/cli/rule index fbacacf..1fbbde4 100644 --- a/build2/cli/rule +++ b/build2/cli/rule @@ -26,10 +26,10 @@ namespace build2 apply (slock&, action, target&) const override; static target_state - perform_update (action, target&); + perform_update (action, const target&); static target_state - perform_clean (action, target&); + perform_clean (action, const target&); }; } } diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx index 9f54950..5dcec99 100644 --- a/build2/cli/rule.cxx +++ b/build2/cli/rule.cxx @@ -199,7 +199,7 @@ namespace build2 } else { - cli_cxx& g (*static_cast<cli_cxx*> (xt.group)); + cli_cxx& g (static_cast<cli_cxx&> (*xt.group)); build2::match (ml, a, g); return group_recipe; // Execute the group's recipe. } @@ -228,16 +228,16 @@ namespace build2 } target_state compile:: - perform_update (action a, target& xt) + perform_update (action a, const target& xt) { - cli_cxx& t (static_cast<cli_cxx&> (xt)); + const cli_cxx& t (static_cast<const cli_cxx&> (xt)); // Update prerequisites and determine if any relevant ones render us // out-of-date. Note that currently we treat all the prerequisites // as potentially affecting the result (think prologues/epilogues, // etc). // - cli* s; + const cli* s; { auto p (execute_prerequisites<cli> (a, t, t.mtime ())); @@ -320,9 +320,9 @@ namespace build2 } target_state compile:: - perform_clean (action a, target& xt) + perform_clean (action a, const target& xt) { - cli_cxx& t (static_cast<cli_cxx&> (xt)); + const cli_cxx& t (static_cast<const cli_cxx&> (xt)); // The reverse order of update: first delete the files, then clean // prerequisites. Also update timestamp in case there are operations diff --git a/build2/install/rule b/build2/install/rule index 169c43a..24111ce 100644 --- a/build2/install/rule +++ b/build2/install/rule @@ -53,12 +53,12 @@ namespace build2 using install_dir = install::install_dir; virtual void - install_extra (file&, const install_dir&) const; + install_extra (const file&, const install_dir&) const; // Return true if anything was uninstalled. // virtual bool - uninstall_extra (file&, const install_dir&) const; + uninstall_extra (const file&, const install_dir&) const; // Installation "commands". // @@ -84,16 +84,16 @@ namespace build2 // static bool uninstall_f (const install_dir& base, - file* t, + const file* t, const path& name, bool verbose); private: target_state - perform_install (action, target&) const; + perform_install (action, const target&) const; target_state - perform_uninstall (action, target&) const; + perform_uninstall (action, const target&) const; }; } } diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx index 8c21dc1..3be7eef 100644 --- a/build2/install/rule.cxx +++ b/build2/install/rule.cxx @@ -129,7 +129,8 @@ namespace build2 apply (slock& ml, action a, target& t) const { match_data md (move (t.data<match_data> ())); - t.clear_data (); // In case delegated-to rule also uses aux storage. + t.clear_data (); // In case delegated-to rule (or the rule that overrides + // us; see cc/install) also uses aux storage. if (!md.install) // Not installable. return noop_recipe; @@ -220,8 +221,8 @@ namespace build2 // Ok, the worst case scenario: we need to cause update of // prerequisite targets and also delegate to the real update. // - return [pt = move (p), dr = move (d)] - (action a, target& t) mutable -> target_state + return [pt = move (p), dr = move (d)] ( + action a, const target& t) mutable -> target_state { // Do the target update first. // @@ -237,16 +238,22 @@ namespace build2 }; } else if (a.operation () == install_id) - return [this] (action a, target& t) {return perform_install (a, t);}; + return [this] (action a, const target& t) + { + return perform_install (a, t); + }; else - return [this] (action a, target& t) {return perform_uninstall (a, t);}; + return [this] (action a, const target& t) + { + return perform_uninstall (a, t); + }; } void file_rule:: - install_extra (file&, const install_dir&) const {} + install_extra (const file&, const install_dir&) const {} bool file_rule:: - uninstall_extra (file&, const install_dir&) const {return false;} + uninstall_extra (const file&, const install_dir&) const {return false;} struct install_dir { @@ -279,7 +286,7 @@ namespace build2 // static install_dir& resolve_subdir (install_dirs& rs, - target& t, + const target& t, const scope& s, const lookup& l) { @@ -310,7 +317,7 @@ namespace build2 // all the super-directories leading up to the destination (last). // static install_dirs - resolve (target& t, dir_path d, const string* var = nullptr) + resolve (const target& t, dir_path d, const string* var = nullptr) { install_dirs rs; @@ -498,7 +505,7 @@ namespace build2 static void install_f (const install_dir& base, const path& name, - file& t, + const file& t, bool verbose) { path relf (relative (t.path ())); @@ -605,12 +612,12 @@ namespace build2 } target_state file_rule:: - perform_install (action a, target& xt) const + perform_install (action a, const target& xt) const { - file& t (static_cast<file&> (xt)); + const file& t (static_cast<const file&> (xt)); assert (!t.path ().empty ()); // Should have been assigned by update. - auto install_target = [this] (file& t, const path& p, bool verbose) + auto install_target = [this] (const file& t, const path& p, bool verbose) { bool n (!p.to_directory ()); dir_path d (n ? p.directory () : path_cast<dir_path> (p)); @@ -653,10 +660,10 @@ namespace build2 // Then installable ad hoc group members, if any. // - for (target* m (t.member); m != nullptr; m = m->member) + for (const target* m (t.member); m != nullptr; m = m->member) { if (const path* p = lookup_install<path> (*m, "install")) - install_target (static_cast<file&> (*m), *p, false); + install_target (static_cast<const file&> (*m), *p, false); } // Finally install the target itself (since we got here we know the @@ -777,7 +784,7 @@ namespace build2 bool file_rule:: uninstall_f (const install_dir& base, - file* t, + const file* t, const path& name, bool verbose) { @@ -863,13 +870,13 @@ namespace build2 } target_state file_rule:: - perform_uninstall (action a, target& xt) const + perform_uninstall (action a, const target& xt) const { - file& t (static_cast<file&> (xt)); + const file& t (static_cast<const file&> (xt)); assert (!t.path ().empty ()); // Should have been assigned by update. - auto uninstall_target = [this] (file& t, const path& p, bool verbose) - -> target_state + auto uninstall_target = [this] ( + const file& t, const path& p, bool verbose) -> target_state { bool n (!p.to_directory ()); dir_path d (n ? p.directory () : path_cast<dir_path> (p)); @@ -920,10 +927,10 @@ namespace build2 // we would have to do it in reverse, but that's not easy (it's a // single-linked list). // - for (target* m (t.member); m != nullptr; m = m->member) + for (const target* m (t.member); m != nullptr; m = m->member) { if (const path* p = lookup_install<path> (*m, "install")) - r |= uninstall_target (static_cast<file&> (*m), + r |= uninstall_target (static_cast<const file&> (*m), *p, r != target_state::changed); } diff --git a/build2/operation.cxx b/build2/operation.cxx index 19f1ff7..01f469e 100644 --- a/build2/operation.cxx +++ b/build2/operation.cxx @@ -133,11 +133,11 @@ namespace build2 // Execute collecting postponed targets (to be re-examined later). // Do it in reverse order if the execution mode is 'last'. // - vector<reference_wrapper<target>> psp; + vector<reference_wrapper<const target>> psp; - auto body = [a, quiet, &psp, &trace] (void* v) + auto body = [a, quiet, &psp, &trace] (const void* v) { - target& t (*static_cast<target*> (v)); + const target& t (*static_cast<const target*> (v)); l5 ([&]{trace << diag_doing (a, t);}); @@ -162,9 +162,9 @@ namespace build2 }; if (current_mode == execution_mode::first) - for (void* v: ts) body (v); + for (const void* v: ts) body (v); else - for (void* v: reverse_iterate (ts)) body (v); + for (const void* v: reverse_iterate (ts)) body (v); // We should have executed every target that we matched. // @@ -175,7 +175,7 @@ namespace build2 // // Note: must be serial. // - for (target& t: psp) + for (const target& t: psp) { switch (execute (a, t)) { diff --git a/build2/prerequisite b/build2/prerequisite index a6d3632..9e1dbfa 100644 --- a/build2/prerequisite +++ b/build2/prerequisite @@ -71,10 +71,9 @@ namespace build2 const optional<string> ext; // Absent if unspecified. const scope_type& scope; - target_type* target; // NULL if not yet resolved. Note that this - // should always be the "primary target", not - // a member of a target group. - + const_ptr<target_type> target; // NULL if not yet resolved. Note that this + // should always be the "primary target", + // not a member of a target group. public: prerequisite (optional<string> p, const target_type_type& t, diff --git a/build2/rule b/build2/rule index cc98bfa..fad4316 100644 --- a/build2/rule +++ b/build2/rule @@ -85,10 +85,10 @@ namespace build2 apply (slock&, action, target&) const override; static target_state - perform_update (action, target&); + perform_update (action, const target&); static target_state - perform_clean (action, target&); + perform_clean (action, const target&); static const fsdir_rule instance; }; diff --git a/build2/rule.cxx b/build2/rule.cxx index 88c7941..922a4f7 100644 --- a/build2/rule.cxx +++ b/build2/rule.cxx @@ -163,7 +163,7 @@ namespace build2 } target_state fsdir_rule:: - perform_update (action a, target& t) + perform_update (action a, const target& t) { target_state ts (target_state::unchanged); @@ -203,7 +203,7 @@ namespace build2 } target_state fsdir_rule:: - perform_clean (action a, target& t) + perform_clean (action a, const target& t) { // The reverse order of update: first delete this directory, // then clean prerequisites (e.g., delete parent directories). diff --git a/build2/target b/build2/target index fb8fd08..7653da6 100644 --- a/build2/target +++ b/build2/target @@ -75,7 +75,7 @@ namespace build2 // 14u2). With the size ranging (in bytes for 64-bit target) from 32 (GCC) // to 64 (VC). // - using recipe_function = target_state (action, target&); + using recipe_function = target_state (action, const target&); using recipe = function<recipe_function>; // Commonly-used recipes. The default recipe executes the action on @@ -92,10 +92,10 @@ namespace build2 extern const recipe group_recipe; target_state - noop_action (action, target&); // Defined in <build2/algorithm>. + noop_action (action, const target&); // Defined in <build2/algorithm>. target_state - group_action (action, target&); // Defined in <build2/algorithm>. + group_action (action, const target&); // Defined in <build2/algorithm>. // A view of target group members. // @@ -179,7 +179,8 @@ namespace build2 // special target_state::group state. You would normally also use the // group_recipe for group members. // - target* group = nullptr; + const_ptr<target> group = nullptr; + // What has been described above is a "normal" group. That is, there is // a dedicated target type that explicitly serves as a group and there @@ -221,7 +222,7 @@ namespace build2 // - Member variable lookup skips the ad hoc group (since the group is // the first member, this is normally what we want). // - target* member = nullptr; + const_ptr<target> member = nullptr; bool adhoc_group () const @@ -307,8 +308,10 @@ namespace build2 // track of the action here since the targets will be updated // if the recipe is updated, normally as part of rule::apply(). // - typedef vector<target*> prerequisite_targets_type; - prerequisite_targets_type prerequisite_targets; + // Note that the recipe may modify (mutable) this list. + // + using prerequisite_targets_type = vector<const target*>; + mutable prerequisite_targets_type prerequisite_targets; // Check if there are any prerequisites, taking into account // group prerequisites. @@ -477,13 +480,10 @@ namespace build2 // // Currenly the data is not destroyed until the next match. // - std::aligned_storage<sizeof (void*) * 4>::type data_pad; + static constexpr size_t data_size = sizeof (string) * 4; + std::aligned_storage<data_size>::type data_pad; void (*data_dtor) (void*) = nullptr; - // VC 14 needs decltype. - // - static const size_t data_size = sizeof (decltype (data_pad)); - template <typename R, typename T = typename std::remove_cv< typename std::remove_reference<R>::type>::type> @@ -611,32 +611,30 @@ namespace build2 // also be traversed in reverse, but that's what you usually want, // anyway. // - class group_prerequisites + // For constant iteration use const_group_prerequisites(). + // + template <typename T, typename P, typename I> + class group_prerequisites_impl { public: - typedef target::prerequisites_type prerequisites_type; - explicit - group_prerequisites (target& t) + group_prerequisites_impl (T& t) : t_ (t), - g_ (t_.group == nullptr || - t_.group->member != nullptr || // Ad hoc group member. + g_ (t_.group == nullptr || + t_.group->member != nullptr || // Ad hoc group member. t_.group->prerequisites.empty () ? nullptr : t_.group) {} 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; + using value_type = typename I::value_type; + using pointer = typename I::pointer; + using reference = typename I::reference; + using difference_type = typename I::difference_type; + using iterator_category = std::bidirectional_iterator_tag; iterator () {} - iterator (target* t, target* g, prerequisites_type* c, base_iterator i) - : t_ (t), g_ (g), c_ (c), i_ (i) {} + iterator (T* t, T* g, P* c, I i): t_ (t), g_ (g), c_ (c), i_ (i) {} iterator& operator++ () @@ -681,25 +679,25 @@ namespace build2 operator!= (const iterator& x, const iterator& y) {return !(x == y);} private: - target* t_ = nullptr; - target* g_ = nullptr; - prerequisites_type* c_ = nullptr; - base_iterator i_; + T* t_ = nullptr; + T* g_ = nullptr; + P* c_ = nullptr; + I i_; }; - typedef std::reverse_iterator<iterator> reverse_iterator; + using reverse_iterator = std::reverse_iterator<iterator>; iterator begin () const { - auto& c ((g_ != nullptr ? *g_ : t_).prerequisites); + P& c ((g_ != nullptr ? *g_ : t_).prerequisites); return iterator (&t_, g_, &c, c.begin ()); } iterator end () const { - auto& c (t_.prerequisites); + P& c (t_.prerequisites); return iterator (&t_, g_, &c, c.end ()); } @@ -717,10 +715,20 @@ namespace build2 } private: - target& t_; - target* g_; + T& t_; + T* g_; }; + using group_prerequisites = group_prerequisites_impl< + target, + target::prerequisites_type, + target::prerequisites_type::iterator>; + + using const_group_prerequisites = group_prerequisites_impl< + const target, + const target::prerequisites_type, + target::prerequisites_type::const_iterator>; + // A member of a prerequisite. If 'target' is NULL, then this is the // prerequisite itself. Otherwise, it is its member. In this case // 'prerequisite' still refers to the prerequisite. @@ -1128,21 +1136,21 @@ namespace build2 timestamp mtime (bool load = true) const { - const mtime_target* t (state_ == target_state::group - ? static_cast<const mtime_target*> (group) - : this); + const mtime_target& t (state_ == target_state::group + ? static_cast<const mtime_target&> (*group) + : *this); - if (load && t->mtime_ == timestamp_unknown) - t->mtime_ = t->load_mtime (); + if (load && t.mtime_ == timestamp_unknown) + t.mtime_ = t.load_mtime (); - return t->mtime_; + return t.mtime_; } // Note that while we can cache the mtime at any time, it may be ignored // if the target state is group (see the mtime() accessor). // void - mtime (timestamp mt) + mtime (timestamp mt) const { mtime_ = mt; } @@ -1150,7 +1158,7 @@ namespace build2 // Return true if this target is newer than the specified timestamp. // bool - newer (timestamp mt) + newer (timestamp mt) const { timestamp mp (mtime ()); diff --git a/build2/target.cxx b/build2/target.cxx index f7387c7..c032028 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -173,7 +173,7 @@ namespace build2 if (auto p = vars.find (var)) r.first = lookup (p, &vars); - target* g (nullptr); + const target* g (nullptr); if (!r.first) { diff --git a/build2/test/common b/build2/test/common index d3678c8..44c7bf8 100644 --- a/build2/test/common +++ b/build2/test/common @@ -25,18 +25,18 @@ namespace build2 // prerequisites. // bool - pass (target& alias_target) const; + pass (const target& alias_target) const; // Return true if the specified target should be tested. // bool - test (target& test_target) const; + test (const target& test_target) const; // Return true if the specified target should be tested with the // specified testscript test (or group). // bool - test (target& test_target, const path& id_path) const; + test (const target& test_target, const path& id_path) const; }; } } diff --git a/build2/test/common.cxx b/build2/test/common.cxx index 1b4c194..be0e690 100644 --- a/build2/test/common.cxx +++ b/build2/test/common.cxx @@ -55,7 +55,7 @@ namespace build2 } bool common:: - pass (target& a) const + pass (const target& a) const { if (test_ == nullptr) return true; @@ -95,7 +95,7 @@ namespace build2 } bool common:: - test (target& t) const + test (const target& t) const { if (test_ == nullptr) return true; @@ -159,7 +159,7 @@ namespace build2 } bool common:: - test (target& t, const path& id) const + test (const target& t, const path& id) const { if (test_ == nullptr) return true; diff --git a/build2/test/rule b/build2/test/rule index 0e208ff..da55173 100644 --- a/build2/test/rule +++ b/build2/test/rule @@ -24,7 +24,7 @@ namespace build2 match (slock&, action, target&, const string&) const override; target_state - perform_script (action, target&) const; + perform_script (action, const target&) const; }; class rule: public rule_common @@ -34,7 +34,7 @@ namespace build2 apply (slock&, action, target&) const override; static target_state - perform_test (action, target&); + perform_test (action, const target&); }; class alias_rule: public rule_common @@ -44,7 +44,7 @@ namespace build2 apply (slock&, action, target&) const override; target_state - perform_test (action, target&) const; + perform_test (action, const target&) const; }; } } diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx index 05d5eac..38c24f9 100644 --- a/build2/test/rule.cxx +++ b/build2/test/rule.cxx @@ -186,7 +186,7 @@ namespace build2 // If not a test then also redirect to the alias rule. // return md.test - ? [this] (action a, target& t) {return perform_test (a, t);} + ? [this] (action a, const target& t) {return perform_test (a, t);} : default_recipe; } @@ -218,7 +218,10 @@ namespace build2 t.prerequisite_targets.push_back (&p.search ()); } - return [this] (action a, target& t) {return perform_script (a, t);}; + return [this] (action a, const target& t) + { + return perform_script (a, t); + }; } else { @@ -327,7 +330,8 @@ namespace build2 // update of input/output targets and also delegate to the real // update. // - return [it, ot, dr = move (d)] (action a, target& t) -> target_state + return [it, ot, dr = move (d)] ( + action a, const target& t) -> target_state { // Do the general update first. // @@ -363,7 +367,7 @@ namespace build2 } target_state rule_common:: - perform_script (action, target& t) const + perform_script (action, const target& t) const { // Figure out whether the testscript file is called 'testscript', in // which case it should be the only one. @@ -371,11 +375,11 @@ namespace build2 bool one; { optional<bool> o; - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { // In case we are using the alias rule's list (see above). // - if (testscript* ts = pt->is_a<testscript> ()) + if (const testscript* ts = pt->is_a<testscript> ()) { bool r (ts->name == "testscript"); @@ -435,9 +439,9 @@ namespace build2 // Run all the testscripts. // - for (target* pt: t.prerequisite_targets) + for (const target* pt: t.prerequisite_targets) { - if (testscript* ts = pt->is_a<testscript> ()) + if (const testscript* ts = pt->is_a<testscript> ()) { // If this is just the testscript, then its id path is empty (and // it can only be ignored by ignoring the test target, which makes @@ -489,7 +493,7 @@ namespace build2 // nameN arg arg ... nullptr nullptr // static bool - run_test (target& t, + run_test (const target& t, diag_record& dr, char const** args, process* prev = nullptr) @@ -547,7 +551,7 @@ namespace build2 } target_state rule:: - perform_test (action, target& tt) + perform_test (action, const target& tt) { // @@ Would be nice to print what signal/core was dumped. // @@ -562,7 +566,7 @@ namespace build2 // Note that we have similar code for scripted tests. // - target* t (nullptr); + const target* t (nullptr); if (l.defined ()) { @@ -636,7 +640,7 @@ namespace build2 auto& pts (tt.prerequisite_targets); if (pts.size () != 0 && pts[0] != nullptr) { - file& it (static_cast<file&> (*pts[0])); + const file& it (static_cast<const file&> (*pts[0])); assert (!it.path ().empty ()); // Should have been assigned by update. args.push_back (it.path ().string ().c_str ()); } @@ -656,7 +660,7 @@ namespace build2 process_path dpp; if (pts.size () != 0 && pts[1] != nullptr) { - file& ot (static_cast<file&> (*pts[1])); + const file& ot (static_cast<const file&> (*pts[1])); assert (!ot.path ().empty ()); // Should have been assigned by update. dpp = run_search (dp, true); @@ -693,7 +697,7 @@ namespace build2 } target_state alias_rule:: - perform_test (action a, target& t) const + perform_test (action a, const target& t) const { // Run the alias recipe first then the test. // diff --git a/build2/test/script/script b/build2/test/script/script index e528cdd..2438fa5 100644 --- a/build2/test/script/script +++ b/build2/test/script/script @@ -511,8 +511,8 @@ namespace build2 class script: public script_base, public group { public: - script (target& test_target, - testscript& script_target, + script (const target& test_target, + const testscript& script_target, const dir_path& root_wd); script (script&&) = delete; @@ -521,8 +521,8 @@ namespace build2 script& operator= (const script&) = delete; public: - target& test_target; // Target we are testing. - testscript& script_target; // Target of the testscript file. + const target& test_target; // Target we are testing. + const testscript& script_target; // Target of the testscript file. // Pre-parse data. // diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx index bd13845..bc816d7 100644 --- a/build2/test/script/script.cxx +++ b/build2/test/script/script.cxx @@ -500,7 +500,7 @@ namespace build2 // script // script:: - script (target& tt, testscript& st, const dir_path& rwd) + script (const target& tt, const testscript& st, const dir_path& rwd) : group (st.name == "testscript" ? string () : st.name), test_target (tt), script_target (st) @@ -522,7 +522,7 @@ namespace build2 // Note that we have similar code for simple tests. // - target* t (nullptr); + const target* t (nullptr); if (l.defined ()) { diff --git a/build2/types b/build2/types index 7afc24e..0e6012b 100644 --- a/build2/types +++ b/build2/types @@ -37,6 +37,7 @@ #include <butl/process> #include <butl/fdstream> #include <butl/optional> +#include <butl/const-ptr> #include <butl/timestamp> #include <butl/vector-view> #include <butl/small-vector> @@ -116,6 +117,10 @@ namespace build2 using butl::optional; using butl::nullopt; + // <butl/const-ptr> + // + using butl::const_ptr; + // <butl/path> // using butl::path; |