Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various changes to the build script #28

Merged
merged 11 commits into from
Aug 27, 2019
8 changes: 6 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ notifications:
- [email protected]
os:
- osx
- linux
before_script:
- rustc --version
- cargo --version
- rustup component add rustfmt
- if [ "$TRAVIS_OS_NAME" = linux ]; then rustup target add x86_64-apple-darwin; fi
script:
- cargo build --verbose
- cargo test --verbose
- if [ "$TRAVIS_OS_NAME" = linux ]; then curl -sL https://github.com/phracker/MacOSX-SDKs/releases/download/10.13/MacOSX10.13.sdk.tar.xz | tar -Jxf -; export COREAUDIO_SDK_PATH="$PWD/MacOSX10.13.sdk"; fi
- cargo build --verbose --target=x86_64-apple-darwin
- if [ "$TRAVIS_OS_NAME" = osx ]; then cargo test --verbose; fi
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ homepage = "https://github.com/RustAudio/coreaudio-sys"
repository = "https://github.com/RustAudio/coreaudio-sys.git"
build = "build.rs"

[build-dependencies]
bindgen = "0.42"
[build-dependencies.bindgen]
version = "0.50"
default-features = false

[features]
default = ["audio_toolbox", "audio_unit", "core_audio", "open_al", "core_midi"]
Expand Down
14 changes: 3 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,10 @@ Raw bindings to Apple's Core Audio API for macos and iOS generated using [rust-b

When cross-compiling for MacOS on Linux there are two environment variables that are used to configure how `coreaudio-sys` finds the required headers and libraries. The following examples assume that you have OSXCross installed at `/build/osxcross`.

#### `COREAUDIO_CFLAGS`
#### `COREAUDIO_SDK_PATH`

This allows you to add arbitrary flags to the `clang` call that is made when auto-generating the Rust bindings to coreaudio. This will need to be set to include some headers used by coreaudio:
This tell `coreaudio-sys` where to find the MacOS SDK:

```bash
export COREAUDIO_CFLAGS=-I/build/osxcross/target/SDK/MacOSX10.11.sdk/System/Library/Frameworks/Kernel.framework/Headers -I/build/osxcross/target/SDK/MacOSX10.11.sdk/usr/include
```

#### `COREAUDIO_FRAMEWORKS_PATH`

This tell `coreaudio-sys` where to find the Frameworks path of the MacOS SDK:

```bash
export COREAUDIO_FRAMEWORKS_PATH=/build/osxcross/target/SDK/MacOSX10.11.sdk/System/Library/Frameworks
export COREAUDIO_SDK_PATH=/build/osxcross/target/SDK/MacOSX10.11.sdk
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to mention using BINDGEN_EXTRA_CLANG_ARGS here as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It did work for me without having to set any extra clang args, so it might be unnecessary, but in the even that somebody needs it, it could save them some time searching for it.

```
149 changes: 42 additions & 107 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,78 +1,30 @@
extern crate bindgen;

fn osx_version() -> Result<String, std::io::Error> {
use std::process::Command;

let output = Command::new("defaults")
.arg("read")
.arg("loginwindow")
.arg("SystemVersionStampAsString")
.output()?
.stdout;
let version_str = std::str::from_utf8(&output).expect("invalid output from `defaults`");
let version = version_str.trim_right();

Ok(version.to_owned())
}

fn parse_version(version: &str) -> Option<i32> {
version
.split(".")
.skip(1)
.next()
.and_then(|m| m.parse::<i32>().ok())
}

