thlorenz / rid

Rust integrated Dart framework providing an easy way to build Flutter apps with Rust.
64 stars 4 forks source link

todo example build fails on macOS due to missing lib/libclang.dylib #31

Closed mz2 closed 2 years ago

mz2 commented 2 years ago

(Apologies if this is not the place to report this, and awesome work on rid – super happy to be sponsoring your efforts!)

I'm successful at building and running the todo example on Linux with the provided steps, but failing at it on a M1 based Mac on macOS 11.3. rid-build and rid-ffi in the rid repo itself build happily (I have nightly rust toolchain installed), flutter doctor reports success, etc.

./sh/bindgen in flutter/todo however fails thusly:

2021-09-26 16:20:26,839 DEBUG [rid_build::ffigen::run_ffigen] Running '"dart" "run" "ffigen" "--config" "/var/folders/h0/dgzvb85s2v719b_p2mxggkyr0000gn/T/.tmp6WxOrH/ffigen_config.yaml"' from: '"/Users/mz2/Developer/rid-examples/flutter/todo"'
thread 'main' panicked at 'Build failed: 
'dart run ffigen' failed to run successfully
stderr: 
stdout: Running in Directory: '/Users/mz2/Developer/rid-examples/flutter/todo'
[SEVERE] : Couldn't find dynamic library in default locations.
[SEVERE] : Please supply one or more path/to/llvm in ffigen's config under the key 'llvm-path'.

I have tried providing llvm-path via the pubspec.yaml (I thought it's the pubspec.yaml in flutter/todo/plugin?) by adding

ffigen:
  llvm-path: /Users/mz2/homebrew/opt/llvm

(I installed llvm via homebrew, also tried the version I have via Xcode 13 with no luck)

Given there indeed exists /Users/mz2/homebrew/opt/llvm/include and /Users/mz2/homebrew/opt/llvm/lib under that path, I must be providing the LLVM path somehow incorrectly, or to the wrong context? Also tried passing the actual path to libclang.dylib with DYLD_LIBRARY_PATH ./sh/bindgen but that didn't make a difference either.

Again, thanks for your work and sorry if this is not the place this issue should be reported.

thlorenz commented 2 years ago

I'm sorry you're running into these issues ... it's basically the ffigen tool I'm using under the hood that has trouble finding the llvm path. We solved a similar issue on linux: https://github.com/thlorenz/rid/pull/18 Maybe you can try to do something like that for paths on macos here: https://github.com/thlorenz/rid/blob/5c6a3b89543be5249b832f1a55f6fdb55ddc9ace/rid-build/src/ffigen/host_props.rs#L12

It's hard to triage this for me without reproducing the problem, so you're in the best position to find a fix. I'd very much appreciate a PR if you do.

If you need more help LMK!

mz2 commented 2 years ago

Ok, so I was able to work around that part of the problem by adding the following in (the two paths are synonymous, only one of them I guess would be required):

const MACOS_LLVM_PATHS: [&str; 3] = 
    ["/usr/local/opt/llvm/lib/",
     "/Users/mz2/homebrew/opt/llvm",
     "/Users/mz2/homebrew/Cellar/llvm/12.0.1"];

On the ffigen package's doc page (sorry, no anchor there for the precise spot in the doc) I see that there is a llvm-path configuration parameter also available, which should also be possible to pass in using a separate config file. Maybe this would be a more longer term solution for this category of problems?


This is probably where on an Intel machine I'd now have happily worked around the problem. However, on an M1 the gift keeps on giving once the linker now finds the library:

Invalid argument(s): Failed to load dynamic library '/Users/mz2/homebrew/opt/llvm/lib/libclang.dylib': dlopen(/Users/mz2/homebrew/opt/llvm/lib/libclang.dylib, 1): no suitable image found.  Did find:
    /Users/mz2/homebrew/opt/llvm/lib/libclang.dylib: mach-o, but wrong architecture
    /Users/mz2/homebrew/Cellar/llvm/12.0.1/lib/libclang.dylib: mach-o, but wrong architecture
#0      _open (dart:ffi-patch/ffi_dynamic_library_patch.dart:11:55)
#1      new DynamicLibrary.open (dart:ffi-patch/ffi_dynamic_library_patch.dart:20:12)
#2      initializeGlobals (package:ffigen/src/header_parser/data.dart:41:33)
#3      initParser (package:ffigen/src/header_parser/parser.dart:48:3)
#4      parse (package:ffigen/src/header_parser/parser.dart:22:3)
#5      main (package:ffigen/src/executables/ffigen.dart:54:19)
#6      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:281:32)
#7      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

Checking the architecture(s) of that dylib, I can see that has been built for ARM64:

❯ lipo -archs /Users/mz2/homebrew/opt/llvm/lib/libclang.dylib
arm64

I guess the dart run ffigen part is itself an Intel executable? Maybe I can work around this whole thing by using Xcode's LLVM, which I imagine is a fat binary... Checking...

mz2 commented 2 years ago

Yep, Xcode's version of LLVM works for the purpose, so this modification makes ./sh/bindgen build for me:

const MACOS_LLVM_PATHS: [&str; 2] = 
    ["/usr/local/opt/llvm/lib/",
     "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr"];
thlorenz commented 2 years ago

OK, awesome I'm happy you solved this! Could you please provide a PR with those fixes?

I'd like to get the home brew path in as well, but I'd have thought that home brew paths are linked from /usr/local/lib or /usr/local/opt. As a matter of fact my llvm is installed via brew and got linked to the dir we pass to ffigen:

$ ls -la /usr/local/opt/llvm
lrwxr-xr-x  1 thlorenz  admin    21B Sep 20 11:14 /usr/local/opt/llvm@ -> ../Cellar/llvm/12.0.1

Is it possible that in your case it just didn't get linked correctly or from somewhere else? If you installed a bunch of things with brew you should be able to see them by doing: ls -la /usr/local/opt/.

Either way if we try to link to the brew install directly we'd need a more generic way to point to the home directory, i.e. instead of:

"/Users/mz2/homebrew/opt/llvm" "~/homebrew/opt/llvm" or "$HOME/homebrew/opt/llvm"

Thanks for your help!

mz2 commented 2 years ago

Is it possible that in your case it just didn't get linked correctly or from somewhere else? If you installed a bunch of things with brew you should be able to see them by doing: ls -la /usr/local/opt/.

I've intentionally not wanted to symlink llvm on my system into /usr/local/opt (it's provided "keg only" by homebrew by default) because I use Xcode regularly and things get a bit f'd mixing and matching Xcode's and non-Xcode's.

mz2 commented 2 years ago

Could you please provide a PR with those fixes?

Sure thing, will send something for review on the weekend once I've tested out that the ~/ or $HOME gets evaluated in that context.

thlorenz commented 2 years ago

Also we should consider resolving the brew directory via a home dir obtained at runtime via this crate. I'm obviously fine with it not being a const in this case. Just look if you can do anything with lazy_static! to not resolve this more than once.