1*7594170eSAndroid Build Coastguard Worker#! /bin/bash -eu 2*7594170eSAndroid Build Coastguard Worker 3*7594170eSAndroid Build Coastguard Worker# Compares two APEX files. 4*7594170eSAndroid Build Coastguard Worker# This script is aimed at regression testing. It allows to compare an 5*7594170eSAndroid Build Coastguard Worker# APEX target built by Bazel to the same target built by Soong. 6*7594170eSAndroid Build Coastguard Worker# The first of its arguments is the reference APEX (the one built by 7*7594170eSAndroid Build Coastguard Worker# Soong), the second is "our" APEX (built by Bazel). 8*7594170eSAndroid Build Coastguard Worker# 9*7594170eSAndroid Build Coastguard Worker# An APEX is a ZIP archive, so we treat each APEX as a file system and 10*7594170eSAndroid Build Coastguard Worker# compare these two file systems. The script displays: 11*7594170eSAndroid Build Coastguard Worker# - missing files (those in the reference APEX missing from our APEX) 12*7594170eSAndroid Build Coastguard Worker# - extra files (those only in our APEX) 13*7594170eSAndroid Build Coastguard Worker# - for each file present in both, their difference. 14*7594170eSAndroid Build Coastguard Worker# The main part of an APEX is an image file (payload.img), which is an 15*7594170eSAndroid Build Coastguard Worker# image of a filesystem in EXT2 format. The script "mounts" such image 16*7594170eSAndroid Build Coastguard Worker# and then compares them side by side. 17*7594170eSAndroid Build Coastguard Worker# 18*7594170eSAndroid Build Coastguard Worker# This script relies on the presence of an executable (binary/script) 19*7594170eSAndroid Build Coastguard Worker# to "mount" a file of certain formats as file systems. It runs this 20*7594170eSAndroid Build Coastguard Worker# executable as follows: 21*7594170eSAndroid Build Coastguard Worker# * mount ZIPFILE at DIR: 22*7594170eSAndroid Build Coastguard Worker# view_file_as_fs zip ZIPFILE DIR 23*7594170eSAndroid Build Coastguard Worker# * unmount ZIPFILE at DIR: 24*7594170eSAndroid Build Coastguard Worker# view_file_as_fs -u zip DIR 25*7594170eSAndroid Build Coastguard Worker# * mount EXT2 image IMGFILE at DIR: 26*7594170eSAndroid Build Coastguard Worker# view_file_as_fs ext2 IMGFILE DIR 27*7594170eSAndroid Build Coastguard Worker# * unmount EXT2 image IMGFILE at DIR: 28*7594170eSAndroid Build Coastguard Worker# view_file_as_fs -u ext2 DIR 29*7594170eSAndroid Build Coastguard Worker# 30*7594170eSAndroid Build Coastguard Worker 31*7594170eSAndroid Build Coastguard Workerfunction die() { format=$1; shift; printf "$format\n" $@; exit 1; } 32*7594170eSAndroid Build Coastguard Worker 33*7594170eSAndroid Build Coastguard Worker# Delouse 34*7594170eSAndroid Build Coastguard Worker(($# == 2)) || die "usage: ${0##*/} REF_APEX OUR_APEX" 35*7594170eSAndroid Build Coastguard Workerdeclare -r ref_apex=$1 our_apex=$2 36*7594170eSAndroid Build Coastguard Workerfor f in $ref_apex $our_apex; do 37*7594170eSAndroid Build Coastguard Worker [[ -f $f ]] || die "$f does not exist" 38*7594170eSAndroid Build Coastguard Workerdone 39*7594170eSAndroid Build Coastguard Worker 40*7594170eSAndroid Build Coastguard Worker# Maybe we are lucky. 41*7594170eSAndroid Build Coastguard Workercmp -s $ref_apex $our_apex && exit 42*7594170eSAndroid Build Coastguard Worker 43*7594170eSAndroid Build Coastguard Workerdeclare -r file_as_fs_viewer=$(which view_file_as_fs) 44*7594170eSAndroid Build Coastguard Workerif [[ -z "${file_as_fs_viewer}" ]]; then 45*7594170eSAndroid Build Coastguard Worker cat <<"EOF" 46*7594170eSAndroid Build Coastguard WorkerYou need to have file-as-filesystem viewer application `view_file_as_fs` 47*7594170eSAndroid Build Coastguard Workeron the PATH. If you have FUSE's fuse-ext2 and fuse-zip installed, you 48*7594170eSAndroid Build Coastguard Workercan the following script below view_file_as_fs: 49*7594170eSAndroid Build Coastguard Worker 50*7594170eSAndroid Build Coastguard Worker#!/bin/bash -eu 51*7594170eSAndroid Build Coastguard Worker# 52*7594170eSAndroid Build Coastguard Worker# Mounts a file as a read-only filesystem or unmounts such previously 53*7594170eSAndroid Build Coastguard Worker# mounted file system. 54*7594170eSAndroid Build Coastguard Worker# This script can mount a zip file or an file containing an ext2 image 55*7594170eSAndroid Build Coastguard Worker# as a file system. It requires the presence of fuse-zip and fuse-ext2 56*7594170eSAndroid Build Coastguard Worker# FUSE packages. 57*7594170eSAndroid Build Coastguard Workerfunction die() { format=$1; shift; printf "$format\n" $@; exit 1; } 58*7594170eSAndroid Build Coastguard Workerfunction usage() { 59*7594170eSAndroid Build Coastguard Worker die "Usage:\n ${0##*/} {ext2|zip} FILE MOUNT-POINT\nor\n ${0##*/} -u {ext2|zip} MOUNT-POINT" 60*7594170eSAndroid Build Coastguard Worker} 61*7594170eSAndroid Build Coastguard Worker 62*7594170eSAndroid Build Coastguard Workerdeclare umount= 63*7594170eSAndroid Build Coastguard Workerwhile getopts "u" opt; do 64*7594170eSAndroid Build Coastguard Worker case $opt in 65*7594170eSAndroid Build Coastguard Worker u) umount=t ;; 66*7594170eSAndroid Build Coastguard Worker ?) usage 67*7594170eSAndroid Build Coastguard Worker esac 68*7594170eSAndroid Build Coastguard Workerdone 69*7594170eSAndroid Build Coastguard Worker 70*7594170eSAndroid Build Coastguard Workershift $(($OPTIND-1)) 71*7594170eSAndroid Build Coastguard Workerif [[ -n "$umount" ]]; then 72*7594170eSAndroid Build Coastguard Worker (($#==2)) || usage 73*7594170eSAndroid Build Coastguard Worker mount | grep -q "on $2 " && umount "$2" 74*7594170eSAndroid Build Coastguard Workerelse 75*7594170eSAndroid Build Coastguard Worker (($#==3)) || usage 76*7594170eSAndroid Build Coastguard Worker declare -r file="$2" mt="$3" 77*7594170eSAndroid Build Coastguard Worker [[ -d "$mt" && -z "$(ls -1A $mt)" ]] || die "$mt should be an empty directory" 78*7594170eSAndroid Build Coastguard Worker case "$1" in 79*7594170eSAndroid Build Coastguard Worker ext2) fuse-ext2 "$file" "$mt" ;; 80*7594170eSAndroid Build Coastguard Worker zip) 81*7594170eSAndroid Build Coastguard Worker [[ -f $file ]] || die "$file is not a file" # Because fuse-zip silently mounts it as empty 82*7594170eSAndroid Build Coastguard Worker fuse-zip "$file" "$mt" ;; 83*7594170eSAndroid Build Coastguard Worker *) usage ;; 84*7594170eSAndroid Build Coastguard Worker esac 85*7594170eSAndroid Build Coastguard Workerfi 86*7594170eSAndroid Build Coastguard WorkerEOF 87*7594170eSAndroid Build Coastguard Worker exit 1 88*7594170eSAndroid Build Coastguard Workerfi 89*7594170eSAndroid Build Coastguard Worker 90*7594170eSAndroid Build Coastguard Worker# "Mounts" file as filesystem and prints the sorted list of files in it. 91*7594170eSAndroid Build Coastguard Workerfunction mount_and_list() { 92*7594170eSAndroid Build Coastguard Worker $file_as_fs_viewer $1 $2 $3 2>/dev/null 93*7594170eSAndroid Build Coastguard Worker find $3 -type f -printf "%P\n" 94*7594170eSAndroid Build Coastguard Worker} 95*7594170eSAndroid Build Coastguard Worker 96*7594170eSAndroid Build Coastguard Workerfunction cleanup() { 97*7594170eSAndroid Build Coastguard Worker for d in $fuse_dir/*.img; do 98*7594170eSAndroid Build Coastguard Worker $file_as_fs_viewer -u ext2 $d || /bin/true 99*7594170eSAndroid Build Coastguard Worker done 100*7594170eSAndroid Build Coastguard Worker for d in $fuse_dir/*.apex; do 101*7594170eSAndroid Build Coastguard Worker $file_as_fs_viewer -u zip $d || /bin/true 102*7594170eSAndroid Build Coastguard Worker done 103*7594170eSAndroid Build Coastguard Worker rm -rf $fuse_dir 104*7594170eSAndroid Build Coastguard Worker} 105*7594170eSAndroid Build Coastguard Worker 106*7594170eSAndroid Build Coastguard Workerfunction dump_proto() { 107*7594170eSAndroid Build Coastguard Worker protoc --decode $1 $2 108*7594170eSAndroid Build Coastguard Worker} 109*7594170eSAndroid Build Coastguard Worker 110*7594170eSAndroid Build Coastguard Workerfunction dump_buildinfo() { 111*7594170eSAndroid Build Coastguard Worker dump_proto apex.proto.ApexBuildInfo system/apex/proto/apex_build_info.proto 112*7594170eSAndroid Build Coastguard Worker} 113*7594170eSAndroid Build Coastguard Worker 114*7594170eSAndroid Build Coastguard Workerfunction dump_apex_manifest() { 115*7594170eSAndroid Build Coastguard Worker dump_proto apex.proto.ApexManifest system/apex/proto/apex_manifest.proto 116*7594170eSAndroid Build Coastguard Worker} 117*7594170eSAndroid Build Coastguard Worker 118*7594170eSAndroid Build Coastguard Workerfunction compare_images() { 119*7594170eSAndroid Build Coastguard Worker local -r ref_img=$1 our_img=$2 120*7594170eSAndroid Build Coastguard Worker 121*7594170eSAndroid Build Coastguard Worker # Mount each APEX and save its sorted contents. Classify the contents 122*7594170eSAndroid Build Coastguard Worker mount_and_list ext2 $ref_img $fuse_dir/ref.img >$fuse_dir/ref.img.list 123*7594170eSAndroid Build Coastguard Worker mount_and_list ext2 $our_img $fuse_dir/our.img >$fuse_dir/our.img.list 124*7594170eSAndroid Build Coastguard Worker . <(classify $fuse_dir/ref.img.list $fuse_dir/our.img.list; /bin/true) 125*7594170eSAndroid Build Coastguard Worker 126*7594170eSAndroid Build Coastguard Worker # Now we have missing/extra/common holding respective file lists. Compare 127*7594170eSAndroid Build Coastguard Worker ((${#missing[@]}==0)) || \ 128*7594170eSAndroid Build Coastguard Worker { printf "Missing image files:"; printf " %s" ${missing[@]}; printf "\n"; } 129*7594170eSAndroid Build Coastguard Worker ((${#extra[@]}==0)) || \ 130*7594170eSAndroid Build Coastguard Worker { printf "Extra image files:"; printf " %s" ${extra[@]}; printf "\n"; } 131*7594170eSAndroid Build Coastguard Worker for f in "${common[@]}"; do 132*7594170eSAndroid Build Coastguard Worker cmp -s $fuse_dir/{ref,our}.img/$f && continue 133*7594170eSAndroid Build Coastguard Worker echo " $f" in image differs: 134*7594170eSAndroid Build Coastguard Worker case $f in 135*7594170eSAndroid Build Coastguard Worker etc/init.rc) 136*7594170eSAndroid Build Coastguard Worker diff $fuse_dir/{ref,our}.img/$f || /bin/true 137*7594170eSAndroid Build Coastguard Worker ;; 138*7594170eSAndroid Build Coastguard Worker apex_manifest.pb) 139*7594170eSAndroid Build Coastguard Worker diff <(dump_apex_manifest <$fuse_dir/ref.img/$f) <(dump_apex_manifest <$fuse_dir/our.img/$f) || bin/true 140*7594170eSAndroid Build Coastguard Worker ;; 141*7594170eSAndroid Build Coastguard Worker *) 142*7594170eSAndroid Build Coastguard Worker # TODO: should do more than just size comparison. 143*7594170eSAndroid Build Coastguard Worker sizes=($(stat --format "%s" $fuse_dir/{ref,our}.img/$f)) 144*7594170eSAndroid Build Coastguard Worker delta=$((${sizes[1]}-${sizes[0]})) 145*7594170eSAndroid Build Coastguard Worker (($delta==0)) || printf " size differs: %d (%d)\n" ${sizes[1]} $delta 146*7594170eSAndroid Build Coastguard Worker ;; 147*7594170eSAndroid Build Coastguard Worker esac 148*7594170eSAndroid Build Coastguard Worker done 149*7594170eSAndroid Build Coastguard Worker} 150*7594170eSAndroid Build Coastguard Worker 151*7594170eSAndroid Build Coastguard Worker# Prints the script that sets `missing`/`extra`/`common` shell 152*7594170eSAndroid Build Coastguard Worker# variable to an array containing corresponding files, i.e. its 153*7594170eSAndroid Build Coastguard Worker# output is 154*7594170eSAndroid Build Coastguard Worker# declare declare -a missing=() extra=() common=() 155*7594170eSAndroid Build Coastguard Worker# missing+=(missing_file) 156*7594170eSAndroid Build Coastguard Worker# extra+=(extra_file) 157*7594170eSAndroid Build Coastguard Worker# common+=(common_file) 158*7594170eSAndroid Build Coastguard Worker# ..... 159*7594170eSAndroid Build Coastguard Workerfunction classify() { 160*7594170eSAndroid Build Coastguard Worker comm $1 $2 | sed -nr \ 161*7594170eSAndroid Build Coastguard Worker -e '1ideclare -a missing=() extra=() common=()' \ 162*7594170eSAndroid Build Coastguard Worker -e '/^\t\t/{s/\t\t(.*)/common+=(\1)/p;d}' \ 163*7594170eSAndroid Build Coastguard Worker -e '/^\t/{s/^\t(.*)/extra+=(\1)/p;d}' \ 164*7594170eSAndroid Build Coastguard Worker -e 's/(.*)/missing+=(\1)/p'; /bin/true 165*7594170eSAndroid Build Coastguard Worker} 166*7594170eSAndroid Build Coastguard Worker 167*7594170eSAndroid Build Coastguard Workerfuse_dir=$(mktemp -d --tmpdir apexfuse.XXXXX) 168*7594170eSAndroid Build Coastguard Workermkdir -p $fuse_dir/{our,ref}.{apex,img} 169*7594170eSAndroid Build Coastguard Workertrap cleanup EXIT 170*7594170eSAndroid Build Coastguard Worker 171*7594170eSAndroid Build Coastguard Worker# Mount each APEX and save its sorted contents. Classify the contents 172*7594170eSAndroid Build Coastguard Workermount_and_list zip $ref_apex $fuse_dir/ref.apex >$fuse_dir/ref.apex.list 173*7594170eSAndroid Build Coastguard Workermount_and_list zip $our_apex $fuse_dir/our.apex >$fuse_dir/our.apex.list 174*7594170eSAndroid Build Coastguard Worker. <(classify $fuse_dir/ref.apex.list $fuse_dir/our.apex.list; /bin/true) 175*7594170eSAndroid Build Coastguard Worker 176*7594170eSAndroid Build Coastguard Worker# Now we have missing/extra/common holding respective file lists. Compare 177*7594170eSAndroid Build Coastguard Worker((${#missing[@]}==0)) || { printf "Missing files:"; printf " %s" ${missing[@]}; printf "\n"; } 178*7594170eSAndroid Build Coastguard Worker((${#extra[@]}==0)) || { printf "Extra files:"; printf " %s" ${extra[@]}; printf "\n"; } 179*7594170eSAndroid Build Coastguard Worker 180*7594170eSAndroid Build Coastguard Workerfor f in "${common[@]}"; do 181*7594170eSAndroid Build Coastguard Worker cmp -s $fuse_dir/{ref,our}.apex/$f && continue 182*7594170eSAndroid Build Coastguard Worker # File differs, compare known file types intelligently 183*7594170eSAndroid Build Coastguard Worker case $f in 184*7594170eSAndroid Build Coastguard Worker AndroidManifest.xml) 185*7594170eSAndroid Build Coastguard Worker echo $f differs: 186*7594170eSAndroid Build Coastguard Worker diff \ 187*7594170eSAndroid Build Coastguard Worker <(aapt dump xmltree $fuse_dir/ref.apex AndroidManifest.xml) \ 188*7594170eSAndroid Build Coastguard Worker <(aapt dump xmltree $fuse_dir/our.apex AndroidManifest.xml) || /bin/true 189*7594170eSAndroid Build Coastguard Worker ;; 190*7594170eSAndroid Build Coastguard Worker apex_build_info.pb) 191*7594170eSAndroid Build Coastguard Worker echo $f differs: 192*7594170eSAndroid Build Coastguard Worker diff <(dump_buildinfo <$fuse_dir/ref.apex/$f) <(dump_buildinfo <$fuse_dir/our.apex/$f) || /bin/true 193*7594170eSAndroid Build Coastguard Worker ;; 194*7594170eSAndroid Build Coastguard Worker manifest.pb) 195*7594170eSAndroid Build Coastguard Worker echo $f differs: 196*7594170eSAndroid Build Coastguard Worker diff <(dump_apex_manifest <$fuse_dir/ref.apex/$f) <(dump_apex_manifest <$fuse_dir/our.apex/$f) || bin/true 197*7594170eSAndroid Build Coastguard Worker ;; 198*7594170eSAndroid Build Coastguard Worker apex_payload.img) 199*7594170eSAndroid Build Coastguard Worker echo image $f differs, mounting it: 200*7594170eSAndroid Build Coastguard Worker compare_images $fuse_dir/{ref,our}.apex/$f 201*7594170eSAndroid Build Coastguard Worker ;; 202*7594170eSAndroid Build Coastguard Worker META-INF/*) 203*7594170eSAndroid Build Coastguard Worker # Ignore these. They are derived from the rest 204*7594170eSAndroid Build Coastguard Worker # showing their difference does not help. 205*7594170eSAndroid Build Coastguard Worker ;; 206*7594170eSAndroid Build Coastguard Worker *) echo $f; diff $fuse_dir/{ref,our}.apex/$f || /bin/true 207*7594170eSAndroid Build Coastguard Worker esac 208*7594170eSAndroid Build Coastguard Workerdone 209