aboutsummaryrefslogtreecommitdiff
path: root/build2/target
diff options
context:
space:
mode:
Diffstat (limited to 'build2/target')
-rw-r--r--build2/target1084
1 files changed, 1084 insertions, 0 deletions
diff --git a/build2/target b/build2/target
new file mode 100644
index 0000000..19bec9e
--- /dev/null
+++ b/build2/target
@@ -0,0 +1,1084 @@
+// file : build2/target -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_TARGET
+#define BUILD2_TARGET
+
+#include <map>
+#include <string>
+#include <vector>
+#include <memory> // unique_ptr
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+#include <functional> // reference_wrapper
+#include <ostream>
+#include <cassert>
+#include <utility> // move(), forward(), declval()
+#include <iterator>
+#include <type_traits>
+
+#include <butl/utility> // reverse_iterate()
+#include <butl/multi-index> // map_iterator_adapter
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/scope>
+#include <build2/variable>
+#include <build2/operation>
+#include <build2/target-type>
+#include <build2/target-key>
+#include <build2/prerequisite>
+
+namespace build2
+{
+ class scope;
+ class target;
+
+ target&
+ search (prerequisite&); // From <build2/algorithm>.
+
+ // Target state.
+ //
+ enum class target_state: std::uint8_t
+ {
+ // The order of the enumerators is arranged so that their integral
+ // values indicate whether one "overrides" the other in the merge
+ // operator (see below).
+ //
+ unknown,
+ unchanged,
+ postponed,
+ changed,
+ failed,
+ group // Target's state is the group's state.
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, target_state);
+
+ inline target_state&
+ operator |= (target_state& l, target_state r)
+ {
+ if (static_cast<std::uint8_t> (r) > static_cast<std::uint8_t> (l))
+ l = r;
+
+ return l;
+ }
+
+ // Recipe.
+ //
+ // The returned target state should be changed, unchanged, or
+ // postponed, though you shouldn't be returning postponed
+ // directly. If there is an error, then the recipe should
+ // throw rather than returning failed.
+ //
+ // The return value of the recipe is used to update the target
+ // state except if the state was manually set by the recipe to
+ // target_state::group. Note that in this case the returned by
+ // the recipe value is still used as the resulting target state
+ // so it should match the group's state.
+ //
+ using recipe_function = target_state (action, target&);
+ using recipe = std::function<recipe_function>;
+
+ // Commonly-used recipes. The default recipe executes the action on
+ // all the prerequisites in a loop, skipping ignored. Specifically,
+ // for actions with the "first" execution mode, it calls
+ // execute_prerequisites() while for those with the "last" mode --
+ // reverse_execute_prerequisites(); see <build2/operation>,
+ // <build2/algorithm> for details. The group recipe call's the group's
+ // recipe.
+ //
+ extern const recipe empty_recipe;
+ extern const recipe noop_recipe;
+ extern const recipe default_recipe;
+ extern const recipe group_recipe;
+
+ target_state
+ noop_action (action, target&); // Defined in <build2/algorithm>.
+
+ target_state
+ group_action (action, target&); // Defined in <build2/algorithm>.
+
+ // Prerequisite references as used in the target::prerequisites list
+ // below.
+ //
+ struct prerequisite_ref: std::reference_wrapper<prerequisite>
+ {
+ typedef std::reference_wrapper<prerequisite> base;
+
+ using base::base;
+
+ // Return true if this reference belongs to the target's prerequisite
+ // list. Note that this test only works if you use references to
+ // the container elements and the container hasn't been resized
+ // since such a reference was obtained. Normally this function is
+ // used when iterating over a combined prerequisites range (see
+ // group_prerequisites below).
+ //
+ bool
+ belongs (const target&) const;
+ };
+
+ // A view of target group members.
+ //
+ struct group_view
+ {
+ target* const* members; // NULL means not yet known.
+ std::size_t count;
+ };
+
+ // Target.
+ //
+ class target
+ {
+ public:
+ typedef build2::action action_type;
+
+ virtual
+ ~target () = default;
+
+ target (const target&) = delete;
+ target& operator= (const target&) = delete;
+
+ target (dir_path d, std::string n, const std::string* e)
+ : dir (std::move (d)), name (std::move (n)), ext (e) {}
+
+ // Reset the target before matching a rule for it. The
+ // default implementation clears prerequisite_targets.
+ //
+ virtual void
+ reset (action_type);
+
+ const dir_path dir; // Absolute and normalized.
+ const std::string name;
+ const std::string* ext; // Extension, NULL means unspecified,
+ // empty means no extension.
+
+ // Target group to which this target belongs, if any. Note that
+ // we assume that the group and all its members are in the same
+ // scope (for example, in variable lookup). We also don't support
+ // nested groups.
+ //
+ // The semantics of the interaction between the group and its
+ // members and what it means to, say, update the group, is
+ // unspecified and determined by the group's type. In particular,
+ // a group can be created out of member types that have no idea
+ // they are part of this group (e.g., cli.cxx{}).
+ //
+ // Normally, however, there are two kinds of groups: "alternatives"
+ // and "combination". In an alternatives group, normally one of the
+ // members is selected when the group is mentioned as a prerequisite
+ // with, perhaps, an exception for special rules, like aliases, where
+ // it makes more sense to treat the group as a whole. In this case we
+ // say that the rule "semantically recognizes" the group and picks
+ // some of its members.
+ //
+ // Updating an alternatives group as a whole can mean updating some
+ // subset of its members (e.g., lib{}). Or the group may not support
+ // this at all (e.g., obj{}).
+ //
+ // In a combination group, when a group is updated, normally all
+ // members are updates (and usually with a single command), though
+ // there could be some members that are omitted, depending on the
+ // configuration (e.g., an inline file not/being generated). When
+ // a combination group is mentioned as a prerequisite, the rule
+ // is usually interested in the individual members rather than
+ // the whole group. For example, a C++ compile rule would like to
+ // "see" the ?xx{} members when it gets a cli.cxx{} group.
+ //
+ // Which brings us to the group iteration mode. The target type
+ // contains a member called see_through that indicates whether the
+ // default iteration mode for the group should be "see through";
+ // that is, whether we see the members or the group itself. For
+ // the iteration support itself, see the *_prerequisite_members()
+ // machinery below.
+ //
+ target* group {nullptr};
+
+ // You should not call this function directly; rather use
+ // resolve_group_members() from <build2/algorithm>.
+ //
+ virtual group_view
+ group_members (action_type) const;
+
+ target_key
+ key () const {return target_key {&type (), &dir, &name, &ext};}
+
+ // Scoping.
+ //
+ public:
+ // Most qualified scope that contains this target.
+ //
+ scope&
+ base_scope () const;
+
+ // Root scope of a project that contains this target. Note that
+ // a target can be out of any (known) project root in which case
+ // this function asserts. If you need to detect this situation,
+ // then use base_scope().root_scope() expression instead.
+ //
+ scope&
+ root_scope () const;
+
+ // Root scope of a strong amalgamation that contains this target.
+ // The same notes as to root_scope() apply.
+ //
+ scope&
+ strong_scope () const {return *root_scope ().strong_scope ();}
+
+ // Root scope of the outermost amalgamation that contains this target.
+ // The same notes as to root_scope() apply.
+ //
+ scope&
+ weak_scope () const {return *root_scope ().weak_scope ();}
+
+
+ bool
+ in (const scope& s) const
+ {
+ return
+ (s.out_path_ != nullptr && dir.sub (*s.out_path_)) ||
+ (s.src_path_ != nullptr && dir.sub (*s.src_path_));
+ }
+
+ // Prerequisites.
+ //
+ public:
+ typedef std::vector<prerequisite_ref> prerequisites_type;
+ prerequisites_type prerequisites;
+
+ // Targets to which prerequisites resolve for this recipe. Note
+ // that unlike prerequisite::target, these can be resolved to
+ // group members. NULL means the target should be skipped (or
+ // the rule may simply not add such a target to the list).
+ //
+ // Note also that it is possible the target can vary from
+ // action to action, just like recipes. We don't need to keep
+ // track of the action here since the targets will be updated
+ // if the recipe is updated, normally as part of rule::apply().
+ //
+ typedef std::vector<target*> prerequisite_targets_type;
+ prerequisite_targets_type prerequisite_targets;
+
+ // Check if there are any prerequisites, taking into account
+ // group prerequisites.
+ //
+ bool
+ has_prerequisites () const
+ {
+ return !prerequisites.empty () ||
+ (group != nullptr && !group->prerequisites.empty ());
+ }
+
+ // Target-specific variables.
+ //
+ public:
+ variable_map vars;
+
+ // Lookup, including in groups to which this target belongs and
+ // then in outer scopes (including target type/pattern-specific
+ // variables). If you only want to lookup in this target, do it
+ // on the variable map directly.
+ //
+ lookup<const value>
+ operator[] (const variable&) const;
+
+ lookup<const value>
+ operator[] (const std::string& name) const
+ {
+ return operator[] (var_pool.find (name));
+ }
+
+ // Return a value suitable for assignment. See class scope for
+ // details.
+ //
+ value&
+ assign (const variable& var) {return vars.assign (var).first;}
+
+ value&
+ assign (const std::string& name) {return vars.assign (name).first;}
+
+ // Return a value suitable for appending. See class scope for
+ // details.
+ //
+ value&
+ append (const variable&);
+
+ value&
+ append (const std::string& name)
+ {
+ return append (var_pool.find (name));
+ }
+
+ public:
+ target_state raw_state;
+
+ target_state
+ state () const
+ {
+ return raw_state != target_state::group ? raw_state : group->raw_state;
+ }
+
+ // Number of direct targets that depend on this target in the current
+ // action. It is incremented during the match phase and then decremented
+ // during execution, before running the recipe. As a result, the recipe
+ // can detect the last chance (i.e., last dependent) to execute the
+ // command (see also the first/last execution modes in <operation>).
+ //
+ // Note that setting a new recipe (which happens when we match the rule
+ // and which in turn is triggered by the first dependent) clears this
+ // counter. However, if the previous action was the same as the current,
+ // then the existing recipe is reused. In this case, however, the counter
+ // should have been decremented to 0 naturally, as part of the previous
+ // action execution.
+ //
+ std::size_t dependents;
+
+ public:
+ action_type action; // Action this recipe is for.
+
+ public:
+ typedef build2::recipe recipe_type;
+
+ const recipe_type&
+ recipe (action_type a) const {return a > action ? empty_recipe : recipe_;}
+
+ void
+ recipe (action_type, recipe_type);
+
+ // Target type info.
+ //
+ public:
+ template <typename T>
+ T*
+ is_a () {return dynamic_cast<T*> (this);}
+
+ template <typename T>
+ const T*
+ is_a () const {return dynamic_cast<const T*> (this);}
+
+ // Dynamic derivation to support define.
+ //
+ const target_type* derived_type = nullptr;
+
+ const target_type&
+ type () const
+ {
+ return derived_type != nullptr ? *derived_type : dynamic_type ();
+ }
+
+ virtual const target_type& dynamic_type () const = 0;
+ static const target_type static_type;
+
+ private:
+ recipe_type recipe_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, const target&);
+
+ // A "range" that presents the prerequisites of a group and one of
+ // its members as one continuous sequence, or, in other words, as
+ // if they were in a single container. The group's prerequisites
+ // come first followed by the member's. If you need to see them
+ // in the other direction, iterate in reverse, for example:
+ //
+ // for (prerequisite_ref& pr: group_prerequisites (t))
+ //
+ // for (prerequisite_ref& pr: reverse_iterate (group_prerequisites (t))
+ //
+ // Note that in this case the individual elements of each list will
+ // also be traversed in reverse, but that's what you usually want,
+ // anyway.
+ //
+ class group_prerequisites
+ {
+ public:
+ typedef target::prerequisites_type prerequisites_type;
+
+ explicit
+ group_prerequisites (target& t): t_ (t) {}
+
+ struct iterator
+ {
+ typedef prerequisites_type::iterator base_iterator;
+
+ typedef base_iterator::value_type value_type;
+ typedef base_iterator::pointer pointer;
+ typedef base_iterator::reference reference;
+ typedef base_iterator::difference_type difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+ iterator () {}
+ iterator (target* t, prerequisites_type* c, base_iterator i)
+ : t_ (t), c_ (c), i_ (i) {}
+
+ iterator&
+ operator++ ()
+ {
+ if (++i_ == c_->end () && c_ != &t_->prerequisites)
+ {
+ c_ = &t_->prerequisites;
+ i_ = c_->begin ();
+ }
+ return *this;
+ }
+
+ iterator
+ operator++ (int) {iterator r (*this); operator++ (); return r;}
+
+ iterator&
+ operator-- ()
+ {
+ if (i_ == c_->begin () && c_ == &t_->prerequisites)
+ {
+ c_ = &t_->group->prerequisites;
+ i_ = c_->end ();
+ }
+
+ --i_;
+ return *this;
+ }
+
+ iterator
+ operator-- (int) {iterator r (*this); operator-- (); return r;}
+
+ reference operator* () const {return *i_;}
+ pointer operator-> () const {return i_.operator -> ();}
+
+ friend bool
+ operator== (const iterator& x, const iterator& y)
+ {
+ return x.t_ == y.t_ && x.c_ == y.c_ && x.i_ == y.i_;
+ }
+
+ friend bool
+ operator!= (const iterator& x, const iterator& y) {return !(x == y);}
+
+ private:
+ target* t_ {nullptr};
+ prerequisites_type* c_ {nullptr};
+ base_iterator i_;
+ };
+
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+
+ iterator
+ begin () const
+ {
+ auto& c ((t_.group != nullptr && !t_.group->prerequisites.empty ()
+ ? *t_.group : t_).prerequisites);
+ return iterator (&t_, &c, c.begin ());
+ }
+
+ iterator
+ end () const
+ {
+ auto& c (t_.prerequisites);
+ return iterator (&t_, &c, c.end ());
+ }
+
+ reverse_iterator
+ rbegin () const {return reverse_iterator (end ());}
+
+ reverse_iterator
+ rend () const {return reverse_iterator (begin ());}
+
+ std::size_t
+ size () const
+ {
+ return t_.prerequisites.size () +
+ (t_.group != nullptr ? t_.group->prerequisites.size () : 0);
+ }
+
+ private:
+ target& t_;
+ };
+
+ // 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.
+ //
+ struct prerequisite_member
+ {
+ typedef build2::target target_type;
+ typedef build2::prerequisite prerequisite_type;
+
+ prerequisite_ref& prerequisite;
+ target_type* target;
+
+ template <typename T>
+ bool
+ is_a () const
+ {
+ return target != nullptr
+ ? target->is_a<T> () != nullptr
+ : prerequisite.get ().is_a<T> ();
+ }
+
+ prerequisite_key
+ key () const
+ {
+ return target != nullptr
+ ? prerequisite_key {prerequisite.get ().proj, target->key (), nullptr}
+ : prerequisite.get ().key ();
+ }
+
+ const build2::target_type&
+ type () const
+ {
+ return target != nullptr ? target->type () : prerequisite.get ().type;
+ }
+
+ const std::string&
+ name () const
+ {
+ return target != nullptr ? target->name : prerequisite.get ().name;
+ }
+
+ const std::string*
+ proj () const
+ {
+ // Target cannot be project-qualified.
+ //
+ return target != nullptr ? nullptr : prerequisite.get ().proj;
+ }
+
+ target_type&
+ search () const
+ {
+ return target != nullptr ? *target : build2::search (prerequisite);
+ }
+
+ prerequisite_type&
+ as_prerequisite (tracer&) const;
+ };
+
+ inline std::ostream&
+ operator<< (std::ostream& os, const prerequisite_member& pm)
+ {
+ return os << pm.key ();
+ }
+
+ // A "range" that presents a sequence of prerequisites (e.g., from
+ // group_prerequisites()) as a sequence of prerequisite_member's. For
+ // each group prerequisite you will "see" either the prerequisite
+ // itself or all its members, depending on the default iteration
+ // mode of the target group type. You can skip the rest of the
+ // group members with leave_group() and you can force iteration
+ // over the members with enter_group(). Usage:
+ //
+ // for (prerequisite_member pm: prerequisite_members (a, ...))
+ //
+ // Where ... can be:
+ //
+ // t.prerequisites
+ // reverse_iterate(t.prerequisites)
+ // group_prerequisites (t)
+ // reverse_iterate (group_prerequisites (t))
+ //
+ // But use shortcuts instead:
+ //
+ // prerequisite_members (a, t)
+ // reverse_prerequisite_members (a, t)
+ // group_prerequisite_members (a, t)
+ // reverse_group_prerequisite_members (a, t)
+ //
+ template <typename T>
+ class prerequisite_members_range;
+
+ template <typename T>
+ inline prerequisite_members_range<T>
+ prerequisite_members (action a, T&& x, bool members = true)
+ {
+ return prerequisite_members_range<T> (a, std::forward<T> (x), members);
+ }
+
+ template <typename T>
+ class prerequisite_members_range
+ {
+ public:
+ prerequisite_members_range (action a, T&& r, bool m)
+ : a_ (a), members_ (m), r_ (std::forward<T> (r)), e_ (r_.end ()) {}
+
+ using base_iterator = decltype (std::declval<T> ().begin ());
+
+ struct iterator
+ {
+ typedef prerequisite_member value_type;
+ typedef const value_type* pointer;
+ typedef const value_type& reference;
+ typedef typename base_iterator::difference_type difference_type;
+ typedef std::forward_iterator_tag iterator_category;
+
+ iterator (): r_ (nullptr) {}
+ iterator (const prerequisite_members_range* r, const base_iterator& i)
+ : r_ (r), i_ (i), g_ {nullptr, 0}
+ {
+ if (r_->members_ && i_ != r_->e_ && i_->get ().type.see_through)
+ {
+ bool r (switch_members ());
+ assert (r); // Group could not be resolved.
+ }
+ }
+
+ iterator& operator++ ();
+ iterator operator++ (int) {iterator r (*this); operator++ (); return r;}
+
+ // Skip iterating over the rest of this group's members, if any.
+ // Note that the only valid operation after this call is to
+ // increment the iterator.
+ //
+ void
+ leave_group ()
+ {
+ // Pretend we are on the last member of some group.
+ //
+ j_ = 0;
+ g_.count = 1;
+ }
+
+ // Iterate over this group's members. Return false if the member
+ // information is not available. Similar to leave_group(), you
+ // should increment the iterator after calling this function
+ // (provided it returned true).
+ //
+ bool
+ enter_group ()
+ {
+ bool r (switch_members ());
+ if (r)
+ --j_; // Compensate for the increment that will follow.
+ return r;
+ }
+
+ value_type operator* () const
+ {
+ return value_type {*i_, g_.count != 0 ? g_.members[j_ - 1] : nullptr};
+ }
+
+ pointer operator-> () const
+ {
+ static_assert (
+ std::is_trivially_destructible<prerequisite_member>::value,
+ "prerequisite_member is not trivially destructible");
+
+ return new (&m_)
+ value_type {*i_, g_.count != 0 ? g_.members[j_ - 1] : nullptr};
+ }
+
+ friend bool
+ operator== (const iterator& x, const iterator& y)
+ {
+ return x.i_ == y.i_ &&
+ x.g_.count == y.g_.count &&
+ (x.g_.count == 0 || x.j_ == y.j_);
+ }
+
+ friend bool
+ operator!= (const iterator& x, const iterator& y) {return !(x == y);}
+
+ private:
+ bool
+ switch_members ();
+
+ private:
+ const prerequisite_members_range* r_;
+ base_iterator i_;
+ group_view g_;
+ std::size_t j_; // 1-based index, to support enter_group().
+ mutable std::aligned_storage<sizeof (prerequisite_member),
+ alignof (prerequisite_member)>::type m_;
+ };
+
+ iterator
+ begin () const {return iterator (this, r_.begin ());}
+
+ iterator
+ end () const {return iterator (this, e_);}
+
+ private:
+ action a_;
+ bool members_; // Go into group members by default?
+ T r_;
+ base_iterator e_;
+ };
+
+ // prerequisite_members(t.prerequisites)
+ //
+ inline auto
+ prerequisite_members (action a, target& t, bool members = true)
+ {
+ return prerequisite_members (a, t.prerequisites, members);
+ }
+
+ // prerequisite_members(reverse_iterate(t.prerequisites))
+ //
+ inline auto
+ reverse_prerequisite_members (action a, target& t, bool members = true)
+ {
+ return prerequisite_members (
+ a, butl::reverse_iterate (t.prerequisites), members);
+ }
+
+ // prerequisite_members(group_prerequisites (t))
+ //
+ inline auto
+ group_prerequisite_members (action a, target& t, bool members = true)
+ {
+ return prerequisite_members (a, group_prerequisites (t), members);
+ }
+
+ // prerequisite_members(reverse_iterate (group_prerequisites (t)))
+ //
+ inline auto
+ reverse_group_prerequisite_members (action a, target& t, bool members = true)
+ {
+ return prerequisite_members (
+ a, butl::reverse_iterate (group_prerequisites (t)), members);
+ }
+
+ //
+ //
+ struct target_set
+ {
+ typedef std::map<target_key, std::unique_ptr<target>> map;
+ typedef butl::map_iterator_adapter<map::const_iterator> iterator;
+
+ iterator
+ find (const target_key& k, tracer& trace) const;
+
+ iterator
+ find (const target_type& type,
+ const dir_path& dir,
+ const std::string& name,
+ const std::string* ext,
+ tracer& trace) const
+ {
+ return find (target_key {&type, &dir, &name, &ext}, trace);
+ }
+
+ // As above but ignore the extension and return the target or
+ // nullptr instead of the iterator.
+ //
+ template <typename T>
+ T*
+ find (const dir_path& dir, const std::string& name) const
+ {
+ const std::string* e (nullptr);
+ auto i (map_.find (target_key {&T::static_type, &dir, &name, &e}));
+ return i != map_.end () ? static_cast<T*> (i->second.get ()) : nullptr;
+ }
+
+ iterator begin () const {return map_.begin ();}
+ iterator end () const {return map_.end ();}
+
+ std::pair<target&, bool>
+ insert (const target_type&,
+ dir_path dir,
+ std::string name,
+ const std::string* ext,
+ tracer&);
+
+ template <typename T>
+ T&
+ insert (const dir_path& dir,
+ const std::string& name,
+ const std::string* ext,
+ tracer& t)
+ {
+ return static_cast<T&> (
+ insert (T::static_type, dir, name, ext, t).first);
+ }
+
+ template <typename T>
+ T&
+ insert (const dir_path& dir, const std::string& name, tracer& t)
+ {
+ return static_cast<T&> (
+ insert (T::static_type, dir, name, nullptr, t).first);
+ }
+
+ void
+ clear () {map_.clear ();}
+
+ private:
+ map map_;
+ };
+
+ extern target_set targets;
+
+ // Modification time-based target.
+ //
+ class mtime_target: public target
+ {
+ public:
+ using target::target;
+
+ // Generally, modification time for a target can only be queried
+ // after a rule has been matched since that's where the path is
+ // normally gets assigned. Normally, however, it would make sense
+ // to first execute the rule to get the "updated" timestamp.
+ //
+ // The rule for groups that utilize the group state is as follows:
+ // if it has any members that are mtime_targets, then the group
+ // should be mtime_target and the members get the mtime from it.
+ //
+ timestamp
+ mtime () const
+ {
+ const mtime_target* t (raw_state == target_state::group
+ ? static_cast<const mtime_target*> (group)
+ : this);
+
+ if (t->mtime_ == timestamp_unknown)
+ t->mtime_ = t->load_mtime ();
+
+ return t->mtime_;
+ }
+
+ void
+ mtime (timestamp mt)
+ {
+ // While we can cache the mtime at any time, it may be ignored
+ // if the target state is group (see the mtime() accessor).
+ //
+ mtime_ = mt;
+ }
+
+ protected:
+ virtual timestamp
+ load_mtime () const = 0;
+
+ public:
+ static const target_type static_type;
+
+ private:
+ mutable timestamp mtime_ {timestamp_unknown};
+ };
+
+ // Filesystem path-based target.
+ //
+ class path_target: public mtime_target
+ {
+ public:
+ using mtime_target::mtime_target;
+
+ typedef build2::path path_type;
+
+ const path_type&
+ path () const {return path_;}
+
+ void
+ path (path_type p) {assert (path_.empty ()); path_ = std::move (p);}
+
+ // Derive a path from target's dir, name, and, if specified, ext.
+ // If ext is not specified, then use default_ext and also update
+ // the target's extension (this becomes important if later we need
+ // to reliably determine whether this file has an extension; think
+ // hxx{foo.bar.} and hxx.ext is empty).
+ //
+ // If name_prefix is not NULL, add it before the name part and after
+ // the directory. Similarly, if name_suffix is not NULL, add it after
+ // the name part and before the extension.
+ //
+ // Finally, if the path was already assigned to this target, then
+ // this function verifies that the two are the same.
+ //
+ void
+ derive_path (const char* default_ext = nullptr,
+ const char* name_prefix = nullptr,
+ const char* name_suffix = nullptr);
+
+ public:
+ static const target_type static_type;
+
+ private:
+ path_type path_;
+ };
+
+ // File target.
+ //
+ class file: public path_target
+ {
+ public:
+ using path_target::path_target;
+
+ protected:
+ // Note that it is final in order to be consistent with file_rule,
+ // search_existing_file().
+ //
+ virtual timestamp
+ load_mtime () const final;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // Alias target. It represents a list of targets (its prerequisites)
+ // as a single "name".
+ //
+ class alias: public target
+ {
+ public:
+ using target::target;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // Directory target. Note that this is not a filesystem directory
+ // but rather an alias target with the directory name. For actual
+ // filesystem directory (creation), see fsdir.
+ //
+ class dir: public alias
+ {
+ public:
+ using alias::alias;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // While a filesystem directory is mtime-based, the semantics is
+ // not very useful in our case. In particular, if another target
+ // depends on fsdir{}, then all that's desired is the creation of
+ // the directory if it doesn't already exist. In particular, we
+ // don't want to update the target just because some unrelated
+ // entry was created in that directory.
+ //
+ class fsdir: public target
+ {
+ public:
+ using target::target;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class buildfile: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // Common documentation file targets.
+ //
+ // @@ Maybe these should be in the built-in doc module?
+ //
+ class doc: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // The problem with man pages is this: different platforms have
+ // different sets of sections. What seems to be the "sane" set
+ // is 1-9 (Linux and BSDs). SysV (e.g., Solaris) instead maps
+ // 8 to 1M (system administration). The section determines two
+ // things: the directory where the page is installed (e.g.,
+ // /usr/share/man/man1) as well as the extension of the file
+ // (e.g., test.1). Note also that there could be sub-sections,
+ // e.g., 1p (for POSIX). Such a page would still go into man1
+ // but will have the .1p extension (at least that's what happens
+ // on Linux). The challenge is to somehow handle this in a
+ // portable manner. So here is the plan:
+ //
+ // First of all, we have the man{} target type which can be used
+ // for a custom man page. That is, you can have any extension and
+ // install it anywhere you please:
+ //
+ // man{foo.X}: install = man/manX
+ //
+ // Then we have man1..9{} target types which model the "sane"
+ // section set and that would be automatically installed into
+ // correct locations on other platforms. In other words, the
+ // idea is that you should be able to have the foo.8 file,
+ // write man8{foo} and have it installed as man1m/foo.1m on
+ // some SysV host.
+ //
+ // Re-mapping the installation directory is easy: to help with
+ // that we have assigned install.man1..9 directory names. The
+ // messy part is to change the extension. It seems the only
+ // way to do that would be to have special logic for man pages
+ // in the generic install rule. @@ This is still a TODO.
+ //
+ // Note that handling subsections with man1..9{} is easy, we
+ // simply specify the extension explicitly, e.g., man{foo.1p}.
+ //
+ class man: public doc
+ {
+ public:
+ using doc::doc;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class man1: public man
+ {
+ public:
+ using man::man;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // Common implementation of the target factory, extension, and
+ // search functions.
+ //
+ template <typename T>
+ target*
+ target_factory (const target_type&, dir_path d, string n, const string* e)
+ {
+ return new T (move (d), move (n), e);
+ }
+
+ // Return fixed target extension.
+ //
+ template <const char* ext>
+ const std::string&
+ target_extension_fix (const target_key&, scope&);
+
+ // Get the extension from the variable or use the default if none set.
+ // Issue diagnostics and fail if the default is NULL.
+ //
+ template <const char* var, const char* def>
+ const std::string&
+ target_extension_var (const target_key&, scope&);
+
+ // The default behavior, that is, look for an existing target in the
+ // prerequisite's directory scope.
+ //
+ target*
+ search_target (const prerequisite_key&);
+
+ // First look for an existing target as above. If not found, then look
+ // for an existing file in the target-type-specific list of paths.
+ //
+ target*
+ search_file (const prerequisite_key&);
+
+}
+
+#include <build2/target.ixx>
+#include <build2/target.txx>
+
+#endif // BUILD2_TARGET