Skip to content

Instantly share code, notes, and snippets.

@BennettSmith
Last active October 15, 2022 18:41
Show Gist options
  • Star 95 You must be signed in to star a gist
  • Fork 45 You must be signed in to fork a gist
  • Save BennettSmith/7150245 to your computer and use it in GitHub Desktop.
Save BennettSmith/7150245 to your computer and use it in GitHub Desktop.
Script used to build Google Protobuf 2.5.0 for use with Xcode 5 / iOS 7. Builds all supported architectures and produces a universal binary static library.

Google Protobuf 2.5.0 - Mac OS X and iOS Support

The script in this gist will help you buid the Google Protobuf library for use with Mac OS X and iOS. Other methods (such as homebrew or direct compilation) have issues that prevent their use. The libraries built by this script are universal and support all iOS device architectures including the simluator.

Get the Script

The easiest way to use this script is to simply clone the gist onto your machine using the following command:

$ git clone https://gist.github.com/7150245.git build-protobuf

Performing the Build

The script will automatically download the tarball from Google Code, so all you need to do is run the script.

$ cd build-protobuf
$ ./build-protobuf-2.5.0.sh

Results

Build results are found in a folder called protobuf. This folder contains bin, include and lib folders. In addition to these three folders the script also installs the Python language bindings for the Mac OS X build of protobuf. These files are installed in the user's home directory. On my machine the folder is:

${HOME}/Library/Python/2.7/lib/python/site-packages

Integration with Xcode

Create a build rule in your Xcode project with the following settings.

Process *Source files with names matching:* `*.proto`
Using *Custom script:*

cd ${INPUT_FILE_DIR}
${SRCROOT}/Google/protobuf/bin/protoc --proto_path=${INPUT_FILE_DIR} ${INPUT_FILE_PATH} --cpp_out=${DERIVED_FILE_DIR}

Output Files
$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).pb.cc
$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).pb.h

Depending on where you choose to install the protobuf build, you will need to adjust the path to protoc.

