From 9d514e65f00dde0e309f33591f31fbf7f132a005 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Wed, 12 Jun 2019 09:05:40 -0400 Subject: Runtime execution of Visual Studio Compiler (#978) * Work in progress to be able to invoke VS from within code. * First pass at windows version of refactor of OSProcessSpawner * Closer to getting VS path lookup working. * Make OSString assignable/ctor able * Work out program files directory directly, so don't have to expand %%. * WIP: Improve handling of process spawning. * Add support for splitting input by line. * * Correctly locates visual studio install * Added functionality to invoke vs via cmd * Add option to execute the command line. * Handle in ProcessUtil for windows -> WinHandle. * Rename files slang-win-visual-studio-util.cpp/.h and slang-process-util.h * First pass at unix/linux version of ProcessUtil. * Fix reading Visual Studio path from the registry. * Get compiling on linux with. * Fix vcvarsall.bat name * Use ProcessUtil to execute external code. * Remove OSProcessSpawner. * Remove includes for "os.h" where no longer needed. * Fix tabbing issue in premake5.lua Remove test code from slang-test-main.cpp * Fix premake4.lua tabbing issue. * Small fixes to slang-process-util.h Init ExecuteResult on Win execute. * Improve comments. * Fix bug in StringUtil::calcLines - with oddly terminated source input being able to read past end. Make slang-generate use StringUtil over it's own impl. * Fix off by one bug in working out Visual Studio version. * Fix bug in calculating Visual Studio Version * Fix compilation on linux with string parameter being passed to messageFormat. * Remove erroneous use of kOSError codes - use Result. --- source/core/unix/slang-unix-process-util.cpp | 234 +++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 source/core/unix/slang-unix-process-util.cpp (limited to 'source/core/unix') diff --git a/source/core/unix/slang-unix-process-util.cpp b/source/core/unix/slang-unix-process-util.cpp new file mode 100644 index 000000000..7795463b7 --- /dev/null +++ b/source/core/unix/slang-unix-process-util.cpp @@ -0,0 +1,234 @@ +// slang-unix-process-util.cpp +#include "../slang-process-util.h" + +#include "../slang-common.h" +#include "../slang-string-util.h" + +#include +#include + +//#include +#include +#include +#include +#include +#include +#include + +namespace Slang { + + +/* static */UnownedStringSlice ProcessUtil::getExecutableSuffix() +{ + return UnownedStringSlice::fromLiteral(""); +} + +static void _appendEscaped(const UnownedStringSlice& slice, StringBuilder& out) +{ + // TODO(JS): This escaping is not complete... ! + if (slice.indexOf(' ') >= 0 || slice.indexOf('"') >= 0) + { + out << "\""; + + const char* cur = slice.begin(); + const char* end = slice.end(); + + while (cur < end) + { + char c = *cur++; + switch (c) + { + case '\"': + { + // Escape quotes. + out << "\\\""; + break; + } + default: + out.append(c); + } + } + + out << "\""; + + return; + } + + out << slice; +} + +/* static */String ProcessUtil::getCommandLineString(const CommandLine& commandLine) +{ + StringBuilder cmd; + _appendEscaped(commandLine.m_executable.getUnownedSlice(), cmd); + for (const auto& arg : commandLine.m_args) + { + cmd << " "; + _appendEscaped(arg.getUnownedSlice(), cmd); + } + return cmd.ToString(); +} + +/* static */SlangResult ProcessUtil::execute(const CommandLine& commandLine, ExecuteResult& outExecuteResult) +{ + outExecuteResult.init(); + + List argPtrs; + // Add the command + argPtrs.add(commandLine.m_executable.getBuffer()); + // Add all the args + for (auto arg : commandLine.m_args) + { + argPtrs.add(arg.getBuffer()); + } + // Terminate with a null + argPtrs.add(nullptr); + + int stdoutPipe[2]; + int stderrPipe[2]; + + if (pipe(stdoutPipe) == -1) + return SLANG_FAIL; + + if (pipe(stderrPipe) == -1) + return SLANG_FAIL; + + pid_t childProcessID = fork(); + if (childProcessID == -1) + return SLANG_FAIL; + + if (childProcessID == 0) + { + // We are the child process. + + dup2(stdoutPipe[1], STDOUT_FILENO); + dup2(stderrPipe[1], STDERR_FILENO); + + close(stdoutPipe[0]); + close(stdoutPipe[1]); + + close(stderrPipe[0]); + close(stderrPipe[1]); + + execvp(argPtrs[0], (char* const*)&argPtrs[0]); + + // If we get here, then `exec` failed + fprintf(stderr, "error: `exec` failed\n"); + return SLANG_FAIL; + } + else + { + // We are the parent process + + close(stdoutPipe[1]); + close(stderrPipe[1]); + + int stdoutFD = stdoutPipe[0]; + int stderrFD = stderrPipe[0]; + + pollfd pollInfos[2]; + nfds_t pollInfoCount = 2; + + pollInfos[0].fd = stdoutFD; + pollInfos[0].events = POLLIN; + pollInfos[0].revents = 0; + pollInfos[1].fd = stderrFD; + pollInfos[1].events = POLLIN; + pollInfos[1].revents = 0; + + int remainingCount = 2; + int iterations = 0; + while (remainingCount) + { + // Safeguard against infinite loop: + iterations++; + if (iterations > 10000) + { + fprintf(stderr, "poll(): %d iterations\n", iterations); + return SLANG_FAIL; + } + + // Set a timeout of ten seconds; + // we really shouldn't wait too long... + int pollTimeout = 10000; + int pollResult = poll(pollInfos, pollInfoCount, pollTimeout); + if (pollResult <= 0) + { + // If there was a signal that got in + // the way, then retry... + if (pollResult == -1 && errno == EINTR) + continue; + + // timeout or error... + return SLANG_FAIL; + } + + enum { kBufferSize = 1024 }; + char buffer[kBufferSize]; + + if (pollInfos[0].revents) + { + auto count = read(stdoutFD, buffer, kBufferSize); + if (count <= 0) + { + // end-of-file + close(stdoutFD); + pollInfos[0].fd = -1; + remainingCount--; + } + + outExecuteResult.standardOutput.append(buffer, buffer + count); + } + + if (pollInfos[1].revents) + { + auto count = read(stderrFD, buffer, kBufferSize); + if (count <= 0) + { + // end-of-file + close(stderrFD); + pollInfos[1].fd = -1; + remainingCount--; + } + + outExecuteResult.standardError.append(buffer, buffer + count); + } + } + + int childStatus = 0; + iterations = 0; + for (;;) + { + // Safeguard against infinite loop: + iterations++; + if (iterations > 10000) + { + fprintf(stderr, "waitpid(): %d iterations\n", iterations); + return SLANG_FAIL; + } + + pid_t terminatedProcessID = waitpid(childProcessID, &childStatus, 0); + if (terminatedProcessID == -1) + { + return SLANG_FAIL; + } + + if (terminatedProcessID == childProcessID) + { + if (WIFEXITED(childStatus)) + { + outExecuteResult.resultCode = (int)(int8_t)WEXITSTATUS(childStatus); + } + else + { + outExecuteResult.resultCode = 1; + } + return SLANG_OK; + } + } + } + + return SLANG_FAIL; +} + +} // namespace Slang -- cgit v1.2.3