I want to create a static library (actually, a framework, but I know how to do that part) that bundles up code from, among other things, another static library. However, the OBJC_CLASS
exports from the original library end up as undefined symbols.
For example, in Xcode 5.1.1 (using default settings/choices at every step, unless otherwise specified):
- Create a new "iOS Framework & Library Cocoa Touch Static Library" project named LibA.
- Build (for either simulator or a real device, doesn't matter).
- Create another new "iOS Framework & Library Cocoa Touch Static Library" project named LibB.
- Drag
libLibA.a
from the LibA products to the Frameworks folder in the LibB project tree. - Drag
LibA
from theinclude
directory next to the static lib to the top level of the LibB project tree. - Edit
LibB.h
as shown below. - Build (same target as before).
- Drag
- Create a new "iOS Application" (any type) project named AppC.
- Drag
libLibB.a
from the LibB products to the Frameworks folder in the AppC project tree. - Drag
LibB
from theinclude
directory to the top level. - Drag
LibA
from the first project'sinclude
directory to the top level. - Verify that
LibA
appears in the Link Binary With Libraries phase. - In any method of any class the wizard generated (e.g.,
-[MasterViewController awakeFromNib]
), add(void)[[LibB alloc] init]
. - At the top of the
.m
file you just edited, add#import "LibB.h"
. - Build.
- Drag
Here's the LibB.h
promised above:
#import <Foundation/Foundation.h>
#import "LibA.h"
@interface LibB: LibA
@end
I get the following error:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_LibA", referenced from:
_OBJC_CLASS_$_LibB in libLibB.a(LibB.o)
"_OBJC_METACLASS_$_LibA", referenced from:
_OBJC_METACLASS_$_LibB in libLibB.a(LibB.o)
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Looking at the files, the problem is obvious:
$ nm -g libLibB.a
U _OBJC_CLASS_$_LibA
0000031c S _OBJC_CLASS_$_LibB
U _OBJC_METACLASS_$_LibA
00000308 S _OBJC_METACLASS_$_LibB
U _OBJC_METACLASS_$_NSObject
U __objc_empty_cache
The symbols for _OBJC_CLASS_$_LibA
and _OBJC_METACLASS_$_LibA
are exported as undefined.
I can reference methods, C functions and structs, globals, etc. from LibA. Even categories on Foundation objects (as long as I do the category-dummy trick). It's only the class and metaclass objects that I can't figure out how to export.
Here's what I've tried to fix it:
- Turn off Dead Code Stripping (in all three projects).
- Add
-ObjC
as an extra linker flag (in all projects). (This makes no sense for static libs, and all it does is give you a warning error telling you exactly that, but everyone suggests it to me.) - Create an "Exported Symbols File" (for
LibB
). (This also only makes sense for dynamic libs.) - Pass
${PROJECT_DIR}/libLibA.a
as an "Other Linker Flags" (forLibB
) instead of addinglibLibA
as a framework (in case-lLibA
is processed differently fromlibLibA.a
).
What I've tried that I still think may be on the right path, but I'm not sure:
- Try to figure out appropriate
libtool
options that have no corresponding settings in Xcode. (I can wrap it in a Makefile, or and Xcode custom build step, if necessary.) - Enable "Perform Single-Object Prelink", then add
${PROJECT_DIR}/libLibA.a
to "Prelink libraries". I get warnings about duplicate symbols and then success but with an emptylibLibB.a
, so obviously there's something else I need to do. I've done this with .dylibs and dynamic Frameworks on OS X, and there wasn't anything else I needed to do there… but never with static libs.
Workarounds that I know about (and I'll use one of these if there's no real solution):
- Require that anyone who wants to use
LibB
also has to addLibA
to their project. And, in particular, the pre-built copy ofLibA
that we provide. - Distribute
LibB
as source to be included in your project, instead of a static lib and headers. - Manually
ar
libLibA.a
andLibB.o
, thenranlib
like it's 1999 (although the docs say this doesn't work, it seems to).
(None of these are too terrible for my simple test project, but in real life, this is not an open source project, that LibA is actually 80 different libs from 3 different projects, and some of the LibA code builds fat armv7/armv7s (which means ar
doesn't work on it…), and we're planning to do the usual hack of lipo'ing together the simulator and native builds and making a framework out of them, all of which makes things more of a problem.
Adding this to
Other Linker Flags
appears to workI think I may have solved it with single-object prelink (basically this means it does an
ld -r
to build a giant object file, then passes that tolibtool
), although I'm still not sure, and I don't love the solution. So, I will post what I've got as an answer, but hope someone else comes along with a better answer.To get single-object prelink to work, you need to (in
LibB
):libLibA.a
as a Framework.${PROJECT_DIR}/libLibA.a
(The second step is what I was doing wrong earlier…)
Unfortunately, this seems to break the dependency rules completely, so that every build recompiles every .m (and .pch) that's part of the target, even if nothing has changed.
Other than that annoyance, this seems to work for both
AppC
and my real project just fine.AppC
does not need "Preserve Private External Symbols"; my real project does. I believe this is because one of the third-party libraries does anld -r
with an empty-exported_symbols_list
explicitly to "transform all symbols toprivate_extern
. Otherwise, class objects don't end up that way. However, I'm not 100% sure I understand this one.