• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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