Properly support multiple targets

This commit is contained in:
Félix Saparelli 2022-06-08 00:56:18 +12:00
parent fcf5728dde
commit 235bcac300
No known key found for this signature in database
GPG key ID: B948C4BAE44FC474

View file

@ -37,8 +37,23 @@ struct Options {
version: String,
/// Override binary target set. Defaults to a set of targets based on the current platform.
#[clap(help_heading = "OVERRIDES", long)]
target: Option<String>,
///
/// Binstall is able to look for binaries for several targets, installing the first one it finds
/// in the order the targets were given. For example, on a 64-bit glibc Linux distribution, the
/// default is to look first for a `x86_64-unknown-linux-gnu` binary, then for a
/// `x86_64-unknown-linux-musl` binary. However, on a musl system, the gnu version will not be
/// considered.
///
/// This option takes a comma-separated list of target triples, which will be tried in order.
///
/// If falling back to installing from source, the first target will be used.
#[clap(
help_heading = "OVERRIDES",
alias = "target",
long,
value_name = "triple"
)]
targets: Option<String>,
/// Override install path for downloaded binary.
///
@ -154,7 +169,7 @@ async fn entry() -> Result<()> {
}
// Load options
let mut opts = Options::parse_from(args.iter());
let mut opts = Options::parse_from(args);
let cli_overrides = PkgOverride {
pkg_url: opts.pkg_url.take(),
pkg_fmt: opts.pkg_fmt.take(),
@ -174,6 +189,13 @@ async fn entry() -> Result<()> {
)
.unwrap();
// Compute install directory
let install_path = get_install_path(opts.install_path.as_deref()).ok_or_else(|| {
error!("No viable install path found of specified, try `--install-path`");
miette!("No install path found or specified")
})?;
debug!("Using install path: {}", install_path.display());
// Create a temporary directory for downloads etc.
let temp_dir = TempDir::new()
.map_err(BinstallError::from)
@ -218,20 +240,58 @@ async fn entry() -> Result<()> {
manifest.bin,
);
let desired_targets = {
let from_opts = opts
.targets
.as_ref()
.map(|ts| ts.split(',').map(|t| t.to_string()).collect());
if let Some(ts) = from_opts {
ts
} else {
detect_targets().await
}
};
let mut fetchers = MultiFetcher::default();
for target in &desired_targets {
debug!("Building metadata for target: {target}");
let mut target_meta = meta.clone();
// Merge any overrides
if let Some(o) = meta.overrides.remove(&opts.target) {
meta.merge(&o);
if let Some(o) = target_meta.overrides.get(target).cloned() {
target_meta.merge(&o);
}
meta.merge(&cli_overrides);
debug!("Found metadata: {:?}", meta);
target_meta.merge(&cli_overrides);
debug!("Found metadata: {target_meta:?}");
// Compute install directory
let install_path = get_install_path(opts.install_path.as_deref()).ok_or_else(|| {
error!("No viable install path found of specified, try `--install-path`");
miette!("No install path found or specified")
})?;
debug!("Using install path: {}", install_path.display());
let fetcher_data = Data {
name: package.name.clone(),
target: target.clone(),
version: package.version.clone(),
repo: package.repository.clone(),
meta: target_meta,
};
fetchers.add(GhCrateMeta::new(&fetcher_data).await);
fetchers.add(QuickInstall::new(&fetcher_data).await);
}
match fetchers.first_available().await {
Some(fetcher) => {
// Build final metadata
let fetcher_target = fetcher.target();
if let Some(o) = meta.overrides.get(&fetcher_target.to_owned()).cloned() {
meta.merge(&o);
}
meta.merge(&cli_overrides);
debug!(
"Found a binary install source: {} ({fetcher_target})",
fetcher.source_name()
);
// Compute temporary directory for downloads
let pkg_path = temp_dir
@ -239,30 +299,9 @@ async fn entry() -> Result<()> {
.join(format!("pkg-{}.{}", opts.name, meta.pkg_fmt));
debug!("Using temporary download path: {}", pkg_path.display());
let fetcher_data: Vec<_> = detect_targets()
.await
.into_iter()
.map(|target| Data {
name: package.name.clone(),
target: target.into(),
version: package.version.clone(),
repo: package.repository.clone(),
meta: meta.clone(),
})
.collect();
// Try github releases, then quickinstall
let mut fetchers = MultiFetcher::default();
for data in &fetcher_data {
fetchers.add(GhCrateMeta::new(data).await);
fetchers.add(QuickInstall::new(data).await);
}
match fetchers.first_available().await {
Some(fetcher) => {
install_from_package(
binaries,
&*fetcher,
fetcher.as_ref(),
install_path,
meta,
opts,
@ -273,10 +312,17 @@ async fn entry() -> Result<()> {
.await
}
None => {
if !opts.no_cleanup {
temp_dir.close().unwrap_or_else(|err| {
warn!("Failed to clean up some resources: {err}");
});
install_from_source(opts, package).await
}
let target = desired_targets
.first()
.ok_or_else(|| miette!("No viable targets found, try with `--targets`"))?;
install_from_source(opts, package, target).await
}
}
}
@ -421,7 +467,7 @@ async fn install_from_package(
Ok(())
}
async fn install_from_source(opts: Options, package: Package<Meta>) -> Result<()> {
async fn install_from_source(opts: Options, package: Package<Meta>, target: &str) -> Result<()> {
// Prompt user for source install
warn!("The package will be installed from source (with cargo)",);
if !opts.no_confirm && !opts.dry_run {
@ -430,14 +476,14 @@ async fn install_from_source(opts: Options, package: Package<Meta>) -> Result<()
if opts.dry_run {
info!(
"Dry-run: running `cargo install {} --version {} --target {}`",
package.name, package.version, opts.target
"Dry-run: running `cargo install {} --version {} --target {target}`",
package.name, package.version
);
Ok(())
} else {
debug!(
"Running `cargo install {} --version {} --target {}`",
package.name, package.version, opts.target
"Running `cargo install {} --version {} --target {target}`",
package.name, package.version
);
let mut child = Command::new("cargo")
.arg("install")
@ -445,7 +491,7 @@ async fn install_from_source(opts: Options, package: Package<Meta>) -> Result<()
.arg("--version")
.arg(package.version)
.arg("--target")
.arg(opts.target)
.arg(target)
.spawn()
.into_diagnostic()
.wrap_err("Spawning cargo install failed.")?;