diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-12-01 13:39:09 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-12-01 13:42:10 +0200 |
commit | 7996c2bfc2d7e998e2f9f1236d457ec7bea8ad8a (patch) | |
tree | dca79d3657bec47d4cd5db85899a70d3d49c079e /build | |
parent | f355a4379f035df61a7702f5ff805eefb004fb20 (diff) |
Implement support for definition target type aliases
For example:
define cli=file
Currently, the semantics is that of a real alias with only name differences
that are used for display. See tests/define/buildfile for more use cases.
Diffstat (limited to 'build')
-rw-r--r-- | build/bin/target | 14 | ||||
-rw-r--r-- | build/bin/target.cxx | 41 | ||||
-rw-r--r-- | build/cli/target | 4 | ||||
-rw-r--r-- | build/cli/target.cxx | 8 | ||||
-rw-r--r-- | build/cxx/target | 12 | ||||
-rw-r--r-- | build/cxx/target.cxx | 18 | ||||
-rw-r--r-- | build/parser | 3 | ||||
-rw-r--r-- | build/parser.cxx | 76 | ||||
-rw-r--r-- | build/scope | 6 | ||||
-rw-r--r-- | build/scope.cxx | 15 | ||||
-rw-r--r-- | build/target | 35 | ||||
-rw-r--r-- | build/target-type | 48 | ||||
-rw-r--r-- | build/target.cxx | 39 | ||||
-rw-r--r-- | build/types | 9 | ||||
-rw-r--r-- | build/utility | 4 |
15 files changed, 246 insertions, 86 deletions
diff --git a/build/bin/target b/build/bin/target index 729f119..cbabcaf 100644 --- a/build/bin/target +++ b/build/bin/target @@ -19,8 +19,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class objso: public file @@ -29,8 +29,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class obj: public target @@ -42,8 +42,8 @@ namespace build objso* so {nullptr}; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class exe: public file @@ -52,8 +52,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; // The lib{} target group. @@ -64,8 +64,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class libso: public file @@ -74,8 +74,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class lib: public target @@ -90,8 +90,8 @@ namespace build reset (action_type); public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; } } diff --git a/build/bin/target.cxx b/build/bin/target.cxx index 5a1bb92..c2c5e05 100644 --- a/build/bin/target.cxx +++ b/build/bin/target.cxx @@ -11,10 +11,10 @@ namespace build namespace bin { static target* - obja_factory (dir_path d, std::string n, const std::string* e) + obja_factory (const target_type&, dir_path d, string n, const string* e) { obj* o (targets.find<obj> (d, n)); - obja* a (new obja (std::move (d), std::move (n), e)); + obja* a (new obja (move (d), move (n), e)); if ((a->group = o)) o->a = a; @@ -30,14 +30,15 @@ namespace build &obja_factory, nullptr, &search_target, // Note: not _file(); don't look for an existing file. - false + false, + nullptr }; static target* - objso_factory (dir_path d, std::string n, const std::string* e) + objso_factory (const target_type&, dir_path d, string n, const string* e) { obj* o (targets.find<obj> (d, n)); - objso* so (new objso (std::move (d), std::move (n), e)); + objso* so (new objso (move (d), move (n), e)); if ((so->group = o)) o->so = so; @@ -53,11 +54,12 @@ namespace build &objso_factory, nullptr, &search_target, // Note: not _file(); don't look for an existing file. - false + false, + nullptr }; static target* - obj_factory (dir_path d, string n, const string* e) + obj_factory (const target_type&, dir_path d, string n, const string* e) { obja* a (targets.find<obja> (d, n)); objso* so (targets.find<objso> (d, n)); @@ -80,7 +82,8 @@ namespace build &obj_factory, nullptr, &search_target, - false + false, + nullptr }; const target_type exe::static_type @@ -91,14 +94,15 @@ namespace build &target_factory<exe>, nullptr, &search_file, - false + false, + nullptr }; static target* - liba_factory (dir_path d, std::string n, const std::string* e) + liba_factory (const target_type&, dir_path d, string n, const string* e) { lib* l (targets.find<lib> (d, n)); - liba* a (new liba (std::move (d), std::move (n), e)); + liba* a (new liba (move (d), move (n), e)); if ((a->group = l)) l->a = a; @@ -127,14 +131,15 @@ namespace build &liba_factory, &target_extension_fix<a_ext>, &search_file, - false + false, + nullptr }; static target* - libso_factory (dir_path d, std::string n, const std::string* e) + libso_factory (const target_type&, dir_path d, string n, const string* e) { lib* l (targets.find<lib> (d, n)); - libso* so (new libso (std::move (d), std::move (n), e)); + libso* so (new libso (move (d), move (n), e)); if ((so->group = l)) l->so = so; @@ -151,7 +156,8 @@ namespace build &libso_factory, &target_extension_fix<so_ext>, &search_file, - false + false, + nullptr }; // lib @@ -164,7 +170,7 @@ namespace build } static target* - lib_factory (dir_path d, string n, const string* e) + lib_factory (const target_type&, dir_path d, string n, const string* e) { liba* a (targets.find<liba> (d, n)); libso* so (targets.find<libso> (d, n)); @@ -187,7 +193,8 @@ namespace build &lib_factory, nullptr, &search_target, - false + false, + nullptr }; } } diff --git a/build/cli/target b/build/cli/target index 7ee9a98..dbb05bd 100644 --- a/build/cli/target +++ b/build/cli/target @@ -19,8 +19,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class cli_cxx: public mtime_target @@ -53,8 +53,8 @@ namespace build load_mtime () const; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; } } diff --git a/build/cli/target.cxx b/build/cli/target.cxx index e8ca1cf..0666335 100644 --- a/build/cli/target.cxx +++ b/build/cli/target.cxx @@ -24,7 +24,8 @@ namespace build &target_factory<cli>, &target_extension_fix<cli_ext>, &search_file, - false + false, + nullptr }; // cli.cxx @@ -47,7 +48,7 @@ namespace build } static target* - cli_cxx_factory (dir_path d, string n, const string* e) + cli_cxx_factory (const target_type&, dir_path d, string n, const string* e) { tracer trace ("cli::cli_cxx_factory"); @@ -71,7 +72,8 @@ namespace build &cli_cxx_factory, nullptr, &search_target, - true // "See through" default iteration mode. + true, // "See through" default iteration mode. + nullptr }; } } diff --git a/build/cxx/target b/build/cxx/target index bcc5ff1..fe27c79 100644 --- a/build/cxx/target +++ b/build/cxx/target @@ -17,8 +17,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class ixx: public file @@ -27,8 +27,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class txx: public file @@ -37,8 +37,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class cxx: public file @@ -47,8 +47,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; //@@ TMP @@ -59,8 +59,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class c: public file @@ -69,8 +69,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; } } diff --git a/build/cxx/target.cxx b/build/cxx/target.cxx index c23fdd0..e3d98e7 100644 --- a/build/cxx/target.cxx +++ b/build/cxx/target.cxx @@ -19,7 +19,8 @@ namespace build &target_factory<hxx>, &target_extension_var<hxx_ext_var>, &search_file, - false + false, + nullptr }; constexpr const char ixx_ext_var[] = "ixx.ext"; @@ -31,7 +32,8 @@ namespace build &target_factory<ixx>, &target_extension_var<ixx_ext_var>, &search_file, - false + false, + nullptr }; constexpr const char txx_ext_var[] = "txx.ext"; @@ -43,7 +45,8 @@ namespace build &target_factory<txx>, &target_extension_var<txx_ext_var>, &search_file, - false + false, + nullptr }; constexpr const char cxx_ext_var[] = "cxx.ext"; @@ -55,7 +58,8 @@ namespace build &target_factory<cxx>, &target_extension_var<cxx_ext_var>, &search_file, - false + false, + nullptr }; constexpr const char h_ext_var[] = "h.ext"; @@ -67,7 +71,8 @@ namespace build &target_factory<h>, &target_extension_var<h_ext_var>, &search_file, - false + false, + nullptr }; constexpr const char c_ext_var[] = "c.ext"; @@ -79,7 +84,8 @@ namespace build &target_factory<c>, &target_extension_var<c_ext_var>, &search_file, - false + false, + nullptr }; } } diff --git a/build/parser b/build/parser index e37e68a..4390c67 100644 --- a/build/parser +++ b/build/parser @@ -72,6 +72,9 @@ namespace build using_ (token&, token_type&); void + define (token&, token_type&); + + void variable (token&, token_type&, std::string name, token_type kind); std::string diff --git a/build/parser.cxx b/build/parser.cxx index bc6caa9..4981347 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -12,6 +12,9 @@ #include <iterator> // make_move_iterator() #include <iostream> +#include <build/types> +#include <build/utility> + #include <build/token> #include <build/lexer> @@ -130,6 +133,11 @@ namespace build using_ (t, tt); continue; } + else if (n == "define") + { + define (t, tt); + continue; + } } // ': foo' is equvalent to '{}: foo' and to 'dir{}: foo'. @@ -343,7 +351,7 @@ namespace build const target_type* ti ( n.untyped () ? &target::static_type - : scope_->find_target_type (n.type.c_str ())); + : scope_->find_target_type (n.type)); if (ti == nullptr) fail (nloc) << "unknown target type " << n.type; @@ -799,6 +807,72 @@ namespace build fail (t) << "expected newline instead of " << t; } + static target* + alias_factory (const target_type& tt, dir_path d, string n, const string* e) + { + assert (tt.origin != nullptr); + target* r (tt.origin->factory (*tt.origin, move (d), move (n), e)); + r->alias_type = &tt; + return r; + } + + void parser:: + define (token& t, token_type& tt) + { + // define <alias>=<name> + // + // See tests/define/buildfile. + // + if (next (t, tt) != type::name) + fail (t) << "expected name instead of " << t << " in target type " + << "definition"; + + string a (move (t.value)); + const location al (get_location (t, &path_)); + + if (next (t, tt) != type::equal) + fail (t) << "expected '=' instead of " << t << " in target type " + << "definition"; + + next (t, tt); + + if (tt == type::name) + { + // Alias. + // + const string& n (t.value); + const target_type* ntt (scope_->find_target_type (n)); + + if (ntt == nullptr) + fail (t) << "unknown target type " << n; + + unique_ptr<target_type> att (new target_type (*ntt)); + att->factory = &alias_factory; + att->origin = ntt->origin != nullptr ? ntt->origin : ntt; + + target_type& ratt (*att); // Save non-const reference to the object. + + auto pr (scope_->target_types.emplace (a, target_type_ref (move (att)))); + + if (!pr.second) + fail (al) << "target type " << a << " already define in this scope"; + + // Patch the alias name to use the map's key storage. + // + ratt.name = pr.first->first.c_str (); + + next (t, tt); // Get newline. + } + else + fail (t) << "expected name instead of " << t << " in target type " + << "definition"; + + if (tt == type::newline) + next (t, tt); + else if (tt != type::eos) + fail (t) << "expected newline instead of " << t; + } + void parser:: print (token& t, token_type& tt) { diff --git a/build/scope b/build/scope index 2afc9f4..8b0d0ad 100644 --- a/build/scope +++ b/build/scope @@ -12,6 +12,8 @@ #include <butl/path-map> #include <build/types> +#include <build/utility> + #include <build/module> #include <build/variable> #include <build/prerequisite> @@ -138,7 +140,7 @@ namespace build target_type_map target_types; const target_type* - find_target_type (const char*, const scope** = nullptr) const; + find_target_type (const string&, const scope** = nullptr) const; // Given a name, figure out its type, taking into account extensions, // special names (e.g., '.' and '..'), or anything else that might be @@ -147,7 +149,7 @@ namespace build // necessarily normalized). Return NULL if not found. // const target_type* - find_target_type (name&, const std::string*& ext) const; + find_target_type (name&, const string*& ext) const; // Rules. // diff --git a/build/scope.cxx b/build/scope.cxx index 53e3a53..d407ce0 100644 --- a/build/scope.cxx +++ b/build/scope.cxx @@ -44,7 +44,7 @@ namespace build } const target_type* scope:: - find_target_type (const char* tt, const scope** rs) const + find_target_type (const string& tt, const scope** rs) const { // Search scopes outwards, stopping at the project root. // @@ -69,6 +69,9 @@ namespace build return nullptr; } + static const string dir_tt ("dir"); + static const string file_tt ("file"); + const target_type* scope:: find_target_type (name& n, const string*& ext) const { @@ -78,22 +81,22 @@ namespace build // First determine the target type. // - const char* tt; + const string* tt; if (n.untyped ()) { // Empty name or '.' and '..' signify a directory. // if (v.empty () || v == "." || v == "..") - tt = "dir"; + tt = &dir_tt; else //@@ TODO: derive type from extension. // - tt = "file"; + tt = &file_tt; } else - tt = n.type.c_str (); + tt = &n.type; - const target_type* r (find_target_type (tt)); + const target_type* r (find_target_type (*tt)); if (r == nullptr) return r; diff --git a/build/target b/build/target index a866c98..a1c7880 100644 --- a/build/target +++ b/build/target @@ -22,6 +22,8 @@ #include <butl/multi-index> // map_iterator_adapter #include <build/types> +#include <build/utility> + #include <build/scope> #include <build/variable> #include <build/operation> @@ -352,7 +354,18 @@ namespace build const T* is_a () const {return dynamic_cast<const T*> (this);} - virtual const target_type& type () const = 0; + // An alias target type should be the same as its target type except + // for the name. + // + const target_type* alias_type = nullptr; + + const target_type& + type () const + { + return alias_type != nullptr ? *alias_type : dynamic_type (); + } + + virtual const target_type& dynamic_type () const = 0; static const target_type static_type; private: @@ -897,8 +910,8 @@ namespace build load_mtime () const final; public: - virtual const target_type& type () const {return static_type;} 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) @@ -910,8 +923,8 @@ namespace build using target::target; public: - virtual const target_type& type () const {return static_type;} 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 @@ -924,8 +937,8 @@ namespace build using alias::alias; public: - virtual const target_type& type () const {return static_type;} 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 @@ -941,8 +954,8 @@ namespace build using target::target; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class buildfile: public file @@ -951,8 +964,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; // Common documentation file targets. @@ -965,8 +978,8 @@ namespace build using file::file; public: - virtual const target_type& type () const {return static_type;} 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 @@ -1009,8 +1022,8 @@ namespace build using doc::doc; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; class man1: public man @@ -1019,8 +1032,8 @@ namespace build using man::man; public: - virtual const target_type& type () const {return static_type;} static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} }; // Common implementation of the target factory, extension, and @@ -1028,9 +1041,9 @@ namespace build // template <typename T> target* - target_factory (dir_path d, std::string n, const std::string* e) + target_factory (const target_type&, dir_path d, string n, const string* e) { - return new T (std::move (d), std::move (n), e); + return new T (move (d), move (n), e); } // Return fixed target extension. diff --git a/build/target-type b/build/target-type index 154714c..2c93455 100644 --- a/build/target-type +++ b/build/target-type @@ -9,7 +9,6 @@ #include <string> #include <ostream> #include <typeindex> -#include <functional> // reference_wrapper #include <butl/utility> // compare_c_string @@ -29,11 +28,13 @@ namespace build std::type_index id; const char* name; const target_type* base; - target* (*const factory) (dir_path, std::string, const std::string*); - const std::string& (*const extension) (const target_key&, scope&); - target* (*const search) (const prerequisite_key&); + target* (*factory) (const target_type&, dir_path, string, const string*); + const string& (*extension) (const target_key&, scope&); + target* (*search) (const prerequisite_key&); bool see_through; // A group with the default "see through" semantics. + const target_type* origin; // Original target if this is an alias. + bool is_a (const std::type_index&) const; // Defined in target.cxx @@ -56,16 +57,45 @@ namespace build // Target type map. // - using target_type_map_base = std::map< - const char*, - std::reference_wrapper<const target_type>, - butl::compare_c_string>; + struct target_type_ref + { + // Like reference_wrapper except it deletes the target type if it is + // an alias (aliases are always dynamically allocated). + // + explicit + target_type_ref (const target_type& r): p_ (&r) + { + assert (p_->origin == nullptr); + } + + explicit + target_type_ref (unique_ptr<target_type>&& p): p_ (p.release ()) + { + assert (p_->origin != nullptr); + } + + ~target_type_ref () + { + if (p_ != nullptr && p_->origin != nullptr) + delete p_; + } + + explicit operator const target_type& () const {return *p_;} + const target_type& get () const {return *p_;} + + target_type_ref (target_type_ref&& r): p_ (r.p_) {r.p_ = nullptr;} + + private: + const target_type* p_; + }; + + using target_type_map_base = std::map<string, target_type_ref>; class target_type_map: public target_type_map_base { public: void - insert (const target_type& tt) {emplace (tt.name, tt);} + insert (const target_type& tt) {emplace (tt.name, target_type_ref (tt));} template <typename T> void diff --git a/build/target.cxx b/build/target.cxx index 22c9ad4..649e570 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -286,7 +286,7 @@ namespace build if (r) { - unique_ptr<target> pt (tt.factory (move (dir), move (name), ext)); + unique_ptr<target> pt (tt.factory (tt, move (dir), move (name), ext)); i = map_.emplace ( make_pair (target_key {&tt, &pt->dir, &pt->name, &pt->ext}, move (pt))).first; @@ -446,7 +446,8 @@ namespace build nullptr, nullptr, &search_target, - false + false, + nullptr }; const target_type mtime_target::static_type @@ -457,7 +458,8 @@ namespace build nullptr, nullptr, &search_target, - false + false, + nullptr }; const target_type path_target::static_type @@ -468,12 +470,13 @@ namespace build nullptr, nullptr, &search_target, - false + false, + nullptr }; template <typename T> static target* - file_factory (dir_path d, string n, const string* e) + file_factory (const target_type&, dir_path d, string n, const string* e) { // The file target type doesn't imply any extension. So if one // wasn't specified, set it to empty rather than unspecified. @@ -493,7 +496,8 @@ namespace build &file_factory<file>, &target_extension_fix<file_ext>, &search_file, - false + false, + nullptr }; const target_type alias::static_type @@ -504,7 +508,8 @@ namespace build &target_factory<alias>, nullptr, // Should never need. &search_alias, - false + false, + nullptr }; const target_type dir::static_type @@ -515,7 +520,8 @@ namespace build &target_factory<dir>, nullptr, // Should never need. &search_alias, - false + false, + nullptr }; const target_type fsdir::static_type @@ -526,7 +532,8 @@ namespace build &target_factory<fsdir>, nullptr, // Should never need. &search_target, - false + false, + nullptr }; static const std::string& @@ -546,7 +553,8 @@ namespace build &file_factory<buildfile>, &buildfile_target_extension, &search_file, - false + false, + nullptr }; constexpr const char doc_ext[] = ""; @@ -558,11 +566,12 @@ namespace build &file_factory<doc>, &target_extension_fix<doc_ext>, &search_file, - false + false, + nullptr }; static target* - man_factory (dir_path d, string n, const string* e) + man_factory (const target_type&, dir_path d, string n, const string* e) { if (e == nullptr) fail << "man target '" << n << "' must include extension (man section)"; @@ -578,7 +587,8 @@ namespace build &man_factory, nullptr, // Should be specified explicitly. &search_file, - false + false, + nullptr }; constexpr const char man1_ext[] = "1"; @@ -590,6 +600,7 @@ namespace build &file_factory<man1>, &target_extension_fix<man1_ext>, &search_file, - false + false, + nullptr }; } diff --git a/build/types b/build/types index 0a71252..c119839 100644 --- a/build/types +++ b/build/types @@ -7,6 +7,8 @@ #include <vector> #include <string> +#include <memory> // unique_ptr, shared_ptr +#include <functional> // reference_wrapper #include <butl/path> #include <butl/timestamp> @@ -17,7 +19,12 @@ namespace build { // Commonly-used types. // - using strings = std::vector<std::string>; + using std::string; + using std::unique_ptr; + using std::shared_ptr; + using std::reference_wrapper; + + using strings = std::vector<string>; using cstrings = std::vector<const char*>; // <butl/path> diff --git a/build/utility b/build/utility index 42d7f0f..7e6b0df 100644 --- a/build/utility +++ b/build/utility @@ -7,7 +7,7 @@ #include <tuple> #include <string> -#include <utility> +#include <utility> // move() #include <exception> #include <unordered_set> @@ -15,6 +15,8 @@ namespace build { + using std::move; + // Empty string and path. // extern const std::string empty_string; |