Skip to content

Commit

Permalink
Merge pull request #60 from antoniusnaumann/feature/experimental-targets
Browse files Browse the repository at this point in the history
  • Loading branch information
antoniusnaumann authored Aug 5, 2024
2 parents a853494 + a94c1e7 commit fbecac7
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 49 deletions.
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

144 changes: 125 additions & 19 deletions src/commands/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,17 @@ fn run_for_crate(

if !skip_toolchains_check {
let missing_toolchains = check_installed_toolchains(&targets);
if !missing_toolchains.is_empty() {
if config.accept_all || prompt_toolchain_installation(&missing_toolchains) {
let nightly_toolchains = check_nightly_installed(&targets);

let installation_required =
&[missing_toolchains.as_slice(), nightly_toolchains.as_slice()].concat();

if !installation_required.is_empty() {
if config.accept_all || prompt_toolchain_installation(installation_required) {
install_toolchains(&missing_toolchains, config.silent)?;
if !nightly_toolchains.is_empty() {
install_nightly_src(config.silent)?;
}
} else {
Err("Toolchains for some target platforms were missing!")?;
}
Expand Down Expand Up @@ -193,51 +201,74 @@ fn run_for_crate(
Ok(())
}

// FIXME: This can be removed once variant_count is stabilized: https://doc.rust-lang.org/std/mem/fn.variant_count.html#:~:text=Function%20std%3A%3Amem%3A%3Avariant_count&text=Returns%20the%20number%20of%20variants,the%20return%20value%20is%20unspecified.
const PLATFORM_COUNT: usize = 5;

#[derive(ValueEnum, Copy, Clone, Debug)]
#[value()]
pub enum Platform {
Macos,
Ios,
// Platforms below are removed until they are appropriately supported
// Tvos,
// Watchos,
// Platforms below are experimental
Tvos,
Watchos,
Visionos,
}

impl Platform {
fn into_apple_platforms(self) -> Vec<ApplePlatform> {
match self {
Platform::Macos => vec![ApplePlatform::MacOS],
Platform::Ios => vec![ApplePlatform::IOS, ApplePlatform::IOSSimulator],
// Platform::Tvos => vec![ApplePlatform::TvOS],
// Platform::Watchos => vec![ApplePlatform::WatchOS],
Platform::Tvos => vec![ApplePlatform::TvOS, ApplePlatform::TvOSSimulator],
Platform::Watchos => vec![ApplePlatform::WatchOS, ApplePlatform::WatchOSSimulator],
Platform::Visionos => vec![ApplePlatform::VisionOS, ApplePlatform::VisionOSSimulator],
}
}

fn display_name(&self) -> &'static str {
match self {
fn display_name(&self) -> String {
let name = match self {
Platform::Macos => "macOS",
Platform::Ios => "iOS",
// Platform::Tvos => "tvOS",
// Platform::Watchos => "watchOS",
Platform::Tvos => "tvOS",
Platform::Watchos => "watchOS",
Platform::Visionos => "visionOS",
};

format!(
"{name}{}",
if self.is_experimental() {
" (Experimental)"
} else {
""
}
)
}

fn is_experimental(&self) -> bool {
match self {
Platform::Macos | Platform::Ios => false,
Platform::Tvos | Platform::Watchos | Platform::Visionos => true,
}
}

fn all() -> Vec<Self> {
vec![
fn all() -> [Self; PLATFORM_COUNT] {
[
Self::Macos,
Self::Ios,
// Self::Tvos,
// Self::Watchos
Self::Tvos,
Self::Watchos,
Self::Visionos,
]
}
}

fn prompt_platforms(accept_all: bool) -> Vec<Platform> {
let platforms = Platform::all();
let items: Vec<_> = platforms.iter().map(|p| p.display_name()).collect();
let items = platforms.map(|p| p.display_name());

if accept_all {
return platforms;
return platforms.to_vec();
}

let theme = prompt_theme();
Expand All @@ -246,7 +277,7 @@ fn prompt_platforms(accept_all: bool) -> Vec<Platform> {
.with_prompt("Select Target Platforms")
// TODO: Move this to separate class and disable reporting to change style on success
// .report(false)
.defaults(&[true, true, true, false]);
.defaults(&platforms.map(|p| !p.is_experimental()));

let chosen: Vec<usize> = selector.interact().unwrap();

Expand All @@ -271,6 +302,7 @@ fn check_installed_toolchains(targets: &[Target]) -> Vec<&'static str> {

targets
.iter()
.filter(|t| !t.platform().is_tier_3())
.flat_map(|t| t.architectures())
.filter(|arch| {
!installed
Expand All @@ -280,6 +312,35 @@ fn check_installed_toolchains(targets: &[Target]) -> Vec<&'static str> {
.collect()
}

/// Checks if rust-src component for tier 3 targets are installed
fn check_nightly_installed(targets: &[Target]) -> Vec<&'static str> {
if !targets.iter().any(|t| t.platform().is_tier_3()) {
return vec![];
}

// TODO: Check if the correct nightly toolchain itself is installed
let mut rustup = command("rustup component list --toolchain nightly");
rustup.stdout(Stdio::piped());
// HACK: Silence error that toolchain is not installed
rustup.stderr(Stdio::null());

let output = rustup
.execute_output()
.expect("Failed to check installed components. Is rustup installed on your system?");
let output = String::from_utf8_lossy(&output.stdout);

if output
.split('\n')
.filter(|s| s.contains("installed"))
.map(|s| s.replace("(installed)", "").trim().to_owned())
.any(|s| s.eq_ignore_ascii_case("rust-src"))
{
vec![]
} else {
vec!["rust-src (nightly)"]
}
}

/// Prompts the user to install the given **toolchains** by name
fn prompt_toolchain_installation(toolchains: &[&str]) -> bool {
println!("The following toolchains are not installed:");
Expand All @@ -302,6 +363,10 @@ fn prompt_toolchain_installation(toolchains: &[&str]) -> bool {

/// Attempts to install the given **toolchains**
fn install_toolchains(toolchains: &[&str], silent: bool) -> Result<()> {
if toolchains.is_empty() {
return Ok(());
};

let multi = silent.not().then(MultiProgress::new);
let spinner = silent
.not()
Expand All @@ -320,7 +385,7 @@ fn install_toolchains(toolchains: &[&str], silent: bool) -> Result<()> {
// TODO: make this a separate function and show error spinner on fail
install
.execute()
.map_err(|e| format!("Error while donwloading toolchain {toolchain}: \n\t{e}"))?;
.map_err(|e| format!("Error while downloading toolchain {toolchain}: \n\t{e}"))?;

step.finish();
}
Expand All @@ -329,6 +394,47 @@ fn install_toolchains(toolchains: &[&str], silent: bool) -> Result<()> {
Ok(())
}

/// Attempts to install the "rust-src" component on nightly
fn install_nightly_src(silent: bool) -> Result<()> {
let multi = silent.not().then(MultiProgress::new);
let spinner = silent
.not()
.then(|| MainSpinner::with_message("Installing Toolchains...".to_owned()));
multi.add(&spinner);
spinner.start();

let mut install = command("rustup toolchain install nightly");
install.stdin(Stdio::null());

let step = silent.not().then(|| CommandSpinner::with_command(&install));
multi.add(&step);
step.start();

// TODO: make this a separate function and show error spinner on fail
install
.execute()
.map_err(|e| format!("Error while installing rust-src on nightly: \n\t{e}"))?;

step.finish();

let mut install = command("rustup component add rust-src --toolchain nightly");
install.stdin(Stdio::null());

let step = silent.not().then(|| CommandSpinner::with_command(&install));
multi.add(&step);
step.start();

// TODO: make this a separate function and show error spinner on fail
install
.execute()
.map_err(|e| format!("Error while installing rust-src on nightly: \n\t{e}"))?;

step.finish();
spinner.finish();

Ok(())
}

fn prompt_package_name(crate_name: &str, accept_all: bool) -> String {
let default = crate_name.to_case(Case::UpperCamel);

Expand Down
74 changes: 54 additions & 20 deletions src/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ use crate::package::FeatureOptions;

pub trait TargetInfo {
fn target(&self) -> Target;
/// Marks whether a pre-built std-lib is provided for this target (Tier 1 and Tier 2) via rustup or target needs to
/// be build (Tier 3)
/// See: https://doc.rust-lang.org/nightly/rustc/platform-support.html
fn is_tier_3(&self) -> bool;
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -46,7 +50,8 @@ impl Target {
self.architectures()
.into_iter()
.map(|arch| {
let mut cmd = command("cargo build");
// FIXME: Remove nightly for Tier 3 targets here once build-std is stabilized
let mut cmd = if self.platform().is_tier_3() { command("cargo +nightly build -Z build-std") } else { command("cargo build") };
cmd.arg("--target").arg(arch);

match mode {
Expand Down Expand Up @@ -185,12 +190,13 @@ pub enum ApplePlatform {
IOS,
IOSSimulator,
MacOS,
MacCatalyst,
// MacCatalyst,
TvOS,
TvOSSimulator,
WatchOS,
WatchOSSimulator,
CarPlayOS,
CarPlayOSSimulator,
VisionOS,
VisionOSSimulator,
}

impl TargetInfo for ApplePlatform {
Expand All @@ -214,25 +220,53 @@ impl TargetInfo for ApplePlatform {
display_name: "macOS",
platform: *self,
},
MacCatalyst => {
unimplemented!("No official Rust target for platform \"Mac Catalyst\"!")
}
TvOS => Target::Universal {
universal_name: "universal-tvos",
architectures: nonempty!["aarch64-apple-tvos", "x86_64-apple-tvos"],
TvOS => Target::Single {
architecture: "aarch64-apple-tvos",
display_name: "tvOS",
platform: *self,
},
WatchOS => {
unimplemented!("No official Rust target for platform \"watchOS\"!")
}
WatchOSSimulator => {
unimplemented!("No official Rust target for platform \"watchOS Simulator\"!")
}
CarPlayOS => unimplemented!("No official Rust target for platform \"CarPlay\"!"),
CarPlayOSSimulator => {
unimplemented!("No official Rust target for platform \"CarPlay Simulator\"!")
}
TvOSSimulator => Target::Universal {
universal_name: "universal-tvos-simulator",
architectures: nonempty!["aarch64-apple-tvos-sim", "x86_64-apple-tvos"],
display_name: "tvOS Simulator",
platform: *self,
},
WatchOS => Target::Universal {
universal_name: "universal-watchos",
architectures: nonempty![
"aarch64-apple-watchos",
"arm64_32-apple-watchos",
"armv7k-apple-watchos"
],
display_name: "watchOS",
platform: *self,
},
WatchOSSimulator => Target::Universal {
universal_name: "universal-watchos-sim",
architectures: nonempty!["aarch64-apple-watchos-sim", "x86_64-apple-watchos-sim"],
display_name: "watchOS Simulator",
platform: *self,
},
VisionOS => Target::Single {
architecture: "aarch64-apple-visionos",
display_name: "visionOS",
platform: *self,
},
VisionOSSimulator => Target::Single {
architecture: "aarch64-apple-visionos-sim",
display_name: "visionOS Simulator",
platform: *self,
},
}
}

fn is_tier_3(&self) -> bool {
match self {
ApplePlatform::IOS | ApplePlatform::IOSSimulator => false,
ApplePlatform::MacOS => false,
ApplePlatform::TvOS | ApplePlatform::TvOSSimulator => true,
ApplePlatform::WatchOS | ApplePlatform::WatchOSSimulator => true,
ApplePlatform::VisionOS | ApplePlatform::VisionOSSimulator => true,
}
}
}

0 comments on commit fbecac7

Please sign in to comment.