From 70a76a8a03f0390b44d372fb8816cf529a9f1c35 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 14 Aug 2018 14:06:23 +0200 Subject: 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. --- build2/bin/guess.cxx | 115 ++++++++++++++++++++++++++++++++++++------------ build2/bin/guess.hxx | 1 + build2/bin/init.cxx | 34 +++++++++++--- build2/cc/link-rule.cxx | 54 ++++++++++++++++++----- build2/types.hxx | 6 +++ 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 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 ("bin.ar.signature") = move (ari.ar_signature); r.assign ("bin.ar.checksum") = move (ari.ar_checksum); + { + semantic_version& v (ari.ar_version); + + r.assign ("bin.ar.version") = v.string (); + r.assign ("bin.ar.version.major") = v.major; + r.assign ("bin.ar.version.minor") = v.minor; + r.assign ("bin.ar.version.patch") = v.patch; + r.assign ("bin.ar.version.build") = move (v.build); + } + if (ranlib != nullptr) { r.assign ("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 (rs["bin.ar.id"])); + + for (bool g (id == "gnu"); g || id == "llvm"; ) // Breakout loop. + { + auto mj (cast (rs["bin.ar.version.major"])); + if (mj < (g ? 2 : 3)) break; + if (mj == (g ? 2 : 3)) + { + auto mi (cast (rs["bin.ar.version.minor"])); + if (mi < (g ? 18 : 8)) break; + if (mi == 18 && g) + { + auto pa (cast (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 #include #include +#include #include namespace build2 @@ -269,6 +270,11 @@ namespace build2 // using butl::target_triplet; + // + // + using butl::semantic_version; + using butl::parse_semantic_version; + // // using butl::standard_version; -- cgit v1.1