1#!/bin/sh
2#
3# Copyright (c) 2013 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7# Sanitize environment.
8set -e
9export LANG=C
10export LC_ALL=C
11
12PROGDIR=$(dirname "$0")
13PROGNAME=$(basename "$0")
14
15# Defaults
16VERBOSE=1
17
18. "$PROGDIR/openssl-chromium.config"
19
20# Print error message and exit script.
21# $1+: Error message.
22panic () {
23  echo "ERROR: $@"
24  exit 1
25}
26
27# $1: Variable name
28# Output: variable value.
29var_value () {
30  # NOTE: Don't use 'echo' here, it is sensitive to options like -n.
31  eval printf \"%s\\n\" \$$1
32}
33
34# Print a message if verbosity is sufficiently high.
35# $1: Verbosity threshold, only if '$VERBOSE > $1' does this print.
36# $2+: Message.
37dump_n () {
38  local LEVEL=$1
39  shift
40  if [ "$VERBOSE" -gt "$LEVEL" ]; then
41    printf "%s\n" "$@"
42  fi
43}
44
45# Print a message, unless --quiet was used.
46dump () {
47  dump_n 0 "$@"
48}
49
50# Print a message if --verbose was used.
51log () {
52  dump_n 1 "$@"
53}
54
55# Print a message if --verbose --verbose was used.
56log2 () {
57  dump_n 2 "$@"
58}
59
60# Run a command silently, unless --verbose is used.
61# More specifically:
62#  - By default, this runs the command but redirects its stdout/stderr
63#    to /dev/null to avoid printing anything.
64#  - If --verbose is used, this prints the command's name, and stderr
65#    will not be redirected.
66#  - If '--verbose --verbose' is used, this prints the commands and its
67#    complete output.
68# $1+: Command
69# Return: Command status
70run () {
71  if [ "$VERBOSE" -gt 1 ]; then
72    echo "COMMAND: $@"
73  fi
74  case $VERBOSE in
75    0)
76        "$@" > /dev/null 2>&1
77        ;;
78    1)
79        "$@" > /dev/null
80        ;;
81    *)
82        "$@"
83        ;;
84  esac
85}
86
87# Support cleaning up stuff when the script exits, even in case of
88# error.
89_ALL_CLEANUPS=
90
91clean_atexit () {
92  local CLEANUPS CLEANUP
93  CLEANUPS=$_ALL_CLEANUPS
94  _ALL_CLEANUPS=
95  for CLEANUP in $CLEANUPS; do
96    ($CLEANUP)
97  done
98  exit $1
99}
100
101trap "clean_atexit 0" EXIT
102trap "clean_atexit \$?" HUP INT QUIT TERM
103
104# Add a cleanup function to the list of cleanups that will be run when
105# the script exits.
106atexit () {
107    # Prepend to ensure that the cleanup steps are performed in reverse
108    # order or registration.
109    _ALL_CLEANUPS="$* $_ALL_CLEANUPS"
110}
111
112# Support code to write into a gyp file
113_GYP_MARGIN=""
114
115# Increment margin of gyp printer.
116incr_gyp_margin () {
117  _GYP_MARGIN="$_GYP_MARGIN  "
118}
119
120decr_gyp_margin () {
121  _GYP_MARGIN=$(echo "$_GYP_MARGIN" | cut --bytes=3-)
122}
123
124print_gyp () {
125  printf "%s%s\n" "$_GYP_MARGIN" "$@"
126}
127
128# This prints a list variable definition in a gyp file.
129# $1: Variable name (e.g. 'openssl_common_defines')
130# $2+: List items (e.g. defines)
131print_gyp_variable () {
132  local VARNAME=$1
133  local VALUE
134  shift
135  print_gyp "'$VARNAME': ["
136  for VALUE; do
137    print_gyp "  '$VALUE',"
138  done
139  print_gyp "],"
140}
141
142# Same as print_gyp_variable, but for source file lists, this
143# prepends openssl/ as required by the Chromium build to each item
144# in the list.
145# $1: Variable name (e.g. 'openssl_common_sources')
146# $2+: List items (source file names).
147print_gyp_source_variable () {
148  local VARNAME=$1
149  local VALUE
150  shift
151  print_gyp "'$VARNAME': ["
152  for VALUE; do
153    print_gyp "  'openssl/$VALUE',"
154  done
155  print_gyp "],"
156}
157
158# Print usage instructions.
159usage () {
160    echo \
161"Usage: $PROGNAME [options]
162
163This script is used to regenerate the content of the Chromium
164third_party/openssl/ directory according to the configuration file
165named 'openssl-chromium.config'.
166
167In particular, it will perform the following steps:
168
169  1) Download the Android sources from the AOSP git servers.
170
171  2) Add Chromium-specific patches to the Android source tree.
172     (they must be under patches.chromium/ in $PROGDIR).
173
174  3) Download a versioned openssl package from the official OpenSSL
175     servers, and check its MD5. The version is taken from the
176     'openssl.version' file in the Android source tree.
177
178  4) Run the Android 'import_openssl.sh' script that rebuilds all sources
179     from a clean slate.
180
181  5) Generate the 'openssl.gypi' that contains gyp-specific declarations
182     for the library.
183
184  6) Generate 64-bit compatible opensslconf.h header.
185
186Valid options are the following (defaults are in brackets):
187
188  --help|-h|-?          Display this message.
189  --aosp-git=<url>      Change git source for Android repository.
190                        [$ANDROID_OPENSSL_GIT_SOURCE]
191  --aosp-commit=<name>  Specify git commit or branch name [$ANDROID_OPENSSL_GIT_COMMIT]
192  --temp-dir=<path>     Specify temporary directory, will not be cleaned.
193                        [<random-temp-file-cleaned-on-exit>]
194  --verbose             Increase verbosity.
195  --quiet               Decrease verbosity.
196"
197  exit 1
198}
199
200# Parse command-line.
201DO_HELP=
202
203for OPT; do
204  OPTARG=$()
205  case $OPT in
206    --help|-h|-?)
207        DO_HELP=true
208        ;;
209    --aosp-commit=*)
210        ANDROID_OPENSSL_GIT_COMMIT=${OPT#--aosp-commit=}
211        if [ -z "$ANDROID_OPENSSL_GIT_COMMIT" ]; then
212            panic "Missing option value: $OPT"
213        fi
214        ;;
215    --aosp-git=*)
216        ANDROID_OPENSSL_GIT_SOURCE=${OPT#--aosp-git=}
217        if [ -z "$ANDROID_OPENSSL_GIT_SOURCE" ]; then
218            panic "Missing option value: $OPT"
219        fi
220        ;;
221    --temp-dir=*)
222        TEMP_DIR=${OPT#--temp-dir=}
223        if [ -z "$TEMP_DIR" ]; then
224            panic "Missing option value: $OPT"
225        fi
226        ;;
227    --quiet)
228        VERBOSE=$(( $VERBOSE - 1 ))
229        ;;
230    --verbose)
231        VERBOSE=$(( $VERBOSE + 1 ))
232        ;;
233    -*)
234        panic "Invalid option '$OPT', see --help for details."
235        ;;
236    *)
237        panic "This script doesn't take parameters. See --help for details."
238        ;;
239  esac
240done
241
242if [ "$DO_HELP" ]; then
243  usage
244fi
245
246# Create temporary directory. Ensure it's always cleaned up on exit.
247if [ -z "$TEMP_DIR" ]; then
248  TEMP_DIR=$(mktemp -d)
249  clean_tempdir () {
250    rm -rf "$TEMP_DIR"
251  }
252  atexit clean_tempdir
253  log "Temporary directory created: $TEMP_DIR"
254else
255  log "Using user-provided temp directory: $TEMP_DIR"
256fi
257
258GIT_FLAGS=
259case $VERBOSE in
260  0|1)
261    GIT_CLONE_FLAGS="--quiet"
262    GIT_CHECKOUT_FLAGS="--quiet"
263    CURL_FLAGS="-s"
264    ;;
265  2)
266    GIT_CLONE_FLAGS=""
267    GIT_CHECKOUT_FLAGS=""
268    CURL_FLAGS=""
269    ;;
270  *)
271    GIT_CLONE_FLAGS="--verbose"
272    GIT_CHECKOUT_FLAGS=""
273    CURL_FLAGS=""
274    ;;
275esac
276
277BUILD_DIR=$TEMP_DIR/build
278mkdir -p "$BUILD_DIR" && rm -rf "$BUILD_DIR"/*
279
280# Download the Android sources.
281ANDROID_SRC_DIR=$BUILD_DIR/android-openssl
282dump "Downloading Android sources"
283log "Downloading branch $ANDROID_OPENSSL_GIT_COMMIT from: $ANDROID_OPENSSL_GIT_SOURCE"
284(
285  run mkdir -p $ANDROID_SRC_DIR
286  run cd $ANDROID_SRC_DIR
287  run git clone $GIT_CLONE_FLAGS $ANDROID_OPENSSL_GIT_SOURCE .
288  run git checkout $GIT_CHECKOUT_FLAGS $ANDROID_OPENSSL_GIT_COMMIT
289  run rm -rf .git
290)
291
292# Apply chromium-specific patches located in patches.chromium
293CHROMIUM_PATCHES_DIR=$PROGDIR/patches.chromium
294if [ ! -d "$CHROMIUM_PATCHES_DIR" ]; then
295  dump "No Chromium-specific patches to apply."
296else
297  dump "Applying Chromium-specific patches:"
298  CHROMIUM_PATCHES=$(/bin/ls $CHROMIUM_PATCHES_DIR/*.patch 2>/dev/null)
299  for CHROMIUM_PATCH in $CHROMIUM_PATCHES; do
300    dump "Applying: $CHROMIUM_PATCH"
301    (cd $ANDROID_SRC_DIR && run patch -p1) < $CHROMIUM_PATCH
302  done
303fi
304
305# Get the openssl version
306. $ANDROID_SRC_DIR/openssl.version
307if [ -z "$OPENSSL_VERSION" ]; then
308  panic "Could not find OPENSSL_VERSION definition from $ANDROID_SRC_DIR!"
309fi
310dump "Found OpenSSL version: $OPENSSL_VERSION"
311
312# Download OpenSSL package
313DOWNLOAD_DIR=$BUILD_DIR/download
314mkdir -p "$DOWNLOAD_DIR"
315
316OPENSSL_PACKAGE=openssl-$OPENSSL_VERSION.tar.gz
317dump "Downloading $OPENSSL_PACKAGE from $OPENSSL_TAR_SOURCE"
318run curl $CURL_FLAGS -o $DOWNLOAD_DIR/$OPENSSL_PACKAGE $OPENSSL_TAR_SOURCE/$OPENSSL_PACKAGE
319run curl $CURL_FLAGS -o $DOWNLOAD_DIR/$OPENSSL_PACKAGE.md5 $OPENSSL_TAR_SOURCE/$OPENSSL_PACKAGE.md5
320
321OPENSSL_SHA1_DOWNLOADED=$(sha1sum $DOWNLOAD_DIR/$OPENSSL_PACKAGE | cut -d" " -f1)
322OPENSSL_SHA1_EXPECTED=$OPENSSL_TAR_SHA1
323if [ "$OPENSSL_SHA1_DOWNLOADED" != "$OPENSSL_SHA1_EXPECTED" ]; then
324  echo "ERROR: Content mismatch for downloaded OpenSSL package:"
325  echo "       Downloaded SHA-1: $OPENSSL_SHA1_DOWNLOADED"
326  echo "       Expected SHA-1  : $OPENSSL_SHA1_EXPECTED"
327  exit 1
328fi
329dump "Checking content of downloaded package: ok"
330
331# The import_openssl.sh script will really remove the existing 'openssl'
332# directory and replace it with something completely new. This is a problem
333# when using subversion because this also gets rid of all .svn
334# subdirectories. This makes it impossible to commit the right set of
335# changes with "gcl commit".
336#
337# To work-around this, copy all the .svn subdirectories into a temporary
338# tarball, which will be extracted after the import process.
339#
340dump "Saving .svn subdirectories"
341SVN_LIST_FILE=$BUILD_DIR/svn-subdirs
342run find . -type d -name ".svn" > $SVN_LIST_FILE
343SAVED_SVN_TARBALL=$BUILD_DIR/saved-svn-subdirs.tar.gz
344run tar czf $SAVED_SVN_TARBALL -T $SVN_LIST_FILE
345
346# Re-run the import_openssl.sh script.
347dump "Re-running the 'import_openssl.sh' script to reconfigure all sources."
348(
349  cd $ANDROID_SRC_DIR
350  run ./import_openssl.sh import $DOWNLOAD_DIR/$OPENSSL_PACKAGE
351)
352
353dump "Copying new Android sources to final location."
354clean_openssl_new () {
355  rm -rf "$PROGDIR/openssl.new"
356}
357atexit clean_openssl_new
358
359run cp -rp "$ANDROID_SRC_DIR" "$PROGDIR/openssl.new"
360run mv "$PROGDIR/openssl" "$PROGDIR/openssl.old"
361run mv "$PROGDIR/openssl.new" "$PROGDIR/openssl"
362run rm -rf "$PROGDIR/openssl.old"
363
364dump "Restoring .svn subdirectores"
365run tar xzf $SAVED_SVN_TARBALL
366
367# Extract list of source files or compiler defines from openssl.config
368# variable definition. This assumes that the lists are in variables that
369# are named as <prefix><suffix> or <prefix><suffix><arch>.
370#
371# A few examples:
372#   get_gyp_list "FOO BAR" _SOURCES
373#     -> returns '$FOO_SOURCES $BAR_SOURCES'
374#
375#   get_gyp_list FOO _SOURCES_ "arm x86"
376#     -> returns '$FOO_SOURCES_arm $FOO_SOURCES_x86"
377#
378#   get_gyp_list "FOO BAR" _SOURCES_ "arm x86"
379#     -> returns '$FOO_SOURCES_arm $FOO_SOURCES_x86 $BAR_SOURCES_arm $BAR_SOURCES_x86'
380#
381# $1: list of variable prefixes
382# $2: variable suffix
383# $3: optional list of architectures.
384get_gyp_list () {
385  local ALL_PREFIXES="$1"
386  local SUFFIX="$2"
387  local ALL_ARCHS="$3"
388  local LIST PREFIX ARCH
389  for PREFIX in $ALL_PREFIXES; do
390    if [ "$ALL_ARCHS" ]; then
391      for ARCH in $ALL_ARCHS; do
392        LIST="$LIST $(var_value ${PREFIX}${SUFFIX}${ARCH})"
393      done
394    else
395      LIST="$LIST $(var_value ${PREFIX}${SUFFIX})"
396    fi
397  done
398  echo "$LIST"
399}
400
401generate_gyp_file () {
402  echo "# Auto-generated file - DO NOT EDIT"
403  echo "# To regenerate - run import_from_android.sh."
404  echo "# See 'import_from_android.sh --help' for details."
405
406  local ALL_PREFIXES="OPENSSL_CRYPTO OPENSSL_SSL"
407  local ALL_ARCHS="arm mips x86 x86_64 mac_ia32"
408  local PREFIX ARCH LIST
409
410  print_gyp "{"
411  incr_gyp_margin
412
413    print_gyp "'variables': {"
414    incr_gyp_margin
415
416      # First, the common sources and defines
417      print_gyp_source_variable "openssl_common_sources" \
418          $(get_gyp_list "$ALL_PREFIXES" _SOURCES)
419
420      print_gyp_variable "openssl_common_defines" \
421          $(get_gyp_list "$ALL_PREFIXES" _DEFINES)
422
423      # Now, conditions section with add architecture-specific sub-sections.
424      for ARCH in $ALL_ARCHS; do
425        # Convert ARCH to gyp-specific architecture name
426        case $ARCH in
427          x86)
428            GYP_ARCH=ia32
429            ;;
430          x86_64)
431            GYP_ARCH=x64
432            ;;
433          *)
434            GYP_ARCH=$ARCH
435            ;;
436        esac
437
438        print_gyp_source_variable "openssl_${ARCH}_source_excludes" \
439            $(get_gyp_list "$ALL_PREFIXES" _SOURCES_EXCLUDES_ $ARCH)
440
441        print_gyp_source_variable "openssl_${ARCH}_sources" \
442            $(get_gyp_list "$ALL_PREFIXES" _SOURCES_ $ARCH)
443
444        print_gyp_variable "openssl_${ARCH}_defines" \
445            $(get_gyp_list "$ALL_PREFIXES" _DEFINES_ $ARCH)
446
447      done # for ARCH
448
449    decr_gyp_margin
450    print_gyp "}"  # variables
451
452  decr_gyp_margin
453  print_gyp "}"  # top-level dict.
454}
455
456dump "Generating 64-bit configuration header file."
457mkdir -p $PROGDIR/config/x64/openssl/
458sed \
459  -e 's|^#define RC4_INT unsigned char|#define RC4_INT unsigned int|g' \
460  -e 's|^#define BN_LLONG|#undef BN_LLONG|g' \
461  -e 's|^#define THIRTY_TWO_BIT|#undef THIRTY_TWO_BIT|g' \
462  -e 's|^#undef SIXTY_FOUR_BIT_LONG|#define SIXTY_FOUR_BIT_LONG|g' \
463  -e 's|^#define BF_PTR|#undef BF_PTR|g' \
464  $PROGDIR/openssl/include/openssl/opensslconf.h \
465  > $PROGDIR/config/x64/openssl/opensslconf.h
466
467dump "Generating OS X 32-bit configuration header file."
468mkdir -p $PROGDIR/config/mac/ia32/openssl/
469sed \
470  -e '4a#ifndef OPENSSL_SYSNAME_MACOSX\n# define OPENSSL_SYSNAME_MACOSX\n#endif' \
471  -e 's|^#define RC4_INT unsigned char|#define RC4_INT unsigned int|g' \
472  -e 's|^#define DES_LONG unsigned int|#define DES_LONG unsigned long|g' \
473  $PROGDIR/openssl/include/openssl/opensslconf.h \
474  > $PROGDIR/config/mac/ia32/openssl/opensslconf.h
475
476dump "Generating .gypi file."
477. $ANDROID_SRC_DIR/openssl.config
478generate_gyp_file > $PROGDIR/openssl.gypi.new
479run mv $PROGDIR/openssl.gypi $PROGDIR/openssl.gypi.old
480run mv $PROGDIR/openssl.gypi.new $PROGDIR/openssl.gypi
481run rm $PROGDIR/openssl.gypi.old
482
483dump "Done."
484