// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "execute.h" #include "ctor.h" #if !defined(_WIN32) #include #include #include #include #else #define WINDOWS_LEAN_AND_MEAN #include #undef max #endif #include #include #include #include #include /* https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ https://github.com/famzah/popen-noshell/commit/1f9eaf4eeef348d1efe0f3c7fe8ab670653cfbb1 https://blog.famzah.net/2018/12/19/posix_spawn-performance-benchmarks-and-usage-examples/ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork-and-vfork/5207945#5207945 */ namespace { class Env : public std::vector { public: Env(const std::vector& args) { for(const auto& arg : args) { #if !defined(_WIN32) push_back(strdup(arg.data())); #else push_back(_strdup(arg.data())); #endif } push_back(nullptr); } ~Env() { for(auto ptr : *this) { free(ptr); } } }; #if !defined(_WIN32) int parent_waitpid(pid_t pid) { int status{}; if(waitpid(pid, &status, 0) != pid) { return 1; } return WEXITSTATUS(status); } #endif //_WIN32 } // namespace :: #if !defined(_WIN32) extern char **environ; // see 'man environ' #endif #if defined(_WIN32) //Returns the last Win32 error, in string format. Returns an empty string if there is no error. std::string GetLastErrorAsString() { //Get the error message ID, if any. DWORD errorMessageID = ::GetLastError(); if(errorMessageID == 0) { return std::string(); //No error message has been recorded } LPSTR messageBuffer = nullptr; //Ask Win32 to give us the string version of that message ID. //The parameters we pass in, tell Win32 to create the buffer that holds the message for us // (because we don't yet know how long the message string will be). size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); //Copy the error message into a std::string. std::string message(messageBuffer, size); //Free the Win32's string's buffer. LocalFree(messageBuffer); return message; } #endif // _WIN32 int execute(const ctor::settings& settings, const std::string& command, const std::vector& args, const std::map& env, [[maybe_unused]] bool terminate) { std::vector argv; argv.push_back(command.data()); for(const auto& arg : args) { argv.push_back(arg.data()); } argv.push_back(nullptr); std::string cmd; for(const auto& arg : argv) { if(arg == nullptr) { break; } if(!cmd.empty()) { cmd += " "; } cmd += arg; } if(settings.verbose) { std::cout << cmd << std::endl; } #if !defined(_WIN32) #if 1 auto pid = vfork(); if(pid == 0) { std::vector venv; for(const auto& [key, value] : env) { venv.push_back(key + "=" + value); } for(auto current = environ; *current; ++current) { venv.emplace_back(*current); } Env penv(venv); execve(command.data(), (char**)argv.data(), penv.data()); std::cout << "Could not execute " << command << ": " << strerror(errno) << "\n"; _exit(1); // execve only returns if an error occurred } return parent_waitpid(pid); #elif 0 pid_t pid; std::vector venv; for(const auto& [key, value] : env) { venv.push_back(key + "=" + value); } Env penv(venv); if(posix_spawn(&pid, command.data(), nullptr, nullptr, (char**)argv.data(), penv.data())) { return 1; } return parent_waitpid(pid); #else (void)parent_waitpid; return system(cmd.data()); #endif #else // _WIN32 std::map new_env; { auto env_strings = GetEnvironmentStrings(); const char* ptr = env_strings; std::string key; std::string value; bool key_state{true}; while(true) { if(key_state) // searching for key { if(*ptr == '\0') { break; } if(*ptr != '=') { key += *ptr; } else { key_state = false; } } else { if(*ptr != '\0') { value += *ptr; } else { new_env.insert({key, value}); key = {}; value = {}; key_state = true; } } ++ptr; } FreeEnvironmentStrings(env_strings); } // add new env vars (if any) for(const auto& [key, value] : env) { //if(key == "CL" || key == "LINK" || key == "LIB") { new_env[key] = value; } } std::string env_str; for(const auto& [k,v] : new_env) { env_str += k + "=" + v; env_str += '\0'; } env_str += '\0'; // std::cout <<"#### Length of new env: " << env_str.size() << "\n"; STARTUPINFO si{}; // si.hStdInput = GetStdHandle(((DWORD)-10)/*STD_INPUT_HANDLE*/); // si.hStdOutput = GetStdHandle(((DWORD)-11)/*STD_OUTPUT_HANDLE*/); // si.hStdError = GetStdHandle(((DWORD)-12)/*STD_ERROR_HANDLE*/); // si.dwFlags = /*STARTF_USESTDHANDLES*/0x00000100; PROCESS_INFORMATION pi{}; si.cb = sizeof(si); // TODO: Use SetDllDirectory(...) to set DLL search directory if(terminate) { char tmpdir[MAX_PATH+1]; int cnt{0}; // The returned string ends with a backslash if(GetTempPathA(sizeof(tmpdir), tmpdir) == 0) { std::cerr << "Could not read TMP folder\n"; return GetLastError(); } char source[MAX_PATH]; HMODULE module = GetModuleHandle(0); GetModuleFileNameA(module, source, MAX_PATH); while(true) { if(cnt > 10) // If we need to try more than 10 times something is wrong { return 1; } std::filesystem::path tmp_file = settings.builddir; tmp_file /= "tmp"; std::string target = tmp_file.string() + "-" + std::to_string(cnt); std::cout << "move " << source << " => " << target <