Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking 2 static libs into 1 for iOS

Tags:

xcode

ios

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?

like image 565
Alexander van Elsas Avatar asked Mar 02 '12 09:03

Alexander van Elsas


2 Answers

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.

like image 115
lawicko Avatar answered Oct 05 '22 05:10

lawicko


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}
    
like image 32
xverges Avatar answered Oct 05 '22 06:10

xverges