; | ; | ||||
// 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) { |