aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-24 16:37:29 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-28 15:01:48 +0300
commitea24f530048cbce0c5335ca3fd3632c8ce34315a (patch)
tree3f7153cad1c3e34da5e992fe53db104512d7af71 /libbuild2
parentd07521c67db21aa02dddf9615dce9f9f3dfe784c (diff)
Move bin build system module to separate library
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/bash/export.hxx4
-rw-r--r--libbuild2/bin/buildfile69
-rw-r--r--libbuild2/bin/export.hxx38
-rw-r--r--libbuild2/bin/guess.cxx464
-rw-r--r--libbuild2/bin/guess.hxx108
-rw-r--r--libbuild2/bin/init.cxx963
-rw-r--r--libbuild2/bin/init.hxx40
-rw-r--r--libbuild2/bin/rule.cxx89
-rw-r--r--libbuild2/bin/rule.hxx65
-rw-r--r--libbuild2/bin/target.cxx474
-rw-r--r--libbuild2/bin/target.hxx363
-rw-r--r--libbuild2/buildfile2
-rw-r--r--libbuild2/export.hxx4
-rw-r--r--libbuild2/in/export.hxx4
-rw-r--r--libbuild2/module.cxx1
-rw-r--r--libbuild2/version/export.hxx4
16 files changed, 2691 insertions, 1 deletions
diff --git a/libbuild2/bash/export.hxx b/libbuild2/bash/export.hxx
index d87e677..f971b2a 100644
--- a/libbuild2/bash/export.hxx
+++ b/libbuild2/bash/export.hxx
@@ -1,3 +1,7 @@
+// file : libbuild2/bash/export.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
#pragma once
// Normally we don't export class templates (but do complete specializations),
diff --git a/libbuild2/bin/buildfile b/libbuild2/bin/buildfile
new file mode 100644
index 0000000..e490214
--- /dev/null
+++ b/libbuild2/bin/buildfile
@@ -0,0 +1,69 @@
+# file : libbuild2/bin/buildfile
+# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import int_libs = libbutl%lib{butl}
+
+include ../
+int_libs += ../lib{build2}
+
+./: lib{build2-bin}: libul{build2-bin}: {hxx ixx txx cxx}{** -**.test...} \
+ $int_libs
+
+# Unit tests.
+#
+exe{*.test}:
+{
+ test = true
+ install = false
+}
+
+for t: cxx{**.test...}
+{
+ d = $directory($t)
+ n = $name($t)...
+
+ ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n}
+ $d/exe{$n}: libul{build2-bin}: bin.whole = false
+}
+
+# Build options.
+#
+obja{*}: cxx.poptions += -DLIBBUILD2_BIN_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBBUILD2_BIN_SHARED_BUILD
+
+# Export options.
+#
+lib{build2-bin}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+}
+
+liba{build2-bin}: cxx.export.poptions += -DLIBBUILD2_BIN_STATIC
+libs{build2-bin}: cxx.export.poptions += -DLIBBUILD2_BIN_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+# And because this is a build system module, we also embed the same value as
+# the interface version (note that we cannot use build.version.interface for
+# bundled modules because we could be built with a different version of the
+# build system).
+#
+ver = ($version.pre_release \
+ ? "$version.project_id" \
+ : "$version.major.$version.minor")
+
+lib{build2-bin}: bin.lib.version = @"-$ver"
+libs{build2-bin}: bin.lib.load_suffix = "-$ver"
+
+# Install into the libbuild2/bin/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+{hxx ixx txx}{*}:
+{
+ install = include/libbuild2/bin/
+ install.subdirs = true
+}
diff --git a/libbuild2/bin/export.hxx b/libbuild2/bin/export.hxx
new file mode 100644
index 0000000..944a756
--- /dev/null
+++ b/libbuild2/bin/export.hxx
@@ -0,0 +1,38 @@
+// file : libbuild2/bin/export.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#pragma once
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported functions being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBBUILD2_BIN_STATIC) // Using static.
+# define LIBBUILD2_BIN_SYMEXPORT
+#elif defined(LIBBUILD2_BIN_STATIC_BUILD) // Building static.
+# define LIBBUILD2_BIN_SYMEXPORT
+#elif defined(LIBBUILD2_BIN_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBBUILD2_BIN_SYMEXPORT __declspec(dllimport)
+# else
+# define LIBBUILD2_BIN_SYMEXPORT
+# endif
+#elif defined(LIBBUILD2_BIN_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBBUILD2_BIN_SYMEXPORT __declspec(dllexport)
+# else
+# define LIBBUILD2_BIN_SYMEXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBBUILD2_BIN_SYMEXPORT // Using static or shared.
+#endif
diff --git a/libbuild2/bin/guess.cxx b/libbuild2/bin/guess.cxx
new file mode 100644
index 0000000..68ef827
--- /dev/null
+++ b/libbuild2/bin/guess.cxx
@@ -0,0 +1,464 @@
+// file : libbuild2/bin/guess.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/bin/guess.hxx>
+
+#include <libbuild2/diagnostics.hxx>
+
+using namespace std;
+
+namespace build2
+{
+ namespace bin
+ {
+ struct guess_result
+ {
+ string id;
+ string signature;
+ string checksum;
+ semantic_version version;
+
+ guess_result () = default;
+ guess_result (string&& i, string&& s, semantic_version&& v)
+ : id (move (i)), signature (move (s)), version (move (v)) {}
+
+ bool
+ empty () const {return id.empty ();}
+ };
+
+ // Try to parse a semantic-like version from the specified position.
+ // Return 0-version if the version is invalid.
+ //
+ static inline semantic_version
+ parse_version (const string& s, size_t p = 0, const char* bs = ".-+~ ")
+ {
+ optional<semantic_version> v (parse_semantic_version (s, p, bs));
+ return v ? *v : semantic_version ();
+ }
+
+ ar_info
+ guess_ar (const path& ar, const path* rl, const dir_path& fallback)
+ {
+ tracer trace ("bin::guess_ar");
+
+ process_path arp, rlp;
+ guess_result arr, rlr;
+
+ {
+ auto df = make_diag_frame (
+ [](const diag_record& dr)
+ {
+ dr << info << "use config.bin.ar to override";
+ });
+
+ // Only search in PATH (specifically, omitting the current
+ // executable's directory on Windows).
+ //
+ arp = run_search (ar, true, fallback, true /* path_only */);
+ }
+
+ if (rl != nullptr)
+ {
+ auto df = make_diag_frame (
+ [](const diag_record& dr)
+ {
+ dr << info << "use config.bin.ranlib to override";
+ });
+
+ rlp = run_search (*rl, true, fallback, true /* path_only */);
+ }
+
+ // Binutils, LLVM, and FreeBSD ar/ranlib all recognize the --version
+ // option. While Microsoft's lib.exe doesn't support --version, it only
+ // issues a warning and exits with zero status, printing its usual
+ // banner before that (running lib.exe without any options result in
+ // non-zero exit status -- go figure). So we are going to start with
+ // that.
+ //
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ // Normally GNU binutils ar --version output has a line that starts
+ // with "GNU ar" and ends with the version. For example:
+ //
+ // "GNU ar (GNU Binutils) 2.26"
+ // "GNU ar (GNU Binutils for Ubuntu) 2.26.1"
+ //
+ // However, some embedded toolchain makers customize this stuff in
+ // all kinds of ways. For example:
+ //
+ // "ppc-vle-ar (HighTec Release HDP-v4.6.6.1-bosch-1.3-3c1e3bc) build on 2017-03-23 (GNU Binutils) 2.20"
+ // "GNU ar version 2.13 (tricore) using BFD version 2.13 (2008-12-10)"
+ //
+ // So let's look for "GNU " and be prepared to find junk instead of
+ // the version.
+ //
+ if (l.find ("GNU ") != string::npos)
+ {
+ semantic_version v (parse_version (l, l.rfind (' ') + 1));
+ return guess_result ("gnu", move (l), move (v));
+ }
+
+ // LLVM ar --version output has a line that starts with
+ // "LLVM version " and ends with the version, for example:
+ //
+ // "LLVM version 3.5.2"
+ // "LLVM version 5.0.0"
+ //
+ if (l.compare (0, 13, "LLVM version ") == 0)
+ {
+ semantic_version v (parse_version (l, l.rfind (' ') + 1));
+ return guess_result ("llvm", move (l), move (v));
+ }
+
+ // FreeBSD ar --verison output starts with "BSD ar " followed by
+ // the version and some extra information, for example:
+ //
+ // "BSD ar 1.1.0 - libarchive 3.1.2"
+ //
+ // We will treat the extra information as the build component.
+ //
+ if (l.compare (0, 7, "BSD ar ") == 0)
+ {
+ semantic_version v (parse_version (l, 7));
+ return guess_result ("bsd", move (l), move (v));
+ }
+
+ // Microsoft lib.exe output starts with "Microsoft (R) " and ends
+ // with a four-component version, for example:
+ //
+ // "Microsoft (R) Library Manager Version 14.00.24215.1"
+ // "Microsoft (R) Library Manager Version 14.14.26428.1"
+ //
+ if (l.compare (0, 14, "Microsoft (R) ") == 0)
+ {
+ semantic_version v (parse_version (l, l.rfind (' ') + 1));
+ return guess_result ("msvc", move (l), move (v));
+ }
+
+ return guess_result ();
+ };
+
+ // Suppress all the errors because we may be trying an unsupported
+ // option. Note that in case of lib.exe we will hash the warning
+ // (yes, it goes to stdout) but that seems harmless.
+ //
+ sha256 cs;
+ arr = run<guess_result> (3, arp, "--version", f, false, false, &cs);
+
+ if (!arr.empty ())
+ arr.checksum = cs.string ();
+ }
+
+ // On Mac OS X (and probably also older BSDs) ar/ranlib doesn't have an
+ // option to display version or help. If we run it without any arguments
+ // it dumps usage and exist with an error status. So we will have to use
+ // that.
+ //
+ if (arr.empty ())
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ return l.find (" ar ") != string::npos
+ ? guess_result ("generic", move (l), semantic_version ())
+ : guess_result ();
+ };
+
+ // Redirect STDERR to STDOUT and ignore exit status.
+ //
+ sha256 cs;
+ arr = run<guess_result> (3, arp, f, false, true, &cs);
+
+ if (!arr.empty ())
+ {
+ l4 ([&]{trace << "generic ar '" << arr.signature << "'";});
+ arr.checksum = cs.string ();
+ }
+ }
+
+ if (arr.empty ())
+ fail << "unable to guess " << ar << " signature";
+
+ // Now repeat pretty much the same steps for ranlib if requested. We
+ // don't bother with the version assuming it is the same as for ar.
+ //
+ if (rl != nullptr)
+ {
+ // Binutils, LLVM, and FreeBSD.
+ //
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ // The same story as with ar: normally starts with "GNU ranlib "
+ // but can vary.
+ //
+ if (l.find ("GNU ") != string::npos)
+ return guess_result ("gnu", move (l), semantic_version ());
+
+ // "LLVM version ".
+ //
+ if (l.compare (0, 13, "LLVM version ") == 0)
+ return guess_result ("llvm", move (l), semantic_version ());
+
+ // On FreeBSD we get "ranlib" rather than "BSD ranlib" for some
+ // reason. Which means we can't really call it 'bsd' for sure.
+ //
+ //if (l.compare (0, 7, "ranlib ") == 0)
+ // return guess_result ("bsd", move (l), semantic_version ());
+
+ return guess_result ();
+ };
+
+ sha256 cs;
+ rlr = run<guess_result> (3, rlp, "--version", f, false, false, &cs);
+
+ if (!rlr.empty ())
+ rlr.checksum = cs.string ();
+ }
+
+ // Mac OS X (and probably also older BSDs).
+ //
+ if (rlr.empty ())
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ return l.find ("ranlib") != string::npos
+ ? guess_result ("generic", move (l), semantic_version ())
+ : guess_result ();
+ };
+
+ // Redirect STDERR to STDOUT and ignore exit status.
+ //
+ sha256 cs;
+ rlr = run<guess_result> (3, rlp, f, false, true, &cs);
+
+ if (!rlr.empty ())
+ {
+ l4 ([&]{trace << "generic ranlib '" << rlr.signature << "'";});
+ rlr.checksum = cs.string ();
+ }
+ }
+
+ if (rlr.empty ())
+ fail << "unable to guess " << *rl << " signature";
+ }
+
+ return ar_info {
+ move (arp),
+ move (arr.id),
+ move (arr.signature),
+ move (arr.checksum),
+ move (arr.version),
+
+ move (rlp),
+ move (rlr.id),
+ move (rlr.signature),
+ move (rlr.checksum)};
+ }
+
+ ld_info
+ guess_ld (const path& ld, const dir_path& fallback)
+ {
+ tracer trace ("bin::guess_ld");
+
+ guess_result r;
+
+ process_path pp;
+ {
+ auto df = make_diag_frame (
+ [](const diag_record& dr)
+ {
+ dr << info << "use config.bin.ld to override";
+ });
+
+ // Only search in PATH (specifically, omitting the current
+ // executable's directory on Windows).
+ //
+ pp = run_search (ld, true, fallback, true /* path_only */);
+ }
+
+ // Binutils ld recognizes the --version option. Microsoft's link.exe
+ // doesn't support --version (nor any other way to get the version
+ // without the error exit status) but it will still print its banner.
+ // We also want to recognize link.exe as fast as possible since it will
+ // be the most commonly configured linker (for other platoforms the
+ // linker will normally be used indirectly via the compiler and the
+ // bin.ld module won't be loaded). So we are going to ignore the error
+ // exit status. Our signatures are fairly specific to avoid any kind
+ // of false positives.
+ //
+ // Version extraction is a @@ TODO.
+ //
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ // Microsoft link.exe output starts with "Microsoft (R) ".
+ //
+ if (l.compare (0, 14, "Microsoft (R) ") == 0)
+ return guess_result ("msvc", move (l), semantic_version ());
+
+ // Binutils ld.bfd --version output has a line that starts with
+ // "GNU ld " while ld.gold -- "GNU gold". Again, fortify it against
+ // embedded toolchain customizations by search for "GNU " in the
+ // former case.
+ //
+ if (l.compare (0, 9, "GNU gold ") == 0)
+ return guess_result ("gold", move (l), semantic_version ());
+
+ if (l.find ("GNU ") != string::npos)
+ return guess_result ("gnu", move (l), semantic_version ());
+
+ return guess_result ();
+ };
+
+ // Redirect STDERR to STDOUT and ignore exit status. Note that in case
+ // of link.exe we will hash the diagnostics (yes, it goes to stdout)
+ // but that seems harmless.
+ //
+ sha256 cs;
+ r = run<guess_result> (3, pp, "--version", f, false, true, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ // Next try -v which will cover Apple's linkers.
+ //
+ if (r.empty ())
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ // New ld64 has "PROJECT:ld64" in the first line (output to stderr),
+ // for example:
+ //
+ // @(#)PROGRAM:ld PROJECT:ld64-242.2
+ //
+ if (l.find ("PROJECT:ld64") != string::npos)
+ return guess_result ("ld64", move (l), semantic_version ());
+
+ // Old ld has "cctools" in the first line, for example:
+ //
+ // Apple Computer, Inc. version cctools-622.9~2
+ //
+ if (l.find ("cctools") != string::npos)
+ return guess_result ("cctools", move (l), semantic_version ());
+
+ return guess_result ();
+ };
+
+ sha256 cs;
+ r = run<guess_result> (3, pp, "-v", f, false, false, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ // Finally try -version which will take care of LLVM's lld.
+ //
+ if (r.empty ())
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ // Unlike other LLVM tools (e.g., ar), the lld's version is printed
+ // (to stderr) as:
+ //
+ // LLVM Linker Version: 3.7
+ //
+ if (l.compare (0, 19, "LLVM Linker Version") == 0)
+ return guess_result ("llvm", move (l), semantic_version ());
+
+ return guess_result ();
+ };
+
+ // Suppress all the errors because we may be trying an unsupported
+ // option.
+ //
+ sha256 cs;
+ r = run<guess_result> (3, pp, "-version", f, false, false, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ if (r.empty ())
+ fail << "unable to guess " << ld << " signature";
+
+ return ld_info {
+ move (pp), move (r.id), move (r.signature), move (r.checksum)};
+ }
+
+ rc_info
+ guess_rc (const path& rc, const dir_path& fallback)
+ {
+ tracer trace ("bin::guess_rc");
+
+ guess_result r;
+
+ process_path pp;
+ {
+ auto df = make_diag_frame (
+ [](const diag_record& dr)
+ {
+ dr << info << "use config.bin.rc to override";
+ });
+
+ // Only search in PATH (specifically, omitting the current
+ // executable's directory on Windows).
+ //
+ pp = run_search (rc, true, fallback, true /* path_only */);
+ }
+
+ // Binutils windres recognizes the --version option.
+ //
+ // Version extraction is a @@ TODO.
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ // Binutils windres --version output has a line that starts with
+ // "GNU windres " but search for "GNU ", similar to other tools.
+ //
+ if (l.find ("GNU ") != string::npos)
+ return guess_result ("gnu", move (l), semantic_version ());
+
+ return guess_result ();
+ };
+
+ // Suppress all the errors because we may be trying an unsupported
+ // option.
+ //
+ sha256 cs;
+ r = run<guess_result> (3, pp, "--version", f, false, false, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ // Microsoft rc.exe /? prints its standard banner and exits with zero
+ // status.
+ //
+ if (r.empty ())
+ {
+ auto f = [] (string& l, bool) -> guess_result
+ {
+ if (l.compare (0, 14, "Microsoft (R) ") == 0)
+ return guess_result ("msvc", move (l), semantic_version ());
+
+ return guess_result ();
+ };
+
+ sha256 cs;
+ r = run<guess_result> (3, pp, "/?", f, false, false, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ if (r.empty ())
+ fail << "unable to guess " << rc << " signature";
+
+ return rc_info {
+ move (pp), move (r.id), move (r.signature), move (r.checksum)};
+ }
+ }
+}
diff --git a/libbuild2/bin/guess.hxx b/libbuild2/bin/guess.hxx
new file mode 100644
index 0000000..0e04ba5
--- /dev/null
+++ b/libbuild2/bin/guess.hxx
@@ -0,0 +1,108 @@
+// file : libbuild2/bin/guess.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBUILD2_BIN_GUESS_HXX
+#define LIBBUILD2_BIN_GUESS_HXX
+
+#include <libbuild2/types.hxx>
+#include <libbuild2/utility.hxx>
+
+namespace build2
+{
+ namespace bin
+ {
+ // ar/ranlib information.
+ //
+ // Currently recognized ar/ranlib and their ids:
+ //
+ // gnu GNU binutils
+ // llvm LLVM
+ // bsd FreeBSD (and maybe other BSDs)
+ // msvc Microsoft's lib.exe
+ // generic Generic/unrecognized
+ //
+ // The signature is normally the --version/-V line.
+ //
+ // The checksum is used to detect ar/ranlib changes. It is calculated in
+ // a toolchain-specific manner (usually the output of --version/-V) and
+ // is not bulletproof.
+ //
+ struct ar_info
+ {
+ process_path ar_path;
+ string ar_id;
+ string ar_signature;
+ string ar_checksum;
+ semantic_version ar_version;
+
+ process_path ranlib_path;
+ string ranlib_id;
+ string ranlib_signature;
+ string ranlib_checksum;
+ };
+
+ // The ranlib path can be NULL, in which case no ranlib guessing will be
+ // attemplated and the returned ranlib_* members will be left empty.
+ //
+ ar_info
+ guess_ar (const path& ar, const path* ranlib, const dir_path& fallback);
+
+ // ld information.
+ //
+ // Currently recognized linkers and their ids:
+ //
+ // gnu GNU binutils ld.bfd
+ // gold GNU binutils ld.gold
+ // llvm LLVM lld (note: not llvm-ld or llvm-link)
+ // ld64 Apple's new linker
+ // cctools Apple's old/classic linker
+ // msvc Microsoft's link.exe
+ //
+ // Note that BSDs are currently using GNU ld but some of them (e.g.,
+ // FreeBSD) are hoping to migrate to lld.
+ //
+ // The signature is normally the --version/-version/-v line.
+ //
+ // The checksum is used to detect ld changes. It is calculated in a
+ // toolchain-specific manner (usually the output of --version/-version/-v)
+ // and is not bulletproof.
+ //
+ struct ld_info
+ {
+ process_path path;
+ string id;
+ string signature;
+ string checksum;
+ };
+
+ ld_info
+ guess_ld (const path& ld, const dir_path& fallback);
+
+ // rc information.
+ //
+ // Currently recognized resource compilers and their ids:
+ //
+ // gnu GNU binutils windres
+ // msvc Microsoft's rc.exe
+ //
+ // The signature is normally the --version line.
+ //
+ // The checksum is used to detect rc changes. It is calculated in a
+ // toolchain-specific manner (usually the output of --version) and is not
+ // bulletproof.
+ //
+ struct rc_info
+ {
+ process_path path;
+ string id;
+ string signature;
+ string checksum;
+ };
+
+ rc_info
+ guess_rc (const path& rc, const dir_path& fallback);
+ }
+}
+
+#endif // LIBBUILD2_BIN_GUESS_HXX
diff --git a/libbuild2/bin/init.cxx b/libbuild2/bin/init.cxx
new file mode 100644
index 0000000..d56e0a5
--- /dev/null
+++ b/libbuild2/bin/init.cxx
@@ -0,0 +1,963 @@
+// file : build2/bin/init.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/bin/init.hxx>
+
+#include <map>
+
+#include <libbuild2/scope.hxx>
+#include <libbuild2/variable.hxx>
+#include <libbuild2/diagnostics.hxx>
+
+#include <libbuild2/config/utility.hxx>
+
+#include <libbuild2/test/module.hxx>
+
+#include <libbuild2/install/rule.hxx>
+#include <libbuild2/install/utility.hxx>
+
+#include <libbuild2/bin/rule.hxx>
+#include <libbuild2/bin/guess.hxx>
+#include <libbuild2/bin/target.hxx>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace bin
+ {
+ static const fail_rule fail_;
+ static const lib_rule lib_;
+
+ // Default config.bin.*.lib values.
+ //
+ static const strings exe_lib {"shared", "static"};
+ static const strings liba_lib {"static", "shared"};
+ static const strings libs_lib {"shared", "static"};
+
+ bool
+ vars_init (scope& rs,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map&)
+ {
+ tracer trace ("bin::vars_init");
+ l5 ([&]{trace << "for " << rs;});
+
+ assert (first);
+
+ // Enter variables. Note: some overridable, some not.
+ //
+ // Target is a string and not target_triplet because it can be
+ // specified by the user.
+ //
+ auto& vp (rs.ctx.var_pool.rw (rs));
+
+ vp.insert<string> ("config.bin.target", true);
+ vp.insert<string> ("config.bin.pattern", true);
+
+ // Library types to build.
+ //
+ vp.insert<string> ("config.bin.lib", true);
+
+ // Library types to use (in priority order).
+ //
+ vp.insert<strings> ("config.bin.exe.lib", true);
+ vp.insert<strings> ("config.bin.liba.lib", true);
+ vp.insert<strings> ("config.bin.libs.lib", true);
+
+ // The rpath[_link].auto flag controls automatic rpath behavior, for
+ // example, addition of rpaths for prerequisite libraries (see the cc
+ // module for an example). Default is true.
+ //
+ vp.insert<dir_paths> ("config.bin.rpath", true);
+ vp.insert<bool> ("config.bin.rpath.auto", true);
+
+ vp.insert<dir_paths> ("config.bin.rpath_link", true);
+ vp.insert<bool> ("config.bin.rpath_link.auto", true);
+
+ vp.insert<string> ("config.bin.prefix", true);
+ vp.insert<string> ("config.bin.suffix", true);
+ vp.insert<string> ("config.bin.lib.prefix", true);
+ vp.insert<string> ("config.bin.lib.suffix", true);
+ vp.insert<string> ("config.bin.exe.prefix", true);
+ vp.insert<string> ("config.bin.exe.suffix", true);
+
+ vp.insert<string> ("bin.lib");
+
+ vp.insert<strings> ("bin.exe.lib");
+ vp.insert<strings> ("bin.liba.lib");
+ vp.insert<strings> ("bin.libs.lib");
+
+ vp.insert<dir_paths> ("bin.rpath");
+ vp.insert<bool> ("bin.rpath.auto");
+
+ vp.insert<dir_paths> ("bin.rpath_link");
+ vp.insert<bool> ("bin.rpath_link.auto");
+
+ // Link whole archive. Note: non-overridable with target visibility.
+ //
+ // The lookup semantics is as follows: we first look for a prerequisite-
+ // specific value, then for a target-specific value in the library being
+ // linked, and then for target type/pattern-specific value starting from
+ // the scope of the target being linked-to. In that final lookup we do
+ // not look in the target being linked-to itself since that is used to
+ // indicate how this target should be linked to other targets. For
+ // example:
+ //
+ // exe{test}: liba{foo}
+ // liba{foo}: libua{foo1 foo2}
+ // liba{foo}: bin.whole = false # Affects test but not foo1 and foo2.
+ //
+ // If unspecified, defaults to false for liba{} and to true for libu*{}.
+ //
+ vp.insert<bool> ("bin.whole", false, variable_visibility::target);
+
+ vp.insert<string> ("bin.exe.prefix");
+ vp.insert<string> ("bin.exe.suffix");
+ vp.insert<string> ("bin.lib.prefix");
+ vp.insert<string> ("bin.lib.suffix");
+
+ vp.insert<string> ("bin.lib.load_suffix",
+ variable_visibility::project);
+
+ vp.insert<map<string, string>> ("bin.lib.version",
+ variable_visibility::project);
+
+ return true;
+ }
+
+ bool
+ config_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::config_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // We only support root loading (which means there can only be one).
+ //
+ if (&rs != &bs)
+ fail (loc) << "bin.config module must be loaded in project root";
+
+ // Load bin.vars.
+ //
+ if (!cast_false<bool> (rs["bin.vars.loaded"]))
+ load_module (rs, rs, "bin.vars", loc);
+
+ // Configure.
+ //
+ using config::required;
+ using config::optional;
+ using config::omitted;
+
+ // Adjust module priority (binutils).
+ //
+ config::save_module (rs, "bin", 350);
+
+ // The idea here is as follows: if we already have one of
+ // the bin.* variables set, then we assume this is static
+ // project configuration and don't bother setting the
+ // corresponding config.bin.* variable.
+ //
+ //@@ Need to validate the values. Would be more efficient
+ // to do it once on assignment than every time on query.
+ // Custom var type?
+ //
+
+ // config.bin.lib
+ //
+ {
+ value& v (rs.assign ("bin.lib"));
+ if (!v)
+ v = *required (rs, "config.bin.lib", "both").first;
+ }
+
+ // config.bin.exe.lib
+ //
+ {
+ value& v (rs.assign ("bin.exe.lib"));
+ if (!v)
+ v = *required (rs, "config.bin.exe.lib", exe_lib).first;
+ }
+
+ // config.bin.liba.lib
+ //
+ {
+ value& v (rs.assign ("bin.liba.lib"));
+ if (!v)
+ v = *required (rs, "config.bin.liba.lib", liba_lib).first;
+ }
+
+ // config.bin.libs.lib
+ //
+ {
+ value& v (rs.assign ("bin.libs.lib"));
+ if (!v)
+ v = *required (rs, "config.bin.libs.lib", libs_lib).first;
+ }
+
+ // config.bin.rpath[_link]
+ //
+ // These ones are optional and we merge them into bin.rpath[_link], if
+ // any.
+ //
+ rs.assign ("bin.rpath") += cast_null<dir_paths> (
+ optional (rs, "config.bin.rpath"));
+
+ rs.assign ("bin.rpath_link") += cast_null<dir_paths> (
+ optional (rs, "config.bin.rpath_link"));
+
+ // config.bin.rpath[_link].auto
+ //
+ {
+ lookup l;
+
+ rs.assign ("bin.rpath.auto") =
+ (l = omitted (rs, "config.bin.rpath.auto").first)
+ ? cast<bool> (l)
+ : true;
+
+ rs.assign ("bin.rpath_link.auto") =
+ (l = omitted (rs, "config.bin.rpath_link.auto").first)
+ ? cast<bool> (l)
+ : true;
+ }
+
+ // config.bin.{lib,exe}.{prefix,suffix}
+ //
+ // These ones are not used very often so we will omit them from the
+ // config.build if not specified. We also override any existing value
+ // that might have been specified before loading the module.
+ //
+ {
+ lookup p (omitted (rs, "config.bin.prefix").first);
+ lookup s (omitted (rs, "config.bin.suffix").first);
+
+ auto set = [&rs] (const char* bv, const char* cv, lookup l)
+ {
+ if (lookup o = omitted (rs, cv).first)
+ l = o;
+
+ if (l)
+ rs.assign (bv) = *l;
+ };
+
+ set ("bin.lib.prefix", "config.bin.lib.prefix", p);
+ set ("bin.lib.suffix", "config.bin.lib.suffix", s);
+
+ set ("bin.exe.prefix", "config.bin.exe.prefix", p);
+ set ("bin.exe.suffix", "config.bin.exe.suffix", s);
+ }
+
+ if (first)
+ {
+ bool new_val (false); // Set any new values?
+
+ // config.bin.target
+ //
+ {
+ const variable& var (rs.ctx.var_pool["config.bin.target"]);
+
+ // We first see if the value was specified via the configuration
+ // mechanism.
+ //
+ auto p (omitted (rs, var));
+ lookup l (p.first);
+
+ // Then see if there is a config hint (e.g., from the cc module).
+ //
+ bool hint (false);
+ if (!l)
+ {
+ if (auto hl = hints[var])
+ {
+ l = hl;
+ hint = true;
+ }
+ }
+
+ if (!l)
+ fail (loc) << "unable to determine binutils target" <<
+ info << "consider specifying it with " << var <<
+ info << "or first load a module that can provide it as a hint, "
+ << "such as c or cxx";
+
+ // Split/canonicalize the target.
+ //
+ string s (cast<string> (l));
+
+ // Did the user ask us to use config.sub? If this is a hinted value,
+ // then we assume it has already been passed through config.sub.
+ //
+ if (!hint && config_sub)
+ {
+ s = run<string> (3,
+ *config_sub,
+ s.c_str (),
+ [] (string& l, bool) {return move (l);});
+ l5 ([&]{trace << "config.sub target: '" << s << "'";});
+ }
+
+ try
+ {
+ target_triplet t (s);
+
+ l5 ([&]{trace << "canonical target: '" << t.string () << "'; "
+ << "class: " << t.class_;});
+
+ assert (!hint || s == t.string ());
+
+ // Also enter as bin.target.{cpu,vendor,system,version,class}
+ // for convenience of access.
+ //
+ rs.assign<string> ("bin.target.cpu") = t.cpu;
+ rs.assign<string> ("bin.target.vendor") = t.vendor;
+ rs.assign<string> ("bin.target.system") = t.system;
+ rs.assign<string> ("bin.target.version") = t.version;
+ rs.assign<string> ("bin.target.class") = t.class_;
+
+ rs.assign<target_triplet> ("bin.target") = move (t);
+ }
+ catch (const invalid_argument& e)
+ {
+ // This is where we suggest that the user specifies --config-sub
+ // to help us out.
+ //
+ fail << "unable to parse binutils target '" << s << "': " << e <<
+ info << "consider using the --config-sub option";
+ }
+
+ new_val = new_val || p.second; // False for a hinted value.
+ }
+
+ // config.bin.pattern
+ //
+ {
+ const variable& var (rs.ctx.var_pool["config.bin.pattern"]);
+
+ // We first see if the value was specified via the configuration
+ // mechanism.
+ //
+ auto p (omitted (rs, var));
+ lookup l (p.first);
+
+ // Then see if there is a config hint (e.g., from the C++ module).
+ //
+ if (!l)
+ {
+ if (auto hl = hints[var])
+ l = hl;
+ }
+
+ // For ease of use enter it as bin.pattern (since it can come from
+ // different places).
+ //
+ if (l)
+ {
+ const string& s (cast<string> (l));
+
+ if (s.empty () ||
+ (!path::traits_type::is_separator (s.back ()) &&
+ s.find ('*') == string::npos))
+ {
+ fail << "missing '*' in binutils pattern '" << s << "'";
+ }
+
+ rs.assign<string> ("bin.pattern") = s;
+ new_val = new_val || p.second; // False for a hinted value.
+ }
+ }
+
+ // If we set any new values (e.g., we are configuring), then print the
+ // report at verbosity level 2 and up (-v).
+ //
+ if (verb >= (new_val ? 2 : 3))
+ {
+ diag_record dr (text);
+
+ dr << "bin " << project (rs) << '@' << rs << '\n'
+ << " target " << cast<target_triplet> (rs["bin.target"]);
+
+ if (auto l = rs["bin.pattern"])
+ dr << '\n'
+ << " pattern " << cast<string> (l);
+ }
+ }
+
+ return true;
+ }
+
+ bool
+ init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // Load bin.config.
+ //
+ if (!cast_false<bool> (rs["bin.config.loaded"]))
+ load_module (rs, rs, "bin.config", loc, false, hints);
+
+ // Cache some config values we will be needing below.
+ //
+ const string& tclass (cast<string> (rs["bin.target.class"]));
+
+ // Register target types and configure their default "installability".
+ //
+ bool install_loaded (cast_false<bool> (rs["install.loaded"]));
+ {
+ using namespace install;
+
+ if (first)
+ {
+ rs.insert_target_type<obj> ();
+ rs.insert_target_type<obje> ();
+ rs.insert_target_type<obja> ();
+ rs.insert_target_type<objs> ();
+
+ rs.insert_target_type<bmi> ();
+ rs.insert_target_type<bmie> ();
+ rs.insert_target_type<bmia> ();
+ rs.insert_target_type<bmis> ();
+
+ rs.insert_target_type<hbmi> ();
+ rs.insert_target_type<hbmie> ();
+ rs.insert_target_type<hbmia> ();
+ rs.insert_target_type<hbmis> ();
+
+ rs.insert_target_type<libul> ();
+ rs.insert_target_type<libue> ();
+ rs.insert_target_type<libua> ();
+ rs.insert_target_type<libus> ();
+
+ rs.insert_target_type<lib> ();
+ rs.insert_target_type<liba> ();
+ rs.insert_target_type<libs> ();
+
+ // Register the def{} target type. Note that we do it here since it
+ // is input and can be specified unconditionally (i.e., not only
+ // when building for Windows).
+ //
+ rs.insert_target_type<def> ();
+ }
+
+ // Note: libu*{} members are not installable.
+ //
+ if (install_loaded)
+ {
+ install_path<liba> (bs, dir_path ("lib")); // Install in install.lib.
+ install_mode<liba> (bs, "644");
+ }
+
+ // Should shared libraries have the executable bit? That depends on
+ // who you ask. In Debian, for example, it should not unless, it
+ // really is executable (i.e., has main()). On the other hand, on
+ // some systems, this may be required in order for the dynamic
+ // linker to be able to load the library. So, by default, we will
+ // keep it executable, especially seeing that this is also the
+ // behavior of autotools. At the same time, it is easy to override
+ // this, for example:
+ //
+ // config.install.lib.mode=644
+ //
+ // And a library that wants to override any such overrides (e.g.,
+ // because it does have main()) can do:
+ //
+ // libs{foo}: install.mode=755
+ //
+ // Everyone is happy then? On Windows libs{} is the DLL and goes to
+ // bin/, not lib/.
+ //
+ if (install_loaded)
+ install_path<libs> (bs,
+ dir_path (tclass == "windows" ? "bin" : "lib"));
+
+ // Create additional target types for certain targets.
+ //
+ if (tclass == "windows")
+ {
+ // Import library.
+ //
+ if (first)
+ rs.insert_target_type<libi> ();
+
+ if (install_loaded)
+ {
+ install_path<libi> (bs, dir_path ("lib"));
+ install_mode<libi> (bs, "644");
+ }
+ }
+ }
+
+ // Register rules.
+ //
+ {
+ auto& r (bs.rules);
+
+ r.insert<obj> (perform_update_id, "bin.obj", fail_);
+ r.insert<obj> (perform_clean_id, "bin.obj", fail_);
+
+ r.insert<bmi> (perform_update_id, "bin.bmi", fail_);
+ r.insert<bmi> (perform_clean_id, "bin.bmi", fail_);
+
+ r.insert<hbmi> (perform_update_id, "bin.hbmi", fail_);
+ r.insert<hbmi> (perform_clean_id, "bin.hbmi", fail_);
+
+ r.insert<libul> (perform_update_id, "bin.libul", fail_);
+ r.insert<libul> (perform_clean_id, "bin.libul", fail_);
+
+ // Similar to alias.
+ //
+
+ //@@ outer
+ r.insert<lib> (perform_id, 0, "bin.lib", lib_);
+ r.insert<lib> (configure_id, 0, "bin.lib", lib_);
+
+ // Treat as a see through group for install and test.
+ //
+ if (install_loaded)
+ {
+ auto& gr (install::group_rule::instance);
+
+ r.insert<lib> (perform_install_id, "bin.lib", gr);
+ r.insert<lib> (perform_uninstall_id, "bin.lib", gr);
+ }
+
+ if (const test::module* m = rs.lookup_module<test::module> ("test"))
+ {
+ r.insert<lib> (perform_test_id, "bin.lib", m->group_rule ());
+ }
+ }
+
+ return true;
+ }
+
+ bool
+ ar_config_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::ar_config_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // Make sure bin.config is loaded.
+ //
+ if (!cast_false<bool> (rs["bin.config.loaded"]))
+ load_module (rs, bs, "bin.config", loc, false, hints);
+
+ // Enter configuration variables.
+ //
+ if (first)
+ {
+ auto& v (rs.ctx.var_pool.rw (rs));
+
+ v.insert<process_path> ("bin.ar.path");
+ v.insert<process_path> ("bin.ranlib.path");
+
+ v.insert<path> ("config.bin.ar", true);
+ v.insert<path> ("config.bin.ranlib", true);
+ }
+
+ // Configure.
+ //
+ if (first)
+ {
+ // config.bin.ar
+ // config.bin.ranlib
+ //
+ // For config.bin.ar we have the default (plus the pattern) while
+ // ranlib should be explicitly specified by the user in order for us
+ // to use it (all targets that we currently care to support have the
+ // ar -s option but if that changes we can always force the use of
+ // ranlib for certain targets).
+ //
+ // Another idea is to refuse to use default 'ar' (without the pattern)
+ // if the host/build targets don't match. On the other hand, a cross-
+ // toolchain can be target-unprefixed. Also, without canonicalization,
+ // comparing targets will be unreliable.
+ //
+
+ // Use the target to decide on the default binutils program names.
+ //
+ const string& tsys (cast<string> (rs["bin.target.system"]));
+ const char* ar_d (tsys == "win32-msvc" ? "lib" : "ar");
+
+ // This can be either a pattern or a fallback search directory.
+ //
+ const string* pat (cast_null<string> (rs["bin.pattern"]));
+
+ bool fb (pat != nullptr &&
+ path::traits_type::is_separator (pat->back ()));
+
+ // Don't save the default value to config.build so that if the user
+ // changes, say, the C++ compiler (which hinted the pattern), then
+ // ar will automatically change as well.
+ //
+ auto ap (
+ config::required (
+ rs,
+ "config.bin.ar",
+ path (apply_pattern (ar_d, fb ? nullptr : pat)),
+ false,
+ config::save_commented));
+
+ auto rp (
+ config::required (
+ rs,
+ "config.bin.ranlib",
+ nullptr,
+ false,
+ config::save_commented));
+
+ const path& ar (cast<path> (ap.first));
+ const path* ranlib (cast_null<path> (rp.first));
+
+ ar_info ari (
+ guess_ar (ar, ranlib, fb ? dir_path (*pat) : dir_path ()));
+
+ // If this is a new value (e.g., we are configuring), then print the
+ // report at verbosity level 2 and up (-v).
+ //
+ if (verb >= (ap.second || rp.second ? 2 : 3))
+ {
+ diag_record dr (text);
+
+ {
+ dr << "bin.ar " << project (rs) << '@' << rs << '\n'
+ << " ar " << ari.ar_path << '\n'
+ << " id " << ari.ar_id << '\n'
+ << " version " << ari.ar_version.string () << '\n'
+ << " major " << ari.ar_version.major << '\n'
+ << " minor " << ari.ar_version.minor << '\n'
+ << " patch " << ari.ar_version.patch << '\n';
+ }
+
+ if (!ari.ar_version.build.empty ())
+ {
+ dr << " build " << ari.ar_version.build << '\n';
+ }
+
+ {
+ dr << " signature " << ari.ar_signature << '\n'
+ << " checksum " << ari.ar_checksum;
+ }
+
+ if (ranlib != nullptr)
+ {
+ dr << '\n'
+ << " ranlib " << ari.ranlib_path << '\n'
+ << " id " << ari.ranlib_id << '\n'
+ << " signature " << ari.ranlib_signature << '\n'
+ << " checksum " << ari.ranlib_checksum;
+ }
+ }
+
+ rs.assign<process_path> ("bin.ar.path") = move (ari.ar_path);
+ rs.assign<string> ("bin.ar.id") = move (ari.ar_id);
+ rs.assign<string> ("bin.ar.signature") = move (ari.ar_signature);
+ rs.assign<string> ("bin.ar.checksum") = move (ari.ar_checksum);
+
+ {
+ semantic_version& v (ari.ar_version);
+
+ rs.assign<string> ("bin.ar.version") = v.string ();
+ rs.assign<uint64_t> ("bin.ar.version.major") = v.major;
+ rs.assign<uint64_t> ("bin.ar.version.minor") = v.minor;
+ rs.assign<uint64_t> ("bin.ar.version.patch") = v.patch;
+ rs.assign<string> ("bin.ar.version.build") = move (v.build);
+ }
+
+ if (ranlib != nullptr)
+ {
+ rs.assign<process_path> ("bin.ranlib.path") = move (ari.ranlib_path);
+ rs.assign<string> ("bin.ranlib.id") = move (ari.ranlib_id);
+ rs.assign<string> ("bin.ranlib.signature") =
+ move (ari.ranlib_signature);
+ rs.assign<string> ("bin.ranlib.checksum") =
+ move (ari.ranlib_checksum);
+ }
+ }
+
+ return true;
+ }
+
+ bool
+ ar_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::ar_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // Make sure the bin core and ar.config are loaded.
+ //
+ if (!cast_false<bool> (bs["bin.loaded"]))
+ load_module (rs, bs, "bin", loc, false, hints);
+
+ if (!cast_false<bool> (bs["bin.ar.config.loaded"]))
+ load_module (rs, bs, "bin.ar.config", loc, false, hints);
+
+ return true;
+ }
+
+ bool
+ ld_config_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::ld_config_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // Make sure bin.config is loaded.
+ //
+ if (!cast_false<bool> (rs["bin.config.loaded"]))
+ load_module (rs, rs, "bin.config", loc, false, hints);
+
+ // Enter configuration variables.
+ //
+ if (first)
+ {
+ auto& v (rs.ctx.var_pool.rw (rs));
+
+ v.insert<process_path> ("bin.ld.path");
+ v.insert<path> ("config.bin.ld", true);
+ }
+
+ // Configure.
+ //
+ if (first)
+ {
+ // config.bin.ld
+ //
+ // Use the target to decide on the default ld name.
+ //
+ const string& tsys (cast<string> (rs["bin.target.system"]));
+ const char* ld_d (tsys == "win32-msvc" ? "link" : "ld");
+
+ // This can be either a pattern or a fallback search directory.
+ //
+ const string* pat (cast_null<string> (rs["bin.pattern"]));
+
+ bool fb (pat != nullptr &&
+ path::traits_type::is_separator (pat->back ()));
+
+ auto p (
+ config::required (
+ rs,
+ "config.bin.ld",
+ path (apply_pattern (ld_d, fb ? nullptr : pat)),
+ false,
+ config::save_commented));
+
+ const path& ld (cast<path> (p.first));
+ ld_info ldi (guess_ld (ld, fb ? dir_path (*pat) : dir_path ()));
+
+ // If this is a new value (e.g., we are configuring), then print the
+ // report at verbosity level 2 and up (-v).
+ //
+ if (verb >= (p.second ? 2 : 3))
+ {
+ text << "bin.ld " << project (rs) << '@' << rs << '\n'
+ << " ld " << ldi.path << '\n'
+ << " id " << ldi.id << '\n'
+ << " signature " << ldi.signature << '\n'
+ << " checksum " << ldi.checksum;
+ }
+
+ rs.assign<process_path> ("bin.ld.path") = move (ldi.path);
+ rs.assign<string> ("bin.ld.id") = move (ldi.id);
+ rs.assign<string> ("bin.ld.signature") = move (ldi.signature);
+ rs.assign<string> ("bin.ld.checksum") = move (ldi.checksum);
+ }
+
+ return true;
+ }
+
+ bool
+ ld_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::ld_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // Make sure the bin core and ld.config are loaded.
+ //
+ if (!cast_false<bool> (bs["bin.loaded"]))
+ load_module (rs, bs, "bin", loc, false, hints);
+
+ if (!cast_false<bool> (bs["bin.ld.config.loaded"]))
+ load_module (rs, bs, "bin.ld.config", loc, false, hints);
+
+ const string& lid (cast<string> (rs["bin.ld.id"]));
+
+ // Register the pdb{} target if using the VC toolchain.
+ //
+ using namespace install;
+
+ if (lid == "msvc")
+ {
+ const target_type& pdb (bs.derive_target_type<file> ("pdb").first);
+ install_path (bs, pdb, dir_path ("bin")); // Goes to install.bin
+ install_mode (bs, pdb, "644"); // But not executable.
+ }
+
+ return true;
+ }
+
+ bool
+ rc_config_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::rc_config_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // Make sure bin.config is loaded.
+ //
+ if (!cast_false<bool> (bs["bin.config.loaded"]))
+ load_module (rs, bs, "bin.config", loc, false, hints);
+
+ // Enter configuration variables.
+ //
+ if (first)
+ {
+ auto& v (rs.ctx.var_pool.rw (rs));
+
+ v.insert<process_path> ("bin.rc.path");
+ v.insert<path> ("config.bin.rc", true);
+ }
+
+ // Configure.
+ //
+ if (first)
+ {
+ // config.bin.rc
+ //
+ // Use the target to decide on the default rc name.
+ //
+ const string& tsys (cast<string> (rs["bin.target.system"]));
+ const char* rc_d (tsys == "win32-msvc" ? "rc" : "windres");
+
+ // This can be either a pattern or a fallback search directory.
+ //
+ const string* pat (cast_null<string> (rs["bin.pattern"]));
+
+ bool fb (pat != nullptr &&
+ path::traits_type::is_separator (pat->back ()));
+
+ auto p (
+ config::required (
+ rs,
+ "config.bin.rc",
+ path (apply_pattern (rc_d, fb ? nullptr : pat)),
+ false,
+ config::save_commented));
+
+ const path& rc (cast<path> (p.first));
+ rc_info rci (guess_rc (rc, fb ? dir_path (*pat) : dir_path ()));
+
+ // If this is a new value (e.g., we are configuring), then print the
+ // report at verbosity level 2 and up (-v).
+ //
+ if (verb >= (p.second ? 2 : 3))
+ {
+ text << "bin.rc " << project (rs) << '@' << rs << '\n'
+ << " rc " << rci.path << '\n'
+ << " id " << rci.id << '\n'
+ << " signature " << rci.signature << '\n'
+ << " checksum " << rci.checksum;
+ }
+
+ rs.assign<process_path> ("bin.rc.path") = move (rci.path);
+ rs.assign<string> ("bin.rc.id") = move (rci.id);
+ rs.assign<string> ("bin.rc.signature") = move (rci.signature);
+ rs.assign<string> ("bin.rc.checksum") = move (rci.checksum);
+ }
+
+ return true;
+ }
+
+ bool
+ rc_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("bin::rc_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // Make sure the bin core and rc.config are loaded.
+ //
+ if (!cast_false<bool> (bs["bin.loaded"]))
+ load_module (rs, bs, "bin", loc, false, hints);
+
+ if (!cast_false<bool> (bs["bin.rc.config.loaded"]))
+ load_module (rs, bs, "bin.rc.config", loc, false, hints);
+
+ return true;
+ }
+
+ static const module_functions mod_functions[] =
+ {
+ // NOTE: don't forget to also update the documentation in init.hxx if
+ // changing anything here.
+
+ {"bin.vars", nullptr, vars_init},
+ {"bin.config", nullptr, config_init},
+ {"bin", nullptr, init},
+ {"bin.ar.config", nullptr, ar_config_init},
+ {"bin.ar", nullptr, ar_init},
+ {"bin.ld.config", nullptr, ld_config_init},
+ {"bin.ld", nullptr, ld_init},
+ {"bin.rc.config", nullptr, rc_config_init},
+ {"bin.rc", nullptr, rc_init},
+ {nullptr, nullptr, nullptr}
+ };
+
+ const module_functions*
+ build2_bin_load ()
+ {
+ return mod_functions;
+ }
+ }
+}
diff --git a/libbuild2/bin/init.hxx b/libbuild2/bin/init.hxx
new file mode 100644
index 0000000..41580df
--- /dev/null
+++ b/libbuild2/bin/init.hxx
@@ -0,0 +1,40 @@
+// file : libbuild2/bin/init.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBUILD2_BIN_INIT_HXX
+#define LIBBUILD2_BIN_INIT_HXX
+
+#include <libbuild2/types.hxx>
+#include <libbuild2/utility.hxx>
+
+#include <libbuild2/module.hxx>
+
+#include <libbuild2/bin/export.hxx>
+
+namespace build2
+{
+ namespace bin
+ {
+ // Module `bin` does not require bootstrapping.
+ //
+ // Submodules:
+ //
+ // `bin.vars` -- registers some variables.
+ // `bin.config` -- loads bin.vars and sets some variables.
+ // `bin.ar.config` -- loads bin.config and registers/sets more variables.
+ // `bin.ar` -- loads bin and bin.ar.config.
+ // `bin.ld.config` -- loads bin.config and registers/sets more variables.
+ // `bin.ld` -- loads bin and bin.ld.config and registers more
+ // target types for msvc.
+ // `bin.rc.config` -- loads bin.config and registers/sets more variables.
+ // `bin.rc` -- loads bin and bin.rc.config.
+ // `bin` -- loads bin.config and registers target types and
+ // rules.
+ //
+ extern "C" LIBBUILD2_BIN_SYMEXPORT const module_functions*
+ build2_bin_load ();
+ }
+}
+
+#endif // LIBBUILD2_BIN_INIT_HXX
diff --git a/libbuild2/bin/rule.cxx b/libbuild2/bin/rule.cxx
new file mode 100644
index 0000000..8c1174a
--- /dev/null
+++ b/libbuild2/bin/rule.cxx
@@ -0,0 +1,89 @@
+// file : build2/bin/rule.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/bin/rule.hxx>
+
+#include <libbuild2/scope.hxx>
+#include <libbuild2/target.hxx>
+#include <libbuild2/algorithm.hxx>
+#include <libbuild2/diagnostics.hxx>
+
+#include <libbuild2/bin/target.hxx>
+
+using namespace std;
+
+namespace build2
+{
+ namespace bin
+ {
+ // fail_rule
+ //
+ bool fail_rule::
+ match (action a, target& t, const string&) const
+ {
+ const char* n (t.dynamic_type ().name); // Ignore derived type.
+
+ fail << diag_doing (a, t) << " target group" <<
+ info << "explicitly select " << n << "e{}, " << n << "a{}, or "
+ << n << "s{} member" << endf;
+ }
+
+ recipe fail_rule::
+ apply (action, target&) const {return empty_recipe;}
+
+ // lib_rule
+ //
+ // The whole logic is pretty much as if we had our two group members as
+ // our prerequisites.
+ //
+ lib_rule::members lib_rule::
+ build_members (const scope& rs)
+ {
+ const string& type (cast<string> (rs["bin.lib"]));
+
+ bool a (type == "static" || type == "both");
+ bool s (type == "shared" || type == "both");
+
+ if (!a && !s)
+ fail << "unknown library type: " << type <<
+ info << "'static', 'shared', or 'both' expected";
+
+ return members {a, s};
+ }
+
+ bool lib_rule::
+ match (action, target& xt, const string&) const
+ {
+ lib& t (xt.as<lib> ());
+
+ members bm (build_members (t.root_scope ()));
+ t.a = bm.a ? &search<liba> (t, t.dir, t.out, t.name) : nullptr;
+ t.s = bm.s ? &search<libs> (t, t.dir, t.out, t.name) : nullptr;
+
+ return true;
+ }
+
+ recipe lib_rule::
+ apply (action a, target& xt) const
+ {
+ lib& t (xt.as<lib> ());
+
+ //@@ outer: also prerequisites (if outer) or not?
+
+ const target* m[] = {t.a, t.s};
+ match_members (a, t, m);
+
+ return &perform;
+ }
+
+ target_state lib_rule::
+ perform (action a, const target& xt)
+ {
+ const lib& t (xt.as<lib> ());
+
+ const target* m[] = {t.a, t.s};
+ return execute_members (a, t, m);
+ }
+ }
+}
diff --git a/libbuild2/bin/rule.hxx b/libbuild2/bin/rule.hxx
new file mode 100644
index 0000000..cfd096d
--- /dev/null
+++ b/libbuild2/bin/rule.hxx
@@ -0,0 +1,65 @@
+// file : libbuild2/bin/rule.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBUILD2_BIN_RULE_HXX
+#define LIBBUILD2_BIN_RULE_HXX
+
+#include <libbuild2/types.hxx>
+#include <libbuild2/utility.hxx>
+
+#include <libbuild2/rule.hxx>
+
+#include <libbuild2/bin/export.hxx>
+
+namespace build2
+{
+ namespace bin
+ {
+ // "Fail rule" for obj{}, [h]bmi{}, and libu{} that issues diagnostics if
+ // someone tries to build any of these groups directly.
+ //
+ class fail_rule: public rule
+ {
+ public:
+ fail_rule () {}
+
+ virtual bool
+ match (action, target&, const string&) const override;
+
+ virtual recipe
+ apply (action, target&) const override;
+ };
+
+ // Pass-through to group members rule, similar to alias.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT lib_rule: public rule
+ {
+ public:
+ lib_rule () {}
+
+ virtual bool
+ match (action, target&, const string&) const override;
+
+ virtual recipe
+ apply (action, target&) const override;
+
+ static target_state
+ perform (action, const target&);
+
+ // Return library types to build according to the bin.lib value (set
+ // on project's root scope by init()).
+ //
+ struct members
+ {
+ bool a; // static
+ bool s; // shared
+ };
+
+ static members
+ build_members (const scope&);
+ };
+ }
+}
+
+#endif // LIBBUILD2_BIN_RULE_HXX
diff --git a/libbuild2/bin/target.cxx b/libbuild2/bin/target.cxx
new file mode 100644
index 0000000..dd8a947
--- /dev/null
+++ b/libbuild2/bin/target.cxx
@@ -0,0 +1,474 @@
+// file : libbuild2/bin/target.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/bin/target.hxx>
+
+#include <libbuild2/context.hxx>
+
+using namespace std;
+
+namespace build2
+{
+ namespace bin
+ {
+ const target_type objx::static_type
+ {
+ "objx",
+ &file::static_type,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ const target_type bmix::static_type
+ {
+ "bmix",
+ &file::static_type,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ const target_type hbmix::static_type
+ {
+ "hbmix",
+ &bmix::static_type,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ const target_type libx::static_type
+ {
+ "libx",
+ &mtime_target::static_type,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ const target_type libux::static_type
+ {
+ "libux",
+ &file::static_type,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ // Note that we link groups during the load phase since this is often
+ // relied upon when setting target-specific variables (e.g., we may set a
+ // common value for lib{} and then append liba/libs-specific values to
+ // it). While sure inelegant, this is MT-safe since during load we are
+ // running serial. For the members it is also safe to set the group during
+ // creation.
+
+ // obj*{} and [h]bmi*{} member factory.
+ //
+ template <typename M, typename G>
+ static target*
+ m_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
+ {
+ const G* g (ctx.targets.find<G> (dir, out, n));
+
+ M* m (new M (ctx, move (dir), move (out), move (n)));
+ m->group = g;
+
+ return m;
+ }
+
+ const target_type obje::static_type
+ {
+ "obje",
+ &objx::static_type,
+ &m_factory<obje, obj>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type bmie::static_type
+ {
+ "bmie",
+ &bmix::static_type,
+ &m_factory<bmie, bmi>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type hbmie::static_type
+ {
+ "hbmie",
+ &hbmix::static_type,
+ &m_factory<hbmie, hbmi>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type obja::static_type
+ {
+ "obja",
+ &objx::static_type,
+ &m_factory<obja, obj>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type bmia::static_type
+ {
+ "bmia",
+ &bmix::static_type,
+ &m_factory<bmia, bmi>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type hbmia::static_type
+ {
+ "hbmia",
+ &hbmix::static_type,
+ &m_factory<hbmia, hbmi>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type objs::static_type
+ {
+ "objs",
+ &objx::static_type,
+ &m_factory<objs, obj>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type bmis::static_type
+ {
+ "bmis",
+ &bmix::static_type,
+ &m_factory<bmis, bmi>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type hbmis::static_type
+ {
+ "hbmis",
+ &hbmix::static_type,
+ &m_factory<hbmis, hbmi>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type libue::static_type
+ {
+ "libue",
+ &libux::static_type,
+ &target_factory<libue>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type libua::static_type
+ {
+ "libua",
+ &libux::static_type,
+ &m_factory<libua, libul>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ const target_type libus::static_type
+ {
+ "libus",
+ &libux::static_type,
+ &m_factory<libus, libul>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &target_search, // Note: not _file(); don't look for an existing file.
+ false
+ };
+
+ // obj{}, [h]bmi{}, and libu{} group factory.
+ //
+ template <typename G, typename E, typename A, typename S>
+ static target*
+ g_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
+ {
+ // Casts are MT-aware (during serial load).
+ //
+ E* e (ctx.phase == run_phase::load
+ ? const_cast<E*> (ctx.targets.find<E> (dir, out, n))
+ : nullptr);
+ A* a (ctx.phase == run_phase::load
+ ? const_cast<A*> (ctx.targets.find<A> (dir, out, n))
+ : nullptr);
+ S* s (ctx.phase == run_phase::load
+ ? const_cast<S*> (ctx.targets.find<S> (dir, out, n))
+ : nullptr);
+
+ G* g (new G (ctx, move (dir), move (out), move (n)));
+
+ if (e != nullptr) e->group = g;
+ if (a != nullptr) a->group = g;
+ if (s != nullptr) s->group = g;
+
+ return g;
+ }
+
+ const target_type obj::static_type
+ {
+ "obj",
+ &target::static_type,
+ &g_factory<obj, obje, obja, objs>,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ const target_type bmi::static_type
+ {
+ "bmi",
+ &target::static_type,
+ &g_factory<bmi, bmie, bmia, bmis>,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ const target_type hbmi::static_type
+ {
+ "hbmi",
+ &target::static_type,
+ &g_factory<hbmi, hbmie, hbmia, hbmis>,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ // The same as g_factory() but without E.
+ //
+ static target*
+ libul_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
+ {
+ libua* a (ctx.phase == run_phase::load
+ ? const_cast<libua*> (ctx.targets.find<libua> (dir, out, n))
+ : nullptr);
+ libus* s (ctx.phase == run_phase::load
+ ? const_cast<libus*> (ctx.targets.find<libus> (dir, out, n))
+ : nullptr);
+
+ libul* g (new libul (ctx, move (dir), move (out), move (n)));
+
+ if (a != nullptr) a->group = g;
+ if (s != nullptr) s->group = g;
+
+ return g;
+ }
+
+ const target_type libul::static_type
+ {
+ "libul",
+ &libx::static_type,
+ &libul_factory,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false
+ };
+
+ // What extensions should we use? At the outset, this is platform-
+ // dependent. And if we consider cross-compilation, is it build or
+ // host-dependent? Feels like it should be host-dependent so that
+ // we can copy things between cross and native environments. So
+ // these will have to be determined based on what we are building.
+ // As if this is not complicated enough, the bin module doesn't
+ // know anything about building. So perhaps the extension should
+ // come from a variable that is set not by bin but by the module
+ // whose rule matched the target (e.g., cxx::link).
+ //
+ const target_type liba::static_type
+ {
+ "liba",
+ &file::static_type,
+ &m_factory<liba, lib>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &file_search,
+ false
+ };
+
+ const target_type libs::static_type
+ {
+ "libs",
+ &file::static_type,
+ &m_factory<libs, lib>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &file_search,
+ false
+ };
+
+ // lib
+ //
+ group_view lib::
+ group_members (action) const
+ {
+ static_assert (sizeof (lib_members) == sizeof (const target*) * 2,
+ "member layout incompatible with array");
+
+ return a != nullptr || s != nullptr
+ ? group_view {reinterpret_cast<const target* const*> (&a), 2}
+ : group_view {nullptr, 0};
+ }
+
+ static target*
+ lib_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
+ {
+ // Casts are MT-aware (during serial load).
+ //
+ liba* a (ctx.phase == run_phase::load
+ ? const_cast<liba*> (ctx.targets.find<liba> (dir, out, n))
+ : nullptr);
+ libs* s (ctx.phase == run_phase::load
+ ? const_cast<libs*> (ctx.targets.find<libs> (dir, out, n))
+ : nullptr);
+
+ lib* l (new lib (ctx, move (dir), move (out), move (n)));
+
+ if (a != nullptr) a->group = l;
+ if (s != nullptr) s->group = l;
+
+ return l;
+ }
+
+ const target_type lib::static_type
+ {
+ "lib",
+ &libx::static_type,
+ &lib_factory,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &target_search,
+ false // Note: not see-through ("alternatives" group).
+ };
+
+ // libi
+ //
+ const target_type libi::static_type
+ {
+ "libi",
+ &file::static_type,
+ &target_factory<libi>,
+ nullptr, /* fixed_extension */
+ &target_extension_var<nullptr>,
+ &target_pattern_var<nullptr>,
+ nullptr,
+ &file_search,
+ false
+ };
+
+ // def
+ //
+ extern const char def_ext[] = "def"; // VC14 rejects constexpr.
+
+ const target_type def::static_type
+ {
+ "def",
+ &file::static_type,
+ &target_factory<def>,
+ &target_extension_fix<def_ext>,
+ nullptr, /* default_extension */
+ &target_pattern_fix<def_ext>,
+ nullptr,
+ &file_search,
+ false
+ };
+ }
+}
diff --git a/libbuild2/bin/target.hxx b/libbuild2/bin/target.hxx
new file mode 100644
index 0000000..3b1708a
--- /dev/null
+++ b/libbuild2/bin/target.hxx
@@ -0,0 +1,363 @@
+// file : libbuild2/bin/target.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBUILD2_BIN_TARGET_HXX
+#define LIBBUILD2_BIN_TARGET_HXX
+
+#include <libbuild2/types.hxx>
+#include <libbuild2/utility.hxx>
+
+#include <libbuild2/target.hxx>
+
+#include <libbuild2/bin/export.hxx>
+
+namespace build2
+{
+ namespace bin
+ {
+ // The obj{} target group.
+ //
+ // Common base of all objX{} object files.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT objx: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT obje: public objx
+ {
+ public:
+ using objx::objx;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT obja: public objx
+ {
+ public:
+ using objx::objx;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT objs: public objx
+ {
+ public:
+ using objx::objx;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT obj: public target
+ {
+ public:
+ using target::target;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // Binary module interface (BMI).
+ //
+ // While currently there are only C++ modules, if things pan out, chances
+ // are we will have C (or Obj-C) modules. And in that case it is plausible
+ // we will also have some binutils to examine BMIs, similar to objdump,
+ // etc. So that's why this target type is in bin and not cxx.
+ //
+ // bmi*{} is similar to obj*{} though the semantics is a bit different:
+ // the idea is that we should try hard to re-use a single bmiX{} file for
+ // an entire "build" but if that's not possible (because the compilation
+ // options are too different), then compile a private version for
+ // ourselves (the definition of "too different" is, of course, compiler-
+ // specific).
+ //
+ // When we compile a module interface unit, we end up with bmi*{} and
+ // obj*{}. How that obj*{} is produced is compiler-dependent. While it
+ // makes sense to decouple the production of the two in order to increase
+ // parallelism, doing so will further complicate the already hairy
+ // organization. So, at least for now, we produce the two at the same time
+ // and make obj*{} an ad hoc member of bmi*{}.
+ //
+ // There are also header units for which we define a parallel hbmi*{}
+ // hierarchy. Note that hbmix{} is-a bmix{} (we think of header BMIs as a
+ // more specialized kind of BMI) so where you need to distinguish between
+ // header and module BMIs, you should check for headers first. Note also
+ // that in case of a header unit there may be no obj*{}.
+ //
+ // Common base of all bmiX{} interface files.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT bmix: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ };
+
+ // Common base of all hbmiX{} interface files.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT hbmix: public bmix
+ {
+ public:
+ using bmix::bmix;
+
+ public:
+ static const target_type static_type;
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT bmie: public bmix
+ {
+ public:
+ using bmix::bmix;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT hbmie: public hbmix
+ {
+ public:
+ using hbmix::hbmix;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT bmia: public bmix
+ {
+ public:
+ using bmix::bmix;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT hbmia: public hbmix
+ {
+ public:
+ using hbmix::hbmix;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT bmis: public bmix
+ {
+ public:
+ using bmix::bmix;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT hbmis: public hbmix
+ {
+ public:
+ using hbmix::hbmix;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT bmi: public target
+ {
+ public:
+ using target::target;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT hbmi: public target
+ {
+ public:
+ using target::target;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+
+ // Common base for lib{} and libul{} groups.
+ //
+ // We use mtime_target as a base for the "trust me it exists" functionality
+ // which we use, for example, to have installed lib{} prerequisites that
+ // are matched by the fallback file rule.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT libx: public mtime_target
+ {
+ public:
+ using mtime_target::mtime_target;
+
+ public:
+ static const target_type static_type;
+ };
+
+ // The libue{} target, libul{} group and libua{} and libus{} members
+ // (utility library).
+ //
+ // Utility libraries are static libraries that differ based on the kind of
+ // object files they contains. Note that the libul{} group is more like
+ // obj{} rather than lib{} in that one does not build the group directly
+ // rather picking a suitable member.
+ //
+ // libul{} is a "library utility library" in that the choice of members is
+ // libua{} or libus{}, even when linking an executable (normally a unit
+ // test).
+ //
+ // Note that there is no "general utility library" with all three types of
+ // members (that would cause member uplink ambiguity). If you need to
+ // build both a library from libua{}/libus{} and an executable from
+ // libue{} then you will need to arrange this explicitly, for example:
+ //
+ // exe{foo}: libue{foo}
+ // lib{foo}: libul{foo}
+ //
+ // {libue libul}{foo}: cxx{*}
+ //
+ // Common base of all libuX{} static libraries.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT libux: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT libue: public libux
+ {
+ public:
+ using libux::libux;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT libua: public libux
+ {
+ public:
+ using libux::libux;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT libus: public libux
+ {
+ public:
+ using libux::libux;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT libul: public libx
+ {
+ public:
+ using libx::libx;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // The lib{} target group.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT liba: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT libs: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+
+ virtual const target_type&
+ dynamic_type () const override {return static_type;}
+ };
+
+ // Standard layout type compatible with group_view's const target*[2].
+ //
+ struct lib_members
+ {
+ const liba* a = nullptr;
+ const libs* s = nullptr;
+ };
+
+ class LIBBUILD2_BIN_SYMEXPORT lib: public libx, public lib_members
+ {
+ public:
+ using libx::libx;
+
+ virtual group_view
+ group_members (action) const override;
+
+ public:
+ static const target_type static_type;
+
+ virtual const target_type&
+ dynamic_type () const override {return static_type;}
+ };
+
+ // Windows import library.
+ //
+ class LIBBUILD2_BIN_SYMEXPORT libi: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+
+ // Windows module definition (.def).
+ //
+ class LIBBUILD2_BIN_SYMEXPORT def: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ static const target_type static_type;
+ virtual const target_type& dynamic_type () const {return static_type;}
+ };
+ }
+}
+
+#endif // LIBBUILD2_BIN_TARGET_HXX
diff --git a/libbuild2/buildfile b/libbuild2/buildfile
index 3ad2f9b..57f4895 100644
--- a/libbuild2/buildfile
+++ b/libbuild2/buildfile
@@ -5,7 +5,7 @@
# NOTE: remember to update bundled_modules in libbuild2/modules.cxx if adding
# a new module.
#
-./: lib{build2} bash/ in/ version/
+./: lib{build2} bash/ bin/ in/ version/
import int_libs = libbutl%lib{butl}
diff --git a/libbuild2/export.hxx b/libbuild2/export.hxx
index 514c845..fa44df9 100644
--- a/libbuild2/export.hxx
+++ b/libbuild2/export.hxx
@@ -1,3 +1,7 @@
+// file : libbuild2/export.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
#pragma once
// Normally we don't export class templates (but do complete specializations),
diff --git a/libbuild2/in/export.hxx b/libbuild2/in/export.hxx
index 47909e7..776f647 100644
--- a/libbuild2/in/export.hxx
+++ b/libbuild2/in/export.hxx
@@ -1,3 +1,7 @@
+// file : libbuild2/in/export.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
#pragma once
// Normally we don't export class templates (but do complete specializations),
diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx
index 9f951c3..bb7c61d 100644
--- a/libbuild2/module.cxx
+++ b/libbuild2/module.cxx
@@ -38,6 +38,7 @@ namespace build2
//
static const char* bundled_modules[] = {
"bash",
+ "bin",
"in",
"version"
};
diff --git a/libbuild2/version/export.hxx b/libbuild2/version/export.hxx
index c76cd8a..d6bb001 100644
--- a/libbuild2/version/export.hxx
+++ b/libbuild2/version/export.hxx
@@ -1,3 +1,7 @@
+// file : libbuild2/version/export.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
#pragma once
// Normally we don't export class templates (but do complete specializations),