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 +++++++++++---- 3 files changed, 231 insertions(+), 10 deletions(-) create mode 100644 build2/bin/guess create mode 100644 build2/bin/guess.cxx (limited to 'build2/bin') 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); } } -- cgit v1.1