Packaging SDL for OSX

Would it be possible to provide the .a library for OSX ?

I know that dynamically linking is the best way to provide SDL, and it works great with older OSX, but with latest releases, if the application is not signed, the user can still run it, but if it links to a not signed dylib, it will not load, so that in the end static linking is mandatory if the OSX application will not be signed in the end.

Moreover, compiling libSDL on older XCode is not possible, using modern XCode on older OSX is not possible, so that for a C programmer that just want to make an application run also on OSX, precompiled libSDL is the only way to get rid of the Objective-C and OSX continuous changes.

My application bundle contains SDL2.framework (similarly SDL_ttf.framework and SDL_net.framework), as downloaded from libsdl.org, and I don’t experience any problems with it ‘not loading’ on macOS High Sierra (10.13.6). Maybe being a framework rather than a dylib makes a difference, or maybe it’s because it’s packaged as an app bundle.

The difference might be that you signed the .app while I did not.

My .app directory is provided as a simple .tgz tarball.
In the .app directory, there is one executable named storga_osx_20180828 and the libSDL2.dylib
OSX 10.6 will run it perfectly, but OSX 10.13 will reject it (see screenshot bellow).
Statically linking libSDL2 so that the .app directory contains the executable but not the .dylib will make OSX 10.13 accept to run it also.

27

I agree with rtrussell… I don’t think your explanations about why
things are failing factually correct and your problems are caused by
something else.

  1. If you don’t codesign you app and disable Gatekeeper, then the
    should app run, irrespective of whether you have dynamic libraries or
    not.

  2. If you codesign your app, you must also remember to codesign all
    the individual frameworks you bundle with the app. (Otherwise this
    will prevent the app from launching. Also note that all components
    must be signed with the same keys.)

I have done both of the above on High Sierra, and I know it works.

From your terminal screenshot, my suspicion is that you:
a) Did not (correctly) set the install/loader/rpath and install_name
paths correctly on your built dylib and also on your executable.
or
b) You didn’t copy the dylib to the correct subdirectory. The path
shown in the terminal does not appear to follow the official OS
conventions.
or
c) Some things are signed, but not everything, or you signed different
pieces with different keys.

1 Like

I have an additional thought about the problem of shipping a static
libSDL.a, which I think is worth posting in case somebody else asks in
the future.

I think this a problematic idea, especially for your use case where
you are not using the latest Xcode and macOS SDK. The advantage of
building/shipping dynamic libraries is that the environment is “sealed
in”. In particular, all the OS dependencies that SDL links against are
“burned into” the dynamic library. Apple has some dynamic linking
tricks that also note which library versions you linked against and
for what SDK you compiled against, and at both launchtime and runtime,
Apple can actually dynamically run special legacy paths if in the
future they knowingly break certain behaviors.

The problem with static libraries are they are not “sealed”. So when
you build, first, you will be responsible for figuring out all the OS
dependencies that SDL links to. That in itself is not necessarily a
problem (just more hoop jumping), but the real problem is that you are
not using the latest Xcode or macOS SDK. Because SDL needs to link to
newer APIs that are not in your old Mac OS X version, you are probably
going to get link failures.

Even if you did get past that problem, you have this problem where
your SDL is now technically built without knowledge of the latest OS
APIs and potential bug fixes in SDL to deal with any issues that may
have arisen in the newest macOS releases. If you are lucky, Apple will
make dynamic legacy runtime switches to avoid bugs they know about.
But I’ve seen my share of bugs where this doesn’t work or actually
creates new problems.

iOS is less of a problem because Apple pretty much requires you use
the latest Xcode for iOS development to ship anything on the store. So
a statically linked libSDL.a is not as much of an issue there.

1 Like

Thanks for your thoughts.
Here is a more specific explanation about what I’m talking about. The overall objective is to be able to compile C only code, so get rid of the continuous need to upgrade the system.

Let’s create and enter a fresh directory.
Move the SDL2 file you provide in SDL2-2.0.8.dmg there, and rename it as libSDL2.dylib.
It’s exact size is 2300304 bytes.

Let’s create test1.c with the following code:
#include <stdlib.h>
int main() {
printf(“started\n”);
printf(“done\n”);
return 0; }

Let’s create test2.c with the following code:
#include <stdlib.h>
#define SDL_INIT_VIDEO 0x00000020u
#define SDL_INIT_EVENTS 0x00004000u
int SDL_Init(unsigned int flags);
int main() {
printf(“started\n”);
SDL_Init(SDL_INIT_VIDEO+SDL_INIT_EVENTS);
printf(“done\n”);
return 0; }

And finally compile both of them:
gcc -w -m32 -O1 -DMAC_OS_X_VERSION_MIN_REQUIRED=1040 -o test1 test1.c
gcc -w -m32 -O1 -DMAC_OS_X_VERSION_MIN_REQUIRED=1040 -o test2 test2.c libSDL2.dylib
install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/libSDL2.dylib test2

