diff --git a/wrappers/clang-target-wrapper.c b/wrappers/clang-target-wrapper.c index 976d642..ae43810 100644 --- a/wrappers/clang-target-wrapper.c +++ b/wrappers/clang-target-wrapper.c @@ -53,6 +53,12 @@ typedef char TCHAR; #define EXECVP_CAST (char **) #endif +#ifdef _UNICODE +#define TS "%ls" +#else +#define TS "%s" +#endif + static TCHAR *escape(const TCHAR *str) { #ifdef _WIN32 TCHAR *out = malloc((_tcslen(str) * 2 + 3) * sizeof(*out)); @@ -105,6 +111,116 @@ static TCHAR *_tcsrchrs(const TCHAR *str, TCHAR char1, TCHAR char2) { return ptr1; } +#ifdef _WIN32 +static int filter_line = 0, last_char = '\n'; +static void filter_stderr(char *buf, int n) { + // Filter the stderr output from "-v" to rewrite paths from backslash + // to forward slash form. libtool parses the output of "-v" and can't + // handle the backslash form of paths. A proper upstream solution has + // been discussed at https://reviews.llvm.org/D53066 but hasn't been + // finished yet. + int out = 0; + int last = last_char; + for (int i = 0; i < n; i++) { + TCHAR cur = buf[i]; + // All lines that contain command lines or paths currently start + // with a space. + if (last == '\n') { + filter_line = cur == ' '; +} + if (filter_line) { + if (cur == '"') { + // Do nothing; skip the quotes. This assumes that after + // converting backslashes to forward slashes, there's nothing + // else (e.g. spaces) that needs quoting. libtool would + // probably not handle that anyway, but this would break + // a more capable caller which also parses the output of "-v". + } else if (cur == '\\') { + // Convert backslashes to forward slashes. Quoted backslashes + // are doubled, so just output every other one. We don't really + // keep track of whether we're in a quoted context though. + if (last != '\\') { + buf[out++] = '/'; + } else { + // Last output char was a backslash converted into a + // forward slash. Ignore this one, but handle the next + // one in case there's more. + last = ' '; + } + } else { + buf[out++] = cur; + } + } else { + buf[out++] = cur; + } + + last = cur; + } + last_char = last; + fwrite(buf, 1, out, stderr); +} + +static int exec_filtered(const TCHAR **argv) { + int len = 1; + for (int i = 0; argv[i]; i++) + len += _tcslen(argv[i]) + 1; + TCHAR *cmdline = malloc(len * sizeof(*cmdline)); + int pos = 0; + // On Windows, the arguments are already quoted and escaped properly. + for (int i = 0; argv[i]; i++) { + _tcscpy(&cmdline[pos], argv[i]); + pos += _tcslen(argv[i]); + cmdline[pos++] = ' '; + } + if (pos > 0) + pos--; + cmdline[pos] = '\0'; + + STARTUPINFO si = { 0 }; + PROCESS_INFORMATION pi = { 0 }; + HANDLE pipe_read = NULL, pipe_write = NULL; + SECURITY_ATTRIBUTES sa = { 0 }; + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + CreatePipe(&pipe_read, &pipe_write, &sa, 0); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = pipe_write; + if (!CreateProcess(NULL, cmdline, NULL, NULL, /* bInheritHandles */ TRUE, + 0, NULL, NULL, &si, &pi)) { + DWORD err = GetLastError(); + TCHAR *errbuf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &errbuf, 0, NULL); + _ftprintf(stderr, _T("Unable to execute: "TS": "TS"\n"), cmdline, errbuf); + LocalFree(errbuf); + CloseHandle(pipe_read); + CloseHandle(pipe_write); + free(cmdline); + return 1; + } + + CloseHandle(pipe_write); + char stderr_buf[8192]; + DWORD n; + while (ReadFile(pipe_read, stderr_buf, sizeof(stderr_buf), &n, NULL)) + filter_stderr(stderr_buf, n); + CloseHandle(pipe_read); + + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD exit_code = 1; + GetExitCodeProcess(pi.hProcess, &exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + free(cmdline); + return exit_code; +} +#endif + int _tmain(int argc, TCHAR* argv[]) { const TCHAR *argv0 = argv[0]; const TCHAR *sep = _tcsrchrs(argv0, '/', '\\'); @@ -200,6 +316,15 @@ int _tmain(int argc, TCHAR* argv[]) { } #ifdef _WIN32 + // If the command line contains the "-v" argument, filter the stderr + // output to rewrite paths from backslash to forward slash form. + // libtool parses the output of "-v" and can't handle the backslash + // form of paths. A proper upstream solution has been discussed at + // https://reviews.llvm.org/D53066 but hasn't been finished yet. + for (int i = 1; i < argc; i++) + if (!_tcscmp(argv[i], _T("-v"))) + return exec_filtered(exec_argv); + int ret = _tspawnvp(_P_WAIT, exec_argv[0], exec_argv); if (ret == -1) { _tperror(exec_argv[0]);