From 62f6e22802bdbda0e3f0062cf8394485ce71d4a1 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 13:52:41 +0900 Subject: [PATCH 01/11] Remove #[link] attributes They cause the following warnings: warning: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ cfg = "...")]` --> /tmp/coreaudio-sys/target/x86_64-apple-darwin/debug/build/coreaudio-sys-0d8fd082e359c2ec/out/coreaudio.rs:3:1 | 3 | #[link = "/tmp/MacOSX10.14.sdk/System/Library/Frameworks/AudioToolbox"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(ill_formed_attribute_input)] on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 And they are actually redundant with the cargo:rustc-link-lib lines the build script prints already. --- build.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/build.rs b/build.rs index 350761d..9038cdd 100644 --- a/build.rs +++ b/build.rs @@ -85,34 +85,29 @@ 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"); } #[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"); headers.push("CoreAudio.framework/Headers/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"); } @@ -121,7 +116,6 @@ fn build(frameworks_path: &str) { { if std::env::var("TARGET").unwrap().contains("apple-darwin") { println!("cargo:rustc-link-lib=framework=CoreMIDI"); - frameworks.push("CoreMIDI"); headers.push("CoreMIDI.framework/Headers/CoreMIDI.h"); } } @@ -140,12 +134,6 @@ fn build(frameworks_path: &str) { builder = builder.header(absolute_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); - } - // Generate the bindings. builder = builder .trust_clang_mangling(false) From 7150b8ca2ec774c5d8d957946e27fba7f5a5e3e9 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 13:58:53 +0900 Subject: [PATCH 02/11] Run cargo fmt --- build.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/build.rs b/build.rs index 9038cdd..415c16d 100644 --- a/build.rs +++ b/build.rs @@ -32,7 +32,7 @@ fn frameworks_path() -> Result { // Use environment variable if set if let Ok(path) = std::env::var("COREAUDIO_FRAMEWORKS_PATH") { - return Ok(path) + return Ok(path); } if osx_version() @@ -144,9 +144,7 @@ fn build(frameworks_path: &str) { builder = builder.clang_args(cflags.split(" ")); } - 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 @@ -156,7 +154,7 @@ fn build(frameworks_path: &str) { fn main() { let target = std::env::var("TARGET").unwrap(); - if ! (target.contains("apple-darwin") || target.contains("apple-ios")) { + if !(target.contains("apple-darwin") || target.contains("apple-ios")) { eprintln!("coreaudio-sys requires macos or ios target"); } @@ -166,4 +164,3 @@ fn main() { eprintln!("coreaudio-sys could not find frameworks path"); } } - From 765900948df3bafc6f9ca0b0f8eea349459d1ce5 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 13:59:59 +0900 Subject: [PATCH 03/11] Simplify and fix bindgen setup both for native and cross compilation The current setup requires 2 environment variables for cross-compilation, and can fail to build under some circumstances on a freshly installed OSX + Xcode machine. This change simplifies the setup by: - On native builds, using `xcrun --show-sdk-path` instead of `xcode-select -p` to find the SDK. - On cross builds, checking COREAUDIO_SDK_PATH. - Pass --target to clang, which makes things work for cross-compilation with plain clang (as opposed to osxcross). - Pass -isysroot to clang instead of -F, which makes it find all possible headers in the SDK, especially TargetConditionals.h, which lives in $COREAUDIO_SDK_PATH/usr/include. - Instruct cargo of the environment variables that affect the build script. COREAUDIO_CFLAGS is left as a convenience, for now. --- README.md | 14 ++------ build.rs | 97 +++++++++++++++---------------------------------------- 2 files changed, 29 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 34ebc55..2b6557c 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/build.rs b/build.rs index 415c16d..0822457 100644 --- a/build.rs +++ b/build.rs @@ -1,78 +1,32 @@ extern crate bindgen; -fn osx_version() -> Result { - 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 { - version - .split(".") - .skip(1) - .next() - .and_then(|m| m.parse::().ok()) -} - -fn frameworks_path() -> Result { - // 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/…". +const FRAMEWORKS_PATH: &'static str = "System/Library/Frameworks"; +fn sdk_path(target: &str) -> Result { // Use environment variable if set - if let Ok(path) = std::env::var("COREAUDIO_FRAMEWORKS_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: &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 @@ -114,7 +68,7 @@ fn build(frameworks_path: &str) { #[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"); headers.push("CoreMIDI.framework/Headers/CoreMIDI.h"); } @@ -126,11 +80,11 @@ fn build(frameworks_path: &str) { // 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), "-isysroot", sdk_path]); // Add all headers. for relative_path in headers { - let absolute_path = format!("{}/{}", frameworks_path, relative_path); + let absolute_path = format!("{}/{}/{}", sdk_path, FRAMEWORKS_PATH, relative_path); builder = builder.header(absolute_path); } @@ -140,6 +94,7 @@ fn build(frameworks_path: &str) { .derive_default(true) .rustfmt_bindings(false); + println!("cargo:rerun-if-env-changed=COREAUDIO_CFLAGS"); if let Ok(cflags) = std::env::var("COREAUDIO_CFLAGS") { builder = builder.clang_args(cflags.split(" ")); } @@ -158,9 +113,9 @@ fn main() { eprintln!("coreaudio-sys requires macos or ios target"); } - if let Ok(directory) = frameworks_path() { - build(&directory); + if let Ok(directory) = sdk_path(&target) { + build(&directory, &target); } else { - eprintln!("coreaudio-sys could not find frameworks path"); + eprintln!("coreaudio-sys could not find an appropriate SDK"); } } From 5277523245341615fd1837cf004eea5c19fb0c00 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 15:04:26 +0900 Subject: [PATCH 04/11] Update bindgen build dependency to 0.50 As of 0.49, bindgen supports a BINDGEN_EXTRA_CLANG_ARGS environment variable that can be used to pass extra arguments to clang. This can be used instead of COREAUDIO_CFLAGS. --- Cargo.toml | 2 +- build.rs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ff180a0..0b2bc4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/RustAudio/coreaudio-sys.git" build = "build.rs" [build-dependencies] -bindgen = "0.42" +bindgen = "0.50" [features] default = ["audio_toolbox", "audio_unit", "core_audio", "open_al", "core_midi"] diff --git a/build.rs b/build.rs index 0822457..4ae57c0 100644 --- a/build.rs +++ b/build.rs @@ -74,6 +74,7 @@ fn build(sdk_path: &str, target: &str) { } } + 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")); @@ -94,11 +95,6 @@ fn build(sdk_path: &str, target: &str) { .derive_default(true) .rustfmt_bindings(false); - println!("cargo:rerun-if-env-changed=COREAUDIO_CFLAGS"); - if let Ok(cflags) = std::env::var("COREAUDIO_CFLAGS") { - builder = builder.clang_args(cflags.split(" ")); - } - let bindings = builder.generate().expect("unable to generate bindings"); // Write them to the crate root. From 7a51ced741ebd1c35eeef54ce641127d82ff2281 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 15:20:54 +0900 Subject: [PATCH 05/11] Re-enable rustfmt of generated bindings Because recent versions of bindgen generate their bindings on one line, because of bindgen's use of u128 and rust's warnings of its unsafety FFI-wise, and because of https://github.com/rust-lang/rust/issues/62999, combined, building the crate generates massive logs... which exceed travis's maximum log length. It was disabled in #13 because back then rustfmt had some problems with the bindgen output. Now that rustfmt is part of rust, these kind of problems presumably should happen less if at all. --- build.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.rs b/build.rs index 4ae57c0..c597a7d 100644 --- a/build.rs +++ b/build.rs @@ -92,8 +92,7 @@ fn build(sdk_path: &str, target: &str) { // Generate the bindings. builder = builder .trust_clang_mangling(false) - .derive_default(true) - .rustfmt_bindings(false); + .derive_default(true); let bindings = builder.generate().expect("unable to generate bindings"); From 2edcf2e16adf8d3f6ed846ecaf08fe80b3ecef32 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 15:39:34 +0900 Subject: [PATCH 06/11] Panic instead of printing errors in the build script Simply printing errors to stderr does nothing visible by default. So when building without an SDK available, there is no apparent error showing up other than in some file "hidden" in target/, and an error that including coreaudio.rs failed. Similarly, when attempting to build for an unsupported target, the script reaches the `unreachable!()`. --- build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index c597a7d..593922f 100644 --- a/build.rs +++ b/build.rs @@ -105,12 +105,12 @@ fn build(sdk_path: &str, target: &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"); + panic!("coreaudio-sys requires macos or ios target"); } if let Ok(directory) = sdk_path(&target) { build(&directory, &target); } else { - eprintln!("coreaudio-sys could not find an appropriate SDK"); + panic!("coreaudio-sys could not find an appropriate SDK"); } } From 8dd06b54a0f6c76815008d05a2e9c4a6fb6cba20 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 15:46:57 +0900 Subject: [PATCH 07/11] Add rustfmt to the travis configuration --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 42967b0..9a4db39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ os: before_script: - rustc --version - cargo --version +- rustup component add rustfmt script: - cargo build --verbose - cargo test --verbose From 7e87594c92cdbe9dec3666165df8beb3ddbc2589 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 16:06:21 +0900 Subject: [PATCH 08/11] Disable bindgen features --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0b2bc4b..1aebb0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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.50" +[build-dependencies.bindgen] +version = "0.50" +default-features = false [features] default = ["audio_toolbox", "audio_unit", "core_audio", "open_al", "core_midi"] From 8b6ebfdbe9b9e59cbcd031a342a421c8a20d3659 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 17:02:48 +0900 Subject: [PATCH 09/11] Simplify the build script further Instead of passing the full paths to the headers to bindgen, create a meta header that #include's all the needed ones. This leaves it to clang to find them in the right /System/Library/Frameworks directory. --- build.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/build.rs b/build.rs index 593922f..1e337db 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,5 @@ extern crate bindgen; -const FRAMEWORKS_PATH: &'static str = "System/Library/Frameworks"; - fn sdk_path(target: &str) -> Result { // Use environment variable if set println!("cargo:rerun-if-env-changed=COREAUDIO_SDK_PATH"); @@ -44,33 +42,33 @@ fn build(sdk_path: &str, target: &str) { #[cfg(feature = "audio_toolbox")] { println!("cargo:rustc-link-lib=framework=AudioToolbox"); - headers.push("AudioToolbox.framework/Headers/AudioToolbox.h"); + headers.push("AudioToolbox/AudioToolbox.h"); } #[cfg(feature = "audio_unit")] { println!("cargo:rustc-link-lib=framework=AudioUnit"); - headers.push("AudioUnit.framework/Headers/AudioUnit.h"); + headers.push("AudioUnit/AudioUnit.h"); } #[cfg(feature = "core_audio")] { println!("cargo:rustc-link-lib=framework=CoreAudio"); - headers.push("CoreAudio.framework/Headers/CoreAudio.h"); + headers.push("CoreAudio/CoreAudio.h"); } #[cfg(feature = "open_al")] { println!("cargo:rustc-link-lib=framework=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 target.contains("apple-darwin") { println!("cargo:rustc-link-lib=framework=CoreMIDI"); - headers.push("CoreMIDI.framework/Headers/CoreMIDI.h"); + headers.push("CoreMIDI/CoreMIDI.h"); } } @@ -83,11 +81,12 @@ fn build(sdk_path: &str, target: &str) { builder = builder.clang_args(&[&format!("--target={}", target), "-isysroot", sdk_path]); - // Add all headers. - for relative_path in headers { - let absolute_path = format!("{}/{}/{}", sdk_path, FRAMEWORKS_PATH, relative_path); - builder = builder.header(absolute_path); - } + 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 From d2e8a69e408c51b88ee45063ef13eb4cb268f8af Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 17:10:58 +0900 Subject: [PATCH 10/11] Allow the SDK not to be found by the build script Now that the build script itself doesn't need the SDK path, allow not to specify one explicitly. This allows things to work out when the environment works out of the box one way or another (via wrapper scripts, setting BINDGEN_EXTRA_CLANG_ARGS, etc.) --- build.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/build.rs b/build.rs index 1e337db..2b511e0 100644 --- a/build.rs +++ b/build.rs @@ -24,7 +24,7 @@ fn sdk_path(target: &str) -> Result { Ok(prefix_str.trim_end().to_string()) } -fn build(sdk_path: &str, target: &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 @@ -79,7 +79,11 @@ fn build(sdk_path: &str, target: &str) { // Begin building the bindgen params. let mut builder = bindgen::Builder::default(); - builder = builder.clang_args(&[&format!("--target={}", target), "-isysroot", sdk_path]); + builder = builder.clang_args(&[&format!("--target={}", target)]); + + if let Some(sdk_path) = sdk_path { + builder = builder.clang_args(&["-isysroot", sdk_path]); + } let meta_header: Vec<_> = headers .iter() @@ -107,9 +111,6 @@ fn main() { panic!("coreaudio-sys requires macos or ios target"); } - if let Ok(directory) = sdk_path(&target) { - build(&directory, &target); - } else { - panic!("coreaudio-sys could not find an appropriate SDK"); - } + let directory = sdk_path(&target).ok(); + build(directory.as_ref().map(String::as_ref), &target); } From 0ed7f4cfc6d9dec5ecae967e905655e6cb81fae7 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 26 Jul 2019 23:12:07 +0900 Subject: [PATCH 11/11] Add Linux cross builds to the travis configuration --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9a4db39..c48317b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,13 @@ notifications: - mitchell.nordine@gmail.com 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