From ea24f530048cbce0c5335ca3fd3632c8ce34315a Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 24 Aug 2019 16:37:29 +0300 Subject: Move bin build system module to separate library --- bootstrap-mingw.bat | 2 +- bootstrap-msvc.bat | 2 +- bootstrap.gmake | 2 +- bootstrap.sh | 2 +- build2/b.cxx | 13 +- build2/bin/guess.cxx | 464 --------------------- build2/bin/guess.hxx | 108 ----- build2/bin/init.cxx | 940 ----------------------------------------- build2/bin/init.hxx | 100 ----- build2/bin/rule.cxx | 89 ---- build2/bin/rule.hxx | 63 --- build2/bin/target.cxx | 474 --------------------- build2/bin/target.hxx | 353 ---------------- build2/buildfile | 2 +- build2/cc/common.hxx | 2 +- build2/cc/compile-rule.cxx | 2 +- build2/cc/gcc.cxx | 2 +- build2/cc/install-rule.cxx | 2 +- build2/cc/link-rule.cxx | 2 +- build2/cc/module.cxx | 2 +- build2/cc/msvc.cxx | 2 +- build2/cc/pkgconfig.cxx | 2 +- build2/cc/utility.cxx | 4 +- build2/cc/utility.hxx | 2 +- build2/cc/windows-rpath.cxx | 2 +- libbuild2/bash/export.hxx | 4 + libbuild2/bin/buildfile | 69 ++++ libbuild2/bin/export.hxx | 38 ++ libbuild2/bin/guess.cxx | 464 +++++++++++++++++++++ libbuild2/bin/guess.hxx | 108 +++++ libbuild2/bin/init.cxx | 963 +++++++++++++++++++++++++++++++++++++++++++ libbuild2/bin/init.hxx | 40 ++ libbuild2/bin/rule.cxx | 89 ++++ libbuild2/bin/rule.hxx | 65 +++ libbuild2/bin/target.cxx | 474 +++++++++++++++++++++ libbuild2/bin/target.hxx | 363 ++++++++++++++++ libbuild2/buildfile | 2 +- libbuild2/export.hxx | 4 + libbuild2/in/export.hxx | 4 + libbuild2/module.cxx | 1 + libbuild2/version/export.hxx | 4 + tests/libbuild2/buildfile | 2 +- tests/libbuild2/driver.cxx | 2 + 43 files changed, 2713 insertions(+), 2621 deletions(-) delete mode 100644 build2/bin/guess.cxx delete mode 100644 build2/bin/guess.hxx delete mode 100644 build2/bin/init.cxx delete mode 100644 build2/bin/init.hxx delete mode 100644 build2/bin/rule.cxx delete mode 100644 build2/bin/rule.hxx delete mode 100644 build2/bin/target.cxx delete mode 100644 build2/bin/target.hxx create mode 100644 libbuild2/bin/buildfile create mode 100644 libbuild2/bin/export.hxx create mode 100644 libbuild2/bin/guess.cxx create mode 100644 libbuild2/bin/guess.hxx create mode 100644 libbuild2/bin/init.cxx create mode 100644 libbuild2/bin/init.hxx create mode 100644 libbuild2/bin/rule.cxx create mode 100644 libbuild2/bin/rule.hxx create mode 100644 libbuild2/bin/target.cxx create mode 100644 libbuild2/bin/target.hxx diff --git a/bootstrap-mingw.bat b/bootstrap-mingw.bat index 46eb0a4..7fb8ec3 100644 --- a/bootstrap-mingw.bat +++ b/bootstrap-mingw.bat @@ -61,7 +61,6 @@ if "_%libbutl%_" == "__" ( rem All the source directories. rem set "src=build2" -set "src=%src% build2\bin" set "src=%src% build2\c" set "src=%src% build2\cc" set "src=%src% build2\cxx" @@ -72,6 +71,7 @@ set "src=%src% libbuild2\dist" set "src=%src% libbuild2\test" set "src=%src% libbuild2\test\script" set "src=%src% libbuild2\install" +set "src=%src% libbuild2\bin" set "src=%src% libbuild2\version" set "src=%src% libbuild2\in" diff --git a/bootstrap-msvc.bat b/bootstrap-msvc.bat index 9fed752..4341098 100644 --- a/bootstrap-msvc.bat +++ b/bootstrap-msvc.bat @@ -92,7 +92,6 @@ if "_%libbutl%_" == "__" ( rem All the source directories. rem set "src=build2" -set "src=%src% build2\bin" set "src=%src% build2\c" set "src=%src% build2\cc" set "src=%src% build2\cxx" @@ -103,6 +102,7 @@ set "src=%src% libbuild2\dist" set "src=%src% libbuild2\test" set "src=%src% libbuild2\test\script" set "src=%src% libbuild2\install" +set "src=%src% libbuild2\bin" set "src=%src% libbuild2\version" set "src=%src% libbuild2\in" diff --git a/bootstrap.gmake b/bootstrap.gmake index 6a2ca69..c27a965 100644 --- a/bootstrap.gmake +++ b/bootstrap.gmake @@ -130,7 +130,6 @@ endif # Note: list nested subdirectories first (used in clean). # build2_sub := \ -bin \ c \ cc \ cxx @@ -141,6 +140,7 @@ dist \ test/script \ test \ install \ +bin \ version \ in diff --git a/bootstrap.sh b/bootstrap.sh index 6eb570f..3614e93 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -117,7 +117,6 @@ if test -z "$libbutl"; then fi src="build2/*.cxx" -src="$src build2/bin/*.cxx" src="$src build2/c/*.cxx" src="$src build2/cc/*.cxx" src="$src build2/cxx/*.cxx" @@ -128,6 +127,7 @@ src="$src libbuild2/dist/*.cxx" src="$src libbuild2/test/*.cxx" src="$src libbuild2/test/script/*.cxx" src="$src libbuild2/install/*.cxx" +src="$src libbuild2/bin/*.cxx" src="$src libbuild2/version/*.cxx" src="$src libbuild2/in/*.cxx" diff --git a/build2/b.cxx b/build2/b.cxx index 27a9cd0..78f6248 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -53,6 +53,7 @@ #include #include +#include #include #ifndef BUILD2_BOOTSTRAP @@ -61,7 +62,6 @@ # include #endif -#include #include #include #include @@ -519,19 +519,10 @@ main (int argc, char* argv[]) load (&test::build2_test_load); load (&install::build2_install_load); + load (&bin::build2_bin_load); load (&version::build2_version_load); load (&in::build2_in_load); - TMP_LOAD (bin_vars, "bin.vars", bin::vars_init); - TMP_LOAD (bin_config, "bin.config", bin::config_init); - TMP_LOAD (bin, "bin", bin::init); - TMP_LOAD (bin_ar_config, "bin.ar.config", bin::ar_config_init); - TMP_LOAD (bin_ar, "bin.ar", bin::ar_init); - TMP_LOAD (bin_ld_config, "bin.ld.config", bin::ld_config_init); - TMP_LOAD (bin_ld, "bin.ld", bin::ld_init); - TMP_LOAD (bin_rc_config, "bin.rc.config", bin::rc_config_init); - TMP_LOAD (bin_rc, "bin.rc", bin::rc_init); - TMP_LOAD (cc_core_vars, "cc.core.vars", cc::core_vars_init); TMP_LOAD (cc_core_guess, "cc.core.guess", cc::core_guess_init); TMP_LOAD (cc_core_config, "cc.core.config", cc::core_config_init); diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx deleted file mode 100644 index d1b1545..0000000 --- a/build2/bin/guess.cxx +++ /dev/null @@ -1,464 +0,0 @@ -// file : build2/bin/guess.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include - -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 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 (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 (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 (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 (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 (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 (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 (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 (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 (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/build2/bin/guess.hxx b/build2/bin/guess.hxx deleted file mode 100644 index a3b2b34..0000000 --- a/build2/bin/guess.hxx +++ /dev/null @@ -1,108 +0,0 @@ -// file : build2/bin/guess.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_BIN_GUESS_HXX -#define BUILD2_BIN_GUESS_HXX - -#include -#include - -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 // BUILD2_BIN_GUESS_HXX diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx deleted file mode 100644 index 54bd84a..0000000 --- a/build2/bin/init.cxx +++ /dev/null @@ -1,940 +0,0 @@ -// file : build2/bin/init.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include - -#include -#include -#include - -#include - -#include - -#include -#include - -#include -#include -#include - -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&, - 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 ("config.bin.target", true); - vp.insert ("config.bin.pattern", true); - - // Library types to build. - // - vp.insert ("config.bin.lib", true); - - // Library types to use (in priority order). - // - vp.insert ("config.bin.exe.lib", true); - vp.insert ("config.bin.liba.lib", true); - vp.insert ("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 ("config.bin.rpath", true); - vp.insert ("config.bin.rpath.auto", true); - - vp.insert ("config.bin.rpath_link", true); - vp.insert ("config.bin.rpath_link.auto", true); - - vp.insert ("config.bin.prefix", true); - vp.insert ("config.bin.suffix", true); - vp.insert ("config.bin.lib.prefix", true); - vp.insert ("config.bin.lib.suffix", true); - vp.insert ("config.bin.exe.prefix", true); - vp.insert ("config.bin.exe.suffix", true); - - vp.insert ("bin.lib"); - - vp.insert ("bin.exe.lib"); - vp.insert ("bin.liba.lib"); - vp.insert ("bin.libs.lib"); - - vp.insert ("bin.rpath"); - vp.insert ("bin.rpath.auto"); - - vp.insert ("bin.rpath_link"); - vp.insert ("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 ("bin.whole", false, variable_visibility::target); - - vp.insert ("bin.exe.prefix"); - vp.insert ("bin.exe.suffix"); - vp.insert ("bin.lib.prefix"); - vp.insert ("bin.lib.suffix"); - - vp.insert ("bin.lib.load_suffix", - variable_visibility::project); - - vp.insert> ("bin.lib.version", - variable_visibility::project); - - return true; - } - - bool - config_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr&, - 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 (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 ( - optional (rs, "config.bin.rpath")); - - rs.assign ("bin.rpath_link") += cast_null ( - 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 (l) - : true; - - rs.assign ("bin.rpath_link.auto") = - (l = omitted (rs, "config.bin.rpath_link.auto").first) - ? cast (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 (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 (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 ("bin.target.cpu") = t.cpu; - rs.assign ("bin.target.vendor") = t.vendor; - rs.assign ("bin.target.system") = t.system; - rs.assign ("bin.target.version") = t.version; - rs.assign ("bin.target.class") = t.class_; - - rs.assign ("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 (l)); - - if (s.empty () || - (!path::traits_type::is_separator (s.back ()) && - s.find ('*') == string::npos)) - { - fail << "missing '*' in binutils pattern '" << s << "'"; - } - - rs.assign ("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 (rs["bin.target"]); - - if (auto l = rs["bin.pattern"]) - dr << '\n' - << " pattern " << cast (l); - } - } - - return true; - } - - bool - init (scope& rs, - scope& bs, - const location& loc, - unique_ptr&, - bool first, - bool, - const variable_map& hints) - { - tracer trace ("bin::init"); - l5 ([&]{trace << "for " << bs;}); - - // Load bin.config. - // - if (!cast_false (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 (rs["bin.target.class"])); - - // Register target types and configure their default "installability". - // - bool install_loaded (cast_false (rs["install.loaded"])); - { - using namespace install; - - if (first) - { - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - - rs.insert_target_type (); - rs.insert_target_type (); - rs.insert_target_type (); - - // 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 (); - } - - // Note: libu*{} members are not installable. - // - if (install_loaded) - { - install_path (bs, dir_path ("lib")); // Install in install.lib. - install_mode (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 (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 (); - - if (install_loaded) - { - install_path (bs, dir_path ("lib")); - install_mode (bs, "644"); - } - } - } - - // Register rules. - // - { - auto& r (bs.rules); - - r.insert (perform_update_id, "bin.obj", fail_); - r.insert (perform_clean_id, "bin.obj", fail_); - - r.insert (perform_update_id, "bin.bmi", fail_); - r.insert (perform_clean_id, "bin.bmi", fail_); - - r.insert (perform_update_id, "bin.hbmi", fail_); - r.insert (perform_clean_id, "bin.hbmi", fail_); - - r.insert (perform_update_id, "bin.libul", fail_); - r.insert (perform_clean_id, "bin.libul", fail_); - - // Similar to alias. - // - - //@@ outer - r.insert (perform_id, 0, "bin.lib", lib_); - r.insert (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 (perform_install_id, "bin.lib", gr); - r.insert (perform_uninstall_id, "bin.lib", gr); - } - - if (const test::module* m = rs.lookup_module ("test")) - { - r.insert (perform_test_id, "bin.lib", m->group_rule ()); - } - } - - return true; - } - - bool - ar_config_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr&, - 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 (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 ("bin.ar.path"); - v.insert ("bin.ranlib.path"); - - v.insert ("config.bin.ar", true); - v.insert ("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 (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 (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 (ap.first)); - const path* ranlib (cast_null (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 ("bin.ar.path") = move (ari.ar_path); - rs.assign ("bin.ar.id") = move (ari.ar_id); - rs.assign ("bin.ar.signature") = move (ari.ar_signature); - rs.assign ("bin.ar.checksum") = move (ari.ar_checksum); - - { - semantic_version& v (ari.ar_version); - - rs.assign ("bin.ar.version") = v.string (); - rs.assign ("bin.ar.version.major") = v.major; - rs.assign ("bin.ar.version.minor") = v.minor; - rs.assign ("bin.ar.version.patch") = v.patch; - rs.assign ("bin.ar.version.build") = move (v.build); - } - - if (ranlib != nullptr) - { - rs.assign ("bin.ranlib.path") = move (ari.ranlib_path); - rs.assign ("bin.ranlib.id") = move (ari.ranlib_id); - rs.assign ("bin.ranlib.signature") = - move (ari.ranlib_signature); - rs.assign ("bin.ranlib.checksum") = - move (ari.ranlib_checksum); - } - } - - return true; - } - - bool - ar_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr&, - 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 (bs["bin.loaded"])) - load_module (rs, bs, "bin", loc, false, hints); - - if (!cast_false (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&, - 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 (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 ("bin.ld.path"); - v.insert ("config.bin.ld", true); - } - - // Configure. - // - if (first) - { - // config.bin.ld - // - // Use the target to decide on the default ld name. - // - const string& tsys (cast (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 (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 (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 ("bin.ld.path") = move (ldi.path); - rs.assign ("bin.ld.id") = move (ldi.id); - rs.assign ("bin.ld.signature") = move (ldi.signature); - rs.assign ("bin.ld.checksum") = move (ldi.checksum); - } - - return true; - } - - bool - ld_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr&, - 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 (bs["bin.loaded"])) - load_module (rs, bs, "bin", loc, false, hints); - - if (!cast_false (bs["bin.ld.config.loaded"])) - load_module (rs, bs, "bin.ld.config", loc, false, hints); - - const string& lid (cast (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 ("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&, - 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 (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 ("bin.rc.path"); - v.insert ("config.bin.rc", true); - } - - // Configure. - // - if (first) - { - // config.bin.rc - // - // Use the target to decide on the default rc name. - // - const string& tsys (cast (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 (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 (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 ("bin.rc.path") = move (rci.path); - rs.assign ("bin.rc.id") = move (rci.id); - rs.assign ("bin.rc.signature") = move (rci.signature); - rs.assign ("bin.rc.checksum") = move (rci.checksum); - } - - return true; - } - - bool - rc_init (scope& rs, - scope& bs, - const location& loc, - unique_ptr&, - 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 (bs["bin.loaded"])) - load_module (rs, bs, "bin", loc, false, hints); - - if (!cast_false (bs["bin.rc.config.loaded"])) - load_module (rs, bs, "bin.rc.config", loc, false, hints); - - return true; - } - } -} diff --git a/build2/bin/init.hxx b/build2/bin/init.hxx deleted file mode 100644 index 989dcaf..0000000 --- a/build2/bin/init.hxx +++ /dev/null @@ -1,100 +0,0 @@ -// file : build2/bin/init.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_BIN_INIT_HXX -#define BUILD2_BIN_INIT_HXX - -#include -#include - -#include - -namespace build2 -{ - namespace bin - { - bool - vars_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - config_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - ar_config_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - ar_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - ld_config_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - ld_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - rc_config_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - - bool - rc_init (scope&, - scope&, - const location&, - unique_ptr&, - bool, - bool, - const variable_map&); - } -} - -#endif // BUILD2_BIN_INIT_HXX diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx deleted file mode 100644 index 42ba86a..0000000 --- a/build2/bin/rule.cxx +++ /dev/null @@ -1,89 +0,0 @@ -// file : build2/bin/rule.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include -#include -#include - -#include - -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 (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 ()); - - members bm (build_members (t.root_scope ())); - t.a = bm.a ? &search (t, t.dir, t.out, t.name) : nullptr; - t.s = bm.s ? &search (t, t.dir, t.out, t.name) : nullptr; - - return true; - } - - recipe lib_rule:: - apply (action a, target& xt) const - { - lib& t (xt.as ()); - - //@@ 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 ()); - - const target* m[] = {t.a, t.s}; - return execute_members (a, t, m); - } - } -} diff --git a/build2/bin/rule.hxx b/build2/bin/rule.hxx deleted file mode 100644 index 4230933..0000000 --- a/build2/bin/rule.hxx +++ /dev/null @@ -1,63 +0,0 @@ -// file : build2/bin/rule.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_BIN_RULE_HXX -#define BUILD2_BIN_RULE_HXX - -#include -#include - -#include - -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 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 // BUILD2_BIN_RULE_HXX diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx deleted file mode 100644 index 9074317..0000000 --- a/build2/bin/target.cxx +++ /dev/null @@ -1,474 +0,0 @@ -// file : build2/bin/target.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include - -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 - static target* - m_factory (context& ctx, - const target_type&, dir_path dir, dir_path out, string n) - { - const G* g (ctx.targets.find (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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - nullptr, - &target_search, // Note: not _file(); don't look for an existing file. - false - }; - - // obj{}, [h]bmi{}, and libu{} group factory. - // - template - 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 (ctx.targets.find (dir, out, n)) - : nullptr); - A* a (ctx.phase == run_phase::load - ? const_cast (ctx.targets.find (dir, out, n)) - : nullptr); - S* s (ctx.phase == run_phase::load - ? const_cast (ctx.targets.find (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, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, - false - }; - - const target_type bmi::static_type - { - "bmi", - &target::static_type, - &g_factory, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, - false - }; - - const target_type hbmi::static_type - { - "hbmi", - &target::static_type, - &g_factory, - 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 (ctx.targets.find (dir, out, n)) - : nullptr); - libus* s (ctx.phase == run_phase::load - ? const_cast (ctx.targets.find (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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - nullptr, - &file_search, - false - }; - - const target_type libs::static_type - { - "libs", - &file::static_type, - &m_factory, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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 (&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 (ctx.targets.find (dir, out, n)) - : nullptr); - libs* s (ctx.phase == run_phase::load - ? const_cast (ctx.targets.find (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, - nullptr, /* fixed_extension */ - &target_extension_var, - &target_pattern_var, - 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, - &target_extension_fix, - nullptr, /* default_extension */ - &target_pattern_fix, - nullptr, - &file_search, - false - }; - } -} diff --git a/build2/bin/target.hxx b/build2/bin/target.hxx deleted file mode 100644 index 45229ce..0000000 --- a/build2/bin/target.hxx +++ /dev/null @@ -1,353 +0,0 @@ -// file : build2/bin/target.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_BIN_TARGET_HXX -#define BUILD2_BIN_TARGET_HXX - -#include -#include - -#include - -namespace build2 -{ - namespace bin - { - // The obj{} target group. - // - class objx: public file // Common base of all objX{} object files. - { - public: - using file::file; - - public: - static const target_type static_type; - }; - - class 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 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 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 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*{}. - // - class bmix: public file // Common base of all bmiX{} interface files. - { - public: - using file::file; - - public: - static const target_type static_type; - }; - - class hbmix: public bmix // Common base of all hbmiX{} interface files. - { - public: - using bmix::bmix; - - public: - static const target_type static_type; - }; - - class 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 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 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 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 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 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 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 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 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{*} - // - class libux: public file // Common base of all libuX{} static libraries. - { - public: - using file::file; - - public: - static const target_type static_type; - }; - - class 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 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 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 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 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 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 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 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 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 // BUILD2_BIN_TARGET_HXX diff --git a/build2/buildfile b/build2/buildfile index 196c485..1f956a3 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -8,7 +8,7 @@ import libs += libpkgconf%lib{pkgconf} include ../libbuild2/ libs += ../libbuild2/lib{build2} -for m: bash in version +for m: bash bin in version { include ../libbuild2/$m/ libs += ../libbuild2/$m/lib{build2-$m} diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx index b24eb7d..527c31a 100644 --- a/build2/cc/common.hxx +++ b/build2/cc/common.hxx @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include // compiler_id diff --git a/build2/cc/compile-rule.cxx b/build2/cc/compile-rule.cxx index fa43533..5d4c838 100644 --- a/build2/cc/compile-rule.cxx +++ b/build2/cc/compile-rule.cxx @@ -18,7 +18,7 @@ #include // create_project() -#include +#include #include #include // h diff --git a/build2/cc/gcc.cxx b/build2/cc/gcc.cxx index e6d7101..a979b2d 100644 --- a/build2/cc/gcc.cxx +++ b/build2/cc/gcc.cxx @@ -8,7 +8,7 @@ #include #include -#include +#include #include diff --git a/build2/cc/install-rule.cxx b/build2/cc/install-rule.cxx index 9e52501..876e780 100644 --- a/build2/cc/install-rule.cxx +++ b/build2/cc/install-rule.cxx @@ -6,7 +6,7 @@ #include -#include +#include #include #include // match() diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx index ce5fce9..adf76d1 100644 --- a/build2/cc/link-rule.cxx +++ b/build2/cc/link-rule.cxx @@ -18,7 +18,7 @@ #include #include -#include +#include #include // c, pc* #include diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index bd853cc..478cabe 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -9,7 +9,7 @@ #include #include -#include +#include #include // pc* diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx index 7d8c3f5..886975a 100644 --- a/build2/cc/msvc.cxx +++ b/build2/cc/msvc.cxx @@ -11,7 +11,7 @@ #include #include -#include +#include #include diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx index 3b4c711..0ebf818 100644 --- a/build2/cc/pkgconfig.cxx +++ b/build2/cc/pkgconfig.cxx @@ -19,7 +19,7 @@ #include -#include +#include #include #include // pc diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx index e9d4ce3..f17d1b0 100644 --- a/build2/cc/utility.cxx +++ b/build2/cc/utility.cxx @@ -8,8 +8,8 @@ #include #include // search() -#include -#include +#include +#include using namespace std; diff --git a/build2/cc/utility.hxx b/build2/cc/utility.hxx index 6222b5f..002dea7 100644 --- a/build2/cc/utility.hxx +++ b/build2/cc/utility.hxx @@ -9,7 +9,7 @@ #include #include -#include +#include #include diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx index 4478f7d..c4ef358 100644 --- a/build2/cc/windows-rpath.cxx +++ b/build2/cc/windows-rpath.cxx @@ -11,7 +11,7 @@ #include #include -#include +#include #include 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 + +#include + +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 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 (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 (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 (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 (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 (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 (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 (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 (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 (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 +#include + +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 + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +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&, + 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 ("config.bin.target", true); + vp.insert ("config.bin.pattern", true); + + // Library types to build. + // + vp.insert ("config.bin.lib", true); + + // Library types to use (in priority order). + // + vp.insert ("config.bin.exe.lib", true); + vp.insert ("config.bin.liba.lib", true); + vp.insert ("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 ("config.bin.rpath", true); + vp.insert ("config.bin.rpath.auto", true); + + vp.insert ("config.bin.rpath_link", true); + vp.insert ("config.bin.rpath_link.auto", true); + + vp.insert ("config.bin.prefix", true); + vp.insert ("config.bin.suffix", true); + vp.insert ("config.bin.lib.prefix", true); + vp.insert ("config.bin.lib.suffix", true); + vp.insert ("config.bin.exe.prefix", true); + vp.insert ("config.bin.exe.suffix", true); + + vp.insert ("bin.lib"); + + vp.insert ("bin.exe.lib"); + vp.insert ("bin.liba.lib"); + vp.insert ("bin.libs.lib"); + + vp.insert ("bin.rpath"); + vp.insert ("bin.rpath.auto"); + + vp.insert ("bin.rpath_link"); + vp.insert ("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 ("bin.whole", false, variable_visibility::target); + + vp.insert ("bin.exe.prefix"); + vp.insert ("bin.exe.suffix"); + vp.insert ("bin.lib.prefix"); + vp.insert ("bin.lib.suffix"); + + vp.insert ("bin.lib.load_suffix", + variable_visibility::project); + + vp.insert> ("bin.lib.version", + variable_visibility::project); + + return true; + } + + bool + config_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + 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 (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 ( + optional (rs, "config.bin.rpath")); + + rs.assign ("bin.rpath_link") += cast_null ( + 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 (l) + : true; + + rs.assign ("bin.rpath_link.auto") = + (l = omitted (rs, "config.bin.rpath_link.auto").first) + ? cast (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 (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 (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 ("bin.target.cpu") = t.cpu; + rs.assign ("bin.target.vendor") = t.vendor; + rs.assign ("bin.target.system") = t.system; + rs.assign ("bin.target.version") = t.version; + rs.assign ("bin.target.class") = t.class_; + + rs.assign ("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 (l)); + + if (s.empty () || + (!path::traits_type::is_separator (s.back ()) && + s.find ('*') == string::npos)) + { + fail << "missing '*' in binutils pattern '" << s << "'"; + } + + rs.assign ("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 (rs["bin.target"]); + + if (auto l = rs["bin.pattern"]) + dr << '\n' + << " pattern " << cast (l); + } + } + + return true; + } + + bool + init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + bool first, + bool, + const variable_map& hints) + { + tracer trace ("bin::init"); + l5 ([&]{trace << "for " << bs;}); + + // Load bin.config. + // + if (!cast_false (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 (rs["bin.target.class"])); + + // Register target types and configure their default "installability". + // + bool install_loaded (cast_false (rs["install.loaded"])); + { + using namespace install; + + if (first) + { + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + + rs.insert_target_type (); + rs.insert_target_type (); + rs.insert_target_type (); + + // 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 (); + } + + // Note: libu*{} members are not installable. + // + if (install_loaded) + { + install_path (bs, dir_path ("lib")); // Install in install.lib. + install_mode (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 (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 (); + + if (install_loaded) + { + install_path (bs, dir_path ("lib")); + install_mode (bs, "644"); + } + } + } + + // Register rules. + // + { + auto& r (bs.rules); + + r.insert (perform_update_id, "bin.obj", fail_); + r.insert (perform_clean_id, "bin.obj", fail_); + + r.insert (perform_update_id, "bin.bmi", fail_); + r.insert (perform_clean_id, "bin.bmi", fail_); + + r.insert (perform_update_id, "bin.hbmi", fail_); + r.insert (perform_clean_id, "bin.hbmi", fail_); + + r.insert (perform_update_id, "bin.libul", fail_); + r.insert (perform_clean_id, "bin.libul", fail_); + + // Similar to alias. + // + + //@@ outer + r.insert (perform_id, 0, "bin.lib", lib_); + r.insert (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 (perform_install_id, "bin.lib", gr); + r.insert (perform_uninstall_id, "bin.lib", gr); + } + + if (const test::module* m = rs.lookup_module ("test")) + { + r.insert (perform_test_id, "bin.lib", m->group_rule ()); + } + } + + return true; + } + + bool + ar_config_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + 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 (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 ("bin.ar.path"); + v.insert ("bin.ranlib.path"); + + v.insert ("config.bin.ar", true); + v.insert ("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 (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 (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 (ap.first)); + const path* ranlib (cast_null (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 ("bin.ar.path") = move (ari.ar_path); + rs.assign ("bin.ar.id") = move (ari.ar_id); + rs.assign ("bin.ar.signature") = move (ari.ar_signature); + rs.assign ("bin.ar.checksum") = move (ari.ar_checksum); + + { + semantic_version& v (ari.ar_version); + + rs.assign ("bin.ar.version") = v.string (); + rs.assign ("bin.ar.version.major") = v.major; + rs.assign ("bin.ar.version.minor") = v.minor; + rs.assign ("bin.ar.version.patch") = v.patch; + rs.assign ("bin.ar.version.build") = move (v.build); + } + + if (ranlib != nullptr) + { + rs.assign ("bin.ranlib.path") = move (ari.ranlib_path); + rs.assign ("bin.ranlib.id") = move (ari.ranlib_id); + rs.assign ("bin.ranlib.signature") = + move (ari.ranlib_signature); + rs.assign ("bin.ranlib.checksum") = + move (ari.ranlib_checksum); + } + } + + return true; + } + + bool + ar_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + 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 (bs["bin.loaded"])) + load_module (rs, bs, "bin", loc, false, hints); + + if (!cast_false (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&, + 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 (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 ("bin.ld.path"); + v.insert ("config.bin.ld", true); + } + + // Configure. + // + if (first) + { + // config.bin.ld + // + // Use the target to decide on the default ld name. + // + const string& tsys (cast (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 (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 (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 ("bin.ld.path") = move (ldi.path); + rs.assign ("bin.ld.id") = move (ldi.id); + rs.assign ("bin.ld.signature") = move (ldi.signature); + rs.assign ("bin.ld.checksum") = move (ldi.checksum); + } + + return true; + } + + bool + ld_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + 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 (bs["bin.loaded"])) + load_module (rs, bs, "bin", loc, false, hints); + + if (!cast_false (bs["bin.ld.config.loaded"])) + load_module (rs, bs, "bin.ld.config", loc, false, hints); + + const string& lid (cast (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 ("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&, + 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 (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 ("bin.rc.path"); + v.insert ("config.bin.rc", true); + } + + // Configure. + // + if (first) + { + // config.bin.rc + // + // Use the target to decide on the default rc name. + // + const string& tsys (cast (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 (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 (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 ("bin.rc.path") = move (rci.path); + rs.assign ("bin.rc.id") = move (rci.id); + rs.assign ("bin.rc.signature") = move (rci.signature); + rs.assign ("bin.rc.checksum") = move (rci.checksum); + } + + return true; + } + + bool + rc_init (scope& rs, + scope& bs, + const location& loc, + unique_ptr&, + 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 (bs["bin.loaded"])) + load_module (rs, bs, "bin", loc, false, hints); + + if (!cast_false (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 +#include + +#include + +#include + +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 + +#include +#include +#include +#include + +#include + +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 (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 ()); + + members bm (build_members (t.root_scope ())); + t.a = bm.a ? &search (t, t.dir, t.out, t.name) : nullptr; + t.s = bm.s ? &search (t, t.dir, t.out, t.name) : nullptr; + + return true; + } + + recipe lib_rule:: + apply (action a, target& xt) const + { + lib& t (xt.as ()); + + //@@ 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 ()); + + 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 +#include + +#include + +#include + +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 + +#include + +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 + static target* + m_factory (context& ctx, + const target_type&, dir_path dir, dir_path out, string n) + { + const G* g (ctx.targets.find (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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &target_search, // Note: not _file(); don't look for an existing file. + false + }; + + // obj{}, [h]bmi{}, and libu{} group factory. + // + template + 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 (ctx.targets.find (dir, out, n)) + : nullptr); + A* a (ctx.phase == run_phase::load + ? const_cast (ctx.targets.find (dir, out, n)) + : nullptr); + S* s (ctx.phase == run_phase::load + ? const_cast (ctx.targets.find (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, + nullptr, + nullptr, + nullptr, + nullptr, + &target_search, + false + }; + + const target_type bmi::static_type + { + "bmi", + &target::static_type, + &g_factory, + nullptr, + nullptr, + nullptr, + nullptr, + &target_search, + false + }; + + const target_type hbmi::static_type + { + "hbmi", + &target::static_type, + &g_factory, + 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 (ctx.targets.find (dir, out, n)) + : nullptr); + libus* s (ctx.phase == run_phase::load + ? const_cast (ctx.targets.find (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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + nullptr, + &file_search, + false + }; + + const target_type libs::static_type + { + "libs", + &file::static_type, + &m_factory, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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 (&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 (ctx.targets.find (dir, out, n)) + : nullptr); + libs* s (ctx.phase == run_phase::load + ? const_cast (ctx.targets.find (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, + nullptr, /* fixed_extension */ + &target_extension_var, + &target_pattern_var, + 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, + &target_extension_fix, + nullptr, /* default_extension */ + &target_pattern_fix, + 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 +#include + +#include + +#include + +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), diff --git a/tests/libbuild2/buildfile b/tests/libbuild2/buildfile index bd22b4c..beb82b2 100644 --- a/tests/libbuild2/buildfile +++ b/tests/libbuild2/buildfile @@ -4,7 +4,7 @@ import libs = build2%lib{build2} -for m: bash in version +for m: bash bin in version import libs += build2%lib{build2-$m} exe{driver}: {hxx cxx}{*} $libs testscript diff --git a/tests/libbuild2/driver.cxx b/tests/libbuild2/driver.cxx index a70e707..93c145a 100644 --- a/tests/libbuild2/driver.cxx +++ b/tests/libbuild2/driver.cxx @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -23,6 +24,7 @@ main (int, char* argv[]) init (nullptr, argv[0]); bash::build2_bash_load (); + bin::build2_bin_load (); in::build2_in_load (); version::build2_version_load (); -- cgit v1.1