From 48e2e4140b8e5aacdfd107a1215f21c9632c81c8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 22 Aug 2016 12:55:21 +0200 Subject: Cache process_path, use fallback search directory for binutils --- build2/bin/guess | 10 ++++-- build2/bin/guess.cxx | 81 +++++++++++++++++++++++++----------------- build2/bin/init.cxx | 83 +++++++++++++++++++++++++++++-------------- build2/c/init.cxx | 21 +++++------ build2/cc/common | 1 + build2/cc/compile.cxx | 14 +++++--- build2/cc/guess | 6 +++- build2/cc/guess.cxx | 49 ++++++++++++++++++++------ build2/cc/link | 8 +++-- build2/cc/link.cxx | 27 ++++++++------ build2/cc/module.cxx | 3 +- build2/cc/msvc.cxx | 16 ++++----- build2/cli/init.cxx | 34 ++++++++++++------ build2/cxx/init.cxx | 21 +++++------ build2/types | 4 +++ build2/utility | 59 +++++++++++++++++++++++++++++-- build2/utility.cxx | 47 +++++++++++++++++++++--- build2/utility.txx | 9 ++--- build2/variable | 22 ++++++++++++ build2/variable.cxx | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ build2/variable.ixx | 29 +++++++++++++++ 21 files changed, 498 insertions(+), 144 deletions(-) (limited to 'build2') diff --git a/build2/bin/guess b/build2/bin/guess index 540e644..bcf9732 100644 --- a/build2/bin/guess +++ b/build2/bin/guess @@ -30,10 +30,12 @@ namespace build2 // struct ar_info { + process_path ar_path; string ar_id; string ar_signature; string ar_checksum; + process_path ranlib_path; string ranlib_id; string ranlib_signature; string ranlib_checksum; @@ -43,7 +45,7 @@ namespace build2 // attemplated and the returned ranlib_* members will be left empty. // ar_info - guess_ar (const path& ar, const path* ranlib); + guess_ar (const path& ar, const path* ranlib, const dir_path& fallback); // ld information. // @@ -67,13 +69,14 @@ namespace build2 // struct ld_info { + process_path path; string id; string signature; string checksum; }; ld_info - guess_ld (const path& ld); + guess_ld (const path& ld, const dir_path& fallback); // rc information. // @@ -90,13 +93,14 @@ namespace build2 // struct rc_info { + process_path path; string id; string signature; string checksum; }; rc_info - guess_rc (const path& rc); + guess_rc (const path& rc, const dir_path& fallback); } } diff --git a/build2/bin/guess.cxx b/build2/bin/guess.cxx index de91db3..13f3933 100644 --- a/build2/bin/guess.cxx +++ b/build2/bin/guess.cxx @@ -18,17 +18,26 @@ namespace build2 string signature; string checksum; + guess_result () = default; + guess_result (string&& i, string&& s) + : id (move (i)), signature (move (s)) {} + bool empty () const {return id.empty ();} }; ar_info - guess_ar (const path& ar, const path* rl) + guess_ar (const path& ar, const path* rl, const dir_path& fallback) { tracer trace ("bin::guess_ar"); guess_result arr, rlr; + process_path arp (run_search (ar, true, fallback)); + process_path rlp (rl != nullptr + ? run_search (*rl, true, fallback) + : process_path ()); + // Binutils, LLVM, and FreeBSD ar/ranlib all recognize the --version // option. While Microsoft's lib.exe doesn't support --version, it only // issues a warning and exits with zero status, printing its usual @@ -43,23 +52,23 @@ namespace build2 // "GNU ar ". // if (l.compare (0, 7, "GNU ar ") == 0) - return guess_result {"gnu", move (l), ""}; + return guess_result ("gnu", move (l)); // LLVM ar --version output has a line that starts with // "LLVM version ". // if (l.compare (0, 13, "LLVM version ") == 0) - return guess_result {"llvm", move (l), ""}; + return guess_result ("llvm", move (l)); // FreeBSD ar --verison output starts with "BSD ar ". // if (l.compare (0, 7, "BSD ar ") == 0) - return guess_result {"bsd", move (l), ""}; + return guess_result ("bsd", move (l)); // Microsoft lib.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)); return guess_result (); }; @@ -69,7 +78,7 @@ namespace build2 // (yes, it goes to stdout) but that seems harmless. // sha256 cs; - arr = run (ar, "--version", f, false, false, &cs); + arr = run (arp, "--version", f, false, false, &cs); if (!arr.empty ()) arr.checksum = cs.string (); @@ -85,14 +94,14 @@ namespace build2 auto f = [] (string& l) -> guess_result { return l.find (" ar ") != string::npos - ? guess_result {"generic", move (l), ""} + ? guess_result ("generic", move (l)) : guess_result (); }; // Redirect STDERR to STDOUT and ignore exit status. // sha256 cs; - arr = run (ar, f, false, true, &cs); + arr = run (arp, f, false, true, &cs); if (!arr.empty ()) { @@ -116,24 +125,24 @@ namespace build2 // "GNU ranlib ". // if (l.compare (0, 11, "GNU ranlib ") == 0) - return guess_result {"gnu", move (l), ""}; + return guess_result ("gnu", move (l)); // "LLVM version ". // if (l.compare (0, 13, "LLVM version ") == 0) - return guess_result {"llvm", move (l), ""}; + return guess_result ("llvm", move (l)); // 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)); return guess_result (); }; sha256 cs; - rlr = run (*rl, "--version", f, false, false, &cs); + rlr = run (rlp, "--version", f, false, false, &cs); if (!rlr.empty ()) rlr.checksum = cs.string (); @@ -146,14 +155,14 @@ namespace build2 auto f = [] (string& l) -> guess_result { return l.find ("ranlib") != string::npos - ? guess_result {"generic", move (l), ""} + ? guess_result ("generic", move (l)) : guess_result (); }; // Redirect STDERR to STDOUT and ignore exit status. // sha256 cs; - rlr = run (*rl, f, false, true, &cs); + rlr = run (rlp, f, false, true, &cs); if (!rlr.empty ()) { @@ -167,17 +176,19 @@ namespace build2 } return ar_info { - move (arr.id), move (arr.signature), move (arr.checksum), - move (rlr.id), move (rlr.signature), move (rlr.checksum)}; + move (arp), move (arr.id), move (arr.signature), move (arr.checksum), + move (rlp), move (rlr.id), move (rlr.signature), move (rlr.checksum)}; } ld_info - guess_ld (const path& ld) + guess_ld (const path& ld, const dir_path& fallback) { tracer trace ("bin::guess_ld"); guess_result r; + process_path pp (run_search (ld, true, fallback)); + // 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. @@ -194,16 +205,16 @@ namespace build2 // 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)); // 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), ""}; + return guess_result ("gnu", move (l)); if (l.compare (0, 9, "GNU gold ") == 0) - return guess_result {"gold", move (l), ""}; + return guess_result ("gold", move (l)); return guess_result (); }; @@ -213,7 +224,7 @@ namespace build2 // but that seems harmless. // sha256 cs; - r = run (ld, "--version", f, false, true, &cs); + r = run (pp, "--version", f, false, true, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -231,20 +242,20 @@ 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)); // 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)); return guess_result (); }; sha256 cs; - r = run (ld, "-v", f, false, false, &cs); + r = run (pp, "-v", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -262,7 +273,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)); return guess_result (); }; @@ -271,7 +282,7 @@ namespace build2 // option. // sha256 cs; - r = run (ld, "-version", f, false, false, &cs); + r = run (pp, "-version", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -280,16 +291,19 @@ namespace build2 if (r.empty ()) fail << "unable to guess " << ld << " signature"; - return ld_info {move (r.id), move (r.signature), move (r.checksum)}; + return ld_info { + move (pp), move (r.id), move (r.signature), move (r.checksum)}; } rc_info - guess_rc (const path& rc) + guess_rc (const path& rc, const dir_path& fallback) { tracer trace ("bin::guess_rc"); guess_result r; + process_path pp (run_search (rc, true, fallback)); + // Binutils windres recognizes the --version option. // { @@ -299,7 +313,7 @@ namespace build2 // "GNU windres ". // if (l.compare (0, 12, "GNU windres ") == 0) - return guess_result {"gnu", move (l), ""}; + return guess_result ("gnu", move (l)); return guess_result (); }; @@ -308,7 +322,7 @@ namespace build2 // option. // sha256 cs; - r = run (rc, "--version", f, false, false, &cs); + r = run (pp, "--version", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -322,13 +336,13 @@ 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)); return guess_result (); }; sha256 cs; - r = run (rc, "/?", f, false, false, &cs); + r = run (pp, "/?", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -337,7 +351,8 @@ namespace build2 if (r.empty ()) fail << "unable to guess " << rc << " signature"; - return rc_info {move (r.id), move (r.signature), move (r.checksum)}; + return rc_info { + move (pp), move (r.id), move (r.signature), move (r.checksum)}; } } } diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx index 424a729..125bc7d 100644 --- a/build2/bin/init.cxx +++ b/build2/bin/init.cxx @@ -273,8 +273,12 @@ namespace build2 { const string& s (cast (*v)); - if (s.find ('*') == string::npos) + if (s.empty () || + (!path::traits::is_separator (s.back ()) && + s.find ('*') == string::npos)) + { fail << "missing '*' in binutils pattern '" << s << "'"; + } r.assign ("bin.pattern") = s; new_val = new_val || p.second; // False for a hinted value. @@ -428,8 +432,11 @@ namespace build2 { auto& v (var_pool); - v.insert ("config.bin.ar", true); - v.insert ("config.bin.ranlib", true); + v.insert ("bin.rc.path"); + v.insert ("bin.ranlib.path"); + + v.insert ("config.bin.ar", true); + v.insert ("config.bin.ranlib", true); } // Configure. @@ -456,6 +463,11 @@ namespace build2 const string& tsys (cast (r["bin.target.system"])); const char* ar_d (tsys == "win32-msvc" ? "lib" : "ar"); + // This can be either a pattern or a fallback search directory. + // + const string* pat (cast_null (r["bin.pattern"])); + bool fb (pat != nullptr && path::traits::is_separator (pat->back ())); + // Don't save the default value to config.build so that if the user // changes, say, the C++ compiler (which hinted the pattern), then // ar will automatically change as well. @@ -464,7 +476,7 @@ namespace build2 config::required ( r, "config.bin.ar", - path (apply_pattern (ar_d, cast_null (r["bin.pattern"]))), + path (apply_pattern (ar_d, fb ? nullptr : pat)), false, config::save_commented)); @@ -482,7 +494,8 @@ namespace build2 if (ranlib != nullptr && ranlib->empty ()) // @@ BC LT [null]. ranlib = nullptr; - ar_info ari (guess_ar (ar, ranlib)); + ar_info ari ( + guess_ar (ar, ranlib, fb ? dir_path (*pat) : dir_path ())); // If this is a new value (e.g., we are configuring), then print the // report at verbosity level 2 and up (-v). @@ -492,7 +505,7 @@ namespace build2 diag_record dr (text); dr << "bin.ar " << project (r) << '@' << r.out_path () << '\n' - << " ar " << ar << '\n' + << " ar " << ari.ar_path << '\n' << " id " << ari.ar_id << '\n' << " signature " << ari.ar_signature << '\n' << " checksum " << ari.ar_checksum; @@ -500,23 +513,25 @@ namespace build2 if (ranlib != nullptr) { dr << '\n' - << " ranlib " << *ranlib << '\n' + << " ranlib " << ari.ranlib_path << '\n' << " id " << ari.ranlib_id << '\n' << " signature " << ari.ranlib_signature << '\n' << " checksum " << ari.ranlib_checksum; } } - r.assign ("bin.ar.id") = move (ari.ar_id); - r.assign ("bin.ar.signature") = move (ari.ar_signature); - r.assign ("bin.ar.checksum") = move (ari.ar_checksum); + r.assign ("bin.ar.path") = move (ari.ar_path); + r.assign ("bin.ar.id") = move (ari.ar_id); + r.assign ("bin.ar.signature") = move (ari.ar_signature); + r.assign ("bin.ar.checksum") = move (ari.ar_checksum); if (ranlib != nullptr) { - r.assign ("bin.ranlib.id") = move (ari.ranlib_id); - r.assign ("bin.ranlib.signature") = + r.assign ("bin.ranlib.path") = move (ari.ranlib_path); + r.assign ("bin.ranlib.id") = move (ari.ranlib_id); + r.assign ("bin.ranlib.signature") = move (ari.ranlib_signature); - r.assign ("bin.ranlib.checksum") = + r.assign ("bin.ranlib.checksum") = move (ari.ranlib_checksum); } } @@ -570,7 +585,8 @@ namespace build2 { auto& v (var_pool); - v.insert ("config.bin.ld", true); + v.insert ("bin.ld.path"); + v.insert ("config.bin.ld", true); } // Configure. @@ -584,16 +600,21 @@ namespace build2 const string& tsys (cast (r["bin.target.system"])); const char* ld_d (tsys == "win32-msvc" ? "link" : "ld"); + // This can be either a pattern or a fallback search directory. + // + const string* pat (cast_null (r["bin.pattern"])); + bool fb (pat != nullptr && path::traits::is_separator (pat->back ())); + auto p ( config::required ( r, "config.bin.ld", - path (apply_pattern (ld_d, cast_null (r["bin.pattern"]))), + path (apply_pattern (ld_d, fb ? nullptr : pat)), false, config::save_commented)); const path& ld (cast (p.first)); - ld_info ldi (guess_ld (ld)); + ld_info ldi (guess_ld (ld, fb ? dir_path (*pat) : dir_path ())); // If this is a new value (e.g., we are configuring), then print the // report at verbosity level 2 and up (-v). @@ -601,15 +622,16 @@ namespace build2 if (verb >= (p.second ? 2 : 3)) { text << "bin.ld " << project (r) << '@' << r.out_path () << '\n' - << " ld " << ld << '\n' + << " ld " << ldi.path << '\n' << " id " << ldi.id << '\n' << " signature " << ldi.signature << '\n' << " checksum " << ldi.checksum; } - r.assign ("bin.ld.id") = move (ldi.id); - r.assign ("bin.ld.signature") = move (ldi.signature); - r.assign ("bin.ld.checksum") = move (ldi.checksum); + r.assign ("bin.ld.path") = move (ldi.path); + r.assign ("bin.ld.id") = move (ldi.id); + r.assign ("bin.ld.signature") = move (ldi.signature); + r.assign ("bin.ld.checksum") = move (ldi.checksum); } return true; @@ -674,7 +696,8 @@ namespace build2 { auto& v (var_pool); - v.insert ("config.bin.rc", true); + v.insert ("bin.rc.path"); + v.insert ("config.bin.rc", true); } // Configure. @@ -688,16 +711,21 @@ namespace build2 const string& tsys (cast (r["bin.target.system"])); const char* rc_d (tsys == "win32-msvc" ? "rc" : "windres"); + // This can be either a pattern or a fallback search directory. + // + const string* pat (cast_null (r["bin.pattern"])); + bool fb (pat != nullptr && path::traits::is_separator (pat->back ())); + auto p ( config::required ( r, "config.bin.rc", - path (apply_pattern (rc_d, cast_null (r["bin.pattern"]))), + path (apply_pattern (rc_d, fb ? nullptr : pat)), false, config::save_commented)); const path& rc (cast (p.first)); - rc_info rci (guess_rc (rc)); + rc_info rci (guess_rc (rc, fb ? dir_path (*pat) : dir_path ())); // If this is a new value (e.g., we are configuring), then print the // report at verbosity level 2 and up (-v). @@ -705,15 +733,16 @@ namespace build2 if (verb >= (p.second ? 2 : 3)) { text << "bin.rc " << project (r) << '@' << r.out_path () << '\n' - << " rc " << rc << '\n' + << " rc " << rci.path << '\n' << " id " << rci.id << '\n' << " signature " << rci.signature << '\n' << " checksum " << rci.checksum; } - r.assign ("bin.rc.id") = move (rci.id); - r.assign ("bin.rc.signature") = move (rci.signature); - r.assign ("bin.rc.checksum") = move (rci.checksum); + r.assign ("bin.rc.path") = move (rci.path); + r.assign ("bin.rc.id") = move (rci.id); + r.assign ("bin.rc.signature") = move (rci.signature); + r.assign ("bin.rc.checksum") = move (rci.checksum); } return true; diff --git a/build2/c/init.cxx b/build2/c/init.cxx index 52bf0b6..88f0b2b 100644 --- a/build2/c/init.cxx +++ b/build2/c/init.cxx @@ -121,16 +121,17 @@ namespace build2 // Note: some overridable, some not. // - v.insert ("config.c", true), - v.insert ("config.c.poptions", true), - v.insert ("config.c.coptions", true), - v.insert ("config.c.loptions", true), - v.insert ("config.c.libs", true), - - v.insert ("c.poptions"), - v.insert ("c.coptions"), - v.insert ("c.loptions"), - v.insert ("c.libs"), + v.insert ("config.c", true), + v.insert ("config.c.poptions", true), + v.insert ("config.c.coptions", true), + v.insert ("config.c.loptions", true), + v.insert ("config.c.libs", true), + + v.insert ("c.path"), + v.insert ("c.poptions"), + v.insert ("c.coptions"), + v.insert ("c.loptions"), + v.insert ("c.libs"), v["cc.poptions"], v["cc.coptions"], diff --git a/build2/cc/common b/build2/cc/common index 2be290a..987fe7b 100644 --- a/build2/cc/common +++ b/build2/cc/common @@ -36,6 +36,7 @@ namespace build2 const variable& config_x_loptions; const variable& config_x_libs; + const variable& x_path; const variable& x_poptions; const variable& x_coptions; const variable& x_loptions; diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx index 92ddf3b..33be687 100644 --- a/build2/cc/compile.cxx +++ b/build2/cc/compile.cxx @@ -644,12 +644,14 @@ namespace build2 // Initialize lazily, only if required. // + const process_path* xc (nullptr); cstrings args; string std; // Storage. - auto init_args = [&t, lo, &src, &rs, &args, &std, this] () + auto init_args = [&t, lo, &src, &rs, &xc, &args, &std, this] () { - args.push_back (cast (rs[config_x]).string ().c_str ()); + xc = &cast (rs[x_path]); + args.push_back (xc->recall_string ()); // Add *.export.poptions from prerequisite libraries. Note that here // we don't need to see group members (see apply()). @@ -1034,7 +1036,8 @@ namespace build2 // For VC with /EP we need a pipe to stderr and stdout should go // to /dev/null. // - process pr (args.data (), + process pr (*xc, + args.data (), 0, cid == "msvc" ? -2 : -1, cid == "msvc" ? -1 : 2); @@ -1261,7 +1264,8 @@ namespace build2 scope& rs (*bs.root_scope ()); otype ct (compile_type (t)); - cstrings args {cast (rs[config_x]).string ().c_str ()}; + const process_path& xc (cast (rs[x_path])); + cstrings args {xc.recall_string ()}; // Translate paths to relative (to working directory) ones. This // results in easier to read diagnostics. @@ -1418,7 +1422,7 @@ namespace build2 // bool filter (cid == "msvc"); - process pr (args.data (), 0, (filter ? -1 : 2)); + process pr (xc, args.data (), 0, (filter ? -1 : 2)); if (filter) { diff --git a/build2/cc/guess b/build2/cc/guess index d852a5c..c49fb4f 100644 --- a/build2/cc/guess +++ b/build2/cc/guess @@ -100,10 +100,14 @@ namespace build2 // derived for some toolchains. For example, i686-w64-mingw32-*-4.9. // // The bin_pattern is the binutils program pattern that could sometimes be - // derived for some toolchains. For example, i686-w64-mingw32-*. + // derived for some toolchains. For example, i686-w64-mingw32-*. If the + // pattern could not be derived, then it could contain a fallback search + // directory, in which case it will end with a directory separator but + // will not contain '*'. // struct compiler_info { + process_path path; compiler_id id; compiler_version version; string signature; diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx index 5001879..c2b552a 100644 --- a/build2/cc/guess.cxx +++ b/build2/cc/guess.cxx @@ -105,6 +105,11 @@ namespace build2 compiler_id id; string signature; string checksum; + process_path path; + + guess_result () = default; + guess_result (compiler_id&& i, string&& s) + : id (move (i)), signature (move (s)) {} bool empty () const {return id.empty ();} @@ -117,6 +122,8 @@ namespace build2 guess_result r; + process_path pp (run_search (xc, true)); + // Start with -v. This will cover gcc and clang. // // While icc also writes what may seem like something we can use to @@ -152,7 +159,7 @@ namespace build2 // gcc version 6.0.0 20160131 (experimental) (GCC) // if (l.compare (0, 4, "gcc ") == 0) - return guess_result {{"gcc", ""}, move (l), ""}; + return guess_result (compiler_id {"gcc", ""}, move (l)); // The Apple clang/clang++ -v output will have a line (currently // first) in the form: @@ -182,7 +189,7 @@ namespace build2 if (l.compare (0, 6, "Apple ") == 0 && (l.compare (6, 5, "LLVM ") == 0 || l.compare (6, 6, "clang ") == 0)) - return guess_result {{"clang", "apple"}, move (l), ""}; + return guess_result (compiler_id {"clang", "apple"}, move (l)); // The vanilla clang/clang++ -v output will have a line (currently // first) in the form: @@ -197,7 +204,7 @@ namespace build2 // clang version 3.7.0 (tags/RELEASE_370/final) // if (l.find ("clang ") != string::npos) - return guess_result {{"clang", ""}, move (l), ""}; + return guess_result (compiler_id {"clang", ""}, move (l)); return guess_result (); }; @@ -216,7 +223,7 @@ namespace build2 // Suppress all the compiler errors because we may be trying an // unsupported option. // - r = run (xc, "-v", f, false, false, &cs); + r = run (pp, "-v", f, false, false, &cs); if (!r.empty ()) r.checksum = cs.string (); @@ -239,12 +246,12 @@ namespace build2 // icc (ICC) 16.0.2 20160204 // if (l.find (" (ICC) ") != string::npos) - return guess_result {{"icc", ""}, move (l), ""}; + return guess_result (compiler_id {"icc", ""}, move (l)); return guess_result (); }; - r = run (xc, "--version", f, false); + r = run (pp, "--version", f, false); } // Finally try to run it without any options to detect msvc. @@ -271,12 +278,12 @@ namespace build2 // if (l.find ("Microsoft (R)") != string::npos && l.find ("C/C++") != string::npos) - return guess_result {{"msvc", ""}, move (l), ""}; + return guess_result (compiler_id {"msvc", ""}, move (l)); return guess_result (); }; - r = run (xc, f, false); + r = run (pp, f, false); } if (!r.empty ()) @@ -290,8 +297,12 @@ namespace build2 r = guess_result (); } else + { l5 ([&]{trace << xc << " is " << r.id << ": '" << r.signature << "'";}); + + r.path = move (pp); + } } else l4 ([&]{trace << "unable to determine compiler type of " << xc;}); @@ -463,6 +474,7 @@ namespace build2 pat = pattern (xc, xl == lang::c ? "cc" : "c++"); return compiler_info { + move (gr.path), move (gr.id), move (v), move (gr.signature), @@ -579,6 +591,7 @@ namespace build2 pat = pattern (xc, xl == lang::c ? "cc" : "c++"); return compiler_info { + move (gr.path), move (gr.id), move (v), move (gr.signature), @@ -798,6 +811,7 @@ namespace build2 sha256 cs (s); return compiler_info { + move (gr.path), move (gr.id), move (v), move (gr.signature), @@ -1011,6 +1025,7 @@ namespace build2 sha256 cs (s); return compiler_info { + move (gr.path), move (gr.id), move (v), move (gr.signature), @@ -1075,11 +1090,12 @@ namespace build2 // Derive binutils pattern unless this has already been done by the // compiler-specific code. // + + // When cross-compiling the whole toolchain is normally prefixed with + // the target triplet, e.g., x86_64-w64-mingw32-{gcc,g++,ar,ld}. + // if (r.bin_pattern.empty ()) { - // When cross-compiling the whole toolchain is normally prefixed with - // the target triplet, e.g., x86_64-w64-mingw32-{gcc,g++,ar,ld}. - // // BTW, for GCC we also get gcc-{ar,ranlib} which add support for the // LTO plugin though it seems more recent GNU binutils (2.25) are able // to load the plugin when needed automatically. So it doesn't seem we @@ -1103,6 +1119,17 @@ namespace build2 } } + // If we could not derive the pattern, then see if we can come up with a + // fallback search directory. + // + if (r.bin_pattern.empty ()) + { + const path& p (r.path.recall.empty () ? xc : r.path.recall); + + if (!p.simple ()) + r.bin_pattern = p.directory ().representation (); // Trailing slash. + } + return r; } diff --git a/build2/cc/link b/build2/cc/link index 8be386f..f6a16c0 100644 --- a/build2/cc/link +++ b/build2/cc/link @@ -56,10 +56,14 @@ namespace build2 // Alternative search logic for VC (msvc.cxx). // bin::liba* - msvc_search_static (const path&, const dir_path&, prerequisite&) const; + msvc_search_static (const process_path&, + const dir_path&, + prerequisite&) const; bin::libs* - msvc_search_shared (const path&, const dir_path&, prerequisite&) const; + msvc_search_shared (const process_path&, + const dir_path&, + prerequisite&) const; target* search_library (optional&, prerequisite&) const; diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index 61dd0ad..7a6b059 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -420,7 +420,7 @@ namespace build2 if (cid == "msvc") { scope& rs (*p.scope.root_scope ()); - const path& ld (cast (rs["config.bin.ld"])); + const process_path& ld (cast (rs["bin.ld.path"])); if (s == nullptr && !sn.empty ()) s = msvc_search_shared (ld, d, p); @@ -1135,10 +1135,12 @@ namespace build2 { path of (relative (manifest)); + const process_path& rc (cast (rs["bin.rc.path"])); + // @@ Would be good to add this to depdb (e.g,, rc changes). // const char* args[] = { - cast (rs["config.bin.rc"]).string ().c_str (), + rc.recall_string (), "--input-format=rc", "--output-format=coff", "-o", of.string ().c_str (), @@ -1149,7 +1151,7 @@ namespace build2 try { - process pr (args, -1); + process pr (rc, args, -1); try { @@ -1224,7 +1226,7 @@ namespace build2 // if (lt == otype::a) { - ranlib = rs["config.bin.ranlib"]; + ranlib = rs["bin.ranlib.path"]; if (ranlib && ranlib->empty ()) // @@ BC LT [null]. ranlib = lookup (); @@ -1481,11 +1483,12 @@ namespace build2 // path relt (relative (t.path ())); + const process_path* ld (nullptr); switch (lt) { case otype::a: { - args[0] = cast (rs["config.bin.ar"]).string ().c_str (); + ld = &cast (rs["bin.ar.path"]); if (cid == "msvc") { @@ -1516,7 +1519,7 @@ namespace build2 { // Using link.exe directly. // - args[0] = cast (rs["config.bin.ld"]).string ().c_str (); + ld = &cast (rs["bin.ld.path"]); args.push_back ("/NOLOGO"); if (lt == otype::s) @@ -1608,7 +1611,7 @@ namespace build2 } else { - args[0] = cast (rs[config_x]).string ().c_str (); + ld = &cast (rs[x_path]); // Add the option that triggers building a shared library and take // care of any extras (e.g., import library). @@ -1639,6 +1642,8 @@ namespace build2 } } + args[0] = ld->recall_string (); + for (target* pt: t.prerequisite_targets) { file* f; @@ -1709,7 +1714,7 @@ namespace build2 // bool filter (cid == "msvc" && lt != otype::a); - process pr (args.data (), 0, (filter ? -1 : 2)); + process pr (*ld, args.data (), 0, (filter ? -1 : 2)); if (filter) { @@ -1756,8 +1761,10 @@ namespace build2 if (ranlib) { + const process_path& rl (cast (ranlib)); + const char* args[] = { - cast (ranlib).string ().c_str (), + rl.recall_string (), relt.string ().c_str (), nullptr}; @@ -1766,7 +1773,7 @@ namespace build2 try { - process pr (args); + process pr (rl, args); if (!pr.wait ()) throw failed (); diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index d3849e1..4926fac 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -105,7 +105,7 @@ namespace build2 { dr << x << ' ' << project (r) << '@' << r.out_path () << '\n' - << " " << left << setw (11) << x << xc << '\n' + << " " << left << setw (11) << x << ci.path << '\n' << " id " << ci.id << '\n' << " version " << ci.version.string << '\n' << " major " << ci.version.major << '\n' @@ -129,6 +129,7 @@ namespace build2 } } + r.assign (x_path) = move (ci.path); r.assign (x_id) = ci.id.string (); r.assign (x_id_type) = move (ci.id.type); r.assign (x_id_variant) = move (ci.id.variant); diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx index 84020d0..84f5853 100644 --- a/build2/cc/msvc.cxx +++ b/build2/cc/msvc.cxx @@ -105,7 +105,7 @@ namespace build2 // Return otype::e if it is neither (which we quietly ignore). // static otype - library_type (const path& ld, const path& l) + library_type (const process_path& ld, const path& l) { // The are several reasonably reliable methods to tell whether it is a // static or import library. One is lib.exe /LIST -- if there aren't any @@ -125,7 +125,7 @@ namespace build2 // for libraries then we have bin.ld. So we will use the link.exe /DUMP // /ARCHIVEMEMBERS. // - const char* args[] = {ld.string ().c_str (), + const char* args[] = {ld.recall_string (), "/DUMP", // Must come first. "/NOLOGO", "/ARCHIVEMEMBERS", @@ -135,7 +135,7 @@ namespace build2 // Link.exe seem to always dump everything to stdout but just in case // redirect stderr to stdout. // - process pr (start_run (args, false)); + process pr (run_start (ld, args, false)); bool obj (false), dll (false); string s; @@ -192,11 +192,11 @@ namespace build2 } catch (const ifdstream::failure&) { - // Presumably the child process failed. Let finish_run() deal with + // Presumably the child process failed. Let run_finish() deal with // that. } - if (!finish_run (args, false, pr, s)) + if (!run_finish (args, false, pr, s)) return otype::e; if (obj && dll) @@ -217,7 +217,7 @@ namespace build2 template static T* msvc_search_library (const char* mod, - const path& ld, + const process_path& ld, const dir_path& d, prerequisite& p, otype lt, @@ -275,7 +275,7 @@ namespace build2 } liba* link:: - msvc_search_static (const path& ld, + msvc_search_static (const process_path& ld, const dir_path& d, prerequisite& p) const { @@ -302,7 +302,7 @@ namespace build2 } libs* link:: - msvc_search_shared (const path& ld, + msvc_search_shared (const process_path& ld, const dir_path& d, prerequisite& p) const { diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx index 467cf82..49947f5 100644 --- a/build2/cli/init.cxx +++ b/build2/cli/init.cxx @@ -58,7 +58,8 @@ namespace build2 v.insert ("config.cli", true); v.insert ("config.cli.options", true); - v.insert ("cli.options"); + v.insert ("cli.path"); + v.insert ("cli.options"); } // Register target types. @@ -91,20 +92,28 @@ namespace build2 { // config.cli // + process_path pp; - // Return version or empty string if unable to execute (e.g., the cli - // executable is not found). + // Return version or empty string if the cli executable is not found. // - auto test = [optional] (const path& cli) -> string + // @@ This needs some more thinking/cleanup. Specifically, what does + // it mean "cli not found"? Is it just not found in PATH? That plus + // was not able to execute (e.g., some shared libraries missing)? + // That plus cli that we found is something else? + // + auto test = [optional, &pp] (const path& cli) -> string { - const char* args[] = {cli.string ().c_str (), "--version", nullptr}; - - if (verb >= 3) - print_process (args); + const char* args[] = {nullptr, "--version", nullptr}; try { - process pr (args, 0, -1); // Open pipe to stdout. + pp = process::path_search (cli, true); // Can throw. + args[0] = pp.recall_string (); + + if (verb >= 3) + print_process (args); + + process pr (pp, args, 0, -1); // Open pipe to stdout. try { @@ -146,7 +155,7 @@ namespace build2 // found). So it would be good to redirect child's STDERR. // if (!optional) - error << "unable to execute " << cli << ": " << e.what (); + error << "unable to execute " << args[0] << ": " << e.what (); if (e.child ()) exit (1); @@ -216,9 +225,12 @@ namespace build2 if (unconf) dr << " cli " << "not found, leaving unconfigured"; else - dr << " cli " << cli << '\n' + dr << " cli " << pp << '\n' << " version " << ver; } + + if (!unconf) + rs.assign ("cli.path") = move (pp); } // Nothing else to do if we are unconfigured. diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx index 14134f1..7d64975 100644 --- a/build2/cxx/init.cxx +++ b/build2/cxx/init.cxx @@ -121,16 +121,17 @@ namespace build2 // Note: some overridable, some not. // - v.insert ("config.cxx", true), - v.insert ("config.cxx.poptions", true), - v.insert ("config.cxx.coptions", true), - v.insert ("config.cxx.loptions", true), - v.insert ("config.cxx.libs", true), - - v.insert ("cxx.poptions"), - v.insert ("cxx.coptions"), - v.insert ("cxx.loptions"), - v.insert ("cxx.libs"), + v.insert ("config.cxx", true), + v.insert ("config.cxx.poptions", true), + v.insert ("config.cxx.coptions", true), + v.insert ("config.cxx.loptions", true), + v.insert ("config.cxx.libs", true), + + v.insert ("cxx.path"), + v.insert ("cxx.poptions"), + v.insert ("cxx.coptions"), + v.insert ("cxx.loptions"), + v.insert ("cxx.libs"), v["cc.poptions"], v["cc.coptions"], diff --git a/build2/types b/build2/types index e3aa1b1..df54860 100644 --- a/build2/types +++ b/build2/types @@ -127,8 +127,12 @@ namespace build2 // // using butl::process; + using butl::process_path; using butl::process_error; + ostream& + operator<< (ostream&, const process_path&); // Print as recall[@effect]. + using butl::ifdstream; using butl::ofdstream; } diff --git a/build2/utility b/build2/utility index ee12545..3fb47b7 100644 --- a/build2/utility +++ b/build2/utility @@ -84,11 +84,23 @@ namespace build2 // diagnostics from the child process). Issue diagnostics and throw failed // in case of an error. // + process_path + run_search (const char*& args0); + + process_path + run_search (const path&, bool init, const dir_path& fallback = dir_path ()); + process - start_run (const char* args[], bool error); + run_start (const process_path&, const char* args[], bool error); + + inline process + run_start (const char* args[], bool error) + { + return run_start (run_search (args[0]), args, error); + } bool - finish_run (const char* args[], bool error, process&, const string&); + run_finish (const char* args[], bool error, process&, const string&); // Start the process as above and then call the specified function on each // trimmed line of the output until it returns a non-empty object T (tested @@ -106,7 +118,8 @@ namespace build2 // template T - run (const char* args[], + run (const process_path&, + const char* args[], T (*) (string&), bool error = true, bool ignore_exit = false, @@ -114,6 +127,19 @@ namespace build2 template inline T + run (const char* args[], + T (*f) (string&), + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr) + { + return run (run_search (args[0]), args, f, error, ignore_exit, checksum); + } + + // run + // + template + inline T run (const path& prog, T (*f) (string&), bool error = true, @@ -126,6 +152,20 @@ namespace build2 template inline T + run (const process_path& pp, + T (*f) (string&), + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr) + { + const char* args[] = {pp.recall_string (), nullptr}; + return run (pp, args, f, error, ignore_exit, checksum); + } + + // run + // + template + inline T run (const path& prog, const char* arg, T (*f) (string&), @@ -137,6 +177,19 @@ namespace build2 return run (args, f, error, ignore_exit, checksum); } + template + inline T + run (const process_path& pp, + const char* arg, + T (*f) (string&), + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr) + { + const char* args[] = {pp.recall_string (), arg, nullptr}; + return run (pp, args, f, error, ignore_exit, checksum); + } + // Empty string and path. // extern const std::string empty_string; diff --git a/build2/utility.cxx b/build2/utility.cxx index 8ebfe06..dc862c6 100644 --- a/build2/utility.cxx +++ b/build2/utility.cxx @@ -28,6 +28,17 @@ namespace build2 : p.representation ()); } + ostream& + operator<< (ostream& os, const process_path& p) + { + os << p.recall_string (); + + if (!p.effect.empty ()) + os << '@' << p.effect.string (); // Suppress relative(). + + return os; + } + // // // @@ -69,21 +80,47 @@ namespace build2 return l; } + process_path + run_search (const char*& args0) + try + { + return process::path_search (args0); + } + catch (const process_error& e) + { + error << "unable to execute " << args0 << ": " << e.what (); + throw failed (); + } + + process_path + run_search (const path& f, bool init, const dir_path& fallback) + try + { + return process::path_search (f, init, fallback); + } + catch (const process_error& e) + { + error << "unable execute " << f << ": " << e.what (); + throw failed (); + } + process - start_run (const char* args[], bool err) + run_start (const process_path& pp, const char* args[], bool err) { + assert (args[0] == pp.recall_string ()); + if (verb >= 3) print_process (args); try { - return process (args, 0, -1, (err ? 2 : 1)); + return process (pp, args, 0, -1, (err ? 2 : 1)); } catch (const process_error& e) { if (e.child ()) { - // Note: finish_run() expects this exact message. + // Note: run_finish() expects this exact message. // cerr << "unable to execute " << args[0] << ": " << e.what () << endl; exit (1); @@ -96,7 +133,7 @@ namespace build2 }; bool - finish_run (const char* args[], bool err, process& pr, const string& l) + run_finish (const char* args[], bool err, process& pr, const string& l) try { if (pr.wait ()) @@ -111,7 +148,7 @@ namespace build2 // want to let through is the inability to execute the program itself. // We cannot reserve a special exit status to signal this so we will // just have to compare the output. This particular situation will - // result in a single error line printed by start_run() above. + // result in a single error line printed by run_start() above. // if (l.compare (0, 18, "unable to execute ") == 0) fail << l; diff --git a/build2/utility.txx b/build2/utility.txx index 3e29db6..7256547 100644 --- a/build2/utility.txx +++ b/build2/utility.txx @@ -6,13 +6,14 @@ namespace build2 { template T - run (const char* args[], + run (const process_path& pp, + const char* args[], T (*f) (string&), bool err, bool ignore_exit, sha256* checksum) { - process pr (start_run (args, err)); + process pr (run_start (pp, args, err)); T r; string l; // Last line of output. @@ -35,10 +36,10 @@ namespace build2 } catch (const ifdstream::failure&) { - // Presumably the child process failed. Let finish_run() deal with that. + // Presumably the child process failed. Let run_finish() deal with that. } - if (!(finish_run (args, err, pr, l) || ignore_exit)) + if (!(run_finish (args, err, pr, l) || ignore_exit)) r = T (); return r; diff --git a/build2/variable b/build2/variable index d6be976..1248bd3 100644 --- a/build2/variable +++ b/build2/variable @@ -589,6 +589,28 @@ namespace build2 static const build2::value_type value_type; }; + // process_path + // + // Note that instances that we store always have non-empty recall and + // initial is its shallow copy. + // + template <> + struct value_traits + { + static_assert (sizeof (process_path) <= value::size_, "insufficient space"); + + // This one is represented as a @-pair of names. As a result it cannot + // be stored in a container. + // + static process_path convert (name&&, name*); + static void assign (value&, process_path&&); + static int compare (const process_path&, const process_path&); + static bool empty (const process_path& x) {return x.empty ();} + + static const char* const type_name; + static const build2::value_type value_type; + }; + // vector // template diff --git a/build2/variable.cxx b/build2/variable.cxx index eec0573..54b61b7 100644 --- a/build2/variable.cxx +++ b/build2/variable.cxx @@ -662,6 +662,104 @@ namespace build2 &default_empty }; + // process_path value + // + process_path value_traits:: + convert (name&& n, name* r) + { + path rp (move (n.dir)); + if (rp.empty ()) + rp = path (move (n.value)); + else + rp /= n.value; + + path ep; + if (r != nullptr) + { + ep = move (r->dir); + if (ep.empty ()) + ep = path (move (r->value)); + else + ep /= r->value; + } + + process_path pp (nullptr, move (rp), move (ep)); + pp.initial = pp.recall.string ().c_str (); + return pp; + } + + void + process_path_copy_ctor (value& l, const value& r, bool m) + { + const auto& rhs (r.as ()); + + if (m) + new (&l.data_) process_path (move (const_cast (rhs))); + else + { + auto& lhs ( + *new (&l.data_) process_path ( + nullptr, path (rhs.recall), path (rhs.effect))); + lhs.initial = lhs.recall.string ().c_str (); + } + } + + void + process_path_copy_assign (value& l, const value& r, bool m) + { + auto& lhs (l.as ()); + const auto& rhs (r.as ()); + + if (m) + lhs = move (const_cast (rhs)); + else + { + lhs.recall = rhs.recall; + lhs.effect = rhs.effect; + lhs.initial = lhs.recall.string ().c_str (); + } + } + + static names_view + process_path_reverse (const value& v, names& s) + { + auto& pp (v.as ()); + s.reserve (pp.effect.empty () ? 1 : 2); + + s.push_back (name (pp.recall.directory (), + string (), + pp.recall.leaf ().string ())); + + if (!pp.effect.empty ()) + { + s.back ().pair = '@'; + s.push_back (name (pp.effect.directory (), + string (), + pp.effect.leaf ().string ())); + } + + return s; + } + + const char* const value_traits::type_name = "process_path"; + + const value_type value_traits::value_type + { + type_name, + sizeof (process_path), + nullptr, // No base. + &default_dtor, + &process_path_copy_ctor, + &process_path_copy_assign, + &simple_assign, // Allow empty values. + nullptr, // Append not supported. + nullptr, // Prepend not supported. + &process_path_reverse, + nullptr, // No cast (cast data_ directly). + &simple_compare, + &default_empty + }; + // variable_pool // const variable& variable_pool:: diff --git a/build2/variable.ixx b/build2/variable.ixx index 4b699a6..393a796 100644 --- a/build2/variable.ixx +++ b/build2/variable.ixx @@ -476,6 +476,35 @@ namespace build2 return l.compare (r); } + // process_path value + // + inline void value_traits:: + assign (value& v, process_path&& x) + { + // Convert the value to its "self-sufficient" form. + // + if (x.recall.empty ()) + x.recall = path (x.initial); + + x.initial = x.recall.string ().c_str (); + + if (v) + v.as () = move (x); + else + new (&v.data_) process_path (move (x)); + } + + inline int value_traits:: + compare (const process_path& x, const process_path& y) + { + int r (x.recall.compare (y.recall)); + + if (r == 0) + r = x.effect.compare (y.effect); + + return r; + } + // vector value // template -- cgit v1.1