In this tutorial you will learn how to compile open source libraries that use the GNU Build System (GBS) for iOS. I’ll be using the open source libogg
library as an example, but the techniques you use should be useful for any source that uses GBS. Make sure you complete the previous tutorial “Open Source on iOS (Part 1): Gathering Your Tools” before attempting this one; it won’t work otherwise!
Preliminaries
The scripts in this tutorial will use the xcode-select
command to locate the active Xcode
installation. This is part of the Command Line Tools package. This package is not installed by default when installing from the App Store. To see if this is installed on your system, start Xcode and choose Xcode > Preferences…. Click the Downloads icon in the dialog. If Command Line Tools is not installed, click the install button.
To test that xcode-select
is correctly installed and is pointing to your preferred Xcode
installation type:
xcode-select -print-path
you should see something like:
/Applications/Xcode-4.5.app/Contents/Developer
Your output may be slightly different. I modify the name of my Xcode
directory to include the version number to allow multiple versions to co-exist.
If the output of xcode-select
does not point to your preferred Xcode
installation you can change it using something like:
xcode-select -switch /path/to/your/Xcode
Cross-Compiling with configure
Cross-compiling refers to compiling a binary on one system for use on another. In your case this refers to compiling on a Mac for either the iOS simulator or an iOS device.
With the GNU Build System, a successful cross-compile depends on the value of a few environment variables and some command line options to the configure
script. In most cases, setting the following environment variables:
CC
the path to the C compiler,CXX
the path to the C++ compiler,CFLAGS
the flags to pass to the C compiler,CXXFLAGS
the flags to pass to the C++ compiler,LDFLAGS
the flags to pass to the linker
and the following command line arguments:
--host
the system the binaries will run on,--prefix
the installation location,--disable-shared
only build static libraries
is sufficient to obtain working static libraries.
The Environment Variable Values
All of the flags (CFLAGS, CXXFLAGS, LDFLAGS
) variables must include -isysroot
and -arch
command line arguments. The -isysroot
command line argument specifies the SDK being used for compilation, while the -arch
flag specifies the architecture; the arch flag should be one of: i386 (for the simulator), armv6, armv7 or armv7s.
These paths are rather long and tedious to type, so I will describe how they are related in script terms (and from that you will derive a script).
If we assume:
IDZ_XCODE_ROOT=`xcode-select -print-path`
that is, the value of IDZ_XCODE_ROOT
is the result of executing xcode-select
, then the path to given platform directory is:
IDZ_PLATFORM_PATH=$IDZ_XCODE_ROOT/Platforms/$IDZ_PLATFORM.platform/Developer
where IDZ_PLATFORM
is one of: iPhoneSimulator
or iPhoneOS
.
The above result can be used to determine the platform binary (bin
) directory needed for CC
and CXX
.
IDZ_PLATFORM_BIN_PATH=$IDZ_PLATFORM_PATH/usr/bin
and the IDZ_PLATFORM_PATH
the SDK path (needed for -sysroot
) can be determined as follows:
IDZ_SDK_PATH=$IDZ_PLATFORM_PATH/SDKs/$IDZ_PLATFORM$IDZ_SDK_VERSION.sdk
where IDZ_SDK_VERSION
is, for example, 5.1 or 6.0.
With these variables in hand, the required environment variables for the configure
script can be derived as follows:
# Cross-compile environment variables IDZ_FLAGS="-isysroot $IDZ_SDK_PATH -arch $IDZ_ARCH" CC=$IDZ_PLATFORM_BIN_PATH/llvm-gcc CXX=$IDZ_PLATFORM_BIN_PATH/g++ CFLAGS=$IDZ_FLAGS CXXFLAGS=$IDZ_FLAGS LDFLAGS=$IDZ_FLAGS export CC CXX CFLAGS CXXFLAGS LDFLAGS
provided the values of IDZ_ARCH
, IDZ_PLATFORM
and IDZ_SDK_VERSION
are known.
The Command Line arguments
The --host
command line argument is either i386-apple-darwin10
for the simulator or arm-apple-darwin10
for a device. Notice that these values depend only on the value of IDZ_ARCH
.
The --prefix
command line argument is used to determine where the project will be installed. To ensure that distinct architecture have their own directories I will use install-$IDZ_PLATFORM-$IDZ_ARCH
.
Putting It All Together
Putting all the above together and adding a little bit of error checking we obtain the following script. I have not covered all the syntax used in this script. I will come back and add more explanation when I have time.
Using your favorite text editor, copy and paste the following script into $IDZ_BUILD_ROOT/bin/idz_configure
:
#!/bin/bash IDZ_SAVE_DIR=`pwd` IDZ_SCRIPT_NAME=`basename $0` # Print usage information and exit idz_usage() { cd $IDZ_SAVE_DIR echo "Usage: $IDZ_SCRIPT_NAME arch sdk_version configure_path" echo " Where arch is one of armv6, armv7, armv7s" exit 1 } # Print error message and exit idz_error() { cd $IDZ_SAVE_DIR echo $1 exit 1 } # Test if the last command failed and call idz_error if appropriate idz_check_error() { if [ $? -ne 0 ] ; then idz_error $1 fi } # Print usage if too few arguments if [ $# -lt 3 ] ; then idz_usage fi # Transfer arguments to more meaningful names IDZ_ARCH=$1 IDZ_SDK_VERSION=$2 IDZ_CONFIGURE=$3 # IDZ_PLATFORM and IDZ_HOST only depend on IDZ_ARCH case $IDZ_ARCH in i386 ) IDZ_PLATFORM=iPhoneSimulator IDZ_HOST=i386-apple-darwin10 ;; armv6 | armv7 | armv7s ) IDZ_PLATFORM=iPhoneOS IDZ_HOST=arm-apple-darwin10 ;; * ) echo "Unrecognised architecture $IDZ_ARCH" idz_usage ;; esac # Convert a possible relative path to configure to an absolute path case $IDZ_CONFIGURE in /* ) ;; * ) IDZ_CONFIGURE=`pwd`/$IDZ_CONFIGURE ;; esac # Derive the environment variables IDZ_XCODE_ROOT=`xcode-select -print-path` IDZ_PLATFORM_PATH=$IDZ_XCODE_ROOT/Platforms/$IDZ_PLATFORM.platform/Developer IDZ_SDK_PATH=$IDZ_PLATFORM_PATH/SDKs/$IDZ_PLATFORM$IDZ_SDK_VERSION.sdk IDZ_FLAGS="-isysroot $IDZ_SDK_PATH -arch $IDZ_ARCH" IDZ_PLATFORM_BIN_PATH=$IDZ_PLATFORM_PATH/usr/bin CC=$IDZ_PLATFORM_BIN_PATH/llvm-gcc CXX=$IDZ_PLATFORM_BIN_PATH/g++ CFLAGS=$IDZ_FLAGS CXXFLAGS=$IDZ_FLAGS LDFLAGS=$IDZ_FLAGS export CC CXX CFLAGS CXXFLAGS LDFLAGS # Derive the command line arguments IDZ_PREFIX=$IDZ_SAVE_DIR/install-$IDZ_PLATFORM-$IDZ_ARCH IDZ_CONFIGURE_FLAGS="--host=$IDZ_HOST \ --prefix=$IDZ_PREFIX \ --disable-shared $IDZ_EXTRA_CONFIGURE_FLAGS" IDZ_BUILD=$IDZ_SAVE_DIR/build-$IDZ_PLATFORM-$IDZ_ARCH mkdir -p $IDZ_BUILD cd $IDZ_BUILD IDZ_SAVE_DIR=$IDZ_BUILD $IDZ_CONFIGURE $IDZ_CONFIGURE_FLAGS | tee configure_$IDZ_ARCH.log 2>&1 make clean | tee make_clean_$IDZ_ARCH.log 2>&1 idz_check_error "Make clean failed." make | tee make_$IDZ_ARCH.log 2>&1 idz_check_error "Build failed." make install | tee make_install_$IDZ_ARCH.log 2>&1 idz_check_error "Install failed." cd $IDZ_SAVE_DIR
Remember to make it executable using:
chmod u+x $IDZ_BUILD_ROOT/bin/idz_configure
Downloading the Source Code
Many open source projects provide stable releases available via download and “cutting edge” releases via source code control. The stable release of libogg, 1.3.0, available via download was not possible to adapt to cross-compilation so for this tutorial you will use the source control version.
Make sure you are in the idz_build
directory:
pushd $IDZ_BUILD_ROOT
Make a directory for libogg
mkdir -p libogg/1.3.0 pushd libogg/1.3.0
Check out the code from source code control
svn co http://svn.xiph.org/tags/ogg/libogg-1.3.0
The download distribution normally has the configure
script pre-made. In this case you will need to create it.
pushd libogg-1.3.0 ./autogen.sh
The result of this process is a configure
script that has been run for a Mac. This will interfere with your iOS build. To remove this configuration information issue the following command:
make distclean
You will also need to make a correction to the configuration script. Open the configure
script in a text editor and locate the section near line 11669:
*-*-darwin*) DEBUG="-fno-common -g -Wall -fsigned-char" CFLAGS="-fno-common -O4 -Wall -fsigned-char -ffast-math" PROFILE="-fno-common -O4 -Wall -pg -g -fsigned-char -ffast-math" ;;
Change the -O4
to -O3
to make it look like this:
*-*-darwin*) DEBUG="-fno-common -g -Wall -fsigned-char" CFLAGS="-fno-common -O3 -Wall -fsigned-char -ffast-math" PROFILE="-fno-common -O3 -Wall -pg -g -fsigned-char -ffast-math" ;;
For some reason gcc
seems to generate invalid arm
object files when optimization level 4 is used. Changing the optimization level to 3 seems to solve this problem.
You should now pop back to parent directory:
popd
Using The idz_configure
Script
This script will only work if you are in the correct directory. So as a precaution execute the command:
cd $IDZ_BUILD_ROOT/libogg/1.3.0
To attempt to build the library issue the following command (if you are not using the 6.0 SDK you should change this):
idz_configure armv7 6.0 libogg-1.3.0/configure
It will fail!
But why did it fail? Checking the error messages we see:
libtool: link: /Applications/Xcode-4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/llvm-gcc -D_V_SELFTEST -fno-common -O4 -Wall -fsigned-char -ffast-math -isysroot /Applications/Xcode-4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk -arch armv7 -isysroot /Applications/Xcode-4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk -arch armv7 -o test_bitwise test_bitwise-bitwise.o ld: warning: ignoring file test_bitwise-bitwise.o, file was built for unsupported file format ( 0x42 0x43 0xc0 0xde 0x21 0x c 0x 0 0x 0 0xf4 0x18 0x 0 0x 0 0x 1 0x10 0x 0 0x 0 ) which is not the architecture being linked (armv7): test_bitwise-bitwise.o Undefined symbols for architecture armv7: "_main", referenced from: start in crt1.o ld: symbol(s) not found for architecture armv7
There is a lot of noise in this message but basically what it means is that, in the course of the build, it tried to run some executable that was not compiled for the build system’s architecture. Of course, since this was a cross-compile it should never have tried to run anything so where was the problem?
It turns out the problem is in the libogg-1.3.0/src/Makefile.am.
If you change the ‘noinst_PROGRAMS’ to ‘check_PROGRAMS’ this will solve the problem. What this change does is that it specifies that some programs were test or check programs and could only be run on the host system.
After making this change you must:
pushd libogg-1.3.0 ./autogen.sh make distclean popd
before running
idz_configure armv7 6.0 libogg-1.3.0/configure
this time the build should complete.
The previous step created the static library for use on a device (running a pre-iOS 6 OS, for iOS 6 and later use armv7s
). To create a version for the simulator you need to use:
idz_configure i386 6.0 libogg-1.3.0/configure
In the next tutorial you will learn how to combine multiple static libraries into a pseudo-framework.
Hello, Thanks a lot for this excellent series of tutorials , I have successfully compiled libogg without any issues, now im trying to compile libflac but I had no success using the same method described above, could you please help me if possible.
You can find the libflac source I’m using here -> http://sourceforge.net/projects/flac/files/flac-src/flac-1.2.1-src/
the error im getting is
/bin/sh: line 0: cd: /Users/dalex/idz_builds/libflac/1.2.1/flac/doc/html/api: No such file or directory
make[4]: *** [install-data-local] Error 1
make[3]: *** [install-am] Error 2
make[2]: *** [install-recursive] Error 1
make[1]: *** [install-recursive] Error 1
make: *** [install-recursive] Error 1
Thanks in advance for any help
P.S. im really new to GNU build system
I have not compiled libflac so I can only guess. It looks like the issue is related to documentation. The documentation is built with a program called doxygen. You can try two things:
1) Install doxygen and try again
2) Use configure –help to determine if you can skip building the documentation (there often is such an option).
If neither of these work you will just have to dig though the logs to find the source of the error. In each build directory you will find the a set of logs that capture all the steps.
Sorry I can’t be of more help,
idz
Yes there is an option to disable oxygen
–disable-doxygen-docs
using your script is there any way to pass that arg??
something like
idz_configure armv7 6.0 flac/configure –disable-doxygen-docs
Sorry, I’m really new to shell script need to read further
Alex
Hi Alex,
The script includes the contents of the environment variable ‘IDZ_EXTRA_CONFIGURE_FLAGS’ on the configure command line.
(It might be nice to support the way you suggest, but I am a bit lazy with my scripts and trade elegance for speed!)
There is an example of this in part four of the tutorial (compiling libvorbis).
Basically what you would do is:
export IDZ_EXTRA_CONFIGURE_FLAGS=–disable-doxygen-docs
idz_configure armv7 6.0 flac/configure
It’s hard to see here but there is two dashes before disable.
Hope this works for you!
— idz
Hi, I have followed this great tutorial and was able to successfully compile libogg. I am now trying to compile libflac and am running into the exact same error as dalexsoto. I have tried installing Doxygen and I have also tried the suggestion that worked for dalexsoto:
export IDZ_EXTRA_CONFIGURE_FLAGS=–disable-doxygen-docs
idz_configure armv7 6.1 libflac-1.2.1/configure
Unfortunately, I am still getting the same error:
/bin/sh: line 0: cd: api: No such file or directory
make[4]: *** [install-data-local] Error 1
make[3]: *** [install-am] Error 2
make[2]: *** [install-recursive] Error 1
make[1]: *** [install-recursive] Error 1
make: *** [install-recursive] Error 1
I have been banging my head on this for days now and I’m just not sure what else to try. Any help would be much appreciated!
Thanks,
Michael
Hi Michael,
Welcome to the site. The problem your are seeing seems to be cause by the build system not copying across some of the needed files into the builds. You can work around this by just manually copying them in. For example, for the i386 build you would do:
cd build-iPhoneSimulator-i386/doc/html
cp -r ../../../flac-1.2.1/doc/html/api .
You may need to adjust the paths to match your directory organization.
Hope this helps,
idz
Thanks. I really like this site a lot!
Your suggestion worked. I had to copy the “api” directory into both the “build” and “install” directories. I also had to set the –with-ogg flag and provide the path to my libogg.a file.
While I am now able to compile libflac for armv7 and armv7s, when I try to compile for i386, I get the following error:
Undefined symbols for architecture i386:
“_FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_decoder.o)
“_FLAC__cpu_have_cpuid_asm_ia32”, referenced from:
_FLAC__cpu_info in libFLAC.a(cpu.o)
“_FLAC__cpu_info_asm_ia32”, referenced from:
_FLAC__cpu_info in libFLAC.a(cpu.o)
“_FLAC__cpu_info_extended_amd_asm_ia32”, referenced from:
_FLAC__cpu_info in libFLAC.a(cpu.o)
“_FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_compute_autocorrelation_asm_ia32”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_encoder.o)
“_FLAC__lpc_restore_signal_asm_ia32”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_decoder.o)
“_FLAC__lpc_restore_signal_asm_ia32_mmx”, referenced from:
_init_stream_internal_ in libFLAC.a(stream_decoder.o)
“_precompute_partition_info_sums_32bit_asm_ia32_”, referenced from:
_find_best_partition_order_ in libFLAC.a(stream_encoder.o)
ld: symbol(s) not found for architecture i386
collect2: ld returned 1 exit status
make[2]: *** [flac] Error 1
make[1]: *** [install-recursive] Error 1
make: *** [install-recursive] Error 1
Not exactly sure what I am missing? Any thoughts?
Thanks again for all your help,
Michael
Hi Michael,
Looks like the missing symbols are assembly optimized routines. Trying reconfiguring using ‘–disable-asm-optimizations’.
Hope this works for you,
idz
Hi, just wanted to say your suggestion to –disable-asm-optimizations worked perfectly! Thank you!
I have now compiled for i386, armv7 and armv7s. I’ve combined them into one FAT file so I can use one binary for the simulator and for my devices.
Thanks again,
Michael
Success thanks a lot, you put me on the right direction i needed more flags, now I know a little more about configure and how it works and understand a little bit more about console, again thanks so much
Alex
why must install the three tools in the tutorial 1. No use in the tutorial 2, only use the gcc or g++.
The tools installed in the first tutorial are used indirectly by autogen.sh.
Pingback: Google Speech Recognition on iOS | DEVCHAPEL