From fac576a331d6587e4343d09d6caf959d9a776118 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 11 Jul 2016 05:56:17 +0200 Subject: Add bin.ld sub-module --- build2/b.cxx | 1 + build2/bin/guess | 36 +++++++++++++-- build2/bin/guess.cxx | 120 ++++++++++++++++++++++++++++++++++++++++++++++++-- build2/bin/module | 9 ++++ build2/bin/module.cxx | 90 +++++++++++++++++++++++++++++++------ 5 files changed, 236 insertions(+), 20 deletions(-) diff --git a/build2/b.cxx b/build2/b.cxx index 8b3c693..f272926 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -189,6 +189,7 @@ main (int argc, char* argv[]) &install::init}; builtin_modules["bin"] = module_functions {nullptr, &bin::init}; + builtin_modules["bin.ld"] = module_functions {nullptr, &bin::ld_init}; builtin_modules["cxx"] = module_functions {nullptr, &cxx::init}; builtin_modules["cli"] = module_functions {nullptr, &cli::init}; diff --git a/build2/bin/guess b/build2/bin/guess index a80539e..d6ff76d 100644 --- a/build2/bin/guess +++ b/build2/bin/guess @@ -28,7 +28,7 @@ namespace build2 // a toolchain-specific manner (usually the output of --version/-V) and // is not bulletproof. // - struct bin_info + struct ar_info { string ar_id; string ar_signature; @@ -43,8 +43,38 @@ namespace build2 // attemplated and the returned ranlib_* members will be left empty as // well. // - bin_info - guess (const path& ar, const path& ranlib); + ar_info + guess_ar (const path& ar, const path& ranlib); + + // ld information. + // + // Currently recognized ld 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 + { + string ld_id; + string ld_signature; + string ld_checksum; + }; + + ld_info + guess_ld (const path& ld); } } diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx index d036f81..ea4ac49 100644 --- a/build2/bin/guess.cxx +++ b/build2/bin/guess.cxx @@ -22,10 +22,10 @@ namespace build2 empty () const {return id.empty ();} }; - bin_info - guess (const path& ar, const path& rl) + ar_info + guess_ar (const path& ar, const path& rl) { - tracer trace ("bin::guess"); + tracer trace ("bin::guess_ar"); guess_result arr, rlr; @@ -166,9 +166,121 @@ namespace build2 fail << "unable to guess " << rl << " signature"; } - return bin_info { + return ar_info { move (arr.id), move (arr.signature), move (arr.checksum), move (rlr.id), move (rlr.signature), move (rlr.checksum)}; } + + ld_info + guess_ld (const path& ld) + { + tracer trace ("bin::guess_ld"); + + guess_result r; + + // 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 exist 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. + // + { + auto f = [] (string& l) -> guess_result + { + // Microsoft link.exe output starts with "Microsoft (R) ". + // + if (l.compare (0, 14, "Microsoft (R) ") == 0) + return guess_result {"msvc", move (l), ""}; + + // Binutils ld.bfd --version output has a line that starts with + // "GNU ld " while ld.gold -- "GNU gold". + // + if (l.compare (0, 7, "GNU ld ") == 0) + return guess_result {"gnu", move (l), ""}; + + if (l.compare (0, 9, "GNU gold ") == 0) + return guess_result {"gold", move (l), ""}; + + 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 (ld, "--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) -> 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), ""}; + + // 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), ""}; + + return guess_result (); + }; + + sha256 cs; + r = run (ld, "-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) -> 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), ""}; + + return guess_result (); + }; + + // Suppress all the errors because we may be trying an unsupported + // option. + // + sha256 cs; + r = run (ld, "-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 (r.id), move (r.signature), move (r.checksum)}; + } } } diff --git a/build2/bin/module b/build2/bin/module index e74cbe2..fa592cf 100644 --- a/build2/bin/module +++ b/build2/bin/module @@ -22,6 +22,15 @@ namespace build2 bool, bool, const variable_map&); + + bool + ld_init (scope&, + scope&, + const location&, + unique_ptr&, + bool, + bool, + const variable_map&); } } diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx index 9ce069c..f5e44db 100644 --- a/build2/bin/module.cxx +++ b/build2/bin/module.cxx @@ -305,7 +305,7 @@ namespace build2 const path& ar (cast (p.first)); const path& ranlib (v ? cast (v) : path ()); - bin_info bi (guess (ar, ranlib)); + ar_info ai (guess_ar (ar, ranlib)); // If this is a new value (e.g., we are configuring), then print the // report at verbosity level 2 and up (-v). @@ -316,30 +316,30 @@ namespace build2 text << "bin.ar\n" << " exe " << ar << '\n' - << " id " << bi.ar_id << '\n' - << " signature " << bi.ar_signature << '\n' - << " checksum " << bi.ar_checksum; + << " id " << ai.ar_id << '\n' + << " signature " << ai.ar_signature << '\n' + << " checksum " << ai.ar_checksum; if (!ranlib.empty ()) { text << "bin.ranlib\n" << " exe " << ranlib << '\n' - << " id " << bi.ranlib_id << '\n' - << " signature " << bi.ranlib_signature << '\n' - << " checksum " << bi.ranlib_checksum; + << " id " << ai.ranlib_id << '\n' + << " signature " << ai.ranlib_signature << '\n' + << " checksum " << ai.ranlib_checksum; } } - r.assign ("bin.ar.id") = move (bi.ar_id); - r.assign ("bin.ar.signature") = move (bi.ar_signature); - r.assign ("bin.ar.checksum") = move (bi.ar_checksum); + r.assign ("bin.ar.id") = move (ai.ar_id); + r.assign ("bin.ar.signature") = move (ai.ar_signature); + r.assign ("bin.ar.checksum") = move (ai.ar_checksum); if (!ranlib.empty ()) { - r.assign ("bin.ranlib.id") = move (bi.ranlib_id); + r.assign ("bin.ranlib.id") = move (ai.ranlib_id); r.assign ("bin.ranlib.signature") = - move (bi.ranlib_signature); - r.assign ("bin.ranlib.checksum") = move (bi.ranlib_checksum); + move (ai.ranlib_signature); + r.assign ("bin.ranlib.checksum") = move (ai.ranlib_checksum); } } @@ -421,5 +421,69 @@ namespace build2 return true; } + + bool + ld_init (scope& r, + scope& b, + const location& loc, + unique_ptr&, + bool first, + bool, + const variable_map& config_hints) + { + tracer trace ("bin::ld_init"); + l5 ([&]{trace << "for " << b.out_path ();}); + + // Make sure the bin core is loaded. + // + if (!cast_false (b["bin.loaded"])) + load_module ("bin", r, b, loc, false, config_hints); + + // Enter module variables. + // + if (first) + { + auto& v (var_pool); + + 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 (r["bin.target.system"])); + const char* ld_d (tsys == "win32-msvc" ? "link" : "ld"); + + auto p (config::required (r, + "config.bin.ld", + path (apply (r["bin.pattern"], ld_d)))); + + const path& ld (cast (p.first)); + ld_info li (guess_ld (ld)); + + // 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\n" + << " exe " << ld << '\n' + << " id " << li.ld_id << '\n' + << " signature " << li.ld_signature << '\n' + << " checksum " << li.ld_checksum; + } + + r.assign ("bin.ld.id") = move (li.ld_id); + r.assign ("bin.ld.signature") = move (li.ld_signature); + r.assign ("bin.ld.checksum") = move (li.ld_checksum); + } + + return true; + } } } -- cgit v1.1