1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::path::PathBuf;
use std::process::Command;

/// 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>),
    macOS(Option<&'static str>),
}

fn sdk_to_name(sdk: SDK) -> String {
    match sdk {
        SDK::iPhoneOS(ver) => match ver {
            Some(s) => format!("iphoneos{}", s),
            None => "iphoneos".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")
        .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"));
}