Tutorial: Open Source on iOS (Part 2): Compiling libogg on iOS

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.


About idz

A professional software engineer dabbling in iOS app development.
This entry was posted in Tutorial. Bookmark the permalink.

13 Responses to Tutorial: Open Source on iOS (Part 2): Compiling libogg on iOS

  1. dalexsoto says:

    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

    • idz says:

      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

  2. dalexsoto says:

    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

    • idz says:

      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

      • maliotti says:

        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

        • idz says:

          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

          • maliotti says:

            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

          • idz says:

            Hi Michael,
            Looks like the missing symbols are assembly optimized routines. Trying reconfiguring using ‘–disable-asm-optimizations’.
            Hope this works for you,
            idz

          • maliotti says:

            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

  3. dalexsoto says:

    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

  4. favormm says:

    why must install the three tools in the tutorial 1. No use in the tutorial 2, only use the gcc or g++.

  5. Pingback: Google Speech Recognition on iOS | DEVCHAPEL

Leave a Reply