/* * The MIT License (MIT) * * Copyright (c) 2015-2022 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #ifdef _WIN32 // cppcheck-suppress definePrefix #define WIN32_LEAN_AND_MEAN #include #else #include #endif namespace migraphx { inline namespace MIGRAPHX_INLINE_NS { MIGRAPHX_DECLARE_ENV_VAR(MIGRAPHX_TRACE_CMD_EXECUTE) #ifndef _WIN32 template int exec(const std::string& cmd, const char* type, F f) { int ec = 0; if(enabled(MIGRAPHX_TRACE_CMD_EXECUTE{})) std::cout << cmd << std::endl; auto closer = [&](FILE* stream) { auto status = pclose(stream); ec = WIFEXITED(status) ? WEXITSTATUS(status) : 0; // NOLINT }; { // TODO: Use execve instead of popen std::unique_ptr pipe(popen(cmd.c_str(), type), closer); // NOLINT if(not pipe) MIGRAPHX_THROW("popen() failed: " + cmd); f(pipe.get()); } return ec; } int exec(const std::string& cmd) { auto std_out{[](const char* x) { std::cout << x; }}; return exec(cmd, "r", [&](FILE* f) { std::array buffer{}; while(fgets(buffer.data(), buffer.size(), f) != nullptr) std_out(buffer.data()); }); } int exec(const std::string& cmd, std::function std_in) { return exec(cmd, "w", [&](FILE* f) { std_in([&](const char* buffer, std::size_t n) { std::fwrite(buffer, 1, n, f); }); }); } #else #define MIGRAPHX_PROCESS_BUFSIZE 4096 class pipe { public: explicit pipe(bool inherit_handle = true) { SECURITY_ATTRIBUTES attrs; attrs.nLength = sizeof(SECURITY_ATTRIBUTES); attrs.bInheritHandle = inherit_handle ? TRUE : FALSE; attrs.lpSecurityDescriptor = nullptr; if(CreatePipe(&hRead_, &hWrite_, &attrs, 0) == FALSE) throw GetLastError(); if(SetHandleInformation(&hRead_, HANDLE_FLAG_INHERIT, 0) == FALSE) throw GetLastError(); } ~pipe() { close_write_handle(); close_read_handle(); } HANDLE get_read_handle() const { return hRead_; } void close_read_handle() { if(hRead_ != nullptr) { CloseHandle(hRead_); hRead_ = nullptr; } } HANDLE get_write_handle() const { return hWrite_; } void close_write_handle() { if(hWrite_ != nullptr) { CloseHandle(hWrite_); hWrite_ = nullptr; } } private: HANDLE hWrite_{nullptr}, hRead_{nullptr}; }; int exec(const std::string& cmd) { try { if(enabled(MIGRAPHX_TRACE_CMD_EXECUTE{})) std::cout << cmd << std::endl; pipe stdin_{}, stdout_{}; STARTUPINFO info; PROCESS_INFORMATION processInfo; ZeroMemory(&info, sizeof(STARTUPINFO)); info.cb = sizeof(STARTUPINFO); info.hStdError = stdout_.get_write_handle(); info.hStdOutput = stdout_.get_write_handle(); info.hStdInput = stdin_.get_read_handle(); info.dwFlags |= STARTF_USESTDHANDLES; ZeroMemory(&processInfo, sizeof(processInfo)); LPSTR lpCmdLn{const_cast(cmd.c_str())}; BOOL bSuccess = CreateProcess( nullptr, lpCmdLn, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &info, &processInfo); if(bSuccess == FALSE) return GetLastError(); DWORD dwRead, dwWritten; TCHAR chBuf[MIGRAPHX_PROCESS_BUFSIZE]; HANDLE hStdOut{GetStdHandle(STD_OUTPUT_HANDLE)}; for(;;) { BOOL bRead = ReadFile( stdout_.get_read_handle(), chBuf, MIGRAPHX_PROCESS_BUFSIZE, &dwRead, nullptr); if(bRead == FALSE) { if(GetLastError() != ERROR_MORE_DATA) break; } if(dwRead == 0) break; BOOL bWrite = WriteFile(hStdOut, chBuf, dwRead, &dwWritten, nullptr); if(bWrite == FALSE) break; } WaitForSingleObject(processInfo.hProcess, INFINITE); DWORD status{}; GetExitCodeProcess(processInfo.hProcess, &status); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); return static_cast(status); } // cppcheck-suppress catchExceptionByValue catch(DWORD lastError) { return lastError; } } #endif struct process_impl { std::string command{}; fs::path cwd{}; std::string get_command() const { std::string result; if(not cwd.empty()) result += "cd " + cwd.string() + "; "; result += command; return result; } template void check_exec(Ts&&... xs) const { int ec = migraphx::exec(std::forward(xs)...); if(ec != 0) MIGRAPHX_THROW("Command " + get_command() + " exited with status " + std::to_string(ec)); } }; process::process(const std::string& cmd) : impl(std::make_unique()) { impl->command = cmd; } process::process(process&&) noexcept = default; process& process::operator=(process rhs) { std::swap(impl, rhs.impl); return *this; } process::~process() noexcept = default; process& process::cwd(const fs::path& p) { impl->cwd = p; return *this; } void process::exec() { impl->check_exec(impl->get_command()); } void process::write(std::function pipe_in) { #ifdef _WIN32 impl->check_exec(impl->get_command()); #else impl->check_exec(impl->get_command(), std::move(pipe_in)); #endif } } // namespace MIGRAPHX_INLINE_NS } // namespace migraphx