#!/bin/bash
echo "$(tput setaf 2)"
echo Building Google Protobuf for Mac OS X / iOS.
echo Use 'tail -f build.log' to monitor progress.
echo "$(tput sgr0)"
# Controls which architectures are build/included in the
# universal binaries and libraries this script produces.
# Set each to '1' to include, '0' to exclude.
BUILD_X86_64_MAC=1
BUILD_I386_IOS_SIM=1
BUILD_ARMV7_IPHONE=1
BUILD_ARMV7S_IPHONE=1
BUILD_ARM64_IPHONE=1
# Set this to the replacement name for the 'google' namespace.
# This is being done to avoid a conflict with the private
# framework build of Google Protobuf that Apple ships with their
# OpenGL ES framework.
GOOGLE_NAMESPACE=google_public
# Set this to the minimum iOS SDK version you wish to support.
IOS_MIN_SDK=6.1
(
PREFIX=`pwd`/protobuf
mkdir -p ${PREFIX}/platform
EXTRA_MAKE_FLAGS="-j4"
XCODEDIR=`xcode-select --print-path`
OSX_SDK=$(xcodebuild -showsdks | grep macosx | sort | head -n 1 | awk '{print $NF}')
IOS_SDK=$(xcodebuild -showsdks | grep iphoneos | sort | head -n 1 | awk '{print $NF}')
SIM_SDK=$(xcodebuild -showsdks | grep iphonesimulator | sort | head -n 1 | awk '{print $NF}')
MACOSX_PLATFORM=${XCODEDIR}/Platforms/MacOSX.platform
MACOSX_SYSROOT=${MACOSX_PLATFORM}/Developer/${OSX_SDK}.sdk
IPHONEOS_PLATFORM=${XCODEDIR}/Platforms/iPhoneOS.platform
IPHONEOS_SYSROOT=${IPHONEOS_PLATFORM}/Developer/SDKs/${IOS_SDK}.sdk
IPHONESIMULATOR_PLATFORM=${XCODEDIR}/Platforms/iPhoneSimulator.platform
IPHONESIMULATOR_SYSROOT=${IPHONESIMULATOR_PLATFORM}/Developer/SDKs/${SIM_SDK}.sdk
CC=clang
CFLAGS="-DNDEBUG -g -O0 -pipe -fPIC -fcxx-exceptions"
CXX=clang
CXXFLAGS="${CFLAGS} -std=c++11 -stdlib=libc++"
LDFLAGS="-stdlib=libc++"
LIBS="-lc++ -lc++abi"
echo "$(tput setaf 2)"
echo "####################################"
echo " Cleanup any earlier build attempts"
echo "####################################"
echo "$(tput sgr0)"
(
cd /tmp
if [ -d ${PREFIX} ]
then
rm -rf ${PREFIX}
fi
mkdir ${PREFIX}
mkdir ${PREFIX}/platform
)
echo "$(tput setaf 2)"
echo "##########################################"
echo " Fetch Google Protobuf 2.5.0 from source."
echo "##########################################"
echo "$(tput sgr0)"
(
cd /tmp
curl http://protobuf.googlecode.com/files/protobuf-2.5.0.tar.gz --output /tmp/protobuf-2.5.0.tar.gz
if [ -d /tmp/protobuf-2.5.0 ]
then
rm -rf /tmp/protobuf-2.5.0
fi
tar xvf /tmp/protobuf-2.5.0.tar.gz
)
echo "$(tput setaf 2)"
echo "###############################################################"
echo " Replace 'namespace google' with 'namespace google_public'"
echo " in all source/header files. This is to address a"
echo " namespace collision issue when building for recent"
echo " versions of iOS. Apple is using the protobuf library"
echo " internally, and embeds it as a private framework."
echo "###############################################################"
echo "$(tput sgr0)"
(
cd /tmp/protobuf-2.5.0/src/google/protobuf
sed -i '' -e "s/namespace\ google /namespace\ ${GOOGLE_NAMESPACE} /g" $(find . -name \*.h -type f)
sed -i '' -e "s/namespace\ google /namespace\ ${GOOGLE_NAMESPACE} /g" $(find . -name \*.cc -type f)
sed -i '' -e "s/namespace\ google /namespace\ ${GOOGLE_NAMESPACE} /g" $(find . -name \*.proto -type f)
sed -i '' -e "s/google::protobuf/${GOOGLE_NAMESPACE}::protobuf/g" $(find . -name \*.h -type f)
sed -i '' -e "s/google::protobuf/${GOOGLE_NAMESPACE}::protobuf/g" $(find . -name \*.cc -type f)
sed -i '' -e "s/google::protobuf/${GOOGLE_NAMESPACE}::protobuf/g" $(find . -name \*.proto -type f)
)
if [ $BUILD_X86_64_MAC -eq 1 ]
then
echo "$(tput setaf 2)"
echo "#####################"
echo " x86_64 for Mac OS X"
echo " and python bindings"
echo "#####################"
echo "$(tput sgr0)"
(
cd /tmp/protobuf-2.5.0
make ${EXTRA_MAKE_FLAGS} distclean
./configure --disable-shared --prefix=${PREFIX} --exec-prefix=${PREFIX}/platform/x86_64 "CC=${CC}" "CFLAGS=${CFLAGS} -arch x86_64" "CXX=${CXX}" "CXXFLAGS=${CXXFLAGS} -arch x86_64" "LDFLAGS=${LDFLAGS}" "LIBS=${LIBS}"
make ${EXTRA_MAKE_FLAGS}
make ${EXTRA_MAKE_FLAGS} test
make ${EXTRA_MAKE_FLAGS} install
cd python
python setup.py build
python setup.py install --user
)
X86_64_MAC_PROTOBUF=x86_64/lib/libprotobuf.a
X86_64_MAC_PROTOBUF_LITE=x86_64/lib/libprotobuf-lite.a
else
X86_64_MAC_PROTOBUF=
X86_64_MAC_PROTOBUF_LITE=
fi
if [ $BUILD_I386_IOS_SIM -eq 1 ]
then
echo "$(tput setaf 2)"
echo "###########################"
echo " i386 for iPhone Simulator"
echo "###########################"
echo "$(tput sgr0)"
(
cd /tmp/protobuf-2.5.0
make ${EXTRA_MAKE_FLAGS} distclean
./configure --build=x86_64-apple-darwin13.0.0 --host=i386-apple-darwin13.0.0 --with-protoc=${PREFIX}/platform/x86_64/bin/protoc --disable-shared --prefix=${PREFIX} --exec-prefix=${PREFIX}/platform/i386 "CC=${CC}" "CFLAGS=${CFLAGS} -miphoneos-version-min=${IOS_MIN_SDK} -arch i386 -isysroot ${IPHONESIMULATOR_SYSROOT}" "CXX=${CXX}" "CXXFLAGS=${CXXFLAGS} -arch i386 -isysroot ${IPHONESIMULATOR_SYSROOT}" LDFLAGS="-arch i386 -miphoneos-version-min=${IOS_MIN_SDK} ${LDFLAGS}" "LIBS=${LIBS}"
make ${EXTRA_MAKE_FLAGS}
make ${EXTRA_MAKE_FLAGS} install
)
I386_IOS_SIM_PROTOBUF=i386/lib/libprotobuf.a
I386_IOS_SIM_PROTOBUF_LITE=i386/lib/libprotobuf-lite.a
else
I386_IOS_SIM_PROTOBUF=
I386_IOS_SIM_PROTOBUF_LITE=
fi
if [ $BUILD_ARMV7_IPHONE -eq 1 ]
then
echo "$(tput setaf 2)"
echo "##################"
echo " armv7 for iPhone"
echo "##################"
echo "$(tput sgr0)"
(
cd /tmp/protobuf-2.5.0
make ${EXTRA_MAKE_FLAGS} distclean
./configure --build=x86_64-apple-darwin13.0.0 --host=armv7-apple-darwin13.0.0 --with-protoc=${PREFIX}/platform/x86_64/bin/protoc --disable-shared --prefix=${PREFIX} --exec-prefix=${PREFIX}/platform/armv7 "CC=${CC}" "CFLAGS=${CFLAGS} -miphoneos-version-min=${IOS_MIN_SDK} -arch armv7 -isysroot ${IPHONEOS_SYSROOT}" "CXX=${CXX}" "CXXFLAGS=${CXXFLAGS} -arch armv7 -isysroot ${IPHONEOS_SYSROOT}" LDFLAGS="-arch armv7 -miphoneos-version-min=${IOS_MIN_SDK} ${LDFLAGS}" "LIBS=${LIBS}"
make ${EXTRA_MAKE_FLAGS}
make ${EXTRA_MAKE_FLAGS} install
)
ARMV7_IPHONE_PROTOBUF=armv7/lib/libprotobuf.a
ARMV7_IPHONE_PROTOBUF_LITE=armv7/lib/libprotobuf-lite.a
else
ARMV7_IPHONE_PROTOBUF=
ARMV7_IPHONE_PROTOBUF_LITE=
fi
if [ $BUILD_ARMV7S_IPHONE -eq 1 ]
then
echo "$(tput setaf 2)"
echo "###################"
echo " armv7s for iPhone"
echo "###################"
echo "$(tput sgr0)"
(
cd /tmp/protobuf-2.5.0
make ${EXTRA_MAKE_FLAGS} distclean
./configure --build=x86_64-apple-darwin13.0.0 --host=armv7s-apple-darwin13.0.0 --with-protoc=${PREFIX}/platform/x86_64/bin/protoc --disable-shared --prefix=${PREFIX} --exec-prefix=${PREFIX}/platform/armv7s "CC=${CC}" "CFLAGS=${CFLAGS} -miphoneos-version-min=${IOS_MIN_SDK} -arch armv7s -isysroot ${IPHONEOS_SYSROOT}" "CXX=${CXX}" "CXXFLAGS=${CXXFLAGS} -arch armv7s -isysroot ${IPHONEOS_SYSROOT}" LDFLAGS="-arch armv7s -miphoneos-version-min=${IOS_MIN_SDK} ${LDFLAGS}" "LIBS=${LIBS}"
make ${EXTRA_MAKE_FLAGS}
make ${EXTRA_MAKE_FLAGS} install
)
ARMV7S_IPHONE_PROTOBUF=armv7s/lib/libprotobuf.a
ARMV7S_IPHONE_PROTOBUF_LITE=armv7s/lib/libprotobuf-lite.a
else
ARMV7S_IPHONE_PROTOBUF=
ARMV7S_IPHONE_PROTOBUF_LITE=
fi
if [ $BUILD_ARM64_IPHONE -eq 1 ]
then
echo "$(tput setaf 2)"
echo "########################################"
echo " Patch Protobuf 2.5.0 for 64bit support"
echo "########################################"
echo "$(tput sgr0)"
(
cd /tmp/protobuf-2.5.0
make ${EXTRA_MAKE_FLAGS} distclean
curl https://gist.github.com/BennettSmith/7111094/raw/171695f70b102de2301f5b45d9e9ab3167b4a0e8/0001-Add-generic-GCC-support-for-atomic-operations.patch --location --output /tmp/0001-Add-generic-GCC-support-for-atomic-operations.patch
curl https://gist.github.com/BennettSmith/7111094/raw/a4e85ffc82af00ae7984020300db51a62110db48/0001-Add-generic-gcc-header-to-Makefile.am.patch --location --output /tmp/0001-Add-generic-gcc-header-to-Makefile.am.patch
patch -p1 < /tmp/0001-Add-generic-GCC-support-for-atomic-operations.patch
patch -p1 < /tmp/0001-Add-generic-gcc-header-to-Makefile.am.patch
rm /tmp/0001-Add-generic-GCC-support-for-atomic-operations.patch
rm /tmp/0001-Add-generic-gcc-header-to-Makefile.am.patch
)
echo "$(tput setaf 2)"
echo "##################"
echo " arm64 for iPhone"
echo "##################"
echo "$(tput sgr0)"
(
cd /tmp/protobuf-2.5.0
./configure --build=x86_64-apple-darwin13.0.0 --host=arm --with-protoc=${PREFIX}/platform/x86_64/bin/protoc --disable-shared --prefix=${PREFIX} --exec-prefix=${PREFIX}/platform/arm64 "CC=${CC}" "CFLAGS=${CFLAGS} -miphoneos-version-min=${IOS_MIN_SDK} -arch arm64 -isysroot ${IPHONEOS_SYSROOT}" "CXX=${CXX}" "CXXFLAGS=${CXXFLAGS} -arch arm64 -isysroot ${IPHONEOS_SYSROOT}" LDFLAGS="-arch arm64 -miphoneos-version-min=${IOS_MIN_SDK} ${LDFLAGS}" "LIBS=${LIBS}"
make ${EXTRA_MAKE_FLAGS}
make ${EXTRA_MAKE_FLAGS} install
)
ARM64_IPHONE_PROTOBUF=arm64/lib/libprotobuf.a
ARM64_IPHONE_PROTOBUF_LITE=arm64/lib/libprotobuf-lite.a
else
ARM64_IPHONE_PROTOBUF=
ARM64_IPHONE_PROTOBUF_LITE=
fi
echo "$(tput setaf 2)"
echo "############################"
echo " Create Universal Libraries"
echo "############################"
echo "$(tput sgr0)"
(
cd ${PREFIX}/platform
mkdir universal
lipo ${X86_64_MAC_PROTOBUF} ${ARM64_IPHONE_PROTOBUF} ${ARMV7S_IPHONE_PROTOBUF} ${ARMV7_IPHONE_PROTOBUF} ${I386_IOS_SIM_PROTOBUF} -create -output universal/libprotobuf.a
lipo ${X86_64_MAC_PROTOBUF_LITE} ${ARM64_IPHONE_PROTOBUF_LITE} ${ARMV7S_IPHONE_PROTOBUF_LITE} ${ARMV7_IPHONE_PROTOBUF_LITE} ${I386_IOS_SIM_PROTOBUF_LITE} -create -output universal/libprotobuf-lite.a
)
echo "$(tput setaf 2)"
echo "########################"
echo " Finalize the packaging"
echo "########################"
echo "$(tput sgr0)"
(
cd ${PREFIX}
mkdir bin
mkdir lib
cp -r platform/x86_64/bin/protoc bin
cp -r platform/x86_64/lib/* lib
cp -r platform/universal/* lib
rm -rf platform
file lib/libprotobuf.a
file lib/libprotobuf-lite.a
file lib/libprotoc.a
)
) 2>&1
#) >build.log 2>&1
echo "$(tput setaf 2)"
echo Done!
echo "$(tput sgr0)"
@BennettSmith
Copy link
Author

Replace "namespace google" with "namespace google_public" in all source/header files. This is to address a namespace collision issue when building for recent versions of iOS. Apple is using the protobuf library internally, and embeds it as a private framework.

NOTE: This "fix" really should not be required, but until Apple either moves their internal use of protobuf out of a private framework or they change the namespace they are using we are forced into this ugly hack.

@danthorpe
Copy link

Hmm. But this doesn't compile the Objective-C compiler.... I'm not entirely sure I get what the purpose is of compiling for ARM64. Are you compiling your proto files on your iPhone 5S?

@wlrwxer
Copy link

wlrwxer commented Feb 18, 2014

Line 147 will failed on mac os x 10.9.1 with xcode 5.0, replace two places of 6.1 to 7.0 will be fixed.

@developforapple
Copy link

这个脚本直接使用的时候会有些问题。问题出在最后两组命令上。
使用这个脚本生成的.a库还是只支持x86_64。是因为倒数第二组命令不对,改为:
lipo -create armv7/lib/libprotobuf-lite.a armv7s/lib/libprotobuf-lite.a i386/lib/libprotobuf-lite.a x86_64/lib/libprotobuf-lite.a -output universal/libprotobuf-lite.a
lipo -create armv7/lib/libprotobuf.a armv7s/lib/libprotobuf.a i386/lib/libprotobuf.a x86_64/lib/libprotobuf.a -output universal/libprotobuf.a
lipo -create armv7/lib/libprotoc.a armv7s/lib/libprotoc.a i386/lib/libprotoc.a x86_64/lib/libprotoc.a -output universal/libprotoc.a
下面的(
cd ${PREFIX}
mkdir bin
mkdir lib
cp -r platform/x86_64/bin/protoc bin
cp -r platform/x86_64/lib/* lib
cp -r platform/universal/* lib
rm -rf platform
)其实没用。

也可以删除最后两组命令。自行在终端下执行文件合并操作也可以。protobuf文件在 /Users//protobuf

@machinekoder
Copy link

I have fixed the script for XCode5 and iOS 7.1. However, arm64 build is still not working:
https://gist.github.com/strahlex/10585771

@saintech
Copy link

Doesn't work. Mac OS 10.9.1, Xcode 5.1.1.

@JackYoung
Copy link

I hava fixed the script for SDKs as @Strahlex said.
Then I download the armv64 support for protobuf patch via wget.And it work for me.
https://gist.github.com/JackYoung/197edcd6a98da287bfae

@BennettSmith
Copy link
Author

GitHub has moved to what they call namespaced urls and this broke the curl commands that were downloading the patch files. You can read about their change here:

https://github.com/blog/1406-namespaced-gists

The script has been updated to account for this change. With the update it works fine for me and builds all architectures. Please let me know if you have any issues.

@sunyou
Copy link

sunyou commented Aug 20, 2014

when i import *.pb.h into my *.m file,i get "lexical or preprocessor issue 'string' file not found",am i missing anything?

@friskerik
Copy link

I have come into some problems, I hope some of you can help me.

I ran the scripts, it compiles and the libraries look fine to me. For example:

file libprotobuf.a
libprotobuf.a: Mach-O universal binary with 5 architectures
libprotobuf.a (for architecture x86_64): current ar archive random library
libprotobuf.a (for architecture armv7s): current ar archive random library
libprotobuf.a (for architecture armv7): current ar archive random library
libprotobuf.a (for architecture i386): current ar archive random library
libprotobuf.a (for architecture cputype (16777228) cpusubtype (0)): current ar archive random library

In my Xcode project I add libprotobuf.a in the libraries to link with binary.

My problem is that I get linker errors like:

Undefined symbols for architecture armv7:
"vtable for google::protobuf::internal::FunctionClosure0", referenced from:
google::protobuf::internal::FunctionClosure0::FunctionClosure0(void (*)(), bool) in protocol.pb.o
NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.

If I look at the linker log it includes -lprotobuf so I think that the linker tries to link the library but the linker errors indicate something else.

Anyone have any idea how to debug this? Any help is appreciated.

regards,
Erik

@SpaceCadetNia
Copy link

Hi,

I'm not sure what I'm doing wrong. I've followed the instructions for the installation (which doesn't say what I should do with the lib directory - so I added it to XCode's "Link Binary With Libraries" configuration under Build Phases.

Everything seems to be correct, it finds the headers. Clears all the version issues. It passes compilation, and...
It crashes on linking.

"Undefined symbols for architecture x86_64:"

Have you or anyone else here ran into this issue?

Thanks!

EDITED: Figured it out! Xcode wasn't including the correct libraries (by that I mean the libraries generated from this script and not the default protobuff generated ones). I was able to force it to use the libraries I want by dragging the .a and .la library files to the project.

@zanazansystems
Copy link

Getting following error /Projects/xxx/ProtoBuf/plugin.proto:74:12: "FileDescriptorProto" is not defined.
How to fix it?

@xiongdi9456
Copy link

Hello BennettSmith ,
Protobuf 2.6.0 was released.......Would you update the script? Thank you

@BennettSmith
Copy link
Author

I just started looking into what needs changing for 2.6.0 today. I'll post again when I have a solution.

@BennettSmith
Copy link
Author

A new script that builds ProtoBuf 2.6.0 is now available. Follow the link below to access it.

https://gist.github.com/BennettSmith/9487468ae3375d0db0cc

It does not apply the namespace change yet. I have not confirmed whether Apple addressed their issue with using a private framework containing protobuf yet. Once I run some tests I will add the namespace change if required.

@fredfei
Copy link

fredfei commented Sep 22, 2014

Hello BennettSmith ,
can i use this scrpit in Xcode6 ?

@david415
Copy link

Hello everyone,

Be aware of remote code execution vulnerabilities in not just your software but the
build systems for your software. OK here's the problem... you must not download
code via http and then execute it:

https://gist.github.com/BennettSmith/7150245#file-build-protobuf-2-5-0-sh-L80

stay safe... and happy malware day (everyday is malware day)

David

@keithkml
Copy link

I had to add this line to the "Finalize the packaging" step, because I don't have autoconf or automake installed on my system, so the patched Makefile.am does not take effect.

    # The Makefile.am patch doesn't get applied when automake is not installed (which it's not by default on OS X)
    # ...this means the new header file doesn't get copied into the dist folder during `make install`
    cp /tmp/protobuf-2.5.0/src/google/protobuf/stubs/atomicops_internals_generic_gcc.h include/google/protobuf/stubs
    sed -i '' -e "s/google::protobuf/${GOOGLE_NAMESPACE}::protobuf/g" include/google/protobuf/stubs/atomicops_internals_generic_gcc.h
    sed -i '' -e "s/namespace\ google /namespace\ ${GOOGLE_NAMESPACE} /g" include/google/protobuf/stubs/atomicops_internals_generic_gcc.h

@pkalisz
Copy link

pkalisz commented Jun 8, 2015

Is it possible to apply it to 2.3.0? Currently it doesn't work, patch is the problem, but to be honest I have no idea why...

@githubYiheng
Copy link

Welcome to 2022!

@89trillion-feiyang
Copy link

Welcome to 2022!

😔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment