6

I have created two separate static libraries in Xcode for usage on iOS, A and B. A uses methods that are defined in B.

When creating a new Xcode project where both A and B are needed, I can include them separately. However, for simplicity of integration I prefer to create a Universal Framework that contains both A and B.

Is it possible in Xcode to merge 2 static libraries into 1, without merging the code of the 2 libraries in 1 project. In other words. Can I somehow link the compiled static library B into static library A when I am compiling/Linking static library A?

If this is possible, how can I accomplish this?

  • Relates to http://stackoverflow.com/questions/8170450/combine-static-libraries – Bruce Jan 22 '14 at 06:37
  • Also related to http://stackoverflow.com/questions/19442593/including-third-party-librariesexample-afnetworking-in-static-library – xverges Jan 29 '14 at 18:33

2 Answers2

5

I've just run some quick tests and it seems to be happening automatically. This is what I did:

  • I used DerivedData folder as a default build location for all 3 projects (this is default in XCode 4.2 which I use)
  • I changed Public Headers Folder Path to include/ProjectX where X is the name of the static library. I did this step only for static libraries A and B, not for the project that will actually link them. This step is to be able to import the headers like <LibX/Header.h>.
  • I made library B direct dependency of the library A and linked A against B
  • I made library A direct dependency of the main project and linked main project against A

After this basic setup, I imported classes from B to A with something similar to <LibB/Header.h> and wrote some code that actually made use of B. Then, I imported A and B to the main project with <LibA/Header.h> and <LibB/Header.h> and wrote code that made use of A and B. Finally, I went to the DerivedData folder with the terminal and navigated to the location where A is built. I checked if LibA.a contains objects from LibB with:

nm LibA.a

And yes, it contains objects from LibB. So, to sum up, with this simple dependency setup you should be able to get what you asked for.

EDIT To make B direct dependency of A and link A against B do this:

Having A open in the XCode, go to Finder and drag&drop B project file into A. Then, choose the root element in A, go to Build Phases, expand Target Dependencies, press '+' button, choose B and confirm. then expand Link Binary With Libraries, press '+' button, choose B.a (or whatever the product name is) and confirm.

Important There is a bug in XCode that prevents you from properly dropping B project file into A workspace. Instead, you are left with B project file in A workspace, but you cannot expand B and do anything with it. To work around this problem, remove the faulty B project file reference from A, close A, close B if you have it open. Then reopen A and use Finder to navigate to B project file, then drag&drop B inside A workspace. If didn't work, repeat.

EDIT2 I case you don't have access to the sources of B (and possibly A), making this work is just a matter of copying the required headers in the proper place. Then in you main project, you don't make the A direct dependency, instead you link against static libA.a that you have. If A uses B, then the symbols from B are already in the libA.a. You can check this with nm tool like I do above. So we are down to exposing those symbols to the main application with B headers. There are a couple of ways to do it, I remember that I simply copied the headers to the Copy Headers destination path of the library that is in the middle of the dependency chain. After that, by linking against A and adding A headers to User Header Search Paths I was able to access B directly. What is the best way to do it for you depends on if you have access to the sources of A. If you have, there are two options to consider:

  • Add B headers to A (they will be automatically copied to the A headers destination). But I guess you don't want this solution.
  • Add custom run script build phase to A target, which will grab the B headers and copy them to A headers destination.

In both cases, you end up with LibA.a which holds compiled sources of A and B, and the headers folder which holds headers from A and B. You can then link your main project agains LibA.a and add the headers folder path to the User Headers Search Path in you main project, and you should be good to go.

Important

If, in your libraries, you have files that hold only category code in them, make sure to link this library with -force_load, or your category symbols will not be packed properly.

lawicko
  • 7,246
  • 3
  • 37
  • 49
  • Lawicko, thanks for your response. A question I am left with is how you create the dependency when you write "I made library B direct dependency of the library A and linked A against B" ? – Alexander van Elsas Mar 02 '12 at 14:12
  • Lawicko, it works after I closed all projects :-) I'm however, not entirely satisfied with the solution, because when I drag and drop the B project file into the A project, the source code for B is exposed to project A (I can expand project B on the left and see all the project code there. I am not sure if that can be solved somehow (I'd rather just drag and drop static lib B into the project and force Xcode to merge B into static lib A when I compile A. I'll accept the answer though as I've learned something new about xcode thanks to you :-) – Alexander van Elsas Mar 02 '12 at 15:15
  • Yeah, this drag&drop part is a pain on the back side. I'm glad you made it! – lawicko Mar 02 '12 at 15:19
  • The fact that you can see lib B when browsing A does not mean that the files from B are now part of A. They are not. This direct dependency trick only tells XCode to recompile B before compiling A. However, I know what you are referring to. What If you don't have access to the sources of B? Then you just want to link to the static libB.a and add headers. I managed to do it in the past with the Reachability library, give me some time and I'll let you know how to do this. – lawicko Mar 02 '12 at 15:31
  • See my edited answer for the case when you don't have access to the library sources. – lawicko Mar 02 '12 at 16:43
3

I've done it with libtool, as per this answer.

To do so, in Xcode 5.0.2, I've added a script to A (Editor -> Add Build Phase -> Add Run Script Build Phase) that

  • figures out the architecture for which A is being built:

    LIPO_ARCH=$(lipo -info ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME} | awk 'END{ print $NF }')
    
  • creates a thin version of B, with only the architecture being built

    lipo -thin ${LIPO_ARCH} ${FULLPATH_OF_B} -output ${FULLPATH_OF_THIN_B}
    
  • joins A and B into a new A

    mv ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME} ${FULLPATH_OF_THIN_A}
    libtool -static -o ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME} ${FULLPATH_OF_THIN_A} ${FULLPATH_OF_THIN_B}
    
  • removes the temp files

    rm ${FULLPATH_OF_THIN_A}
    rm ${FULLPATH_OF_THIN_B}
    
Community
  • 1
  • 1
xverges
  • 4,608
  • 1
  • 39
  • 60