diff options
Diffstat (limited to 'libbuild2/target.cxx')
-rw-r--r-- | libbuild2/target.cxx | 670 |
1 files changed, 514 insertions, 156 deletions
diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx index 6647f75..2a134a4 100644 --- a/libbuild2/target.cxx +++ b/libbuild2/target.cxx @@ -22,26 +22,13 @@ namespace build2 bool target_type:: is_a (const char* n) const { - if (strcmp (name, n) == 0) - return true; - - for (const target_type* b (base); b != nullptr; b = b->base) + for (const target_type* b (this); b != nullptr; b = b->base) if (strcmp (b->name, n) == 0) return true; return false; } - bool target_type:: - is_a_base (const target_type& tt) const - { - for (const target_type* b (base); b != nullptr; b = b->base) - if (*b == tt) - return true; - - return false; - } - // target_key // void target_key:: @@ -51,7 +38,9 @@ namespace build2 if (!name->empty ()) { v = *name; - target::combine_name (v, ext, false /* @@ TODO: what to do? */); + // @@ TMP: see also other calls to combine_name() -- need to fix. + // + target::combine_name (v, ext, false /* @@ TMP: what to do? */); } else assert (!ext || ext->empty ()); // Unspecified or none. @@ -69,6 +58,7 @@ namespace build2 // static const char* const target_state_[] = { + "<invalid>", // Absent/invalid (see target_state for details). "unknown", "unchanged", "postponed", @@ -78,10 +68,10 @@ namespace build2 "group" }; - ostream& - operator<< (ostream& os, target_state ts) + string + to_string (target_state ts) { - return os << target_state_[static_cast<uint8_t> (ts)]; + return target_state_[static_cast<uint8_t> (ts)]; } // target @@ -91,7 +81,6 @@ namespace build2 target:: ~target () { - clear_data (); } const string& target:: @@ -122,31 +111,40 @@ namespace build2 group_view target:: group_members (action) const { - assert (false); // Not a group or doesn't expose its members. + // Not a group or doesn't expose its members. + // return group_view {nullptr, 0}; } const scope& target:: - base_scope () const + base_scope_impl () const { // If this target is from the src tree, use its out directory to find // the scope. // - return ctx.scopes.find (out_dir ()); - } + const scope& s (ctx.scopes.find_out (out_dir ())); - const scope& target:: - root_scope () const - { - // This is tricky to cache so we do the lookup for now. + // Cache unless we are in the load phase. // - const scope* r (base_scope ().root_scope ()); - assert (r != nullptr); - return *r; + if (ctx.phase != run_phase::load) + { + const scope* e (nullptr); + if (!base_scope_.compare_exchange_strong ( + e, + &s, + memory_order_release, + memory_order_consume)) + assert (e == &s); + } + + return s; } pair<lookup, size_t> target:: - lookup_original (const variable& var, bool target_only) const + lookup_original (const variable& var, + bool target_only, + const scope* bs, + bool locked) const { pair<lookup_type, size_t> r (lookup_type (), 0); @@ -157,37 +155,72 @@ namespace build2 r.first = lookup_type (*p.first, p.second, vars); } - const target* g (nullptr); + const target* g1 (nullptr); + const target* g2 (nullptr); if (!r.first) { ++r.second; + // While we went back to not treating the first member as a group for + // variable lookup, let's keep this logic in case one day we end up with + // a separate ad hoc group target. + // +#if 0 + // In case of an ad hoc group, we may have to look in two groups. + // + if ((g1 = group) != nullptr) + { + auto p (g1->vars.lookup (var)); + if (p.first != nullptr) + r.first = lookup_type (*p.first, p.second, g1->vars); + else + { + if ((g2 = g1->group) != nullptr) + { + auto p (g2->vars.lookup (var)); + if (p.first != nullptr) + r.first = lookup_type (*p.first, p.second, g2->vars); + } + } + } +#else // Skip looking up in the ad hoc group, which is semantically the // first/primary member. // - if ((g = group == nullptr + if ((g1 = group == nullptr ? nullptr : group->adhoc_group () ? group->group : group)) { - auto p (g->vars.lookup (var)); + auto p (g1->vars.lookup (var)); if (p.first != nullptr) - r.first = lookup_type (*p.first, p.second, g->vars); + r.first = lookup_type (*p.first, p.second, g1->vars); } +#endif } - // Delegate to scope's find_original(). + // Delegate to scope's lookup_original(). // if (!r.first) { if (!target_only) { - auto p (base_scope ().lookup_original ( - var, - &type (), - &name, - g != nullptr ? &g->type () : nullptr, - g != nullptr ? &g->name : nullptr)); + auto key = [locked] (const target* t) + { + return locked ? t->key_locked () : t->key (); + }; + + target_key tk (key (this)); + target_key g1k (g1 != nullptr ? key (g1) : target_key {}); + target_key g2k (g2 != nullptr ? key (g2) : target_key {}); + + if (bs == nullptr) + bs = &base_scope (); + + auto p (bs->lookup_original (var, + &tk, + g1 != nullptr ? &g1k : nullptr, + g2 != nullptr ? &g2k : nullptr)); r.first = move (p.first); r.second = r.first ? r.second + p.second : p.second; @@ -200,14 +233,30 @@ namespace build2 } value& target:: - append (const variable& var) + append (const variable& var, const scope* bs) { // Note: see also prerequisite::append() if changing anything here. // Note that here we want the original value without any overrides // applied. // - auto l (lookup_original (var).first); + auto l (lookup_original (var, false, bs).first); + + if (l.defined () && l.belongs (*this)) // Existing var in this target. + return vars.modify (l); // Ok since this is original. + + value& r (assign (var)); // NULL. + + if (l.defined ()) + r = *l; // Copy value (and type) from the outer scope. + + return r; + } + + value& target:: + append_locked (const variable& var, const scope* bs) + { + auto l (lookup_original (var, false, bs, true /* locked */).first); if (l.defined () && l.belongs (*this)) // Existing var in this target. return vars.modify (l); // Ok since this is original. @@ -232,7 +281,7 @@ namespace build2 r.first = lookup_type (*p.first, p.second, vars); } - // Delegate to target's find_original(). + // Delegate to target's lookup_original(). // if (!r.first) { @@ -527,28 +576,128 @@ namespace build2 // include() // + // See var_include documentation for details on what's going on here. + // include_type include_impl (action a, const target& t, - const string& v, const prerequisite& p, - const target* m) + const target* m, + lookup* rl) { context& ctx (t.ctx); - include_type r (false); + include_type r (include_type::normal); + { + lookup l (p.vars[ctx.var_include]); - if (v == "false") r = include_type::excluded; - else if (v == "adhoc") r = include_type::adhoc; - else if (v == "true") r = include_type::normal; - else - fail << "invalid " << ctx.var_include->name << " variable value " - << "'" << v << "' specified for prerequisite " << p; + if (l.defined ()) + { + if (l->null) + { + // @@ TMP (added in 0.16.0). + // + warn << "null " << *ctx.var_include << " variable value specified " + << "for prerequisite " << p << + info << "treated as undefined for backwards compatibility" << + info << "this warning will become error in the future"; + } + else + { + const string& v (cast<string> (*l)); + + if (v == "false") r = include_type::excluded; + else if (v == "true") r = include_type::normal; + else if (v == "adhoc") r = include_type::adhoc; + else if (v == "posthoc") r = include_type::posthoc; + else + fail << "invalid " << *ctx.var_include << " variable value '" + << v << "' specified for prerequisite " << p; + } + } + } + + // Handle operation-specific override (see var_include documentation + // for details). + // + lookup l; + optional<bool> r1; // Absent means something other than true|false. + + names storage; + names_view ns; + const variable* ovar (nullptr); + + if (r != include_type::excluded) + { + // Instead of going via potentially expensive target::base_scope(), use + // the prerequisite's scope; while it may not be the same as the + // targets's base scope, they must have the same root scope. + // + const scope& rs (*p.scope.root_scope ()); + + ovar = rs.root_extra->operations[ + (a.outer () + ? ctx.current_outer_oif + : ctx.current_inner_oif)->id].ovar; + + if (ovar != nullptr) + { + l = p.vars[*ovar]; + + if (l.defined ()) + { + if (l->null) + fail << "null " << *ovar << " variable value specified for " + << "prerequisite " << p; + + // Maybe we should optimize this for the common cases (bool, path, + // name)? But then again we don't expect many such overrides. Plus + // will complicate the diagnostics below. + // + ns = reverse (*l, storage, true /* reduce */); + + if (ns.size () == 1) + { + const name& n (ns[0]); + + if (n.simple ()) + { + const string& v (n.value); + + if (v == "false") + r1 = false; + else if (v == "true") + r1 = true; + } + } + + if (r1 && !*r1) + r = include_type::excluded; + } + } + } // Call the meta-operation override, if any (currently used by dist). // - if (auto f = ctx.current_mif->include) - r = f (a, t, prerequisite_member {p, m}, r); + if (r != include_type::normal || l) + { + if (auto f = ctx.current_mif->include) + r = f (a, t, prerequisite_member {p, m}, r, l); + } + + if (l) + { + if (rl != nullptr) + *rl = l; + else if (!r1) + { + // Note: we have to delay this until the meta-operation callback above + // had a chance to override it. + // + fail << "unrecognized " << *ovar << " variable value '" << ns + << "' specified for prerequisite " << p; + } + } return r; } @@ -558,7 +707,9 @@ namespace build2 const target* target_set:: find (const target_key& k, tracer& trace) const { - slock sl (mutex_); + bool load (ctx.phase == run_phase::load); + + slock sl (mutex_, defer_lock); if (!load) sl.lock (); map_type::const_iterator i (map_.find (k)); if (i == map_.end ()) @@ -577,14 +728,18 @@ namespace build2 // Between us releasing the shared lock and acquiring unique the // extension could change and possibly a new target that matches the // key could be inserted. In this case we simply re-run find (). + // Naturally, can't happen during load. // - sl.unlock (); - ul = ulock (mutex_); - - if (ext) // Someone set the extension. + if (!load) { - ul.unlock (); - return find (k, trace); + sl.unlock (); + ul = ulock (mutex_); + + if (ext) // Someone set the extension. + { + ul.unlock (); + return find (k, trace); + } } } @@ -618,10 +773,12 @@ namespace build2 string name, optional<string> ext, target_decl decl, - tracer& trace) + tracer& trace, + bool skip_find, + bool need_lock) { target_key tk {&tt, &dir, &out, &name, move (ext)}; - target* t (const_cast<target*> (find (tk, trace))); + target* t (skip_find ? nullptr : const_cast<target*> (find (tk, trace))); if (t == nullptr) { @@ -642,7 +799,9 @@ namespace build2 // case we proceed pretty much like find() except already under the // exclusive lock. // - ulock ul (mutex_); + ulock ul (mutex_, defer_lock); + if (ctx.phase != run_phase::load || need_lock) + ul.lock (); auto p (map_.emplace (target_key {&tt, &t->dir, &t->out, &t->name, e}, unique_ptr<target> (t))); @@ -651,10 +810,28 @@ namespace build2 if (p.second) { +#if 0 + { + size_t n (map_.bucket_count ()); + if (n > buckets_) + { + text << "target_set buckets: " << buckets_ << " -> " << n + << " (" << map_.size () << ")"; + buckets_ = n; + } + } +#endif + t->ext_ = &i->first.ext; t->decl = decl; t->state.inner.target_ = t; t->state.outer.target_ = t; + t->state.inner.vars.target_ = t; + t->state.outer.vars.target_ = t; + + if (ctx.phase != run_phase::load && !need_lock) + ul.unlock (); + return pair<target&, ulock> (*t, move (ul)); } @@ -706,9 +883,14 @@ namespace build2 static const optional<string> unknown_ext ("?"); - ostream& - to_stream (ostream& os, const target_key& k, optional<stream_verbosity> osv) + bool + to_stream (ostream& os, + const target_key& k, + optional<stream_verbosity> osv, + bool name_only) { + // Note: similar code in print_diag_impl(vector<target_key>). + stream_verbosity sv (osv ? *osv : stream_verb (os)); uint16_t dv (sv.path); uint16_t ev (sv.extension); @@ -718,22 +900,29 @@ namespace build2 // bool n (!k.name->empty ()); - // Note: relative() returns empty for './'. - // - const dir_path& rd (dv < 1 ? relative (*k.dir) : *k.dir); // Relative. - const dir_path& pd (n ? rd : rd.directory ()); // Parent. + const target_type& tt (*k.type); - if (!pd.empty ()) + dir_path rds; // Storage. + if (!name_only) { + // Note: relative() returns empty for './'. + // if (dv < 1) - os << diag_relative (pd); - else - to_stream (os, pd, true /* representation */); - } + rds = relative (*k.dir); - const target_type& tt (*k.type); + const dir_path& rd (dv < 1 ? rds : *k.dir); // Relative. + const dir_path& pd (n ? rd : rd.directory ()); // Parent. + + if (!pd.empty ()) + { + if (dv < 1) + os << diag_relative (pd); + else + to_stream (os, pd, true /* representation */); + } - os << tt.name << '{'; + os << tt.name << '{'; + } if (n) { @@ -776,37 +965,47 @@ namespace build2 } } else + { + if (name_only && dv < 1) // Already done if !name_only. + rds = relative (*k.dir); + + const dir_path& rd (dv < 1 ? rds : *k.dir); + to_stream (os, rd.empty () ? dir_path (".") : rd.leaf (), true /* representation */); + } - os << '}'; - - // If this target is from src, print its out. - // - if (!k.out->empty ()) + if (!name_only) { - if (dv < 1) + os << '}'; + + // If this target is from src, print its out. + // + if (!k.out->empty ()) { - // Don't print '@./'. - // - const string& o (diag_relative (*k.out, false)); + if (dv < 1) + { + // Don't print '@./'. + // + const string& o (diag_relative (*k.out, false)); - if (!o.empty ()) - os << '@' << o; + if (!o.empty ()) + os << '@' << o; + } + else + os << '@' << *k.out; } - else - os << '@' << *k.out; } - return os; + return n; // Regular if we had the name. } ostream& operator<< (ostream& os, const target_key& k) { if (auto p = k.type->print) - p (os, k); + p (os, k, false /* name_only */); else to_stream (os, k, stream_verb (os)); @@ -827,14 +1026,19 @@ namespace build2 case run_phase::load: break; case run_phase::match: { - // Similar logic to matched_state_impl(). + // Similar logic to target::matched(). // const opstate& s (state[action () /* inner */]); - // Note: already synchronized. - size_t o (s.task_count.load (memory_order_relaxed) - ctx.count_base ()); + // Note: use acquire for group_state(). + // + size_t c (s.task_count.load (memory_order_acquire)); + size_t b (ctx.count_base ()); // Note: cannot do (c - b)! - if (o != offset_applied && o != offset_executed) + if (!(c == (b + offset_applied) || + c == (b + offset_executed) || + (c >= (b + offset_busy) && + s.match_extra.cur_options_.load (memory_order_relaxed) != 0))) break; } // Fall through. @@ -957,41 +1161,60 @@ namespace build2 // const target* - target_search (const target& t, const prerequisite_key& pk) + target_search (context& ctx, const target*, const prerequisite_key& pk) { // The default behavior is to look for an existing target in the // prerequisite's directory scope. // - return search_existing_target (t.ctx, pk); + return search_existing_target (ctx, pk, true /* out_only */); } const target* - file_search (const target& t, const prerequisite_key& pk) + file_search (context& ctx, const target* t, const prerequisite_key& pk) { - // First see if there is an existing target. + // First see if there is an existing target in the out or src tree. // - if (const target* e = search_existing_target (t.ctx, pk)) + if (const target* e = search_existing_target (ctx, + pk, + false /* out_only */)) return e; // Then look for an existing file in the src tree. // - return search_existing_file (t.ctx, pk); + return t != nullptr ? search_existing_file (ctx, pk) : nullptr; + } + + extern const char target_extension_none_[] = ""; + + const char* + target_extension_none (const target_key& k, const scope* s) + { + return target_extension_fix<target_extension_none_> (k, s); + } + + const char* + target_extension_must (const target_key& tk, const scope*) + { + if (!tk.ext) + fail << tk.type->name << " target " << tk << " must include extension"; + + return tk.ext->c_str (); } - void - target_print_0_ext_verb (ostream& os, const target_key& k) + bool + target_print_0_ext_verb (ostream& os, const target_key& k, bool no) { stream_verbosity sv (stream_verb (os)); if (sv.extension == 1) sv.extension = 0; // Remap 1 to 0. - to_stream (os, k, sv); + return to_stream (os, k, sv, no); } - void - target_print_1_ext_verb (ostream& os, const target_key& k) + bool + target_print_1_ext_verb (ostream& os, const target_key& k, bool no) { stream_verbosity sv (stream_verb (os)); if (sv.extension == 0) sv.extension = 1; // Remap 0 to 1. - to_stream (os, k, sv); + return to_stream (os, k, sv, no); } // type info @@ -1007,7 +1230,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::none, }; const target_type mtime_target::static_type @@ -1020,7 +1243,7 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::none }; const target_type path_target::static_type @@ -1033,33 +1256,95 @@ namespace build2 nullptr, nullptr, &target_search, - false + target_type::flag::none }; - extern const char file_ext_def[] = ""; - const target_type file::static_type { "file", &path_target::static_type, &target_factory<file>, - &target_extension_fix<file_ext_def>, + &target_extension_none, nullptr, /* default_extension */ nullptr, /* pattern */ &target_print_1_ext_verb, // Print extension even at verbosity level 0. &file_search, - false + target_type::flag::none }; + // group + // + group_view group:: + group_members (action a) const + { + if (members_on == 0) // Not yet discovered. + return group_view {nullptr, 0}; + + // Members discovered during anything other than perform_update are only + // good for that operation. For example, we only return the static members + // ("representative sample") for perform_configure. + // + // We also re-discover the members on each update and clean not to + // overcomplicate the already twisted adhoc_buildscript_rule::apply() + // logic. + // + if (members_on != ctx.current_on) + { + if (members_action != perform_update_id || + a == perform_update_id || + a == perform_clean_id) + return group_view {nullptr, 0}; + } + + // Note that we may have no members (e.g., perform_configure and there are + // no static members). However, whether std::vector returns a non-NULL + // pointer in this case is undefined. + // + size_t n (members.size ()); + return group_view { + n != 0 + ? members.data () + : reinterpret_cast<const target* const*> (this), + n}; + } + + const target_type group::static_type + { + "group", + &mtime_target::static_type, + &target_factory<group>, + nullptr, + nullptr, + nullptr, + nullptr, + &target_search, + // + // Note that the dyn_members semantics is used not only to handle + // depdb-dyndep --dyn-target, but also pattern rule-static members. + // + target_type::flag::group | target_type::flag::dyn_members + }; + + // alias + // static const target* - alias_search (const target& t, const prerequisite_key& pk) + alias_search (context& ctx, const target* t, const prerequisite_key& pk) { // For an alias we don't want to silently create a target since it will do - // nothing and it most likely not what the user intended. + // nothing and it most likely not what the user intended (but omit this + // check when searching for an existing target since presumably a new one + // won't be created in this case). // - const target* e (search_existing_target (t.ctx, pk)); + // But, allowing implied aliases seems harmless since all the alias does + // is pull its prerequisites. And they are handy to use as metadata + // carriers. + // + // Doesn't feel like an alias in the src tree makes much sense. + // + const target* e (search_existing_target (ctx, pk, true /* out_only */)); - if (e == nullptr || e->decl != target_decl::real) + if ((e == nullptr || + !(operator>= (e->decl, target_decl::implied))) && t != nullptr) fail << "no explicit target for " << pk; return e; @@ -1075,7 +1360,7 @@ namespace build2 nullptr, nullptr, &alias_search, - false + target_type::flag::none }; // dir @@ -1085,7 +1370,7 @@ namespace build2 { try { - for (const dir_entry& e: dir_iterator (d, true /* ignore_dangling */)) + for (const dir_entry& e: dir_iterator (d, dir_iterator::detect_dangling)) { switch (e.type ()) { @@ -1103,6 +1388,16 @@ namespace build2 break; } + case entry_type::unknown: + { + bool sl (e.ltype () == entry_type::symlink); + + warn << "skipping " + << (sl ? "dangling symlink" : "inaccessible entry") << ' ' + << d / e.path (); + + break; + } default: break; } @@ -1124,17 +1419,26 @@ namespace build2 try { - for (const dir_entry& e: dir_iterator (d, true /* ignore_dangling */)) + for (const dir_entry& e: dir_iterator (d, dir_iterator::detect_dangling)) { if (e.type () == entry_type::directory) + { r.push_back ( - prerequisite (nullopt, - dir::static_type, + prerequisite (dir::static_type, dir_path (e.path ().representation ()), // Relative. dir_path (), // In the out tree. string (), nullopt, bs)); + } + else if (e.type () == entry_type::unknown) + { + bool sl (e.ltype () == entry_type::symlink); + + warn << "skipping " + << (sl ? "dangling symlink" : "inaccessible entry") << ' ' + << d / e.path (); + } } } catch (const system_error& e) @@ -1146,17 +1450,27 @@ namespace build2 } static const target* - dir_search (const target& t, const prerequisite_key& pk) + dir_search (context& ctx, const target* t, const prerequisite_key& pk) { tracer trace ("dir_search"); - // The first step is like in search_alias(): looks for an existing target. + // The first step is like in alias_search(): looks for an existing target + // (but unlike alias, no implied, think `test/: install=false`). // - const target* e (search_existing_target (t.ctx, pk)); + // Likewise, dir{} in the src tree doesn't make much sense. + // + const target* e (search_existing_target (ctx, pk, true /* out_only */)); if (e != nullptr && e->decl == target_decl::real) return e; + // The search for an existing target can also be done during execute so + // none of the below code applied. Note: return implied instead of NULL + // (to be consistent with search_new(), for example). + // + if (t == nullptr) + return e; + // If not found (or is implied), then try to load the corresponding // buildfile (which would normally define this target). Failed that, see // if we can assume an implied buildfile which would be equivalent to: @@ -1190,18 +1504,18 @@ namespace build2 // bool retest (false); - assert (t.ctx.phase == run_phase::match); + assert (ctx.phase == run_phase::match); { // Switch the phase to load. // - phase_switch ps (t.ctx, run_phase::load); + phase_switch ps (ctx, run_phase::load); // This is subtle: while we were fussing around another thread may have // loaded the buildfile. So re-test now that we are in an exclusive // phase. // if (e == nullptr) - e = search_existing_target (t.ctx, pk); + e = search_existing_target (ctx, pk, true); if (e != nullptr && e->decl == target_decl::real) retest = true; @@ -1209,6 +1523,10 @@ namespace build2 { // Ok, no luck, switch the scope. // + // Note that we don't need to do anything for the project's + // environment: source_once() will take care of it itself and + // search_implied() is not affected. + // pair<scope&, scope*> sp ( switch_scope (*s.rw ().root_scope (), out_base)); @@ -1235,14 +1553,14 @@ namespace build2 } } - assert (t.ctx.phase == run_phase::match); + assert (ctx.phase == run_phase::match); // If we loaded/implied the buildfile, examine the target again. // if (retest) { if (e == nullptr) - e = search_existing_target (t.ctx, pk); + e = search_existing_target (ctx, pk, true); if (e != nullptr && e->decl == target_decl::real) return e; @@ -1287,7 +1605,7 @@ namespace build2 &dir_pattern, nullptr, &dir_search, - false + target_type::flag::none }; const target_type fsdir::static_type @@ -1300,7 +1618,7 @@ namespace build2 &dir_pattern, nullptr, &target_search, - false + target_type::flag::none }; static optional<string> @@ -1367,8 +1685,8 @@ namespace build2 nullptr, #endif nullptr, - &file_search, - false + &file_search, // Note: can also be a script in src. + target_type::flag::none }; static const char* @@ -1454,7 +1772,56 @@ namespace build2 &buildfile_target_pattern, nullptr, &file_search, - false + target_type::flag::none + }; + + static const char* + buildscript_target_extension (const target_key& tk, const scope*) + { + // If the name is special 'buildscript', then there is no extension, + // otherwise it is .buildscript. + // + return *tk.name == "buildscript" ? "" : "buildscript"; + } + + static bool + buildscript_target_pattern (const target_type&, + const scope&, + string& v, + optional<string>& e, + const location& l, + bool r) + { + if (r) + { + assert (e); + e = nullopt; + } + else + { + e = target::split_name (v, l); + + if (!e && v != "buildscript") + { + e = "buildscript"; + return true; + } + } + + return false; + } + + const target_type buildscript::static_type + { + "buildscript", + &file::static_type, + &target_factory<buildscript>, + &buildscript_target_extension, + nullptr, /* default_extension */ + &buildscript_target_pattern, + nullptr, + &file_search, + target_type::flag::none }; const target_type doc::static_type @@ -1462,12 +1829,12 @@ namespace build2 "doc", &file::static_type, &target_factory<doc>, - &target_extension_fix<file_ext_def>, // Same as file (no extension). + &target_extension_none, // Same as file (no extension). nullptr, /* default_extension */ nullptr, /* pattern */ // Same as file. &target_print_1_ext_verb, // Same as file. &file_search, - false + target_type::flag::none }; const target_type legal::static_type @@ -1475,34 +1842,25 @@ namespace build2 "legal", &doc::static_type, &target_factory<legal>, - &target_extension_fix<file_ext_def>, // Same as file (no extension). + &target_extension_none, // Same as file (no extension). nullptr, /* default_extension */ nullptr, /* pattern */ // Same as file. &target_print_1_ext_verb, // Same as file. &file_search, - false + target_type::flag::none }; - static const char* - man_extension (const target_key& tk, const scope*) - { - if (!tk.ext) - fail << "man target " << tk << " must include extension (man section)"; - - return tk.ext->c_str (); - } - const target_type man::static_type { "man", &doc::static_type, &target_factory<man>, - &man_extension, // Should be specified explicitly. + &target_extension_must, // Should be specified explicitly. nullptr, /* default_extension */ nullptr, &target_print_1_ext_verb, // Print extension even at verbosity level 0. &file_search, - false + target_type::flag::none }; extern const char man1_ext[] = "1"; // VC14 rejects constexpr. @@ -1517,7 +1875,7 @@ namespace build2 &target_pattern_fix<man1_ext>, &target_print_0_ext_verb, // Fixed extension, no use printing. &file_search, - false + target_type::flag::none }; static const char* @@ -1566,6 +1924,6 @@ namespace build2 &manifest_target_pattern, nullptr, &file_search, - false + target_type::flag::none }; } |