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