Search code examples
iosxcodeprotocol-buffersautotoolsbitcode

How to enable Xcode-specific build flags for autotool-based projects


I'm trying to build Google protobuf project for iOS, but it's unclear how to enable iOS-specific flags such as -fembed-bitcode and BITCODE_GENERATION_MODE=bitcode with autotool. I've tried with the following script:

#!/bin/bash

## Environments
ScriptDir="$( cd "$( dirname "$0" )" && pwd )"
cd - &> /dev/null

# Exit the build pass if any command returns a non-zero value
#set -o errexit


# Echo commands
set -x

# 13.4.0 - Mavericks
# 14.0.0 - Yosemite
# 15.0.0 - El Capitan
# 16.0.0 - Sierra
DARWIN=darwin`uname -r`

MIN_SDK_VERSION=8.0

IPHONEOS_SYSROOT=`xcrun --sdk iphoneos --show-sdk-path`
IPHONESIMULATOR_SYSROOT=`xcrun --sdk iphonesimulator --show-sdk-path`

# Verbose clang output
#CLANG_VERBOSE="--verbose"

CC=clang
CXX=clang

SILENCED_WARNINGS="-Wno-unused-local-typedef -Wno-unused-function"

# NOTE: Google Protobuf does not currently build if you specify 'libstdc++'
# instead of `libc++` here.
STDLIB=libc++

CFLAGS="${CLANG_VERBOSE} ${SILENCED_WARNINGS} -DNDEBUG -g -O0 -pipe -fPIC -fcxx-exceptions"
CXXFLAGS="${CFLAGS} -std=c++11 -stdlib=${STDLIB}"

LDFLAGS="-stdlib=${STDLIB}"
LIBS="-lc++ -lc++abi"

PROTOC=`which protoc`

PROTOBUF_SOURCE_DIR="$ScriptDir"

PREFIX="$ScriptDir"/_build
if [ -d ${PREFIX} ]
then
    rm -rf "${PREFIX}"
fi
mkdir -p "${PREFIX}/platform" &> /dev/null

## Functions

build-arch() {
  HOST=$1
  ARCH=$2
  PLATFORM_CFLAGS=$3
  PLATFORM_NAME=${ARCH}
  SYSROOT=${IPHONEOS_SYSROOT}

  make distclean
  ./configure \
    --build=x86_64-apple-${DARWIN} \
    --host=${HOST} \
    --with-protoc=${PROTOC} \
    --disable-shared \
    --prefix=${PREFIX} \
    --exec-prefix=${PREFIX}/platform/${PLATFORM_NAME} \
    "CC=${CC}" \
    "CFLAGS=${CFLAGS} -miphoneos-version-min=${MIN_SDK_VERSION} -arch ${ARCH} -isysroot ${SYSROOT} ${PLATFORM_CFLAGS}" \
    "CXX=${CXX}" \
    "CXXFLAGS=${CXXFLAGS} -miphoneos-version-min=${MIN_SDK_VERSION} -arch ${ARCH} -isysroot ${SYSROOT}" \
    LDFLAGS="-arch ${ARCH} -miphoneos-version-min=${MIN_SDK_VERSION} ${LDFLAGS}" \
    "LIBS=${LIBS}"

  make -j8
  make install
}

build-i386-simulator() {
  ARCH=i386
  HOST=${ARCH}-apple-${DARWIN}
  PLATFORM_NAME=${ARCH}-simulator
  SYSROOT=${IPHONESIMULATOR_SYSROOT}

  make distclean
  ./configure \
    --build=x86_64-apple-${DARWIN} \
    --host=${HOST} \
    --with-protoc=${PROTOC} \
    --disable-shared \
    --prefix=${PREFIX} \
    --exec-prefix=${PREFIX}/platform/${PLATFORM_NAME} \
    "CC=${CC}" \
    "CFLAGS=${CFLAGS} -mios-simulator-version-min=${MIN_SDK_VERSION} -arch ${ARCH} -isysroot ${SYSROOT}" \
    "CXX=${CXX}" \
    "CXXFLAGS=${CXXFLAGS} -mios-simulator-version-min=${MIN_SDK_VERSION} -arch ${ARCH} -isysroot ${SYSROOT}" \
    LDFLAGS="-arch ${ARCH} -mios-simulator-version-min=${MIN_SDK_VERSION} ${LDFLAGS}" \
    "LIBS=${LIBS}"

  make -j8
  make install
}

