aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-08-14 14:06:23 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-08-14 14:06:23 +0200
commit70a76a8a03f0390b44d372fb8816cf529a9f1c35 (patch)
tree303d0447705e81397bb2cf4b0ed069cbcd06366c
parent3cad4e39c8d20e1811c75d4c83aa108185bd0a47 (diff)
Use thin archives if available for utility libraries
Thin archives are supported by GNU ar since binutils 2.19.1 and LLVM ar since LLVM 3.8.0.
-rw-r--r--build2/bin/guess.cxx115
-rw-r--r--build2/bin/guess.hxx1
-rw-r--r--build2/bin/init.cxx34
-rw-r--r--build2/cc/link-rule.cxx54
-rw-r--r--build2/types.hxx6
5 files changed, 166 insertions, 44 deletions
diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx
index 0be2f29..a142e59 100644
--- a/build2/bin/guess.cxx
+++ b/build2/bin/guess.cxx
@@ -17,15 +17,26 @@ namespace build2
string id;
string signature;
string checksum;
+ semantic_version version;
guess_result () = default;
- guess_result (string&& i, string&& s)
- : id (move (i)), signature (move (s)) {}
+ 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<semantic_version> 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)
{
@@ -48,29 +59,63 @@ namespace build2
{
auto f = [] (string& l) -> guess_result
{
- // Binutils ar --version output has a line that starts with "GNU ar"
- // and/or contains "GNU Binutils" (some embedded toolchain makers
- // customize this stuff in all kinds of ways). So let's just look
- // for "GNU ".
+ // 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)
- return guess_result ("gnu", move (l));
+ {
+ 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 ".
+ // "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)
- return guess_result ("llvm", move (l));
+ {
+ 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 ".
+ // 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)
- return guess_result ("bsd", move (l));
+ {
+ semantic_version v (parse_version (l, 7));
+ return guess_result ("bsd", move (l), move (v));
+ }
- // Microsoft lib.exe output starts with "Microsoft (R) ".
+ // 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)
- return guess_result ("msvc", move (l));
+ {
+ semantic_version v (parse_version (l, l.rfind (' ') + 1));
+ return guess_result ("msvc", move (l), move (v));
+ }
return guess_result ();
};
@@ -96,7 +141,7 @@ namespace build2
auto f = [] (string& l) -> guess_result
{
return l.find (" ar ") != string::npos
- ? guess_result ("generic", move (l))
+ ? guess_result ("generic", move (l), semantic_version ())
: guess_result ();
};
@@ -115,7 +160,8 @@ namespace build2
if (arr.empty ())
fail << "unable to guess " << ar << " signature";
- // Now repeat pretty much the same steps for ranlib if requested.
+ // 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)
{
@@ -128,18 +174,18 @@ namespace build2
// but can vary.
//
if (l.find ("GNU ") != string::npos)
- return guess_result ("gnu", move (l));
+ return guess_result ("gnu", move (l), semantic_version ());
// "LLVM version ".
//
if (l.compare (0, 13, "LLVM version ") == 0)
- return guess_result ("llvm", move (l));
+ 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));
+ // return guess_result ("bsd", move (l), semantic_version ());
return guess_result ();
};
@@ -158,7 +204,7 @@ namespace build2
auto f = [] (string& l) -> guess_result
{
return l.find ("ranlib") != string::npos
- ? guess_result ("generic", move (l))
+ ? guess_result ("generic", move (l), semantic_version ())
: guess_result ();
};
@@ -179,8 +225,16 @@ namespace build2
}
return ar_info {
- move (arp), move (arr.id), move (arr.signature), move (arr.checksum),
- move (rlp), move (rlr.id), move (rlr.signature), move (rlr.checksum)};
+ 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
@@ -202,13 +256,15 @@ namespace build2
// exit status. Our signatures are fairly specific to avoid any kind
// of false positives.
//
+ // Version extraction is a @@ TODO.
+ //
{
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));
+ 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
@@ -216,10 +272,10 @@ namespace build2
// former case.
//
if (l.compare (0, 9, "GNU gold ") == 0)
- return guess_result ("gold", move (l));
+ return guess_result ("gold", move (l), semantic_version ());
if (l.find ("GNU ") != string::npos)
- return guess_result ("gnu", move (l));
+ return guess_result ("gnu", move (l), semantic_version ());
return guess_result ();
};
@@ -247,14 +303,14 @@ namespace build2
// @(#)PROGRAM:ld PROJECT:ld64-242.2
//
if (l.find ("PROJECT:ld64") != string::npos)
- return guess_result ("ld64", move (l));
+ 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));
+ return guess_result ("cctools", move (l), semantic_version ());
return guess_result ();
};
@@ -278,7 +334,7 @@ namespace build2
// LLVM Linker Version: 3.7
//
if (l.compare (0, 19, "LLVM Linker Version") == 0)
- return guess_result ("llvm", move (l));
+ return guess_result ("llvm", move (l), semantic_version ());
return guess_result ();
};
@@ -311,6 +367,7 @@ namespace build2
// Binutils windres recognizes the --version option.
//
+ // Version extraction is a @@ TODO.
{
auto f = [] (string& l) -> guess_result
{
@@ -318,7 +375,7 @@ namespace build2
// "GNU windres " but search for "GNU ", similar to other tools.
//
if (l.find ("GNU ") != string::npos)
- return guess_result ("gnu", move (l));
+ return guess_result ("gnu", move (l), semantic_version ());
return guess_result ();
};
@@ -341,7 +398,7 @@ namespace build2
auto f = [] (string& l) -> guess_result
{
if (l.compare (0, 14, "Microsoft (R) ") == 0)
- return guess_result ("msvc", move (l));
+ return guess_result ("msvc", move (l), semantic_version ());
return guess_result ();
};
diff --git a/build2/bin/guess.hxx b/build2/bin/guess.hxx
index b815660..13cee46 100644
--- a/build2/bin/guess.hxx
+++ b/build2/bin/guess.hxx
@@ -34,6 +34,7 @@ namespace build2
string ar_id;
string ar_signature;
string ar_checksum;
+ semantic_version ar_version;
process_path ranlib_path;
string ranlib_id;
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index ea4a0f1..7e022fda 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -584,11 +584,25 @@ namespace build2
{
diag_record dr (text);
- dr << "bin.ar " << project (r) << '@' << r.out_path () << '\n'
- << " ar " << ari.ar_path << '\n'
- << " id " << ari.ar_id << '\n'
- << " signature " << ari.ar_signature << '\n'
- << " checksum " << ari.ar_checksum;
+ {
+ dr << "bin.ar " << project (r) << '@' << r.out_path () << '\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)
{
@@ -605,6 +619,16 @@ namespace build2
r.assign<string> ("bin.ar.signature") = move (ari.ar_signature);
r.assign<string> ("bin.ar.checksum") = move (ari.ar_checksum);
+ {
+ semantic_version& v (ari.ar_version);
+
+ r.assign<string> ("bin.ar.version") = v.string ();
+ r.assign<uint64_t> ("bin.ar.version.major") = v.major;
+ r.assign<uint64_t> ("bin.ar.version.minor") = v.minor;
+ r.assign<uint64_t> ("bin.ar.version.patch") = v.patch;
+ r.assign<string> ("bin.ar.version.build") = move (v.build);
+ }
+
if (ranlib != nullptr)
{
r.assign<process_path> ("bin.ranlib.path") = move (ari.ranlib_path);
diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx
index 154cadb..ba6add4 100644
--- a/build2/cc/link-rule.cxx
+++ b/build2/cc/link-rule.cxx
@@ -1540,7 +1540,7 @@ namespace build2
// Storage.
//
- string soname1, soname2;
+ string arg1, arg2;
strings sargs;
if (lt.static_library ())
@@ -1549,10 +1549,44 @@ namespace build2
else
{
// If the user asked for ranlib, don't try to do its function with
- // -s. Some ar implementations (e.g., the LLVM one) don't support
+ // -s. Some ar implementations (e.g., the LLVM one) don't support
// leading '-'.
//
- args.push_back (ranlib ? "rc" : "rcs");
+ arg1 = ranlib ? "rc" : "rcs";
+
+ // For utility libraries use thin archives if possible.
+ //
+ // Thin archives are supported by GNU ar since binutils 2.19.1 and
+ // LLVM ar since LLVM 3.8.0. Note that strictly speaking thin
+ // archives also have to be supported by the linker but it is
+ // probably safe to assume that the two came from the same version
+ // of binutils/LLVM.
+ //
+ if (lt.utility)
+ {
+ const string& id (cast<string> (rs["bin.ar.id"]));
+
+ for (bool g (id == "gnu"); g || id == "llvm"; ) // Breakout loop.
+ {
+ auto mj (cast<uint64_t> (rs["bin.ar.version.major"]));
+ if (mj < (g ? 2 : 3)) break;
+ if (mj == (g ? 2 : 3))
+ {
+ auto mi (cast<uint64_t> (rs["bin.ar.version.minor"]));
+ if (mi < (g ? 18 : 8)) break;
+ if (mi == 18 && g)
+ {
+ auto pa (cast<uint64_t> (rs["bin.ar.version.patch"]));
+ if (pa < 1) break;
+ }
+ }
+
+ arg1 += 'T';
+ break;
+ }
+ }
+
+ args.push_back (arg1.c_str ());
}
}
else
@@ -1613,17 +1647,17 @@ namespace build2
// install. However, if we don't make it @rpath, then the user
// won't be able to use config.bin.rpath for installed libraries.
//
- soname1 = "-install_name";
- soname2 = "@rpath/" + leaf;
+ arg1 = "-install_name";
+ arg2 = "@rpath/" + leaf;
}
else
- soname1 = "-Wl,-soname," + leaf;
+ arg1 = "-Wl,-soname," + leaf;
- if (!soname1.empty ())
- args.push_back (soname1.c_str ());
+ if (!arg1.empty ())
+ args.push_back (arg1.c_str ());
- if (!soname2.empty ())
- args.push_back (soname2.c_str ());
+ if (!arg2.empty ())
+ args.push_back (arg2.c_str ());
}
// Add rpaths. We used to first add the ones specified by the user
diff --git a/build2/types.hxx b/build2/types.hxx
index 94604e0..0f2f95b 100644
--- a/build2/types.hxx
+++ b/build2/types.hxx
@@ -44,6 +44,7 @@
#include <libbutl/small-vector.mxx>
#include <libbutl/project-name.mxx>
#include <libbutl/target-triplet.mxx>
+#include <libbutl/semantic-version.mxx>
#include <libbutl/standard-version.mxx>
namespace build2
@@ -269,6 +270,11 @@ namespace build2
//
using butl::target_triplet;
+ // <libbutl/semantic-version.mxx>
+ //
+ using butl::semantic_version;
+ using butl::parse_semantic_version;
+
// <libbutl/standard-version.mxx>
//
using butl::standard_version;