From 977d07a3ae47ef204665d1eda2d642e5064724f3 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 24 Jun 2019 12:01:19 +0200 Subject: Split build system into library and driver --- build2/target.cxx | 1260 ----------------------------------------------------- 1 file changed, 1260 deletions(-) delete mode 100644 build2/target.cxx (limited to 'build2/target.cxx') diff --git a/build2/target.cxx b/build2/target.cxx deleted file mode 100644 index fee77b4..0000000 --- a/build2/target.cxx +++ /dev/null @@ -1,1260 +0,0 @@ -// file : build2/target.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace butl; - -namespace build2 -{ - // target_type - // - 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_state - // - static const char* const target_state_[] = - { - "unknown", - "unchanged", - "postponed", - "busy", - "changed", - "failed", - "group" - }; - - ostream& - operator<< (ostream& os, target_state ts) - { - return os << target_state_[static_cast (ts)]; - } - - // recipe - // - const recipe empty_recipe; - const recipe noop_recipe (&noop_action); - const recipe default_recipe (&default_action); - const recipe group_recipe (&group_action); - - // target - // - const target::prerequisites_type target::empty_prerequisites_; - - target:: - ~target () - { - clear_data (); - } - - const string& target:: - ext (string v) - { - ulock l (targets.mutex_); - - // Once the extension is set, it is immutable. However, it is possible - // that someone has already "branded" this target with a different - // extension. - // - optional& e (*ext_); - - if (!e) - e = move (v); - else if (*e != v) - { - string o (*e); - l.unlock (); - - fail << "conflicting extensions '" << o << "' and '" << v << "' " - << "for target " << *this; - } - - return *e; - } - - group_view target:: - group_members (action) const - { - assert (false); // Not a group or doesn't expose its members. - return group_view {nullptr, 0}; - } - - const scope& target:: - base_scope () const - { - // If this target is from the src tree, use its out directory to find - // the scope. - // - return scopes.find (out_dir ()); - } - - const scope& target:: - root_scope () const - { - // This is tricky to cache so we do the lookup for now. - // - const scope* r (base_scope ().root_scope ()); - assert (r != nullptr); - return *r; - } - - pair target:: - find_original (const variable& var, bool target_only) const - { - pair r (lookup (), 0); - - ++r.second; - { - auto p (vars.find (var)); - if (p.first != nullptr) - r.first = lookup (*p.first, p.second, vars); - } - - const target* g (nullptr); - - if (!r.first) - { - ++r.second; - - // Skip looking up in the ad hoc group, which is semantically the - // first/primary member. - // - if ((g = group == nullptr - ? nullptr - : group->adhoc_group () ? group->group : group)) - { - auto p (g->vars.find (var)); - if (p.first != nullptr) - r.first = lookup (*p.first, p.second, g->vars); - } - } - - // Delegate to scope's find_original(). - // - if (!r.first) - { - if (!target_only) - { - auto p (base_scope ().find_original ( - var, - &type (), - &name, - g != nullptr ? &g->type () : nullptr, - g != nullptr ? &g->name : nullptr)); - - r.first = move (p.first); - r.second = r.first ? r.second + p.second : p.second; - } - else - r.second = size_t (~0); - } - - return r; - } - - value& target:: - append (const variable& var) - { - // Note: see also prerequisite::append() if changing anything here. - - // Note that here we want the original value without any overrides - // applied. - // - lookup l (find_original (var).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; - } - - pair target::opstate:: - find_original (const variable& var, bool target_only) const - { - pair r (lookup (), 0); - - ++r.second; - { - auto p (vars.find (var)); - if (p.first != nullptr) - r.first = lookup (*p.first, p.second, vars); - } - - // Delegate to target's find_original(). - // - if (!r.first) - { - auto p (target_->find_original (var, target_only)); - - r.first = move (p.first); - r.second = r.first ? r.second + p.second : p.second; - } - - return r; - } - - optional target:: - split_name (string& v, const location& loc) - { - assert (!v.empty ()); - - // We treat a single trailing dot as "specified no extension", double dots - // as a single trailing dot (that is, an escape sequence which can be - // repeated any number of times; in such cases we naturally assume there - // is no default extension) and triple dots as "unspecified (default) - // extension" (used when the extension in the name is not "ours", for - // example, cxx{foo.test...} for foo.test.cxx). An odd number of dots - // other than one or three is invalid. - // - optional r; - - size_t p; - if (v.back () != '.') - { - if ((p = path::traits_type::find_extension (v)) != string::npos) - r = string (v.c_str () + p + 1); - } - else - { - if ((p = v.find_last_not_of ('.')) == string::npos) - fail (loc) << "invalid target name '" << v << "'"; - - p++; // Position of the first trailing dot. - size_t n (v.size () - p); // Number of the trailing dots. - - if (n == 1) - r = string (); - else if (n == 3) - ; - else if (n % 2 == 0) - { - p += n / 2; // Keep half of the dots. - r = string (); - } - else - fail (loc) << "invalid trailing dot sequence in target name '" - << v << "'"; - } - - if (p != string::npos) - v.resize (p); - - return r; - } - - void target:: - combine_name (string& v, const optional& e, bool de) - { - if (v.back () == '.') - { - assert (e && e->empty ()); - - size_t p (v.find_last_not_of ('.')); - assert (p != string::npos); - - p++; // Position of the first trailing dot. - size_t n (v.size () - p); // Number of the trailing dots. - v.append (n, '.'); // Double them. - } - else if (e) - { - v += '.'; - v += *e; // Empty or not. - } - else if (de) - { - if (path::traits_type::find_extension (v) != string::npos) - v += "..."; - } - } - - // target_set - // - target_set targets; - - const target* target_set:: - find (const target_key& k, tracer& trace) const - { - slock sl (mutex_); - map_type::const_iterator i (map_.find (k)); - - if (i == map_.end ()) - return nullptr; - - const target& t (*i->second); - optional& ext (i->first.ext); - - if (ext != k.ext) - { - ulock ul; // Keep locked for trace. - - if (k.ext) - { - // To update the extension we have to re-lock for exclusive access. - // 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 (). - // - sl.unlock (); - ul = ulock (mutex_); - - if (ext) // Someone set the extension. - { - ul.unlock (); - return find (k, trace); - } - } - - l5 ([&]{ - diag_record r (trace); - r << "assuming target "; - to_stream (r.os, - target_key {&t.type (), &t.dir, &t.out, &t.name, ext}, - stream_verb_max); // Always print the extension. - r << " is the same as the one with "; - - if (!k.ext) - r << "unspecified extension"; - else if (k.ext->empty ()) - r << "no extension"; - else - r << "extension " << *k.ext; - }); - - if (k.ext) - ext = k.ext; - } - - return &t; - } - - pair target_set:: - insert_locked (const target_type& tt, - dir_path dir, - dir_path out, - string name, - optional ext, - bool implied, - tracer& trace) - { - target_key tk {&tt, &dir, &out, &name, move (ext)}; - target* t (const_cast (find (tk, trace))); - - if (t == nullptr) - { - // We sometimes call insert() even if we expect to find an existing - // target in order to keep the same code (see cc/search_library()). - // - assert (phase != run_phase::execute); - - optional e ( - tt.fixed_extension != nullptr - ? string (tt.fixed_extension (tk, nullptr /* root scope */)) - : move (tk.ext)); - - t = tt.factory (tt, move (dir), move (out), move (name)); - - // Re-lock for exclusive access. In the meantime, someone could have - // inserted this target so emplace() below could return false, in which - // case we proceed pretty much like find() except already under the - // exclusive lock. - // - ulock ul (mutex_); - - auto p (map_.emplace (target_key {&tt, &t->dir, &t->out, &t->name, e}, - unique_ptr (t))); - - map_type::iterator i (p.first); - - if (p.second) - { - t->ext_ = &i->first.ext; - t->implied = implied; - t->state.data[0].target_ = t; - t->state.data[1].target_ = t; - return pair (*t, move (ul)); - } - - // The "tail" of find(). - // - t = i->second.get (); - optional& ext (i->first.ext); - - if (ext != e) - { - l5 ([&]{ - diag_record r (trace); - r << "assuming target "; - to_stream ( - r.os, - target_key {&t->type (), &t->dir, &t->out, &t->name, ext}, - stream_verb_max); // Always print the extension. - r << " is the same as the one with "; - - if (!e) - r << "unspecified extension"; - else if (e->empty ()) - r << "no extension"; - else - r << "extension " << *e; - }); - - if (e) - ext = e; - } - - // Fall through (continue as if the first find() returned this target). - } - - if (!implied) - { - // The implied flag can only be cleared during the load phase. - // - assert (phase == run_phase::load); - - // Clear the implied flag. - // - if (t->implied) - t->implied = false; - } - - return pair (*t, ulock ()); - } - - ostream& - to_stream (ostream& os, const target_key& k, optional osv) - { - stream_verbosity sv (osv ? *osv : stream_verb (os)); - uint16_t dv (sv.path); - uint16_t ev (sv.extension); - - // If the name is empty, then we want to print the last component of the - // directory inside {}, e.g., dir{bar/}, not bar/dir{}. - // - 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. - - if (!pd.empty ()) - { - if (dv < 1) - os << diag_relative (pd); - else - os << pd.representation (); - } - - const target_type& tt (*k.type); - - os << tt.name << '{'; - - if (n) - { - os << *k.name; - - // If the extension derivation functions are NULL, then it means this - // target type doesn't use extensions. - // - if (tt.fixed_extension != nullptr || tt.default_extension != nullptr) - { - // For verbosity level 0 we don't print the extension. For 1 we print - // it if there is one. For 2 we print 'foo.?' if it hasn't yet been - // assigned and 'foo.' if it is assigned as "no extension" (empty). - // - if (ev > 0 && (ev > 1 || (k.ext && !k.ext->empty ()))) - { - os << '.' << (k.ext ? *k.ext : "?"); - } - } - else - assert (!k.ext); - } - else - os << (rd.empty () ? dir_path (".") : rd.leaf ()).representation (); - - os << '}'; - - // If this target is from src, print its out. - // - if (!k.out->empty ()) - { - if (dv < 1) - { - // Don't print '@./'. - // - const string& o (diag_relative (*k.out, false)); - - if (!o.empty ()) - os << '@' << o; - } - else - os << '@' << *k.out; - } - - return os; - } - - ostream& - operator<< (ostream& os, const target_key& k) - { - if (auto p = k.type->print) - p (os, k); - else - to_stream (os, k, stream_verb (os)); - - return os; - } - - // mtime_target - // - timestamp mtime_target:: - mtime () const - { - // Figure out from which target we should get the value. - // - const mtime_target* t (this); - - switch (phase) - { - case run_phase::load: break; - case run_phase::match: - { - // Similar logic to matched_state_impl(). - // - const opstate& s (state[action () /* inner */]); - size_t o (s.task_count.load (memory_order_relaxed) - // Synchronized. - target::count_base ()); - - if (o != target::offset_applied && o != target::offset_executed) - break; - } - // Fall through. - case run_phase::execute: - { - if (group_state (action () /* inner */)) - t = &group->as (); - - break; - } - } - - return timestamp (duration (t->mtime_.load (memory_order_consume))); - } - - // path_target - // - const string* path_target:: - derive_extension (bool search, const char* de) - { - // See also search_existing_file() if updating anything here. - - // Should be no default extension if searching. - // - assert (!search || de == nullptr); - - // The target should use extensions and they should not be fixed. - // - assert (de == nullptr || type ().default_extension != nullptr); - - if (const string* p = ext ()) - // Note that returning by reference is now MT-safe since once the - // extension is specified, it is immutable. - // - return p; - else - { - optional e; - - // If the target type has the default extension function then try that - // first. The reason for preferring it over what's been provided by the - // caller is that this function will often use the 'extension' variable - // which the user can use to override extensions. But since we pass the - // provided default extension, the target type can override this logic - // (see the exe{} target type for a use case). - // - if (auto f = type ().default_extension) - e = f (key (), base_scope (), de, search); - - if (!e) - { - if (de != nullptr) - e = de; - else - { - if (search) - return nullptr; - - fail << "no default extension for target " << *this << endf; - } - } - - return &ext (move (*e)); - } - } - - const path& path_target:: - derive_path (const char* de, const char* np, const char* ns) - { - path_type p (dir); - - if (np == nullptr || np[0] == '\0') - p /= name; - else - { - p /= np; - p += name; - } - - if (ns != nullptr) - p += ns; - - return derive_path (move (p), de); - } - - const path& path_target:: - derive_path (path_type p, const char* de) - { - // Derive and add the extension if any. - // - { - const string& e (derive_extension (de)); - - if (!e.empty ()) - { - p += '.'; - p += e; - } - } - - path (move (p)); - return path_; - } - - // Search functions. - // - - const target* - target_search (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 (pk); - } - - const target* - file_search (const target&, const prerequisite_key& pk) - { - // First see if there is an existing target. - // - if (const target* t = search_existing_target (pk)) - return t; - - // Then look for an existing file in the src tree. - // - return search_existing_file (pk); - } - - void - target_print_0_ext_verb (ostream& os, const target_key& k) - { - stream_verbosity sv (stream_verb (os)); - if (sv.extension == 1) sv.extension = 0; // Remap 1 to 0. - to_stream (os, k, sv); - } - - void - target_print_1_ext_verb (ostream& os, const target_key& k) - { - stream_verbosity sv (stream_verb (os)); - if (sv.extension == 0) sv.extension = 1; // Remap 0 to 1. - to_stream (os, k, sv); - } - - // type info - // - - const target_type target::static_type - { - "target", - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, - false - }; - - const target_type mtime_target::static_type - { - "mtime_target", - &target::static_type, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, - false - }; - - const target_type path_target::static_type - { - "path_target", - &mtime_target::static_type, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, - false - }; - - extern const char file_ext_def[] = ""; - - const target_type file::static_type - { - "file", - &path_target::static_type, - &target_factory, - &target_extension_fix, - nullptr, /* default_extension */ - nullptr, /* pattern */ - &target_print_1_ext_verb, // Print extension even at verbosity level 0. - &file_search, - false - }; - - static const target* - alias_search (const target&, 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. - // - const target* t (search_existing_target (pk)); - - if (t == nullptr || t->implied) - fail << "no explicit target for " << pk; - - return t; - } - - const target_type alias::static_type - { - "alias", - &target::static_type, - &target_factory, - nullptr, // Extension not used. - nullptr, - nullptr, - nullptr, - &alias_search, - false - }; - - // dir - // - bool dir:: - check_implied (const scope& rs, const dir_path& d) - { - try - { - for (const dir_entry& e: dir_iterator (d, true /* ignore_dangling */)) - { - switch (e.type ()) - { - case entry_type::directory: - { - if (check_implied (rs, d / path_cast (e.path ()))) - return true; - - break; - } - case entry_type::regular: - { - if (e.path () == rs.root_extra->buildfile_file) - return true; - - break; - } - default: - break; - } - } - } - catch (const system_error& e) - { - fail << "unable to iterate over " << d << ": " << e << endf; - } - - return false; - } - - prerequisites dir:: - collect_implied (const scope& bs) - { - prerequisites_type r; - const dir_path& d (bs.src_path ()); - - try - { - for (const dir_entry& e: dir_iterator (d, true /* ignore_dangling */)) - { - if (e.type () == entry_type::directory) - r.push_back ( - prerequisite (nullopt, - dir::static_type, - dir_path (e.path ().representation ()), // Relative. - dir_path (), // In the out tree. - string (), - nullopt, - bs)); - } - } - catch (const system_error& e) - { - fail << "unable to iterate over " << d << ": " << e; - } - - return r; - } - - static const target* - dir_search (const target&, const prerequisite_key& pk) - { - tracer trace ("dir_search"); - - // The first step is like in search_alias(): looks for an existing target. - // - const target* t (search_existing_target (pk)); - - if (t != nullptr && !t->implied) - return t; - - // 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: - // - // ./: */ - // - const dir_path& d (*pk.tk.dir); - - // We only do this for relative paths. - // - if (d.relative ()) - { - // Note: this code is a custom version of parser::parse_include(). - - const scope& s (*pk.scope); - - // Calculate the new out_base. - // - dir_path out_base (s.out_path () / d); - out_base.normalize (); - - // In our world modifications to the scope structure during search & - // match should be "pure append" in the sense that they should not - // affect any existing targets that have already been searched & - // matched. - // - // A straightforward way to enforce this is to not allow any existing - // targets to be inside any newly created scopes (except, perhaps for - // the directory target itself which we know hasn't been searched yet). - // This, however, is not that straightforward to implement: we would - // need to keep a directory prefix map for all the targets (e.g., in - // target_set). Also, a buildfile could load from a directory that is - // not a subdirectory of out_base. So for now we just assume that this - // is so. And so it is. - // - bool retest (false); - - assert (phase == run_phase::match); - { - // Switch the phase to load. - // - phase_switch ps (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 exclusive - // phase. - // - if (t == nullptr) - t = search_existing_target (pk); - - if (t != nullptr && !t->implied) - retest = true; - else - { - // Ok, no luck, switch the scope. - // - pair sp ( - switch_scope (*s.rw ().root_scope (), out_base)); - - if (sp.second != nullptr) // Ignore scopes out of any project. - { - scope& base (sp.first); - scope& root (*sp.second); - - const dir_path& src_base (base.src_path ()); - - path bf (src_base / root.root_extra->buildfile_file); - - if (exists (bf)) - { - l5 ([&]{trace << "loading buildfile " << bf << " for " << pk;}); - retest = source_once (root, base, bf, root); - } - else if (exists (src_base)) - { - t = dir::search_implied (base, pk, trace); - retest = (t != nullptr); - } - } - } - } - assert (phase == run_phase::match); - - // If we loaded/implied the buildfile, examine the target again. - // - if (retest) - { - if (t == nullptr) - t = search_existing_target (pk); - - if (t != nullptr && !t->implied) - return t; - } - } - - fail << "no explicit target for " << pk << endf; - } - - static bool - dir_pattern (const target_type&, - const scope&, - string& v, - optional&, - const location&, - bool r) - { - // Add/strip trailing directory separator unless already there. - // - bool d (path::traits_type::is_separator (v.back ())); - - if (r) - { - assert (d); - v.resize (v.size () - 1); - } - else if (!d) - { - v += path::traits_type::directory_separator; - return true; - } - - return false; - } - - const target_type dir::static_type - { - "dir", - &alias::static_type, - &target_factory, - nullptr, // Extension not used. - nullptr, - &dir_pattern, - nullptr, - &dir_search, - false - }; - - const target_type fsdir::static_type - { - "fsdir", - &target::static_type, - &target_factory, - nullptr, // Extension not used. - nullptr, - &dir_pattern, - nullptr, - &target_search, - false - }; - - static optional - exe_target_extension (const target_key&, - const scope&, - const char* e, - bool search) - { - // If we are searching for an executable that is not a target, then use - // the build machine executable extension. Otherwise, if this is a target, - // then we expect the rule to supply the target machine extension. But if - // it doesn't, then fallback to no extension (e.g., a script). - // - return string (!search - ? (e != nullptr ? e : "") - : -#ifdef _WIN32 - "exe" -#else - "" -#endif - ); - } - -#ifdef _WIN32 - static bool - exe_target_pattern (const target_type&, - const scope&, - string& v, - optional& e, - const location& l, - bool r) - { - if (r) - { - assert (e); - e = nullopt; - } - else - { - e = target::split_name (v, l); - - if (!e) - { - e = "exe"; - return true; - } - } - - return false; - } -#endif - - const target_type exe::static_type - { - "exe", - &file::static_type, - &target_factory, - nullptr, /* fixed_extension */ - &exe_target_extension, -#ifdef _WIN32 - &exe_target_pattern, -#else - nullptr, -#endif - nullptr, - &file_search, - false - }; - - static const char* - buildfile_target_extension (const target_key& tk, const scope* root) - { - // If the name is the special 'buildfile', then there is no extension, - // otherwise it is 'build' (or 'build2file' and 'build2' in the - // alternative naming scheme). - - // Let's try hard not to need the root scope by trusting the extensions - // we were given. - // - // BTW, one way to get rid of all this root scope complication is to - // always require explicit extension specification for buildfiles. Since - // they are hardly ever mentioned explicitly, this should probably be ok. - // - if (tk.ext) - return tk.ext->c_str (); - - if (root == nullptr) - { - // The same login as in target::root_scope(). - // - // Note: we are guaranteed the scope is never NULL for prerequisites - // (where out/dir could be relative and none of this will work). - // - root = scopes.find (tk.out->empty () ? *tk.dir : *tk.out).root_scope (); - - if (root == nullptr || root->root_extra == nullptr) - fail << "unable to determine extension for buildfile target " << tk; - } - - return *tk.name == root->root_extra->buildfile_file.string () - ? "" - : root->root_extra->build_ext.c_str (); - } - - static bool - buildfile_target_pattern (const target_type&, - const scope& base, - string& v, - optional& e, - const location& l, - bool r) - { - if (r) - { - assert (e); - e = nullopt; - } - else - { - e = target::split_name (v, l); - - if (!e) - { - const scope* root (base.root_scope ()); - - if (root == nullptr || root->root_extra == nullptr) - fail (l) << "unable to determine extension for buildfile pattern"; - - if (v != root->root_extra->buildfile_file.string ()) - { - e = root->root_extra->build_ext; - return true; - } - } - } - - return false; - } - - const target_type buildfile::static_type - { - "build", - &file::static_type, - &target_factory, - &buildfile_target_extension, - nullptr, /* default_extension */ - &buildfile_target_pattern, - nullptr, - &file_search, - false - }; - - const target_type doc::static_type - { - "doc", - &file::static_type, - &target_factory, - &target_extension_fix, // Same as file (no extension). - nullptr, /* default_extension */ - nullptr, /* pattern */ // Same as file. - &target_print_1_ext_verb, // Same as file. - &file_search, - false - }; - - 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_extension, // Should be specified explicitly. - nullptr, /* default_extension */ - nullptr, - &target_print_1_ext_verb, // Print extension even at verbosity level 0. - &file_search, - false - }; - - extern const char man1_ext[] = "1"; // VC14 rejects constexpr. - - const target_type man1::static_type - { - "man1", - &man::static_type, - &target_factory, - &target_extension_fix, - nullptr, /* default_extension */ - &target_pattern_fix, - &target_print_0_ext_verb, // Fixed extension, no use printing. - &file_search, - false - }; - - static const char* - manifest_target_extension (const target_key& tk, const scope*) - { - // If the name is special 'manifest', then there is no extension, - // otherwise it is .manifest. - // - return *tk.name == "manifest" ? "" : "manifest"; - } - - static bool - manifest_target_pattern (const target_type&, - const scope&, - string& v, - optional& e, - const location& l, - bool r) - { - if (r) - { - assert (e); - e = nullopt; - } - else - { - e = target::split_name (v, l); - - if (!e && v != "manifest") - { - e = "manifest"; - return true; - } - } - - return false; - } - - const target_type manifest::static_type - { - "manifest", - &doc::static_type, - &target_factory, - &manifest_target_extension, - nullptr, /* default_extension */ - &manifest_target_pattern, - nullptr, - &file_search, - false - }; -} -- cgit v1.1