use std::fmt;
use rucc_tuple::{Env, Os, TargetTuple};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Wall {
Apple,
Microsoft,
}
impl Wall {
#[must_use]
pub const fn of(target: TargetTuple) -> Option<Self> {
match (target.os(), target.env()) {
(Os::MacOs | Os::IOs, _) => Some(Wall::Apple),
(Os::Windows, Env::Msvc) => Some(Wall::Microsoft),
_ => None,
}
}
#[must_use]
pub const fn sdk(self) -> &'static str {
match self {
Wall::Apple => "a macOS SDK",
Wall::Microsoft => "the Windows SDK and its universal CRT",
}
}
#[must_use]
pub const fn licence(self) -> &'static str {
match self {
Wall::Apple => {
"it is under Apple's Xcode licence, which limits its use to Apple branded hardware"
}
Wall::Microsoft => "Microsoft does not allow it to be redistributed",
}
}
#[must_use]
pub const fn under(self) -> crate::Licence {
match self {
Wall::Apple => crate::Licence::AppleSdk,
Wall::Microsoft => crate::Licence::MicrosoftSdk,
}
}
#[must_use]
pub const fn ways(self) -> &'static str {
match self {
Wall::Apple => {
"Compile on a macOS machine, where the installed SDK is found by asking xcrun, or \
download Xcode yourself under that licence and name the SDK with -isysroot <dir> \
or in SDKROOT"
}
Wall::Microsoft => {
"Build for the mingw-w64 environment instead, which is fully redistributable and \
needs nothing installed, or install Visual Studio and the Windows SDK, which are \
found without being named on a Windows machine and are what INCLUDE names \
anywhere else, or point --sysroot=<dir> at a tree laid out with crt/include and \
sdk/include"
}
}
}
#[must_use]
pub fn no_headers(self, target: &str) -> String {
format!(
"{target} needs {} to compile against and none of it is on this machine. This compiler \
does not ship it and will not download it for you, because {}. {}, or pass -nostdinc \
for a program that includes none of the library",
self.sdk(),
self.licence(),
self.ways()
)
}
#[must_use]
pub fn no_fetch(self, target: &str) -> String {
format!(
"there is nothing to fetch for {target} and there never will be. What a program for it \
compiles against is {}, and {}, so no release of this compiler pins it. {}",
self.sdk(),
self.licence(),
self.ways()
)
}
}
impl fmt::Display for Wall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Wall::Apple => "Apple's licence wall",
Wall::Microsoft => "Microsoft's licence wall",
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn target(tuple: &str) -> TargetTuple {
tuple.parse().expect("a target this understands")
}
#[test]
fn the_apple_platforms_are_behind_one_wall_and_the_msvc_environment_behind_the_other() {
assert_eq!(Wall::of(target("aarch64-macos")), Some(Wall::Apple));
assert_eq!(Wall::of(target("x86_64-macos")), Some(Wall::Apple));
assert_eq!(Wall::of(target("aarch64-ios")), Some(Wall::Apple));
assert_eq!(Wall::of(target("x86_64-windows-msvc")), Some(Wall::Microsoft));
assert_eq!(Wall::of(target("arm64ec-windows-msvc")), Some(Wall::Microsoft));
}
#[test]
fn the_windows_target_that_needs_nothing_installed_is_not_behind_a_wall() {
assert_eq!(Wall::of(target("x86_64-pc-windows-gnu")), None);
assert_eq!(Wall::of(target("aarch64-windows-gnu")), None);
for tuple in ["x86_64-linux-gnu", "riscv64-linux-musl", "armv7m-none-eabi", "wasm32-wasi"] {
assert_eq!(Wall::of(target(tuple)), None, "{tuple}");
}
}
#[test]
fn the_licence_behind_each_wall_is_the_one_that_is_never_redistributable() {
for wall in [Wall::Apple, Wall::Microsoft] {
assert!(!wall.under().redistributable(), "{wall}");
}
assert_eq!(Wall::Apple.under().as_str(), "apple-sdk");
assert_eq!(Wall::Microsoft.under().as_str(), "microsoft-sdk");
}
#[test]
fn each_message_names_the_target_the_licence_and_a_way_out() {
let said = Wall::Apple.no_headers("aarch64-macos");
assert!(said.contains("aarch64-macos"), "{said}");
assert!(said.contains("Xcode licence"), "{said}");
assert!(said.contains("-isysroot"), "{said}");
assert!(said.contains("-nostdinc"), "{said}");
let said = Wall::Microsoft.no_fetch("x86_64-windows-msvc");
assert!(said.contains("x86_64-windows-msvc"), "{said}");
assert!(said.contains("mingw-w64"), "{said}");
assert!(said.contains("there never will be"), "{said}");
}
}