diff options
Diffstat (limited to 'build/cli')
-rw-r--r-- | build/cli/module | 23 | ||||
-rw-r--r-- | build/cli/module.cxx | 244 | ||||
-rw-r--r-- | build/cli/rule | 32 | ||||
-rw-r--r-- | build/cli/rule.cxx | 305 | ||||
-rw-r--r-- | build/cli/target | 62 | ||||
-rw-r--r-- | build/cli/target.cxx | 77 |
6 files changed, 0 insertions, 743 deletions
diff --git a/build/cli/module b/build/cli/module deleted file mode 100644 index 221e6a0..0000000 --- a/build/cli/module +++ /dev/null @@ -1,23 +0,0 @@ -// file : build/cli/module -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD_CLI_MODULE -#define BUILD_CLI_MODULE - -#include <build/types> -#include <build/utility> - -#include <build/module> - -namespace build -{ - namespace cli - { - extern "C" bool - cli_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); - } -} - -#endif // BUILD_CLI_MODULE diff --git a/build/cli/module.cxx b/build/cli/module.cxx deleted file mode 100644 index de7d576..0000000 --- a/build/cli/module.cxx +++ /dev/null @@ -1,244 +0,0 @@ -// file : build/cli/module.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build/cli/module> - -#include <butl/process> -#include <butl/fdstream> - -#include <build/scope> -#include <build/target> -#include <build/variable> -#include <build/diagnostics> - -#include <build/cxx/target> - -#include <build/config/utility> - -#include <build/cli/target> -#include <build/cli/rule> - -using namespace std; -using namespace butl; - -namespace build -{ - namespace cli - { - static compile compile_; - - extern "C" bool - cli_init (scope& root, - scope& base, - const location& loc, - std::unique_ptr<module>&, - 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<bool> (*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<cli> (); - t.insert<cli_cxx> (); - } - - // 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<bool> (*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<string> (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<string> (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<strings> (v); - - // Register our rules. - // - { - auto& r (base.rules); - - r.insert<cli_cxx> (perform_update_id, "cli.compile", compile_); - r.insert<cli_cxx> (perform_clean_id, "cli.compile", compile_); - - r.insert<cxx::hxx> (perform_update_id, "cli.compile", compile_); - r.insert<cxx::hxx> (perform_clean_id, "cli.compile", compile_); - - r.insert<cxx::cxx> (perform_update_id, "cli.compile", compile_); - r.insert<cxx::cxx> (perform_clean_id, "cli.compile", compile_); - - r.insert<cxx::ixx> (perform_update_id, "cli.compile", compile_); - r.insert<cxx::ixx> (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<cli_cxx> (configure_update_id, "cli.compile", compile_); - } - - return true; - } - } -} diff --git a/build/cli/rule b/build/cli/rule deleted file mode 100644 index 21fcf8a..0000000 --- a/build/cli/rule +++ /dev/null @@ -1,32 +0,0 @@ -// file : build/cli/rule -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD_CLI_RULE -#define BUILD_CLI_RULE - -#include <build/rule> - -namespace build -{ - namespace cli - { - class compile: public rule - { - public: - virtual match_result - match (action, target&, const std::string& hint) const; - - virtual recipe - apply (action, target&, const match_result&) const; - - static target_state - perform_update (action, target&); - - static target_state - perform_clean (action, target&); - }; - } -} - -#endif // BUILD_CLI_RULE diff --git a/build/cli/rule.cxx b/build/cli/rule.cxx deleted file mode 100644 index 274f193..0000000 --- a/build/cli/rule.cxx +++ /dev/null @@ -1,305 +0,0 @@ -// file : build/cli/rule.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build/cli/rule> - -#include <butl/process> - -#include <build/types> -#include <build/scope> -#include <build/target> -#include <build/context> -#include <build/algorithm> -#include <build/diagnostics> - -#include <build/cli/target> - -#include <build/config/utility> - -using namespace std; -using namespace butl; - -namespace build -{ - namespace cli - { - match_result compile:: - match (action a, target& xt, const std::string&) const - { - tracer trace ("cli::compile::match"); - - if (cli_cxx* pt = xt.is_a<cli_cxx> ()) - { - // The cli.cxx{} group. - // - cli_cxx& t (*pt); - - // See if we have a .cli source file. - // - match_result r; - for (prerequisite_member p: group_prerequisite_members (a, t)) - { - if (p.is_a<cli> ()) - { - // Check that the stems match. - // - if (t.name != p.name ()) - { - level4 ([&]{trace << ".cli file stem '" << p.name () << "' " - << "doesn't match target " << t;}); - return r; - } - - r = p; - break; - } - } - - if (!r) - { - level4 ([&]{trace << "no .cli source file for target " << t;}); - return r; - } - - // If we still haven't figured out the member list, we can do - // that now. Specifically, at this stage, no further changes to - // cli.options are possible and we can determine whether the - // --suppress-inline option is present. - // - if (t.h == nullptr) - { - t.h = &search<cxx::hxx> (t.dir, t.name, nullptr, nullptr); - t.h->group = &t; - - t.c = &search<cxx::cxx> (t.dir, t.name, nullptr, nullptr); - t.c->group = &t; - - if (!config::find_option ("--suppress-inline", t, "cli.options")) - { - t.i = &search<cxx::ixx> (t.dir, t.name, nullptr, nullptr); - t.i->group = &t; - } - } - - return r; - } - else - { - // One of the ?xx{} members. - // - target& t (xt); - - // First see if we are already linked-up to the cli.cxx{} group. - // If it is some other group, then we are definitely not a match. - // - if (t.group != nullptr) - return t.group->is_a<cli_cxx> (); - - // Then check if there is a corresponding cli.cxx{} group. - // - cli_cxx* g (targets.find<cli_cxx> (t.dir, t.name)); - - // If not or if it has no prerequisites (happens when we use it to - // set cli.options) and this target has a cli{} prerequisite, then - // synthesize the group. - // - if (g == nullptr || !g->has_prerequisites ()) - { - for (prerequisite_member p: group_prerequisite_members (a, t)) - { - if (p.is_a<cli> ()) - { - // Check that the stems match. - // - if (t.name == p.name ()) - { - if (g == nullptr) - g = &targets.insert<cli_cxx> (t.dir, t.name, trace); - - g->prerequisites.emplace_back (p.as_prerequisite (trace)); - } - else - level4 ([&]{trace << ".cli file stem '" << p.name () << "' " - << "doesn't match target " << t;}); - break; - } - } - } - - if (g != nullptr) - { - // Resolve the group's members. This should link us up to - // the group. - // - resolve_group_members (a, *g); - - // For ixx{}, verify it is part of the group. - // - if (t.is_a<cxx::ixx> () && g->i == nullptr) - { - level4 ([&]{trace << "generation of inline file " << t - << " is disabled with --suppress-inline";}); - g = nullptr; - } - } - - assert (t.group == g); - return g; - } - } - - recipe compile:: - apply (action a, target& xt, const match_result& mr) const - { - if (cli_cxx* pt = xt.is_a<cli_cxx> ()) - { - cli_cxx& t (*pt); - - // Derive file names for the members. - // - t.h->derive_path (); - t.c->derive_path (); - if (t.i != nullptr) - t.i->derive_path (); - - // Inject dependency on the output directory. - // - inject_parent_fsdir (a, t); - - // Search and match prerequisite members. - // - search_and_match_prerequisite_members (a, t); - - switch (a) - { - case perform_update_id: return &perform_update; - case perform_clean_id: return &perform_clean; - default: return noop_recipe; // Configure update. - } - } - else - { - cli_cxx& g (*static_cast<cli_cxx*> (mr.target)); - build::match (a, g); - return group_recipe; // Execute the group's recipe. - } - } - - static void - append_extension (cstrings& args, - path_target& t, - const char* option, - const char* default_extension) - { - assert (t.ext != nullptr); // Should have been figured out in apply(). - - if (*t.ext != default_extension) - { - // CLI needs the extension with the leading dot (unless it is empty) - // while we store the extension without. But if there is an extension, - // then we can get it (with the dot) from the file name. - // - args.push_back (option); - args.push_back (t.ext->empty () - ? t.ext->c_str () - : t.path ().extension () - 1); - } - } - - target_state compile:: - perform_update (action a, target& xt) - { - cli_cxx& t (static_cast<cli_cxx&> (xt)); - - // Execute our prerequsites and check if we are out of date. - // - cli* s (execute_prerequisites<cli> (a, t, t.mtime ())); - - if (s == nullptr) - return target_state::unchanged; - - // Translate paths to relative (to working directory). This - // results in easier to read diagnostics. - // - path relo (relative (t.dir)); - path rels (relative (s->path ())); - - scope& rs (t.root_scope ()); - const string& cli (as<string> (*rs["config.cli"])); - - cstrings args {cli.c_str ()}; - - // See if we need to pass any --?xx-suffix options. - // - append_extension (args, *t.h, "--hxx-suffix", "hxx"); - append_extension (args, *t.c, "--cxx-suffix", "cxx"); - if (t.i != nullptr) - append_extension (args, *t.i, "--ixx-suffix", "ixx"); - - config::append_options (args, t, "cli.options"); - - if (!relo.empty ()) - { - args.push_back ("-o"); - args.push_back (relo.string ().c_str ()); - } - - args.push_back (rels.string ().c_str ()); - args.push_back (nullptr); - - if (verb >= 2) - print_process (args); - else if (verb) - text << "cli " << *s; - - try - { - process pr (args.data ()); - - if (!pr.wait ()) - throw failed (); - - t.mtime (system_clock::now ()); - } - catch (const process_error& e) - { - error << "unable to execute " << args[0] << ": " << e.what (); - - if (e.child ()) - exit (1); - - throw failed (); - } - - return target_state::changed; - } - - target_state compile:: - perform_clean (action a, target& xt) - { - cli_cxx& t (static_cast<cli_cxx&> (xt)); - - // The reverse order of update: first delete the files, then clean - // prerequisites. Also update timestamp in case there are operations - // after us that could use the information. - // - bool r (false); - - if (t.i != nullptr) - r = rmfile (t.i->path (), *t.i) || r; - r = rmfile (t.c->path (), *t.c) || r; - r = rmfile (t.h->path (), *t.h) || r; - - t.mtime (timestamp_nonexistent); - - target_state ts (r ? target_state::changed : target_state::unchanged); - - // Clean prerequisites. - // - ts |= reverse_execute_prerequisites (a, t); - - return ts; - } - } -} diff --git a/build/cli/target b/build/cli/target deleted file mode 100644 index dbb05bd..0000000 --- a/build/cli/target +++ /dev/null @@ -1,62 +0,0 @@ -// file : build/cli/target -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD_CLI_TARGET -#define BUILD_CLI_TARGET - -#include <build/target> - -#include <build/cxx/target> - -namespace build -{ - namespace cli - { - class cli: public file - { - public: - using file::file; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - - class cli_cxx: public mtime_target - { - public: - using mtime_target::mtime_target; - - union - { - // It is theoretically possible that the compiler will add - // padding between the members of this struct. This would - // mean that the optimal alignment for a pointer is greater - // than its size (and that an array of pointers is sub- - // optimally aligned). We will deal with such a beast of - // an architecture when we see it. - // - struct - { - cxx::hxx* h; - cxx::cxx* c; - cxx::ixx* i; - }; - target* m[3] = {nullptr, nullptr, nullptr}; - }; - - virtual group_view - group_members (action_type) const; - - virtual timestamp - load_mtime () const; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - } -} - -#endif // BUILD_CLI_TARGET diff --git a/build/cli/target.cxx b/build/cli/target.cxx deleted file mode 100644 index 6eef99b..0000000 --- a/build/cli/target.cxx +++ /dev/null @@ -1,77 +0,0 @@ -// file : build/cli/target.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build/cli/target> - -#include <butl/filesystem> - -using namespace std; -using namespace butl; - -namespace build -{ - namespace cli - { - // cli - // - constexpr const char cli_ext_var[] = "extension"; - constexpr const char cli_ext_def[] = "cli"; - - const target_type cli::static_type - { - "cli", - &file::static_type, - &target_factory<cli>, - &target_extension_var<cli_ext_var, cli_ext_def>, - &search_file, - false - }; - - // cli.cxx - // - group_view cli_cxx:: - group_members (action_type) const - { - return h != nullptr - ? group_view {m, (i != nullptr ? 3U : 2U)} - : group_view {nullptr, 0}; - } - - timestamp cli_cxx:: - load_mtime () const - { - // The rule has been matched which means the members should - // be resolved and paths assigned. - // - return file_mtime (h->path ()); - } - - static target* - cli_cxx_factory (const target_type&, dir_path d, string n, const string* e) - { - tracer trace ("cli::cli_cxx_factory"); - - // Pre-enter (potential) members as targets. The main purpose - // of doing this is to avoid searching for existing files in - // src_base if the buildfile mentions some of them explicitly - // as prerequisites. - // - targets.insert<cxx::hxx> (d, n, trace); - targets.insert<cxx::cxx> (d, n, trace); - targets.insert<cxx::ixx> (d, n, trace); - - return new cli_cxx (move (d), move (n), e); - } - - const target_type cli_cxx::static_type - { - "cli.cxx", - &mtime_target::static_type, - &cli_cxx_factory, - nullptr, - &search_target, - true // "See through" default iteration mode. - }; - } -} |