VCX is part of Hyperledger’s Indy and Aries projects. It’s a library for exchange of verifiable credentials written in Rust. It has also c-callable API with wrappers for iOS, Java, Node and other languages.
Our team needed to build the iOS static library from scratch and I decided to write down some notes from the process. The description of the build process is not only a good help for those who need the same, but it’s also good for understanding the build process itself which can be useful for any other library.
I won’t go into details describing all commands, I just want to provide a high-level description. If you want to see specifics, go to this repo jakubkoci/indy-vcx-build with the final Bash script or look at these two links which were my inspiration (thank you to all authors for their contribution):
- indy-sdk/README.txt at master · hyperledger/indy-sdk · GitHub
- indy-sdk-android/android.build.sh at master · faisal00813/indy-sdk-android · GitHub
Prerequisities
You need to run a build script on Mac OS with Rust and some other libraries installed. You will find everything described in the setup.sh script.
Goal
At the end of the process, we want to have a vcx.framework
folder containing a binary file compiled for all required architectures together. This binary file will be combined from libvcx
library and all its dependencies libindy
, libssl
, libcrypto
, libsodium
and libzmq
.
Architectures
Whenever you want to build a native library, you need to select the architecture a.k.a type of processor of a device where your app, and therefore library, will be running. There are usually following options for iOS:
-
arm64
(aarch64-apple-ios
): 64-bit ARM processor in iPhone 5s, iPad Air and newer. -
armv7s
(armv7s-apple-ios
): 32-bit ARM processor in iPhone 5. -
armv7
(armv7-apple-ios
): 32-bit ARM processor in iPhone 3GS, 4, 4S. -
x86_64
(x86_64-apple-ios
): 64-bit simulator. -
i386
(i386-apple-ios
): 32-bit simulator.
In my case, OpenSSL library supports only two architectures, arm64
for devices and x86_64
for a simulator, so I need to build libraries only for these two.
You can find more about architectures here:
- Blake's iOS Device Specification Grid
- About ARM architectures for iOS development – Stanley SIU’s Blog
- CPU Architectures
Build Native Libraries
For every 3rd party library I checkout particular repo and run a build script within it:
OpenSSL
- clone https://github.com/x2on/OpenSSL-for-iPhone
- run
./build-libssl.sh
which produces bothlibssl.a
andlibcrypto.a
Libsodium
- clone https://github.com/evernym/libsodium-ios
- run
./libsodium.rb
which produceslibsodium.a
ZMQ
- clone https://github.com/evernym/libzmq-ios
- run
./libzmq.rb
-> which produceslibzmq.a
Libindy
I take similar steps with libindy
, but on top of cloning the repo, I also checkout the required version by tag:
$ git clone git clone https://github.com/hyperledger/indy-sdk
$ cd indy-sdk/libindy
$ git checkout $INDY_VERSION
$ cargo lipo —release —targets="aarch64-apple-ios,x86_64-apple-ios”
You can spot that instead of architectures arm64
or x86_64
I use aarch64-apple-ios, x86_64-apple-ios
. I call them triplets. Some libraries use the first architecture format, some use triplets, but you can see the mapping between architectures and triplets in the Architectures section above.
You can find more about architectures here:
- Blake's iOS Device Specification Grid
- About ARM architectures for iOS development – Stanley SIU’s Blog
- CPU Architectures
Lipo
Lipo is a command-line tool that allows us to create universal binary with more architectures in one file (called Universal Static Library, or just “fat file”). We can also use it for extracting one universal file into more files according to given architecture (“non-fat file”).
If you check the output library with lipo
you will see whether it’s a fat-file or not, and what architectures it contains.
For example, in our case:
$ lipo -info output/libsodium-ios/dist/ios/lib/libsodium.a
Architectures in the fat file: output/libsodium-ios/dist/ios/lib/libsodium.a are: armv7 armv7s i386 x86_64 arm64
or, for libindy
:
$ lipo -info output/indy-sdk/libindy/target/aarch64-apple-ios/release/libindy.a
Non-fat file: output/indy-sdk/libindy/target/aarch64-apple-ios/release/libindy.a is architecture: arm64
If you run this command for every library you’ll see that all 3rd party libraries are fat files with all architectures, but libindy
has been built as non-fat files. We need to extract architectures from fat-files to non-fat files:
$ lipo -extract ${arch} ../$FILE_PATH -o ${arch}/$FILE_NAME-fat.a
$ lipo ${arch}/$FILE_NAME-fat.a -thin $arch -output ${arch}/$FILE_NAME.a
You may be confused why there are two steps (extracting — keep output as a fat file with given architecture only, and thinning — create a non-fat file with given architecture). That’s because you need to extract architecture before you thin the file. If you try to thin the file directly, you will get the following error:
fatal error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: input file (arm64/libzmq.a) must be a fat file when the -thin option is specified
Libvcx
I also copied libindy
from indy-sdk
to a different directory. Extracting and copying helps us set environment variables which are required to build libvcx
in the following way:
export OPENSSL_LIB_DIR=$WORK_DIR/libs/openssl/${ARCH}
export IOS_SODIUM_LIB=$WORK_DIR/libs/sodium/${ARCH}
export IOS_ZMQ_LIB=$WORK_DIR/libs/zmq/${ARCH}
export LIBINDY_DIR=$WORK_DIR/libs/indy/${ARCH}
cargo build —target “${TRIPLET}” --release --no-default-features —features “ci”
Combine It All
Now, we have all libraries libssl
, libcrypto
, libsodium
, libzmq
, libindy
and libvcx
separated by architecture. What we want is one file with all libraries for all architectures. This part is too code heavy to describe it here. In general, we first combine all libraries for given platform and then create a fat file with all architectures with lipo
. I prepared a simple drawing describing it:
This still not over. To call output libvcxall.a
library from our code we need to wrap it with Objective-C and c-callable headers. First, we copy the libvcxall.a
to indy-sdk/vcx/wrappers/ios/vcx
which contains these headers and then we use xcodebuild
and lipo
utility to create our final vcx.framework
.
Yes, we’re finally done. I hope you find it helpful even if you don’t need to build the library by yourself, but were curious about the process.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.