use super::BuildTargets;
use crate::cli::Cli;
use monotony::{MonotonicClock, StdMonotonicClock};
use std::{io, path::Path, process::Command};
mod child_exit;
mod command_list_telemetry;
mod command_logging;
mod dyndep_files;
mod dyndep_retention;
mod dyndep_telemetry;
mod failure_attribution;
mod file_io;
mod ninja_program;
mod ninja_status;
mod output_forwarding;
mod paths;
mod redaction;
mod streaming;
#[cfg(test)]
mod tests;
use child_exit::{ExitFailureContext, check_exit_status_with_context};
use command_logging::{
CommandLogContext, command_span, log_command_execution, log_command_spawn_failure,
};
pub(crate) use dyndep_files::materialize_dyndep_files;
pub use dyndep_retention::MAX_RETAINED_DYNDEP_FILES;
pub(crate) use dyndep_retention::{DyndepPublicationLease, prune_dyndep_cache};
pub use file_io::*;
pub use ninja_program::resolve_ninja_program;
#[cfg(doctest)]
pub use ninja_program::resolve_ninja_program_utf8;
#[cfg(test)]
use ninja_program::{resolve_ninja_program_utf8_with, resolve_ninja_program_with};
use output_forwarding::{StatusObserver, spawn_and_stream_output};
mod command_env;
mod configure;
mod request;
mod stderr_mode;
pub use command_env::CommandEnv;
use configure::{configure_ninja_build_command, configure_ninja_tool_command};
pub use paths::*;
pub use request::{NinjaBuildRequest, NinjaToolRequest};
pub use stderr_mode::StderrMode;
struct CommandExecutionContext<'a, Clock> {
operation: &'a str,
stderr_mode: StderrMode,
captures_ninja_failure_output: bool,
clock: &'a Clock,
}
#[cfg(doctest)]
pub mod doc {
pub use super::redaction::{
CommandArg, is_sensitive_arg, redact_argument, redact_sensitive_args,
};
pub use super::{
create_temp_ninja_file, resolve_ninja_program, resolve_ninja_program_utf8,
write_ninja_file, write_text_file_utf8,
};
}
fn run_command_and_stream_with_context<Clock: MonotonicClock>(
mut cmd: Command,
status_observer: Option<StatusObserver<'_>>,
execution: &CommandExecutionContext<'_, Clock>,
) -> io::Result<()> {
let context = CommandLogContext::from_command(&cmd);
let span = command_span(&context, execution.operation, execution.stderr_mode);
let _entered = span.enter();
log_command_execution(&context, execution.operation, execution.stderr_mode);
let started_at = execution.clock.now();
let child = cmd.spawn().inspect_err(|err| {
tracing::Span::current().record("failure_category", "spawn");
log_command_spawn_failure(&context, execution.operation, execution.stderr_mode, err);
})?;
let (status, command_list_failure) = spawn_and_stream_output(
child,
status_observer,
execution.stderr_mode,
execution.captures_ninja_failure_output,
)?;
let failure_context = ExitFailureContext {
operation: execution.operation,
stderr_mode: execution.stderr_mode,
command_list_failure: command_list_failure.as_ref(),
clock: execution.clock,
started_at,
};
check_exit_status_with_context(status, &context, &failure_context)
}
pub fn run_ninja_with(request: &NinjaBuildRequest<'_>) -> io::Result<()> {
run_ninja_with_clock(request, &StdMonotonicClock)
}
fn run_ninja_with_clock(
request: &NinjaBuildRequest<'_>,
clock: &impl MonotonicClock,
) -> io::Result<()> {
run_ninja_build_internal(*request, None, clock)
}
pub fn run_ninja_tool_with(request: &NinjaToolRequest<'_>) -> io::Result<()> {
run_ninja_tool_internal(*request, None, &StdMonotonicClock)
}
struct NinjaInternalRequest<'request, 'observer> {
program: &'request Path,
stderr_mode: StderrMode,
status_observer: Option<StatusObserver<'observer>>,
operation: &'request str,
captures_ninja_failure_output: bool,
}
fn run_ninja_internal<F, Clock>(
request: NinjaInternalRequest<'_, '_>,
clock: &Clock,
configure: F,
) -> io::Result<()>
where
F: FnOnce(&mut Command) -> io::Result<()>,
Clock: MonotonicClock,
{
let mut cmd = Command::new(request.program);
configure(&mut cmd)?;
let execution = CommandExecutionContext {
operation: request.operation,
stderr_mode: request.stderr_mode,
captures_ninja_failure_output: request.captures_ninja_failure_output,
clock,
};
run_command_and_stream_with_context(cmd, request.status_observer, &execution)
}
fn run_ninja_build_internal(
request: NinjaBuildRequest<'_>,
status_observer: Option<StatusObserver<'_>>,
clock: &impl MonotonicClock,
) -> io::Result<()> {
run_ninja_internal(
NinjaInternalRequest {
program: request.program,
stderr_mode: request.stderr_mode,
status_observer,
operation: "build",
captures_ninja_failure_output: true,
},
clock,
|cmd| configure_ninja_build_command(cmd, &request),
)
}
fn run_ninja_tool_internal(
request: NinjaToolRequest<'_>,
status_observer: Option<StatusObserver<'_>>,
clock: &impl MonotonicClock,
) -> io::Result<()> {
run_ninja_internal(
NinjaInternalRequest {
program: request.program,
stderr_mode: request.stderr_mode,
status_observer,
operation: request.tool,
captures_ninja_failure_output: false,
},
clock,
|cmd| configure_ninja_tool_command(cmd, &request),
)
}
pub(crate) fn run_ninja_with_status(
request: NinjaBuildRequest<'_>,
status_observer: StatusObserver<'_>,
) -> io::Result<()> {
run_ninja_build_internal(request, Some(status_observer), &StdMonotonicClock)
}
pub(crate) fn run_ninja_tool_with_status(
request: NinjaToolRequest<'_>,
status_observer: StatusObserver<'_>,
) -> io::Result<()> {
run_ninja_tool_internal(request, Some(status_observer), &StdMonotonicClock)
}
pub(super) const DYNDEP_DIR: &str = ".netsuke/dyndep";