mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Implement process launching on Windows to support macros. Prefer to use the LLVM types wherever possible. The pipes are converted into file descriptors as the types are internal to the process. This allows us to have similar paths on both sides and avoid having to drag in `Windows.h` for the definition of `HANDLE`. This is the core missing functionality for Windows to support macros.
293 lines
8.8 KiB
C++
293 lines
8.8 KiB
C++
//===--- Program.cpp - Implement OS Program Concept -----------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/Program.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include "llvm/Support/Program.h"
|
|
#if defined(_WIN32)
|
|
#include "llvm/Support/Windows/WindowsSupport.h"
|
|
#endif
|
|
|
|
#include <memory>
|
|
#include <system_error>
|
|
|
|
#if HAVE_POSIX_SPAWN
|
|
#include <spawn.h>
|
|
#endif
|
|
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h>
|
|
#include <io.h>
|
|
#endif
|
|
|
|
using namespace swift;
|
|
|
|
int swift::ExecuteInPlace(const char *Program, const char **args,
|
|
const char **env) {
|
|
#if LLVM_ON_UNIX
|
|
int result;
|
|
if (env)
|
|
result = execve(Program, const_cast<char **>(args),
|
|
const_cast<char **>(env));
|
|
else
|
|
result = execv(Program, const_cast<char **>(args));
|
|
|
|
return result;
|
|
#else
|
|
llvm::Optional<llvm::ArrayRef<llvm::StringRef>> Env = llvm::None;
|
|
if (env)
|
|
Env = llvm::toStringRefArray(env);
|
|
int result =
|
|
llvm::sys::ExecuteAndWait(Program, llvm::toStringRefArray(args), Env);
|
|
if (result >= 0)
|
|
exit(result);
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
static const char **
|
|
toNullTerminatedCStringArray(ArrayRef<StringRef> array,
|
|
llvm::BumpPtrAllocator &Alloc) {
|
|
size_t size = array.size();
|
|
const char **result = Alloc.Allocate<const char *>(size + 1);
|
|
for (size_t i = 0; i < size; ++i) {
|
|
result[i] = NullTerminatedStringRef(array[i], Alloc).data();
|
|
}
|
|
result[size] = nullptr;
|
|
return result;
|
|
}
|
|
|
|
#if HAVE_UNISTD_H
|
|
|
|
namespace {
|
|
struct Pipe {
|
|
int read;
|
|
int write;
|
|
|
|
Pipe() {
|
|
int fds[2];
|
|
if (pipe(fds) == -1) {
|
|
read = write = -1; // '-1' to inidicate the failure.
|
|
}
|
|
read = fds[0];
|
|
write = fds[1];
|
|
}
|
|
|
|
operator bool() const { return read != -1 || write != -1; }
|
|
};
|
|
} // namespace
|
|
|
|
llvm::ErrorOr<swift::ChildProcessInfo>
|
|
swift::ExecuteWithPipe(llvm::StringRef program,
|
|
llvm::ArrayRef<llvm::StringRef> args,
|
|
llvm::Optional<llvm::ArrayRef<llvm::StringRef>> env) {
|
|
Pipe p1; // Parent: write, child: read (child's STDIN).
|
|
if (!p1)
|
|
return std::error_code(errno, std::system_category());
|
|
Pipe p2; // Parent: read, child: write (child's STDOUT).
|
|
if (!p2)
|
|
return std::error_code(errno, std::system_category());
|
|
|
|
llvm::BumpPtrAllocator Alloc;
|
|
const char **argv = toNullTerminatedCStringArray(args, Alloc);
|
|
const char **envp = nullptr;
|
|
if (env.has_value()) {
|
|
envp = toNullTerminatedCStringArray(*env, Alloc);
|
|
}
|
|
const char *progCStr = args[0] == program
|
|
? argv[0]
|
|
: NullTerminatedStringRef(program, Alloc).data();
|
|
|
|
pid_t pid;
|
|
|
|
#if HAVE_POSIX_SPAWN
|
|
posix_spawn_file_actions_t FileActions;
|
|
posix_spawn_file_actions_init(&FileActions);
|
|
|
|
posix_spawn_file_actions_adddup2(&FileActions, p1.read, STDIN_FILENO);
|
|
posix_spawn_file_actions_addclose(&FileActions, p1.write);
|
|
|
|
posix_spawn_file_actions_adddup2(&FileActions, p2.write, STDOUT_FILENO);
|
|
posix_spawn_file_actions_addclose(&FileActions, p2.read);
|
|
|
|
// Spawn the subtask.
|
|
int error = posix_spawn(&pid, progCStr, &FileActions, nullptr,
|
|
const_cast<char **>(argv), const_cast<char **>(envp));
|
|
|
|
posix_spawn_file_actions_destroy(&FileActions);
|
|
|
|
close(p1.read);
|
|
close(p2.write);
|
|
|
|
if (error != 0 || pid == 0) {
|
|
close(p1.write);
|
|
close(p2.read);
|
|
return std::error_code(error, std::system_category());
|
|
}
|
|
#else
|
|
// Create a child process.
|
|
switch (pid = fork()) {
|
|
// An error occurred: Return to the caller.
|
|
case -1:
|
|
close(p1.read);
|
|
close(p1.write);
|
|
close(p2.read);
|
|
close(p2.write);
|
|
return std::error_code(errno, std::system_category());
|
|
|
|
// Child process.
|
|
case 0:
|
|
close(p1.write);
|
|
close(p2.read);
|
|
|
|
// Redirect file descriptors...
|
|
dup2(p1.read, STDIN_FILENO);
|
|
dup2(p2.write, STDOUT_FILENO);
|
|
|
|
// Execute the program.
|
|
if (envp) {
|
|
execve(progCStr, const_cast<char **>(argv), const_cast<char **>(envp));
|
|
} else {
|
|
execv(progCStr, const_cast<char **>(argv));
|
|
}
|
|
|
|
// If the execv()/execve() failed, we should exit. Follow Unix protocol and
|
|
// return 127 if the executable was not found, and 126 otherwise. Use _exit
|
|
// rather than exit so that atexit functions and static object destructors
|
|
// cloned from the parent process aren't redundantly run, and so that any
|
|
// data buffered in stdio buffers cloned from the parent aren't redundantly
|
|
// written out.
|
|
_exit(errno == ENOENT ? 127 : 126);
|
|
|
|
// Parent process.
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
close(p1.read);
|
|
close(p2.write);
|
|
return ChildProcessInfo(pid, p1.write, p2.read);
|
|
}
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
llvm::ErrorOr<swift::ChildProcessInfo>
|
|
swift::ExecuteWithPipe(llvm::StringRef program,
|
|
llvm::ArrayRef<llvm::StringRef> args,
|
|
llvm::Optional<llvm::ArrayRef<llvm::StringRef>> env) {
|
|
using unique_handle = std::unique_ptr<void, decltype(&CloseHandle)>;
|
|
enum { PI_READ, PI_WRITE };
|
|
|
|
unique_handle input[2] = {
|
|
{INVALID_HANDLE_VALUE, CloseHandle},
|
|
{INVALID_HANDLE_VALUE, CloseHandle},
|
|
};
|
|
unique_handle output[2] = {
|
|
{INVALID_HANDLE_VALUE, CloseHandle},
|
|
{INVALID_HANDLE_VALUE, CloseHandle},
|
|
};
|
|
unique_handle error{INVALID_HANDLE_VALUE, CloseHandle};
|
|
HANDLE hRead = INVALID_HANDLE_VALUE, hWrite = INVALID_HANDLE_VALUE;
|
|
SECURITY_ATTRIBUTES saAttrs{sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
|
|
|
|
if (!CreatePipe(&hRead, &hWrite, &saAttrs, 0))
|
|
return std::error_code(GetLastError(), std::system_category());
|
|
output[PI_READ].reset(hRead);
|
|
output[PI_WRITE].reset(hWrite);
|
|
|
|
if (!SetHandleInformation(output[PI_READ].get(), HANDLE_FLAG_INHERIT, FALSE))
|
|
return std::error_code(GetLastError(), std::system_category());
|
|
|
|
if (!CreatePipe(&hRead, &hWrite, &saAttrs, 0))
|
|
return std::error_code(GetLastError(), std::system_category());
|
|
input[PI_READ].reset(hRead);
|
|
input[PI_WRITE].reset(hWrite);
|
|
|
|
if (!SetHandleInformation(input[PI_WRITE].get(), HANDLE_FLAG_INHERIT, FALSE))
|
|
return std::error_code(GetLastError(), std::system_category());
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
|
|
GetCurrentProcess(), &hWrite, DUPLICATE_SAME_ACCESS,
|
|
TRUE, DUPLICATE_SAME_ACCESS))
|
|
return std::error_code(GetLastError(), std::system_category());
|
|
error.reset(hWrite);
|
|
|
|
STARTUPINFO si = {0};
|
|
si.cb = sizeof(si);
|
|
si.hStdInput = input[PI_READ].get();
|
|
si.hStdOutput = output[PI_WRITE].get();
|
|
si.hStdError = error.get();
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
llvm::SmallVector<wchar_t, MAX_PATH> executable;
|
|
if (std::error_code ec = llvm::sys::windows::widenPath(program, executable))
|
|
return ec;
|
|
|
|
std::vector<StringRef> components;
|
|
components.push_back(program);
|
|
components.assign(args.begin(), args.end());
|
|
llvm::ErrorOr<std::wstring> commandline =
|
|
llvm::sys::flattenWindowsCommandLine(components);
|
|
if (!commandline)
|
|
return commandline.getError();
|
|
|
|
std::vector<wchar_t> command(commandline->size() + 1, 0);
|
|
std::copy(commandline->begin(), commandline->end(), command.begin());
|
|
|
|
PROCESS_INFORMATION pi = {0};
|
|
if (!CreateProcessW(executable.data(),
|
|
command.data(), nullptr, nullptr, TRUE, 0, nullptr,
|
|
nullptr, &si, &pi))
|
|
return std::error_code(GetLastError(), std::system_category());
|
|
|
|
unique_handle hThread{pi.hThread, CloseHandle};
|
|
unique_handle hProcess{pi.hProcess, CloseHandle};
|
|
|
|
int ifd = _open_osfhandle(reinterpret_cast<intptr_t>(input[PI_WRITE].get()), 0);
|
|
if (ifd < 0)
|
|
return std::error_code(errno, std::system_category());
|
|
input[PI_WRITE].release();
|
|
|
|
int ofd = _open_osfhandle(reinterpret_cast<intptr_t>(output[PI_READ].get()), 0);
|
|
if (ofd < 0) {
|
|
_close(ifd);
|
|
return std::error_code(errno, std::system_category());
|
|
}
|
|
output[PI_READ].release();
|
|
|
|
llvm::sys::ProcessInfo proc;
|
|
proc.Process = pi.hProcess;
|
|
return ChildProcessInfo(proc, ifd, ofd);
|
|
}
|
|
|
|
#else // HAVE_UNISTD_H
|
|
|
|
llvm::ErrorOr<swift::ChildProcessInfo>
|
|
swift::ExecuteWithPipe(llvm::StringRef program,
|
|
llvm::ArrayRef<llvm::StringRef> args,
|
|
llvm::Optional<llvm::ArrayRef<llvm::StringRef>> env) {
|
|
// Not supported.
|
|
return std::errc::not_supported;
|
|
}
|
|
|
|
#endif
|