From 43a1c24b089ae92bcf9a80584ebdf4c4011b9664 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 19 May 2018 09:02:17 +0200 Subject: Support for deriving target type from file name, handle testscript, buildfile In particular, instead of: exe{test}: test{testscript} We should now write: exe{test}: testscript --- build2/context.cxx | 5 +++ build2/scope.cxx | 94 +++++++++++++++++++++++++------------------ build2/target-type.hxx | 105 ++++++++++++++++++++++++++++++++++++------------- build2/test/init.cxx | 3 +- tests/hooks/buildfile | 2 +- 5 files changed, 141 insertions(+), 68 deletions(-) diff --git a/build2/context.cxx b/build2/context.cxx index 48776b0..7067d8f 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -524,6 +524,11 @@ namespace build2 t.insert (); t.insert (); t.insert (); + + { + auto& tt (t.insert ()); + t.insert_file ("buildfile", tt); + } } // Parse and enter the command line variables. We do it before entering diff --git a/build2/scope.cxx b/build2/scope.cxx index e5762f1..8ec2c8c 100644 --- a/build2/scope.cxx +++ b/build2/scope.cxx @@ -551,56 +551,67 @@ namespace build2 if (s->target_types.empty ()) continue; - auto i (s->target_types.find (tt)); - - if (i != s->target_types.end ()) + if (const target_type* r = s->target_types.find (tt)) { if (rs != nullptr) *rs = s; - return &i->second.get (); + return r; } } return nullptr; } - static const string dir_tt ("dir"); - static const string file_tt ("file"); + // Find target type from file name. + // + static const target_type* + find_file_target_type (const scope* s, const string& n) + { + // Pretty much the same logic as in find_target_type() above. + // + for (; s != nullptr; s = s->root () ? global_scope : s->parent_scope ()) + { + if (s->target_types.empty ()) + continue; + + if (const target_type* r = s->target_types.find_file (n)) + return r; + } + + return nullptr; + } const target_type* scope:: find_target_type (name& n, optional& ext, const location& loc) const { ext = nullopt; - string& v (n.value); - // First determine the target type. + // If the target type is specified, resolve it and bail out if not found. + // Otherwise, we know in the end it will resolve to something (if nothing + // else, either dir{} or file{}), so we can go ahead and process the name. // - const string* tt; - if (n.untyped ()) + const target_type* r (nullptr); + if (n.typed ()) + { + r = find_target_type (n.type); + + if (r == nullptr) + return r; + } + else { - // Empty name or '.' and '..' signify a directory. + // Empty name as well as '.' and '..' signify a directory. // if (v.empty () || v == "." || v == "..") - tt = &dir_tt; - else - //@@ TODO: derive type from extension. - // - tt = &file_tt; + r = &dir::static_type; } - else - tt = &n.type; - - const target_type* r (find_target_type (*tt)); - - if (r == nullptr) - return r; // Directories require special name processing. If we find that more // targets deviate, then we should make this target-type-specific. // - if (r->is_a () || r->is_a ()) + if (r != nullptr && (r->is_a () || r->is_a ())) { // The canonical representation of a directory name is with empty // value. @@ -613,10 +624,9 @@ namespace build2 } else if (!v.empty ()) { - // Split the path into its directory part (if any) the name part, - // and the extension (if any). We cannot assume the name part is - // a valid filesystem name so we will have to do the splitting - // manually. + // Split the path into its directory part (if any) the name part, and + // the extension (if any). We cannot assume the name part is a valid + // filesystem name so we will have to do the splitting manually. // path::size_type i (path::traits::rfind_separator (v)); @@ -648,6 +658,22 @@ namespace build2 } } + // If the target type is still unknown, map it using the name/extension, + // falling back to file{}. + // + if (r == nullptr) + { + // We only consider files without extension for file name mapping. + // + if (!ext) + r = find_file_target_type (this, v); + + //@@ TODO: derive type from extension. + + if (r == nullptr) + r = &file::static_type; + } + return r; } @@ -724,17 +750,7 @@ namespace build2 ? &target_print_0_ext_verb // Fixed extension, no use printing. : nullptr; // Normal. - target_type& rdt (*dt); // Save a non-const reference to the object. - - auto pr (target_types.emplace (name, target_type_ref (move (dt)))); - - // Patch the alias name to use the map's key storage. - // - if (pr.second) - rdt.name = pr.first->first.c_str (); - - return pair, bool> ( - pr.first->second.get (), pr.second); + return target_types.insert (name, move (dt)); } scope* scope::global_; diff --git a/build2/target-type.hxx b/build2/target-type.hxx index 16513d4..3e519f4 100644 --- a/build2/target-type.hxx +++ b/build2/target-type.hxx @@ -91,45 +91,96 @@ namespace build2 // Target type map. // - struct target_type_ref + class target_type_map { - // Like reference_wrapper except it sometimes deletes the target type. + public: + // Target type name to target type mapping. // - explicit - target_type_ref (const target_type& r): p_ (&r), d_ (false) {} - - explicit - target_type_ref (unique_ptr&& p) - : p_ (p.release ()), d_ (true) {} - - target_type_ref (target_type_ref&& r) - : p_ (r.p_), d_ (r.d_) {r.p_ = nullptr;} - - ~target_type_ref () {if (p_ != nullptr && d_) delete p_;} - - explicit operator const target_type& () const {return *p_;} - const target_type& get () const {return *p_;} - - private: - const target_type* p_; - bool d_; - }; + const target_type* + find (const string& n) const + { + auto i (type_map_.find (n)); + return i != type_map_.end () ? &i->second.get () : nullptr; + } - using target_type_map_base = std::map; + bool + empty () const + { + return type_map_.empty (); + } - class target_type_map: public target_type_map_base - { - public: const target_type& insert (const target_type& tt) { - emplace (tt.name, target_type_ref (tt)); + type_map_.emplace (tt.name, target_type_ref (tt)); return tt; } template const target_type& - insert () {return insert (T::static_type);} + insert () + { + return insert (T::static_type); + } + + pair, bool> + insert (const string& n, unique_ptr&& tt) + { + target_type& rtt (*tt); // Save a non-const reference to the object. + + auto p (type_map_.emplace (n, target_type_ref (move (tt)))); + + // Patch the alias name to use the map's key storage. + // + if (p.second) + rtt.name = p.first->first.c_str (); + + return pair, bool> ( + p.first->second.get (), p.second); + } + + // File name to target type mapping. + // + const target_type* + find_file (const string& n) const + { + auto i (file_map_.find (n)); + return i != file_map_.end () ? &i->second.get () : nullptr; + } + + void + insert_file (const string& n, const target_type& tt) + { + file_map_.emplace (n, tt); + } + + private: + struct target_type_ref + { + // Like reference_wrapper except it sometimes deletes the target type. + // + explicit + target_type_ref (const target_type& r): p_ (&r), d_ (false) {} + + explicit + target_type_ref (unique_ptr&& p) + : p_ (p.release ()), d_ (true) {} + + target_type_ref (target_type_ref&& r) + : p_ (r.p_), d_ (r.d_) {r.p_ = nullptr;} + + ~target_type_ref () {if (p_ != nullptr && d_) delete p_;} + + explicit operator const target_type& () const {return *p_;} + const target_type& get () const {return *p_;} + + private: + const target_type* p_; + bool d_; + }; + + std::map type_map_; + std::map> file_map_; }; } diff --git a/build2/test/init.cxx b/build2/test/init.cxx index 6d28e9e..036f41c 100644 --- a/build2/test/init.cxx +++ b/build2/test/init.cxx @@ -202,7 +202,8 @@ namespace build2 { auto& t (rs.target_types); - t.insert (); + auto& tt (t.insert ()); + t.insert_file ("testscript", tt); } // Register our test running rule. diff --git a/tests/hooks/buildfile b/tests/hooks/buildfile index c951d56..1bb5fa4 100644 --- a/tests/hooks/buildfile +++ b/tests/hooks/buildfile @@ -2,4 +2,4 @@ # copyright : Copyright (c) 2014-2017 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -./: test{testscript} $b +./: testscript $b -- cgit v1.1