libsdl-org / SDL

Simple Directmedia Layer
https://libsdl.org
zlib License
9.98k stars 1.84k forks source link

[macos] SDL3.xcframework installed in /Library/Frameworks, clang not finding framework #9479

Open nbriggs opened 7 months ago

nbriggs commented 7 months ago

On macOS Monterey, 12.7.4 - have both SDL2 and SDL3 installed in /Library/Frameworks:

 % ls /Library/Frameworks | grep SDL
SDL2-2.28.framework
SDL2-2.30.0.framework
SDL2-2.30.1.framework
SDL2.framework
SDL3.xcframework

and while compiling with -framework SDL2 works, compiling with -framework SDL3 does not, and produces the error

clang -m64 -target x86_64-apple-darwin -std=gnu99 [...] -F /Library/Frameworks -framework SDL3 -o ../darwin.x86_64/ldesdl
ld: framework not found SDL3
clang: error: linker command failed with exit code 1 (use -v to see invocation)

It looks as though clang

% clang --version
Apple clang version 13.0.0 (clang-1300.0.29.30)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

isn't prepared to deal with the .xcframework properly?

sridenour commented 7 months ago

An .xcframework isn't the same as a .framework

Does it work if you put SDL3.framework in /Library/Frameworks?

nbriggs commented 6 months ago

I've recently upgraded the build system to run macOS Ventura.

@sridenour I tried symlinking /Library/Frameworks/SDL3.framework to the corresponding directory (for macOS) in SDL3.xcframework -- didn't work, but it did expose this warning:

CMake Warning (dev) at /Library/Frameworks/SDL3.framework/Resources/CMake/sdl3-config-version.cmake:14 (message):
  Cannot not find SDL_version.h.  This script is meant to be placed in
  share/cmake/SDL3, next to SDL3.xcframework
Call Stack (most recent call first):
  CMakeLists.txt:99 (FIND_PACKAGE)
This warning is for project developers.  Use -Wno-dev to suppress it.

CMake Error at /Library/Frameworks/SDL3.framework/Resources/CMake/sdl3-config.cmake:17 (message):
  File or directory
  /Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework/Versions/SDL3.xcframework
  referenced by variable _sdl3_xcframework_path does not exist !
Call Stack (most recent call first):
  /Library/Frameworks/SDL3.framework/Resources/CMake/sdl3-config.cmake:73 (set_and_check)
  CMakeLists.txt:99 (FIND_PACKAGE)

and if you put a copy of .../SDL3.framework/Resources/CMake/sdl3-config-version.cmake in /Library/Frameworks/share/cmake/SDL3/, and get rid of the symlink so it's just /Library/Frameworks/SDL3.xcframework/... then it almost works. Configuration using find_package(SDL3 REQUIRED) is OK, compilation is OK, but the resulting executable (on macOS Ventura 13.6.7) fails because it depends on:

    @rpath/SDL3.framework/Versions/A/SDL3 (compatibility version 101.0.0, current version 101.0.0)

and @rpath is not set (there's neither a system default that would point to the SDL3.framework under the SDL3.xcframework; nor is there an LC_RPATH in the executable). With SDL2 builds on Ventura I had to add a "-rpath /Library/Frameworks" to handle the dependency on @rpath/SDL2.framework/... - I suppose for SDL3 builds I could add -rpath /Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/ -- but this amount of tweaking is getting ridiculous.

sridenour commented 6 months ago

A couple things:

1) Why are you using SDL3.xcframework? I built and use SDL3.framework and it works just fine.

2) Speaking of using a .framework instead of an .xcframework, it's better to embed SDL3 in your app bundle rather than require users to install it to a system location and have to deal with version conflicts, requiring extra steps to install, etc

3) I don't know very much about CMake. For macOS, I use Xcode, which makes it so much easier to deal with stuff like building correct app bundles, embedding any .frameworks and other resources in the bundle, code signing, etc. That said, it looks like trying to symlink into the .xcframework is part of the problem.

