Cross-compile to static lib (libgcrypt) for use on iOS
Asked Answered
G

2

7

I have downloaded latest libgcrypt & libgpg-error libraries from https://www.gnupg.org/download/index.html. I have successfully built (command line) both libraries using ./configure --enable-static --disable-shared; make ; make install on my Mac (Mavericks w/OSX 10.10 & latest Xcode 6.1).

I can link just fine to these new libs from an OS X client app I am building. So far, so good. Just perfect. BUT, I also need to build an iOS client using same exact source code.

Questions:

1) What are the modifications to the command line build sequence for the library I would need to build a universal static library for the (simulator, Mac & iOS)? 2) Or do I need to build separate static libs for iOS? And if so, again, what command line magic would I need to accomplish getting the target architecture right?

Genitals answered 7/11, 2014 at 23:58 Comment(0)
T
14

Note that it is not possible to build a universal library that will work for both the iOS Simulator and macOS. iOS/Intel and macOS/Intel are not ABI compatible above the C runtime library (Libc). This answer serves to show you how to crosscompile autoconf based projects for iOS targets, and you can easily lipo the resulting static archives together.

You will want to do something like this:

#!/bin/bash -e -x

OPT_FLAGS="-Os -g3"
MAKE_JOBS=16

dobuild() {
    export CC="$(xcrun -find -sdk ${SDK} cc)"
    export CXX="$(xcrun -find -sdk ${SDK} cxx)"
    export CPP="$(xcrun -find -sdk ${SDK} cpp)"
    export CFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
    export CXXFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
    export LDFLAGS="${HOST_FLAGS}"

    ./configure --host=${CHOST} --prefix=${PREFIX} --enable-static --disable-shared

    make clean
    make -j${MAKE_JOBS}
    make install
}

SDK="iphoneos"
ARCH_FLAGS="-arch armv7"
HOST_FLAGS="${ARCH_FLAGS} -miphoneos-version-min=8.0 -isysroot $(xcrun -sdk ${SDK} --show-sdk-path)"
CHOST="arm-apple-darwin"
PREFIX="${HOME}/DEVICE_ARM"
dobuild

SDK="iphoneos"
ARCH_FLAGS="-arch arm64"
HOST_FLAGS="${ARCH_FLAGS} -miphoneos-version-min=8.0 -isysroot $(xcrun -sdk ${SDK} --show-sdk-path)"
CHOST="arm-apple-darwin"
PREFIX="${HOME}/DEVICE_ARM64"
dobuild

SDK="iphonesimulator"
ARCH_FLAGS="-arch i386"
HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=8.0 -isysroot $(xcrun -sdk ${SDK} --show-sdk-path)"
CHOST="i386-apple-darwin"
PREFIX="${HOME}/SIM_i386"
dobuild

SDK="iphonesimulator"
ARCH_FLAGS="-arch x86_64"
HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=8.0 -isysroot $(xcrun -sdk ${SDK} --show-sdk-path)"
CHOST="x86_64-apple-darwin"
PREFIX="${HOME}/SIM_x86_64"
dobuild

I just threw that script together and verified it works (with the addition of --disable-libpng and skipping tests) for pixman. You will probably need to customize it for libgcrypt, but it serves to show the general pattern for building autoconf/automake/glibtool based projects for iOS.

After building, you'll have content in ~/{DEVICE_ARM{,64},SIM_{i386,x86_64}} and you can either lipo the static libraries together or just use all of them in your project (the linker will emit warnings about missing slices for the "other" archives which you can ignore).

lipo -create -output lib.a DEVICE_ARM/lib/lib.a DEVICE_ARM64/lib/lib.a SIM_i386/lib/lib.a SIM_x86_64/lib/lib.a
Trichloromethane answered 8/11, 2014 at 0:56 Comment(4)
Can you be explicit since I am completely unfamiliar with the nuance of configure types of builds. I did notice that config.log is producing multiple #error Unsupported architecture.Genitals
Ok, I've updated the answer to be even more explicit. I wrote a script and tested it on pixman. You'll need to adapt it for other autoconf/automake/glibtool projects, but you get the general idea.Trichloromethane
Note that with the release of Apple Silicon Macs, it is no longer possible to make a single library that will work for both device and sim because we cannot include both sim/arm64 and device/arm64 slices in the same fat binary. Xcode introduced XCFrameworks as a way to solve this type of issue.Trichloromethane
For some reason when I do this, all of my outputs are the same architecture (x86_64). Any idea why?Protuberate
H
2

