Home | History | Annotate | Download | only in mac
      1 #!/bin/sh
      2 
      3 # Copyright (c) 2012 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 # This script signs the Chromoting binaries, builds the Chrome Remote Desktop
      8 # installer and then packages it into a .dmg.  It requires that Packages be
      9 # installed (for 'packagesbuild').
     10 # Packages: http://s.sudre.free.fr/Software/Packages/about.html
     11 #
     12 # usage: do_signing.sh output_dir input_dir [codesign_keychain codesign_id
     13 #            [productsign_id]]
     14 #
     15 # The final disk image (dmg) is placed in |output_dir|.
     16 
     17 set -e -u
     18 
     19 ME="$(basename "${0}")"
     20 readonly ME
     21 
     22 declare -a g_cleanup_dirs
     23 
     24 setup() {
     25   local input_dir="${1}"
     26 
     27   # The file that contains the properties for this signing build.
     28   # The file should contain only key=value pairs, one per line.
     29   PROPS_FILENAME="${input_dir}/do_signing.props"
     30 
     31   # Individually load the properties for this build. Don't 'source' the file
     32   # to guard against code accidentally being added to the props file.
     33   DMG_VOLUME_NAME=$(read_property "DMG_VOLUME_NAME")
     34   DMG_FILE_NAME=$(read_property "DMG_FILE_NAME")
     35   HOST_BUNDLE_NAME=$(read_property "HOST_BUNDLE_NAME")
     36   HOST_PKG=$(read_property "HOST_PKG")
     37   HOST_UNINSTALLER_NAME=$(read_property "HOST_UNINSTALLER_NAME")
     38   NATIVE_MESSAGING_HOST_BUNDLE_NAME=$(read_property\
     39     "NATIVE_MESSAGING_HOST_BUNDLE_NAME")
     40   PREFPANE_BUNDLE_NAME=$(read_property "PREFPANE_BUNDLE_NAME")
     41   REMOTE_ASSISTANCE_HOST_BUNDLE_NAME=$(read_property\
     42     "REMOTE_ASSISTANCE_HOST_BUNDLE_NAME")
     43 
     44   # Binaries to sign.
     45   ME2ME_HOST="PrivilegedHelperTools/${HOST_BUNDLE_NAME}"
     46   ME2ME_NM_HOST="PrivilegedHelperTools/${HOST_BUNDLE_NAME}/Contents/MacOS/"`
     47                 `"${NATIVE_MESSAGING_HOST_BUNDLE_NAME}/Contents/MacOS/"`
     48                 `"native_messaging_host"
     49   IT2ME_NM_HOST="PrivilegedHelperTools/${HOST_BUNDLE_NAME}/Contents/MacOS/"`
     50                 `"${REMOTE_ASSISTANCE_HOST_BUNDLE_NAME}/Contents/MacOS/"`
     51                 `"remote_assistance_host"
     52   UNINSTALLER="Applications/${HOST_UNINSTALLER_NAME}.app"
     53   PREFPANE="PreferencePanes/${PREFPANE_BUNDLE_NAME}"
     54 
     55   # The Chromoting Host installer is a meta-package that consists of 3
     56   # components:
     57   #  * Chromoting Host Service package
     58   #  * Chromoting Host Uninstaller package
     59   #  * Keystone package (GoogleSoftwareUpdate - for Official builds only)
     60   PKGPROJ_HOST="ChromotingHost.pkgproj"
     61   PKGPROJ_HOST_SERVICE="ChromotingHostService.pkgproj"
     62   PKGPROJ_HOST_UNINSTALLER="ChromotingHostUninstaller.pkgproj"
     63 
     64   # Final (user-visible) pkg name.
     65   PKG_FINAL="${HOST_PKG}.pkg"
     66 
     67   DMG_FILE_NAME="${DMG_FILE_NAME}.dmg"
     68 
     69   # Temp directory for Packages output.
     70   PKG_DIR=build
     71   g_cleanup_dirs+=("${PKG_DIR}")
     72 
     73   # Temp directories for building the dmg.
     74   DMG_TEMP_DIR="$(mktemp -d -t "${ME}"-dmg)"
     75   g_cleanup_dirs+=("${DMG_TEMP_DIR}")
     76 
     77   DMG_EMPTY_DIR="$(mktemp -d -t "${ME}"-empty)"
     78   g_cleanup_dirs+=("${DMG_EMPTY_DIR}")
     79 }
     80 
     81 err() {
     82   echo "[$(date +'%Y-%m-%d %H:%M:%S%z')]: ${@}" >&2
     83 }
     84 
     85 err_exit() {
     86   err "${@}"
     87   exit 1
     88 }
     89 
     90 # shell_safe_path ensures that |path| is safe to pass to tools as a
     91 # command-line argument. If the first character in |path| is "-", "./" is
     92 # prepended to it. The possibly-modified |path| is output.
     93 shell_safe_path() {
     94   local path="${1}"
     95   if [[ "${path:0:1}" = "-" ]]; then
     96     echo "./${path}"
     97   else
     98     echo "${path}"
     99   fi
    100 }
    101 
    102 # Read a single property from the properties file.
    103 read_property() {
    104   local property="${1}"
    105   local filename="${PROPS_FILENAME}"
    106   echo `grep "\<${property}\>=" "${filename}" | tail -n 1 | cut -d "=" -f2-`
    107 }
    108 
    109 verify_clean_dir() {
    110   local dir="${1}"
    111   if [[ ! -d "${dir}" ]]; then
    112     mkdir "${dir}"
    113   fi
    114 
    115   if [[ -e "${output_dir}/${DMG_FILE_NAME}" ]]; then
    116     err "Output directory is dirty from previous build."
    117     exit 1
    118   fi
    119 }
    120 
    121 sign() {
    122   local name="${1}"
    123   local keychain="${2}"
    124   local id="${3}"
    125 
    126   if [[ ! -e "${name}" ]]; then
    127     err_exit "Input file doesn't exist: ${name}"
    128   fi
    129 
    130   echo Signing "${name}"
    131   codesign -vv -s "${id}" --keychain "${keychain}" "${name}"
    132   codesign -v "${name}"
    133 }
    134 
    135 sign_binaries() {
    136   local input_dir="${1}"
    137   local keychain="${2}"
    138   local id="${3}"
    139 
    140   sign "${input_dir}/${ME2ME_NM_HOST}" "${keychain}" "${id}"
    141   sign "${input_dir}/${IT2ME_NM_HOST}" "${keychain}" "${id}"
    142   sign "${input_dir}/${ME2ME_HOST}" "${keychain}" "${id}"
    143   sign "${input_dir}/${UNINSTALLER}" "${keychain}" "${id}"
    144   sign "${input_dir}/${PREFPANE}" "${keychain}" "${id}"
    145 }
    146 
    147 sign_installer() {
    148   local input_dir="${1}"
    149   local keychain="${2}"
    150   local id="${3}"
    151 
    152   local package="${input_dir}/${PKG_DIR}/${PKG_FINAL}"
    153   productsign --sign "${id}" --keychain "${keychain}" \
    154       "${package}" "${package}.signed"
    155   mv -f "${package}.signed" "${package}"
    156 }
    157 
    158 build_package() {
    159   local pkg="${1}"
    160   echo "Building .pkg from ${pkg}"
    161   packagesbuild -v "${pkg}"
    162 }
    163 
    164 build_packages() {
    165   local input_dir="${1}"
    166   build_package "${input_dir}/${PKGPROJ_HOST_SERVICE}"
    167   build_package "${input_dir}/${PKGPROJ_HOST_UNINSTALLER}"
    168   build_package "${input_dir}/${PKGPROJ_HOST}"
    169 }
    170 
    171 build_dmg() {
    172   local input_dir="${1}"
    173   local output_dir="${2}"
    174 
    175   # Create the .dmg.
    176   echo "Building .dmg..."
    177   "${input_dir}/pkg-dmg" \
    178       --format UDBZ \
    179       --tempdir "${DMG_TEMP_DIR}" \
    180       --source "${DMG_EMPTY_DIR}" \
    181       --target "${output_dir}/${DMG_FILE_NAME}" \
    182       --volname "${DMG_VOLUME_NAME}" \
    183       --copy "${input_dir}/${PKG_DIR}/${PKG_FINAL}" \
    184       --copy "${input_dir}/Scripts/keystone_install.sh:/.keystone_install"
    185 
    186   if [[ ! -f "${output_dir}/${DMG_FILE_NAME}" ]]; then
    187     err_exit "Unable to create disk image: ${DMG_FILE_NAME}"
    188   fi
    189 }
    190 
    191 cleanup() {
    192   if [[ "${#g_cleanup_dirs[@]}" > 0 ]]; then
    193     rm -rf "${g_cleanup_dirs[@]}"
    194   fi
    195 }
    196 
    197 usage() {
    198   echo "Usage: ${ME} output_dir input_dir [keychain codesign_id"\
    199       "[productsign_id]]" >&2
    200   echo "  Sign the binaries using the specified <codesign_id>, build" >&2
    201   echo "  the installer and then sign the installer using the given" >&2
    202   echo "  <productsign_id>." >&2
    203   echo "  If the <keychain> and signing ids are not specified then the" >&2
    204   echo "  installer is built without signing any binaries." >&2
    205 }
    206 
    207 main() {
    208   local output_dir="$(shell_safe_path "${1}")"
    209   local input_dir="$(shell_safe_path "${2}")"
    210   local do_sign_binaries=0
    211   local keychain=""
    212   if [[ ${#} -ge 3 ]]; then
    213     keychain="$(shell_safe_path "${3}")"
    214     do_sign_binaries=1
    215     echo "Signing binaries using ${keychain}"
    216   else
    217     echo "Not signing binaries (no keychain or identify specified)"
    218   fi
    219   local codesign_id=""
    220   if [[ ${#} -ge 4 ]]; then
    221     codesign_id="${4}"
    222   fi
    223   local productsign_id=""
    224   if [[ ${#} -ge 5 ]]; then
    225     productsign_id="${5}"
    226   fi
    227 
    228   if [[ "${do_sign_binaries}" == 1 && -z "${codesign_id}" ]]; then
    229     err_exit "Can't sign binaries - please specify a codesign_id"
    230   fi
    231 
    232   setup "${input_dir}"
    233   verify_clean_dir "${output_dir}"
    234 
    235   if [[ "${do_sign_binaries}" == 1 ]]; then
    236     sign_binaries "${input_dir}" "${keychain}" "${codesign_id}"
    237   fi
    238   build_packages "${input_dir}"
    239   if [[ "${do_sign_binaries}" == 1 && -n "${productsign_id}" ]]; then
    240     echo "Signing installer..."
    241     sign_installer "${input_dir}" "${keychain}" "${productsign_id}"
    242   fi
    243   build_dmg "${input_dir}" "${output_dir}"
    244 
    245   cleanup
    246 }
    247 
    248 if [[ ${#} < 2 ]]; then
    249   usage
    250   exit 1
    251 fi
    252 
    253 main "${@}"
    254 exit ${?}
    255