madebr commented 6 months ago

Are you using CMake 3.28+? That version introduced support for xcframeworks, which the xcframework cmake config file uses. https://github.com/libsdl-org/SDL/blob/4d32f66fe7a1946384b062cea87f520b75139536/Xcode/SDL/pkg-support/resources/CMake/sdl3-config.cmake#L93-L100

nbriggs commented 6 months ago

@sridenour -

  1. Because SDL3 is being released as SDL3.xcframework (in a .dmg) - and I'd rather install it rather than building it
  2. We're not delivering as a app bundle, and it's not been a problem for anyone that uses the executable to install the framework (SDL2 up to this point) from the released .dmg
  3. If we were only building for macOS then I might use only XCode, but we are building for various Linux flavors, FreeBSD, Solaris 10 and 11, other *BSDs, macOS, etc. In the past we released simple Makefiles, but now people want to build on odd systems and don't seem to be comfortable with fixing Makefiles (and the autoconf etc. have got completely out of control) so someone made the choice to use CMake as an alternative (we still release the hand crafted Makefiles too).
nbriggs commented 6 months ago

@madebr

% cmake --version
cmake version 3.29.2
nbriggs commented 6 months ago

@madebr - BTW, it's annoying that from the SDL3 dmg one can't just drop the SDL3.xcframework into /Library/Frameworks -- one has to then go and install /Library/Frameworks/share/cmake/SDL3/sdl3-config.cmake by hand. I think it's a CMake bug that they don't automatically look in the right places in the .xcframework -- there's a feature request in with Kitware to fix that, but no idea if/when they'll do it.

madebr commented 6 months ago

Let's verify things first, are you extracing the 2 complete directories from the dmg into /Library/Frameworks? SDL3.xcframework and share? The share folder situation is annoying indeed. I created that issue upstream, but I don't know what priority they assigned it.

During generation of the release binaries, a small test project is built, that generates this output. Perhaps you can experiment a bit with your CMake arguments?

Also, I assume your project is not fiddling with *_RPATH cmake properties?

madebr commented 6 months ago

Looking at the actions output, it seems like we don't test running macos executables so the problem might appear there as well.

Does doing set_property(TARGET yourgame APPEND PROPERTY BUILD_RPATH "/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64") fix anything? You might need to play a bit with the path: I can't test due to no access to an Apple.

edit: my initial message had a wrong cmake command. Please try the final iteration :)

nbriggs commented 6 months ago

I didn't initially copy over the share directory (it was hidden in/behind the SDL logo as Finder displayed the contents to me), but I did install the required file by hand. Thanks for putting in the CMake issue (I think that's where I commented on having the same problem)

Yeah, everything links OK, it just doesn't run - it would be good to test that in the verification ;-)

I haven't tried your set_property... hack yet - I was just seeing if I could get the target_link_options(...) to let me just add -rpath ...

nbriggs commented 6 months ago

BTW - the ReadMe.txt in the prerelease SDL3.dmg only talks about it as SDL3.framework, not SDL3.xcframework, and it doesn't mention the need to copy over the share directory.

madebr commented 6 months ago

I didn't initially copy over the share directory (it was hidden in/behind the SDL logo as Finder displayed the contents to me), but I did install the required file by hand. Thanks for putting in the CMake issue (I think that's where I commented on having the same problem)

Indeed, you were the only non-kitware person to comment on that issue. Before creating that issue, I asked on the #cmake channel at cpplang.slack.com, where the response was lukewarm. So I don't expect a fix anytime soon.

I haven't tried your set_property... hack yet - I was just seeing if I could get the target_link_options(...) to let me just add -rpath ...

