[cmake-developers] Linking Apple frameworks

Eric Wing ewmailing at gmail.com
Mon Aug 7 17:27:17 EDT 2017


On 8/7/17, Brad King <brad.king at kitware.com> wrote:
> On 08/05/2017 07:58 PM, Craig Scott wrote:
>>     target_link_libraries(foo PRIVATE "-framework AppKit")
>>
>> Strangely, with the library quoted as above, the embedded space
>> is not escaped, leading to the (desirable but surprising) result
>
> Link flags starting in `-` are treated as a raw part of the command
> line.  That's how it evolved historically.
>
>> As extra context, a correct final linker command line can also be achieved
>> like this:
>>
>>     find_library(APPKIT_LIB AppKit)
>>     target_link_libraries(foo PRIVATE ${APPKIT_LIB})
>>
>> In this case, while APPKIT_LIB contains a full absolute path to the
>> framework, CMake manages to recognise it as a framework and shortens
>> the linker flags to just -framework AppKit.
>
> There is special case logic for this, but IMO it was a mistake.
> Ideally when find_library finds a framework library it should
> return the absolute path to the library file within the framework,
> e.g. `/path/to/foo.framework/foo`.  Then no special logic would
> be needed to recognize it and the library file can be passed
> directly on link lines.  All the Modules/Find*.cmake modules
> that provide imported targets have special logic to transform
> the find_library result in this way.  Perhaps we should fix this
> with a policy.
>
> Also the points raised in Eric Wing's response are valid concerns.
>
> -Brad

I think that would be a mistake because it seems that the only purpose
of this change would be so you could bypass CMake and try to directly
invoke some kind of command line invocation on the dynamic library
inside the .framework bundle. This seems to encourage the wrong kind
of behavior and there is almost no real world use case in the native
Apple world where you should ever do that (I can contrive a couple in
my head for iOS but they are esoteric cases.). In general, you should
be treating the framework bundle as a whole because all parts of it
are designed to be useful. The bundle assets like any .nib files and
the Info.plist are sometimes critical components of the framework. So
things like copying the whole framework and embedding them in the app
bundle are important things to do. And I mentioned the current problem
of codesigning an app bundle. But right now, getting the path to the
entire framework is more useful so you can at least manually do these
things with CMake. Changing that would only complicate this process in
that now you have to play guessing games on what the framework’s
internal layout looks like. (Mac and iOS differ, and there are
esoteric corner cases if you have to deal with a framework that ships
multiple versions.) (And you can imagine this kind of change will
break a ton of my scripts that currently do all this missing steps.)


But if you did decide to change this, I think it should only happen in
conjunction of solving the rest of the needed functionality for
dealing with frameworks, i.e. copying the entire framework bundle into
the app bundle, codesigning the framework in the app bundle, and using
the built in Xcode functionality for this with the Xcode generator.
Also making this a little easier to automatically setup in CMake would
be welcome, and could include things like setting the @rpath on the
frameworks and install_name on the app as part of the process (since
these things always go together with bundling the framework). And
possibly some mechanism that can handle create fat binaries for
deploying an iOS framework but including stripping the simulator
architectures for a release app.


A slight tangent, but kind of related example of the problem is
Android’s new AAR library bundle format. AAR is still horribly broken
and incomplete when it comes to the NDK side (and by looking at the
current rate of progress may never get fixed), but does solve some
serious problems for Android libraries in general so I’m starting to
find they are unfortunately the best solution despite the PITA they
are. AARs share some concepts of the .framework in that they are
container packages that contain a lot of different important
components beyond just the dynamic library stub your native code needs
to link to. Two of the most important features is that it can merge
the R.java resource system and the AndroidManifest.xml that the
library declares it needs to use into your final app. (The way Android
works, all this stuff must be put directly into your app and libraries
don’t officially get their own notion of these things. This makes
integrating/using any library a PITA on Android, so AAR finally starts
automating this process.) But of course the consequence is that once
again, we have an alien library format that CMake doesn’t know how to
deal with yet. I think CMake can eventually be taught, but it requires
that users not be making too many direct assumptions about the build
and linking process, and especially direct invocations of flags.

(And another slight tangent…I’m still working on the Swift/CMake
support. I have the basics for apps working on both Makefiles and
Ninja, but the next step is I need to do a massive overhaul for module
support which I fear is going to require non-trivial internal changes
to CMake. I haven’t worked out all the details yet, but I think it is
likely I will have to deal with frameworks and I suspect I’m going to
have to deal with more than just the dynamic library inside of it.)


Thanks,
Eric


More information about the cmake-developers mailing list