mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 20:06:03 +03:00
Move MSVC environment setup to driver/tool.cpp [NFC]
This commit is contained in:
parent
cab165f3d8
commit
0842277572
3 changed files with 218 additions and 208 deletions
|
@ -25,16 +25,10 @@
|
||||||
#include "llvm/Linker/Linker.h"
|
#include "llvm/Linker/Linker.h"
|
||||||
#include "llvm/ProfileData/InstrProf.h"
|
#include "llvm/ProfileData/InstrProf.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/Program.h"
|
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/SourceMgr.h"
|
#include "llvm/Support/SourceMgr.h"
|
||||||
#include "llvm/Target/TargetMachine.h"
|
#include "llvm/Target/TargetMachine.h"
|
||||||
#include "llvm/Target/TargetOptions.h"
|
#include "llvm/Target/TargetOptions.h"
|
||||||
#if _WIN32
|
|
||||||
#include "llvm/Support/SystemUtils.h"
|
|
||||||
#include "llvm/Support/ConvertUTF.h"
|
|
||||||
#include <Windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <algorithm>
|
#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) {
|
static int linkObjToBinaryMSVC(bool sharedLib) {
|
||||||
Logger::println("*** Linking executable ***");
|
Logger::println("*** Linking executable ***");
|
||||||
|
|
||||||
|
|
206
driver/tool.cpp
206
driver/tool.cpp
|
@ -9,7 +9,14 @@
|
||||||
|
|
||||||
#include "driver/tool.h"
|
#include "driver/tool.h"
|
||||||
#include "mars.h"
|
#include "mars.h"
|
||||||
|
#include "driver/exe_path.h"
|
||||||
|
#include "llvm/Support/ConvertUTF.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
#include "llvm/Support/Program.h"
|
#include "llvm/Support/Program.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
int executeToolAndWait(const std::string &tool_,
|
int executeToolAndWait(const std::string &tool_,
|
||||||
std::vector<std::string> const &args, bool verbose) {
|
std::vector<std::string> const &args, bool verbose) {
|
||||||
|
@ -53,3 +60,202 @@ int executeToolAndWait(const std::string &tool_,
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#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 // _WIN32
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//===-- driver/tool.h - External tool invocation helpers ---------*- C++
|
//===-- driver/tool.h - External tool invocation helpers --------*- C++ -*-===//
|
||||||
//-*-===//
|
|
||||||
//
|
//
|
||||||
// LDC – the LLVM D compiler
|
// LDC – the LLVM D compiler
|
||||||
//
|
//
|
||||||
|
@ -8,7 +7,7 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// Functionaliy for invoking external tools executables, such as the system
|
// Functionality for invoking external tools executables, such as the system
|
||||||
// assembler, linker, ...
|
// assembler, linker, ...
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -23,4 +22,14 @@ int executeToolAndWait(const std::string &tool,
|
||||||
std::vector<std::string> const &args,
|
std::vector<std::string> const &args,
|
||||||
bool verbose = false);
|
bool verbose = false);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
namespace windows {
|
||||||
|
// Tries to set up the MSVC environment variables and returns true if
|
||||||
|
// successful.
|
||||||
|
bool setupMsvcEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue