diff --git a/src/lib.rs b/src/lib.rs index 905d5edb8..798177f42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -518,59 +518,26 @@ pub fn run( let cwd = std::env::current_dir()?; if let Some(metadata) = cargo_metadata_with_args(None, Some(&args), msg_info)? { - let host = host_version_meta.host(); - let toml = toml(&metadata, msg_info)?; - let config = Config::new(toml); - let target = args - .target - .or_else(|| config.target(&target_list)) - .unwrap_or_else(|| Target::from(host.triple(), &target_list)); - config.confusable_target(&target, msg_info)?; - - let uses_zig = config.zig(&target).unwrap_or(false); - let zig_version = config.zig_version(&target)?; - // Get the image we're supposed to base all our next actions on. - // The image we actually run in might get changed with - // `target.{{TARGET}}.dockerfile` or `target.{{TARGET}}.pre-build` - let image = match docker::get_image(&config, &target, uses_zig) { - Ok(i) => i, - Err(err) => { - msg_info.warn(err)?; - + let CrossSetup { + config, + target, + uses_xargo, + uses_zig, + uses_build_std, + zig_version, + toolchain, + is_remote, + engine, + image, + } = match setup(&host_version_meta, &metadata, &args, target_list, msg_info)? { + Some(setup) => setup, + _ => { return Ok(None); } }; - // Grab the current toolchain, this might be the one we mount in the image later - let default_toolchain = QualifiedToolchain::default(&config, msg_info)?; - - // `cross +channel`, where channel can be `+channel[-YYYY-MM-DD]` - let mut toolchain = if let Some(channel) = args.channel { - let picked_toolchain: Toolchain = channel.parse()?; - - if let Some(picked_host) = &picked_toolchain.host { - return Err(eyre::eyre!("the specified toolchain `{picked_toolchain}` can't be used")) - .with_suggestion(|| { - format!( - "try `cross +{}` instead", - picked_toolchain.remove_host() - ) - }).with_section(|| format!( -r#"Overriding the toolchain in cross is only possible in CLI by specifying a channel and optional date: `+channel[-YYYY-MM-DD]`. -To override the toolchain mounted in the image, set `target.{}.image.toolchain = "{picked_host}"`"#, target).header("Note:".bright_cyan())); - } - - default_toolchain.with_picked(picked_toolchain)? - } else { - default_toolchain - }; - - let is_remote = docker::Engine::is_remote(); - let engine = docker::Engine::new(None, Some(is_remote), msg_info)?; - - let image = image.to_definite_with(&engine, msg_info); + config.confusable_target(&target, msg_info)?; - toolchain.replace_host(&image.platform); let picked_generic_channel = matches!(toolchain.channel.as_str(), "stable" | "beta" | "nightly"); @@ -580,28 +547,7 @@ To override the toolchain mounted in the image, set `target.{}.image.toolchain = "toolchain `{toolchain}` may not run on image `{image}`" ))?; } - // set the sysroot explicitly to the toolchain let mut is_nightly = toolchain.channel.contains("nightly"); - - if !toolchain.is_custom - && !rustup::installed_toolchains(msg_info)? - .into_iter() - .any(|t| t == toolchain.to_string()) - { - rustup::install_toolchain(&toolchain, msg_info)?; - } - let available_targets = if !toolchain.is_custom { - rustup::available_targets(&toolchain.full, msg_info).with_note(|| { - format!("cross would use the toolchain '{toolchain}' for mounting rust") - })? - } else { - rustup::AvailableTargets { - default: String::new(), - installed: vec![], - not_installed: vec![], - } - }; - let mut rustc_version = None; if let Some((version, channel, commit)) = toolchain.rustc_version()? { if picked_generic_channel && toolchain.date.is_none() { @@ -617,103 +563,32 @@ To override the toolchain mounted in the image, set `target.{}.image.toolchain = rustc_version = Some(version); } - let uses_build_std = config.build_std(&target).unwrap_or(false); - let uses_xargo = - !uses_build_std && config.xargo(&target).unwrap_or(!target.is_builtin()); - let cargo_variant = CargoVariant::create(uses_zig, uses_xargo)?; - if !toolchain.is_custom { - // build-std overrides xargo, but only use it if it's a built-in - // tool but not an available target or doesn't have rust-std. - - if !is_nightly && uses_build_std { - eyre::bail!( - "no rust-std component available for {}: must use nightly", - target.triple() - ); - } - - if !uses_xargo - && !uses_build_std - && !available_targets.is_installed(&target) - && available_targets.contains(&target) - { - rustup::install(&target, &toolchain, msg_info)?; - } else if !rustup::component_is_installed("rust-src", &toolchain, msg_info)? { - rustup::install_component("rust-src", &toolchain, msg_info)?; - } - if args.subcommand.map_or(false, |sc| sc == Subcommand::Clippy) - && !rustup::component_is_installed("clippy", &toolchain, msg_info)? - { - rustup::install_component("clippy", &toolchain, msg_info)?; - } - } + let available_targets = rustup::setup_rustup(&toolchain, msg_info)?; + + rustup::setup_components( + &target, + uses_xargo, + uses_build_std, + &toolchain, + is_nightly, + available_targets, + &args, + msg_info, + )?; - let needs_interpreter = args.subcommand.map_or(false, |sc| sc.needs_interpreter()); - - let add_libc = |triple: &str| add_libc_version(triple, zig_version.as_deref()); - let mut filtered_args = if args - .subcommand - .map_or(false, |s| !s.needs_target_in_command()) - { - let mut filtered_args = Vec::new(); - let mut args_iter = args.cargo_args.clone().into_iter(); - while let Some(arg) = args_iter.next() { - if arg == "--target" { - args_iter.next(); - } else if arg.starts_with("--target=") { - // NOOP - } else { - filtered_args.push(arg); - } - } - filtered_args - // Make sure --target is present - } else if !args.cargo_args.iter().any(|a| a.starts_with("--target")) { - let mut args_with_target = args.cargo_args.clone(); - args_with_target.push("--target".to_owned()); - args_with_target.push(add_libc(target.triple())); - args_with_target - } else if zig_version.is_some() { - let mut filtered_args = Vec::new(); - let mut args_iter = args.cargo_args.clone().into_iter(); - while let Some(arg) = args_iter.next() { - if arg == "--target" { - filtered_args.push("--target".to_owned()); - if let Some(triple) = args_iter.next() { - filtered_args.push(add_libc(&triple)); - } - } else if let Some(stripped) = arg.strip_prefix("--target=") { - filtered_args.push(format!("--target={}", add_libc(stripped))); - } else { - filtered_args.push(arg); - } - } - filtered_args - } else { - args.cargo_args.clone() - }; - - let is_test = args.subcommand.map_or(false, |sc| sc == Subcommand::Test); - if is_test && config.doctests().unwrap_or_default() && is_nightly { - filtered_args.push("-Zdoctest-xcompile".to_owned()); - } - if uses_build_std { - filtered_args.push("-Zbuild-std".to_owned()); - } - filtered_args.extend(args.rest_args.iter().cloned()); + let filtered_args = get_filtered_args( + zig_version, + &args, + &target, + &config, + is_nightly, + uses_build_std, + ); let needs_docker = args .subcommand .map_or(false, |sc| sc.needs_docker(is_remote)); if target.needs_docker() && needs_docker { - if host_version_meta.needs_interpreter() - && needs_interpreter - && target.needs_interpreter() - && !interpreter::is_registered(&target)? - { - engine.register_binfmt(&target, msg_info)?; - } - let paths = docker::DockerPaths::create( &engine, metadata, @@ -726,9 +601,18 @@ To override the toolchain mounted in the image, set `target.{}.image.toolchain = target.clone(), config, image, - cargo_variant, + crate::CargoVariant::create(uses_zig, uses_xargo)?, rustc_version, ); + + install_interpreter_if_needed( + &args, + host_version_meta, + &target, + &options, + msg_info, + )?; + let status = docker::run(options, paths, &filtered_args, args.subcommand, msg_info) .wrap_err("could not run container")?; let needs_host = args.subcommand.map_or(false, |sc| sc.needs_host(is_remote)); @@ -744,6 +628,169 @@ To override the toolchain mounted in the image, set `target.{}.image.toolchain = Ok(None) } +/// Check if an interpreter is needed and then install it. +pub fn install_interpreter_if_needed( + args: &Args, + host_version_meta: rustc_version::VersionMeta, + target: &Target, + options: &docker::DockerOptions, + msg_info: &mut MessageInfo, +) -> Result<(), color_eyre::Report> { + let needs_interpreter = args.subcommand.map_or(false, |sc| sc.needs_interpreter()); + + if host_version_meta.needs_interpreter() + && needs_interpreter + && target.needs_interpreter() + && !interpreter::is_registered(target)? + { + options.engine.register_binfmt(target, msg_info)?; + } + Ok(()) +} + +/// Get filtered args to pass to cargo +pub fn get_filtered_args( + zig_version: Option, + args: &Args, + target: &Target, + config: &Config, + is_nightly: bool, + uses_build_std: bool, +) -> Vec { + let add_libc = |triple: &str| add_libc_version(triple, zig_version.as_deref()); + let mut filtered_args = if args + .subcommand + .map_or(false, |s| !s.needs_target_in_command()) + { + let mut filtered_args = Vec::new(); + let mut args_iter = args.cargo_args.clone().into_iter(); + while let Some(arg) = args_iter.next() { + if arg == "--target" { + args_iter.next(); + } else if arg.starts_with("--target=") { + // NOOP + } else { + filtered_args.push(arg); + } + } + filtered_args + // Make sure --target is present + } else if !args.cargo_args.iter().any(|a| a.starts_with("--target")) { + let mut args_with_target = args.cargo_args.clone(); + args_with_target.push("--target".to_owned()); + args_with_target.push(add_libc(target.triple())); + args_with_target + } else if zig_version.is_some() { + let mut filtered_args = Vec::new(); + let mut args_iter = args.cargo_args.clone().into_iter(); + while let Some(arg) = args_iter.next() { + if arg == "--target" { + filtered_args.push("--target".to_owned()); + if let Some(triple) = args_iter.next() { + filtered_args.push(add_libc(&triple)); + } + } else if let Some(stripped) = arg.strip_prefix("--target=") { + filtered_args.push(format!("--target={}", add_libc(stripped))); + } else { + filtered_args.push(arg); + } + } + filtered_args + } else { + args.cargo_args.clone() + }; + + let is_test = args.subcommand.map_or(false, |sc| sc == Subcommand::Test); + if is_test && config.doctests().unwrap_or_default() && is_nightly { + filtered_args.push("-Zdoctest-xcompile".to_owned()); + } + if uses_build_std { + filtered_args.push("-Zbuild-std".to_owned()); + } + filtered_args.extend(args.rest_args.iter().cloned()); + filtered_args +} + +/// Setup cross configuration +pub fn setup( + host_version_meta: &rustc_version::VersionMeta, + metadata: &CargoMetadata, + args: &Args, + target_list: TargetList, + msg_info: &mut MessageInfo, +) -> Result, color_eyre::Report> { + let host = host_version_meta.host(); + let toml = toml(metadata, msg_info)?; + let config = Config::new(toml); + let target = args + .target + .clone() + .or_else(|| config.target(&target_list)) + .unwrap_or_else(|| Target::from(host.triple(), &target_list)); + let uses_build_std = config.build_std(&target).unwrap_or(false); + let uses_xargo = !uses_build_std && config.xargo(&target).unwrap_or(!target.is_builtin()); + let uses_zig = config.zig(&target).unwrap_or(false); + let zig_version = config.zig_version(&target)?; + let image = match docker::get_image(&config, &target, uses_zig) { + Ok(i) => i, + Err(err) => { + msg_info.warn(err)?; + + return Ok(None); + } + }; + let default_toolchain = QualifiedToolchain::default(&config, msg_info)?; + let mut toolchain = if let Some(channel) = &args.channel { + let picked_toolchain: Toolchain = channel.parse()?; + + if let Some(picked_host) = &picked_toolchain.host { + return Err(eyre::eyre!("the specified toolchain `{picked_toolchain}` can't be used")) + .with_suggestion(|| { + format!( + "try `cross +{}` instead", + picked_toolchain.remove_host() + ) + }).with_section(|| format!( + r#"Overriding the toolchain in cross is only possible in CLI by specifying a channel and optional date: `+channel[-YYYY-MM-DD]`. +To override the toolchain mounted in the image, set `target.{target}.image.toolchain = "{picked_host}"`"#).header("Note:".bright_cyan())); + } + + default_toolchain.with_picked(picked_toolchain)? + } else { + default_toolchain + }; + let is_remote = docker::Engine::is_remote(); + let engine = docker::Engine::new(None, Some(is_remote), msg_info)?; + let image = image.to_definite_with(&engine, msg_info); + toolchain.replace_host(&image.platform); + Ok(Some(CrossSetup { + config, + target, + uses_xargo, + uses_zig, + uses_build_std, + zig_version, + toolchain, + is_remote, + engine, + image, + })) +} + +#[derive(Debug)] +pub struct CrossSetup { + pub config: Config, + pub target: Target, + pub uses_xargo: bool, + pub uses_zig: bool, + pub uses_build_std: bool, + pub zig_version: Option, + pub toolchain: QualifiedToolchain, + pub is_remote: bool, + pub engine: docker::Engine, + pub image: docker::Image, +} + #[derive(PartialEq, Eq, Debug)] pub(crate) enum VersionMatch { Same, diff --git a/src/rustup.rs b/src/rustup.rs index 39122b6a3..90b8b4f97 100644 --- a/src/rustup.rs +++ b/src/rustup.rs @@ -28,6 +28,31 @@ impl AvailableTargets { } } +pub fn setup_rustup( + toolchain: &QualifiedToolchain, + msg_info: &mut MessageInfo, +) -> Result { + if !toolchain.is_custom + && !installed_toolchains(msg_info)? + .into_iter() + .any(|t| t == toolchain.to_string()) + { + install_toolchain(toolchain, msg_info)?; + } + let available_targets = if !toolchain.is_custom { + available_targets(&toolchain.full, msg_info).with_note(|| { + format!("cross would use the toolchain '{toolchain}' for mounting rust") + })? + } else { + AvailableTargets { + default: String::new(), + installed: vec![], + not_installed: vec![], + } + }; + Ok(available_targets) +} + fn rustup_command(msg_info: &mut MessageInfo, no_flags: bool) -> Command { let mut cmd = Command::new("rustup"); if no_flags { @@ -241,6 +266,48 @@ pub fn component_is_installed( Ok(check_component(component, toolchain, msg_info)?.is_installed()) } +#[allow(clippy::too_many_arguments)] +pub fn setup_components( + target: &Target, + uses_xargo: bool, + uses_build_std: bool, + toolchain: &QualifiedToolchain, + is_nightly: bool, + available_targets: AvailableTargets, + args: &crate::cli::Args, + msg_info: &mut MessageInfo, +) -> Result<(), color_eyre::Report> { + if !toolchain.is_custom { + // build-std overrides xargo, but only use it if it's a built-in + // tool but not an available target or doesn't have rust-std. + + if !is_nightly && uses_build_std { + eyre::bail!( + "no rust-std component available for {}: must use nightly", + target.triple() + ); + } + + if !uses_xargo + && !uses_build_std + && !available_targets.is_installed(target) + && available_targets.contains(target) + { + install(target, toolchain, msg_info)?; + } else if !component_is_installed("rust-src", toolchain, msg_info)? { + install_component("rust-src", toolchain, msg_info)?; + } + if args + .subcommand + .map_or(false, |sc| sc == crate::Subcommand::Clippy) + && !component_is_installed("clippy", toolchain, msg_info)? + { + install_component("clippy", toolchain, msg_info)?; + } + } + Ok(()) +} + fn rustc_channel(version: &Version) -> Result { match version .pre