From bbff2b69459a370afd6c74b6b0d3bb080ff22b89 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 14 Mar 2016 13:30:47 +0200 Subject: Add support for guessing ar/ranlib signatures --- build2/bin/guess | 41 ++++++++++++++ build2/bin/guess.cxx | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++ build2/bin/module.cxx | 46 +++++++++++---- build2/buildfile | 1 + build2/cxx/guess.cxx | 9 +-- build2/cxx/link.cxx | 7 ++- build2/cxx/module.cxx | 2 +- build2/utility | 11 +++- build2/utility.txx | 8 ++- 9 files changed, 255 insertions(+), 24 deletions(-) create mode 100644 build2/bin/guess create mode 100644 build2/bin/guess.cxx diff --git a/build2/bin/guess b/build2/bin/guess new file mode 100644 index 0000000..005235d --- /dev/null +++ b/build2/bin/guess @@ -0,0 +1,41 @@ +// file : build2/bin/guess -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_BIN_GUESS +#define BUILD2_BIN_GUESS + +#include +#include + +namespace build2 +{ + namespace bin + { + // ar/ranlib information. + // + // 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 bin_info + { + string ar_signature; + string ar_checksum; + + string ranlib_signature; + string ranlib_checksum; + }; + + // The ranlib path can be empty, in which case no ranlib guessing will be + // attemplated and the returned ranlib_* members will be left empty as + // well. + // + bin_info + guess (const path& ar, const path& ranlib); + } +} + +#endif // BUILD2_BIN_GUESS diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx new file mode 100644 index 0000000..1cdb40d --- /dev/null +++ b/build2/bin/guess.cxx @@ -0,0 +1,154 @@ +// file : build2/bin/guess.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +using namespace std; + +namespace build2 +{ + namespace bin + { + bin_info + guess (const path& ar, const path& ranlib) + { + tracer trace ("bin::guess"); + + bin_info r; + string& as (r.ar_signature); + + // Binutils, LLVM, and FreeBSD ar/ranlib all recognize the --version + // option, so start with that. + // + { + auto f = [] (string& l) -> string + { + // Binutils ar --version output has a line that starts with + // "GNU ar ". + // + if (l.compare (0, 7, "GNU ar ") == 0) + return move (l); + + // LLVM ar --version output has a line that starts with + // "LLVM version ". + // + if (l.compare (0, 13, "LLVM version ") == 0) + return move (l); + + // FreeBSD ar --verison output starts with "BSD ar ". + // + if (l.compare (0, 7, "BSD ar ") == 0) + return move (l); + + return string (); + }; + + // Suppress all the errors because we may be trying an unsupported + // option. + // + sha256 cs; + as = run (ar, "--version", f, false, false, &cs); + + if (!as.empty ()) + r.ar_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 (as.empty ()) + { + auto f = [] (string& l) -> string + { + return l.find (" ar ") == string::npos ? string () : move (l); + }; + + // Redirect STDERR to STDOUT and ignore exit status. + // + sha256 cs; + as = run (ar, f, false, true, &cs); + + if (!as.empty ()) + { + l4 ([&]{trace << "generic ar signature '" << as << "'";}); + + r.ar_signature = "Generic ar"; + r.ar_checksum = cs.string (); + } + } + + if (as.empty ()) + fail << "unable to guess " << ar << " signature"; + + // Now repeat pretty much the same steps for ranlib if requested. + // + if (ranlib.empty ()) + return r; + + string& rs (r.ranlib_signature); + + // Binutils, LLVM, and FreeBSD. + // + { + auto f = [] (string& l) -> string + { + // "GNU ranlib ". + // + if (l.compare (0, 11, "GNU ranlib ") == 0) + return move (l); + + // "LLVM version ". + // + if (l.compare (0, 13, "LLVM version ") == 0) + return move (l); + + // "ranlib " (note: not "BSD ranlib " for some reason). + // + if (l.compare (0, 7, "ranlib ") == 0) + return move (l); + + return string (); + }; + + sha256 cs; + rs = run (ranlib, "--version", f, false, false, &cs); + + if (!rs.empty ()) + r.ranlib_checksum = cs.string (); + } + + // Mac OS X (and probably also older BSDs). + // + if (rs.empty ()) + { + auto f = [] (string& l) -> string + { + return l.find ("ranlib") == string::npos ? string () : move (l); + }; + + // Redirect STDERR to STDOUT and ignore exit status. + // + sha256 cs; + rs = run (ranlib, f, false, true, &cs); + + if (!rs.empty ()) + { + l4 ([&]{trace << "generic ranlib signature '" << rs << "'";}); + + r.ranlib_signature = "Generic ranlib"; + r.ranlib_checksum = cs.string (); + } + } + + if (rs.empty ()) + fail << "unable to guess " << ranlib << " signature"; + + return r; + } + } +} diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx index c5ba25a..2b89eeb 100644 --- a/build2/bin/module.cxx +++ b/build2/bin/module.cxx @@ -12,6 +12,7 @@ #include #include +#include #include using namespace std; @@ -164,20 +165,45 @@ namespace build2 // specified by the user in order for us to use it (most targets support // the -s option to ar). // - - // @@ Maybe, if explicitly specified by the user, we should try to run - // them? - // if (first) { - config::required (r, "config.bin.ar", "ar"); - r.assign ("bin.ar.signature", string_type) = "Some ar"; - r.assign ("bin.ar.checksum", string_type) = "123"; + auto p (config::required (r, "config.bin.ar", "ar")); + auto& v (config::optional (r, "config.bin.ranlib")); + + const path& ar (path (as (p.first))); // @@ VAR + const path& ranlib (v ? path (as (v)) : path ()); // @@ VAR + + bin_info bi (guess (ar, ranlib)); + + // 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)) + { + //@@ Print project out root or name? See cxx. + + text << ar << ":\n" + << " signature " << bi.ar_signature << "\n" + << " checksum " << bi.ar_checksum; + + if (!ranlib.empty ()) + { + text << ranlib << ":\n" + << " signature " << bi.ranlib_signature << "\n" + << " checksum " << bi.ranlib_checksum; + } + } - if (auto& v = config::optional (r, "config.bin.ranlib")) + r.assign ("bin.ar.signature", string_type) = move (bi.ar_signature); + r.assign ("bin.ar.checksum", string_type) = move (bi.ar_checksum); + + if (!ranlib.empty ()) { - r.assign ("bin.ranlib.signature", string_type) = "Some ranlib"; - r.assign ("bin.ranlib.checksum", string_type) = "234"; + r.assign ("bin.ranlib.signature", string_type) = + move (bi.ranlib_signature); + + r.assign ("bin.ranlib.checksum", string_type) = + move (bi.ranlib_checksum); } } diff --git a/build2/buildfile b/build2/buildfile index 3081e61..fa68657 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -33,6 +33,7 @@ exe{b}: \ {hxx ixx txx cxx}{ utility } \ {hxx ixx txx cxx}{ variable } \ {hxx }{ version } \ + bin/{hxx cxx}{ guess } \ bin/{hxx cxx}{ module } \ bin/{hxx cxx}{ rule } \ bin/{hxx cxx}{ target } \ diff --git a/build2/cxx/guess.cxx b/build2/cxx/guess.cxx index 90bf693..d2f828d 100644 --- a/build2/cxx/guess.cxx +++ b/build2/cxx/guess.cxx @@ -9,7 +9,6 @@ #include using namespace std; -using namespace butl; namespace build2 { @@ -97,7 +96,6 @@ namespace build2 static guess_result guess (const path& cxx, const string& pre) - try { tracer trace ("cxx::guess"); @@ -196,7 +194,7 @@ namespace build2 // Suppress all the compiler errors because we may be trying an // unsupported option. // - r = run (cxx, "-v", f, false, &cs); + r = run (cxx, "-v", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -277,11 +275,6 @@ namespace build2 return r; } - catch (const process_error& e) - { - error << "unable to execute " << cxx << ": " << e.what (); - throw failed (); - } static compiler_info guess_gcc (const path& cxx, diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx index 0002797..f9fa69d 100644 --- a/build2/cxx/link.cxx +++ b/build2/cxx/link.cxx @@ -827,8 +827,10 @@ namespace build2 if (lt == type::a) { // If the user asked for ranlib, don't try to do its function with -s. + // Some ar implementations (e.g., the LLVM one) doesn't support + // leading '-'. // - args.push_back (ranlib ? "-rc" : "-rcs"); + args.push_back (ranlib ? "rc" : "rcs"); } else { @@ -1048,6 +1050,9 @@ namespace build2 const char* args[] = { as (*ranlib).c_str (), relt.string ().c_str (), nullptr}; + if (verb >= 2) + print_process (args); + try { process pr (args); diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx index 8e83a9b..32f811a 100644 --- a/build2/cxx/module.cxx +++ b/build2/cxx/module.cxx @@ -194,7 +194,7 @@ namespace build2 << " build " << ci.version.build << "\n" << " signature " << ci.signature << "\n" << " checksum " << ci.checksum << "\n" - << " target " << ci.target << ""; + << " target " << ci.target; } r.assign ("cxx.id", string_type) = ci.id.string (); diff --git a/build2/utility b/build2/utility index 0c442b0..a3f9be4 100644 --- a/build2/utility +++ b/build2/utility @@ -90,6 +90,10 @@ namespace build2 // is false, only in case of a "content match" (so that any diagnostics // lines are left intact). // + // If ignore_exit is true, then the program's exist status is ignored (if it + // is false and the program exits with the non-zero status, then an empty T + // instance is returned). + // // If checksum is not NULL, then feed it the content of each line. // template @@ -97,6 +101,7 @@ namespace build2 run (const char* const* args, T (*) (string&), bool error = true, + bool ignore_exit = false, sha256* checksum = nullptr); template @@ -104,10 +109,11 @@ namespace build2 run (const path& prog, T (*f) (string&), bool error = true, + bool ignore_exit = false, sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), nullptr}; - return run (args, f, error, checksum); + return run (args, f, error, ignore_exit, checksum); } template @@ -116,10 +122,11 @@ namespace build2 const char* arg, T (*f) (string&), bool error = true, + bool ignore_exit = false, sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), arg, nullptr}; - return run (args, f, error, checksum); + return run (args, f, error, ignore_exit, checksum); } // Empty string and path. diff --git a/build2/utility.txx b/build2/utility.txx index 7848296..bf9d9ab 100644 --- a/build2/utility.txx +++ b/build2/utility.txx @@ -6,7 +6,11 @@ namespace build2 { template T - run (const char* const* args, T (*f) (string&), bool err, sha256* checksum) + run (const char* const* args, + T (*f) (string&), + bool err, + bool ignore_exit, + sha256* checksum) { process pr (start_run (args, err)); ifdstream is (pr.in_ofd); @@ -28,7 +32,7 @@ namespace build2 is.close (); // Don't block. - if (!finish_run (args, err, pr, l)) + if (!(finish_run (args, err, pr, l) || ignore_exit)) r = T (); return r; -- cgit v1.1