build-x86_64-simulator() {
  ARCH=x86_64
  PLATFORM_NAME=${ARCH}-simulator
  SYSROOT=${IPHONESIMULATOR_SYSROOT}

  make distclean
  ./configure --prefix=${PREFIX} \
              --exec-prefix=${PREFIX}/platform/${PLATFORM_NAME} \
              --with-sysroot=${SYSROOT} \
              --with-protoc=`which protoc` \
              --enable-static \
              --disable-shared

  make -j8
  make install
}

build-fat-lib() {
  OUT=${PREFIX}/universal
  mkdir -p ${OUT}

  PLATFORM_ROOT=${PREFIX}/platform
  LIPO=lipo

  LIB=libprotobuf.a
  ${LIPO} ${PLATFORM_ROOT}/arm64/lib/${LIB} \
          ${PLATFORM_ROOT}/armv7/lib/${LIB} \
          ${PLATFORM_ROOT}/x86_64-simulator/lib/${LIB} \
          ${PLATFORM_ROOT}/i386-simulator/lib/${LIB} \
          -create \
          -output ${OUT}/${LIB}

  LIB_LITE=libprotobuf-lite.a
  ${LIPO} ${PLATFORM_ROOT}/arm64/lib/${LIB_LITE} \
          ${PLATFORM_ROOT}/armv7/lib/${LIB_LITE} \
          ${PLATFORM_ROOT}/x86_64-simulator/lib/${LIB_LITE} \
          ${PLATFORM_ROOT}/i386-simulator/lib/${LIB_LITE} \
          -create \
          -output ${OUT}/${LIB_LITE}
}

## Build pass

cd ${PROTOBUF_SOURCE_DIR}

./autogen.sh

build-x86_64-simulator
build-i386-simulator

build-arch arm arm64 "BITCODE_GENERATION_MODE=bitcode -fembed-bitcode"
build-arch armv7-apple-${DARWIN} armv7 -fembed-bitcode

build-fat-lib

echo DONE!

In the above script, I tried to pass my intended options through CFLAGS, which didn't work: The build of protobuf libs via this script passes, but linking against the output Universal libs with another Xcode project fails. So clearly the bitcode wasn't included, i.e., the flags didn't work.

I wonder if it's impossible to achieve what I do with autotool alone. Should I create a real Xcode project for this purpose?


Solution

  • The Autotools provide a framework for building and maintaining build systems. They do provide some standardization, but to a large extent, the capabilities of an Autotools-based build system are project specific.

    But with that said, your script is not passing anything to configure via CFLAGS, CC, or any of the rest. These are environment variables recognized by configure, but you are attempting to convey them as arguments, instead. That's the best way to do it if you want to inject those arguments at the make step, but it does not by default work at all for configure. Shell syntax requires variable assignments to precede the command name, so, for example:

    "CC=${CC}" \
    "CFLAGS=${CFLAGS} -miphoneos-version-min=${MIN_SDK_VERSION} -arch ${ARCH} -isysroot ${SYSROOT} ${PLATFORM_CFLAGS}" \
    "CXX=${CXX}" \
    "CXXFLAGS=${CXXFLAGS} -miphoneos-version-min=${MIN_SDK_VERSION} -arch ${ARCH} -isysroot ${SYSROOT}" \
    LDFLAGS="-arch ${ARCH} -miphoneos-version-min=${MIN_SDK_VERSION} ${LDFLAGS}" \
    "LIBS=${LIBS}" \
    ./configure \
      --build=x86_64-apple-${DARWIN} \
      --host=${HOST} \
      --with-protoc=${PROTOC} \
      --disable-shared \
      --prefix=${PREFIX} \
      --exec-prefix=${PREFIX}/platform/${PLATFORM_NAME}
    

    Or you could just set and export those variables via one or more separate commands preceding configure.


    Also, as an aside, make distclean doesn't do what you want when there is no makefile, which is the case in an Autotools project that has not yet been configured. You ought to protect against failures there by making execution of that command conditional on the existence of the top-level makefile.