diff --git a/java/boot_jars.go b/java/boot_jars.go index 900eb7adf..e70654781 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -87,6 +87,7 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { rule := android.NewRuleBuilder() checkBootJars := rule.Command().BuiltTool(ctx, "check_boot_jars"). + Input(ctx.Config().HostToolPath(ctx, "dexdump")). Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")) // If this is not an unbundled build and missing dependencies are not allowed @@ -96,14 +97,9 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { // Iterate over the module names on the boot classpath in order for _, name := range android.SortedStringKeys(moduleToApex) { if apexVariant, ok := nameToApexVariant[name]; ok { - if dep, ok := apexVariant.(Dependency); ok { - // Add the implementation jars for the module to be checked. This uses implementation - // and resources jar as that is what the previous make based check uses. - for _, jar := range dep.ImplementationAndResourcesJars() { - checkBootJars.Input(jar) - } - } else if _, ok := apexVariant.(*DexImport); ok { - // TODO(b/171479578): ignore deximport when doing package check until boot_jars.go can check dex jars. + if dep, ok := apexVariant.(interface{ DexJarBuildPath() android.Path }); ok { + // Add the dex implementation jar for the module to be checked. + checkBootJars.Input(dep.DexJarBuildPath()) } else { ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant)) } diff --git a/scripts/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py index cf4ef2782..63fc9a9bd 100755 --- a/scripts/check_boot_jars/check_boot_jars.py +++ b/scripts/check_boot_jars/check_boot_jars.py @@ -3,7 +3,7 @@ """ Check boot jars. -Usage: check_boot_jars.py ... +Usage: check_boot_jars.py ... """ import logging import os.path @@ -38,28 +38,44 @@ def LoadAllowList(filename): return False return True +# Pattern that matches the class descriptor in a "Class descriptor" line output +# by dexdump and extracts the class name - with / instead of . +CLASS_DESCRIPTOR_RE = re.compile("'L([^;]+);'") -def CheckJar(allow_list_path, jar): - """Check a jar file. +def CheckDexJar(dexdump_path, allow_list_path, jar): + """Check a dex jar file. """ - # Get the list of files inside the jar file. - p = subprocess.Popen(args='jar tf %s' % jar, + # Get the class descriptor lines in the dexdump output. This filters out lines + # that do not contain class descriptors to reduce the size of the data read by + # this script. + p = subprocess.Popen(args='%s %s | grep "Class descriptor "' % (dexdump_path, jar), stdout=subprocess.PIPE, shell=True) stdout, _ = p.communicate() if p.returncode != 0: return False - items = stdout.split() + # Split the output into lines + lines = stdout.split('\n') classes = 0 - for f in items: - if f.endswith('.class'): - classes += 1 - package_name = os.path.dirname(f) - package_name = package_name.replace('/', '.') - if not package_name or not allow_list_re.match(package_name): - print >> sys.stderr, ('Error: %s contains class file %s, whose package name %s is empty or' - ' not in the allow list %s of packages allowed on the bootclasspath.' - % (jar, f, package_name, allow_list_path)) - return False + for line in lines: + # The last line will be empty + if line == '': + continue + # Try and find the descriptor on the line. Fail immediately if it cannot be found + # as the dexdump output has probably changed. + found = CLASS_DESCRIPTOR_RE.search(line) + if not found: + print >> sys.stderr, ('Could not find class descriptor in line `%s`' % line) + return False + # Extract the class name (using / instead of .) from the class descriptor line + f = found.group(1) + classes += 1 + package_name = os.path.dirname(f) + package_name = package_name.replace('/', '.') + if not package_name or not allow_list_re.match(package_name): + print >> sys.stderr, ('Error: %s contains class file %s, whose package name "%s" is empty or' + ' not in the allow list %s of packages allowed on the bootclasspath.' + % (jar, f, package_name, allow_list_path)) + return False if classes == 0: print >> sys.stderr, ('Error: %s does not contain any class files.' % jar) return False @@ -67,17 +83,18 @@ def CheckJar(allow_list_path, jar): def main(argv): - if len(argv) < 2: + if len(argv) < 3: print __doc__ return 1 - allow_list_path = argv[0] + dexdump_path = argv[0] + allow_list_path = argv[1] if not LoadAllowList(allow_list_path): return 1 passed = True - for jar in argv[1:]: - if not CheckJar(allow_list_path, jar): + for jar in argv[2:]: + if not CheckDexJar(dexdump_path, allow_list_path, jar): passed = False if not passed: return 1