aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-03-14 13:30:47 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-03-14 13:30:47 +0200
commitbbff2b69459a370afd6c74b6b0d3bb080ff22b89 (patch)
treee0528ae3ffd46bdaa0b76063edb918bf1dc93de6
parent10dfd69f4a714f8df2ea98e8cd2ff9359a63016a (diff)
Add support for guessing ar/ranlib signatures
-rw-r--r--build2/bin/guess41
-rw-r--r--build2/bin/guess.cxx154
-rw-r--r--build2/bin/module.cxx46
-rw-r--r--build2/buildfile1
-rw-r--r--build2/cxx/guess.cxx9
-rw-r--r--build2/cxx/link.cxx7
-rw-r--r--build2/cxx/module.cxx2
-rw-r--r--build2/utility11
-rw-r--r--build2/utility.txx8
9 files changed, 255 insertions, 24 deletions
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 <build2/types>
+#include <build2/utility>
+
+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 <build2/bin/guess>
+
+#include <build2/diagnostics>
+
+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<string> (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<string> (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<string> (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<string> (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 <build2/install/utility>
#include <build2/bin/rule>
+#include <build2/bin/guess>
#include <build2/bin/target>
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<string> (p.first))); // @@ VAR
+ const path& ranlib (v ? path (as<string> (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 <build2/diagnostics>
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<guess_result> (cxx, "-v", f, false, &cs);
+ r = run<guess_result> (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<string> (*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 <typename T>
@@ -97,6 +101,7 @@ namespace build2
run (const char* const* args,
T (*) (string&),
bool error = true,
+ bool ignore_exit = false,
sha256* checksum = nullptr);
template <typename T>
@@ -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<T> (args, f, error, checksum);
+ return run<T> (args, f, error, ignore_exit, checksum);
}
template <typename T>
@@ -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<T> (args, f, error, checksum);
+ return run<T> (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 <typename T>
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;