xref: /aosp_15_r20/build/bazel/scripts/apex_compare.sh (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
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