xcrun 1.0.4

A simple way to get macOS/iOS SDK paths using xcrun.
Documentation
use std::fmt;
use std::path::PathBuf;
use std::process::{Command, Stdio};

/// Represents the `--sdk` argument for xcrun.
/// A `None` argument will select the default version.
/// Example usages:
/// `SDK::iPhoneOS(None)`, `SDK::macOS(Some("10.15"))`
#[allow(non_camel_case_types)]
pub enum SDK {
    iPhoneOS(Option<&'static str>),
    iPhoneSimulator(Option<&'static str>),
    macOS(Option<&'static str>),
}

impl fmt::Display for SDK {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let output = match *self {
            SDK::iPhoneOS(ver) => match ver {
                Some(s) => format!("iOS {}", s),
                None => "iOS".to_string(),
            },
            SDK::iPhoneSimulator(ver) => match ver {
                Some(s) => format!("iPhone Simulator {}", s),
                None => "iPhone Simulator".to_string(),
            },
            SDK::macOS(ver) => match ver {
                Some(s) => format!("macOS {}", s),
                None => "macOS".to_string(),
            },
        };
        write!(f, "{}", output)
    }
}

fn sdk_to_name(sdk: SDK) -> String {
    match sdk {
        SDK::iPhoneOS(ver) => match ver {
            Some(s) => format!("iphoneos{}", s),
            None => "iphoneos".to_string(),
        },
        SDK::iPhoneSimulator(ver) => match ver {
            Some(s) => format!("iphonesimulator{}", s),
            None => "iphonesimulator".to_string(),
        },
        SDK::macOS(ver) => match ver {
            Some(s) => format!("macosx{}", s),
            None => "macosx".to_string(),
        },
    }
}

/// Find the path for the specified SDK, using xcrun.
pub fn find_sdk(sdk: SDK) -> Option<PathBuf> {
    let name = sdk_to_name(sdk);
    let xcrun_output = Command::new("xcrun")
        .arg("--sdk")
        .arg(&name)
        .arg("--show-sdk-path")
        .stderr(Stdio::inherit())
        .output();
    match xcrun_output {
        Ok(path) => {
            let stdout = String::from_utf8_lossy(&path.stdout).to_string();
            Some(PathBuf::from(stdout))
        }
        Err(_) => None,
    }
}

/// Find the platform path for the specified SDK, using xcrun.
pub fn find_sdk_platform(sdk: SDK) -> Option<PathBuf> {
    let name = sdk_to_name(sdk);
    let xcrun_output = Command::new("xcrun")
        .arg("--sdk")
        .arg(&name)
        .arg("--show-sdk-platform-path")
        .output();
    match xcrun_output {
        Ok(path) => {
            let stdout = String::from_utf8_lossy(&path.stdout).to_string();
            Some(PathBuf::from(stdout))
        }
        Err(_) => None,
    }
}

/// Find the platform path for the specified SDK, using xcrun.
pub fn find_tool(sdk: SDK, tool: &str) -> Option<PathBuf> {
    let name = sdk_to_name(sdk);
    let xcrun_output = Command::new("xcrun")
        .arg("--sdk")
        .arg(&name)
        .arg("--find")
        .arg(tool)
        .output();
    match xcrun_output {
        Ok(path) => {
            let stdout = String::from_utf8_lossy(&path.stdout).to_string();
            Some(PathBuf::from(stdout))
        }
        Err(_) => None,
    }
}

#[test]
fn macos_sdk_test() {
    let mac_sdk = find_sdk(SDK::macOS(None));
    assert!(mac_sdk.is_some());
    assert!(mac_sdk.unwrap().to_str().unwrap().contains("MacOSX"));
}

#[test]
fn ios_sdk_test() {
    let ios_sdk = find_sdk(SDK::iPhoneOS(None));
    assert!(ios_sdk.is_some());
    assert!(ios_sdk.unwrap().to_str().unwrap().contains("iPhoneOS"));
}

#[test]
fn macos_platform_test() {
    let mac_platform = find_sdk_platform(SDK::macOS(None));
    assert!(mac_platform.is_some());
    assert!(mac_platform
        .unwrap()
        .to_str()
        .unwrap()
        .contains("MacOSX.platform"));
}

#[test]
fn ios_platform_test() {
    let ios_platform = find_sdk_platform(SDK::iPhoneOS(None));
    assert!(ios_platform.is_some());
    assert!(ios_platform
        .unwrap()
        .to_str()
        .unwrap()
        .contains("iPhoneOS.platform"));
}

#[test]
fn macos_tool_test() {
    let macos_clang = find_tool(SDK::macOS(None), "clang");
    assert!(macos_clang.is_some());
    assert!(macos_clang.unwrap().to_str().unwrap().contains("clang"));
}

#[test]
fn ios_tool_test() {
    let ios_clang = find_tool(SDK::iPhoneOS(None), "clang");
    assert!(ios_clang.is_some());
    assert!(ios_clang.unwrap().to_str().unwrap().contains("clang"));
}