Bash程序  |  464行  |  11.93 KB

#!/bin/bash
#
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

#
# This script imports new versions of scrypt (http://www.tarsnap.com/scrypt/) into the
# Android source tree.  To run, (1) fetch the appropriate tarball from the scrypt repository,
# (2) check the gpg/pgp signature, and then (3) run:
#   ./import_scrypt.sh import scrypt-*.tar.gz
#
# IMPORTANT: See README.android for additional details.

# turn on exit on error as well as a warning when it happens
set -e
set -x
trap  "echo WARNING: Exiting on non-zero subprocess exit code" ERR;

# Ensure consistent sorting order / tool output.
export LANG=C
export LC_ALL=C

export DIRNAME=$(dirname $0)

function die() {
  declare -r message=$1

  echo $message
  exit 1
}

function usage() {
  declare -r message=$1

  if [ ! "$message" = "" ]; then
    echo $message
  fi
  echo "Usage:"
  echo "  ./import_scrypt.sh import </path/to/scrypt-*.tar.gz>"
  echo "  ./import_scrypt.sh regenerate <patch/*.patch>"
  echo "  ./import_scrypt.sh generate <patch/*.patch> </path/to/scrypt-*.tar.gz>"
  exit 1
}

function main() {
  if [ ! -d patches ]; then
    die "scrypt patch directory patches/ not found"
  fi

  if [ ! -f scrypt.version ]; then
    die "scrypt.version not found"
  fi

  source $DIRNAME/scrypt.version
  if [ "$SCRYPT_VERSION" == "" ]; then
    die "Invalid scrypt.version; see README.android for more information"
  fi

  SCRYPT_DIR=scrypt-$SCRYPT_VERSION
  SCRYPT_DIR_ORIG=$SCRYPT_DIR.orig

  if [ ! -f scrypt.config ]; then
    die "scrypt.config not found"
  fi

  source $DIRNAME/scrypt.config
  if [ "$CONFIGURE_ARGS" == "" -o "$UNNEEDED_SOURCES" == "" -o "$NEEDED_SOURCES" == "" ]; then
    die "Invalid scrypt.config; see README.android for more information"
  fi

  declare -r command=$1
  shift || usage "No command specified. Try import, regenerate, or generate."
  if [ "$command" = "import" ]; then
    declare -r tar=$1
    shift || usage "No tar file specified."
    import $tar
  elif [ "$command" = "regenerate" ]; then
    declare -r patch=$1
    shift || usage "No patch file specified."
    [ -d $SCRYPT_DIR ] || usage "$SCRYPT_DIR not found, did you mean to use generate?"
    [ -d $SCRYPT_DIR_ORIG ] || usage "$SCRYPT_DIR_ORIG not found, did you mean to use generate?"
    regenerate $patch
  elif [ "$command" = "generate" ]; then
    declare -r patch=$1
    shift || usage "No patch file specified."
    declare -r tar=$1
    shift || usage "No tar file specified."
    generate $patch $tar
  else
    usage "Unknown command specified $command. Try import, regenerate, or generate."
  fi
}

# Compute the name of an assembly source file generated by one of the
# gen_asm_xxxx() functions below. The logic is the following:
# - if "$2" is not empty, output it directly
# - otherwise, change the file extension of $1 from .pl to .S and output
#   it.
# Usage: default_asm_file "$1" "$2"
#     or default_asm_file "$@"
#
# $1: generator path (perl script)
# $2: optional output file name.
function default_asm_file () {
  if [ "$2" ]; then
    echo "$2"
  else
    echo "${1%%.pl}.S"
  fi
}

# Generate an ARM assembly file.
# $1: generator (perl script)
# $2: [optional] output file name
function gen_asm_arm () {
  local OUT
  OUT=$(default_asm_file "$@")
  perl "$1" > "$OUT"
}

function gen_asm_mips () {
  local OUT
  OUT=$(default_asm_file "$@")
  # The perl scripts expect to run the target compiler as $CC to determine
  # the endianess of the target. Setting CC to true is a hack that forces the scripts
  # to generate little endian output
  CC=true perl "$1" o32 > "$OUT"
}

function gen_asm_x86 () {
  local OUT
  OUT=$(default_asm_file "$@")
  perl "$1" elf -fPIC > "$OUT"
}

function gen_asm_x86_64 () {
  local OUT
  OUT=$(default_asm_file "$@")
  perl "$1" elf "$OUT" > "$OUT"
}


