From 9fb791e9fad6c63fc1dac49f4d05ae63b8a3db9b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 5 Jan 2016 11:55:15 +0200 Subject: Rename build directory/namespace to build2 --- build2/cli/module.cxx | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 build2/cli/module.cxx (limited to 'build2/cli/module.cxx') diff --git a/build2/cli/module.cxx b/build2/cli/module.cxx new file mode 100644 index 0000000..eafa4a0 --- /dev/null +++ b/build2/cli/module.cxx @@ -0,0 +1,244 @@ +// file : build2/cli/module.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace cli + { + static compile compile_; + + extern "C" bool + cli_init (scope& root, + scope& base, + const location& loc, + std::unique_ptr&, + bool first, + bool optional) + { + tracer trace ("cli::init"); + level5 ([&]{trace << "for " << base.out_path ();}); + + // Make sure the cxx module has been loaded since we need its + // targets types (?xx{}). Note that we don't try to load it + // ourselves because of the non-trivial variable merging + // semantics. So it is better to let the user load cxx + // explicitly. + // + { + auto l (base["cxx.loaded"]); + + if (!l || !as (*l)) + fail (loc) << "cxx module must be loaded before cli"; + } + + // Enter module variables. + // + if (first) + { + auto& v (var_pool); + + v.find ("config.cli.configured", bool_type); + + v.find ("config.cli", string_type); //@@ VAR type + + v.find ("config.cli.options", strings_type); + v.find ("cli.options", strings_type); + } + + // Register target types. + // + { + auto& t (base.target_types); + + t.insert (); + t.insert (); + } + + // Configure. + // + // The plan is as follows: try to configure the module. If this fails, + // we are using default values, and the module is optional, leave it + // unconfigured. + // + + // We will only honor optional if the user didn't specify any cli + // configuration explicitly. + // + optional = optional && !config::specified (root, "config.cli"); + + // Don't re-run tests if the configuration says we are unconfigured. + // + if (optional) + { + auto l (root["config.cli.configured"]); + + if (l && !as (*l)) + return false; + } + + // config.cli + // + if (first) + { + // Return version or empty string if unable to execute (e.g., + // the cli executable is not found). + // + auto test = [optional] (const char* cli) -> string + { + const char* args[] = {cli, "--version", nullptr}; + + if (verb >= 2) + print_process (args); + else if (verb) + text << "test " << cli; + + string ver; + try + { + process pr (args, 0, -1); // Open pipe to stdout. + ifdstream is (pr.in_ofd); + + // The version should be the last word on the first line. + // + getline (is, ver); + auto p (ver.rfind (' ')); + if (p != string::npos) + ver = string (ver, p + 1); + + is.close (); // Don't block the other end. + + if (!pr.wait ()) + return string (); // Not found. + + if (ver.empty ()) + fail << "unexpected output from " << cli; + + return ver; + } + catch (const process_error& e) + { + // In some cases this is not enough (e.g., the runtime linker + // will print scary errors if some shared libraries are not + // found. So it would be good to redirect child's STDERR. + // + if (!optional) + error << "unable to execute " << cli << ": " << e.what (); + + if (e.child ()) + exit (1); + + throw failed (); + } + }; + + string ver; + const char* cli ("cli"); // Default. + + if (optional) + { + // Test the default value before setting any config.cli.* values + // so that if we fail to configure, nothing will be written to + // config.build. + // + ver = test (cli); + + if (ver.empty ()) + { + // Note that we are unconfigured so that we don't keep re-testing + // this on each run. + // + root.assign ("config.cli.configured") = false; + + if (verb >= 2) + text << cli << " not found, leaving cli module unconfigured"; + + return false; + } + else + { + auto p (config::required (root, "config.cli", cli)); + assert (p.second && as (p.first) == cli); + } + } + else + { + auto p (config::required (root, "config.cli", cli)); + + // If we actually set a new value, test it by trying to execute. + // + if (p.second) + { + cli = as (p.first).c_str (); + ver = test (cli); + + if (ver.empty ()) + throw failed (); + } + } + + // Clear the unconfigured flag, if any. + // + root.assign ("config.cli.configured") = true; + + if (!ver.empty () && verb >= 2) + text << cli << " " << ver; + } + + // config.cli.options + // + // This one is optional. We also merge it into the corresponding + // cli.* variables. See the cxx module for more information on + // this merging semantics and some of its tricky aspects. + // + if (const value& v = config::optional (root, "config.cli.options")) + base.assign ("cli.options") += as (v); + + // Register our rules. + // + { + auto& r (base.rules); + + r.insert (perform_update_id, "cli.compile", compile_); + r.insert (perform_clean_id, "cli.compile", compile_); + + r.insert (perform_update_id, "cli.compile", compile_); + r.insert (perform_clean_id, "cli.compile", compile_); + + r.insert (perform_update_id, "cli.compile", compile_); + r.insert (perform_clean_id, "cli.compile", compile_); + + r.insert (perform_update_id, "cli.compile", compile_); + r.insert (perform_clean_id, "cli.compile", compile_); + + // Other rules (e.g., cxx::compile) may need to have the group + // members resolved. Looks like a general pattern: groups should + // resolve on configure(update). + // + r.insert (configure_update_id, "cli.compile", compile_); + } + + return true; + } + } +} -- cgit v1.1