Don't extract APEXes directly.
Use deapexer instead, to avoid knowing about .apex file format
internals.
Test: m art-check-{release,debug,testing}-apex-gen
Test: art/build/apex/runtests.sh
Bug: 188809029
Change-Id: I8414acfe23feaafb5bacc748e362d9677db7cc1a
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index ed335c4..78f62c4 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -445,7 +445,8 @@
// Genrules so we can run the checker, and empty Java library so that it gets executed.
art_check_apex_gen_stem = "$(location art-apex-tester)" +
- " --debugfs $(location debugfs)" +
+ " --deapexer $(location deapexer)" +
+ " --debugfs $(location debugfs_static)" +
" --tmpdir $(genDir)"
// The non-flattened APEXes are always checked, as they are always generated
@@ -463,7 +464,8 @@
tools: [
"art-apex-tester",
- "debugfs",
+ "deapexer",
+ "debugfs_static",
],
}
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index b83b8af..4ae274c 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -21,6 +21,7 @@
import logging
import os
import os.path
+import shutil
import subprocess
import sys
import zipfile
@@ -57,6 +58,17 @@
return var in os.environ and os.environ[var] == 'true'
+def extract_apex(apex_path, deapexer_path, debugfs_path, tmpdir):
+ _, apex_name = os.path.split(apex_path)
+ extract_path = os.path.join(tmpdir, apex_name)
+ if os.path.exists(extract_path):
+ shutil.rmtree(extract_path)
+ subprocess.check_call([deapexer_path, '--debugfs', debugfs_path,
+ 'extract', apex_path, extract_path],
+ stdout=subprocess.DEVNULL)
+ return extract_path
+
+
class FSObject:
def __init__(self, name, is_dir, is_exec, is_symlink, size):
self.name = name
@@ -71,83 +83,6 @@
class TargetApexProvider:
- def __init__(self, apex, tmpdir, debugfs):
- self._tmpdir = tmpdir
- self._debugfs = debugfs
- self._folder_cache = {}
- self._payload = os.path.join(self._tmpdir, 'apex_payload.img')
- # Extract payload to tmpdir.
- apex_zip = zipfile.ZipFile(apex)
- apex_zip.extract('apex_payload.img', tmpdir)
-
- def __del__(self):
- # Delete temps.
- if os.path.exists(self._payload):
- os.remove(self._payload)
-
- def get(self, path):
- apex_dir, name = os.path.split(path)
- if not apex_dir:
- apex_dir = '.'
- apex_map = self.read_dir(apex_dir)
- return apex_map[name] if name in apex_map else None
-
- def read_dir(self, apex_dir):
- if apex_dir in self._folder_cache:
- return self._folder_cache[apex_dir]
- # Cannot use check_output as it will annoy with stderr.
- process = subprocess.Popen([self._debugfs, '-R', 'ls -l -p %s' % apex_dir, self._payload],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- universal_newlines=True)
- stdout, _ = process.communicate()
- res = str(stdout)
- apex_map = {}
- # Debugfs output looks like this:
- # debugfs 1.44.4 (18-Aug-2018)
- # /12/040755/0/2000/.//
- # /2/040755/1000/1000/..//
- # /13/100755/0/2000/dalvikvm32/28456/
- # /14/100755/0/2000/dexoptanalyzer/20396/
- # /15/100755/0/2000/linker/1152724/
- # /16/100755/0/2000/dex2oat/563508/
- # /17/100755/0/2000/linker64/1605424/
- # /18/100755/0/2000/profman/85304/
- # /19/100755/0/2000/dalvikvm64/28576/
- # | | | | | |
- # | | | #- gid #- name #- size
- # | | #- uid
- # | #- type and permission bits
- # #- inode nr (?)
- #
- # Note: could break just on '/' to avoid names with newlines.
- for line in res.split("\n"):
- if not line:
- continue
- comps = line.split('/')
- if len(comps) != 8:
- logging.warning('Could not break and parse line \'%s\'', line)
- continue
- bits = comps[2]
- name = comps[5]
- size_str = comps[6]
- # Use a negative value as an indicator of undefined/unknown size.
- size = int(size_str) if size_str != '' else -1
- if len(bits) != 6:
- logging.warning('Dont understand bits \'%s\'', bits)
- continue
- is_dir = bits[1] == '4'
-
- def is_exec_bit(ch):
- return int(ch) & 1 == 1
-
- is_exec = is_exec_bit(bits[3]) and is_exec_bit(bits[4]) and is_exec_bit(bits[5])
- is_symlink = bits[1] == '2'
- apex_map[name] = FSObject(name, is_dir, is_exec, is_symlink, size)
- self._folder_cache[apex_dir] = apex_map
- return apex_map
-
-
-class TargetFlattenedApexProvider:
def __init__(self, apex):
self._folder_cache = {}
self._apex = apex
@@ -953,9 +888,13 @@
if not test_args.flattened and not test_args.tmpdir:
logging.error("Need a tmpdir.")
return 1
- if not test_args.flattened and not test_args.host and not test_args.debugfs:
- logging.error("Need debugfs.")
- return 1
+ if not test_args.flattened and not test_args.host:
+ if not test_args.deapexer:
+ logging.error("Need deapexer.")
+ return 1
+ if not test_args.debugfs:
+ logging.error("Need debugfs.")
+ return 1
if test_args.host:
# Host APEX.
@@ -990,10 +929,13 @@
if test_args.host:
apex_provider = HostApexProvider(test_args.apex, test_args.tmpdir)
else:
- if test_args.flattened:
- apex_provider = TargetFlattenedApexProvider(test_args.apex)
- else:
- apex_provider = TargetApexProvider(test_args.apex, test_args.tmpdir, test_args.debugfs)
+ apex_dir = test_args.apex
+ if not test_args.flattened:
+ # Extract the apex. It would be nice to use the output from "deapexer list"
+ # to avoid this work, but it doesn't provide info about executable bits.
+ apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs,
+ test_args.tmpdir)
+ apex_provider = TargetApexProvider(apex_dir)
except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
logging.error('Failed to create provider: %s', e)
return 1
@@ -1130,6 +1072,7 @@
parser.add_argument('--size', help='Print file sizes', action='store_true')
parser.add_argument('--tmpdir', help='Directory for temp files')
+ parser.add_argument('--deapexer', help='Path to deapexer')
parser.add_argument('--debugfs', help='Path to debugfs')
parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL,
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index d4ecbca..5911288 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -34,7 +34,7 @@
}
function setup_die {
- die "You need to source and lunch before you can use this script."
+ die "You need to run lunch, banchan, or tapas before you can use this script."
}
[[ -n "$ANDROID_BUILD_TOP" ]] || setup_die
@@ -49,13 +49,14 @@
export TARGET_BUILD_UNBUNDLED=true
fi
-have_debugfs_p=false
+have_deapexer_p=false
if $flattened_apex_p; then :; else
- if [ ! -e "$ANDROID_HOST_OUT/bin/debugfs" ] ; then
- say "Could not find debugfs, building now."
- build/soong/soong_ui.bash --make-mode debugfs-host || die "Cannot build debugfs"
+ if [ ! -e "$ANDROID_HOST_OUT/bin/deapexer" -o ! -e "$ANDROID_HOST_OUT/bin/debugfs_static" ] ; then
+ say "Could not find deapexer and/or debugfs_static, building now."
+ build/soong/soong_ui.bash --make-mode deapexer debugfs_static-host || \
+ die "Cannot build deapexer and debugfs_static"
fi
- have_debugfs_p=true
+ have_deapexer_p=true
fi
# Fail early.
@@ -181,8 +182,9 @@
else
apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
fi
- if $have_debugfs_p; then
- art_apex_test_args="$art_apex_test_args --debugfs $ANDROID_HOST_OUT/bin/debugfs"
+ if $have_deapexer_p; then
+ art_apex_test_args="$art_apex_test_args --deapexer $ANDROID_HOST_OUT/bin/deapexer"
+ art_apex_test_args="$art_apex_test_args --debugfs $ANDROID_HOST_OUT/bin/debugfs_static"
fi
case $apex_module in
(*.debug) test_only_args="--flavor debug";;