fn frameworks_path() -> Result<String, std::io::Error> {
// For 10.13 and higher:
//
// 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/…".

fn sdk_path(target: &str) -> Result<String, std::io::Error> {
// Use environment variable if set
if let Ok(path) = std::env::var("COREAUDIO_FRAMEWORKS_PATH") {
return Ok(path)
println!("cargo:rerun-if-env-changed=COREAUDIO_SDK_PATH");
if let Ok(path) = std::env::var("COREAUDIO_SDK_PATH") {
return Ok(path);
}

if osx_version()
.and_then(|version| Ok(parse_version(&version).map(|v| v >= 13).unwrap_or(false)))
.unwrap_or(false)
{
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 target = std::env::var("TARGET").unwrap();
let platform = if target.contains("apple-darwin") {
"MacOSX"
} else if target.contains("apple-ios") {
"iPhoneOS"
} else {
unreachable!();
};

let infix = if prefix == "/Library/Developer/CommandLineTools" {
format!("SDKs/{}.sdk", platform)
} else {
format!(
"Platforms/{}.platform/Developer/SDKs/{}.sdk",
platform, platform
)
};

let suffix = "System/Library/Frameworks";
let directory = format!("{}/{}/{}", prefix, infix, suffix);

Ok(directory)
use std::process::Command;

let sdk = if target.contains("apple-darwin") {
"macosx"
} else if target.contains("apple-ios") {
"iphoneos"
} else {
Ok("/System/Library/Frameworks".to_string())
}
unreachable!();
};
let output = Command::new("xcrun")
.args(&["--sdk", sdk, "--show-sdk-path"])
.output()?
.stdout;
let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcrun`");
Ok(prefix_str.trim_end().to_string())
}

fn build(frameworks_path: &str) {
fn build(sdk_path: Option<&str>, target: &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
Expand All @@ -85,80 +37,67 @@ fn build(frameworks_path: &str) {
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");
headers.push("AudioToolbox/AudioToolbox.h");
}

#[cfg(feature = "audio_unit")]
{
println!("cargo:rustc-link-lib=framework=AudioUnit");
frameworks.push("AudioUnit");
headers.push("AudioUnit.framework/Headers/AudioUnit.h");
headers.push("AudioUnit/AudioUnit.h");
}

#[cfg(feature = "core_audio")]
{
println!("cargo:rustc-link-lib=framework=CoreAudio");
frameworks.push("CoreAudio");
headers.push("CoreAudio.framework/Headers/CoreAudio.h");
headers.push("CoreAudio/CoreAudio.h");
}

#[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");
headers.push("OpenAL/al.h");
headers.push("OpenAL/alc.h");
}

#[cfg(all(feature = "core_midi"))]
{
if std::env::var("TARGET").unwrap().contains("apple-darwin") {
if target.contains("apple-darwin") {
println!("cargo:rustc-link-lib=framework=CoreMIDI");
frameworks.push("CoreMIDI");
headers.push("CoreMIDI.framework/Headers/CoreMIDI.h");
headers.push("CoreMIDI/CoreMIDI.h");
}
}

println!("cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS");
// 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();

builder = builder.clang_arg(format!("-F/{}", frameworks_path));
builder = builder.clang_args(&[&format!("--target={}", target)]);

// Add all headers.
for relative_path in headers {
let absolute_path = format!("{}/{}", frameworks_path, relative_path);
builder = builder.header(absolute_path);
if let Some(sdk_path) = sdk_path {
builder = builder.clang_args(&["-isysroot", sdk_path]);
}

// Link to all frameworks.
for relative_path in frameworks {
let link_instruction = format!("#[link = \"{}/{}\"]", frameworks_path, relative_path);
builder = builder.raw_line(link_instruction);
}
let meta_header: Vec<_> = headers
.iter()
.map(|h| format!("#include <{}>\n", h))
.collect();

builder = builder.header_contents("coreaudio.h", &meta_header.concat());

// Generate the bindings.
builder = builder
.trust_clang_mangling(false)
.derive_default(true)
.rustfmt_bindings(false);

if let Ok(cflags) = std::env::var("COREAUDIO_CFLAGS") {
builder = builder.clang_args(cflags.split(" "));
}
.derive_default(true);

let bindings = builder
.generate()
.expect("unable to generate bindings");
let bindings = builder.generate().expect("unable to generate bindings");

// Write them to the crate root.
bindings
Expand All @@ -168,14 +107,10 @@ fn build(frameworks_path: &str) {

fn main() {
let target = std::env::var("TARGET").unwrap();
if ! (target.contains("apple-darwin") || target.contains("apple-ios")) {
eprintln!("coreaudio-sys requires macos or ios target");
if !(target.contains("apple-darwin") || target.contains("apple-ios")) {
panic!("coreaudio-sys requires macos or ios target");
}

if let Ok(directory) = frameworks_path() {
build(&directory);
} else {
eprintln!("coreaudio-sys could not find frameworks path");
}
let directory = sdk_path(&target).ok();
build(directory.as_ref().map(String::as_ref), &target);
}