Skip to content

Instantly share code, notes, and snippets.

@Moxinilian
Created June 22, 2018 15:25
Show Gist options
  • Save Moxinilian/5cb29863dd13e9f1ebc879a6ffde9fe2 to your computer and use it in GitHub Desktop.
Save Moxinilian/5cb29863dd13e9f1ebc879a6ffde9fe2 to your computer and use it in GitHub Desktop.
extern crate bindgen;
fn isysroot_path(platform: &str) -> Result<String,std::io::Error>{
use std::process::Command;
let output = Command::new("xcrun")
.arg("--sdk").arg(platform).arg("--show-sdk-path")
.output()?.stdout;
let sdk_path = std::str::from_utf8(&output)
.expect("invalid output from `xcrun --show-sdk-path`");
let directory = String::from(sdk_path.trim());
println!("SDK Directory: {}", directory);
Ok(directory)
}
fn frameworks_path(platform: &str) -> Result<String, std::io::Error> {
// While macOS has its system frameworks located at "/System/Library/Frameworks"
// for actually linking against them (especially for cross-compilation) once
// has to refer to the frameworks as found within "Xcode.app/Contents/Developer/…".
use std::process::Command;
let output = Command::new("xcode-select").arg("-p").output()?.stdout;
let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcode-select`");
let prefix = prefix_str.trim_right();
let infix = format!("Platforms/{}.platform/Developer/SDKs/{}.sdk", platform, platform);
let suffix = "System/Library/Frameworks";
let directory = format!("{}/{}/{}", prefix, infix, suffix);
println!("Framework Directory: {}", directory);
Ok(directory)
}
fn build(target: &str, platform: &str, sdk_path: &str, frameworks_path: &str) {
// Generate one large set of bindings for all frameworks.
//
// We do this rather than generating a module per framework as some frameworks depend on other
// frameworks and in turn share types. To ensure all types are compatible across each
// framework, we feed all headers to bindgen at once.
//
// Only link to each framework and include their headers if their features are enabled and they
// are available on the target os.
use std::env;
use std::path::PathBuf;
let mut frameworks = vec![];
let mut headers = vec![];
#[cfg(feature = "audio_toolbox")]
{
println!("cargo:rustc-link-lib=framework=AudioToolbox");
frameworks.push("AudioToolbox");
headers.push("AudioToolbox.framework/Headers/AudioToolbox.h");
}
#[cfg(feature = "audio_unit")]
{
println!("cargo:rustc-link-lib=framework=AudioUnit");
frameworks.push("AudioUnit");
headers.push("AudioUnit.framework/Headers/AudioUnit.h");
}
#[cfg(feature = "core_audio")]
{
println!("cargo:rustc-link-lib=framework=CoreAudio");
frameworks.push("CoreAudio");
match platform {
"MacOSX" => headers.push("CoreAudio.framework/Headers/CoreAudio.h"),
"iPhoneOS" | "iPhoneSimulator" => headers.push("CoreAudio.framework/Headers/CoreAudioTypes.h"),
_ => unreachable!(),
}
}
#[cfg(feature = "open_al")]
{
println!("cargo:rustc-link-lib=framework=OpenAL");
frameworks.push("OpenAL");
headers.push("OpenAL.framework/Headers/al.h");
headers.push("OpenAL.framework/Headers/alc.h");
}
#[cfg(feature = "core_midi")]
{
if (platform == "MacOSX") {
println!("cargo:rustc-link-lib=framework=CoreMIDI");
frameworks.push("CoreMIDI");
headers.push("CoreMIDI.framework/Headers/CoreMIDI.h");
} else {
panic!("framework CoreMIDI is not supported by Apple on this platform");
}
}
#[cfg(feature = "av_foundation")]
{
if (platform == "MacOSX") {
panic!("framework AVFoundation is not supported by Apple on this platform");
} else {
println!("cargo:rustc-link-lib=framework=AVFoundation");
frameworks.push("AVFoundation");
headers.push("AVFoundation.framework/Headers/AVFoundation.h");
}
}
// Get the cargo out directory.
let out_dir = PathBuf::from(env::var("OUT_DIR")
.expect("env variable OUT_DIR not found"));
// Begin building the bindgen params.
let mut builder = bindgen::Builder::default();
// Set SDK Path and Frameworks Path
builder = builder
.clang_arg("-isysroot").clang_arg(sdk_path)
.clang_arg(format!("-F/{}", frameworks_path));
// Add all headers.
for relative_path in headers {
let absolute_path = format!("{}/{}", frameworks_path, relative_path);
println!("{}", absolute_path);
builder = builder.header(absolute_path);
}
println!("Target: {}", target);
println!("Platform: {}", platform);
println!("SDK Path: {}", sdk_path);
println!("Frameworks Path: {}", frameworks_path);
// Generate the bindings.
let bindings = builder
.clang_arg("-target").clang_arg(target)
.trust_clang_mangling(false)
.derive_default(true)
// Debug Code
//.clang_arg("-v")
//.dump_preprocessed_input()
.generate()
.expect("unable to generate bindings");
// Write them to the crate root.
bindings.write_to_file(out_dir.join("coreaudio.rs"))
.expect("could not write bindings");
}
fn main() {
let (target, platform) = match std::env::var("TARGET") {
Ok(val) => {
match val.as_ref() {
"x86_64-apple-darwin" |
"i686-apple-darwin" => (val, "MacOSX"),
"aarch64-apple-ios" => (String::from("arm64-apple-ios"), "iPhoneOS"),
"armv7-apple-ios" |
"armv7s-apple-ios" |
"i386-apple-ios" |
"x86_64-apple-ios" => (val, "iPhoneOS"),
_ => panic!("coreaudio-sys requires macos or ios target. Found: {}", val),
}
},
Err(_e) => panic!("TARGET environment variable not found (are you running this from cargo?)")
};
if let Ok(sdk_path) = isysroot_path(&platform.to_lowercase()) {
if let Ok(frameworks_path) = frameworks_path(platform) {
build(&target, platform, &sdk_path, &frameworks_path);
} else {
eprintln!("coreaudio-sys could not find frameworks path");
}
} else {
eprintln!("coreaudio-sys could not find sdk path");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment