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" == s390x ]]; then 97 if [[ $host_machine == s390x ]];then 98 assertEq $format "elf64-s390" $LINENO 99 else 100 assertEq $format "elf64-big" $LINENO 101 fi 102 elif [[ "$ARCH" == ppcle_64 ]]; then 103 if [[ $host_machine == ppc64le ]];then 104 assertEq $format "elf64-powerpcle" $LINENO 105 else 106 assertEq $format "elf64-little" $LINENO 107 fi 108 else 109 fail "Unsupported arch: $ARCH" 110 fi 111 else 112 # $OS == windows 113 if [[ "$ARCH" == x86_32 ]]; then 114 assertEq $format "pei-i386" $LINENO 115 elif [[ "$ARCH" == x86_64 ]]; then 116 assertEq $format "pei-x86-64" $LINENO 117 else 118 fail "Unsupported arch: $ARCH" 119 fi 120 fi 121 elif [[ "$OS" == osx ]]; then 122 format="$(file -b "$1" | grep -o "[^ ]*$")" 123 echo Format=$format 124 if [[ "$ARCH" == x86_32 ]]; then 125 assertEq $format "i386" $LINENO 126 elif [[ "$ARCH" == x86_64 ]]; then 127 assertEq $format "x86_64" $LINENO 128 else 129 fail "Unsupported arch: $ARCH" 130 fi 131 else 132 fail "Unsupported system: $OS" 133 fi 134 echo 135} 136 137# Checks the dependencies of the artifact. Artifacts should only depend on 138# system libraries. 139# Usage: checkDependencies <path-to-protoc> 140checkDependencies () 141{ 142 if [[ "$OS" == windows ]]; then 143 dump_cmd='objdump -x '"$1"' | fgrep "DLL Name"' 144 white_list="KERNEL32\.dll\|msvcrt\.dll" 145 elif [[ "$OS" == linux ]]; then 146 host_machine="$(uname -m)"; 147 dump_cmd='ldd '"$1" 148 if [[ "$ARCH" == x86_32 ]]; then 149 white_list="linux-gate\.so\.1\|libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|ld-linux\.so\.2" 150 elif [[ "$ARCH" == x86_64 ]]; then 151 white_list="linux-vdso\.so\.1\|libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|ld-linux-x86-64\.so\.2" 152 elif [[ "$ARCH" == s390x ]]; then 153 if [[ $host_machine != s390x ]];then 154 dump_cmd='objdump -p '"$1"' | grep NEEDED' 155 fi 156 white_list="linux-vdso64\.so\.1\|libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|libz\.so\.1\|ld64\.so\.1" 157 elif [[ "$ARCH" == ppcle_64 ]]; then 158 if [[ $host_machine != ppc64le ]];then 159 dump_cmd='objdump -p '"$1"' | grep NEEDED' 160 fi 161 white_list="linux-vdso64\.so\.1\|libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|libz\.so\.1\|ld64\.so\.2" 162 elif [[ "$ARCH" == aarch_64 ]]; then 163 dump_cmd='objdump -p '"$1"' | grep NEEDED' 164 white_list="libpthread\.so\.0\|libm\.so\.6\|libc\.so\.6\|ld-linux-aarch64\.so\.1" 165 fi 166 elif [[ "$OS" == osx ]]; then 167 dump_cmd='otool -L '"$1"' | fgrep dylib' 168 white_list="libz\.1\.dylib\|libstdc++\.6\.dylib\|libSystem\.B\.dylib" 169 fi 170 if [[ -z "$white_list" || -z "$dump_cmd" ]]; then 171 fail "Unsupported platform $OS-$ARCH." 172 fi 173 echo "Checking for expected dependencies ..." 174 eval $dump_cmd | grep -i "$white_list" || fail "doesn't show any expected dependencies" 175 echo "Checking for unexpected dependencies ..." 176 eval $dump_cmd | grep -i -v "$white_list" 177 ret=$? 178 if [[ $ret == 0 ]]; then 179 fail "found unexpected dependencies (listed above)." 180 elif [[ $ret != 1 ]]; then 181 fail "Error when checking dependencies." 182 fi # grep returns 1 when "not found", which is what we expect 183 echo "Dependencies look good." 184 echo 185} 186############################################################################ 187 188echo "Building protoc, OS=$OS ARCH=$ARCH TARGET=$MAKE_TARGET" 189 190CONFIGURE_ARGS="--disable-shared" 191 192if [[ "$OS" == windows ]]; then 193 MAKE_TARGET="${MAKE_TARGET}.exe" 194fi 195 196# Override the default value set in configure.ac that has '-g' which produces 197# huge binary. 198CXXFLAGS="-DNDEBUG" 199LDFLAGS="" 200 201if [[ "$(uname)" == CYGWIN* ]]; then 202 assertEq "$OS" windows $LINENO 203 # Use mingw32 compilers because executables produced by Cygwin compiler 204 # always have dependency on Cygwin DLL. 205 if [[ "$ARCH" == x86_64 ]]; then 206 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=x86_64-w64-mingw32" 207 elif [[ "$ARCH" == x86_32 ]]; then 208 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=i686-pc-mingw32" 209 else 210 fail "Unsupported arch by CYGWIN: $ARCH" 211 fi 212elif [[ "$(uname)" == MINGW32* ]]; then 213 assertEq "$OS" windows $LINENO 214 assertEq "$ARCH" x86_32 $LINENO 215elif [[ "$(uname)" == MINGW64* ]]; then 216 assertEq "$OS" windows $LINENO 217 assertEq "$ARCH" x86_64 $LINENO 218elif [[ "$(uname)" == Linux* ]]; then 219 if [[ "$OS" == linux ]]; then 220 if [[ "$ARCH" == x86_64 ]]; then 221 CXXFLAGS="$CXXFLAGS -m64" 222 elif [[ "$ARCH" == x86_32 ]]; then 223 CXXFLAGS="$CXXFLAGS -m32" 224 elif [[ "$ARCH" == aarch_64 ]]; then 225 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=aarch64-linux-gnu" 226 elif [[ "$ARCH" == ppcle_64 ]]; then 227 CXXFLAGS="$CXXFLAGS -m64" 228 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=powerpc64le-linux-gnu" 229 elif [[ "$ARCH" == s390x ]]; then 230 CXXFLAGS="$CXXFLAGS -m64" 231 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=s390x-linux-gnu" 232 else 233 fail "Unsupported arch: $ARCH" 234 fi 235 elif [[ "$OS" == windows ]]; then 236 # Cross-compilation for Windows 237 CONFIGURE_ARGS="$CONFIGURE_ARGS" 238 if [[ "$ARCH" == x86_64 ]]; then 239 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=x86_64-w64-mingw32" 240 elif [[ "$ARCH" == x86_32 ]]; then 241 CONFIGURE_ARGS="$CONFIGURE_ARGS --host=i686-w64-mingw32" 242 else 243 fail "Unsupported arch: $ARCH" 244 fi 245 else 246 fail "Cannot build $OS on $(uname)" 247 fi 248elif [[ "$(uname)" == Darwin* ]]; then 249 assertEq "$OS" osx $LINENO 250 # Make the binary compatible with OSX 10.7 and later 251 CXXFLAGS="$CXXFLAGS -mmacosx-version-min=10.7" 252 if [[ "$ARCH" == x86_64 ]]; then 253 CXXFLAGS="$CXXFLAGS -m64" 254 elif [[ "$ARCH" == x86_32 ]]; then 255 CXXFLAGS="$CXXFLAGS -m32" 256 else 257 fail "Unsupported arch: $ARCH" 258 fi 259else 260 fail "Unsupported system: $(uname)" 261fi 262 263# Statically link libgcc and libstdc++. 264# -s to produce stripped binary. 265if [[ "$OS" == windows ]]; then 266 # Also static link libpthread required by mingw64 267 LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -s" 268elif [[ "$OS" != osx ]]; then 269 # And they don't work under Mac. 270 LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++ -s" 271fi 272 273export CXXFLAGS LDFLAGS 274 275# Nested double quotes are unintuitive, but it works. 276cd "$(dirname "$0")" 277 278WORKING_DIR="$(pwd)" 279BUILD_DIR="build/$OS/$ARCH" 280TARGET_FILE="target/$OS/$ARCH/$MAKE_TARGET.exe" 281 282mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR" && 283 ../../../../configure $CONFIGURE_ARGS && 284 cd src && make $MAKE_TARGET -j8 && 285 cd "$WORKING_DIR" && mkdir -p $(dirname $TARGET_FILE) && 286 cp $BUILD_DIR/src/$MAKE_TARGET $TARGET_FILE || 287 exit 1 288 289if [[ "$OS" == osx ]]; then 290 # Since Mac linker doesn't accept "-s", we need to run strip 291 strip $TARGET_FILE || exit 1 292fi 293 294checkArch $TARGET_FILE && checkDependencies $TARGET_FILE 295