From 4d43fc686427252367576bb1a37724bb45958358 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 7 Oct 2019 09:41:13 +0200 Subject: Pass MSVC system library search paths to linker if LIB envvar is unset --- libbuild2/c/init.cxx | 10 ++-- libbuild2/cc/guess.cxx | 44 +++++++++++++++-- libbuild2/cc/guess.hxx | 5 ++ libbuild2/cc/link-rule.cxx | 57 ++++++++++++++++----- libbuild2/cc/module.cxx | 121 +++++++++++++++++++++++++-------------------- libbuild2/cc/module.hxx | 9 ++-- libbuild2/cc/msvc.cxx | 22 ++++++++- libbuild2/cxx/init.cxx | 10 ++-- 8 files changed, 193 insertions(+), 85 deletions(-) diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx index 337df09..4d59eb2 100644 --- a/libbuild2/c/init.cxx +++ b/libbuild2/c/init.cxx @@ -323,11 +323,11 @@ namespace build2 "c.install", "c.uninstall", - cm.ci_->id.type, - cm.ci_->id.variant, - cm.ci_->class_, - cm.ci_->version.major, - cm.ci_->version.minor, + cm.x_info->id.type, + cm.x_info->id.variant, + cm.x_info->class_, + cm.x_info->version.major, + cm.x_info->version.minor, cast (rs[cm.x_path]), cast (rs[cm.x_target]), diff --git a/libbuild2/cc/guess.cxx b/libbuild2/cc/guess.cxx index 9cfb1c8..5b0072a 100644 --- a/libbuild2/cc/guess.cxx +++ b/libbuild2/cc/guess.cxx @@ -951,7 +951,9 @@ namespace build2 move (bpat), move (rt), move (csl), - move (xsl)}; + move (xsl), + nullopt, + nullopt}; } static compiler_info @@ -1142,7 +1144,9 @@ namespace build2 "", move (rt), move (csl), - move (xsl)}; + move (xsl), + nullopt, + nullopt}; } struct clang_msvc_info @@ -1339,6 +1343,9 @@ namespace build2 return r; } + const char* + msvc_cpu (const string&); // msvc.cxx + static compiler_info guess_clang (const char* xm, lang xl, @@ -1469,6 +1476,8 @@ namespace build2 // For Clang on Windows targeting MSVC we remap the target to match // MSVC's. // + optional sys_lib_dirs; + if (tt.system == "windows-msvc") { // Note that currently there is no straightforward way to determine @@ -1501,6 +1510,29 @@ namespace build2 gr.signature += " MSVC version "; gr.signature += mi.msvc_ver; + + // Come up with the system library search paths. Ideally we would want + // to extract this from Clang and -print-search-paths would have been + // the natural way for Clang to report it. But no luck. + // + dir_paths ds; + const char* cpu (msvc_cpu (tt.cpu)); + + ds.push_back ((dir_path (mi.msvc_dir) /= "lib") /= cpu); + + // This path structure only appeared in Platform SDK 10 (if anyone + // wants to use anything older, they will just have to use the MSVC + // command prompt). + // + if (!mi.psdk_ver.empty ()) + { + dir_path d ((dir_path (mi.psdk_dir) /= "Lib") /= mi.psdk_ver); + + ds.push_back ((dir_path (d) /= "ucrt") /= cpu); + ds.push_back ((dir_path (d) /= "um" ) /= cpu); + } + + sys_lib_dirs = move (ds); } // Derive the toolchain pattern. Try clang/clang++, the gcc/g++ alias, @@ -1595,7 +1627,9 @@ namespace build2 "", move (rt), move (csl), - move (xsl)}; + move (xsl), + move (sys_lib_dirs), + nullopt}; } static compiler_info @@ -1884,7 +1918,9 @@ namespace build2 "", move (rt), move (csl), - move (xsl)}; + move (xsl), + nullopt, + nullopt}; } // Compiler checks can be expensive (we often need to run the compiler diff --git a/libbuild2/cc/guess.hxx b/libbuild2/cc/guess.hxx index 3677cc7..a8784fc 100644 --- a/libbuild2/cc/guess.hxx +++ b/libbuild2/cc/guess.hxx @@ -215,6 +215,11 @@ namespace build2 string runtime; string c_stdlib; string x_stdlib; + + // System library/header search paths, if extracted at the guess stage. + // + optional sys_lib_dirs; + optional sys_inc_dirs; }; // In a sense this is analagous to the language standard which we handle diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 5341a2e..cd31058 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -2105,11 +2105,22 @@ namespace build2 // kind of a hybrid). // cstrings args {nullptr}; // Reserve one for config.bin.ar/config.x. + strings sargs; // Argument tail with storage. - // Storage. + // Stored args. // string arg1, arg2; - strings sargs; + strings sargs1; + + // Shallow-copy over stored args to args. Note that this must only be + // done once we are finished appending to stored args because of + // potential reallocations. + // + auto append_args = [&args] (const strings& sargs) + { + for (const string& a: sargs) + args.push_back (a.c_str ()); + }; if (lt.static_library ()) { @@ -2190,14 +2201,36 @@ namespace build2 // Extra system library dirs (last). // - // @@ /LIBPATH:, not /LIBPATH - // assert (sys_lib_dirs_extra <= sys_lib_dirs.size ()); - append_option_values ( - args, - cclass == compiler_class::msvc ? "/LIBPATH:" : "-L", - sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (), - [] (const dir_path& d) {return d.string ().c_str ();}); + + if (tsys == "win32-msvc") + { + // If we have no LIB environment variable set, then we add all of + // them. But we want extras to come first. + // + auto b (sys_lib_dirs.begin ()); + auto m (b + sys_lib_dirs_extra); + auto e (sys_lib_dirs.end ()); + + for (auto i (m); i != e; ++i) + sargs1.push_back ("/LIBPATH:" + i->string ()); + + if (!getenv ("LIB")) + { + for (auto i (b); i != m; ++i) + sargs1.push_back ("/LIBPATH:" + i->string ()); + } + + append_args (sargs1); + } + else + { + append_option_values ( + args, + "-L", + sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (), + [] (const dir_path& d) {return d.string ().c_str ();}); + } // Handle soname/rpath. // @@ -2649,11 +2682,9 @@ namespace build2 if (!manifest.empty () && tsys == "mingw32") sargs.push_back (relative (manifest).string ()); - // Shallow-copy sargs to args. Why not do it as we go along pushing into - // sargs? Because of potential reallocations in sargs. + // Shallow-copy sargs over to args. // - for (const string& a: sargs) - args.push_back (a.c_str ()); + append_args (sargs); if (!lt.static_library ()) { diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx index 39fd7d5..f3ce32b 100644 --- a/libbuild2/cc/module.cxx +++ b/libbuild2/cc/module.cxx @@ -116,7 +116,7 @@ namespace build2 // Figure out which compiler we are dealing with, its target, etc. // - ci_ = &build2::cc::guess ( + x_info = &build2::cc::guess ( x, x_lang, cast (*p.first), @@ -130,7 +130,7 @@ namespace build2 cast_null (rs[config_c_loptions]), cast_null (rs[config_x_loptions])); - const compiler_info& ci (*ci_); + const compiler_info& xi (*x_info); // Split/canonicalize the target. First see if the user asked us to // use config.sub. @@ -143,14 +143,14 @@ namespace build2 { ct = run (3, *config_sub, - ci.target.c_str (), + xi.target.c_str (), [] (string& l, bool) {return move (l);}); l5 ([&]{trace << "config.sub target: '" << ct << "'";}); } try { - tt = target_triplet (ct.empty () ? ci.target : ct); + tt = target_triplet (ct.empty () ? xi.target : ct); l5 ([&]{trace << "canonical target: '" << tt.string () << "'; " << "class: " << tt.class_;}); } @@ -160,24 +160,24 @@ namespace build2 // help us out. // fail << "unable to parse " << x_lang << " compiler target '" - << ci.target << "': " << e << + << xi.target << "': " << e << info << "consider using the --config-sub option"; } } // Assign values to variables that describe the compiler. // - rs.assign (x_id) = ci.id.string (); - rs.assign (x_id_type) = to_string (ci.id.type); - rs.assign (x_id_variant) = ci.id.variant; + rs.assign (x_id) = xi.id.string (); + rs.assign (x_id_type) = to_string (xi.id.type); + rs.assign (x_id_variant) = xi.id.variant; - rs.assign (x_class) = to_string (ci.class_); + rs.assign (x_class) = to_string (xi.class_); - rs.assign (x_version) = ci.version.string; - rs.assign (x_version_major) = ci.version.major; - rs.assign (x_version_minor) = ci.version.minor; - rs.assign (x_version_patch) = ci.version.patch; - rs.assign (x_version_build) = ci.version.build; + rs.assign (x_version) = xi.version.string; + rs.assign (x_version_major) = xi.version.major; + rs.assign (x_version_minor) = xi.version.minor; + rs.assign (x_version_patch) = xi.version.patch; + rs.assign (x_version_build) = xi.version.build; // Also enter as x.target.{cpu,vendor,system,version,class} for // convenience of access. @@ -190,10 +190,10 @@ namespace build2 rs.assign (x_target) = move (tt); - rs.assign (x_pattern) = ci.pattern; + rs.assign (x_pattern) = xi.pattern; if (!x_stdlib.alias (c_stdlib)) - rs.assign (x_stdlib) = ci.x_stdlib; + rs.assign (x_stdlib) = xi.x_stdlib; new_ = p.second; @@ -211,11 +211,11 @@ namespace build2 h.assign ("config.cc.hinter") = string (x); h.assign ("config.cc.target") = cast (rs[x_target]); - if (!ci.pattern.empty ()) - h.assign ("config.cc.pattern") = ci.pattern; + if (!xi.pattern.empty ()) + h.assign ("config.cc.pattern") = xi.pattern; - h.assign (c_runtime) = ci.runtime; - h.assign (c_stdlib) = ci.c_stdlib; + h.assign (c_runtime) = xi.runtime; + h.assign (c_stdlib) = xi.c_stdlib; load_module (rs, rs, "cc.core.guess", loc, false, h); } @@ -264,11 +264,11 @@ namespace build2 "target"); check (cast (rs["cc.runtime"]), - ci.runtime, + xi.runtime, "runtime"); check (cast (rs["cc.stdlib"]), - ci.c_stdlib, + xi.c_stdlib, "c standard library"); } } @@ -288,7 +288,7 @@ namespace build2 { tracer trace (x, "config_init"); - const compiler_info& ci (*ci_); + const compiler_info& xi (*x_info); const target_triplet& tt (cast (rs[x_target])); // config.x.std overrides x.std @@ -307,7 +307,7 @@ namespace build2 // Translate x_std value (if any) to the compiler option(s) (if any). // - tstd = translate_std (ci, rs, v); + tstd = translate_std (xi, rs, v); } // Extract system header/library search paths from the compiler and @@ -316,19 +316,32 @@ namespace build2 dir_paths lib_dirs; dir_paths inc_dirs; - switch (ci.class_) + if (xi.sys_lib_dirs) + lib_dirs = *xi.sys_lib_dirs; + else { - case compiler_class::gcc: + switch (xi.class_) { - //@@ CLANG-MSVC: libraries don't contain MSVC stuff. - lib_dirs = gcc_library_search_paths (ci.path, rs); - inc_dirs = gcc_header_search_paths (ci.path, rs); + case compiler_class::gcc: + lib_dirs = gcc_library_search_paths (xi.path, rs); + break; + case compiler_class::msvc: + lib_dirs = msvc_library_search_paths (xi.path, rs); break; } - case compiler_class::msvc: + } + + if (xi.sys_inc_dirs) + inc_dirs = *xi.sys_inc_dirs; + else + { + switch (xi.class_) { - lib_dirs = msvc_library_search_paths (ci.path, rs); - inc_dirs = msvc_header_search_paths (ci.path, rs); + case compiler_class::gcc: + inc_dirs = gcc_header_search_paths (xi.path, rs); + break; + case compiler_class::msvc: + inc_dirs = msvc_header_search_paths (xi.path, rs); break; } } @@ -426,34 +439,34 @@ namespace build2 { dr << x << ' ' << project (rs) << '@' << rs << '\n' - << " " << left << setw (11) << x << ci.path << '\n' - << " id " << ci.id << '\n' - << " version " << ci.version.string << '\n' - << " major " << ci.version.major << '\n' - << " minor " << ci.version.minor << '\n' - << " patch " << ci.version.patch << '\n'; + << " " << left << setw (11) << x << xi.path << '\n' + << " id " << xi.id << '\n' + << " version " << xi.version.string << '\n' + << " major " << xi.version.major << '\n' + << " minor " << xi.version.minor << '\n' + << " patch " << xi.version.patch << '\n'; } - if (!ci.version.build.empty ()) + if (!xi.version.build.empty ()) { - dr << " build " << ci.version.build << '\n'; + dr << " build " << xi.version.build << '\n'; } { const string& ct (tt.string ()); // Canonical target. - dr << " signature " << ci.signature << '\n' - << " checksum " << ci.checksum << '\n' + dr << " signature " << xi.signature << '\n' + << " checksum " << xi.checksum << '\n' << " target " << ct; - if (ct != ci.original_target) - dr << " (" << ci.original_target << ")"; + if (ct != xi.original_target) + dr << " (" << xi.original_target << ")"; - dr << "\n runtime " << ci.runtime - << "\n stdlib " << ci.x_stdlib; + dr << "\n runtime " << xi.runtime + << "\n stdlib " << xi.x_stdlib; if (!x_stdlib.alias (c_stdlib)) - dr << "\n c stdlib " << ci.c_stdlib; + dr << "\n c stdlib " << xi.c_stdlib; } if (!tstd.empty ()) @@ -462,9 +475,9 @@ namespace build2 for (const string& o: tstd) dr << ' ' << o; } - if (!ci.pattern.empty ()) // Note: bin_pattern printed by bin + if (!xi.pattern.empty ()) // Note: bin_pattern printed by bin { - dr << "\n pattern " << ci.pattern; + dr << "\n pattern " << xi.pattern; } if (verb >= 3 && !inc_dirs.empty ()) @@ -490,12 +503,12 @@ namespace build2 } } - rs.assign (x_path) = process_path (ci.path, false /* init */); + rs.assign (x_path) = process_path (xi.path, false /* init */); rs.assign (x_sys_lib_dirs) = move (lib_dirs); rs.assign (x_sys_inc_dirs) = move (inc_dirs); - rs.assign (x_signature) = ci.signature; - rs.assign (x_checksum) = ci.checksum; + rs.assign (x_signature) = xi.signature; + rs.assign (x_checksum) = xi.checksum; // config.x.{p,c,l}options // config.x.libs @@ -550,8 +563,8 @@ namespace build2 { variable_map h (rs.ctx); - if (!ci.bin_pattern.empty ()) - h.assign ("config.bin.pattern") = ci.bin_pattern; + if (!xi.bin_pattern.empty ()) + h.assign ("config.bin.pattern") = xi.bin_pattern; load_module (rs, rs, "cc.core.config", loc, false, h); } diff --git a/libbuild2/cc/module.hxx b/libbuild2/cc/module.hxx index 43670c3..77fe55c 100644 --- a/libbuild2/cc/module.hxx +++ b/libbuild2/cc/module.hxx @@ -52,10 +52,13 @@ namespace build2 translate_std (const compiler_info&, scope&, const string*) const = 0; strings tstd; - size_t sys_lib_dirs_extra; // First extra path (size if none). - size_t sys_inc_dirs_extra; // First extra path (size if none). - const compiler_info* ci_; + const compiler_info* x_info; + + // Temporary storage for data::sys_*_dirs_extra. + // + size_t sys_lib_dirs_extra; + size_t sys_inc_dirs_extra; private: // Defined in gcc.cxx. diff --git a/libbuild2/cc/msvc.cxx b/libbuild2/cc/msvc.cxx index 2d53b69..e7251ac 100644 --- a/libbuild2/cc/msvc.cxx +++ b/libbuild2/cc/msvc.cxx @@ -28,6 +28,25 @@ namespace build2 { using namespace bin; + // Translate the target triplet CPU to MSVC CPU (used in directory names, + // etc). + // + const char* + msvc_cpu (const string& cpu) + { + const char* m (cpu == "i386" || cpu == "i686" ? "x86" : + cpu == "x86_64" ? "x64" : + cpu == "arm" ? "arm" : + cpu == "arm64" ? "arm64" : + nullptr); + + if (m == nullptr) + fail << "unable to translate target triplet CPU " << cpu + << " to MSVC CPU"; + + return m; + } + // Translate the target triplet CPU to lib.exe/link.exe /MACHINE option. // const char* @@ -40,7 +59,8 @@ namespace build2 nullptr); if (m == nullptr) - fail << "unable to translate CPU " << cpu << " to /MACHINE"; + fail << "unable to translate target triplet CPU " << cpu + << " to /MACHINE"; return m; } diff --git a/libbuild2/cxx/init.cxx b/libbuild2/cxx/init.cxx index d4e3151..3345485 100644 --- a/libbuild2/cxx/init.cxx +++ b/libbuild2/cxx/init.cxx @@ -630,11 +630,11 @@ namespace build2 "cxx.install", "cxx.uninstall", - cm.ci_->id.type, - cm.ci_->id.variant, - cm.ci_->class_, - cm.ci_->version.major, - cm.ci_->version.minor, + cm.x_info->id.type, + cm.x_info->id.variant, + cm.x_info->class_, + cm.x_info->version.major, + cm.x_info->version.minor, cast (rs[cm.x_path]), cast (rs[cm.x_target]), -- cgit v1.1