# Filter all items in a list that match a given pattern.
# $1: space-separated list
# $2: egrep pattern.
# Out: items in $1 that match $2
function filter_by_egrep() {
  declare -r pattern=$1
  shift
  echo "$@" | tr ' ' '\n' | grep -e "$pattern" | tr '\n' ' '
}

# Sort and remove duplicates in a space-separated list
# $1: space-separated list
# Out: new space-separated list
function uniq_sort () {
  echo "$@" | tr ' ' '\n' | sort -u | tr '\n' ' ' | \
    sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
}

function print_autogenerated_header() {
  echo "// Auto-generated - DO NOT EDIT!"
  echo "// To regenerate, edit scrypt.config, then run:"
  echo "//     ./import_scrypt.sh import /path/to/scrypt-$SCRYPT_VERSION.tar.gz"
  echo "//"
}

# Return the value of a computed variable name.
# E.g.:
#   FOO=foo
#   BAR=bar
#   echo $(var_value FOO_$BAR)   -> prints the value of ${FOO_bar}
# $1: Variable name
# Out: variable value
var_value() {
  # Note: don't use 'echo' here, because it's sensitive to values
  #       that begin with an underscore (e.g. "-n")
  eval printf \"%s\\n\" \$$1
}

# Same as var_value, but returns sorted output without duplicates.
# $1: Variable name
# Out: variable value (if space-separated list, sorted with no duplicates)
var_sorted_value() {
  uniq_sort $(var_value $1)
}

# $1: Property name
# $2: Indent
# $3: Values
print_list_in_bp() {
  declare -r indent="$1"
  shift
  declare -r name="$1"
  shift

  if [ -n "$*" ]; then
    echo "${indent}${name}: ["
    for value in "$@"; do
      echo "${indent}    \"${value}\","
    done
    echo "${indent}],"
  fi
}

# Print an architecture-specific section
# $1: Arch
# $2: Arch variant
print_arch_section_in_bp() {
  declare -r arch="$1"
  declare -r variant="$2"

  declare -r srcs="$(var_sorted_value SCRYPT_SOURCES_${arch})"
  declare -r exclude_srcs="$(var_sorted_value SCRYPT_SOURCES_EXCLUDES_${arch})"

  declare -r srcs_variant="$(var_sorted_value SCRYPT_SOURCES_${arch}_${variant})"
  declare -r exclude_srcs_variant="$(var_sorted_value SCRYPT_SOURCES_EXCLUDES_${arch}_${variant})"

  if [ -n "${srcs}${exclude_srcs}${srcs_variant}${exclude_srcs_variant}" ]; then
    echo "        ${arch}: {"

    print_list_in_bp "            " "srcs" ${srcs}
    print_list_in_bp "            " "exclude_srcs" ${exclude_srcs}

    if [ -n "${src_variant}${exclude_srcs_variant}" ]; then
      echo "            ${variant}: {"
      print_list_in_bp "                " "srcs" ${srcs_variant}
      print_list_in_bp "                " "exclude_srcs" ${exclude_srcs_variant}
      echo "            },"
    fi

    echo "        },"
  fi
}

# Generate sources.bp from scrypt.config
#
# $1: Target file name.  (e.g. sources.bp)
function generate_android_bp() {
  declare -r output="$1"

  # Extract cflags from upstream `./configure` script
  cd $SCRYPT_DIR

  ./configure $CONFIGURE_ARGS

  declare -r tmpfile=$(mktemp)
  (grep -e -D Makefile | grep -v CONFIGURE_ARGS= | grep -v OPTIONS=) > $tmpfile

  declare -r ac_cflags=$(filter_by_egrep "^-D" $(grep -e "^CFLAG=" $tmpfile))
  declare -r ac_depflags=$(filter_by_egrep "^-D" $(grep -e "^DEPFLAG=" $tmpfile))
  rm -f $tmpfile

  cd ..

  # Generate sources.bp
  echo "Generating $(basename $output)"
  (
    print_autogenerated_header

    cat <<__EOF__

//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

cc_defaults {
    name: "libscrypt_sources",

__EOF__

    print_list_in_bp "    " "cflags" \
      $(var_sorted_value SCRYPT_CFLAGS) ${ac_cflags} ${ac_depflags}
    echo

    print_list_in_bp "    " "export_include_dirs" \
      $(var_sorted_value SCRYPT_EXPORT_C_INCLUDE_DIRS)
    echo

    print_list_in_bp "    " "local_include_dirs" \
      $(var_sorted_value SCRYPT_INCLUDES)
    echo

    print_list_in_bp "    " "srcs" $(var_sorted_value SCRYPT_SOURCES)
    echo

    echo "    arch: {"

    print_arch_section_in_bp "arm" "neon"
    print_arch_section_in_bp "arm64"
    print_arch_section_in_bp "x86"
    print_arch_section_in_bp "x86_64"
    print_arch_section_in_bp "mips"
    print_arch_section_in_bp "mips64"

    cat <<__EOF__
    },
}
__EOF__
  ) > "$output"
}

