aboutsummaryrefslogtreecommitdiff
path: root/build/cli
diff options
context:
space:
mode:
Diffstat (limited to 'build/cli')
-rw-r--r--build/cli/module23
-rw-r--r--build/cli/module.cxx244
-rw-r--r--build/cli/rule32
-rw-r--r--build/cli/rule.cxx305
-rw-r--r--build/cli/target62
-rw-r--r--build/cli/target.cxx77
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.
- };
- }
-}