Skip to content

Commit

Permalink
Fail early if required tools can't be found.
Browse files Browse the repository at this point in the history
  • Loading branch information
duckinator committed Dec 16, 2024
1 parent 06877bf commit 6ddc10e
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 0 deletions.
8 changes: 8 additions & 0 deletions cargo-dist/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,14 @@ pub enum DistError {
tool: String,
},

/// One or more required tools are missing.
#[error("The following tools are required to run this task, but are missing:\n- {}", tools.join("\n- "))]
#[diagnostic(help("Please install the tools mentioned above and try again."))]
EnvToolsMissing {
/// the names of the missing tools
tools: Vec<String>,
},

/// Unknown target requested
#[error(
"A build was requested for {target}, but the standalone updater isn't available for it."
Expand Down
82 changes: 82 additions & 0 deletions cargo-dist/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,90 @@ pub mod tasks;
#[cfg(test)]
mod tests;

/// dist env test -- make sure we have everything we need for a build.
pub fn do_env_test(cfg: &Config) -> DistResult<()> {
let (dist, _manifest) = tasks::gather_work(cfg)?;

let builds = dist.config.builds;

let need_cargo_auditable = builds.cargo.cargo_auditable;
let need_cargo_cyclonedx = builds.cargo.cargo_cyclonedx;
let need_omnibor = builds.omnibor;
let mut need_xwin = false;
let mut need_zigbuild = false;

let tools = dist.tools;

let cargo = tools.cargo()?;
let host = cargo.host_target.parse()?;

for step in dist
.local_build_steps
.iter()
.chain(dist.global_build_steps.iter())
{
// Can't require cross-compilation tools if we aren't compiling.
if cfg.artifact_mode == ArtifactMode::Lies {
break;
}

match step {
BuildStep::Cargo(step) => {
let target = step.target_triple.parse()?;
let wrapper = tasks::build_wrapper_for_cross(&host, &target)?;

match wrapper {
Some(CargoBuildWrapper::Xwin) => {
need_xwin = true;
}
Some(CargoBuildWrapper::ZigBuild) => {
need_zigbuild = true;
}
None => {}
}
}
_ => {}
}
}

// These are all of the tools we can check for.
//
// bool::then(f) returns an Option, so we start with a
// Vec<Option<Result<&Tool, DistResult>>>.
let all_tools: Vec<Option<DistResult<&Tool>>> = vec![
need_cargo_auditable.then(|| tools.cargo_auditable()),
need_cargo_cyclonedx.then(|| tools.cargo_cyclonedx()),
need_omnibor.then(|| tools.omnibor()),
need_xwin.then(|| tools.cargo_xwin()),
need_zigbuild.then(|| tools.cargo_zigbuild()),
];

// Drop `None`s, then extract the values from the remaining `Option`s.
let needed_tools = all_tools.into_iter().flatten();

let missing: Vec<String> = needed_tools
.filter_map(|t| match t {
// The tool was found.
Ok(_) => None,
// The tool is missing
Err(DistError::ToolMissing { tool: ref name }) => Some(name.to_owned()),
// This should never happen, but I can't find a way to enforce
// it at the type system level. ;~; -@duckinator
Err(_) => unreachable!(
"do_env_test() got an Err that wasn't DistError::ToolMissing. This is a dist bug."
),
})
.collect();

missing
.is_empty()
.then_some(())
.ok_or(DistError::EnvToolsMissing { tools: missing })
}

/// dist build -- actually build binaries and installers!
pub fn do_build(cfg: &Config) -> DistResult<DistManifest> {
do_env_test(cfg)?;
check_integrity(cfg)?;

let (dist, mut manifest) = tasks::gather_work(cfg)?;
Expand Down
59 changes: 59 additions & 0 deletions cargo-dist/src/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@ pub struct Tools {
///
/// <https://www.ssl.com/guide/esigner-codesigntool-command-guide/>
pub code_sign_tool: Option<Tool>,
/// cargo-auditable, used for auditable builds
pub cargo_auditable: Option<Tool>,
/// cargo-cyclonedx, for generating CycloneDX artifacts
pub cargo_cyclonedx: Option<Tool>,
/// cargo-xwin, for some cross builds
pub cargo_xwin: Option<Tool>,
/// cargo-zigbuild, for some cross builds
pub cargo_zigbuild: Option<Tool>,
}

impl Tools {
Expand All @@ -297,6 +305,34 @@ impl Tools {
tool: "omnibor-cli".to_owned(),
})
}

/// Returns cargo-auditable info or an error
pub fn cargo_auditable(&self) -> DistResult<&Tool> {
self.cargo_auditable.as_ref().ok_or(DistError::ToolMissing {
tool: "cargo-auditable".to_owned(),
})
}

/// Returns cargo-cyclonedx info or an error
pub fn cargo_cyclonedx(&self) -> DistResult<&Tool> {
self.cargo_cyclonedx.as_ref().ok_or(DistError::ToolMissing {
tool: "cargo-cyclonedx".to_owned(),
})
}

/// Returns cargo-xwin info or an error
pub fn cargo_xwin(&self) -> DistResult<&Tool> {
self.cargo_xwin.as_ref().ok_or(DistError::ToolMissing {
tool: "cargo-xwin".to_owned(),
})
}

/// Returns cargo-zigbuild info or an error
pub fn cargo_zigbuild(&self) -> DistResult<&Tool> {
self.cargo_zigbuild.as_ref().ok_or(DistError::ToolMissing {
tool: "cargo-zigbuild".to_owned(),
})
}
}

/// Info about the cargo toolchain we're using
Expand Down Expand Up @@ -3180,6 +3216,29 @@ fn tool_info() -> DistResult<Tools> {
omnibor: find_tool("omnibor", "--version"),
// Computed later if needed
code_sign_tool: None,

// NOTE: This doesn't actually give us cargo-auditable's version info,
// but it does confirm it's installed, which is what we care about.
cargo_auditable: find_tool2("cargo", "auditable", "--version"),

cargo_cyclonedx: find_tool2("cargo", "cyclonedx", "--version"),
cargo_xwin: find_tool2("cargo", "xwin", "--version"),
cargo_zigbuild: find_tool("cargo-zigbuild", "--version"),
})
}

fn find_tool2(name: &str, arg: &str, test_flag: &str) -> Option<Tool> {
let output = Cmd::new(name, "detect tool")
.arg(arg)
.arg(test_flag)
.check(false)
.output()
.ok()?;
let string_output = String::from_utf8(output.stdout).ok()?;
let version = string_output.lines().next()?;
Some(Tool {
cmd: format!("{} {}", name, arg),
version: version.to_owned(),
})
}

Expand Down
4 changes: 4 additions & 0 deletions cargo-dist/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ pub fn mock_tools() -> Tools {
git: None,
omnibor: None,
code_sign_tool: None,
cargo_auditable: None,
cargo_cyclonedx: None,
cargo_xwin: None,
cargo_zigbuild: None,
}
}

Expand Down

0 comments on commit 6ddc10e

Please sign in to comment.