function import() {
  declare -r SCRYPT_SOURCE=$1

  untar $SCRYPT_SOURCE readonly
  applypatches $SCRYPT_DIR

  cd $SCRYPT_DIR/..

  touch MODULE_LICENSE_BSD_LIKE

  generate_android_bp "sources.bp"

  # Prune unnecessary sources
  prune

  NEEDED_SOURCES="$NEEDED_SOURCES"
  for i in $NEEDED_SOURCES; do
    echo "Updating $i"
    rm -r $i
    mv $SCRYPT_DIR/$i .
  done

  cleantar
}

function regenerate() {
  declare -r patch=$1

  generatepatch $patch
}

function generate() {
  declare -r patch=$1
  declare -r SCRYPT_SOURCE=$2

  untar $SCRYPT_SOURCE
  applypatches $SCRYPT_DIR_ORIG $patch
  prune

  for i in $NEEDED_SOURCES; do
    echo "Restoring $i"
    rm -rf $SCRYPT_DIR/$i
    cp -rf $i $SCRYPT_DIR/$i
  done

  generatepatch $patch
  cleantar
}

# Find all files in a sub-directory that are encoded in ISO-8859
# $1: Directory.
# Out: list of files in $1 that are encoded as ISO-8859.
function find_iso8859_files() {
  find $1 -type f -print0 | xargs -0 file | fgrep "ISO-8859" | cut -d: -f1
}

# Convert all ISO-8859 files in a given subdirectory to UTF-8
# $1: Directory name
function convert_iso8859_to_utf8() {
  declare -r iso_files=$(find_iso8859_files "$1")
  for iso_file in $iso_files; do
    iconv --from-code iso-8859-1 --to-code utf-8 $iso_file > $iso_file.tmp
    rm -f $iso_file
    mv $iso_file.tmp $iso_file
  done
}

function untar() {
  declare -r SCRYPT_SOURCE=$1
  declare -r readonly=$2

  # Remove old source
  cleantar

  # Process new source
  tar -zxf $SCRYPT_SOURCE
  convert_iso8859_to_utf8 $SCRYPT_DIR
  cp -rfP $SCRYPT_DIR $SCRYPT_DIR_ORIG
  if [ ! -z $readonly ]; then
    find $SCRYPT_DIR_ORIG -type f -print0 | xargs -0 chmod a-w
  fi
}

function prune() {
  echo "Removing $UNNEEDED_SOURCES"
  (cd $SCRYPT_DIR_ORIG && rm -rf $UNNEEDED_SOURCES)
  (cd $SCRYPT_DIR      && rm -r  $UNNEEDED_SOURCES)
}

function cleantar() {
  rm -rf $SCRYPT_DIR_ORIG
  rm -rf $SCRYPT_DIR
}

function applypatches () {
  declare -r dir=$1
  declare -r skip_patch=$2

  cd $dir

  # Apply appropriate patches
  for i in $SCRYPT_PATCHES; do
    if [ ! "$skip_patch" = "patches/$i" ]; then
      echo "Applying patch $i"
      patch -p1 --merge < ../patches/$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate patches/$i"
    else
      echo "Skiping patch $i"
    fi

  done

  # Cleanup patch output
  find . \( -type f -o -type l \) -name "*.orig" -print0 | xargs -0 rm -f

  cd ..
}

function generatepatch() {
  declare -r patch=$1

  # Cleanup stray files before generating patch
  find $SCRYPT_DIR -type f -name "*.orig" -print0 | xargs -0 rm -f
  find $SCRYPT_DIR -type f -name "*~" -print0 | xargs -0 rm -f

  declare -r variable_name=SCRYPT_PATCHES_`basename $patch .patch | sed s/-/_/`_SOURCES
  # http://tldp.org/LDP/abs/html/ivr.html
  eval declare -r sources=\$$variable_name
  rm -f $patch
  touch $patch
  for i in $sources; do
    LC_ALL=C TZ=UTC0 diff -aupN $SCRYPT_DIR_ORIG/$i $SCRYPT_DIR/$i >> $patch && die "ERROR: No diff for patch $path in file $i"
  done
  echo "Generated patch $patch"
  echo "NOTE To make sure there are not unwanted changes from conflicting patches, be sure to review the generated patch."
}

main $@