aboutsummaryrefslogtreecommitdiff
path: root/build2/config
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-01-05 11:55:15 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-01-05 11:55:15 +0200
commit9fb791e9fad6c63fc1dac49f4d05ae63b8a3db9b (patch)
treed60322d4382ca5f97b676c5abe2e39524f35eab4 /build2/config
parentf159b1dac68c8714f7ba71ca168e3b695891aad9 (diff)
Rename build directory/namespace to build2
Diffstat (limited to 'build2/config')
-rw-r--r--build2/config/module26
-rw-r--r--build2/config/module.cxx90
-rw-r--r--build2/config/operation19
-rw-r--r--build2/config/operation.cxx455
-rw-r--r--build2/config/utility128
-rw-r--r--build2/config/utility.cxx92
-rw-r--r--build2/config/utility.ixx17
-rw-r--r--build2/config/utility.txx45
8 files changed, 872 insertions, 0 deletions
diff --git a/build2/config/module b/build2/config/module
new file mode 100644
index 0000000..5888376
--- /dev/null
+++ b/build2/config/module
@@ -0,0 +1,26 @@
+// file : build2/config/module -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CONFIG_MODULE
+#define BUILD2_CONFIG_MODULE
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/module>
+
+namespace build2
+{
+ namespace config
+ {
+ extern "C" void
+ config_boot (scope&, const location&, unique_ptr<module>&);
+
+ extern "C" bool
+ config_init (
+ scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
+ }
+}
+
+#endif // BUILD2_CONFIG_MODULE
diff --git a/build2/config/module.cxx b/build2/config/module.cxx
new file mode 100644
index 0000000..3d03a51
--- /dev/null
+++ b/build2/config/module.cxx
@@ -0,0 +1,90 @@
+// file : build2/config/module.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/config/module>
+
+#include <butl/filesystem>
+
+#include <build2/file>
+#include <build2/rule>
+#include <build2/scope>
+#include <build2/diagnostics>
+
+#include <build2/config/operation>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace config
+ {
+ //@@ Same as in operation.cxx
+ //
+ static const path config_file ("build/config.build");
+
+ extern "C" void
+ config_boot (scope& root, const location&, unique_ptr<module>&)
+ {
+ tracer trace ("config::boot");
+
+ const dir_path& out_root (root.out_path ());
+ level5 ([&]{trace << "for " << out_root;});
+
+ // Register meta-operations.
+ //
+ root.meta_operations.insert (configure_id, configure);
+ root.meta_operations.insert (disfigure_id, disfigure);
+
+ // Load config.build if one exists.
+ //
+ // Note that we have to do this during bootstrap since the order in
+ // which the modules will be initialized is unspecified. So it is
+ // possible that some module which needs the configuration will get
+ // called first.
+ //
+ path f (out_root / config_file);
+
+ if (file_exists (f))
+ source (f, root, root);
+ }
+
+ extern "C" bool
+ config_init (scope& root,
+ scope&,
+ const location& l,
+ std::unique_ptr<module>&,
+ bool first,
+ bool)
+ {
+ tracer trace ("config::init");
+
+ if (!first)
+ {
+ warn (l) << "multiple config module initializations";
+ return true;
+ }
+
+ level5 ([&]{trace << "for " << root.out_path ();});
+
+ // Register alias and fallback rule for the configure meta-operation.
+ //
+ {
+ // We need this rule for out-of-any-project dependencies (e.g.,
+ // libraries imported from /usr/lib).
+ //
+ global_scope->rules.insert<file> (
+ configure_id, 0, "config.file", file_rule::instance);
+
+ auto& r (root.rules);
+
+ r.insert<target> (configure_id, 0, "config", fallback_rule::instance);
+ r.insert<file> (configure_id, 0, "config.file", fallback_rule::instance);
+ r.insert<alias> (configure_id, 0, "config.alias", alias_rule::instance);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/build2/config/operation b/build2/config/operation
new file mode 100644
index 0000000..63eba7e
--- /dev/null
+++ b/build2/config/operation
@@ -0,0 +1,19 @@
+// file : build2/config/operation -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CONFIG_OPERATION
+#define BUILD2_CONFIG_OPERATION
+
+#include <build2/operation>
+
+namespace build2
+{
+ namespace config
+ {
+ extern meta_operation_info configure;
+ extern meta_operation_info disfigure;
+ }
+}
+
+#endif // BUILD2_CONFIG_OPERATION
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
new file mode 100644
index 0000000..96d9be7
--- /dev/null
+++ b/build2/config/operation.cxx
@@ -0,0 +1,455 @@
+// file : build2/config/operation.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/config/operation>
+
+#include <fstream>
+
+#include <butl/filesystem>
+
+#include <build2/file>
+#include <build2/scope>
+#include <build2/target>
+#include <build2/context>
+#include <build2/algorithm>
+#include <build2/diagnostics>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace config
+ {
+ static const path config_file ("build/config.build");
+
+ // configure
+ //
+ static operation_id
+ configure_operation_pre (operation_id o)
+ {
+ // Don't translate default to update. In our case unspecified
+ // means configure everything.
+ //
+ return o;
+ }
+
+ static void
+ save_src_root (const dir_path& out_root, const dir_path& src_root)
+ {
+ path f (out_root / src_root_file);
+
+ if (verb)
+ text << (verb >= 2 ? "config::save_src_root " : "save ") << f;
+
+ try
+ {
+ ofstream ofs (f.string ());
+ if (!ofs.is_open ())
+ fail << "unable to open " << f;
+
+ ofs.exceptions (ofstream::failbit | ofstream::badbit);
+
+ //@@ TODO: quote path
+ //
+ ofs << "# Created automatically by the config module." << endl
+ << "#" << endl
+ << "src_root = " << src_root << endl;
+ }
+ catch (const ofstream::failure&)
+ {
+ fail << "unable to write " << f;
+ }
+ }
+
+ static void
+ save_config (scope& root)
+ {
+ const dir_path& out_root (root.out_path ());
+ path f (out_root / config_file);
+
+ if (verb)
+ text << (verb >= 2 ? "config::save_config " : "save ") << f;
+
+ try
+ {
+ ofstream ofs (f.string ());
+ if (!ofs.is_open ())
+ fail << "unable to open " << f;
+
+ ofs.exceptions (ofstream::failbit | ofstream::badbit);
+
+ ofs << "# Created automatically by the config module, but" << endl
+ << "# feel free to edit." << endl
+ << "#" << endl;
+
+ if (auto l = root.vars["amalgamation"])
+ {
+ const dir_path& d (as<dir_path> (*l));
+
+ ofs << "# Base configuration inherited from " << d << endl
+ << "#" << endl;
+ }
+
+ // Save all the variables in the config namespace that are set
+ // on the project's root scope.
+ //
+ for (auto p (root.vars.find_namespace ("config"));
+ p.first != p.second;
+ ++p.first)
+ {
+ const variable& var (p.first->first);
+ const value& val (p.first->second);
+ const string& n (var.name);
+
+ // Skip special variables.
+ //
+ if (n == "config.loaded" ||
+ n == "config.configured")
+ continue;
+
+ // We will only write config.*.configured if it is false
+ // (true is implied by its absence).
+ //
+ if (n.size () > 11 &&
+ n.compare (n.size () - 11, 11, ".configured") == 0)
+ {
+ if (val == nullptr || as<bool> (val))
+ continue;
+ }
+
+ // Warn the user if the value that we are saving differs
+ // from the one they specified on the command line.
+ //
+ auto l ((*global_scope)[var]);
+ if (l.defined () && *l != val)
+ {
+ warn << "variable " << var.name << " configured value "
+ << "differs from command line value" <<
+ info << "reconfigure the project to use command line value";
+ }
+
+ if (val)
+ {
+ ofs << var.name << " = " << val.data_ << endl;
+ //text << var.name << " = " << val.data_;
+ }
+ else
+ {
+ ofs << var.name << " = #[null]" << endl; // @@ TODO: [null]
+ //text << var.name << " = [null]";
+ }
+ }
+ }
+ catch (const ofstream::failure&)
+ {
+ fail << "unable to write " << f;
+ }
+ }
+
+ static void
+ configure_project (action a, scope& root)
+ {
+ tracer trace ("configure_project");
+
+ const dir_path& out_root (root.out_path ());
+ const dir_path& src_root (root.src_path ());
+
+ // Make sure the directories exist.
+ //
+ if (out_root != src_root)
+ {
+ mkdir_p (out_root);
+ mkdir (out_root / build_dir);
+ mkdir (out_root / bootstrap_dir);
+ }
+
+ // We distinguish between a complete configure and operation-
+ // specific.
+ //
+ if (a.operation () == default_id)
+ {
+ level5 ([&]{trace << "completely configuring " << out_root;});
+
+ // Save src-root.build unless out_root is the same as src.
+ //
+ if (out_root != src_root)
+ save_src_root (out_root, src_root);
+
+ // Save config.build.
+ //
+ save_config (root);
+ }
+ else
+ {
+ }
+
+ // Configure subprojects that have been loaded.
+ //
+ if (auto l = root.vars["subprojects"])
+ {
+ for (auto p: as<subprojects> (*l))
+ {
+ const dir_path& pd (p.second);
+ dir_path out_nroot (out_root / pd);
+ scope& nroot (scopes.find (out_nroot));
+
+ // @@ Strictly speaking we need to check whether the config
+ // module was loaded for this subproject.
+ //
+ if (nroot.out_path () != out_nroot) // This subproject not loaded.
+ continue;
+
+ configure_project (a, nroot);
+ }
+ }
+ }
+
+ static void
+ configure_match (action, action_targets&)
+ {
+ // Don't match anything -- see execute ().
+ }
+
+ static void
+ configure_execute (action a, const action_targets& ts, bool)
+ {
+ // Match rules to configure every operation supported by each
+ // project. Note that we are not calling operation_pre/post()
+ // callbacks here since the meta operation is configure and we
+ // know what we are doing.
+ //
+ for (void* v: ts)
+ {
+ target& t (*static_cast<target*> (v));
+ scope* rs (t.base_scope ().root_scope ());
+
+ if (rs == nullptr)
+ fail << "out of project target " << t;
+
+ for (operations::size_type id (default_id + 1); // Skip default_id
+ id < rs->operations.size ();
+ ++id)
+ {
+ const operation_info* oi (rs->operations[id]);
+ if (oi == nullptr)
+ continue;
+
+ current_inner_oif = oi;
+ current_outer_oif = nullptr;
+ current_mode = oi->mode;
+ dependency_count = 0;
+
+ match (action (configure_id, id), t);
+ }
+
+ configure_project (a, *rs);
+ }
+ }
+
+ meta_operation_info configure {
+ "configure",
+ "configure",
+ "configuring",
+ "is configured",
+ nullptr, // meta-operation pre
+ &configure_operation_pre,
+ &load, // normal load
+ &search, // normal search
+ &configure_match,
+ &configure_execute,
+ nullptr, // operation post
+ nullptr // meta-operation post
+ };
+
+ // disfigure
+ //
+ static operation_id
+ disfigure_operation_pre (operation_id o)
+ {
+ // Don't translate default to update. In our case unspecified
+ // means disfigure everything.
+ //
+ return o;
+ }
+
+ static void
+ disfigure_load (const path& bf,
+ scope&,
+ const dir_path&,
+ const dir_path&,
+ const location&)
+ {
+ tracer trace ("disfigure_load");
+ level6 ([&]{trace << "skipping " << bf;});
+ }
+
+ static void
+ disfigure_search (scope& root,
+ const target_key&,
+ const location&,
+ action_targets& ts)
+ {
+ tracer trace ("disfigure_search");
+ level6 ([&]{trace << "collecting " << root.out_path ();});
+ ts.push_back (&root);
+ }
+
+ static void
+ disfigure_match (action, action_targets&) {}
+
+ static bool
+ disfigure_project (action a, scope& root)
+ {
+ tracer trace ("disfigure_project");
+
+ bool m (false); // Keep track of whether we actually did anything.
+
+ const dir_path& out_root (root.out_path ());
+ const dir_path& src_root (root.src_path ());
+
+ // Disfigure subprojects. Since we don't load buildfiles during
+ // disfigure, we do it for all known subprojects.
+ //
+ if (auto l = root.vars["subprojects"])
+ {
+ for (auto p: as<subprojects> (*l))
+ {
+ const dir_path& pd (p.second);
+
+ // Create and bootstrap subproject's root scope.
+ //
+ dir_path out_nroot (out_root / pd);
+
+ // The same logic for src_root as in create_bootstrap_inner().
+ //
+ scope& nroot (create_root (out_nroot, dir_path ()));
+ bootstrap_out (nroot);
+
+ value& val (nroot.assign ("src_root"));
+
+ if (!val)
+ val = is_src_root (out_nroot) ? out_nroot : (src_root / pd);
+
+ setup_root (nroot);
+
+ bootstrap_src (nroot);
+
+ m = disfigure_project (a, nroot) || m;
+
+ // We use mkdir_p() to create the out_root of a subproject
+ // which means there could be empty parent directories left
+ // behind. Clean them up.
+ //
+ if (!pd.simple () && out_root != src_root)
+ {
+ for (dir_path d (pd.directory ());
+ !d.empty ();
+ d = d.directory ())
+ {
+ rmdir_status s (rmdir (out_root / d));
+
+ if (s == rmdir_status::not_empty)
+ break; // No use trying do remove parent ones.
+
+ m = (s == rmdir_status::success) || m;
+ }
+ }
+ }
+ }
+
+ // We distinguish between a complete disfigure and operation-
+ // specific.
+ //
+ if (a.operation () == default_id)
+ {
+ level5 ([&]{trace << "completely disfiguring " << out_root;});
+
+ m = rmfile (out_root / config_file) || m;
+
+ if (out_root != src_root)
+ {
+ m = rmfile (out_root / src_root_file) || m;
+
+ // Clean up the directories.
+ //
+ m = rmdir (out_root / bootstrap_dir) || m;
+ m = rmdir (out_root / build_dir) || m;
+
+ switch (rmdir (out_root))
+ {
+ case rmdir_status::not_empty:
+ {
+ warn << "directory " << out_root << " is "
+ << (out_root == work
+ ? "current working directory"
+ : "not empty") << ", not removing";
+ break;
+ }
+ case rmdir_status::success:
+ m = true;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ }
+
+ return m;
+ }
+
+ static void
+ disfigure_execute (action a, const action_targets& ts, bool quiet)
+ {
+ tracer trace ("disfigure_execute");
+
+ for (void* v: ts)
+ {
+ scope& root (*static_cast<scope*> (v));
+
+ if (!disfigure_project (a, root))
+ {
+ // Create a dir{$out_root/} target to signify the project's
+ // root in diagnostics. Not very clean but seems harmless.
+ //
+ target& t (
+ targets.insert (
+ dir::static_type, root.out_path (), "", nullptr, trace).first);
+
+ if (!quiet)
+ info << diag_done (a, t);
+ }
+ }
+ }
+
+ static void
+ disfigure_meta_operation_post ()
+ {
+ tracer trace ("disfigure_meta_operation_post");
+
+ // Reset the dependency state since anything that could have been
+ // loaded earlier using a previous configuration is now invalid.
+ //
+ level6 ([&]{trace << "resetting dependency state";});
+ reset ();
+ }
+
+ meta_operation_info disfigure {
+ "disfigure",
+ "disfigure",
+ "disfiguring",
+ "is disfigured",
+ nullptr, // meta-operation pre
+ &disfigure_operation_pre,
+ &disfigure_load,
+ &disfigure_search,
+ &disfigure_match,
+ &disfigure_execute,
+ nullptr, // operation post
+ &disfigure_meta_operation_post
+ };
+ }
+}
diff --git a/build2/config/utility b/build2/config/utility
new file mode 100644
index 0000000..608cf5e
--- /dev/null
+++ b/build2/config/utility
@@ -0,0 +1,128 @@
+// file : build2/config/utility -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_CONFIG_UTILITY
+#define BUILD2_CONFIG_UTILITY
+
+#include <string>
+#include <utility> // pair
+#include <functional> // reference_wrapper
+
+#include <build2/types>
+#include <build2/variable>
+#include <build2/diagnostics>
+
+namespace build2
+{
+ class scope;
+
+ namespace config
+ {
+ // Set, if necessary, a required config.* variable.
+ //
+ // If override is true and the variable doesn't come from this root
+ // scope or from the command line, then its value is "overridden"
+ // for this root scope.
+ //
+ // Return the reference to the value as well as the indication of
+ // whether the variable has actually been set.
+ //
+ template <typename T>
+ std::pair<std::reference_wrapper<const value>, bool>
+ required (scope& root,
+ const variable&,
+ const T& default_value,
+ bool override = false);
+
+ template <typename T>
+ inline std::pair<std::reference_wrapper<const value>, bool>
+ required (scope& root,
+ const std::string& name,
+ const T& default_value,
+ bool override = false)
+ {
+ return required (root, var_pool.find (name), default_value, override);
+ }
+
+ inline std::pair<std::reference_wrapper<const value>, bool>
+ required (scope& root,
+ const std::string& name,
+ const char* default_value,
+ bool override = false)
+ {
+ return required (root, name, std::string (default_value), override);
+ }
+
+ // Set, if necessary, an optional config.* variable. In particular,
+ // an unspecified variable is set to NULL which is used to distinguish
+ // between the "configured as unspecified" and "not yet configured"
+ // cases.
+ //
+ // Return the value, which can be NULL.
+ //
+ const value&
+ optional (scope& root, const variable&);
+
+ inline const value&
+ optional (scope& root, const std::string& var)
+ {
+ return optional (root, var_pool.find (var));
+ }
+
+ // As above but assumes the value is dir_path and makes it
+ // absolute if the value specified on the command line is
+ // relative.
+ //
+ const value&
+ optional_absolute (scope& root, const variable&);
+
+ inline const value&
+ optional_absolute (scope& root, const std::string& var)
+ {
+ return optional_absolute (root, var_pool.find (var));
+ }
+
+ // Check whether there are any variables specified from the config
+ // namespace. The idea is that we can check if there are any, say,
+ // config.install.* values. If there are none, then we can assume
+ // this functionality is not (yet) used and omit writing a whole
+ // bunch of NULL config.install.* values to the config.build file.
+ // We call it omitted/delayed configuration.
+ //
+ // Note that this function detects and ignores the special
+ // config.*.configured variable which may be used by a module to
+ // "remember" that it is unconfigured.
+ //
+ bool
+ specified (scope& root, const std::string& ns);
+
+ // @@ Why are these here?
+ //
+
+ // Add all the values from a variable to the C-string list. T is
+ // either target or scope. The variable is expected to be of type
+ // strings.
+ //
+ template <typename T>
+ void
+ append_options (cstrings& args, T& s, const char* var);
+
+ // As above but from the strings value directly.
+ //
+ void
+ append_options (cstrings& args, const const_strings_value&);
+
+ // Check if a specified option is present in the variable value.
+ // T is either target or scope.
+ //
+ template <typename T>
+ bool
+ find_option (const char* option, T& s, const char* var);
+ }
+}
+
+#include <build2/config/utility.txx>
+#include <build2/config/utility.ixx>
+
+#endif // BUILD2_CONFIG_UTILITY
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
new file mode 100644
index 0000000..361bd5e
--- /dev/null
+++ b/build2/config/utility.cxx
@@ -0,0 +1,92 @@
+// file : build2/config/utility.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/config/utility>
+
+#include <build2/context>
+
+using namespace std;
+
+namespace build2
+{
+ namespace config
+ {
+ const value&
+ optional (scope& root, const variable& var)
+ {
+ auto l (root[var]);
+
+ return l.defined ()
+ ? l.belongs (*global_scope) ? (root.assign (var) = *l) : *l
+ : root.assign (var); // NULL
+ }
+
+ const value&
+ optional_absolute (scope& root, const variable& var)
+ {
+ auto l (root[var]);
+
+ if (!l.defined ())
+ return root.assign (var); // NULL
+
+ if (!l.belongs (*global_scope)) // Value from (some) root scope.
+ return *l;
+
+ // Make the command-line value absolute. This is necessary to avoid
+ // a warning issued by the config module about global/root scope
+ // value mismatch.
+ //
+ value& v (const_cast<value&> (*l));
+
+ if (v && !v.empty ())
+ {
+ dir_path& d (as<dir_path> (v));
+
+ if (d.relative ())
+ {
+ d = work / d;
+ d.normalize ();
+ }
+ }
+
+ return root.assign (var) = v;
+ }
+
+ bool
+ specified (scope& r, const string& ns)
+ {
+ // Search all outer scopes for any value in this namespace.
+ //
+ for (scope* s (&r); s != nullptr; s = s->parent_scope ())
+ {
+ for (auto p (s->vars.find_namespace (ns));
+ p.first != p.second;
+ ++p.first)
+ {
+ const variable& var (p.first->first);
+
+ // Ignore config.*.configured.
+ //
+ if (var.name.size () < 11 ||
+ var.name.compare (var.name.size () - 11, 11, ".configured") != 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void
+ append_options (cstrings& args, const const_strings_value& sv)
+ {
+ if (!sv.empty ())
+ {
+ args.reserve (args.size () + sv.size ());
+
+ for (const string& s: sv)
+ args.push_back (s.c_str ());
+ }
+ }
+ }
+}
diff --git a/build2/config/utility.ixx b/build2/config/utility.ixx
new file mode 100644
index 0000000..b665248
--- /dev/null
+++ b/build2/config/utility.ixx
@@ -0,0 +1,17 @@
+// file : build2/config/utility.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+namespace build2
+{
+ namespace config
+ {
+ template <typename T>
+ inline void
+ append_options (cstrings& args, T& s, const char* var)
+ {
+ if (auto l = s[var])
+ append_options (args, as<strings> (*l));
+ }
+ }
+}
diff --git a/build2/config/utility.txx b/build2/config/utility.txx
new file mode 100644
index 0000000..c88a34f
--- /dev/null
+++ b/build2/config/utility.txx
@@ -0,0 +1,45 @@
+// file : build2/config/utility.txx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/scope>
+
+namespace build2
+{
+ namespace config
+ {
+ template <typename T>
+ std::pair<std::reference_wrapper<const value>, bool>
+ required (scope& root, const variable& var, const T& def_value, bool ovr)
+ {
+ using result = std::pair<std::reference_wrapper<const value>, bool>;
+
+ if (auto l = root[var])
+ {
+ if (l.belongs (*global_scope))
+ return result (root.assign (var) = *l, true);
+
+ if (!ovr || l.belongs (root))
+ return result (*l, false);
+ }
+
+ return result (root.assign (var) = def_value, true);
+ }
+
+ template <typename T>
+ bool
+ find_option (const char* option, T& s, const char* var)
+ {
+ if (auto l = s[var])
+ {
+ for (const std::string& s: as<strings> (*l))
+ {
+ if (s == option)
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}