michael@0: #!/bin/bash michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: # michael@0: # This tool generates incremental update packages for the update system. michael@0: # Author: Darin Fisher michael@0: # michael@0: michael@0: . $(dirname "$0")/common.sh michael@0: michael@0: # ----------------------------------------------------------------------------- michael@0: michael@0: print_usage() { michael@0: notice "Usage: $(basename $0) [OPTIONS] ARCHIVE FROMDIR TODIR" michael@0: notice "" michael@0: notice "The differences between FROMDIR and TODIR will be stored in ARCHIVE." michael@0: notice "" michael@0: notice "Options:" michael@0: notice " -h show this help text" michael@0: notice " -f clobber this file in the installation" michael@0: notice " Must be a path to a file to clobber in the partial update." michael@0: notice " -q be less verbose" michael@0: notice "" michael@0: } michael@0: michael@0: check_for_forced_update() { michael@0: force_list="$1" michael@0: forced_file_chk="$2" michael@0: michael@0: local f michael@0: michael@0: if [ "$forced_file_chk" = "precomplete" ]; then michael@0: ## "true" *giggle* michael@0: return 0; michael@0: fi michael@0: michael@0: if [ "${forced_file_chk##*.}" = "chk" ] michael@0: then michael@0: ## "true" *giggle* michael@0: return 0; michael@0: fi michael@0: michael@0: for f in $force_list; do michael@0: #echo comparing $forced_file_chk to $f michael@0: if [ "$forced_file_chk" = "$f" ]; then michael@0: ## "true" *giggle* michael@0: return 0; michael@0: fi michael@0: michael@0: # If the file in the skip list ends with /*, do a prefix match. michael@0: # This allows TorBrowser/Data/Browser/profile.default/extensions/https-everywhere@eff.org/* to be used to force all HTTPS Everywhere files to be updated. michael@0: f_suffix=${f##*/} michael@0: if [[ $f_suffix = "*" ]]; then michael@0: f_prefix="${f%\/\*}"; michael@0: if [[ $forced_file_chk == $f_prefix* ]]; then michael@0: ## 0 means "true" michael@0: return 0; michael@0: fi michael@0: fi michael@0: done michael@0: ## 'false'... because this is bash. Oh yay! michael@0: return 1; michael@0: } michael@0: michael@0: if [ $# = 0 ]; then michael@0: print_usage michael@0: exit 1 michael@0: fi michael@0: michael@0: # The last .xpi is NoScript. michael@0: ext_path='TorBrowser/Data/Browser/profile.default/extensions'; michael@0: # TODO: it would be better to pass this as a command line option. michael@0: exts='https-everywhere@eff.org/* tor-launcher@torproject.org.xpi torbutton@torproject.org.xpi uriloader@pdf.js.xpi {73a6fe31-595d-460b-a920-fcc0f8843232}.xpi' michael@0: requested_forced_updates='Contents/MacOS/TorBrowser.app/Contents/MacOS/firefox' michael@0: for ext in $exts; do michael@0: requested_forced_updates="$requested_forced_updates $ext_path/$ext" michael@0: done michael@0: michael@0: michael@0: # TODO: it would be better to pass this as a command line option. michael@0: directories_to_remove='TorBrowser/Data/Browser/profile.default/extensions/https-everywhere@eff.org' michael@0: michael@0: while getopts "hqf:" flag michael@0: do michael@0: case "$flag" in michael@0: h) print_usage; exit 0 michael@0: ;; michael@0: q) QUIET=1 michael@0: ;; michael@0: f) requested_forced_updates="$requested_forced_updates $OPTARG" michael@0: ;; michael@0: ?) print_usage; exit 1 michael@0: ;; michael@0: esac michael@0: done michael@0: michael@0: # ----------------------------------------------------------------------------- michael@0: michael@0: let arg_start=$OPTIND-1 michael@0: shift $arg_start michael@0: michael@0: archive="$1" michael@0: olddir="$2" michael@0: newdir="$3" michael@0: # Prevent the workdir from being inside the targetdir so it isn't included in michael@0: # the update mar. michael@0: if [ $(echo "$newdir" | grep -c '\/$') = 1 ]; then michael@0: # Remove the / michael@0: newdir=$(echo "$newdir" | sed -e 's:\/$::') michael@0: fi michael@0: workdir="$newdir.work" michael@0: updatemanifestv2="$workdir/updatev2.manifest" michael@0: updatemanifestv3="$workdir/updatev3.manifest" michael@0: archivefiles="updatev2.manifest updatev3.manifest" michael@0: michael@0: mkdir -p "$workdir" michael@0: michael@0: # On Mac, the precomplete file added by Bug 386760 will cause OS X to reload the michael@0: # Info.plist so it launches the right architecture, bug 600098 michael@0: michael@0: # Generate a list of all files in the target directory. michael@0: pushd "$olddir" michael@0: if test $? -ne 0 ; then michael@0: exit 1 michael@0: fi michael@0: michael@0: list_files oldfiles michael@0: list_symlinks oldsymlinks oldsymlink_targets michael@0: list_dirs olddirs michael@0: michael@0: popd michael@0: michael@0: pushd "$newdir" michael@0: if test $? -ne 0 ; then michael@0: exit 1 michael@0: fi michael@0: michael@0: if [ ! -f "precomplete" ]; then michael@0: notice "precomplete file is missing!" michael@0: exit 1 michael@0: fi michael@0: michael@0: list_dirs newdirs michael@0: list_files newfiles michael@0: list_symlinks newsymlinks newsymlink_targets michael@0: michael@0: popd michael@0: michael@0: # Add the type of update to the beginning of the update manifests. michael@0: notice "" michael@0: notice "Adding type instruction to update manifests" michael@0: > "$updatemanifestv2" michael@0: > "$updatemanifestv3" michael@0: notice " type partial" michael@0: echo "type \"partial\"" >> "$updatemanifestv2" michael@0: echo "type \"partial\"" >> "$updatemanifestv3" michael@0: michael@0: # If removal of any old, existing directories is desired, emit the appropriate michael@0: # rmrfdir commands. michael@0: notice "" michael@0: notice "Adding directory removal instructions to update manifests" michael@0: for dir_to_remove in $directories_to_remove; do michael@0: # rmrfdir requires a trailing slash, so add one if missing. michael@0: if ! [[ "$dir_to_remove" =~ /$ ]]; then michael@0: dir_to_remove="${dir_to_remove}/" michael@0: fi michael@0: echo "rmrfdir \"$dir_to_remove\"" >> "$updatemanifestv2" michael@0: echo "rmrfdir \"$dir_to_remove\"" >> "$updatemanifestv3" michael@0: done michael@0: michael@0: notice "" michael@0: notice "Adding file patch and add instructions to update manifests" michael@0: michael@0: num_oldfiles=${#oldfiles[*]} michael@0: remove_array= michael@0: num_removes=0 michael@0: michael@0: for ((i=0; $i<$num_oldfiles; i=$i+1)); do michael@0: f="${oldfiles[$i]}" michael@0: michael@0: # This file is created by Talkback, so we can ignore it michael@0: if [ "$f" = "readme.txt" ]; then michael@0: continue 1 michael@0: fi michael@0: michael@0: # removed-files is excluded by make_incremental_updates.py so it is excluded michael@0: # here for consistency. michael@0: if [ "`basename "$f"`" = "removed-files" ]; then michael@0: continue 1 michael@0: fi michael@0: michael@0: # If this file exists in the new directory as well, then check if it differs. michael@0: if [ -f "$newdir/$f" ]; then michael@0: michael@0: if check_for_add_if_not_update "$f"; then michael@0: # The full workdir may not exist yet, so create it if necessary. michael@0: mkdir -p `dirname "$workdir/$f"` michael@0: $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f" michael@0: copy_perm "$newdir/$f" "$workdir/$f" michael@0: make_add_if_not_instruction "$f" "$updatemanifestv3" michael@0: archivefiles="$archivefiles \"$f\"" michael@0: continue 1 michael@0: fi michael@0: michael@0: if check_for_forced_update "$requested_forced_updates" "$f"; then michael@0: # The full workdir may not exist yet, so create it if necessary. michael@0: mkdir -p `dirname "$workdir/$f"` michael@0: $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f" michael@0: copy_perm "$newdir/$f" "$workdir/$f" michael@0: make_add_instruction "$f" "$updatemanifestv2" "$updatemanifestv3" 1 michael@0: archivefiles="$archivefiles \"$f\"" michael@0: continue 1 michael@0: fi michael@0: michael@0: if ! diff "$olddir/$f" "$newdir/$f" > /dev/null; then michael@0: # Compute both the compressed binary diff and the compressed file, and michael@0: # compare the sizes. Then choose the smaller of the two to package. michael@0: dir=$(dirname "$workdir/$f") michael@0: mkdir -p "$dir" michael@0: notice "diffing \"$f\"" michael@0: $MBSDIFF "$olddir/$f" "$newdir/$f" "$workdir/$f.patch" michael@0: $BZIP2 -z9 "$workdir/$f.patch" michael@0: $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f" michael@0: copy_perm "$newdir/$f" "$workdir/$f" michael@0: patchfile="$workdir/$f.patch.bz2" michael@0: patchsize=$(get_file_size "$patchfile") michael@0: fullsize=$(get_file_size "$workdir/$f") michael@0: michael@0: if [ $patchsize -lt $fullsize ]; then michael@0: make_patch_instruction "$f" "$updatemanifestv2" "$updatemanifestv3" michael@0: mv -f "$patchfile" "$workdir/$f.patch" michael@0: rm -f "$workdir/$f" michael@0: archivefiles="$archivefiles \"$f.patch\"" michael@0: else michael@0: make_add_instruction "$f" "$updatemanifestv2" "$updatemanifestv3" michael@0: rm -f "$patchfile" michael@0: archivefiles="$archivefiles \"$f\"" michael@0: fi michael@0: fi michael@0: else michael@0: # remove instructions are added after add / patch instructions for michael@0: # consistency with make_incremental_updates.py michael@0: remove_array[$num_removes]=$f michael@0: (( num_removes++ )) michael@0: fi michael@0: done michael@0: michael@0: # Remove and re-add symlinks michael@0: notice "" michael@0: notice "Adding symlink remove/add instructions to update manifests" michael@0: num_oldsymlinks=${#oldsymlinks[*]} michael@0: for ((i=0; $i<$num_oldsymlinks; i=$i+1)); do michael@0: link="${oldsymlinks[$i]}" michael@0: verbose_notice " remove: $link" michael@0: echo "remove \"$link\"" >> "$updatemanifestv2" michael@0: echo "remove \"$link\"" >> "$updatemanifestv3" michael@0: done michael@0: michael@0: num_newsymlinks=${#newsymlinks[*]} michael@0: for ((i=0; $i<$num_newsymlinks; i=$i+1)); do michael@0: link="${newsymlinks[$i]}" michael@0: target="${newsymlink_targets[$i]}" michael@0: make_addsymlink_instruction "$link" "$target" "$updatemanifestv2" "$updatemanifestv3" michael@0: done michael@0: michael@0: # Newly added files michael@0: notice "" michael@0: notice "Adding file add instructions to update manifests" michael@0: num_newfiles=${#newfiles[*]} michael@0: michael@0: for ((i=0; $i<$num_newfiles; i=$i+1)); do michael@0: f="${newfiles[$i]}" michael@0: michael@0: # removed-files is excluded by make_incremental_updates.py so it is excluded michael@0: # here for consistency. michael@0: if [ "`basename "$f"`" = "removed-files" ]; then michael@0: continue 1 michael@0: fi michael@0: michael@0: # If we've already tested this file, then skip it michael@0: for ((j=0; $j<$num_oldfiles; j=$j+1)); do michael@0: if [ "$f" = "${oldfiles[j]}" ]; then michael@0: continue 2 michael@0: fi michael@0: done michael@0: michael@0: dir=$(dirname "$workdir/$f") michael@0: mkdir -p "$dir" michael@0: michael@0: $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f" michael@0: copy_perm "$newdir/$f" "$workdir/$f" michael@0: michael@0: if check_for_add_if_not_update "$f"; then michael@0: make_add_if_not_instruction "$f" "$updatemanifestv3" michael@0: else michael@0: make_add_instruction "$f" "$updatemanifestv2" "$updatemanifestv3" michael@0: fi michael@0: michael@0: michael@0: archivefiles="$archivefiles \"$f\"" michael@0: done michael@0: michael@0: notice "" michael@0: notice "Adding file remove instructions to update manifests" michael@0: for ((i=0; $i<$num_removes; i=$i+1)); do michael@0: f="${remove_array[$i]}" michael@0: notice " remove \"$f\"" michael@0: echo "remove \"$f\"" >> "$updatemanifestv2" michael@0: echo "remove \"$f\"" >> "$updatemanifestv3" michael@0: done michael@0: michael@0: # Add remove instructions for any dead files. michael@0: notice "" michael@0: notice "Adding file and directory remove instructions from file 'removed-files'" michael@0: append_remove_instructions "$newdir" "$updatemanifestv2" "$updatemanifestv3" michael@0: michael@0: notice "" michael@0: notice "Adding directory remove instructions for directories that no longer exist" michael@0: num_olddirs=${#olddirs[*]} michael@0: michael@0: for ((i=0; $i<$num_olddirs; i=$i+1)); do michael@0: f="${olddirs[$i]}" michael@0: # If this dir doesn't exist in the new directory remove it. michael@0: if [ ! -d "$newdir/$f" ]; then michael@0: notice " rmdir $f/" michael@0: echo "rmdir \"$f/\"" >> "$updatemanifestv2" michael@0: echo "rmdir \"$f/\"" >> "$updatemanifestv3" michael@0: fi michael@0: done michael@0: michael@0: $BZIP2 -z9 "$updatemanifestv2" && mv -f "$updatemanifestv2.bz2" "$updatemanifestv2" michael@0: $BZIP2 -z9 "$updatemanifestv3" && mv -f "$updatemanifestv3.bz2" "$updatemanifestv3" michael@0: michael@0: eval "$MAR -C \"$workdir\" -c output.mar $archivefiles" michael@0: mv -f "$workdir/output.mar" "$archive" michael@0: michael@0: # cleanup michael@0: rm -fr "$workdir" michael@0: michael@0: notice "" michael@0: notice "Finished" michael@0: notice ""