Jeremy gave a good answer, but I'd like to add a few things and offer my two cents.

The updated script

#!/bin/bash

OPT_FLAGS="-O3 -g3"
MAKE_JOBS=8

dobuild() {
    export CC
    CC=$(xcrun --find --sdk "${SDK}" gcc)
    export CXX
    CXX=$(xcrun --find --sdk "${SDK}" g++)
    export CPP
    CPP=$(xcrun --find --sdk "${SDK}" cpp)
    export CFLAGS
    CFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
    export CXXFLAGS
    CXXFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
    export LDFLAGS
    LDFLAGS="${HOST_FLAGS}"

    ./configure --host="${CHOST}" --prefix="${PREFIX}" --enable-static

    make clean
    make -j"${MAKE_JOBS}"
    make install
}

SDK="iphoneos"
ARCH_FLAGS="-arch armv7 -arch armv7s -arch arm64"
HOST_FLAGS="${ARCH_FLAGS} -miphoneos-version-min=8.0 -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
CHOST="arm-apple-darwin"
PREFIX="${HOME}/DEVICE_ARM"
dobuild

SDK="iphonesimulator"
ARCH_FLAGS="-arch x86_64"
HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=8.0 -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
CHOST="x86_64-apple-darwin"
PREFIX="${HOME}/SIM_x86"
dobuild

The iOS toolchain is separated by SDKs and iphonesimulator has a separate SDK as does macos, tvos, and of course iphoneos. For each SDK you'll need one compile. You can lipo together iphonesimulator and iphoneos output into a single library since they contain different architectures, but really they are not compiled with the same SDK. I recommend against the super binary of mixed SDKs.

Why am I doing this?

If you're compiling a library and need this, it is because the library is using autoconf, or in the case of Boost and OpenSSL other custom build systems. The keys to compiling for the SDKs are the correct clang, -sysroot, -miphoneos-ver-min, and -arch flags. If you get the -arch or -sysroot flags wrong you'll see #error Unsupported architecture errors.

Remove the --disable-shared flag

I like leaving the generation of the shared libs even if I intend to use the static library. This generally means you'll compile with Position Independent Code (-fPIC) so if you decide to include this library in a shared library, you're good to go. Also, unlike a static library which isn't linked, but archived, the linking of the shared library often exposes missing objects.

Multiple architectures on the line

You can pass multiple -arch flags on the line and you'll get FAT binaries. This save you some hassle and time during compilation.

$ lipo -info libwhatever.a 
Architectures in the fat file: libwhatver.a are: i386 x86_64 

Remove architectures

In all honesty, do you need i386 support? If you're not targeting that device then don't include the architecture. You only need i386 if your host system runs an OS predating Lion.

Hulburt answered 29/4, 2018 at 6:43 Comment(4)
Unless you are certain the library you are building supports building fat, I would urge you to build separate slices and lipo them together. Many autoconf-based projects check things like sizeof(long) during configure and save those to macros used during the build. This can result in incorrectly compiled code in some cases.Trichloromethane
Yes, I've dealt with this problem. It is easy to forget that people like writing host tests, so in that case you'd need to recompile for each architecture independently. I have written some Shell scripts to compile dependencies exactly like this. Although, you might not want to continue worrying about armv7.Hulburt
For some reason when I do this, all of my outputs are the same architecture (x86_64). Any idea why?Protuberate
If you look at each invocation of the compiler you should see -arch XXXX, one for each architecture. If you do not, then that is your problem.Hulburt

© 2022 - 2024 — McMap. All rights reserved.