mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-06 10:57:35 +03:00
Windows: fix command line when invoking linker batch file
This command line sent to CreateProcess(): "C:\LDC\bin\amd64.bat" 1 2 "3" 4 is internally transformed to: C:\Windows\system32\cmd.exe /c "C:\LDC\bin\amd64.bat" 1 2 "3" 4 Now guess what - cmd.exe treats the command string after /c or /k in a very special way if it begins with double quotes and is followed by other quoted args. In this example, it tries to invoke: C:\LDC\bin\amd64.bat" 1 2 "3 Another example: C:\Windows\system32\cmd.exe /c "C:\L D C\bin\amd64.bat" 1 2 "3" 4 => C:\L The fix seems to be enclosing the whole command with additional double quotes (but no additional escaping of the command!) and using /s for cmd.exe: C:\Windows\system32\cmd.exe /s /c ""C:\L D C\bin\amd64.bat" 1 2 "3" 4" So the command is quoted, but not escaped, i.e., no regular argument to cmd.exe, so we can't use LLVM's executeAndWait() to launch the cmd.exe process. :/ Additionally, LLVM's arg-quoting code is not public, so we can't use it to quote & escape the regular args inside the command. I therefore implemented a simple quoteArgs() variant which should suffice for our use cases.
This commit is contained in:
parent
86f151d0f3
commit
6dfbfe60e9
1 changed files with 158 additions and 25 deletions
|
@ -24,6 +24,7 @@
|
|||
#include "llvm/Support/Path.h"
|
||||
#if _WIN32
|
||||
#include "llvm/Support/SystemUtils.h"
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -278,26 +279,160 @@ static int linkObjToBinaryGcc(bool sharedLib)
|
|||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool setupMSVCEnvironment(std::string& tool, std::vector<std::string>& args)
|
||||
{
|
||||
// if the VSINSTALLDIR environment variable is NOT set,
|
||||
// the environment is most likely not set up properly
|
||||
bool setup = !getenv("VSINSTALLDIR");
|
||||
#ifdef _WIN32
|
||||
|
||||
if (setup)
|
||||
namespace windows
|
||||
{
|
||||
bool needsQuotes(const std::string& arg)
|
||||
{
|
||||
// use a helper batch file to let MSVC set up the environment and
|
||||
// then invoke the tool
|
||||
args.push_back(tool); // tool is first arg for batch file
|
||||
tool = exe_path::prependBinDir(
|
||||
if ((!arg.empty() && std::find(arg.begin(), arg.end(), ' ') == arg.end()) ||
|
||||
(arg.size() > 1 && arg[0] == '"' && arg.back() == '"'))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t countPrecedingBackslashes(const std::string& 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(const std::string& 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.length();
|
||||
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 executeMsvcToolAndWait(const std::string& tool, const std::vector<std::string>& args, bool verbose)
|
||||
{
|
||||
llvm::SmallString<1024> commandLine; // full command line incl. executable
|
||||
|
||||
// if the VSINSTALLDIR environment variable is NOT set,
|
||||
// the MSVC environment needs to be set up
|
||||
const bool needMsvcSetup = !getenv("VSINSTALLDIR");
|
||||
if (needMsvcSetup)
|
||||
{
|
||||
/* <command line> => %ComSpec% /s /c "<batch file> <command line>"
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
std::string cmdPath = getenv("ComSpec");
|
||||
std::string batchFile = exe_path::prependBinDir(
|
||||
global.params.targetTriple.isArch64Bit() ? "amd64.bat" : "x86.bat");
|
||||
|
||||
commandLine.append(windows::quoteArg(cmdPath));
|
||||
commandLine.append(" /s /c \"");
|
||||
commandLine.append(windows::quoteArg(batchFile));
|
||||
commandLine.push_back(' ');
|
||||
commandLine.append(windows::quoteArg(tool));
|
||||
}
|
||||
else
|
||||
tool = getProgram(tool.c_str());
|
||||
{
|
||||
std::string toolPath = getProgram(tool.c_str());
|
||||
commandLine.append(windows::quoteArg(toolPath));
|
||||
}
|
||||
|
||||
return setup;
|
||||
// append (quoted) args
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
commandLine.push_back(' ');
|
||||
commandLine.append(windows::quoteArg(args[i]));
|
||||
}
|
||||
|
||||
if (needMsvcSetup)
|
||||
commandLine.push_back('"');
|
||||
|
||||
const char* finalCommandLine = commandLine.c_str();
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
fprintf(global.stdmsg, finalCommandLine);
|
||||
fprintf(global.stdmsg, "\n");
|
||||
fflush(global.stdmsg);
|
||||
}
|
||||
|
||||
STARTUPINFO si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
DWORD exitCode;
|
||||
|
||||
// according to MSDN, only CreateProcessW (unicode) may modify the passed command line
|
||||
if (!CreateProcess(NULL, const_cast<char*>(finalCommandLine), 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);
|
||||
}
|
||||
|
||||
if (exitCode != 0)
|
||||
error(Loc(), "%s failed with status: %d", tool.c_str(), exitCode);
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
#else // !_WIN32
|
||||
|
||||
int executeMsvcToolAndWait(const std::string&, const std::vector<std::string>&, bool)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int linkObjToBinaryWin(bool sharedLib)
|
||||
|
@ -309,8 +444,6 @@ static int linkObjToBinaryWin(bool sharedLib)
|
|||
// build arguments
|
||||
std::vector<std::string> args;
|
||||
|
||||
const bool setupMSVC = setupMSVCEnvironment(tool, args);
|
||||
|
||||
args.push_back("/NOLOGO");
|
||||
|
||||
// specify that the image will contain a table of safe exception handlers (32bit only)
|
||||
|
@ -402,7 +535,6 @@ static int linkObjToBinaryWin(bool sharedLib)
|
|||
|
||||
Logger::println("Linking with: ");
|
||||
std::vector<std::string>::const_iterator I = args.begin(), E = args.end();
|
||||
if (setupMSVC) ++I; // skip link.exe, the first arg for the batch file
|
||||
Stream logstr = Logger::cout();
|
||||
for (; I != E; ++I)
|
||||
if (!(*I).empty())
|
||||
|
@ -410,23 +542,23 @@ static int linkObjToBinaryWin(bool sharedLib)
|
|||
logstr << "\n"; // FIXME where's flush ?
|
||||
|
||||
// try to call linker
|
||||
return executeToolAndWait(tool, args, global.params.verbose);
|
||||
return executeMsvcToolAndWait(tool, args, global.params.verbose);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int linkObjToBinary(bool sharedLib)
|
||||
{
|
||||
int status;
|
||||
int exitCode;
|
||||
#if LDC_LLVM_VER >= 305
|
||||
if (global.params.targetTriple.isWindowsMSVCEnvironment())
|
||||
#else
|
||||
if (global.params.targetTriple.getOS() == llvm::Triple::Win32)
|
||||
#endif
|
||||
status = linkObjToBinaryWin(sharedLib);
|
||||
exitCode = linkObjToBinaryWin(sharedLib);
|
||||
else
|
||||
status = linkObjToBinaryGcc(sharedLib);
|
||||
return status;
|
||||
exitCode = linkObjToBinaryGcc(sharedLib);
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -447,9 +579,6 @@ int createStaticLibrary()
|
|||
// build arguments
|
||||
std::vector<std::string> args;
|
||||
|
||||
if (isTargetWindows)
|
||||
setupMSVCEnvironment(tool, args);
|
||||
|
||||
// ask ar to create a new library
|
||||
if (!isTargetWindows)
|
||||
args.push_back("rcs");
|
||||
|
@ -501,8 +630,12 @@ int createStaticLibrary()
|
|||
CreateDirectoryOnDisk(libName);
|
||||
|
||||
// try to call archiver
|
||||
int rc = executeToolAndWait(tool, args, global.params.verbose);
|
||||
return rc;
|
||||
int exitCode;
|
||||
if (isTargetWindows)
|
||||
exitCode = executeMsvcToolAndWait(tool, args, global.params.verbose);
|
||||
else
|
||||
exitCode = executeToolAndWait(tool, args, global.params.verbose);
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue