1#!/bin/sh
2#
3
4set -e
5export LANG=C
6export LC_ALL=C
7
8PROGDIR=$(dirname "$0")
9PROGNAME=$(basename "$0")
10
11panic () {
12  echo "ERROR: $@"
13  exit 1
14}
15
16VERBOSE=1
17
18# Dump message is $VERBOSE >= $1
19# $1+: message.
20dump_n () {
21  local LOG_LEVEL=$1
22  shift
23  if [ "$VERBOSE" -ge "$LOG_LEVEL" ]; then
24    printf "%s\n" "$@"
25  fi
26}
27
28# Dump a message unless --quiet is used.
29# $1+: message.
30dump () {
31  dump_n 1 "$@"
32}
33
34# Dump a message if --verbose is used only.
35# $1+: message.
36log () {
37  dump_n 2 "$@"
38}
39
40# Run a command silently, unless --verbose or '--verbose --verbose'
41# is used.
42# $1+: Command
43# Return: command status.
44run () {
45  log "COMMAND: $*"
46  case $VERBOSE in
47    0)
48      "$@" >/dev/null 2>&1 || return $?
49      ;;
50    1)
51      "$@" >/dev/null || return $?
52      ;;
53    *)
54      "$@" || return $?
55      ;;
56  esac
57}
58
59# $1: string
60# Out: input string, with capital letters replaced by small ones.
61tolower () {
62  echo "$1" | tr '[A-Z]' '[a-z]'
63}
64
65# Return value of a given variable.
66# $1: Variable name
67var_value () {
68  eval printf \"%s\" \"\$$1\"
69}
70
71# Remove some items from a list
72# $1: input space-separated list
73# $2: space-separated list of items to remove from 1
74# Out: items of $1 without items of $2
75filter_out () {
76  local TMP=$(mktemp)
77  local RESULT
78  printf "" > $TMP
79  echo "$2" | tr ' ' '\n' > $TMP
80  RESULT=$(echo "$1" | tr ' ' '\n' | fgrep -x -v -f $TMP | tr '\n' ' ')
81  rm -f $TMP
82  echo "$RESULT"
83}
84
85src_to_obj () {
86  case $1 in
87    *.c)
88      echo ${1%%.c}.o
89      ;;
90    *.S)
91      echo ${1%%.S}.o
92      ;;
93    *)
94      echo $1
95      ;;
96  esac
97}
98
99# Determine host operating system.
100HOST_OS=$(uname -s)
101case $HOST_OS in
102  Linux)
103    HOST_OS=linux
104    ;;
105  Darwin)
106    HOST_OS=darwin
107    ;;
108esac
109
110# Determine host architecture
111HOST_ARCH=$(uname -m)
112case $HOST_ARCH in
113  i?86)
114    HOST_ARCH=x86
115    ;;
116esac
117
118ANDROID_HOST_TAG=$HOST_OS-$HOST_ARCH
119
120case $ANDROID_HOST_TAG in
121  linux-x86_64|darwin-x86-64)
122    ANDROID_HOST_TAG=$HOST_OS-x86
123    ;;
124  *)
125    panic "Sorry, this script can only run on 64-bit Linux or Darwin"
126esac
127
128# Determine number of cores
129case $HOST_OS in
130  linux)
131    NUM_CORES=$(grep -c "processor" /proc/cpuinfo)
132    ;;
133  darwin)
134    NUM_CORES=$(sysctl -n hw.ncpu)
135    ;;
136  *)
137    NUM_CORES=1
138    ;;
139esac
140
141# The list of supported Android target architectures.
142ANDROID_ARCHS="arm x86 mips"
143
144BUILD_TYPES=
145for ARCH in $ANDROID_ARCHS; do
146  BUILD_TYPES="$BUILD_TYPES android-$ARCH"
147done
148ANDROID_BUILD_TYPES=$BUILD_TYPES
149
150# NOTE: The $HOST_OS-x86_64 is currently broken because the single
151#       <openssl/opensslconf.h> header is tailored for 32-bits.
152HOST_BUILD_TYPES="$HOST_OS-x86 $HOST_OS-generic32 $HOST_OS-generic64"
153
154BUILD_TYPES="$ANDROID_BUILD_TYPES $HOST_BUILD_TYPES"
155
156# Parse command-line
157DO_HELP=
158SRC_DIR=$(cd $PROGDIR && pwd)
159OUT_DIR=out
160BUILD_DIR=
161BUILD_TYPES=
162NUM_JOBS=$NUM_CORES
163ANDROID_BUILD_TOP=$(cd $PROGDIR/../.. && pwd)
164for OPT; do
165  case $OPT in
166    --help|-h|-?)
167      DO_HELP=true
168      ;;
169    --build-dir=*)
170      BUILD_DIR=${OPT##--build-dir=}
171      ;;
172    --verbose)
173      VERBOSE=$(( $VERBOSE + 1 ))
174      ;;
175    --jobs=*)
176      NUM_JOBS=${OPT##--jobs=}
177      ;;
178    --quiet)
179      VERBOSE=$(( $VERBOSE - 1 ))
180      ;;
181    -j*)
182      NUM_JOBS=${OPT##-j}
183      ;;
184    -*)
185      panic "Unknown option '$OPT', see --help for details."
186      ;;
187    *)
188      BUILD_TYPES="$BUILD_TYPES $OPT"
189      ;;
190  esac
191done
192
193# Print help when needed.
194if [ "$DO_HELP" ]; then
195  echo \
196"Usage: $PROGNAME [options] [<build-type> ...]
197
198This script is used to ensure that all OpenSSL build variants compile
199properly. It can be used after modifying external/openssl/openssl.config
200and re-running import_openssl.sh to check that any changes didn't break
201the build.
202
203A <build-type> is a description of a given build of the library and its
204program. Its format is:
205
206  <compiler>-<system>-<arch>
207
208Where: <compiler> is either 'gcc' or 'clang'.
209       <system>   is 'android', 'linux' or 'darwin'.
210       <arch>     is 'arm', 'x86'  or 'mips'.
211
212By default, it rebuilds the sources for the following build types:
213"
214  for BUILD_TYPE in $BUILD_TYPES; do
215    echo "  $BUILD_TYPE"
216  done
217
218  echo \
219"However, you can pass custom values on the command-line instead.
220
221This scripts generates a custom Makefile in a temporary directory, then
222launches 'make' in it to build all binaries in parallel. In case of
223problem, you can use the --build-dir=<path> option to specify a custom
224build-directory, which will _not_ be removed when the script exits.
225
226For example, to better see why a build fails:
227
228   ./$PROGNAME --build-dir=/tmp/mydir
229   make -C /tmp/mydir V=1
230
231Valid options:
232
233  --help|-h|-?        Print this message.
234  --build-dir=<path>  Specify build directory.
235  --jobs=<count>      Run <count> parallel build jobs [$NUM_JOBS].
236  -j<count>           Same as --jobs=<count>.
237  --verbose           Increase verbosity.
238  --quiet             Decrease verbosity.
239"
240  exit 0
241fi
242
243log "Host OS: $HOST_OS"
244log "Host arch: $HOST_ARCH"
245log "Host CPU count: $NUM_CORES"
246
247if [ -z "$BUILD_TYPES" ]; then
248  BUILD_TYPES="$ANDROID_BUILD_TYPES $HOST_BUILD_TYPES"
249fi
250log "Build types: $BUILD_TYPES"
251
252if [ -z "$BUILD_DIR" ]; then
253  # Create a temporary directory, ensure it gets destroyed properly
254  # when the script exits.
255  BUILD_DIR=$(mktemp -d)
256  clean_build_dir () {
257    log "Cleaning up temporary directory: $BUILD_DIR"
258    rm -rf "$BUILD_DIR"
259    exit $1
260  }
261  trap "clean_build_dir 0" EXIT
262  trap "clean_build_dir \$?" INT HUP QUIT TERM
263  log "Using temporary build directory: $BUILD_DIR"
264else
265  log "Using user build directory: $BUILD_DIR"
266fi
267
268mkdir -p "$BUILD_DIR" && rm -rf "$BUILD_DIR"/*
269
270MAKEFILE=$BUILD_DIR/GNUmakefile
271
272# Return source files for a given module and architecture.
273# $1: module prefix (e.g. CRYPTO)
274# $2: build arch.
275get_module_src_files_for_arch () {
276  local prefix=$1
277  local arch=$2
278  local src_files="$(var_value OPENSSL_${prefix}_SOURCES)"
279  src_files="$src_files $(var_value OPENSSL_${prefix}_SOURCES_${arch})"
280  local exclude_files="$(var_value OPENSSL_${prefix}_SOURCES_EXCLUDES_${arch})"
281  src_files=$(filter_out "$src_files" "$exclude_files")
282  echo "$src_files"
283}
284
285# Return the compiler defines for a given module and architecture
286# $1: module prefix (e.g. CRYPTO)
287# $2 build arch.
288get_module_defines_for_arch () {
289  local prefix=$1
290  local arch=$2
291  local defines="$(var_value OPENSSL_${prefix}_DEFINES)"
292  defines="$defines $(var_value OPENSSL_${prefix}_DEFINES_${arch})"
293  echo "$defines"
294}
295
296# $1: module prefix (e.g. CRYPTO)
297get_module_c_includes () {
298  var_value OPENSSL_$1_INCLUDES
299}
300
301# $1: build type (e.g. gcc-android-arm)
302# Out: build arch.
303get_build_arch () {
304  echo "$1" | cut -d- -f3
305}
306
307# $1: build arch
308# Out: GNU configuration target (e.g. arm-linux-androideabi)
309get_build_arch_target () {
310  case $1 in
311    arm)
312      echo "arm-linux-androideabi"
313      ;;
314    x86)
315      echo "i686-linux-android"
316      ;;
317    mips)
318      echo "mipsel-linux-android"
319      ;;
320    *)
321      echo "$1-linux-android"
322      ;;
323  esac
324}
325
326GCC_VERSION=4.7
327CLANG_VERSION=3.1
328
329get_prebuilt_gcc_dir_for_arch () {
330  local arch=$1
331  local target=$(get_build_arch_target $arch)
332  echo "$ANDROID_BUILD_TOP/prebuilts/gcc/$ANDROID_HOST_TAG/$arch/$target-$GCC_VERSION"
333}
334
335get_prebuilt_clang () {
336  echo "$ANDROID_BUILD_TOP/prebuilts/clang/$ANDROID_HOST_TAG/$CLANG_VERSION/clang"
337}
338
339get_prebuilt_ndk_sysroot_for_arch () {
340  echo "$ANDROID_BUILD_TOP/prebuilts/ndk/current/platforms/android-9/arch-$1"
341}
342
343get_c_runtime_file () {
344  local build_type=$1
345  local arch=$(get_build_arch $build_type)
346  local filename=$2
347  echo "$(get_prebuilt_ndk_sysroot_for_arch $arch)/usr/lib/$filename"
348}
349
350# $1: build type (e.g. gcc-android-arm)
351get_build_compiler () {
352  local arch=$(get_build_arch $1)
353  local target=$(get_build_arch_target $arch)
354  local gcc_dir=$(get_prebuilt_gcc_dir_for_arch $arch);
355  local result
356
357  # Get the toolchain binary.
358  case $1 in
359    gcc-android-*)
360      result="$gcc_dir/bin/$target-gcc"
361      ;;
362    clang-android-*)
363      result="$(get_prebuilt_clang) -target $target -B$gcc_dir/$target/bin -I$gcc_dir/lib/gcc/$target/$GCC_VERSION/include"
364      ;;
365    gcc-*)
366      result=gcc
367      ;;
368    clang-*) # Must have host clang compiler.
369      result=clang
370      ;;
371  esac
372
373  compiler_check=$(which $result 2>/dev/null || echo "")
374  if [ -z "$compiler_check" ]; then
375    panic "Could not find compiler: $result"
376  fi
377
378  # Get the Android sysroot if needed.
379  case $1 in
380    *-android-*)
381      result="$result --sysroot=$(get_prebuilt_ndk_sysroot_for_arch $arch)"
382      ;;
383  esac
384
385  # Force -m32 flag when needed for 32-bit builds.
386  case $1 in
387    *-linux-x86|*-darwin-x86|*-generic32)
388      result="$result -m32"
389      ;;
390  esac
391  echo "$result"
392}
393
394# $1: build type.
395# Out: common compiler flags for this build.
396get_build_c_flags () {
397  local result="-O2 -fPIC"
398  case $1 in
399    *-android-arm)
400      result="$result -march=armv7-a -mfpu=vfpv3-d16"
401      ;;
402  esac
403
404  case $1 in
405    *-generic32|*-generic64)
406      # Generic builds do not compile without this flag.
407      result="$result -DOPENSSL_NO_ASM"
408      ;;
409  esac
410  echo "$result"
411}
412
413# $1: build type.
414# Out: linker for this build.
415get_build_linker () {
416  get_build_compiler $1
417}
418
419clear_sources () {
420  g_all_objs=""
421}
422
423# Generate build instructions to compile source files.
424# Also update g_all_objs.
425# $1: module prefix (e.g. CRYPTO)
426# $2: build type
427build_sources () {
428  local prefix=$1
429  local build_type=$2
430  echo "## build_sources prefix='$prefix' build_type='$build_type'"
431  local arch=$(get_build_arch $build_type)
432  local src_files=$(get_module_src_files_for_arch $prefix $arch)
433  local c_defines=$(get_module_defines_for_arch $prefix $arch)
434  local c_includes=$(get_module_c_includes $prefix "$SRC_DIR")
435  local build_cc=$(get_build_compiler $build_type)
436  local build_cflags=$(get_build_c_flags $build_type)
437  local build_linker=$(get_build_linker $build_type)
438  local src obj def inc
439
440  printf "OUT_DIR := $OUT_DIR/$build_type\n\n"
441  printf "BUILD_CC := $build_cc\n\n"
442  printf "BUILD_LINKER := $build_linker\n\n"
443  printf "BUILD_CFLAGS := $build_cflags"
444  for inc in $c_includes; do
445    printf " -I\$(SRC_DIR)/$inc"
446  done
447  for def in $c_defines; do
448    printf " -D$def"
449  done
450  printf "\n\n"
451  printf "BUILD_OBJECTS :=\n\n"
452
453  case $build_type in
454    clang-android-*)
455      # The version of clang that comes with the platform build doesn't
456      # support simple linking of shared libraries and executables. One
457      # has to provide the C runtime files explicitely.
458      local crtbegin_so=$(get_c_runtime_file $build_type crtbegin_so.o)
459      local crtend_so=$(get_c_runtime_file $build_type crtend_so.o)
460      local crtbegin_exe=$(get_c_runtime_file $build_type crtbegin_dynamic.o)
461      local crtend_exe=$(get_c_runtime_file $build_type crtend_android.o)
462      printf "CRTBEGIN_SO := $crtbegin_so\n"
463      printf "CRTEND_SO := $crtend_so\n"
464      printf "CRTBEGIN_EXE := $crtbegin_exe\n"
465      printf "CRTEND_EXE := $crtend_exe\n"
466      printf "\n"
467      ;;
468  esac
469
470  for src in $src_files; do
471    obj=$(src_to_obj $src)
472    g_all_objs="$g_all_objs $obj"
473    printf "OBJ := \$(OUT_DIR)/$obj\n"
474    printf "BUILD_OBJECTS += \$(OBJ)\n"
475    printf "\$(OBJ): PRIVATE_CC := \$(BUILD_CC)\n"
476    printf "\$(OBJ): PRIVATE_CFLAGS := \$(BUILD_CFLAGS)\n"
477    printf "\$(OBJ): \$(SRC_DIR)/$src\n"
478    printf "\t@echo [$build_type] CC $src\n"
479    printf "\t@mkdir -p \$\$(dirname \$@)\n"
480    printf "\t\$(hide) \$(PRIVATE_CC) \$(PRIVATE_CFLAGS) -c -o \$@ \$<\n"
481    printf "\n"
482  done
483  printf "\n"
484}
485
486# $1: library name (e.g. crypto).
487# $2: module prefix (e.g. CRYPTO).
488# $3: build type.
489# $4: source directory.
490# $5: output directory.
491build_shared_library () {
492  local name=$1
493  local prefix=$2
494  local build_type=$3
495  local src_dir="$4"
496  local out_dir="$5"
497  local shlib="lib${name}.so"
498  local build_linker=$(get_build_linker $build_type)
499  clear_sources
500  build_sources $prefix $build_type
501
502  # TODO(digit): Make the clang build link properly.
503  printf "SHLIB=\$(OUT_DIR)/$shlib\n"
504  printf "\$(SHLIB): PRIVATE_LINKER := \$(BUILD_LINKER)\n"
505  case $build_type in
506    clang-android-*)
507      printf "\$(SHLIB): PRIVATE_CRTBEGIN := \$(CRTBEGIN_SO)\n"
508      printf "\$(SHLIB): PRIVATE_CRTEND := \$(CRTEND_SO)\n"
509      ;;
510  esac
511  printf "\$(SHLIB): \$(BUILD_OBJECTS)\n"
512  printf "\t@echo [$build_type] SHARED_LIBRARY $(basename $shlib)\n"
513  printf "\t@mkdir -p \$\$(dirname \$@)\n"
514  case $build_type in
515    clang-android-*)
516      printf "\t\$(hide) \$(PRIVATE_LINKER) -nostdlib -shared -o \$@ \$(PRIVATE_CRTBEGIN) \$^ \$(PRIVATE_CRTEND)\n"
517      ;;
518    *)
519      printf "\t\$(hide) \$(PRIVATE_LINKER) -shared -o \$@ \$^\n"
520      ;;
521  esac
522  printf "\n"
523}
524
525# $1: executable name.
526# $2: module prefix (e.g. APPS).
527# $3: build type.
528# $4: source directory.
529# $5: output directory.
530# $6: dependent shared libraries (e.g. 'crypto ssl')
531build_executable () {
532  local name=$1
533  local prefix=$2
534  local build_type=$3
535  local src_dir="$4"
536  local out_dir="$5"
537  local shlibs="$6"
538  local build_linker=$(get_build_linker $build_type)
539  clear_sources
540  build_sources $prefix $build_type
541
542  # TODO(digit): Make the clang build link properly.
543  exec=$name
544  all_shlibs=
545  printf "EXEC := \$(OUT_DIR)/$name\n"
546  printf "openssl_all: \$(EXEC)\n"
547  printf "\$(EXEC): PRIVATE_LINKER := \$(BUILD_LINKER)\n"
548  printf "\$(EXEC): \$(BUILD_OBJECTS)"
549  for lib in $shlibs; do
550    printf " \$(OUT_DIR)/lib${lib}.so"
551  done
552  printf "\n"
553  printf "\t@echo [$build_type] EXECUTABLE $name\n"
554  printf "\t@mkdir -p \$\$(dirname \$@)\n"
555  printf "\t\$(hide) \$(PRIVATE_LINKER) -o \$@ \$^\n"
556  printf "\n"
557}
558
559ALL_BUILDS=
560
561generate_openssl_build () {
562  local build_type=$1
563  local out="$OUT_DIR/$build_type"
564  ALL_BUILDS="$ALL_BUILDS $build_type"
565  echo "# Build type: $build_type"
566  build_shared_library crypto CRYPTO $build_type "$SRC_DIR" "$out"
567  build_shared_library ssl SSL $build_type "$SRC_DIR" "$out"
568  build_executable openssl APPS $build_type "$SRC_DIR" "$out" "crypto ssl"
569}
570
571generate_makefile () {
572  echo \
573"# Auto-generated by $PROGDIR - do not edit
574
575.PHONY: openssl_all
576
577all: openssl_all
578
579# Use 'make V=1' to print build commands.
580ifeq (1,\$(V))
581hide :=
582else
583hide := @
584endif
585
586SRC_DIR=$SRC_DIR
587OUT_DIR=$OUT_DIR
588"
589
590  for BUILD_TYPE in $BUILD_TYPES; do
591    generate_openssl_build gcc-$BUILD_TYPE
592  done
593
594# TODO(digit): Make the Clang build run.
595#   for BUILD_TYPE in $ANDROID_BUILD_TYPES; do
596#     generate_openssl_build clang-$BUILD_TYPE
597#   done
598}
599
600. $SRC_DIR/openssl.config
601
602
603
604dump "Generating Makefile"
605log "Makefile path: $MAKEFILE"
606generate_makefile > $MAKEFILE
607
608dump "Building libraries with $NUM_JOBS jobs"
609dump "For the following builds:"
610for BUILD in $ALL_BUILDS; do
611  dump "  $BUILD"
612done
613MAKE_FLAGS="-j$NUM_JOBS"
614if [ "$VERBOSE" -gt 2 ]; then
615  MAKE_FLAGS="$MAKE_FLAGS V=1"
616fi
617run make $MAKE_FLAGS -f "$MAKEFILE" -C "$BUILD_DIR"
618case $? in
619  0)
620    dump "All OK, congratulations!"
621    ;;
622  *)
623    dump "Error, try doing the following to inspect the issues:"
624    dump "   $PROGNAME --build-dir=/tmp/mybuild"
625    dump "   make -C /tmp/mybuild V=1"
626    dump ""
627    ;;
628esac
629