1#!/bin/bash 2 3# Builds protoc executable into target/<OS>/<ARCH>/protoc.exe; optionally builds 4# protoc plugins into target/<OS>/<ARCH>/protoc-gen-*.exe 5# 6# Usage: ./build-protoc.sh <OS> <ARCH> <TARGET> 7# 8# <TARGET> can be "protoc" or "protoc-gen-javalite". Supported <OS> <ARCH> 9# combinations: 10# HOST <OS> <ARCH> <COMMENT> 11# cygwin windows x86_32 Requires: i686-w64-mingw32-gcc 12# cygwin windows x86_64 Requires: x86_64-w64-mingw32-gcc 13# linux linux aarch_64 Requires: g++-aarch64-linux-gnu 14# linux linux x86_32 15# linux linux x86_64 16# linux windows x86_32 Requires: i686-w64-mingw32-gcc 17# linux windows x86_64 Requires: x86_64-w64-mingw32-gcc 18# macos osx x86_32 19# macos osx x86_64 20# mingw windows x86_32 21# mingw windows x86_64 22# 23# Before running this script, make sure you have generated the configure script 24# in the parent directory (i.e., run ./autogen.sh there). 25 26OS=$1 27ARCH=$2 28MAKE_TARGET=$3 29 30if [[ $# < 3 ]]; then 31 echo "Not enough arguments provided." 32 exit 1 33fi 34 35case $MAKE_TARGET in 36 protoc-gen-javalite) 37 ;; 38 protoc) 39 ;; 40 *) 41 echo "Target ""$MAKE_TARGET"" invalid." 42 exit 1 43esac 44 45# Under Cygwin, bash doesn't have these in PATH when called from Maven which 46# runs in Windows version of Java. 47export PATH="/bin:/usr/bin:$PATH" 48 49############################################################################ 50# Helper functions 51############################################################################ 52E_PARAM_ERR=98 53E_ASSERT_FAILED=99 54 55# Usage: 56fail() 57{ 58 echo "ERROR: $1" 59 echo 60 exit $E_ASSERT_FAILED 61} 62 63# Usage: assertEq VAL1 VAL2 $LINENO 64assertEq () 65{ 66 lineno=$3 67 if [ -z "$lineno" ]; then 68 echo "lineno not given" 69 exit $E_PARAM_ERR 70 fi 71 72 if [[ "$1" != "$2" ]]; then 73 echo "Assertion failed: \"$1\" == \"$2\"" 74 echo "File \"$0\", line $lineno" # Give name of file and line number. 75 exit $E_ASSERT_FAILED 76 fi 77} 78 79# Checks the artifact is for the expected architecture 80# Usage: checkArch <path-to-protoc> 81checkArch () 82{ 83 echo 84 echo "Checking file format ..." 85 if [[ "$OS" == windows || "$OS" == linux ]]; then 86 format="$(objdump -f "$1" | grep -o "file format .*$" | grep -o "[^ ]*$")" 87 echo Format=$format 88 if [[ "$OS" == linux ]]; then 89 host_machine="$(uname -m)"; 90 if [[ "$ARCH" == x86_32 ]]; then 91 assertEq $format "elf32-i386" $LINENO 92 elif [[ "$ARCH" == x86_64 ]]; then 93 assertEq $format "elf64-x86-64" $LINENO 94 elif [[ "$ARCH" == aarch_64 ]]; then 95 assertEq $format "elf64-little" $LINENO 96 elif [[ "$ARCH" == ppcle_64 ]]; then 97 if [[ $host_machine == ppc64le ]];then 98 assertEq $format "elf64-powerpcle" $LINENO 99 else 100 assertEq $format "elf64-little" $LINENO 101 fi 102 else 103 fail "Unsupported arch: $ARCH" 104 fi 105 else 106 # $OS == windows 107 if [[ "$ARCH" == x86_32 ]]; then 108 assertEq $format "pei-i386" $LINENO 109 elif [[ "$ARCH" == x86_64 ]]; then 110 assertEq $format "pei-x86-64" $LINENO 111 else 112 fail "Unsupported arch: $ARCH" 113 fi 114 fi 115 elif [[ "$OS" == osx ]]; then 116 format="$(file -b "$1" | grep -o "[^ ]*$")" 117 echo Format=$format 118 if [[ "$ARCH" == x86_32 ]]; then 119 assertEq $format "i386" $LINENO 120 elif [[ "$ARCH" == x86_64 ]]; then 121 assertEq $format "x86_64" $LINENO 122 else 123 fail "Unsupported arch: $ARCH" 124 fi 125 else 126 fail "Unsupported system: $OS" 127 fi 128 echo 129} 130 131# Checks the dependencies of the artifact. Artifacts should only depend on 132# system libraries. 133# Usage: checkDependencies <path-to-protoc> 134checkDependencies () 135{ 136 if [[ "$OS" == windows ]]; then 137 dump_cmd='objdump -x '"$1"' | fgrep "DLL Name"' 138 white_list="KERNEL32\.dll\|msvcrt\.dll" 139 elif [[ "$OS" == linux ]]; then 140 host_machine="$(uname -m)"; 141 dump_cmd='ldd '"$1" 142 if [[ "$ARCH" == x86_32 ]]; then 143 white_list="linux-gate\.so\.1\|libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|ld-linux\.so\.2" 144 elif [[ "$ARCH" == x86_64 ]]; then 145 white_list="linux-vdso\.so\.1\|libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|ld-linux-x86-64\.so\.2" 146 elif [[ "$ARCH" == ppcle_64 ]]; then 147 if [[ $host_machine != ppc64le ]];then 148 dump_cmd='objdump -p '"$1"' | grep NEEDED' 149 fi 150 white_list="linux-vdso64\.so\.1\|libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|libz\.so\.1\|ld64\.so\.2" 151 elif [[ "$ARCH" == aarch_64 ]]; then 152 dump_cmd='objdump -p '"$1"' | grep NEEDED' 153 white_list="libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|ld-linux-aarch64\.so\.1" 154 fi 155 elif [[ "$OS" == osx ]]; then 156 dump_cmd='otool -L '"$1"' | fgrep dylib' 157 white_list="libz\.1\.dylib\|libstdc++\.6\.dylib\|libSystem\.B\.dylib" 158 fi 159 if [[ -z "$white_list" || -z "$dump_cmd" ]]; then 160 fail "Unsupported platform $OS-$ARCH." 161 fi 162 echo "Checking for expected dependencies ..." 163 eval $dump_cmd | grep -i "$white_list" || fail "doesn't show any expected dependencies" 164 echo "Checking for unexpected dependencies ..." 165 eval $dump_cmd | grep -i -v "$white_list" 166 ret=$? 167 if [[ $ret == 0 ]]; then 168 fail "found unexpected dependencies (listed above)." 169 elif [[ $ret != 1 ]]; then 170 fail "Error when checking dependencies." 171 fi # grep returns 1 when "not found", which is what we expect 172 echo "Dependencies look good." 173 echo 174} 175############################################################################ 176 177echo "Building protoc, OS=$OS ARCH=$ARCH TARGET=$MAKE_TARGET" 178 179CONFIGURE_ARGS="--disable-shared" 180 181if [[ "$OS" == windows ]]; then 182 MAKE_TARGET="${MAKE_TARGET}.exe" 183fi 184 185# Override the default value set in configure.ac that has '-g' which produces 186# huge binary. 187CXXFLAGS="-DNDEBUG" 188LDFLAGS="" 189 190if [[ "$(uname)" == CYGWIN* ]]; then 191 assertEq "$OS" windows $LINENO 192 # Use mingw32 compilers because executables produced by Cygwin compiler 193 # always have dependency on Cygwin DLL. 194 if [[ "$ARCH" == x86_64 ]]; then 195 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=x86_64-w64-mingw32" 196 elif [[ "$ARCH" == x86_32 ]]; then 197 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=i686-pc-mingw32" 198 else 199 fail "Unsupported arch by CYGWIN: $ARCH" 200 fi 201elif [[ "$(uname)" == MINGW32* ]]; then 202 assertEq "$OS" windows $LINENO 203 assertEq "$ARCH" x86_32 $LINENO 204elif [[ "$(uname)" == MINGW64* ]]; then 205 assertEq "$OS" windows $LINENO 206 assertEq "$ARCH" x86_64 $LINENO 207elif [[ "$(uname)" == Linux* ]]; then 208 if [[ "$OS" == linux ]]; then 209 if [[ "$ARCH" == x86_64 ]]; then 210 CXXFLAGS="$CXXFLAGS -m64" 211 elif [[ "$ARCH" == x86_32 ]]; then 212 CXXFLAGS="$CXXFLAGS -m32" 213 elif [[ "$ARCH" == aarch_64 ]]; then 214 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=aarch64-linux-gnu" 215 elif [[ "$ARCH" == ppcle_64 ]]; then 216 CXXFLAGS="$CXXFLAGS -m64" 217 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=powerpc64le-linux-gnu" 218 else 219 fail "Unsupported arch: $ARCH" 220 fi 221 elif [[ "$OS" == windows ]]; then 222 # Cross-compilation for Windows 223 CONFIGURE_ARGS="$CONFIGURE_ARGS" 224 if [[ "$ARCH" == x86_64 ]]; then 225 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=x86_64-w64-mingw32" 226 elif [[ "$ARCH" == x86_32 ]]; then 227 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=i686-w64-mingw32" 228 else 229 fail "Unsupported arch: $ARCH" 230 fi 231 else 232 fail "Cannot build $OS on $(uname)" 233 fi 234elif [[ "$(uname)" == Darwin* ]]; then 235 assertEq "$OS" osx $LINENO 236 # Make the binary compatible with OSX 10.7 and later 237 CXXFLAGS="$CXXFLAGS -mmacosx-version-min=10.7" 238 if [[ "$ARCH" == x86_64 ]]; then 239 CXXFLAGS="$CXXFLAGS -m64" 240 elif [[ "$ARCH" == x86_32 ]]; then 241 CXXFLAGS="$CXXFLAGS -m32" 242 else 243 fail "Unsupported arch: $ARCH" 244 fi 245else 246 fail "Unsupported system: $(uname)" 247fi 248 249# Statically link libgcc and libstdc++. 250# -s to produce stripped binary. 251if [[ "$OS" == windows ]]; then 252 # Also static link libpthread required by mingw64 253 LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -s" 254elif [[ "$OS" != osx ]]; then 255 # And they don't work under Mac. 256 LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++ -s" 257fi 258 259export CXXFLAGS LDFLAGS 260 261# Nested double quotes are unintuitive, but it works. 262cd "$(dirname "$0")" 263 264WORKING_DIR="$(pwd)" 265BUILD_DIR="build/$OS/$ARCH" 266TARGET_FILE="target/$OS/$ARCH/$MAKE_TARGET.exe" 267 268mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR" && 269 ../../../../configure $CONFIGURE_ARGS && 270 cd src && make $MAKE_TARGET -j8 && 271 cd "$WORKING_DIR" && mkdir -p $(dirname $TARGET_FILE) && 272 cp $BUILD_DIR/src/$MAKE_TARGET $TARGET_FILE || 273 exit 1 274 275if [[ "$OS" == osx ]]; then 276 # Since Mac linker doesn't accept "-s", we need to run strip 277 strip $TARGET_FILE || exit 1 278fi 279 280checkArch $TARGET_FILE && checkDependencies $TARGET_FILE 281