It would be nice if you can get it to work with set_property(...), and create a CMake issue with a minimum project (I'll help you trimming it down). When using defaults, the binaries inside the build directory should "just work". (Installed binaries is another matter)

BTW - the ReadMe.txt in the prerelease SDL3.dmg only talks about it as SDL3.framework, not SDL3.xcframework, and it doesn't mention the need to copy over the share directory.

Thanks, I'll make sure to add a few lines to the readme.

nbriggs commented 6 months ago

Re: "I had to add a -rpath /Library/Frameworks for Ventura" -- that was in my alternative Makefile builds, not CMake. CMake for SDL2 appears to add an LC_RPATH command to the executable. It doesn't do that automatically for SDL3. Not sure if that's a difference between the sdl2-config.cmake and sdl3-config.cmake, or CMake itself doing something differently for .framework vs .xcframework, or...

madebr commented 6 months ago

I think it has everything to do with CMake's support for xcframworks not being complete.

The project below should reproduce your issue, with these commands to reproduce:

cmake -S /path/to/cmake/script -B /tmp/sdl3-minimum-example --fresh
cmake --build /tmp/sdl3-minimum-example
ctest --test-dir /tmp/sdl3-minimum-example
cmake_minimum_required(VERSION 3.29)
project(use_sdl3_xcframework LANGUAGES C)

set(SDL3_XCFRAMEWORK "/Library/Frameworks for Ventura/SDL3.xcframework")

# Create imported shared library for the SDL3 xcframework
add_library(SDL3::SDL3-shared SHARED IMPORTED)
set_property(SDL3::SDL3-shared PROPERTY IMPORTED_LOCATION "${SDL3_XCFRAMEWORK}")

# Source for minimal SDL3 executable
file(WRITE main.c [==[
#include <SDL3/SDL.h>
int main(int argc, char *argv[]) {
  (void)argc; (void)argv;
  SDL_Init(SDL_INIT_EVENTS);
  SDL_Quit();
  return 0;
}
]==])

enable_testing()

# Create executable, not setting rpath
add_executable(mygame_without_rpath main.c)
target_link_libraries(mygame_without_rpath PRIVATE SDL3::SDL3-shared)
add_test(NAME mygame_without_rpath COMMAND mygame_without_rpath)

# Create executable, setting rpath
add_executable(mygame_with_rpath main.c)
target_link_libraries(mygame_with_rpath PRIVATE SDL3::SDL3-shared)
set_property(TARGET mygame_with_rpath APPEND PROPERTY BUILD_RPATH "${SDL3_XCFRAMEWORK}")
add_test(NAME mygame_with_rpath COMMAND mygame_with_rpath)

I did not test this, so please correct my errors.

nbriggs commented 6 months ago

@madebr - cool, thanks -- I'll check that out. I had just made my program runnable by adding


  SET_PROPERTY(TARGET ldesdl APPEND PROPERTY BUILD_RPATH "/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/")
`` 
-- i'll swap over to using `"${SDL3_XCFRAMEWORK}"` instead of the raw path, and then run your example.
madebr commented 6 months ago

It would be nice if you could make my minimum project above working, and create a cmake issue for this. As I said before, the executable should work without setting rpath properties.

nbriggs commented 6 months ago

OK -- with your example, need this change:

diff CMakeLists.txt~ CMakeLists.txt
8c8
< set_property(SDL3::SDL3-shared PROPERTY IMPORTED_LOCATION "${SDL3_XCFRAMEWORK}")
---
> set_property(TARGET SDL3::SDL3-shared PROPERTY IMPORTED_LOCATION "${SDL3_XCFRAMEWORK}")

There are questions about what the value of SDL3_XCFRAMEWORK should be:

set(SDL3_XCFRAMEWORK "/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework")

compiles OK, but results in

% ./mygame_with_rpath 
dyld[32771]: Library not loaded: @rpath/SDL3.framework/Versions/A/SDL3
  Referenced from: <6A9B892D-AC32-3278-AD53-CD930FCA4A85> /private/tmp/sdl3-minimum-example/mygame_with_rpath
  Reason: tried: '/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework/SDL3.framework/Versions/A/SDL3' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework/SDL3.framework/Versions/A/SDL3' (no such file), '/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework/SDL3.framework/Versions/A/SDL3' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework/SDL3.framework/Versions/A/SDL3' (no such file)

while

set(SDL3_XCFRAMEWORK "/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64")

results in the compile failing with

[ 25%] Building C object CMakeFiles/mygame_without_rpath.dir/main.c.o
/Users/briggs/Projects/sdl3link/main.c:1:10: fatal error: 'SDL3/SDL.h' file not found
#include <SDL3/SDL.h>
         ^~~~~~~~~~~~
1 error generated.

and

set(SDL3_XCFRAMEWORK "/Library/Frameworks/SDL3.xcframework")

compiles OK but fails with

% /tmp/sdl3-minimum-example/mygame_with_rpath 
dyld[32956]: Library not loaded: @rpath/SDL3.framework/Versions/A/SDL3
  Referenced from: <4D619860-ABA6-34EC-B463-DB565C4104DA> /private/tmp/sdl3-minimum-example/mygame_with_rpath
  Reason: tried: '/Library/Frameworks/SDL3.xcframework/SDL3.framework/Versions/A/SDL3' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Library/Frameworks/SDL3.xcframework/SDL3.framework/Versions/A/SDL3' (no such file), '/Library/Frameworks/SDL3.xcframework/SDL3.framework/Versions/A/SDL3' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Library/Frameworks/SDL3.xcframework/SDL3.framework/Versions/A/SDL3' (no such file)

So there isn't a combination that seems to work for both compilation and running. I'm looking at what might need to be added to get things working.

madebr commented 6 months ago

We're trying to use the xcframework here, so it should be set(SDL3_XCFRAMEWORK "/Library/Frameworks/SDL3.xcframework").

Try doing set_property(TARGET mygame_with_rpath APPEND PROPERTY BUILD_RPATH "${SDL3_XCFRAMEWORK}/macos-arm64_x86_64").

nbriggs commented 6 months ago

Yes -- that's the answer. So, with CMakeLists.txt:

cmake_minimum_required(VERSION 3.29)
project(use_sdl3_xcframework LANGUAGES C)

set(SDL3_XCFRAMEWORK "/Library/Frameworks/SDL3.xcframework")

# Create imported shared library for the SDL3 xcframework
add_library(SDL3::SDL3-shared SHARED IMPORTED)
set_property(TARGET SDL3::SDL3-shared PROPERTY IMPORTED_LOCATION "${SDL3_XCFRAMEWORK}")

# Source for minimal SDL3 executable
file(WRITE main.c [==[
#include <SDL3/SDL.h>
int main(int argc, char *argv[]) {
  (void)argc; (void)argv;
  SDL_Init(SDL_INIT_EVENTS);
  SDL_Quit();
  return 0;
}
]==])

enable_testing()

# Create executable, not setting rpath
add_executable(mygame_without_rpath main.c)
target_link_libraries(mygame_without_rpath PRIVATE SDL3::SDL3-shared)
add_test(NAME mygame_without_rpath COMMAND mygame_without_rpath)

# Create executable, setting rpath
add_executable(mygame_with_rpath main.c)
target_link_libraries(mygame_with_rpath PRIVATE SDL3::SDL3-shared)
set_property(TARGET mygame_with_rpath APPEND PROPERTY BUILD_RPATH "${SDL3_XCFRAMEWORK}/macos-arm64_x86_64")
add_test(NAME mygame_with_rpath COMMAND mygame_with_rpath)

doing

cmake -S . -B /tmp/sdl3-minimum-example --fresh  # from the directory with CMakeLists.txt
cmake --build /tmp/sdl3-minimum-example
ctest --test-dir /tmp/sdl3-minimum-example

produces, for the ctest output:

% ctest --test-dir /tmp/sdl3-minimum-example
Internal ctest changing into directory: /tmp/sdl3-minimum-example
Test project /tmp/sdl3-minimum-example
    Start 1: mygame_without_rpath
1/2 Test #1: mygame_without_rpath .............Subprocess aborted***Exception:   0.00 sec
    Start 2: mygame_with_rpath
2/2 Test #2: mygame_with_rpath ................   Passed    0.05 sec

50% tests passed, 1 tests failed out of 2

Total Test time (real) =   0.05 sec

The following tests FAILED:
      1 - mygame_without_rpath (Subprocess aborted)
Errors while running CTest
Output from these tests are in: /tmp/sdl3-minimum-example/Testing/Temporary/LastTest.log
Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely.
madebr commented 6 months ago

That's a good reproducer, I'd say. Thanks for investing time into this!

nbriggs commented 6 months ago

No worries - it's a small amount of my time invested that (a) lets me fix my builds, and (b) may help others avoid the same issue. Thank you for sharing your knowledge of CMake to get to a workaround. I'll file an issue over on CMake's gitlab.

nbriggs commented 6 months ago

@madebr - would you like credit for working out the details over in CMake issue 25998 ?

madebr commented 6 months ago

There's no need for that.

I'm honestly not sure what's the appropriate solution for this. The xcframework can theoretically have a macos-arm64 and a macos-x86_64 subdirectory (instead of macos-arm64_x86_64 ). Then, a fat binary would need to add 2 paths to the runpath.

nbriggs commented 6 months ago

From a developer standpoint, being a consumer of the tools and libraries/frameworks, something that "just works" on all the different systems that CMake and SDLx are available on, without my having to special-case for macOS, would be a good choice. If I were creating frameworks, I think i'd prefer it to be a fix in CMake, so everybody doesn't have to fuss around in their cmake config. As it stands now, I have an if(APPLE)...fix the rpath... clause in the SDL3 part of my CMakeLists.txt, which will suffice until things get sorted, I guess.

Regarding the .xcframework subdirectories -- is that true? Would xcodebuild -create-xcframework [-framework path]+ not combine all the macOS architectures into a single universal blob? (as you can tell, I've never had to build a framework).

madebr commented 1 month ago

@nbriggs Can you try the SDL3-3.1.2.dmg asset from the recent 3.1.2 release? I think we might need to use the frameworks inside the xcframework, and unconditionally disable this if.

maia-s commented 1 month ago

I have the same problem with the framework from the SDL 3.1.3 preview release. I copied both SDL3.xcframework and share to /Library/Frameworks

If I link to the SDL3.framework inside the xcframework, it links, but then it fails to link at runtime with

dyld[85945]: Library not loaded: @rpath/SDL3.framework/Versions/A/SDL3
  Referenced from: <08A14158-99A6-3A5D-90BA-573BA6D06C04> /private/tmp/test
  Reason: no LC_RPATH's found
zsh: abort      ./test

This happens both with clang and with rust

Clang command that fails to find SDL: clang -framework SDL3 test.c -o test

Clang command that links but fails to run with rpath error: clang -F/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64 -framework SDL3 test.c -o test

madebr commented 1 month ago

When building on the command line, you always need to add the runpath yourself. This is also required on Linux, and other unixes.

The issue with CMake is that it should set the runpath automatically, but it doesn't.

On Linux the command is -Wl,-rpath,/path/to/directory/containing/library. Try adding -Wl,-rpath,/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64

maia-s commented 1 month ago

I'm new to building on mac, but on Linux the rpath is inherited from the library. From searching it seems this should happen on mac as well, but it doesn't here?

Anyway, adding the rpath makes it go a step further, but now it fails to verify the framework and wants to move it to the trash. Isn't the preview signed?

madebr commented 1 month ago

I'm new to building on mac, but on Linux the rpath is inherited from the library. From searching it seems this should happen on mac as well, but it doesn't here?

On Linux, when an executable does not have an rpath, then the loader looks in standard paths (amonst many LD_LIBRARY_PATH). When rpath is set, it first looks in those.

Anyway, adding the rpath makes it go a step further, but now it fails to verify the framework and wants to move it to the trash. Isn't the preview signed?

The assets were built on Github Actions, and supposedly signed by slouken.

About the warning, I've been told the following:

I've always had the warnings for SDL prebuilt frameworks, you have to go to the systems "Privacy & Security" settings to clear it the first time

slouken commented 1 month ago

GitHub actions don't sign the frameworks. Typical workflow is that Xcode will automatically sign and embed frameworks when assembling your app.

maia-s commented 1 month ago

I see, thanks. I'll have to put off dealing with that until later, but at least I can get it to link...

@madebr I think maybe I misunderstood something, but I meant that when you link with a library that has an rpath, you don't have to repeat that rpath yourself

nbriggs commented 1 month ago

@madebr - I'm back online. Currently dealing with the fallout of all the API changes between 2 and 3, but should be able to test soon. Regarding adding the RPATH, my fix was to include in my CMakeLists.txt:

    MESSAGE("-- Applying fixup for macOS RPATH for SDL3.xcframework")
    SET_PROPERTY(TARGET foo APPEND PROPERTY BUILD_RPATH "/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64")
madebr commented 1 month ago

@madebr - I'm back online. Currently dealing with the fallout of all the API changes between 2 and 3, but should be able to test soon. Regarding adding the RPATH, my fix was to include in my CMakeLists.txt:

  MESSAGE("-- Applying fixup for macOS RPATH for SDL3.xcframework")
  SET_PROPERTY(TARGET foo APPEND PROPERTY BUILD_RPATH "/Library/Frameworks/SDL3.xcframework/macos-arm64_x86_64")

Happy to have you back :) The "fix" you mentioned is not a fix, but a workaround because it wouldn't work for ios/tvos/ios-simulator/tvos-simulator etc. In the next prerelease, cmake will probably use the internal framework as IMPORTED_FRAMEWORK instead of the xcframework. Can you test these?

SDL: https://github.com/madebr/SDL/actions/runs/11309111417 SDL_image: https://github.com/madebr/SDL_image/actions/runs/11309547411 SDL_mixer: https://github.com/madebr/SDL_mixer/actions/runs/11309699802 SDL_ttf: https://github.com/madebr/SDL_ttf/actions/runs/11309827817

note: SDL_TARGET_IS_SIMULATOR detection of prerelease 3.1.3 is broken, so you need to configure in a fresh build folder.

nbriggs commented 1 month ago

Yeah, I didn't put the quotes around "fix" - they were in my head but not in the text. As soon as I have a chance to update all the source incompatibilities for 2=>3 I'll be trying a build with 3.

TheZitroX commented 2 weeks ago

@nbringgs why would you quote something you say?

nbriggs commented 2 weeks ago

@TheZitroX https://en.wikipedia.org/wiki/Scare_quotes

TheZitroX commented 2 weeks ago

Thanks for ref Wikipedia. But being ironic in a workflow? I just want to get SDL3 working to try making a small project with it🥲

nbriggs commented 2 weeks ago

@TheZitroX - I shared what I needed to do to get my CMake to run. madebr pointed out that from the SDL devs perspective it's not a real fix, but rather a workaround because I (and everyone else trying to build with SDL3) shouldn't have to do that, and it won't work in the general case of building for iOS/iPadOS/tvOS anyway. From my perspective: it's a couple of lines in one file that I control that solves the problem for the only Apple OS I care about building for at the moment (macOS).

The real fix is for the CMake devs to make changes that allow seamless use of .xcframework frameworks for all the platforms in the framework.

As far as irony in the comments on an issue... that's just part of human communication.

TheZitroX commented 2 weeks ago

But thanks for your help, for now I went back to Windows to start my project until this issue is solved for Mac🙌

madebr commented 2 weeks ago

@nbriggs Can you try today's 3.1.6 prerelease? The CMake script no longer uses the xcframework as IMPORTED_LOCATION, so the rpath issue should be gone. https://github.com/libsdl-org/SDL/releases/tag/preview-3.1.6

TheZitroX commented 2 weeks ago

Hey, just tested the 3.1.6 prerelease. Looks like it’s finally working on macOS (M2p)