If my static library is using AFNetworking and a client project is also using it, it seems like constants defined in AFNetworking will collide, causing the client project not to be able to build (duplicate symbol error). How are things like this resolved usually? What should framework creator do to avoid that?
Try the approach in this link:
http://blog.sigmapoint.pl/avoiding-dependency-collisions-in-ios-static-library-managed-by-cocoapods/
which he describes as:
Build library with prefixed symbols - in my opinion the best way. We
would like to add a prefix to all of the symbols in final .a file so
that e.g. AFHTTPSessionManager becomes
SIGMAPOINT_AFHTTPSessionManager. Your version of dependency is not
connected to version used by developer and no collisions occurs. The
only disadvantage is bigger output file because when someone uses the
same library the final file will contain the same symbols: once
prefixed and once not prefixed. But the whole build process will work
smooth and without errors. This is the solution we will investigate
further in this article.
This tool may help
https://github.com/cocoapods/cocoapods-packager
Related
I have an open source, Objective-C library that I maintain. It has been around for a long time, and I've always distributed it as an embeddable Xcode project that builds a framework, as well as through Carthage and Cocoapods. (The library in question is ORSSerialPort.)
I recently added support for installing it with Swift Package Manager (see this commit), by creating a Package.swift file. I was able to do so without making any source or structure changes, which was helpful because I need to continue to distribute it as a framework as well for the foreseeable future.
However, Xcode 12 included a new (or at least newly on by default?) warning when you do #import with double quotes in public headers in a framework. I had done that in a couple places, so switched to angle brackets as is correct for a framework (see this commit).
I've only now discovered that SwiftPM no longer builds the package because of that. It fails with the following error:
In file included from /Users/andrewmadsen/Developer/ORSSerialPort/Source/ORSSerialPort.m:28:
/Users/andrewmadsen/Developer/ORSSerialPort/Source/ORSSerialRequest.h:28:9: fatal error: 'ORSSerial/ORSSerialPacketDescriptor.h' file not found
#import <ORSSerial/ORSSerialPacketDescriptor.h>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
[0/1] Compiling ORSSerial ORSSerialPort.m
One possible solution (that does work, but is kinda gross), is to conditionalize any import with an #ifdef, then pass in a define in the cSettings argument when creating the target in Package.swift, but that seems kind of gross and unwieldy. In this project it's not bad, but in another project I have many, many more headers affected by this issue.
So, to the root of my question:
Is there a way to convince SPM to build an Objective-C package where the public headers use <> angle brackets to include other public headers in the package?
I've already tried specifying a header search path in cSettings, but this produces the same error:
.target(
name: "ORSSerial",
path: "Source",
exclude: ["ORSSerialBuffer.h"],
publicHeadersPath: ".",
cSettings: [
.headerSearchPath(".")
]
)
(All .h and .m files are in the same Source folder.)
I had the same issue recently. The only way I found to work around this was to change the includes to standard user-style includes
#import "ORSSerialPacketDescriptor.h"
Of course, this breaks the header for Objective-C clients. But since Swift uses the module map, you can still use it from Swift code.
I'm trying to use YGuard to obfuscate some parts of my program which contain encryption methods and other sensitive information (which I'll further protect in other ways once I figure this out).
Because the program is quite complex and contains quite many libraries it obviously gives a series of warning and finally fails with:
WARNING: Method initialize_ffi_type is native but com/sun/jna/Native is not kept/exposed.
WARNING: Method getAPIChecksum is native but com/sun/jna/Native is not kept/exposed.
[...]
yGuard was unable to resolve a class (java.lang.ClassNotFoundException: com.sun.tools.javac.parser.Parser$Factory)
Now whatever that means I'd like to
exclude libraries which being all open source have nothing to hide so far
obfuscate just the methods and variables of some Class or some package and leave the rest untouched.
So far in YGuard it seems I have to specify what I don't want to be obfuscated, however I have far too many classes, I'd like instead to do the opposite: Specify what I'd like to obfuscate and proceed increasing the number of Classes and packages I want obfuscated.
Thanks
It is the normal practice for obfuscators to specify what should be kept and not the other way around.
However, you can define library classpaths with the externalclasses rule (link). Classes that are defined in this path are neither obfuscated nor shrinked. The second error you are getting (ClassNotFoundException) indicates that you have not specified all libraries that your project depends on.
In order to obfuscate your code now, what you could do is:
Pack the code that you want to be obfuscated in one jar and define everything else as a library
use a patternset in your keep rule (link) to define everything to be kept except the classes that you want to have obfuscated.
Let's say I'm trying to use one and only one 3rd-party library in an xcode project - a pretty typical scenario, seemingly harmless.
I plug in the path location of my include files to the project's "header file search path" setting. I haven't even modified any code to make use of the code in the library yet.
It turns out that the project couldn't compile.
Xcode complained something like "Cannot find interface declaration for NSObject", which is pretty absurd. By examining the Build Result, the complained error comes from a header file of the 3rd-party library - it looks something like
So it is indicated in the Build Result that xcode is mistaken that Foundation.h is referring to the assert.h of that 3rd-party library instead of the iOS' built-in assert.h (4th sub-item)
Is there a way to fix the collision of the file names of #import include files?
(Needless to say, I'm new to obj-c -___-)
OK, I know there have been other posts about how you can't actually strip Objective-C symbols from an OS X binary because they're necessary for Obj-C to work at all, but my case is a bit different.
I have a single binary which is a bundle. It is intended to be used as either a VST plugin, or an AudioUnit plugin. The idea is that the binary contains all the entry points for both formats, and you just compile it once, and then name one copy with ".vst" for the VST version, and ".component" for the AU version. (This is using the JUCE framework BTW.)
The problem is that for the AU side, you must export an Obj-C class for creating the Cocoa UI view. On the VST side, this class will never be used. But if you have a host like Ableton Live which allows you to simultaneously load both AU and VST versions of the same plugin, now we run into the typical Obj-C namespace collision issue.
On the VST side, that particular Obj-C class will never get used. So what I'd like to do is to strip those Obj-C classes from the resulting binary using "strip". This still maintains the advantage of just compiling everything once for both formats.
Anyway, I've tried using "strip -R stripfile.txt <path to binary>", where stripfile.txt contains the symbols I want to strip, but it always fails saying that the symbols can't be found in the binary. I've tried mangling the names in the strip file, but that doesn't help (or I'm doing it wrong).
Here are the relevant symbols that I want to strip, as output by "nm -m":
000000000003bb00 (__TEXT,__text) non-external -[JuceDemoProjectAU description]
000000000003bb60 (__TEXT,__text) non-external -[JuceDemoProjectAU interfaceVersion]
000000000003ba00 (__TEXT,__text) non-external -[JuceDemoProjectAU uiViewForAudioUnit:withSize:]
0000000000b02398 (__DATA,__objc_data) external _OBJC_CLASS_$_JuceDemoProjectAU
0000000000b023c0 (__DATA,__objc_data) external _OBJC_METACLASS_$_JuceDemoProjectAU
Any ideas?
BTW, I have subsequently been able to dynamically register the class in question (using a unique name), which also solves the problem. However, if I could get strip working, I could potentially deploy a solution for already existing binaries in the field.
You can not just simply strip a class from a binary. What you can do however is to trick the Objective-C runtime into believing your plugin does not contain any Objective-C code. Just change __objc_imageinfo into __objc_imageinfX for example in your VST plugin binary. You can do it easily with perl:
perl -pi -e 's/__objc_imageinfo/__objc_imageinfX/g' <path to binary>
After patching the VST plugin, all the Objective-C initialization will be bypassed and you won’t see this error message: Class JuceDemoProjectAU is implemented in both …/VSTPlugin and …/AUPlugin. One of the two will be used. Which one is undefined.
Beware, you should really not use this trick! The appropriate solution to your problem is either to compile two different version of your plugin or to register classes dynamically as others suggested.
There was a thread about something similar to this on the coreaudio-list last year: Collision between Cocoa classes for AU and VST plugins.
The solution offered was to register the classes dynamically which is what you say you already have working. If there was a way to strip the symbols like you wanted, I'm sure these guys would have known about it.
I have created a static library containing all my generic classes. Some of these classes use frameworks.
Now I have two projects, one that uses some classes that use frameworks, and one that doesn't use any of the classes that use frameworks.
Because Static Libraries don't support including frameworks (if I am correct). I have to include the frameworks in the project that uses them. But when I compile the project that doesn't use any of the framework-classes the compiler breaks because it still requires the frameworks. Now I know it tries to compile all the (unused) classes from the library because I use the Linker Flag '-ObjC' to prevent 'unrecognized selector' errors.
Does anyone know how to compile only the required source files per project? And prevent from all frameworks having to be included in all projects that use my static library?
First of all, you are right in that a static library cannot include any framework nor other static libraries, it is just the collection of all object files (*.obj) that make up that specific static library.
Does anyone know how to compile only the required source files per project?
The linker will by default only link in object files from the static library that contain symbols referenced by the application. So, if you have two files a.m and b.m in your static library and you only use symbols from a.m in your main program, then b.o (the object file generated from b.c) will not appear in your final executable. As a sub-case, if b.m uses a function/class c which is only declared (not implemented), then you will not get any linker errors. As soon as you include some symbols from b.m in your program, b.o will also be linked and you will get linker errors due to the missing implementation of c.
If you want this kind of selection to happen at symbol rather than at object level granularity, enable dead code stripping in Xcode. This corresponds to the gcc option -Wl,-dead_strip (= linker option -dead_strip in the Build settings Info pane for your project). This would ensure further optimization.
In your case, though, as you correctly say, it is the use of the "-ObjC" linker flag that defeats this mechanism. So this actually depends on you. If you remove the -Objc flag, you get the behavior you like for free, while losing the stricter check on selectors.
And prevent from all frameworks having to be included in all projects that use my static library?
Xcode/GCC support an linking option which is called "weak linking", which allows to lazily load a framework or static library, i.e., only when one of its symbols is actually used.
"weak linking" can be enabled either through a linker flag (see Apple doc above), or through Xcode UI (Target -> Info -> General -> Linked Libraries).
Anyhow, the framework or library must be available in all cases at compile/link time: the "weak" option only affects the moment when the framework is first loaded at runtime. Thus, I don't think this is useful for you, since you would need anyway to include the framework in all of your projects, which is what you do not want.
As a side note, weak_linking is an option that mostly make sense when using features only available on newer SDK version (say, 4.3.2) while also supporting deployment on older SDK versions (say, 3.1.3). In this case, you rely on the fact that the newer SDK frameworks will be actually available on the newer deployment devices, and you conditionally compile in the features requiring it, so that on older devices they will not be required (and will not produce thus the attempt at loading the newer version of the framework and the crash).
To make things worse, GCC does not support a feature known as "auto-linking" with Microsoft compilers, which allow to specify which library to link by means of a #pragma comment in your source file. This could offer a workaround, but is not there.
So, I am really sorry to have to say that you should use a different approach that could equally satisfy your needs:
remove the -ObjC flag;
split your static library in two or more parts according to their dependencies from external frameworks;
resort to including the source files directly.
Abour second part of your question, you can mark a linked framework as Optional :
About first part, it is not clear to me what you intend to do:
A library being declared in a project
A project declaring which files are compiled (via Target > Build phases > Compile sources)
Unless setting complex build rules to include or not files, which if I remember well can be done using .xcconfig files, I don't see any other solutions than splitting your Library. Which I would recommend, for its ease. You should even do several targets in the same project... You could also just use precompiler MACROS (#ifdef...) but that depends on what you want to do.
It sounds like you have library bloat. To keep things small I think you need to refactor your library into separate libraries with minimal dependencies. You could try turning on "Dead Code Stripping" in the "Linker Flags" section of the build target info (Xcode 3.x) to see if that does what you want (doesn't require frameworks used by classes that are dead-stripped.)
When you link against a framework on iOS I don't think that really adds any bloat since the framework is on the device and not in your application. But your library is still a bit bloated by having entire classes that never get used but are not stripped out of the library.
A static library is built before your app is compiled, and then the whole thing is linked into your app. There's no way to include some parts of the library but not others -- you get the whole enchilada.
Since you have the source code for the library, why not just add the code directly to each application? That way you can control exactly what goes into each app. You can still keep your generic classes together in the same location, and use the same code in both apps, but you avoid the hassle of using a library.