| ; | ; | ||||
| // Build up the archive command | // Build up the archive command | ||||
| archive_spec ar; | archive_spec ar; | ||||
| auto ar_cwd = env.output_root; | |||||
| ar.input_files = std::move(objects); | ar.input_files = std::move(objects); | ||||
| ar.out_path = env.output_root / calc_archive_file_path(env.toolchain); | ar.out_path = env.output_root / calc_archive_file_path(env.toolchain); | ||||
| auto ar_cmd = env.toolchain.create_archive_command(ar, fs::current_path(), env.knobs); | |||||
| auto ar_cmd = env.toolchain.create_archive_command(ar, ar_cwd, env.knobs); | |||||
| // `out_relpath` is purely for the benefit of the user to have a short name | // `out_relpath` is purely for the benefit of the user to have a short name | ||||
| // in the logs | // in the logs | ||||
| // Do it! | // Do it! | ||||
| dds_log(info, "[{}] Archive: {}", _qual_name, out_relpath); | dds_log(info, "[{}] Archive: {}", _qual_name, out_relpath); | ||||
| auto&& [dur_ms, ar_res] = timed<std::chrono::milliseconds>([&] { return run_proc(ar_cmd); }); | |||||
| auto&& [dur_ms, ar_res] = timed<std::chrono::milliseconds>( | |||||
| [&] { return run_proc(proc_options{.command = ar_cmd, .cwd = ar_cwd}); }); | |||||
| dds_log(info, "[{}] Archive: {} - {:L}ms", _qual_name, out_relpath, dur_ms.count()); | dds_log(info, "[{}] Archive: {} - {:L}ms", _qual_name, out_relpath, dur_ms.count()); | ||||
| // Check, log, and throw | // Check, log, and throw |
| #pragma once | #pragma once | ||||
| #include <chrono> | #include <chrono> | ||||
| #include <filesystem> | |||||
| #include <optional> | #include <optional> | ||||
| #include <string> | #include <string> | ||||
| #include <string_view> | #include <string_view> | ||||
| } | } | ||||
| struct proc_result { | struct proc_result { | ||||
| int signal = 0; | |||||
| int retc = 0; | |||||
| int signal = 0; | |||||
| int retc = 0; | |||||
| bool timed_out = false; | bool timed_out = false; | ||||
| std::string output; | std::string output; | ||||
| struct proc_options { | struct proc_options { | ||||
| std::vector<std::string> command; | std::vector<std::string> command; | ||||
| std::optional<std::filesystem::path> cwd = std::nullopt; | |||||
| /** | /** | ||||
| * Timeout for the subprocess, in milliseconds. If zero, will wait forever | * Timeout for the subprocess, in milliseconds. If zero, will wait forever | ||||
| */ | */ |
| #ifndef _WIN32 | #ifndef _WIN32 | ||||
| #include "./proc.hpp" | #include "./proc.hpp" | ||||
| #include <dds/util/fs.hpp> | |||||
| #include <dds/util/log.hpp> | #include <dds/util/log.hpp> | ||||
| #include <dds/util/signal.hpp> | #include <dds/util/signal.hpp> | ||||
| } | } | ||||
| } | } | ||||
| ::pid_t | |||||
| spawn_child(const std::vector<std::string>& command, int stdout_pipe, int close_me) noexcept { | |||||
| ::pid_t spawn_child(const proc_options& opts, int stdout_pipe, int close_me) noexcept { | |||||
| // We must allocate BEFORE fork(), since the CRT might stumble with malloc()-related locks that | // We must allocate BEFORE fork(), since the CRT might stumble with malloc()-related locks that | ||||
| // are held during the fork(). | // are held during the fork(). | ||||
| std::vector<const char*> strings; | std::vector<const char*> strings; | ||||
| strings.reserve(command.size() + 1); | |||||
| for (auto& s : command) { | |||||
| strings.reserve(opts.command.size() + 1); | |||||
| for (auto& s : opts.command) { | |||||
| strings.push_back(s.data()); | strings.push_back(s.data()); | ||||
| } | } | ||||
| strings.push_back(nullptr); | strings.push_back(nullptr); | ||||
| std::string workdir = opts.cwd.value_or(fs::current_path()).string(); | |||||
| auto child_pid = ::fork(); | auto child_pid = ::fork(); | ||||
| if (child_pid != 0) { | if (child_pid != 0) { | ||||
| return child_pid; | return child_pid; | ||||
| check_rc(rc != -1, "Failed to dup2 stdout"); | check_rc(rc != -1, "Failed to dup2 stdout"); | ||||
| rc = dup2(stdout_pipe, STDERR_FILENO); | rc = dup2(stdout_pipe, STDERR_FILENO); | ||||
| check_rc(rc != -1, "Failed to dup2 stderr"); | check_rc(rc != -1, "Failed to dup2 stderr"); | ||||
| rc = ::chdir(workdir.data()); | |||||
| check_rc(rc != -1, "Failed to chdir() for subprocess"); | |||||
| ::execvp(strings[0], (char* const*)strings.data()); | ::execvp(strings[0], (char* const*)strings.data()); | ||||
| int read_pipe = stdio_pipe[0]; | int read_pipe = stdio_pipe[0]; | ||||
| int write_pipe = stdio_pipe[1]; | int write_pipe = stdio_pipe[1]; | ||||
| auto child = spawn_child(opts.command, write_pipe, read_pipe); | |||||
| auto child = spawn_child(opts, write_pipe, read_pipe); | |||||
| ::close(write_pipe); | ::close(write_pipe); | ||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||
| #include "./proc.hpp" | #include "./proc.hpp" | ||||
| #include <dds/util/fs.hpp> | |||||
| #include <dds/util/log.hpp> | #include <dds/util/log.hpp> | ||||
| #include <fmt/core.h> | #include <fmt/core.h> | ||||
| true, | true, | ||||
| CREATE_NEW_PROCESS_GROUP, | CREATE_NEW_PROCESS_GROUP, | ||||
| nullptr, | nullptr, | ||||
| nullptr, | |||||
| opts.cwd.value_or(fs::current_path()).c_str(), | |||||
| &startup_info, | &startup_info, | ||||
| &proc_info); | &proc_info); | ||||
| if (!okay) { | if (!okay) { |