diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-06-12 09:05:40 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-06-12 09:05:40 -0400 |
| commit | 9d514e65f00dde0e309f33591f31fbf7f132a005 (patch) | |
| tree | 7e3a751377651ef9eda06f0b1ad345af1796c596 /tools/slang-test | |
| parent | fc083a75b94ac4b4e735b4a7ff566191b9123f74 (diff) | |
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.
Diffstat (limited to 'tools/slang-test')
| -rw-r--r-- | tools/slang-test/options.cpp | 2 | ||||
| -rw-r--r-- | tools/slang-test/os.cpp | 469 | ||||
| -rw-r--r-- | tools/slang-test/os.h | 65 | ||||
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 335 | ||||
| -rw-r--r-- | tools/slang-test/test-context.cpp | 2 | ||||
| -rw-r--r-- | tools/slang-test/test-reporter.cpp | 40 |
6 files changed, 199 insertions, 714 deletions
diff --git a/tools/slang-test/options.cpp b/tools/slang-test/options.cpp index 3e985b493..4c8108f4a 100644 --- a/tools/slang-test/options.cpp +++ b/tools/slang-test/options.cpp @@ -1,8 +1,8 @@ // test-context.cpp #include "options.h" -#include "os.h" #include "../../source/core/slang-string-util.h" +#include "../../source/core/slang-io.h" #include <stdio.h> #include <stdlib.h> diff --git a/tools/slang-test/os.cpp b/tools/slang-test/os.cpp index a938a71e7..eceeef2d7 100644 --- a/tools/slang-test/os.cpp +++ b/tools/slang-test/os.cpp @@ -123,266 +123,6 @@ OSFindFilesResult osFindChildDirectories( return result; } -// OSProcessSpawner - -struct OSProcessSpawner_ReaderThreadInfo -{ - HANDLE file; - String output; -}; - -static DWORD WINAPI osReaderThreadProc(LPVOID threadParam) -{ - OSProcessSpawner_ReaderThreadInfo* info = (OSProcessSpawner_ReaderThreadInfo*)threadParam; - HANDLE file = info->file; - - static const int kChunkSize = 1024; - char buffer[kChunkSize]; - - StringBuilder outputBuilder; - - // We need to re-write the output to deal with line - // endings, so we check for paired '\r' and '\n' - // characters, which may span chunks. - int prevChar = -1; - - for (;;) - { - DWORD bytesRead = 0; - BOOL readResult = ReadFile(file, buffer, kChunkSize, &bytesRead, nullptr); - - if (!readResult || GetLastError() == ERROR_BROKEN_PIPE) - { - break; - } - - // walk the buffer and rewrite to eliminate '\r' '\n' pairs - char* readCursor = buffer; - char const* end = buffer + bytesRead; - char* writeCursor = buffer; - - while (readCursor != end) - { - int p = prevChar; - int c = *readCursor++; - prevChar = c; - switch (c) - { - case '\r': case '\n': - // swallow input if '\r' and '\n' appear in sequence - if ((p ^ c) == ('\r' ^ '\n')) - { - // but don't swallow the next byte - prevChar = -1; - continue; - } - // always replace '\r' with '\n' - c = '\n'; - break; - - default: - break; - } - - *writeCursor++ = (char)c; - } - bytesRead = (DWORD)(writeCursor - buffer); - - // Note: Current "core" implementation gives no way to know - // the length of the buffer, so we ultimately have - // to just assume null termination... - outputBuilder.Append(buffer, bytesRead); - } - - info->output = outputBuilder.ProduceString(); - - return 0; -} - -void OSProcessSpawner::pushExecutableName( - Slang::String executableName) -{ - executableName_ = executableName; - commandLine_.Append(executableName); - isExecutablePath_ = false; -} - -void OSProcessSpawner::pushExecutablePath( - Slang::String executablePath) -{ - executableName_ = executablePath; - commandLine_.Append(executablePath); - isExecutablePath_ = true; -} - -void OSProcessSpawner::pushArgument( - Slang::String argument) -{ - // TODO(tfoley): handle cases where arguments need some escaping - commandLine_.Append(" "); - commandLine_.Append(argument); - - argumentList_.add(argument); -} - -Slang::String OSProcessSpawner::getCommandLine() -{ - return commandLine_; -} - -OSError OSProcessSpawner::spawnAndWaitForCompletion() -{ - SECURITY_ATTRIBUTES securityAttributes; - securityAttributes.nLength = sizeof(securityAttributes); - securityAttributes.lpSecurityDescriptor = nullptr; - securityAttributes.bInheritHandle = true; - - // create stdout pipe for child process - HANDLE childStdOutReadTmp = nullptr; - HANDLE childStdOutWrite = nullptr; - if (!CreatePipe(&childStdOutReadTmp, &childStdOutWrite, &securityAttributes, 0)) - { - return kOSError_OperationFailed; - } - - // create stderr pipe for child process - HANDLE childStdErrReadTmp = nullptr; - HANDLE childStdErrWrite = nullptr; - if (!CreatePipe(&childStdErrReadTmp, &childStdErrWrite, &securityAttributes, 0)) - { - return kOSError_OperationFailed; - } - - // create stdin pipe for child process - HANDLE childStdInRead = nullptr; - HANDLE childStdInWriteTmp = nullptr; - if (!CreatePipe(&childStdInRead, &childStdInWriteTmp, &securityAttributes, 0)) - { - return kOSError_OperationFailed; - } - - HANDLE currentProcess = GetCurrentProcess(); - - // create a non-inheritable duplicate of the stdout reader - HANDLE childStdOutRead = nullptr; - if (!DuplicateHandle( - currentProcess, childStdOutReadTmp, - currentProcess, &childStdOutRead, - 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - return kOSError_OperationFailed; - } - if (!CloseHandle(childStdOutReadTmp)) - { - return kOSError_OperationFailed; - } - - // create a non-inheritable duplicate of the stderr reader - HANDLE childStdErrRead = nullptr; - if (!DuplicateHandle( - currentProcess, childStdErrReadTmp, - currentProcess, &childStdErrRead, - 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - return kOSError_OperationFailed; - } - if (!CloseHandle(childStdErrReadTmp)) - { - return kOSError_OperationFailed; - } - - // create a non-inheritable duplicate of the stdin writer - HANDLE childStdInWrite = nullptr; - if (!DuplicateHandle( - currentProcess, childStdInWriteTmp, - currentProcess, &childStdInWrite, - 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - return kOSError_OperationFailed; - } - if (!CloseHandle(childStdInWriteTmp)) - { - return kOSError_OperationFailed; - } - - // Now we can actually get around to starting a process - PROCESS_INFORMATION processInfo; - ZeroMemory(&processInfo, sizeof(processInfo)); - - // TODO: switch to proper wide-character versions of these... - STARTUPINFOW startupInfo; - ZeroMemory(&startupInfo, sizeof(startupInfo)); - startupInfo.cb = sizeof(startupInfo); - startupInfo.hStdError = childStdErrWrite; - startupInfo.hStdOutput = childStdOutWrite; - startupInfo.hStdInput = childStdInRead; - startupInfo.dwFlags = STARTF_USESTDHANDLES; - - // `CreateProcess` requires write access to this, for some reason... - BOOL success = CreateProcessW( - isExecutablePath_ ? executableName_.toWString().begin() : nullptr, - (LPWSTR)commandLine_.ToString().toWString().begin(), - nullptr, - nullptr, - true, - CREATE_NO_WINDOW, - nullptr, // TODO: allow specifying environment variables? - nullptr, - &startupInfo, - &processInfo); - if (!success) - { - return kOSError_OperationFailed; - } - - // close handles we are now done with - CloseHandle(processInfo.hThread); - CloseHandle(childStdOutWrite); - CloseHandle(childStdErrWrite); - CloseHandle(childStdInRead); - - // Create a thread to read from the child's stdout. - OSProcessSpawner_ReaderThreadInfo stdOutThreadInfo; - stdOutThreadInfo.file = childStdOutRead; - HANDLE stdOutThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdOutThreadInfo, 0, nullptr); - - // Create a thread to read from the child's stderr. - OSProcessSpawner_ReaderThreadInfo stdErrThreadInfo; - stdErrThreadInfo.file = childStdErrRead; - HANDLE stdErrThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdErrThreadInfo, 0, nullptr); - - // wait for the process to exit - // TODO: set a timeout as a safety measure... - WaitForSingleObject(processInfo.hProcess, INFINITE); - - // get exit code for process - DWORD childExitCode = 0; - if (!GetExitCodeProcess(processInfo.hProcess, &childExitCode)) - { - return kOSError_OperationFailed; - } - - // wait for the reader threads - WaitForSingleObject(stdOutThread, INFINITE); - WaitForSingleObject(stdErrThread, INFINITE); - - CloseHandle(processInfo.hProcess); - CloseHandle(childStdOutRead); - CloseHandle(childStdErrRead); - CloseHandle(childStdInWrite); - - standardOutput_ = stdOutThreadInfo.output; - standardError_ = stdErrThreadInfo.output; - resultCode_ = childExitCode; - - return kOSError_None; -} - -char const* osGetExecutableSuffix() -{ - return ".exe"; -} - #else static bool advance(OSFindFilesResult& result) @@ -475,213 +215,4 @@ OSFindFilesResult osFindChildDirectories( return result; } -// OSProcessSpawner - -void OSProcessSpawner::pushExecutableName( - Slang::String executableName) -{ - executableName_ = executableName; - arguments_.add(executableName); - isExecutablePath_ = false; -} - -void OSProcessSpawner::pushExecutablePath( - Slang::String executablePath) -{ - executableName_ = executablePath; - arguments_.add(executablePath); - isExecutablePath_ = true; -} - -void OSProcessSpawner::pushArgument( - Slang::String argument) -{ - arguments_.add(argument); - argumentList_.add(argument); -} - -Slang::String OSProcessSpawner::getCommandLine() -{ - Slang::UInt argCount = arguments_.getCount(); - - Slang::StringBuilder sb; - for(Slang::UInt ii = 0; ii < argCount; ++ii) - { - if(ii != 0) sb << " "; - sb << arguments_[ii]; - } - return sb.ProduceString(); -} - -OSError OSProcessSpawner::spawnAndWaitForCompletion() -{ - List<char const*> argPtrs; - for(auto arg : arguments_) - { - argPtrs.add(arg.getBuffer()); - } - argPtrs.add(NULL); - - int stdoutPipe[2]; - int stderrPipe[2]; - - if(pipe(stdoutPipe) == -1) - return kOSError_OperationFailed; - - if(pipe(stderrPipe) == -1) - return kOSError_OperationFailed; - - pid_t childProcessID = fork(); - if (childProcessID == -1) - return kOSError_OperationFailed; - - 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"); - exit(1); - } - 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 kOSError_OperationFailed; - } - - // 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 kOSError_OperationFailed; - } - - 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--; - } - - 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--; - } - - 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 kOSError_OperationFailed; - } - - - pid_t terminatedProcessID = waitpid( - childProcessID, - &childStatus, - 0); - if (terminatedProcessID == -1) - { - return kOSError_OperationFailed; - } - - if(terminatedProcessID == childProcessID) - { - if(WIFEXITED(childStatus)) - { - resultCode_ = (int)(int8_t)WEXITSTATUS(childStatus); - } - else - { - resultCode_ = 1; - } - - return kOSError_None; - } - } - - } - - return kOSError_OperationFailed; -} - -char const* osGetExecutableSuffix() -{ - return ""; -} - #endif diff --git a/tools/slang-test/os.h b/tools/slang-test/os.h index 3e6d93942..9dd20c62d 100644 --- a/tools/slang-test/os.h +++ b/tools/slang-test/os.h @@ -1,3 +1,6 @@ +#ifndef SLANG_OS_H +#define SLANG_OS_H + // os.h #include "../../source/core/slang-io.h" @@ -21,7 +24,7 @@ #include <dirent.h> #include <errno.h> -#include <poll.h> +//#include <poll.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> @@ -129,62 +132,4 @@ OSFindFilesResult osFindFilesInDirectoryMatchingPattern( OSFindFilesResult osFindFilesInDirectory( Slang::String directoryPath); - -// An `OSProcessSpawner` can be used to launch a process, and handles -// putting together the arguments in the form required by the target -// platform, as well as capturing any output from the process (both -// standard output and standard error) as strings. -struct OSProcessSpawner -{ - // Set the executable name for the process to be spawned. - // Note: this call must be made before any arguments are pushed. - void pushExecutableName( - Slang::String executableName); - - // Set the executable name for the process to be spawned. - // Note: this call must be made before any arguments are pushed. - void pushExecutablePath( - Slang::String executablePath); - - // Append an argument for the process to be spawned. - void pushArgument( - Slang::String argument); - - // Get a printable version of the command line - // that will be run (can be used for debugging) - Slang::String getCommandLine(); - - // Attempt to spawn the process, and wait for it to complete. - // Returns an error if the attempt to spawn and/or wait fails, - // but returns `kOSError_None` if the process is run to completion, - // whether or not the process returns "successfully" (with a zero - // result code); - OSError spawnAndWaitForCompletion(); - - // If the process is successfully spawned and completes, then - // the user can query the result code that the process produce - // on exit, along with the output it wrote to stdout and stderr. - typedef int ResultCode; - ResultCode getResultCode() { return resultCode_; } - Slang::String const& getStandardOutput() { return standardOutput_; } - Slang::String const& getStandardError() { return standardError_; } - - // "private" data follows - Slang::String standardOutput_; - Slang::String standardError_; - ResultCode resultCode_; - Slang::String executableName_; -#ifdef WIN32 - Slang::StringBuilder commandLine_; -#else - Slang::List<Slang::String> arguments_; -#endif - - // Only holds the argumements in order - Slang::List<Slang::String> argumentList_; - - // Is the executable specified by path, rather than just by name? - bool isExecutablePath_; -}; - -char const* osGetExecutableSuffix(); +#endif // SLANG_OS_H diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index f030dc8aa..dac6ff9fe 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -18,6 +18,8 @@ using namespace Slang; #include "options.h" #include "slangc-tool.h" +#include "../../source/core/slang-process-util.h" + #define STB_IMAGE_IMPLEMENTATION #include "external/stb/stb_image.h" @@ -93,7 +95,7 @@ typedef TestResult(*TestCallback)(TestContext* context, TestInput& input); // Globals // Pre declare -static void _addRenderTestOptions(const Options& options, OSProcessSpawner& spawner); +static void _addRenderTestOptions(const Options& options, CommandLine& cmdLine); /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! Functions !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -382,51 +384,46 @@ TestResult gatherTestsForFile( return TestResult::Pass; } -OSError spawnAndWaitExe(TestContext* context, const String& testPath, OSProcessSpawner& spawner) +Result spawnAndWaitExe(TestContext* context, const String& testPath, const CommandLine& cmdLine, ExecuteResult& outRes) { const auto& options = context->options; if (options.shouldBeVerbose) { - String commandLine = spawner.getCommandLine(); + String commandLine = ProcessUtil::getCommandLineString(cmdLine); context->reporter->messageFormat(TestMessageType::Info, "%s\n", commandLine.begin()); } - OSError err = spawner.spawnAndWaitForCompletion(); - if (err != kOSError_None) + Result res = ProcessUtil::execute(cmdLine, outRes); + if (SLANG_FAILED(res)) { // fprintf(stderr, "failed to run test '%S'\n", testPath.ToWString()); context->reporter->messageFormat(TestMessageType::RunError, "failed to run test '%S'", testPath.toWString().begin()); } - return err; + return res; } -OSError spawnAndWaitSharedLibrary(TestContext* context, const String& testPath, OSProcessSpawner& spawner) +Result spawnAndWaitSharedLibrary(TestContext* context, const String& testPath, const CommandLine& cmdLine, ExecuteResult& outRes) { const auto& options = context->options; - String exeName = Path::getFileNameWithoutExt(spawner.executableName_); + String exeName = Path::getFileNameWithoutExt(cmdLine.m_executable); if (options.shouldBeVerbose) { - StringBuilder builder; - - builder << "slang-test"; + CommandLine testCmdLine; + testCmdLine.setExecutableFilename("slang-test"); if (options.binDir.getLength()) { - builder << " -bindir " << options.binDir; + testCmdLine.addArg("-bindir"); + testCmdLine.addArg(options.binDir); } - builder << " " << exeName; + testCmdLine.addArg(exeName); + testCmdLine.m_args.addRange(cmdLine.m_args); - // TODO(js): Potentially this should handle escaping parameters for the command line if need be - const auto& argList = spawner.argumentList_; - for (Index i = 0; i < argList.getCount(); ++i) - { - builder << " " << argList[i]; - } - - context->reporter->messageFormat(TestMessageType::Info, "%s\n", builder.begin()); + String testCmdLineString = ProcessUtil::getCommandLineString(testCmdLine); + context->reporter->messageFormat(TestMessageType::Info, "%s\n", testCmdLineString.getBuffer()); } auto func = context->getInnerMainFunc(context->options.binDir, exeName); @@ -452,24 +449,24 @@ OSError spawnAndWaitSharedLibrary(TestContext* context, const String& testPath, List<const char*> args; args.add(exeName.getBuffer()); - for (Index i = 0; i < spawner.argumentList_.getCount(); ++i) + for (Index i = 0; i < cmdLine.m_args.getCount(); ++i) { - args.add(spawner.argumentList_[i].getBuffer()); + args.add(cmdLine.m_args[i].getBuffer()); } SlangResult res = func(&stdWriters, context->getSession(), int(args.getCount()), args.begin()); StdWriters::setSingleton(prevStdWriters); - spawner.standardError_ = stdErrorString; - spawner.standardOutput_ = stdOutString; + outRes.standardError = stdErrorString; + outRes.standardOutput = stdOutString; - spawner.resultCode_ = (int)TestToolUtil::getReturnCode(res); + outRes.resultCode = (int)TestToolUtil::getReturnCode(res); - return kOSError_None; + return SLANG_OK; } - return kOSError_OperationFailed; + return SLANG_FAIL; } @@ -580,10 +577,10 @@ static SlangCompileTarget _getCompileTarget(const UnownedStringSlice& name) return SLANG_TARGET_UNKNOWN; } -static SlangResult _extractRenderTestRequirements(OSProcessSpawner& spawner, TestRequirements* ioRequirements) +static SlangResult _extractRenderTestRequirements(const CommandLine& cmdLine, TestRequirements* ioRequirements) { - const auto& args = spawner.argumentList_; - + const auto& args = cmdLine.m_args; + // TODO(JS): // This is rather convoluted in that it has to work out from the command line parameters passed // to render-test what renderer will be used. @@ -697,11 +694,11 @@ static SlangResult _extractRenderTestRequirements(OSProcessSpawner& spawner, Tes return SLANG_OK; } -static SlangResult _extractSlangCTestRequirements(OSProcessSpawner& spawner, TestRequirements* ioRequirements) +static SlangResult _extractSlangCTestRequirements(const CommandLine& cmdLine, TestRequirements* ioRequirements) { // This determines what the requirements are for a slangc like command line - const auto& args = spawner.argumentList_; - + const auto& args = cmdLine.m_args; + // First check pass through { String passThrough; @@ -724,27 +721,27 @@ static SlangResult _extractSlangCTestRequirements(OSProcessSpawner& spawner, Tes } -static SlangResult _extractReflectionTestRequirements(OSProcessSpawner& spawner, TestRequirements* ioRequirements) +static SlangResult _extractReflectionTestRequirements(const CommandLine& cmdLine, TestRequirements* ioRequirements) { // There are no specialized constraints for a reflection test return SLANG_OK; } -static SlangResult _extractTestRequirements(OSProcessSpawner& spawner, TestRequirements* ioInfo) +static SlangResult _extractTestRequirements(const CommandLine& cmdLine, TestRequirements* ioInfo) { - String exeName = Path::getFileNameWithoutExt(spawner.executableName_); + String exeName = Path::getFileNameWithoutExt(cmdLine.m_executable); if (exeName == "render-test") { - return _extractRenderTestRequirements(spawner, ioInfo); + return _extractRenderTestRequirements(cmdLine, ioInfo); } else if (exeName == "slangc") { - return _extractSlangCTestRequirements(spawner, ioInfo); + return _extractSlangCTestRequirements(cmdLine, ioInfo); } else if (exeName == "slang-reflection-test") { - return _extractReflectionTestRequirements(spawner, ioInfo); + return _extractReflectionTestRequirements(cmdLine, ioInfo); } SLANG_ASSERT(!"Unknown tool type"); @@ -768,20 +765,21 @@ static RenderApiFlags _getAvailableRenderApiFlags(TestContext* context) if (RenderApiUtil::calcHasApi(apiType)) { // Try starting up the device - OSProcessSpawner spawner; - spawner.pushExecutablePath(Path::combine(context->options.binDir, String("render-test") + osGetExecutableSuffix())); - _addRenderTestOptions(context->options, spawner); + CommandLine cmdLine; + cmdLine.setExecutablePath(Path::combine(context->options.binDir, String("render-test") + ProcessUtil::getExecutableSuffix())); + _addRenderTestOptions(context->options, cmdLine); // We just want to see if the device can be started up - spawner.pushArgument("-only-startup"); + cmdLine.addArg("-only-startup"); // Select what api to use StringBuilder builder; builder << "-" << RenderApiUtil::getApiName(apiType); - spawner.pushArgument(builder); + cmdLine.addArg(builder); // Run the render-test tool and see if the device could startup - if (spawnAndWaitSharedLibrary(context, "device-startup", spawner) == OSError::kOSError_None - && TestToolUtil::getReturnCodeFromInt(spawner.resultCode_) == ToolReturnCode::Success) + ExecuteResult exeRes; + if (SLANG_SUCCEEDED(spawnAndWaitSharedLibrary(context, "device-startup", cmdLine, exeRes)) + && TestToolUtil::getReturnCodeFromInt(exeRes.resultCode) == ToolReturnCode::Success) { availableRenderApiFlags |= RenderApiFlags(1) << int(apiType); } @@ -795,17 +793,17 @@ static RenderApiFlags _getAvailableRenderApiFlags(TestContext* context) return context->availableRenderApiFlags; } -ToolReturnCode getReturnCode(OSProcessSpawner& spawner) +ToolReturnCode getReturnCode(const ExecuteResult& exeRes) { - return TestToolUtil::getReturnCodeFromInt(spawner.getResultCode()); + return TestToolUtil::getReturnCodeFromInt(exeRes.resultCode); } -ToolReturnCode spawnAndWait(TestContext* context, const String& testPath, SpawnType spawnType, OSProcessSpawner& spawner) +ToolReturnCode spawnAndWait(TestContext* context, const String& testPath, SpawnType spawnType, const CommandLine& cmdLine, ExecuteResult& outExeRes) { if (context->isCollectingRequirements()) { // If we just want info... don't bother running anything - const SlangResult res = _extractTestRequirements(spawner, context->testRequirements); + const SlangResult res = _extractTestRequirements(cmdLine, context->testRequirements); // Keep compiler happy on release SLANG_UNUSED(res); SLANG_ASSERT(SLANG_SUCCEEDED(res)); @@ -815,37 +813,37 @@ ToolReturnCode spawnAndWait(TestContext* context, const String& testPath, SpawnT const auto& options = context->options; - OSError spawnResult = kOSError_OperationFailed; + SlangResult spawnResult = SLANG_FAIL; switch (spawnType) { case SpawnType::UseExe: { - spawnResult = spawnAndWaitExe(context, testPath, spawner); + spawnResult = spawnAndWaitExe(context, testPath, cmdLine, outExeRes); break; } case SpawnType::UseSharedLibrary: { - spawnResult = spawnAndWaitSharedLibrary(context, testPath, spawner); + spawnResult = spawnAndWaitSharedLibrary(context, testPath, cmdLine, outExeRes); break; } default: break; } - if (spawnResult != kOSError_None) + if (SLANG_FAILED(spawnResult)) { return ToolReturnCode::FailedToRun; } - return getReturnCode(spawner); + return getReturnCode(outExeRes); } -String getOutput(OSProcessSpawner& spawner) +String getOutput(const ExecuteResult& exeRes) { - OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); - - String standardOuptut = spawner.getStandardOutput(); - String standardError = spawner.getStandardError(); - + ExecuteResult::ResultCode resultCode = exeRes.resultCode; + + String standardOuptut = exeRes.standardOutput; + String standardError = exeRes.standardError; + // We construct a single output string that captures the results StringBuilder actualOutputBuilder; actualOutputBuilder.Append("result code = "); @@ -897,13 +895,13 @@ String findExpectedPath(const TestInput& input, const char* postFix) return ""; } -static void _initSlangCompiler(TestContext* context, OSProcessSpawner& spawnerOut) +static void _initSlangCompiler(TestContext* context, CommandLine& ioCmdLine) { - spawnerOut.pushExecutablePath(Path::combine(context->options.binDir, String("slangc") + osGetExecutableSuffix())); + ioCmdLine.setExecutablePath(Path::combine(context->options.binDir, String("slangc") + ProcessUtil::getExecutableSuffix())); if (context->options.verbosePaths) { - spawnerOut.pushArgument("-verbose-paths"); + ioCmdLine.addArg("-verbose-paths"); } } @@ -933,24 +931,25 @@ TestResult runSimpleTest(TestContext* context, TestInput& input) auto filePath999 = input.filePath; auto outputStem = input.outputStem; - OSProcessSpawner spawner; - _initSlangCompiler(context, spawner); + CommandLine cmdLine; + _initSlangCompiler(context, cmdLine); - spawner.pushArgument(filePath999); + cmdLine.addArg(filePath999); for( auto arg : input.testOptions->args ) { - spawner.pushArgument(arg); + cmdLine.addArg(arg); } - - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, spawner)); + + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); if (context->isCollectingRequirements()) { return TestResult::Pass; } - String actualOutput = getOutput(spawner); + String actualOutput = getOutput(exeRes); String expectedOutputPath = outputStem + ".expected"; String expectedOutput; @@ -1009,24 +1008,25 @@ TestResult runReflectionTest(TestContext* context, TestInput& input) auto filePath = input.filePath; auto outputStem = input.outputStem; - OSProcessSpawner spawner; - - spawner.pushExecutablePath(Path::combine(options.binDir, String("slang-reflection-test") + osGetExecutableSuffix())); - spawner.pushArgument(filePath); + CommandLine cmdLine; + + cmdLine.setExecutablePath(Path::combine(options.binDir, String("slang-reflection-test") + ProcessUtil::getExecutableSuffix())); + cmdLine.addArg(filePath); for( auto arg : input.testOptions->args ) { - spawner.pushArgument(arg); + cmdLine.addArg(arg); } - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, spawner)); + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); if (context->isCollectingRequirements()) { return TestResult::Pass; } - String actualOutput = getOutput(spawner); + String actualOutput = getOutput(exeRes); String expectedOutputPath = outputStem + ".expected"; String expectedOutput; @@ -1097,13 +1097,13 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input) auto filePath = input.filePath; auto outputStem = input.outputStem; - OSProcessSpawner actualSpawner; - OSProcessSpawner expectedSpawner; + CommandLine actualCmdLine; + CommandLine expectedCmdLine; - _initSlangCompiler(context, actualSpawner); - _initSlangCompiler(context, expectedSpawner); + _initSlangCompiler(context, actualCmdLine); + _initSlangCompiler(context, expectedCmdLine); - actualSpawner.pushArgument(filePath); + actualCmdLine.addArg(filePath); // TODO(JS): This should no longer be needed with TestInfo accumulated for a test @@ -1124,23 +1124,23 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input) { case SLANG_DXIL_ASM: { - expectedSpawner.pushArgument(filePath + ".hlsl"); - expectedSpawner.pushArgument("-pass-through"); - expectedSpawner.pushArgument("dxc"); + expectedCmdLine.addArg(filePath + ".hlsl"); + expectedCmdLine.addArg("-pass-through"); + expectedCmdLine.addArg("dxc"); break; } case SLANG_DXBC_ASM: { - expectedSpawner.pushArgument(filePath + ".hlsl"); - expectedSpawner.pushArgument("-pass-through"); - expectedSpawner.pushArgument("fxc"); + expectedCmdLine.addArg(filePath + ".hlsl"); + expectedCmdLine.addArg("-pass-through"); + expectedCmdLine.addArg("fxc"); break; } default: { - expectedSpawner.pushArgument(filePath + ".glsl"); - expectedSpawner.pushArgument("-pass-through"); - expectedSpawner.pushArgument("glslang"); + expectedCmdLine.addArg(filePath + ".glsl"); + expectedCmdLine.addArg("-pass-through"); + expectedCmdLine.addArg("glslang"); break; } } @@ -1148,16 +1148,17 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input) for( auto arg : input.testOptions->args ) { - actualSpawner.pushArgument(arg); - expectedSpawner.pushArgument(arg); + actualCmdLine.addArg(arg); + expectedCmdLine.addArg(arg); } - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, expectedSpawner)); + ExecuteResult expectedExeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, expectedCmdLine, expectedExeRes)); String expectedOutput; if (context->isExecuting()) { - expectedOutput = getOutput(expectedSpawner); + expectedOutput = getOutput(expectedExeRes); String expectedOutputPath = outputStem + ".expected"; try { @@ -1169,14 +1170,15 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input) } } - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, actualSpawner)); + ExecuteResult actualExeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, actualCmdLine, actualExeRes)); if (context->isCollectingRequirements()) { return TestResult::Pass; } - String actualOutput = getOutput(actualSpawner); + String actualOutput = getOutput(actualExeRes); TestResult result = TestResult::Pass; @@ -1190,7 +1192,7 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input) // to catch situations where, e.g., command-line options parsing // caused the same error in both the Slang and glslang cases. // - if( actualSpawner.getResultCode() != 0 ) + if(actualExeRes.resultCode != 0 ) { result = TestResult::Fail; } @@ -1214,29 +1216,30 @@ TestResult generateHLSLBaseline(TestContext* context, TestInput& input) auto filePath999 = input.filePath; auto outputStem = input.outputStem; - OSProcessSpawner spawner; - _initSlangCompiler(context, spawner); + CommandLine cmdLine; + _initSlangCompiler(context, cmdLine); - spawner.pushArgument(filePath999); + cmdLine.addArg(filePath999); for( auto arg : input.testOptions->args ) { - spawner.pushArgument(arg); + cmdLine.addArg(arg); } - spawner.pushArgument("-target"); - spawner.pushArgument("dxbc-assembly"); - spawner.pushArgument("-pass-through"); - spawner.pushArgument("fxc"); + cmdLine.addArg("-target"); + cmdLine.addArg("dxbc-assembly"); + cmdLine.addArg("-pass-through"); + cmdLine.addArg("fxc"); - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, spawner)); + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); if (context->isCollectingRequirements()) { return TestResult::Pass; } - String expectedOutput = getOutput(spawner); + String expectedOutput = getOutput(exeRes); String expectedOutputPath = outputStem + ".expected"; try { @@ -1262,24 +1265,25 @@ TestResult runHLSLComparisonTest(TestContext* context, TestInput& input) // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect - OSProcessSpawner spawner; - _initSlangCompiler(context, spawner); + CommandLine cmdLine; + _initSlangCompiler(context, cmdLine); - spawner.pushArgument(filePath999); + cmdLine.addArg(filePath999); for( auto arg : input.testOptions->args ) { - spawner.pushArgument(arg); + cmdLine.addArg(arg); } // TODO: The compiler should probably define this automatically... - spawner.pushArgument("-D"); - spawner.pushArgument("__SLANG__"); + cmdLine.addArg("-D"); + cmdLine.addArg("__SLANG__"); - spawner.pushArgument("-target"); - spawner.pushArgument("dxbc-assembly"); + cmdLine.addArg("-target"); + cmdLine.addArg("dxbc-assembly"); - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, spawner)); + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); if (context->isCollectingRequirements()) { @@ -1289,11 +1293,11 @@ TestResult runHLSLComparisonTest(TestContext* context, TestInput& input) // We ignore output to stdout, and only worry about what the compiler // wrote to stderr. - OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); - - String standardOutput = spawner.getStandardOutput(); - String standardError = spawner.getStandardError(); - + ExecuteResult::ResultCode resultCode = exeRes.resultCode; + + String standardOutput = exeRes.standardOutput; + String standardError = exeRes.standardError; + // We construct a single output string that captures the results StringBuilder actualOutputBuilder; actualOutputBuilder.Append("result code = "); @@ -1364,42 +1368,43 @@ TestResult doGLSLComparisonTestRun(TestContext* context, auto filePath999 = input.filePath; auto outputStem = input.outputStem; - OSProcessSpawner spawner; - _initSlangCompiler(context, spawner); + CommandLine cmdLine; + _initSlangCompiler(context, cmdLine); - spawner.pushArgument(filePath999); + cmdLine.addArg(filePath999); if( langDefine ) { - spawner.pushArgument("-D"); - spawner.pushArgument(langDefine); + cmdLine.addArg("-D"); + cmdLine.addArg(langDefine); } if( passThrough ) { - spawner.pushArgument("-pass-through"); - spawner.pushArgument(passThrough); + cmdLine.addArg("-pass-through"); + cmdLine.addArg(passThrough); } - spawner.pushArgument("-target"); - spawner.pushArgument("spirv-assembly"); + cmdLine.addArg("-target"); + cmdLine.addArg("spirv-assembly"); for( auto arg : input.testOptions->args ) { - spawner.pushArgument(arg); + cmdLine.addArg(arg); } - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, spawner)); + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); if (context->isCollectingRequirements()) { return TestResult::Pass; } - OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); + ExecuteResult::ResultCode resultCode = exeRes.resultCode; - String standardOuptut = spawner.getStandardOutput(); - String standardError = spawner.getStandardError(); + String standardOuptut = exeRes.standardOutput; + String standardError = exeRes.standardError; // We construct a single output string that captures the results StringBuilder outputBuilder; @@ -1458,12 +1463,12 @@ TestResult runGLSLComparisonTest(TestContext* context, TestInput& input) return TestResult::Pass; } -static void _addRenderTestOptions(const Options& options, OSProcessSpawner& spawner) +static void _addRenderTestOptions(const Options& options, CommandLine& ioCmdLine) { if (options.adapter.getLength()) { - spawner.pushArgument("-adapter"); - spawner.pushArgument(options.adapter); + ioCmdLine.addArg("-adapter"); + ioCmdLine.addArg(options.adapter); } } @@ -1473,25 +1478,25 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons auto filePath999 = input.filePath; auto outputStem = input.outputStem; - OSProcessSpawner spawner; + CommandLine cmdLine; - spawner.pushExecutablePath(Path::combine(context->options.binDir, String("render-test") + osGetExecutableSuffix())); - spawner.pushArgument(filePath999); + cmdLine.setExecutablePath(Path::combine(context->options.binDir, String("render-test") + ProcessUtil::getExecutableSuffix())); + cmdLine.addArg(filePath999); - _addRenderTestOptions(context->options, spawner); + _addRenderTestOptions(context->options, cmdLine); for (auto arg : input.testOptions->args) { - spawner.pushArgument(arg); + cmdLine.addArg(arg); } for (int i = 0; i < int(numLangOpts); ++i) { - spawner.pushArgument(langOpts[i]); + cmdLine.addArg(langOpts[i]); } - spawner.pushArgument("-o"); + cmdLine.addArg("-o"); auto actualOutputFile = outputStem + ".actual.txt"; - spawner.pushArgument(actualOutputFile); + cmdLine.addArg(actualOutputFile); if (context->isExecuting()) { @@ -1499,7 +1504,8 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons File::writeAllText(actualOutputFile, ""); } - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, spawner)); + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); if (context->isCollectingRequirements()) { @@ -1512,7 +1518,7 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons return TestResult::Fail; } - auto actualOutput = getOutput(spawner); + auto actualOutput = getOutput(exeRes); auto expectedOutput = getExpectedOutput(outputStem); if (actualOutput != expectedOutput) { @@ -1591,34 +1597,35 @@ TestResult doRenderComparisonTestRun(TestContext* context, TestInput& input, cha auto filePath = input.filePath; auto outputStem = input.outputStem; - OSProcessSpawner spawner; + CommandLine cmdLine; - spawner.pushExecutablePath(Path::combine(context->options.binDir, String("render-test") + osGetExecutableSuffix())); - spawner.pushArgument(filePath); + cmdLine.setExecutablePath(Path::combine(context->options.binDir, String("render-test") + ProcessUtil::getExecutableSuffix())); + cmdLine.addArg(filePath); - _addRenderTestOptions(context->options, spawner); + _addRenderTestOptions(context->options, cmdLine); for( auto arg : input.testOptions->args ) { - spawner.pushArgument(arg); + cmdLine.addArg(arg); } - spawner.pushArgument(langOption); - spawner.pushArgument("-o"); - spawner.pushArgument(outputStem + outputKind + ".png"); + cmdLine.addArg(langOption); + cmdLine.addArg("-o"); + cmdLine.addArg(outputStem + outputKind + ".png"); - TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, spawner)); + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); if (context->isCollectingRequirements()) { return TestResult::Pass; } - OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); - - String standardOutput = spawner.getStandardOutput(); - String standardError = spawner.getStandardError(); + ExecuteResult::ResultCode resultCode = exeRes.resultCode; + String standardOutput = exeRes.standardOutput; + String standardError = exeRes.standardError; + // We construct a single output string that captures the results StringBuilder outputBuilder; outputBuilder.Append("result code = "); diff --git a/tools/slang-test/test-context.cpp b/tools/slang-test/test-context.cpp index e4d1df449..0b17b3672 100644 --- a/tools/slang-test/test-context.cpp +++ b/tools/slang-test/test-context.cpp @@ -1,7 +1,7 @@ // test-context.cpp #include "test-context.h" -#include "os.h" +#include "../../source/core/slang-io.h" #include "../../source/core/slang-string-util.h" #include <stdio.h> diff --git a/tools/slang-test/test-reporter.cpp b/tools/slang-test/test-reporter.cpp index d2402d61b..f7ab3cf1e 100644 --- a/tools/slang-test/test-reporter.cpp +++ b/tools/slang-test/test-reporter.cpp @@ -1,8 +1,8 @@ // test-reporter.cpp #include "test-reporter.h" -#include "os.h" #include "../../source/core/slang-string-util.h" +#include "../../source/core/slang-process-util.h" #include <stdio.h> #include <stdlib.h> @@ -373,29 +373,31 @@ void TestReporter::_addResult(const TestInfo& info) break; } - OSProcessSpawner spawner; - spawner.pushExecutableName("appveyor"); - spawner.pushArgument("AddTest"); - spawner.pushArgument(info.name); - spawner.pushArgument("-FileName"); + CommandLine cmdLine; + cmdLine.setExecutableFilename("appveyor"); + cmdLine.addArg("AddTest"); + cmdLine.addArg(info.name); + cmdLine.addArg("-FileName"); // TODO: this isn't actually a file name in all cases - spawner.pushArgument(info.name); - spawner.pushArgument("-Framework"); - spawner.pushArgument("slang-test"); - spawner.pushArgument("-Outcome"); - spawner.pushArgument(resultString); - - auto err = spawner.spawnAndWaitForCompletion(); - - if (err != kOSError_None) + cmdLine.addArg(info.name); + cmdLine.addArg("-Framework"); + cmdLine.addArg("slang-test"); + cmdLine.addArg("-Outcome"); + cmdLine.addArg(resultString); + + ExecuteResult exeRes; + SlangResult res = ProcessUtil::execute(cmdLine, exeRes); + + if (SLANG_FAILED(res)) { messageFormat(TestMessageType::Info, "failed to add appveyor test results for '%S'\n", info.name.toWString().begin()); #if 0 - fprintf(stderr, "[%d] TEST RESULT: %s {%d} {%s} {%s}\n", err, spawner.commandLine_.getBuffer(), - spawner.getResultCode(), - spawner.getStandardOutput().begin(), - spawner.getStandardError().begin()); + String cmdLineString = ProcessUtil::getCommandLineString(cmdLine); + fprintf(stderr, "[%d] TEST RESULT: %s {%d} {%s} {%s}\n", err, cmdLineString.getBuffer(), + exeRes.resultCode, + exeRes.standardOutput.begin(), + exeRes.standardError.begin()); #endif } |
