aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-07-11 05:56:17 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-07-11 05:56:17 +0200
commitfac576a331d6587e4343d09d6caf959d9a776118 (patch)
tree93057af1757ca47cf603ebba895bdd2416cf6c05
parent80f55f5857d340c31fcd951f645d3f337ed66a6b (diff)
Add bin.ld sub-module
-rw-r--r--build2/b.cxx1
-rw-r--r--build2/bin/guess36
-rw-r--r--build2/bin/guess.cxx120
-rw-r--r--build2/bin/module9
-rw-r--r--build2/bin/module.cxx90
5 files changed, 236 insertions, 20 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 8b3c693..f272926 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -189,6 +189,7 @@ main (int argc, char* argv[])
&install::init};
builtin_modules["bin"] = module_functions {nullptr, &bin::init};
+ builtin_modules["bin.ld"] = module_functions {nullptr, &bin::ld_init};
builtin_modules["cxx"] = module_functions {nullptr, &cxx::init};
builtin_modules["cli"] = module_functions {nullptr, &cli::init};
diff --git a/build2/bin/guess b/build2/bin/guess
index a80539e..d6ff76d 100644
--- a/build2/bin/guess
+++ b/build2/bin/guess
@@ -28,7 +28,7 @@ namespace build2
// a toolchain-specific manner (usually the output of --version/-V) and
// is not bulletproof.
//
- struct bin_info
+ struct ar_info
{
string ar_id;
string ar_signature;
@@ -43,8 +43,38 @@ namespace build2
// attemplated and the returned ranlib_* members will be left empty as
// well.
//
- bin_info
- guess (const path& ar, const path& ranlib);
+ ar_info
+ guess_ar (const path& ar, const path& ranlib);
+
+ // ld information.
+ //
+ // Currently recognized ld and their ids:
+ //
+ // gnu GNU binutils ld.bfd
+ // gold GNU binutils ld.gold
+ // llvm LLVM lld (note: not llvm-ld or llvm-link)
+ // ld64 Apple's new linker
+ // cctools Apple's old/classic linker
+ // msvc Microsoft's link.exe
+ //
+ // Note that BSDs are currently using GNU ld but some of them (e.g.,
+ // FreeBSD) are hoping to migrate to lld.
+ //
+ // The signature is normally the --version/-version/-v line.
+ //
+ // The checksum is used to detect ld changes. It is calculated in a
+ // toolchain-specific manner (usually the output of --version/-version/-v)
+ // and is not bulletproof.
+ //
+ struct ld_info
+ {
+ string ld_id;
+ string ld_signature;
+ string ld_checksum;
+ };
+
+ ld_info
+ guess_ld (const path& ld);
}
}
diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx
index d036f81..ea4ac49 100644
--- a/build2/bin/guess.cxx
+++ b/build2/bin/guess.cxx
@@ -22,10 +22,10 @@ namespace build2
empty () const {return id.empty ();}
};
- bin_info
- guess (const path& ar, const path& rl)
+ ar_info
+ guess_ar (const path& ar, const path& rl)
{
- tracer trace ("bin::guess");
+ tracer trace ("bin::guess_ar");
guess_result arr, rlr;
@@ -166,9 +166,121 @@ namespace build2
fail << "unable to guess " << rl << " signature";
}
- return bin_info {
+ return ar_info {
move (arr.id), move (arr.signature), move (arr.checksum),
move (rlr.id), move (rlr.signature), move (rlr.checksum)};
}
+
+ ld_info
+ guess_ld (const path& ld)
+ {
+ tracer trace ("bin::guess_ld");
+
+ guess_result r;
+
+ // Binutils ld recognizes the --version option. Microsoft's link.exe
+ // doesn't support --version (nor any other way to get the version
+ // without the error exist status) but it will still print its banner.
+ // We also want to recognize link.exe as fast as possible since it will
+ // be the most commonly configured linker (for other platoforms the
+ // linker will normally be used indirectly via the compiler and the
+ // bin.ld module won't be loaded). So we are going to ignore the error
+ // exit status. Our signatures are fairly specific to avoid any kind
+ // of false positives.
+ //
+ {
+ 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), ""};
+
+ // Binutils ld.bfd --version output has a line that starts with
+ // "GNU ld " while ld.gold -- "GNU gold".
+ //
+ if (l.compare (0, 7, "GNU ld ") == 0)
+ return guess_result {"gnu", move (l), ""};
+
+ if (l.compare (0, 9, "GNU gold ") == 0)
+ return guess_result {"gold", move (l), ""};
+
+ return guess_result ();
+ };
+
+ // Redirect STDERR to STDOUT and ignore exit status. Note that in case
+ // of link.exe we will hash the diagnostics (yes, it goes to stdout)
+ // but that seems harmless.
+ //
+ sha256 cs;
+ r = run<guess_result> (ld, "--version", f, false, true, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ // Next try -v which will cover Apple's linkers.
+ //
+ if (r.empty ())
+ {
+ auto f = [] (string& l) -> guess_result
+ {
+ // New ld64 has "PROJECT:ld64" in the first line (output to stderr),
+ // for example:
+ //
+ // @(#)PROGRAM:ld PROJECT:ld64-242.2
+ //
+ if (l.find ("PROJECT:ld64") != string::npos)
+ return guess_result {"ld64", move (l), ""};
+
+ // 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 ();
+ };
+
+ sha256 cs;
+ r = run<guess_result> (ld, "-v", f, false, false, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ // Finally try -version which will take care of LLVM's lld.
+ //
+ if (r.empty ())
+ {
+ auto f = [] (string& l) -> guess_result
+ {
+ // Unlike other LLVM tools (e.g., ar), the lld's version is printed
+ // (to stderr) as:
+ //
+ // LLVM Linker Version: 3.7
+ //
+ if (l.compare (0, 19, "LLVM Linker Version") == 0)
+ return guess_result {"llvm", move (l), ""};
+
+ return guess_result ();
+ };
+
+ // Suppress all the errors because we may be trying an unsupported
+ // option.
+ //
+ sha256 cs;
+ r = run<guess_result> (ld, "-version", f, false, false, &cs);
+
+ if (!r.empty ())
+ r.checksum = cs.string ();
+ }
+
+ if (r.empty ())
+ fail << "unable to guess " << ld << " signature";
+
+ return ld_info {move (r.id), move (r.signature), move (r.checksum)};
+ }
}
}
diff --git a/build2/bin/module b/build2/bin/module
index e74cbe2..fa592cf 100644
--- a/build2/bin/module
+++ b/build2/bin/module
@@ -22,6 +22,15 @@ namespace build2
bool,
bool,
const variable_map&);
+
+ bool
+ ld_init (scope&,
+ scope&,
+ const location&,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map&);
}
}
diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx
index 9ce069c..f5e44db 100644
--- a/build2/bin/module.cxx
+++ b/build2/bin/module.cxx
@@ -305,7 +305,7 @@ namespace build2
const path& ar (cast<path> (p.first));
const path& ranlib (v ? cast<path> (v) : path ());
- bin_info bi (guess (ar, ranlib));
+ ar_info ai (guess_ar (ar, ranlib));
// If this is a new value (e.g., we are configuring), then print the
// report at verbosity level 2 and up (-v).
@@ -316,30 +316,30 @@ namespace build2
text << "bin.ar\n"
<< " exe " << ar << '\n'
- << " id " << bi.ar_id << '\n'
- << " signature " << bi.ar_signature << '\n'
- << " checksum " << bi.ar_checksum;
+ << " id " << ai.ar_id << '\n'
+ << " signature " << ai.ar_signature << '\n'
+ << " checksum " << ai.ar_checksum;
if (!ranlib.empty ())
{
text << "bin.ranlib\n"
<< " exe " << ranlib << '\n'
- << " id " << bi.ranlib_id << '\n'
- << " signature " << bi.ranlib_signature << '\n'
- << " checksum " << bi.ranlib_checksum;
+ << " id " << ai.ranlib_id << '\n'
+ << " signature " << ai.ranlib_signature << '\n'
+ << " checksum " << ai.ranlib_checksum;
}
}
- r.assign<string> ("bin.ar.id") = move (bi.ar_id);
- r.assign<string> ("bin.ar.signature") = move (bi.ar_signature);
- r.assign<string> ("bin.ar.checksum") = move (bi.ar_checksum);
+ r.assign<string> ("bin.ar.id") = move (ai.ar_id);
+ r.assign<string> ("bin.ar.signature") = move (ai.ar_signature);
+ r.assign<string> ("bin.ar.checksum") = move (ai.ar_checksum);
if (!ranlib.empty ())
{
- r.assign<string> ("bin.ranlib.id") = move (bi.ranlib_id);
+ r.assign<string> ("bin.ranlib.id") = move (ai.ranlib_id);
r.assign<string> ("bin.ranlib.signature") =
- move (bi.ranlib_signature);
- r.assign<string> ("bin.ranlib.checksum") = move (bi.ranlib_checksum);
+ move (ai.ranlib_signature);
+ r.assign<string> ("bin.ranlib.checksum") = move (ai.ranlib_checksum);
}
}
@@ -421,5 +421,69 @@ namespace build2
return true;
}
+
+ bool
+ ld_init (scope& r,
+ scope& b,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& config_hints)
+ {
+ tracer trace ("bin::ld_init");
+ l5 ([&]{trace << "for " << b.out_path ();});
+
+ // Make sure the bin core is loaded.
+ //
+ if (!cast_false<bool> (b["bin.loaded"]))
+ load_module ("bin", r, b, loc, false, config_hints);
+
+ // Enter module variables.
+ //
+ if (first)
+ {
+ auto& v (var_pool);
+
+ v.insert<path> ("config.bin.ld", true);
+ }
+
+ // Configure.
+ //
+ if (first)
+ {
+ // config.bin.ld
+ //
+ // Use the target to decide on the default ld name.
+ //
+ const string& tsys (cast<string> (r["bin.target.system"]));
+ const char* ld_d (tsys == "win32-msvc" ? "link" : "ld");
+
+ auto p (config::required (r,
+ "config.bin.ld",
+ path (apply (r["bin.pattern"], ld_d))));
+
+ const path& ld (cast<path> (p.first));
+ ld_info li (guess_ld (ld));
+
+ // 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))
+ {
+ text << "bin.ld\n"
+ << " exe " << ld << '\n'
+ << " id " << li.ld_id << '\n'
+ << " signature " << li.ld_signature << '\n'
+ << " checksum " << li.ld_checksum;
+ }
+
+ r.assign<string> ("bin.ld.id") = move (li.ld_id);
+ r.assign<string> ("bin.ld.signature") = move (li.ld_signature);
+ r.assign<string> ("bin.ld.checksum") = move (li.ld_checksum);
+ }
+
+ return true;
+ }
}
}