aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
Diffstat (limited to 'build2')
-rw-r--r--build2/b.cxx13
-rw-r--r--build2/bash/init.cxx89
-rw-r--r--build2/bash/init.hxx28
-rw-r--r--build2/bash/rule.cxx442
-rw-r--r--build2/bash/rule.hxx89
-rw-r--r--build2/bash/target.cxx30
-rw-r--r--build2/bash/target.hxx32
-rw-r--r--build2/bash/utility.hxx27
-rw-r--r--build2/buildfile2
9 files changed, 8 insertions, 744 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index a016278..2bb5920 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -54,16 +54,17 @@
#include <libbuild2/in/init.hxx>
#include <libbuild2/version/init.hxx>
+#ifndef BUILD2_BOOTSTRAP
+# include <libbuild2/bash/init.hxx>
+
+# include <build2/cli/init.hxx>
+#endif
+
#include <build2/bin/init.hxx>
#include <build2/c/init.hxx>
#include <build2/cc/init.hxx>
#include <build2/cxx/init.hxx>
-#ifndef BUILD2_BOOTSTRAP
-# include <build2/cli/init.hxx>
-# include <build2/bash/init.hxx>
-#endif
-
using namespace butl;
using namespace std;
@@ -481,7 +482,7 @@ main (int argc, char* argv[])
bm["cli.config"] = mf {"cli.config", nullptr, &cli::config_init};
bm["cli"] = mf {"cli", nullptr, &cli::init};
- bm["bash"] = mf {"bash", nullptr, &bash::init};
+ reg (&bash::build2_bash_load);
#endif
}
diff --git a/build2/bash/init.cxx b/build2/bash/init.cxx
deleted file mode 100644
index 146e680..0000000
--- a/build2/bash/init.cxx
+++ /dev/null
@@ -1,89 +0,0 @@
-// file : build2/bash/init.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/bash/init.hxx>
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/context.hxx>
-#include <libbuild2/variable.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <libbuild2/install/utility.hxx>
-
-#include <build2/bash/rule.hxx>
-#include <build2/bash/target.hxx>
-#include <build2/bash/utility.hxx>
-
-using namespace std;
-
-namespace build2
-{
- namespace bash
- {
- static const in_rule in_rule_;
- static const install_rule install_rule_ (in_rule_);
-
- bool
- init (scope& rs,
- scope& bs,
- const location& l,
- unique_ptr<module_base>&,
- bool,
- bool,
- const variable_map&)
- {
- tracer trace ("bash::init");
- l5 ([&]{trace << "for " << bs;});
-
- // Load in.base (in.* variables, in{} target type).
- //
- if (!cast_false<bool> (rs["in.base.loaded"]))
- load_module (rs, rs, "in.base", l);
-
- bool install_loaded (cast_false<bool> (rs["install.loaded"]));
-
- // Register target types and configure default installability.
- //
- bs.target_types.insert<bash> ();
-
- if (install_loaded)
- {
- using namespace install;
-
- // Install into bin/<project>/ by default stripping the .bash
- // extension from <project> if present.
- //
- const project_name& p (cast<project_name> (rs.vars[var_project]));
-
- install_path<bash> (bs, dir_path ("bin") /= project_base (p));
- install_mode<bash> (bs, "644");
- }
-
- // Register rules.
- //
- {
- auto& r (bs.rules);
-
- r.insert<exe> (perform_update_id, "bash.in", in_rule_);
- r.insert<exe> (perform_clean_id, "bash.in", in_rule_);
- r.insert<exe> (configure_update_id, "bash.in", in_rule_);
-
- r.insert<bash> (perform_update_id, "bash.in", in_rule_);
- r.insert<bash> (perform_clean_id, "bash.in", in_rule_);
- r.insert<bash> (configure_update_id, "bash.in", in_rule_);
-
- if (install_loaded)
- {
- r.insert<exe> (perform_install_id, "bash.install", install_rule_);
- r.insert<exe> (perform_uninstall_id, "bash.uninstall", install_rule_);
-
- r.insert<bash> (perform_install_id, "bash.install", install_rule_);
- r.insert<bash> (perform_uninstall_id, "bash.uninstall", install_rule_);
- }
- }
-
- return true;
- }
- }
-}
diff --git a/build2/bash/init.hxx b/build2/bash/init.hxx
deleted file mode 100644
index 2a7e95c..0000000
--- a/build2/bash/init.hxx
+++ /dev/null
@@ -1,28 +0,0 @@
-// file : build2/bash/init.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_BASH_INIT_HXX
-#define BUILD2_BASH_INIT_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/module.hxx>
-
-namespace build2
-{
- namespace bash
- {
- bool
- init (scope&,
- scope&,
- const location&,
- unique_ptr<module_base>&,
- bool,
- bool,
- const variable_map&);
- }
-}
-
-#endif // BUILD2_BASH_INIT_HXX
diff --git a/build2/bash/rule.cxx b/build2/bash/rule.cxx
deleted file mode 100644
index a612d23..0000000
--- a/build2/bash/rule.cxx
+++ /dev/null
@@ -1,442 +0,0 @@
-// file : build2/bash/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/bash/rule.hxx>
-
-#include <cstring> // strlen(), strchr()
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/target.hxx>
-#include <libbuild2/algorithm.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <libbuild2/in/target.hxx>
-
-#include <build2/bash/target.hxx>
-#include <build2/bash/utility.hxx>
-
-using namespace std;
-using namespace butl;
-
-namespace build2
-{
- namespace bash
- {
- using in::in;
-
- struct match_data
- {
- // The "for install" condition is signalled to us by install_rule when
- // it is matched for the update operation. It also verifies that if we
- // have already been executed, then it was for install.
- //
- // See cc::link_rule for a discussion of some subtleties in this logic.
- //
- optional<bool> for_install;
- };
-
- static_assert (sizeof (match_data) <= target::data_size,
- "insufficient space");
-
- // in_rule
- //
- bool in_rule::
- match (action a, target& t, const string&) const
- {
- tracer trace ("bash::in_rule::match");
-
- // Note that for bash{} we match even if the target does not depend on
- // any modules (while it could have been handled by the in module, that
- // would require loading it).
- //
- bool fi (false); // Found in.
- bool fm (t.is_a<bash> ()); // Found module.
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- if (include (a, t, p) != include_type::normal) // Excluded/ad hoc.
- continue;
-
- fi = fi || p.is_a<in> ();
- fm = fm || p.is_a<bash> ();
- }
-
- if (!fi)
- l4 ([&]{trace << "no in file prerequisite for target " << t;});
-
- if (!fm)
- l4 ([&]{trace << "no bash module prerequisite for target " << t;});
-
- return (fi && fm);
- }
-
- recipe in_rule::
- apply (action a, target& t) const
- {
- // Note that for-install is signalled by install_rule and therefore
- // can only be relied upon during execute.
- //
- t.data (match_data ());
-
- return rule::apply (a, t);
- }
-
- target_state in_rule::
- perform_update (action a, const target& t) const
- {
- // Unless the outer install rule signalled that this is update for
- // install, signal back that we've performed plain update.
- //
- match_data& md (t.data<match_data> ());
-
- if (!md.for_install)
- md.for_install = false;
-
- return rule::perform_update (a, t);
- }
-
- prerequisite_target in_rule::
- search (action a,
- const target& t,
- const prerequisite_member& pm,
- include_type i) const
- {
- tracer trace ("bash::in_rule::search");
-
- // Handle import of installed bash{} modules.
- //
- if (i == include_type::normal && pm.proj () && pm.is_a<bash> ())
- {
- // We only need this during update.
- //
- if (a != perform_update_id)
- return nullptr;
-
- const prerequisite& p (pm.prerequisite);
-
- // Form the import path.
- //
- // Note that unless specified, we use the standard .bash extension
- // instead of going through the bash{} target type since this path is
- // not in our project (and thus no project-specific customization
- // apply).
- //
- string ext (p.ext ? *p.ext : "bash");
- path ip (dir_path (project_base (*p.proj)) / p.dir / p.name);
-
- if (!ext.empty ())
- {
- ip += '.';
- ip += ext;
- }
-
- // Search in PATH, similar to butl::path_search().
- //
- if (optional<string> s = getenv ("PATH"))
- {
- for (const char* b (s->c_str ()), *e;
- b != nullptr;
- b = (e != nullptr ? e + 1 : e))
- {
- e = strchr (b, path::traits_type::path_separator);
-
- // Empty path (i.e., a double colon or a colon at the beginning or
- // end of PATH) means search in the current dirrectory. We aren't
- // going to do that. Also silently skip invalid paths, stat()
- // errors, etc.
- //
- if (size_t n = (e != nullptr ? e - b : strlen (b)))
- {
- try
- {
- path ap (b, n);
- ap /= ip;
- ap.normalize ();
-
- timestamp mt (file_mtime (ap));
-
- if (mt != timestamp_nonexistent)
- {
- auto rp (targets.insert_locked (bash::static_type,
- ap.directory (),
- dir_path () /* out */,
- p.name,
- ext,
- true /* implied */,
- trace));
-
- bash& pt (rp.first.as<bash> ());
-
- // Only set mtime/path on first insertion.
- //
- if (rp.second.owns_lock ())
- {
- pt.mtime (mt);
- pt.path (move (ap));
- }
-
- // Save the length of the import path in auxuliary data. We
- // use it in substitute_import() to infer the installation
- // directory.
- //
- return prerequisite_target (&pt, i, ip.size ());
- }
- }
- catch (const invalid_path&) {}
- catch (const system_error&) {}
- }
- }
- }
-
- // Let standard search() handle it.
- }
-
- return rule::search (a, t, pm, i);
- }
-
- optional<string> in_rule::
- substitute (const location& l,
- action a,
- const target& t,
- const string& n,
- bool strict) const
- {
- return n.compare (0, 6, "import") == 0 && (n[6] == ' ' || n[6] == '\t')
- ? substitute_import (l, a, t, trim (string (n, 7)))
- : rule::substitute (l, a, t, n, strict);
- }
-
- string in_rule::
- substitute_import (const location& l,
- action a,
- const target& t,
- const string& n) const
- {
- // Derive (relative) import path from the import name.
- //
- path ip;
-
- try
- {
- ip = path (n);
-
- if (ip.empty () || ip.absolute ())
- throw invalid_path (n);
-
- if (ip.extension_cstring () == nullptr)
- ip += ".bash";
-
- ip.normalize ();
- }
- catch (const invalid_path&)
- {
- fail (l) << "invalid import path '" << n << "'";
- }
-
- // Look for a matching prerequisite.
- //
- const path* ap (nullptr);
- for (const prerequisite_target& pt: t.prerequisite_targets[a])
- {
- if (pt.target == nullptr || pt.adhoc)
- continue;
-
- if (const bash* b = pt.target->is_a<bash> ())
- {
- const path& pp (b->path ());
- assert (!pp.empty ()); // Should have been assigned by update.
-
- // The simple "tail match" can be ambigous. Consider, for example,
- // the foo/bar.bash import path and /.../foo/bar.bash as well as
- // /.../x/foo/bar.bash prerequisites: they would both match.
- //
- // So the rule is the match must be from the project root directory
- // or from the installation directory for the import-installed
- // prerequisites.
- //
- // But we still do a simple match first since it can quickly weed
- // out candidates that cannot possibly match.
- //
- if (!pp.sup (ip))
- continue;
-
- // See if this is import-installed target (refer to search() for
- // details).
- //
- if (size_t n = pt.data)
- {
- // Both are normalized so we can compare the "tails".
- //
- const string& ps (pp.string ());
- const string& is (ip.string ());
-
- if (path::traits_type::compare (
- ps.c_str () + ps.size () - n, n,
- is.c_str (), is.size ()) == 0)
- {
- ap = &pp;
- break;
- }
- else
- continue;
- }
-
- if (const scope* rs = scopes.find (b->dir).root_scope ())
- {
- const dir_path& d (pp.sub (rs->src_path ())
- ? rs->src_path ()
- : rs->out_path ());
-
- if (pp.leaf (d) == ip)
- {
- ap = &pp;
- break;
- }
- else
- continue;
- }
-
- fail (l) << "target " << *b << " is out of project nor imported";
- }
- }
-
- if (ap == nullptr)
- fail (l) << "unable to resolve import path " << ip;
-
- match_data& md (t.data<match_data> ());
- assert (md.for_install);
-
- if (*md.for_install)
- {
- // For the installed case we assume the script and all its modules are
- // installed into the same location (i.e., the same bin/ directory)
- // and so we use the path relative to the script.
- //
- // BTW, the semantics of the source builtin in bash is to search in
- // PATH if it's a simple path (that is, does not contain directory
- // components) and then in the current working directory.
- //
- // So we have to determine the scripts's directory ourselves for which
- // we use the BASH_SOURCE array. Without going into the gory details,
- // the last element in this array is the script's path regardless of
- // whether we are in the script or (sourced) module (but it turned out
- // not to be what we need; see below).
- //
- // We also want to get the script's "real" directory even if it was
- // itself symlinked somewhere else. And this is where things get
- // hairy: we could use either realpath or readlink -f but neither is
- // available on Mac OS (there is readlink but it doesn't support the
- // -f option).
- //
- // One can get GNU readlink from Homebrew but it will be called
- // greadlink. Note also that for any serious development one will
- // probably be also getting newer bash from Homebrew since the system
- // one is stuck in the GPLv2 version 3.2.X era. So a bit of a mess.
- //
- // For now let's use readlink -f and see how it goes. If someone wants
- // to use/support their scripts on Mac OS, they have several options:
- //
- // 1. Install greadlink (coreutils) and symlink it as readlink.
- //
- // 2. Add the readlink function to their script that does nothing;
- // symlinking scripts won't be supported but the rest should work
- // fine.
- //
- // 3. Add the readlink function to their script that calls greadlink.
- //
- // 4. Add the readlink function to their script that implements the
- // -f mode (or at least the part of it that we need). See the bash
- // module tests for some examples.
- //
- // In the future we could automatically inject an implementation along
- // the (4) lines at the beginning of the script.
- //
- // Note also that we really, really want to keep the substitution a
- // one-liner since the import can be in an (indented) if-block, etc.,
- // and we still want the resulting scripts to be human-readable.
- //
- if (t.is_a<exe> ())
- {
- return
- "source \"$(dirname"
- " \"$(readlink -f"
- " \"${BASH_SOURCE[0]}\")\")/"
- + ip.string () + "\"";
- }
- else
- {
- // Things turned out to be trickier for the installed modules: we
- // cannot juts use the script's path since it itself might not be
- // installed (import installed). So we have to use the importer's
- // path and calculate its "offset" to the installation directory.
- //
- dir_path d (t.dir.leaf (t.root_scope ().out_path ()));
-
- string o;
- for (auto i (d.begin ()), e (d.end ()); i != e; ++i)
- o += "../";
-
- // Here we don't use readlink since we assume nobody will symlink
- // the modules (or they will all be symlinked together).
- //
- return
- "source \"$(dirname"
- " \"${BASH_SOURCE[0]}\")/"
- + o + ip.string () + "\"";
- }
- }
- else
- return "source " + ap->string ();
- }
-
- // install_rule
- //
- bool install_rule::
- match (action a, target& t, const string& hint) const
- {
- // We only want to handle installation if we are also the ones building
- // this target. So first run in's match().
- //
- return in_.match (a, t, hint) && file_rule::match (a, t, "");
- }
-
- recipe install_rule::
- apply (action a, target& t) const
- {
- recipe r (file_rule::apply (a, t));
-
- if (a.operation () == update_id)
- {
- // Signal to the in rule that this is update for install. And if the
- // update has already been executed, verify it was done for install.
- //
- auto& md (t.data<match_data> ());
-
- if (md.for_install)
- {
- if (!*md.for_install)
- fail << "target " << t << " already updated but not for install";
- }
- else
- md.for_install = true;
- }
-
- return r;
- }
-
- const target* install_rule::
- filter (action a, const target& t, const prerequisite& p) const
- {
- // If this is a module prerequisite, install it as long as it is in the
- // same amalgamation as we are.
- //
- if (p.is_a<bash> ())
- {
- const target& pt (search (t, p));
- return pt.in (t.weak_scope ()) ? &pt : nullptr;
- }
- else
- return file_rule::filter (a, t, p);
- }
- }
-}
diff --git a/build2/bash/rule.hxx b/build2/bash/rule.hxx
deleted file mode 100644
index c160bb7..0000000
--- a/build2/bash/rule.hxx
+++ /dev/null
@@ -1,89 +0,0 @@
-// file : build2/bash/rule.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_BASH_RULE_HXX
-#define BUILD2_BASH_RULE_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/install/rule.hxx>
-
-#include <libbuild2/in/rule.hxx>
-
-namespace build2
-{
- namespace bash
- {
- // Preprocess a bash script (exe{}) or module (bash{}) .in file that
- // imports one or more bash modules.
- //
- // Note that the default substitution symbol is '@' and the mode is lax
- // (think bash arrays). The idea is that '@' is normally used in ways that
- // are highly unlikely to be misinterpreted as substitutions. The user,
- // however, is still able to override both of these choices with the
- // corresponding in.* variables (e.g., to use '`' and strict mode).
- //
- class in_rule: public in::rule
- {
- public:
- in_rule (): rule ("bash.in 1", "bash.in", '@', false /* strict */) {}
-
- virtual bool
- match (action, target&, const string&) const override;
-
- virtual recipe
- apply (action, target&) const override;
-
- virtual target_state
- perform_update (action, const target&) const override;
-
- virtual prerequisite_target
- search (action,
- const target&,
- const prerequisite_member&,
- include_type) const override;
-
- virtual optional<string>
- substitute (const location&,
- action a,
- const target&,
- const string&,
- bool) const override;
-
- string
- substitute_import (const location&,
- action a,
- const target&,
- const string&) const;
- };
-
- // Installation rule for bash scripts (exe{}) and modules (bash{}). Here
- // we do:
- //
- // 1. Signal to in_rule that this is update for install.
- //
- // 2. Custom filtering of prerequisites.
- //
- class install_rule: public install::file_rule
- {
- public:
- install_rule (const in_rule& in): in_ (in) {}
-
- virtual bool
- match (action, target&, const string&) const override;
-
- virtual recipe
- apply (action, target&) const override;
-
- virtual const target*
- filter (action, const target&, const prerequisite&) const override;
-
- private:
- const in_rule& in_;
- };
- }
-}
-
-#endif // BUILD2_BASH_RULE_HXX
diff --git a/build2/bash/target.cxx b/build2/bash/target.cxx
deleted file mode 100644
index e843d53..0000000
--- a/build2/bash/target.cxx
+++ /dev/null
@@ -1,30 +0,0 @@
-// file : build2/bash/target.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/bash/target.hxx>
-
-#include <libbuild2/context.hxx>
-
-using namespace std;
-
-namespace build2
-{
- namespace bash
- {
- extern const char bash_ext_def[] = "bash";
-
- const target_type bash::static_type
- {
- "bash",
- &file::static_type,
- &target_factory<bash>,
- nullptr, /* fixed_extension */
- &target_extension_var<var_extension, bash_ext_def>,
- &target_pattern_var<var_extension, bash_ext_def>,
- nullptr,
- &file_search,
- false
- };
- }
-}
diff --git a/build2/bash/target.hxx b/build2/bash/target.hxx
deleted file mode 100644
index 6be83c4..0000000
--- a/build2/bash/target.hxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// file : build2/bash/target.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_BASH_TARGET_HXX
-#define BUILD2_BASH_TARGET_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-#include <libbuild2/target.hxx>
-
-namespace build2
-{
- namespace bash
- {
- // Bash module file to be sourced by a script. The default/standard
- // extension is .bash.
- //
- class bash: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
- }
-}
-
-#endif // BUILD2_BASH_TARGET_HXX
diff --git a/build2/bash/utility.hxx b/build2/bash/utility.hxx
deleted file mode 100644
index 31a6b99..0000000
--- a/build2/bash/utility.hxx
+++ /dev/null
@@ -1,27 +0,0 @@
-// file : build2/bash/utility.hxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_BASH_UTILITY_HXX
-#define BUILD2_BASH_UTILITY_HXX
-
-#include <libbuild2/types.hxx>
-#include <libbuild2/utility.hxx>
-
-namespace build2
-{
- namespace bash
- {
- // Strip the .bash extension from the project name.
- //
- // Note that the result may not be a valid project name.
- //
- inline string
- project_base (const project_name& pn)
- {
- return pn.base ("bash");
- }
- }
-}
-
-#endif // BUILD2_BASH_UTILITY_HXX
diff --git a/build2/buildfile b/build2/buildfile
index ea39e87..57c2561 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -8,7 +8,7 @@ import libs += libpkgconf%lib{pkgconf}
include ../libbuild2/
libs += ../libbuild2/lib{build2}
-for m: version in
+for m: bash in version
{
include ../libbuild2/$m/
libs += ../libbuild2/$m/lib{build2-$m}