// file : build/target -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file #ifndef BUILD_TARGET #define BUILD_TARGET #include #include #include #include // unique_ptr #include // function, reference_wrapper #include #include #include #include // move #include #include #include #include #include // compare_*, extension_pool namespace build { class target; enum class target_state {unknown, uptodate, updated, failed}; // Note: should throw rather than returning target_state::failed. // typedef std::function recipe; struct target_type { std::type_index id; const char* name; const target_type* base; target* (*const factory) (path, std::string, const std::string*); target* (*const search) (prerequisite&); }; inline std::ostream& operator<< (std::ostream& os, const target_type& tt) { return os << tt.name; } class target { public: target (path d, std::string n, const std::string* e) : dir (std::move (d)), name (std::move (n)), ext (e) {} const path dir; // Absolute and normalized. const std::string name; const std::string* ext; // Extension, NULL means unspecified. public: typedef std::vector> prerequisites_type; prerequisites_type prerequisites; public: typedef build::recipe recipe_type; const recipe_type& recipe () const {return recipe_;} void recipe (recipe_type r) {assert (!recipe_); recipe_ = r;} public: target_state state () const {return state_;} void state (target_state s) {state_ = s;} private: target (const target&) = delete; target& operator= (const target&) = delete; public: virtual const target_type& type () const = 0; static const target_type static_type; private: recipe_type recipe_; target_state state_ {target_state::unknown}; }; std::ostream& operator<< (std::ostream&, const target&); struct target_set { struct key { mutable const std::type_index* type; mutable const path* dir; mutable const std::string* name; mutable const std::string** ext; friend bool operator< (const key& x, const key& y) { //@@ TODO: use compare() to compare once. // Unspecified and specified extension are assumed equal. The // extension strings are from the pool, so we can just compare // pointers. // return (*x.type < *y.type) || (*x.type == *y.type && *x.name < *y.name) || (*x.type == *y.type && *x.name == *y.name && *x.dir < *y.dir) || (*x.type == *y.type && *x.name == *y.name && *x.dir == *y.dir && *x.ext != nullptr && *y.ext != nullptr && **x.ext < **y.ext); } }; typedef std::map> map; typedef map_iterator_adapter iterator; iterator find (const key& k, tracer& trace) const; iterator find (const std::type_index& type, const path& dir, const std::string& name, const std::string*& ext, tracer& trace) const { return find (key {&type, &dir, &name, &ext}, trace); } iterator begin () const {return map_.begin ();} iterator end () const {return map_.end ();} std::pair insert (const target_type&, path dir, std::string name, const std::string* ext, tracer&); private: map map_; }; extern target_set targets; extern target* default_target; class target_type_map: public std::map< const char*, std::reference_wrapper, compare_c_string> { public: void insert (const target_type& tt) {emplace (tt.name, tt);} }; extern target_type_map target_types; template target* target_factory (path d, std::string n, const std::string* e) { return new T (std::move (d), std::move (n), e); } // Modification time-based target. // class mtime_target: public target { public: using target::target; timestamp mtime () const { if (mtime_ == timestamp_unknown) mtime_ = load_mtime (); return mtime_; } void mtime (timestamp mt) {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 build::path path_type; const path_type& path () const {return path_;} void path (path_type p) {assert (path_.empty ()); path_ = std::move (p);} protected: virtual timestamp load_mtime () const; public: static const target_type static_type; private: path_type path_; }; // File target. // class file: public path_target { public: using path_target::path_target; public: virtual const target_type& type () const {return static_type;} static const target_type static_type; }; // Directory alias/action target. Note that it is not mtime-based. // Rather it is meant to represent a group of targets. For actual // filesystem directory (creation), see fsdir. // class dir: public target { public: using target::target; public: virtual const target_type& type () const {return static_type;} static const target_type 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: virtual const target_type& type () const {return static_type;} static const target_type static_type; }; } #endif // BUILD_TARGET