Added scripting quality of life features #299
|
@ -190,8 +190,9 @@ namespace SHADE
|
|||
oss << "[ScriptEngine] Building " << (debug ? " debug " : "") << "Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
|
||||
SHLOG_INFO(oss.str());
|
||||
oss.str("");
|
||||
const bool BUILD_SUCCESS = SHExecUtilties::ExecBlockingCommand(generateBuildCommand(debug)) == 0;
|
||||
if (BUILD_SUCCESS)
|
||||
const auto BUILD_SUCCESS = SHExecUtilties::ExecBlockingProcess(L"C:\\Program Files\\dotnet\\dotnet.exe", generateBuildCommand(debug), true, true);
|
||||
SHLOG_WARNING("Build Output: {}", SHStringUtilities::WstrToStr(BUILD_SUCCESS.StdOutput));
|
||||
if (BUILD_SUCCESS.Code == 0)
|
||||
{
|
||||
// Copy to built dll to the working directory and replace
|
||||
if (!copyFile("./tmp/SHADE_Scripting.dll", "SHADE_Scripting.dll", std::filesystem::copy_options::overwrite_existing))
|
||||
|
@ -235,7 +236,7 @@ namespace SHADE
|
|||
LoadScriptAssembly();
|
||||
}
|
||||
|
||||
return BUILD_SUCCESS;
|
||||
return BUILD_SUCCESS.Code == 0;
|
||||
}
|
||||
|
||||
void SHScriptEngine::GenerateScriptsCsProjFile(const std::filesystem::path& path) const
|
||||
|
@ -626,9 +627,9 @@ namespace SHADE
|
|||
std::wstring SHScriptEngine::generateBuildCommand(bool debug)
|
||||
{
|
||||
std::wostringstream oss;
|
||||
oss << "dotnet build \"" << SHStringUtilities::StrToWstr(CSPROJ_PATH) << "\" -c ";
|
||||
oss << " build \"" << SHStringUtilities::StrToWstr(std::filesystem::absolute(CSPROJ_PATH).string()) << "\" -c ";
|
||||
oss << debug ? "Debug" : "Release";
|
||||
oss << " -o \"./tmp/\" -fl -flp:LogFile=build.log;Verbosity=quiet -r \"win-x64\"";
|
||||
oss << " -o \"./tmp/\" -r \"win-x64\"";
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,44 +23,32 @@ namespace SHADE
|
|||
/*-----------------------------------------------------------------------------------*/
|
||||
PROCESS_INFORMATION SHExecUtilties::ExecProcess(const std::wstring& path, const std::wstring& args)
|
||||
{
|
||||
STARTUPINFOW startInfo;
|
||||
PROCESS_INFORMATION procInfo;
|
||||
ZeroMemory(&startInfo, sizeof(startInfo));
|
||||
ZeroMemory(&procInfo, sizeof(procInfo));
|
||||
startInfo.cb = sizeof(startInfo);
|
||||
|
||||
std::wstring argsWstr = args;
|
||||
|
||||
// Start Process
|
||||
const auto SUCCESS = CreateProcess
|
||||
(
|
||||
path.data(), argsWstr.data(),
|
||||
nullptr, nullptr, false, NULL, nullptr, nullptr,
|
||||
&startInfo, &procInfo
|
||||
);
|
||||
|
||||
// Error Check
|
||||
if (!SUCCESS)
|
||||
{
|
||||
auto err = GetLastError();
|
||||
std::ostringstream oss;
|
||||
oss << "[SHExecUtilties] Failed to launch process. Error code: " << std::hex << err
|
||||
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
|
||||
throw std::runtime_error(oss.str());
|
||||
return execProcess(path, args, nullptr, nullptr);
|
||||
}
|
||||
|
||||
return procInfo;
|
||||
ExecResult SHExecUtilties::ExecBlockingProcess(const std::wstring& path, const std::wstring& args, bool output, bool errorOutput)
|
||||
{
|
||||
// Create pipes for stdout and stderr if specified
|
||||
HANDLE stdoutReadPipe = nullptr;
|
||||
HANDLE stdoutWritePipe = nullptr;
|
||||
HANDLE stderrReadPipe = nullptr;
|
||||
HANDLE stderrWritePipe = nullptr;
|
||||
if (output)
|
||||
{
|
||||
std::tie(stdoutReadPipe, stdoutWritePipe) = createIoPipes();
|
||||
}
|
||||
if (errorOutput)
|
||||
{
|
||||
std::tie(stderrReadPipe, stderrWritePipe) = createIoPipes();
|
||||
}
|
||||
|
||||
DWORD SHExecUtilties::ExecBlockingProcess(const std::wstring& path, const std::wstring& args)
|
||||
{
|
||||
PROCESS_INFORMATION procInfo = ExecProcess(path, args);
|
||||
PROCESS_INFORMATION procInfo = execProcess(path, args, stdoutWritePipe, stderrWritePipe);
|
||||
|
||||
// Wait for execution to end
|
||||
DWORD status;
|
||||
ExecResult result;
|
||||
while (true)
|
||||
{
|
||||
const auto EXEC_SUCCESS = GetExitCodeProcess(procInfo.hProcess, &status);
|
||||
const auto EXEC_SUCCESS = GetExitCodeProcess(procInfo.hProcess, &result.Code);
|
||||
if (!EXEC_SUCCESS)
|
||||
{
|
||||
auto err = GetLastError();
|
||||
|
@ -71,11 +59,24 @@ namespace SHADE
|
|||
}
|
||||
|
||||
// Break only if process ends
|
||||
if (status != STILL_ACTIVE)
|
||||
if (result.Code != STILL_ACTIVE)
|
||||
{
|
||||
// Get stdout/stderror output
|
||||
if (stdoutReadPipe)
|
||||
{
|
||||
readPipeData(stdoutReadPipe, stdoutWritePipe, result.StdOutput);
|
||||
}
|
||||
if (stderrReadPipe)
|
||||
{
|
||||
readPipeData(stderrReadPipe, stderrWritePipe, result.StdErrOutput);
|
||||
}
|
||||
|
||||
// Close the process and threads for that process
|
||||
CloseHandle(procInfo.hProcess);
|
||||
CloseHandle(procInfo.hThread);
|
||||
return status;
|
||||
|
||||
// Return results
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,12 +93,102 @@ namespace SHADE
|
|||
);
|
||||
}
|
||||
|
||||
DWORD SHExecUtilties::ExecBlockingCommand(const std::wstring& command)
|
||||
ExecResult SHExecUtilties::ExecBlockingCommand(const std::wstring& command, bool output, bool errorOutput)
|
||||
{
|
||||
return ExecBlockingProcess
|
||||
(
|
||||
L"C:\\Windows\\system32\\cmd.exe",
|
||||
L"/K \"" + command + L" & exit\""
|
||||
L"/K \"" + command + L" & exit\"",
|
||||
output,
|
||||
errorOutput
|
||||
);
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION SHExecUtilties::execProcess(const std::wstring& path, const std::wstring& args, HANDLE outputWritePipe, HANDLE errorOutputWritePipe)
|
||||
{
|
||||
STARTUPINFOW startInfo;
|
||||
PROCESS_INFORMATION procInfo;
|
||||
ZeroMemory(&startInfo, sizeof(startInfo));
|
||||
ZeroMemory(&procInfo, sizeof(procInfo));
|
||||
startInfo.cb = sizeof(startInfo);
|
||||
if (outputWritePipe)
|
||||
{
|
||||
startInfo.hStdOutput = outputWritePipe;
|
||||
startInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
if (errorOutputWritePipe)
|
||||
{
|
||||
startInfo.hStdError = errorOutputWritePipe;
|
||||
startInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
|
||||
std::wstring argsWstr = args;
|
||||
|
||||
// Start Process
|
||||
const auto SUCCESS = CreateProcess
|
||||
(
|
||||
path.data(), argsWstr.data(),
|
||||
nullptr, nullptr, true, NULL, nullptr, nullptr,
|
||||
&startInfo, &procInfo
|
||||
);
|
||||
|
||||
// Error Check
|
||||
if (!SUCCESS)
|
||||
{
|
||||
auto err = GetLastError();
|
||||
std::ostringstream oss;
|
||||
oss << "[SHExecUtilties] Failed to launch process. Error code: " << std::hex << err
|
||||
<< " (" << SHStringUtilities::GetWin32ErrorMessage(err) << ")";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
return procInfo;
|
||||
}
|
||||
|
||||
std::pair<HANDLE, HANDLE> SHExecUtilties::createIoPipes()
|
||||
{
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = true;
|
||||
saAttr.lpSecurityDescriptor = nullptr;
|
||||
|
||||
// First: Read | Second: Write
|
||||
std::pair<HANDLE, HANDLE> output = { nullptr, nullptr };
|
||||
if (CreatePipe(&output.first, &output.second, &saAttr, 0))
|
||||
{
|
||||
if (!SetHandleInformation(output.first, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
SHLOG_ERROR("[SHExecUtilities] Failed to initialize pipe for process IO.");
|
||||
CloseHandle(output.first);
|
||||
CloseHandle(output.second);
|
||||
output = { nullptr, nullptr };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SHLOG_ERROR("[SHExecUtilities] Failed to create pipe for process IO.");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void SHExecUtilties::readPipeData(HANDLE readPipe, HANDLE writePipe, std::wstring& output)
|
||||
{
|
||||
CloseHandle(writePipe);
|
||||
|
||||
LARGE_INTEGER stdoutSize = {};
|
||||
while (true)
|
||||
{
|
||||
std::array<char, 256> buffer{};
|
||||
DWORD bytesRead = 0;
|
||||
const auto RESULT = ReadFile(readPipe, buffer.data(), buffer.size(), &bytesRead, nullptr); // Blocking call here?
|
||||
if (!RESULT || bytesRead <= 0)
|
||||
break;
|
||||
output.insert(output.end(), buffer.data(), buffer.data() + bytesRead);
|
||||
}
|
||||
|
||||
CloseHandle(readPipe);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,16 @@ of DigiPen Institute of Technology is prohibited.
|
|||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the result of an execution of a process.
|
||||
/// </summary>
|
||||
struct SH_API ExecResult final
|
||||
{
|
||||
DWORD Code;
|
||||
std::wstring StdOutput;
|
||||
std::wstring StdErrOutput;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Static class containing functions for executing external processes or commands.
|
||||
/// </summary>
|
||||
|
@ -43,11 +53,13 @@ namespace SHADE
|
|||
/// </summary>
|
||||
/// <param name="path">Path to the processs to start.</param>
|
||||
/// <param name="args">Arguments to pass to the process.</param>
|
||||
/// <param name="output">If true, stdout will be routed to return.</param>
|
||||
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
|
||||
/// <returns>Return value of the process.</returns>
|
||||
/// <exception cref="std::runtime_error">
|
||||
/// Thrown if failed to start the process.
|
||||
/// </exception>
|
||||
static DWORD ExecBlockingProcess(const std::wstring& path, const std::wstring& args);
|
||||
static ExecResult ExecBlockingProcess(const std::wstring& path, const std::wstring& args, bool output = false, bool errorOutput = false);
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Command Execution Functions */
|
||||
|
@ -69,16 +81,51 @@ namespace SHADE
|
|||
/// executing.
|
||||
/// </summary>
|
||||
/// <param name="command">Command to execute.</param>
|
||||
/// <param name="output">If true, stdout will be routed to return.</param>
|
||||
/// <param name="errorOutput">If true, outstderr will be routed to return.</param>
|
||||
/// <returns>Return value of the process.</returns>
|
||||
/// <exception cref="std::runtime_error">
|
||||
/// Thrown if failed to start the process.
|
||||
/// </exception>
|
||||
static DWORD ExecBlockingCommand(const std::wstring& command);
|
||||
static ExecResult ExecBlockingCommand(const std::wstring& command, bool output = false, bool errorOutput = false);
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* PowerShell Execution Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Executes a specified command in PowerShell.
|
||||
/// This call does not wait for the command to finish executing.
|
||||
/// </summary>
|
||||
/// <param name="command">Command to execute.</param>
|
||||
/// <returns>
|
||||
/// Information about the started cmd process that executes the command.
|
||||
/// </returns>
|
||||
/// <exception cref="std::runtime_error">
|
||||
/// Thrown if failed to start the process.
|
||||
/// </exception>
|
||||
static PROCESS_INFORMATION ExecPowerShellCommand(const std::wstring& command);
|
||||
/// <summary>
|
||||
/// Executes a specified command in PowerShell and waits for that process to finish
|
||||
/// executing.
|
||||
/// </summary>
|
||||
/// <param name="command">Command to execute.</param>
|
||||
/// <returns>Return value of the process.</returns>
|
||||
/// <exception cref="std::runtime_error">
|
||||
/// Thrown if failed to start the process.
|
||||
/// </exception>
|
||||
static DWORD ExecBlockingPowerShellCommand(const std::wstring& command);
|
||||
|
||||
private:
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Constructors/Destructors */
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
SHExecUtilties() = delete;
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
static PROCESS_INFORMATION execProcess(const std::wstring& path, const std::wstring& args, HANDLE outputWritePipe, HANDLE errorOutputWritePipe);
|
||||
static std::pair<HANDLE, HANDLE> createIoPipes();
|
||||
static void readPipeData(HANDLE readPipe, HANDLE writePipe, std::wstring& output);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue