RustAudio / coreaudio-sys

Raw bindings to the OSX CoreAudio framework generated by bindgen (see coreaudio-rs for a more rust-esque wrapper).
MIT License
63 stars 39 forks source link

Doesn't cross-compile from Linux #23

Open totorigolo opened 5 years ago

totorigolo commented 5 years ago

Hi!

I tried to compile an Amethyst (mini) game for macos from my Linux, since I don't have a mac but some of my friends do. Here are my commands:

rustup default nightly
rustup install nightly-x86_64-apple-darwin
rustup target add x86_64-apple-darwin
cargo build --release --target=x86_64-apple-darwin

Everything went fine until:

$ cargo build --release --target=x86_64-apple-darwin                                                 130 ↵
   Compiling coreaudio-sys v0.2.2
error: couldn't read /home/totorigolo/Programmation/Rust/ldjam-43/target/x86_64-apple-darwin/release/build/coreaudio-sys-a35cb65331eb6c13/out/coreaudio.rs: No such file or directory (os error 2)
 --> /home/totorigolo/.cargo/registry/src/github.com-1ecc6299db9ec823/coreaudio-sys-0.2.2/src/lib.rs:6:1
  |
6 | include!(concat!(env!("OUT_DIR"), "/coreaudio.rs"));
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: Could not compile `coreaudio-sys`.

To learn more, run the command again with --verbose.

In ./target/x86_64-apple-darwin/release/build/coreaudio-sys-a35cb65331eb6c13/, there is this error:

coreaudio-sys requires macos or ios target

Any ideas on how I can fix this? On the 368 dependencies, at least 360 do compile, so I guess that it's not the only crate that does bindings.


$ cargo --version --verbose                   
cargo 1.32.0-nightly (5e85ba14a 2018-12-02)
release: 1.32.0
commit-hash: 5e85ba14aaa20f8133863373404cb0af69eeef2c
commit-date: 2018-12-02

$ rustc --version --verbose                 
... cannot execute binary file
$ rustup update
info: syncing channel updates for 'nightly-x86_64-apple-darwin'
...
info: latest update on 2018-12-06, rust version 1.32.0-nightly (14997d56a 2018-12-05)
...
Rhuagh commented 5 years ago

You need xcode headers installed to build, no way around that fact unfortunately.

ChunMinChang commented 5 years ago

One way for cross-compiling is to import your own raw bindings rather than using bindgen on the platform that you have no idea where those headers are. servo's core-foundation-rs is an example.

You could copy the coreaudio.rs generated from this crate and replace #[link = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/XXXX"] to #[link(name = "XXXX", kind = "framework")] as your fullback raw bindings. core-audio-sys might be another choice, but I am not sure how many bindings it provides.

If you're running on non-apple os, but your std::env::var("TARGET") is one of apple series, e.g., x86_64-apple-darwin or i686-apple-darwin (see all support target here), you can link to the frameworks by cargo:rustc-link-lib=framework=XXXX as long as they are installed. In lib.rs, including the fullback raw bindings instead of the env!("OUT_DIR")/coreaudio.rs should make it work.

Specifically, you can change the non-apple-os case in build.rs like:

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {
    let target = std::env::var("TARGET").unwrap();
    // Using the fullback raw bindings instead
    if target.contains("-apple") {
        println!("cargo:rustc-link-lib=framework=AudioUnit");
        println!("cargo:rustc-link-lib=framework=CoreAudio");
        println!("cargo:rustc-cfg=fullback_bindings");
    } else {
        eprintln!("{} is not a valid target for coreaudio-sys.", target);
    }
}

In the lib.rs, include your fullback raw bindings if the bindings doesn't be generated from the bindgen:

#[cfg(fullback_bindings)]
include!("fullback_coreaudio.rs");

#[cfg(not(fullback_bindings))]
include!(concat!(env!("OUT_DIR"), "/coreaudio.rs"));

and the fullback raw bindings(fullback_coreaudio.rs here) may look like

// Replace #[link = "...../SDKs/MacOSX.sdk/System/Library/Frameworks/AudioUnit"]
#[link(name = "AudioUnit", kind = "framework")]
// Replace #[link = "...../SDKs/MacOSX.sdk/System/Library/Frameworks/CoreAudio"]
#[link(name = "CoreAudio", kind = "framework")]|
// Replace #[link = "...../SDKs/MacOSX.sdk/System/Library/Frameworks/XXXX"]
#[link(name = "XXXX", kind = "framework")]|
...

// The following code is copied from coreaudio.rs generated from the bindgen
...
...
...
 pub const TARGET_OS_MAC : u32 = 1 ; ....

Hope this helps.

zicklag commented 5 years ago

I am getting the same exact issue, but I do have the MacOS headers ( the whole SDK and the cross-compiler toolchain built with osxcross ). What I'm confused at is that the message coreaudio-sys requires macos or ios target should only be displayed when the target is not either "macos" or "ios". If I've set the cargo --target to x86_64-apple-darwin shouldn't that set the target to "macos"?

Also, after patching the build.rs script for this repo and hardcoding the path to the frameworks path, I still get an error:

error: failed to run custom build command for `coreaudio-sys v0.2.2`
process didn't exit successfully: `/arsenal/arsenal-runtime/target/debug/build/coreaudio-sys-110096316bbc4140/build-script-build` (exit code: 101)
--- stdout
cargo:rustc-link-lib=framework=AudioUnit
cargo:rustc-link-lib=framework=CoreAudio

--- stderr
/System/Library/Frameworks/AudioUnit.framework/Headers/AudioUnit.h:12:10: fatal error: 'TargetConditionals.h' file not found
/System/Library/Frameworks/AudioUnit.framework/Headers/AudioUnit.h:12:10: fatal error: 'TargetConditionals.h' file not found, err: true
thread 'main' panicked at 'unable to generate bindings: ()', src/libcore/result.rs:997:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

It looks like it just isn't adding the /System/Library/Frameworks/Kernel.framework/Headers dir to the include path, but I don't know how to add it.

mitchmindtree commented 5 years ago

It's very likely that to get this working with iOS there might be some tweaking necessary, as I've only tested this with macOS since the last bindgen update.

Very open to PRs for both fallback bindings and any tweaks required to get iOS working!

zicklag commented 5 years ago

I got this to compile on my Linux system for Mac with the following diff to the build.rs file ( note that I symlinked the /System dir from the OSX SDK to /System on my host ):

diff --git a/build.rs b/build.rs
index 2974293..0ab4036 100644
--- a/build.rs
+++ b/build.rs
@@ -140,6 +140,7 @@ fn build(frameworks_path: &str) {

     // Generate the bindings.
     let bindings = builder
+        .clang_args(&["-I/System/Library/Frameworks/Kernel.framework/Headers", "-I/build/osxcros
s/target/SDK/MacOSX10.11.sdk/usr/include"])
         .trust_clang_mangling(false)
         .derive_default(true)
         .rustfmt_bindings(false)
@@ -152,16 +153,16 @@ fn build(frameworks_path: &str) {
         .expect("could not write bindings");
 }

-#[cfg(any(target_os = "macos", target_os = "ios"))]
+//#[cfg(any(target_os = "macos", target_os = "ios"))]
 fn main() {
-    if let Ok(directory) = frameworks_path() {
-        build(&directory);
-    } else {
-        eprintln!("coreaudio-sys could not find frameworks path");
-    }
+    //if let Ok(directory) = frameworks_path() {
+        build("/System/Library/Frameworks");
+    //} else {
+        //eprintln!("coreaudio-sys could not find frameworks path");
+    //}
 }

-#[cfg(not(any(target_os = "macos", target_os = "ios")))]
-fn main() {
-    eprintln!("coreaudio-sys requires macos or ios target");
-}
+//#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+//fn main() {
+    //eprintln!("coreaudio-sys requires macos or ios target");
+//}

So first I have to understand why building with cargo build --target x86_64-apple-darwin causes the second main() function to run and output coreaudio-sys requires macos or ios target error. For some reason it is not detecting that I am building for MacOS.

Second @mitchmindtree what do you think about reading from a CFLAGS environment variable to allow you to add those -I/path/to/headers flags that I needed to add? Or do you know if there is a way to automate the detection of those headers and add them to the path. I'm thinking that there probably isn't because I'm not a Linux system and not a Mac.

I'm good submitting a PR for a way to make this work, I just need to know how we want to handle this.

zicklag commented 5 years ago

I think I just figured out why this isn't correctly detecting that I'm building for MacOS:

#[cfg(not(any(target_os = "macos", target_os = "ios")))]

The problem with the line above is that the build.rs script isn't getting built for MacOS. The build script is getting build for Linux because the build script gets run by Cargo on my host system, not on the target system. That means that we need a different way to tell whether or not the application target is MacOS.

Edit: We can use the TARGET environment variable that is set by Cargo to the target triple. If we do that to detect the target and additionally read a CFLAGS environment variable to allow setting the extra include directories, that would make it possible to compile on Linux, at least in my environment. It would probably be good to have an environment variable like FRAMEWORK_PATH as well for people who don't want to drop the System dir in the root of their fileystem ( I don't mind because I'm building inside of a container, but probably I would mind if it were anywhere else ).