aboutsummaryrefslogtreecommitdiff
path: root/build2
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 /build2
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.
Diffstat (limited to 'build2')
-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;