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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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"));
}