Now, we can try to run ./test1 and ./test2
As you can see, none of them has been signed.
test1 is simulating what we get when staticly linking with SDL2.
test2 is simulating what we get when dynamically linking with SDL2.
On OSX 10.6, both test1 and test2 run just fine.
I had no access to an up to date OSX this week, so could not test, but I bet it will refuse to run test2, and that is why I have to go the test1 way whereas the test2 would have been even better.

My bet is that if I link on an old Xcode with a libSDL2.a you provide and that you have built with the latest Xcode, then everything will be fine because all the crazyness of OSX Objective-C is contained in libSDL2. The remaining of the code is plain C, and an old Xcode will handle it just fine unless the C ABI changed, or the linker core changed witch is not that likely.
The problem is if I try to build libSDL2.a myself as I did with a pre 2.0.5, because it needs dirty patching in many places, so the end result is probably not fine.

No, that generally isn’t going to work. What I was trying explain in
the previous message is that when you use a static library, you must
explicitly link all libSDLa’s dependencies to your app, which means
Cocoa, QuartzCore, CoreGraphics, etc, which you don’t have to do with
the dynamic library. If a newer prebuilt libSDL.a uses new classes
(symbols) that don’t exist on your older system, you will get
undefined symbol errors from the linker when you link to the older
version of Cocoa which is missing the new classes. When you use the
dynamic library, you don’t have to link to these explicitly and they
are “sealed in” to do the right thing. Apple’s weak linking system can
handle building on newer that works on older, but not the other way
around.

Your use of install_name_tool raises some questions which may be
related to your problems.
First, you are changing a framework using @rpath to use
@exectuable_path. Those are two different mechanisms that work
slightly differently. Second, by default, the string length for those
anchor paths is fixed, so if you change from a shorter string to a
longer one, it can fail or truncate or create garbage. I think you are
okay in this example, but you need to be careful. But third, it looks
like you are changing the install_name on your executable after you
built it, but didn’t change any of the install_name information in the
library itself. I don’t think that is correct and you will have
mismatched library and executable pairs now. It would not surprise me
if GateKeeper rejects corrupted binaries regardless of codesigning.

I recommend you not change this, and leave the @rpath mechanism as is.
There is another flag that you pass to your executable to say where to
look. (typically -rpath @executable_path/…/Frameworks). I also
recommend you stick to the defaults and put the libraries in the
correct bundle place. In fact, I recommend you just use the framework.

Also, many of those build flags you use I do not remember offhand. And
normally, don’t you need -l SDL to link to it? (Use -framework SDL for
the framework.)

Thanks for your advises.
As a result, I moved to not liking at all with libSDL2.dylib at compile time, and using dlopen/dlsym to link at run time. I just hope up to date OSX will not forbid the application to use dlopen.

Pliant language code generator is powerfull enough to transparently generate all the extra glue code required. If you don’t know what Pliant language is, just visit
https://www.fullpliant.org/

For other readers that would later discover this topic, Pliant is a free software language that runs on Linux, Windows,OSX, Android x86. It contains it"s own compiler and code generator, it’s own set of high level features such as database engine or HTTP server, a complete operating system named FullPliant, and it’s own high level graphic stack named Pliant UI (instead of using Qt or GTK).
Under OSX or Android, it’s using SDL2 as a C level framebuffer abstraction.

I personally see SDL2 as Posix2.
Posix defined C level standard interface to the operating system for batch applications.
SDL2 is providing C level standard interface to the operating system for interactive applications.
It’s design is great because it did not try include a high level 2D drawing toolkit, and that’s a fine decision because there cannot be a one fit all solution in this area, whereas for 3D, the hardware made OpenGL the de facto standard.
Wayland under SDL2 should even provide a free and clean implementation of what I call Posix2, because Wayland matches SDL2 concepts as opposed to X11.
Thanks for providing a so great piece of software.

I got access to an up to date Mac (2018). The result are the following:
Loading libSDL2.dylib using ‘dlopen’ does not work any more if libSDL2.dylib as been provided in a simple .tgz file. It used to work on old OSX.
As a result, Pliant free software is not usable anymore on OSX :frowning:

Now, the only solution I found to get an unsigned program run on up to date OSX has been to provide a single statically liked executable. So it brings back to my initial expectation to get libSDL2.a
I used the up to date Mac with a up to date XCode to compile SDL 2.0.8. No problem.
Then, in order to be able to use libSLD2.a on OSX 10.6 Xcode, I had to patch it in order to get around link time issues.
I attach the patch in case it would be useful for somebody else. No, in facts, your brand new forum does not allow me to do that ‘Sorry, new users can not upload attachments’.
Seems like bureaucracy is winning everywhere.

I don’t have that problem. My app is built in ancient Xcode 3.2 on equally ancient macOS 10.6 (Snow Leopard) and it installs and runs OK in macOS 10.13 (High Sierra) so long as the security warnings are bypassed. It’s not statically linked: SDL is included in the app bundle as Contents/Frameworks/SDL2.framework/Versions/Current/SDL2 (not as a .dylib).