Index: include/clang/Driver/Job.h =================================================================== --- include/clang/Driver/Job.h +++ include/clang/Driver/Job.h @@ -69,6 +69,9 @@ /// file std::string ResponseFileFlag; + /// See Command::setEnvironment + std::vector Environment; + /// When a response file is needed, we try to put most arguments in an /// exclusive file, while others remains as regular command line arguments. /// This functions fills a vector with the regular command line arguments, @@ -111,6 +114,20 @@ InputFileList = std::move(List); } + /// \brief Sets the environment to be used by the new process. + /// \param NewEnvironment A vector of environment variables to be + /// moved into place. The vector will have a null-terminator + /// appended to it; a potential copy can be avoided by ensuring + /// the vector has space for it before calling this function. + /// \remark If the environment remains unset, then the environment + /// from this process will be used. + /// \sa Command::setEnvironment(const std::vector &) + void setEnvironment(std::vector &&NewEnvironment); + + /// An overload of the above function. It optimises the case where + /// the given vector is to be copied in as opposed to moved. + void setEnvironment(const std::vector &NewEnvironment); + const char *getExecutable() const { return Executable; } const llvm::opt::ArgStringList &getArguments() const { return Arguments; } Index: lib/Driver/Job.cpp =================================================================== --- lib/Driver/Job.cpp +++ lib/Driver/Job.cpp @@ -301,16 +301,39 @@ ResponseFileFlag += FileName; } +void Command::setEnvironment(std::vector &&NewEnvironment) { + Environment = std::move(NewEnvironment); + Environment.push_back(nullptr); +} + +void Command::setEnvironment(const std::vector &NewEnvironment) { + // Reserve space for the null-terminator before copying the input vector. + // This removes the possibility of needing a second copy when appending + // the null-terminator. + Environment.reserve(NewEnvironment.size() + 1); + Environment.assign(NewEnvironment.begin(), NewEnvironment.end()); + Environment.push_back(nullptr); +} + int Command::Execute(const StringRef **Redirects, std::string *ErrMsg, bool *ExecutionFailed) const { SmallVector Argv; + const char **Envp; + if (Environment.empty()) { + Envp = nullptr; + } else { + assert(Environment.back() == nullptr + && "Environment vector should be null-terminated by now"); + Envp = const_cast(Environment.data()); + } + if (ResponseFile == nullptr) { Argv.push_back(Executable); Argv.append(Arguments.begin(), Arguments.end()); Argv.push_back(nullptr); - return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr, + return llvm::sys::ExecuteAndWait(Executable, Argv.data(), Envp, Redirects, /*secondsToWait*/ 0, /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); @@ -337,7 +360,7 @@ return -1; } - return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr, + return llvm::sys::ExecuteAndWait(Executable, Argv.data(), Envp, Redirects, /*secondsToWait*/ 0, /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); } Index: lib/Driver/ToolChains/MSVC.cpp =================================================================== --- lib/Driver/ToolChains/MSVC.cpp +++ lib/Driver/ToolChains/MSVC.cpp @@ -34,7 +34,7 @@ // Include the necessary headers to interface with the Windows registry and // environment. #if defined(LLVM_ON_WIN32) -#define USE_WIN32 + #define USE_WIN32 #endif #ifdef USE_WIN32 @@ -47,20 +47,20 @@ #endif #ifdef _MSC_VER -// Don't support SetupApi on MinGW. -#define USE_MSVC_SETUP_API + // Don't support SetupApi on MinGW. + #define USE_MSVC_SETUP_API -// Make sure this comes before MSVCSetupApi.h -#include + // Make sure this comes before MSVCSetupApi.h + #include -#include "MSVCSetupApi.h" -#include "llvm/Support/COM.h" -_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); -_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); -_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); -_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); -_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); -_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); + #include "MSVCSetupApi.h" + #include "llvm/Support/COM.h" + _COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); + _COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); + _COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); + _COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); + _COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); + _COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); #endif using namespace clang::driver; @@ -446,6 +446,8 @@ TC.addProfileRTLibs(Args, CmdArgs); + std::vector Environment; + // We need to special case some linker paths. In the case of lld, we need to // translate 'lld' into 'lld-link', and in the case of the regular msvc // linker, we need to use a special search algorithm. @@ -459,6 +461,68 @@ // from the program PATH, because other environments like GnuWin32 install // their own link.exe which may come first. linkPath = FindVisualStudioExecutable(TC, "link.exe"); + +#ifdef USE_WIN32 + // When cross-compiling with VS2017 or newer, link.exe expects to have + // its containing bin directory at the top of PATH, followed by the + // native target bin directory. + // e.g. when compiling for x86 on an x64 host, PATH should start with: + // /bin/HostX64/x86;/bin/HostX64/x64 + if (TC.getIsVS2017OrNewer() && + llvm::Triple(llvm::sys::getProcessTriple()).getArch() != TC.getArch()) { + auto HostArch = llvm::Triple(llvm::sys::getProcessTriple()).getArch(); + + auto EnvBlockWide = std::unique_ptr( + GetEnvironmentStringsW(), FreeEnvironmentStringsW); + if (!EnvBlockWide) + goto SkipSettingEnvironment; + + size_t EnvCount = 0; + size_t EnvBlockLen = 0; + while (EnvBlockWide[EnvBlockLen] != L'\0') { + ++EnvCount; + EnvBlockLen += std::wcslen(&EnvBlockWide[EnvBlockLen]) + + 1/*string null-terminator*/; + } + ++EnvBlockLen; // add the block null-terminator + + std::string EnvBlock; + if (!llvm::convertUTF16ToUTF8String( + llvm::ArrayRef(reinterpret_cast(EnvBlockWide.get()), + EnvBlockLen * sizeof(EnvBlockWide[0])), + EnvBlock)) + goto SkipSettingEnvironment; + + Environment.reserve(EnvCount + 1); + + // Now loop over each string in the block and copy them into the + // environment vector, adjusting the PATH variable as needed when we + // find it. + for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) { + llvm::StringRef EnvVar(Cursor); + if (EnvVar.startswith_lower("path=")) { + using SubDirectoryType = toolchains::MSVCToolChain::SubDirectoryType; + constexpr size_t PrefixLen = 5; // strlen("path=") + Environment.push_back(Args.MakeArgString( + EnvVar.substr(0, PrefixLen) + + TC.getSubDirectoryPath(SubDirectoryType::Bin) + + llvm::Twine(llvm::sys::EnvPathSeparator) + + TC.getSubDirectoryPath(SubDirectoryType::Bin, HostArch) + + (EnvVar.size() > PrefixLen + ? llvm::Twine(llvm::sys::EnvPathSeparator) + + EnvVar.substr(PrefixLen) + : "") + )); + } else { + Environment.push_back(Args.MakeArgString(EnvVar)); + } + Cursor += EnvVar.size() + 1/*null-terminator*/; + } + } + SkipSettingEnvironment: + ; +#endif } else { linkPath = Linker; llvm::sys::path::replace_extension(linkPath, "exe"); @@ -465,8 +529,11 @@ linkPath = TC.GetProgramPath(linkPath.c_str()); } - const char *Exec = Args.MakeArgString(linkPath); - C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); + auto LinkCmd = llvm::make_unique( + JA, *this, Args.MakeArgString(linkPath), CmdArgs, Inputs); + if (!Environment.empty()) + LinkCmd->setEnvironment(std::move(Environment)); + C.addCommand(std::move(LinkCmd)); } void visualstudio::Compiler::ConstructJob(Compilation &C, const JobAction &JA,