Move MSVC environment setup to driver/tool.cpp [NFC]

This commit is contained in:
Martin 2017-03-13 23:03:33 +01:00
parent cab165f3d8
commit 0842277572
3 changed files with 218 additions and 208 deletions

View file

@ -25,16 +25,10 @@
#include "llvm/Linker/Linker.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#if _WIN32
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ConvertUTF.h"
#include <Windows.h>
#endif
#include <algorithm>
@ -498,205 +492,6 @@ static int linkObjToBinaryGcc(bool sharedLib, bool fullyStatic) {
//////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
namespace windows {
bool needsQuotes(const llvm::StringRef &arg) {
return // not already quoted
!(arg.size() > 1 && arg[0] == '"' &&
arg.back() == '"') && // empty or min 1 space or min 1 double quote
(arg.empty() || arg.find(' ') != arg.npos || arg.find('"') != arg.npos);
}
size_t countPrecedingBackslashes(llvm::StringRef arg, size_t index) {
size_t count = 0;
for (size_t i = index - 1; i >= 0; --i) {
if (arg[i] != '\\')
break;
++count;
}
return count;
}
std::string quoteArg(llvm::StringRef arg) {
if (!needsQuotes(arg))
return arg;
std::string quotedArg;
quotedArg.reserve(3 + 2 * arg.size()); // worst case
quotedArg.push_back('"');
const size_t argLength = arg.size();
for (size_t i = 0; i < argLength; ++i) {
if (arg[i] == '"') {
// Escape all preceding backslashes (if any).
// Note that we *don't* need to escape runs of backslashes that don't
// precede a double quote! See MSDN:
// http://msdn.microsoft.com/en-us/library/17w5ykft%28v=vs.85%29.aspx
quotedArg.append(countPrecedingBackslashes(arg, i), '\\');
// Escape the double quote.
quotedArg.push_back('\\');
}
quotedArg.push_back(arg[i]);
}
// Make sure our final double quote doesn't get escaped by a trailing
// backslash.
quotedArg.append(countPrecedingBackslashes(arg, argLength), '\\');
quotedArg.push_back('"');
return quotedArg;
}
int executeAndWait(const char *commandLine) {
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
DWORD exitCode;
#if UNICODE
std::wstring wcommandLine;
if (!llvm::ConvertUTF8toWide(commandLine, wcommandLine))
return -3;
auto cmdline = const_cast<wchar_t *>(wcommandLine.data());
#else
auto cmdline = const_cast<char *>(commandLine);
#endif
// according to MSDN, only CreateProcessW (unicode) may modify the passed
// command line
if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0,
NULL, NULL, &si, &pi)) {
exitCode = -1;
} else {
if (WaitForSingleObject(pi.hProcess, INFINITE) != 0 ||
!GetExitCodeProcess(pi.hProcess, &exitCode))
exitCode = -2;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return exitCode;
}
bool setupMsvcEnvironment() {
if (getenv("VSINSTALLDIR"))
return true;
llvm::SmallString<128> tmpFilePath;
if (llvm::sys::fs::createTemporaryFile("ldc_dumpEnv", "", tmpFilePath))
return false;
/* Run `%ComSpec% /s /c "...\dumpEnv.bat <x86|amd64> > <tmpFilePath>"` to dump
* the MSVC environment to the temporary file.
*
* cmd.exe /c treats the following string argument (the command)
* in a very peculiar way if it starts with a double-quote.
* By adding /s and enclosing the command in extra double-quotes
* (WITHOUT additionally escaping the command), the command will
* be parsed properly.
*/
auto comspecEnv = getenv("ComSpec");
if (!comspecEnv) {
warning(Loc(),
"'ComSpec' environment variable is not set, assuming 'cmd.exe'.");
comspecEnv = "cmd.exe";
}
std::string cmdExecutable = comspecEnv;
std::string batchFile = exe_path::prependBinDir("dumpEnv.bat");
std::string arch =
global.params.targetTriple->isArch64Bit() ? "amd64" : "x86";
llvm::SmallString<512> commandLine;
commandLine += quoteArg(cmdExecutable);
commandLine += " /s /c \"";
commandLine += quoteArg(batchFile);
commandLine += ' ';
commandLine += arch;
commandLine += " > ";
commandLine += quoteArg(tmpFilePath);
commandLine += '"';
const int exitCode = executeAndWait(commandLine.c_str());
if (exitCode != 0) {
error(Loc(), "`%s` failed with status: %d", commandLine.c_str(), exitCode);
llvm::sys::fs::remove(tmpFilePath);
return false;
}
auto fileBuffer = llvm::MemoryBuffer::getFile(tmpFilePath);
llvm::sys::fs::remove(tmpFilePath);
if (fileBuffer.getError())
return false;
const auto contents = (*fileBuffer)->getBuffer();
const auto size = contents.size();
// Parse the file.
std::vector<std::pair<llvm::StringRef, llvm::StringRef>> env;
size_t i = 0;
// for each line
while (i < size) {
llvm::StringRef key, value;
for (size_t j = i; j < size; ++j) {
const char c = contents[j];
if (c == '=' && key.empty()) {
key = contents.slice(i, j);
i = j + 1;
} else if (c == '\n' || c == '\r' || c == '\0') {
if (!key.empty()) {
value = contents.slice(i, j);
}
// break and continue with next line
i = j + 1;
break;
}
}
if (!key.empty() && !value.empty())
env.emplace_back(key, value);
}
if (global.params.verbose)
fprintf(global.stdmsg, "Applying environment variables:\n");
bool haveVsInstallDir = false;
for (const auto &pair : env) {
const std::string key = pair.first.str();
const std::string value = pair.second.str();
if (global.params.verbose)
fprintf(global.stdmsg, " %s=%s\n", key.c_str(), value.c_str());
SetEnvironmentVariableA(key.c_str(), value.c_str());
if (key == "VSINSTALLDIR")
haveVsInstallDir = true;
}
return haveVsInstallDir;
}
} // namespace windows
#endif
//////////////////////////////////////////////////////////////////////////////
static int linkObjToBinaryMSVC(bool sharedLib) {
Logger::println("*** Linking executable ***");