xref: /aosp_15_r20/art/build/apex/art_apex_test.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2019 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import argparse
20import fnmatch
21import logging
22import os
23import os.path
24import shutil
25import subprocess
26import sys
27
28logging.basicConfig(format='%(message)s')
29
30# Flavors of ART APEX package.
31FLAVOR_RELEASE = 'release'
32FLAVOR_DEBUG = 'debug'
33FLAVOR_TESTING = 'testing'
34FLAVOR_AUTO = 'auto'
35FLAVORS_ALL = [FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_AUTO]
36
37# Bitness options for APEX package
38BITNESS_32 = '32'
39BITNESS_64 = '64'
40BITNESS_MULTILIB = 'multilib'
41BITNESS_AUTO = 'auto'
42BITNESS_ALL = [BITNESS_32, BITNESS_64, BITNESS_MULTILIB, BITNESS_AUTO]
43
44# Architectures supported by APEX packages.
45ARCHS_32 = ['arm', 'x86']
46ARCHS_64 = ['arm64', 'riscv64', 'x86_64']
47
48# Multilib options
49MULTILIB_32 = '32'
50MULTILIB_64 = '64'
51MULTILIB_BOTH = 'both'
52MULTILIB_FIRST = 'first'
53
54# Directory containing ART tests within an ART APEX (if the package includes
55# any). ART test executables are installed in `bin/art/<arch>`. Segregating
56# tests by architecture is useful on devices supporting more than one
57# architecture, as it permits testing all of them using a single ART APEX
58# package.
59ART_TEST_DIR = 'bin/art'
60
61
62# Test if a given variable is set to a string "true".
63def isEnvTrue(var):
64  return var in os.environ and os.environ[var] == 'true'
65
66
67def extract_apex(apex_path, deapexer_path, debugfs_path, fsckerofs_path,
68                 tmpdir):
69  _, apex_name = os.path.split(apex_path)
70  extract_path = os.path.join(tmpdir, apex_name)
71  if os.path.exists(extract_path):
72    shutil.rmtree(extract_path)
73  subprocess.check_call([deapexer_path, '--debugfs', debugfs_path,
74                         '--fsckerofs', fsckerofs_path,
75                         'extract', apex_path, extract_path],
76                        stdout=subprocess.DEVNULL)
77  return extract_path
78
79
80class FSObject:
81  def __init__(self, name, is_dir, is_exec, is_symlink, size):
82    self.name = name
83    self.is_dir = is_dir
84    self.is_exec = is_exec
85    self.is_symlink = is_symlink
86    self.size = size
87
88  def __str__(self):
89    return '%s(dir=%r,exec=%r,symlink=%r,size=%d)' \
90             % (self.name, self.is_dir, self.is_exec, self.is_symlink, self.size)
91
92  def type_descr(self):
93    if self.is_dir:
94      return 'directory'
95    if self.is_symlink:
96      return 'symlink'
97    return 'file'
98
99
100class TargetApexProvider:
101  def __init__(self, apex):
102    self._folder_cache = {}
103    self._apex = apex
104
105  def get(self, path):
106    apex_dir, name = os.path.split(path)
107    if not apex_dir:
108      apex_dir = '.'
109    apex_map = self.read_dir(apex_dir)
110    return apex_map[name] if name in apex_map else None
111
112  def read_dir(self, apex_dir):
113    if apex_dir in self._folder_cache:
114      return self._folder_cache[apex_dir]
115    apex_map = {}
116    dirname = os.path.join(self._apex, apex_dir)
117    if os.path.exists(dirname):
118      for basename in os.listdir(dirname):
119        filepath = os.path.join(dirname, basename)
120        is_dir = os.path.isdir(filepath)
121        is_exec = os.access(filepath, os.X_OK)
122        is_symlink = os.path.islink(filepath)
123        if is_symlink:
124          # Report the length of the symlink's target's path as file size, like `ls`.
125          size = len(os.readlink(filepath))
126        else:
127          size = os.path.getsize(filepath)
128        apex_map[basename] = FSObject(basename, is_dir, is_exec, is_symlink, size)
129    self._folder_cache[apex_dir] = apex_map
130    return apex_map
131
132
133# DO NOT USE DIRECTLY! This is an "abstract" base class.
134class Checker:
135  def __init__(self, provider):
136    self._provider = provider
137    self._errors = 0
138    self._expected_file_globs = set()
139
140  def fail(self, msg, *fail_args):
141    self._errors += 1
142    logging.error(msg, *fail_args)
143
144  def error_count(self):
145    return self._errors
146
147  def reset_errors(self):
148    self._errors = 0
149
150  def is_file(self, path):
151    fs_object = self._provider.get(path)
152    if fs_object is None:
153      return False, 'Could not find %s'
154    if fs_object.is_dir:
155      return False, '%s is a directory'
156    if fs_object.is_symlink:
157      return False, '%s is a symlink'
158    return True, ''
159
160  def is_dir(self, path):
161    fs_object = self._provider.get(path)
162    if fs_object is None:
163      return False, 'Could not find %s'
164    if not fs_object.is_dir:
165      return False, '%s is not a directory'
166    return True, ''
167
168  def check_file(self, path):
169    ok, msg = self.is_file(path)
170    if not ok:
171      self.fail(msg, path)
172    self._expected_file_globs.add(path)
173    return ok
174
175  def check_dir(self, path):
176    ok, msg = self.is_dir(path)
177    if not ok:
178      self.fail(msg, path)
179    self._expected_file_globs.add(path)
180    return ok
181
182  def check_optional_file(self, path):
183    if not self._provider.get(path):
184      return True
185    return self.check_file(path)
186
187  def check_executable(self, filename):
188    path = 'bin/%s' % filename
189    if not self.check_file(path):
190      return
191    if not self._provider.get(path).is_exec:
192      self.fail('%s is not executable', path)
193
194  def check_executable_symlink(self, filename):
195    path = 'bin/%s' % filename
196    fs_object = self._provider.get(path)
197    if fs_object is None:
198      self.fail('Could not find %s', path)
199      return
200    if fs_object.is_dir:
201      self.fail('%s is a directory', path)
202      return
203    if not fs_object.is_symlink:
204      self.fail('%s is not a symlink', path)
205    self._expected_file_globs.add(path)
206
207  def arch_dirs_for_path(self, path, multilib=None):
208    # Look for target-specific subdirectories for the given directory path.
209    # This is needed because the list of build targets is not propagated
210    # to this script.
211    #
212    # TODO(b/123602136): Pass build target information to this script and fix
213    # all places where this function in used (or similar workarounds).
214    dirs = []
215    for archs_per_bitness in self.possible_archs_per_bitness(multilib):
216      found_dir = False
217      for arch in archs_per_bitness:
218        dir = '%s/%s' % (path, arch)
219        found, _ = self.is_dir(dir)
220        if found:
221          found_dir = True
222          dirs.append(dir)
223      # At least one arch directory per bitness must exist.
224      if not found_dir:
225        self.fail('Arch directories missing in %s - expected at least one of %s',
226                  path, ', '.join(archs_per_bitness))
227    return dirs
228
229  def check_art_test_executable(self, filename, multilib=None):
230    for dir in self.arch_dirs_for_path(ART_TEST_DIR, multilib):
231      test_path = '%s/%s' % (dir, filename)
232      self._expected_file_globs.add(test_path)
233      file_obj = self._provider.get(test_path)
234      if not file_obj:
235        self.fail('ART test binary missing: %s', test_path)
236      elif not file_obj.is_exec:
237        self.fail('%s is not executable', test_path)
238
239  def check_art_test_data(self, filename):
240    for dir in self.arch_dirs_for_path(ART_TEST_DIR):
241      if not self.check_file('%s/%s' % (dir, filename)):
242        return
243
244  def check_single_library(self, filename):
245    lib_path = 'lib/%s' % filename
246    lib64_path = 'lib64/%s' % filename
247    lib_is_file, _ = self.is_file(lib_path)
248    if lib_is_file:
249      self._expected_file_globs.add(lib_path)
250    lib64_is_file, _ = self.is_file(lib64_path)
251    if lib64_is_file:
252      self._expected_file_globs.add(lib64_path)
253    if not lib_is_file and not lib64_is_file:
254      self.fail('Library missing: %s', filename)
255
256  def check_java_library(self, basename):
257    return self.check_file('javalib/%s.jar' % basename)
258
259  def ignore_path(self, path_glob):
260    self._expected_file_globs.add(path_glob)
261
262  def check_optional_art_test_executable(self, filename):
263    for archs_per_bitness in self.possible_archs_per_bitness():
264      for arch in archs_per_bitness:
265        self.ignore_path('%s/%s/%s' % (ART_TEST_DIR, arch, filename))
266
267  def check_no_superfluous_files(self):
268    def recurse(dir_path):
269      paths = []
270      for name, fsobj in sorted(self._provider.read_dir(dir_path).items(), key=lambda p: p[0]):
271        if name in ('.', '..'):
272          continue
273        path = os.path.join(dir_path, name)
274        paths.append(path)
275        if fsobj.is_dir:
276          recurse(path)
277      for path_glob in self._expected_file_globs:
278        paths = [path for path in paths if not fnmatch.fnmatch(path, path_glob)]
279      for unexpected_path in paths:
280        fs_object = self._provider.get(unexpected_path)
281        self.fail('Unexpected %s: %s', fs_object.type_descr(), unexpected_path)
282    recurse('')
283
284  # Just here for docs purposes, even if it isn't good Python style.
285
286  def check_symlinked_multilib_executable(self, filename):
287    """Check bin/filename32, and/or bin/filename64, with symlink bin/filename."""
288    raise NotImplementedError
289
290  def check_symlinked_first_executable(self, filename):
291    """Check bin/filename32, and/or bin/filename64, with symlink bin/filename."""
292    raise NotImplementedError
293
294  def check_native_library(self, basename):
295    """Check lib/basename.so, and/or lib64/basename.so."""
296    raise NotImplementedError
297
298  def check_optional_native_library(self, basename_glob):
299    """Allow lib/basename.so and/or lib64/basename.so to exist."""
300    raise NotImplementedError
301
302  def check_prefer64_library(self, basename):
303    """Check lib64/basename.so, or lib/basename.so on 32 bit only."""
304    raise NotImplementedError
305
306  def possible_archs_per_bitness(self, multilib=None):
307    """Returns a list of lists of possible architectures per bitness."""
308    raise NotImplementedError
309
310class Arch32Checker(Checker):
311  def __init__(self, provider):
312    super().__init__(provider)
313    self.lib_dirs = ['lib']
314
315  def check_symlinked_multilib_executable(self, filename):
316    self.check_executable('%s32' % filename)
317    self.check_executable_symlink(filename)
318
319  def check_symlinked_first_executable(self, filename):
320    self.check_executable('%s32' % filename)
321    self.check_executable_symlink(filename)
322
323  def check_native_library(self, basename):
324    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
325    # the precision of this test?
326    self.check_file('lib/%s.so' % basename)
327
328  def check_optional_native_library(self, basename_glob):
329    self.ignore_path('lib/%s.so' % basename_glob)
330
331  def check_prefer64_library(self, basename):
332    self.check_native_library(basename)
333
334  def possible_archs_per_bitness(self, multilib=None):
335    return [ARCHS_32]
336
337class Arch64Checker(Checker):
338  def __init__(self, provider):
339    super().__init__(provider)
340    self.lib_dirs = ['lib64']
341
342  def check_symlinked_multilib_executable(self, filename):
343    self.check_executable('%s64' % filename)
344    self.check_executable_symlink(filename)
345
346  def check_symlinked_first_executable(self, filename):
347    self.check_executable('%s64' % filename)
348    self.check_executable_symlink(filename)
349
350  def check_native_library(self, basename):
351    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
352    # the precision of this test?
353    self.check_file('lib64/%s.so' % basename)
354
355  def check_optional_native_library(self, basename_glob):
356    self.ignore_path('lib64/%s.so' % basename_glob)
357
358  def check_prefer64_library(self, basename):
359    self.check_native_library(basename)
360
361  def possible_archs_per_bitness(self, multilib=None):
362    return [ARCHS_64]
363
364
365class MultilibChecker(Checker):
366  def __init__(self, provider):
367    super().__init__(provider)
368    self.lib_dirs = ['lib', 'lib64']
369
370  def check_symlinked_multilib_executable(self, filename):
371    self.check_executable('%s32' % filename)
372    self.check_executable('%s64' % filename)
373    self.check_executable_symlink(filename)
374
375  def check_symlinked_first_executable(self, filename):
376    self.check_executable('%s64' % filename)
377    self.check_executable_symlink(filename)
378
379  def check_native_library(self, basename):
380    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
381    # the precision of this test?
382    self.check_file('lib/%s.so' % basename)
383    self.check_file('lib64/%s.so' % basename)
384
385  def check_optional_native_library(self, basename_glob):
386    self.ignore_path('lib/%s.so' % basename_glob)
387    self.ignore_path('lib64/%s.so' % basename_glob)
388
389  def check_prefer64_library(self, basename):
390    self.check_file('lib64/%s.so' % basename)
391
392  def possible_archs_per_bitness(self, multilib=None):
393    if multilib is None or multilib == MULTILIB_BOTH:
394      return [ARCHS_32, ARCHS_64]
395    if multilib == MULTILIB_FIRST or multilib == MULTILIB_64:
396      return [ARCHS_64]
397    elif multilib == MULTILIB_32:
398      return [ARCHS_32]
399    self.fail('Unrecognized multilib option "%s"', multilib)
400
401
402class ReleaseChecker:
403  def __init__(self, checker):
404    self._checker = checker
405
406  def __str__(self):
407    return 'Release Checker'
408
409  def run(self):
410    # Check the root directory.
411    self._checker.check_dir('bin')
412    self._checker.check_dir('etc')
413    self._checker.check_dir('javalib')
414    for lib_dir in self._checker.lib_dirs:
415      self._checker.check_dir(lib_dir)
416    self._checker.check_file('apex_manifest.pb')
417
418    # Check etc.
419    self._checker.check_file('etc/boot-image.prof')
420    self._checker.check_dir('etc/classpaths')
421    self._checker.check_file('etc/classpaths/bootclasspath.pb')
422    self._checker.check_file('etc/classpaths/systemserverclasspath.pb')
423    self._checker.check_dir('etc/compatconfig')
424    self._checker.check_file('etc/compatconfig/libcore-platform-compat-config.xml')
425    self._checker.check_file('etc/init.rc')
426    self._checker.check_file('etc/linker.config.pb')
427    self._checker.check_file('etc/sdkinfo.pb')
428    self._checker.check_file('etc/dirty-image-objects')
429
430    # Check flagging files that don't get added in builds on master-art.
431    # TODO(b/345713436): Make flags work on master-art.
432    self._checker.check_optional_file('etc/aconfig_flags.pb')
433    self._checker.check_optional_file('etc/flag.info')
434    self._checker.check_optional_file('etc/flag.map')
435    self._checker.check_optional_file('etc/flag.val')
436    self._checker.check_optional_file('etc/package.map')
437
438    # Check binaries for ART.
439    self._checker.check_executable('art_boot')
440    self._checker.check_executable('art_exec')
441    self._checker.check_executable('artd')
442    self._checker.check_executable('dexdump')
443    self._checker.check_executable('dexlist')
444    self._checker.check_executable('dexopt_chroot_setup')
445    self._checker.check_executable('dexoptanalyzer')
446    self._checker.check_executable('oatdump')
447    self._checker.check_executable('odrefresh')
448    self._checker.check_executable('profman')
449    self._checker.check_symlinked_multilib_executable('dalvikvm')
450    self._checker.check_symlinked_multilib_executable('dex2oat')
451
452    # Check exported libraries for ART.
453    self._checker.check_native_library('libdexfile')
454    self._checker.check_native_library('libnativebridge')
455    self._checker.check_native_library('libnativehelper')
456    self._checker.check_native_library('libnativeloader')
457
458    # Check internal libraries for ART.
459    self._checker.check_native_library('libadbconnection')
460    self._checker.check_native_library('libart')
461    self._checker.check_native_library('libart-disassembler')
462    self._checker.check_native_library('libartbase')
463    self._checker.check_native_library('libartpalette')
464    self._checker.check_native_library('libdt_fd_forward')
465    self._checker.check_native_library('libopenjdkjvm')
466    self._checker.check_native_library('libopenjdkjvmti')
467    self._checker.check_native_library('libperfetto_hprof')
468    self._checker.check_native_library('libprofile')
469    self._checker.check_native_library('libsigchain')
470    self._checker.check_prefer64_library('libartservice')
471    self._checker.check_prefer64_library('libarttools')
472
473    # Check internal Java libraries for ART.
474    self._checker.check_java_library('service-art')
475    self._checker.check_file('javalib/service-art.jar.prof')
476
477    # Check exported native libraries for Managed Core Library.
478    self._checker.check_native_library('libandroidio')
479
480    # Check Java libraries for Managed Core Library.
481    self._checker.check_java_library('apache-xml')
482    self._checker.check_java_library('bouncycastle')
483    self._checker.check_java_library('core-libart')
484    self._checker.check_java_library('core-oj')
485    self._checker.check_java_library('okhttp')
486    if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'):
487      # In coverage builds jacoco is added to the list of ART apex jars.
488      self._checker.check_java_library('jacocoagent')
489
490    # Check internal native libraries for Managed Core Library.
491    self._checker.check_native_library('libjavacore')
492    self._checker.check_native_library('libopenjdk')
493
494    # Check internal native library dependencies.
495    #
496    # Any internal dependency not listed here will cause a failure in
497    # NoSuperfluousFilesChecker. Internal dependencies are generally just
498    # implementation details, but in the release package we want to track them
499    # because a) they add to the package size and the RAM usage (in particular
500    # if the library is also present in /system or another APEX and hence might
501    # get loaded twice through linker namespace separation), and b) we need to
502    # catch invalid dependencies on /system or other APEXes that should go
503    # through an exported library with stubs (b/128708192 tracks implementing a
504    # better approach for that).
505    self._checker.check_native_library('libbase')
506    self._checker.check_native_library('libc++')
507    self._checker.check_native_library('libdt_socket')
508    self._checker.check_native_library('libexpat')
509    self._checker.check_native_library('libjdwp')
510    self._checker.check_native_library('liblz4')
511    self._checker.check_native_library('liblzma')
512    self._checker.check_native_library('libnpt')
513    self._checker.check_native_library('libunwindstack')
514
515    # Allow extra dependencies that appear in ASAN builds.
516    self._checker.check_optional_native_library('libclang_rt.asan*')
517    self._checker.check_optional_native_library('libclang_rt.hwasan*')
518    self._checker.check_optional_native_library('libclang_rt.ubsan*')
519
520
521class DebugChecker:
522  def __init__(self, checker):
523    self._checker = checker
524
525  def __str__(self):
526    return 'Debug Checker'
527
528  def run(self):
529    # Check binaries for ART.
530    self._checker.check_executable('dexanalyze')
531    self._checker.check_symlinked_multilib_executable('imgdiag')
532
533    # Check debug binaries for ART.
534    self._checker.check_executable('dexoptanalyzerd')
535    self._checker.check_executable('oatdumpd')
536    self._checker.check_executable('profmand')
537    self._checker.check_symlinked_multilib_executable('dex2oatd')
538    self._checker.check_symlinked_multilib_executable('imgdiagd')
539
540    # Check exported libraries for ART.
541    self._checker.check_native_library('libdexfiled')
542
543    # Check internal libraries for ART.
544    self._checker.check_native_library('libadbconnectiond')
545    self._checker.check_native_library('libartbased')
546    self._checker.check_native_library('libartd')
547    self._checker.check_native_library('libartd-disassembler')
548    self._checker.check_native_library('libopenjdkjvmd')
549    self._checker.check_native_library('libopenjdkjvmtid')
550    self._checker.check_native_library('libperfetto_hprofd')
551    self._checker.check_native_library('libprofiled')
552    self._checker.check_prefer64_library('libartserviced')
553
554    # Check internal libraries for Managed Core Library.
555    self._checker.check_native_library('libopenjdkd')
556
557    # Check internal native library dependencies.
558    #
559    # Like in the release package, we check that we don't get other dependencies
560    # besides those listed here. In this case the concern is not bloat, but
561    # rather that we don't get behavioural differences between user (release)
562    # and userdebug/eng builds, which could happen if the debug package has
563    # duplicate library instances where releases don't. In other words, it's
564    # uncontroversial to add debug-only dependencies, as long as they don't make
565    # assumptions on having a single global state (ideally they should have
566    # double_loadable:true, cf. go/double_loadable). Also, like in the release
567    # package we need to look out for dependencies that should go through
568    # exported library stubs (until b/128708192 is fixed).
569    #
570    # (There are currently no debug-only native libraries.)
571
572
573class TestingChecker:
574  def __init__(self, checker):
575    self._checker = checker
576
577  def __str__(self):
578    return 'Testing Checker'
579
580  def run(self):
581    # Check test directories.
582    self._checker.check_dir(ART_TEST_DIR)
583    for arch_dir in self._checker.arch_dirs_for_path(ART_TEST_DIR):
584      self._checker.check_dir(arch_dir)
585
586    # Check ART test binaries.
587    self._checker.check_art_test_executable('art_cmdline_tests')
588    self._checker.check_art_test_executable('art_compiler_tests')
589    self._checker.check_art_test_executable('art_dex2oat_tests')
590    self._checker.check_art_test_executable('art_dexanalyze_tests')
591    self._checker.check_art_test_executable('art_dexdump_tests')
592    self._checker.check_art_test_executable('art_dexlist_tests')
593    self._checker.check_art_test_executable('art_dexoptanalyzer_tests')
594    self._checker.check_art_test_executable('art_disassembler_tests')
595    self._checker.check_art_test_executable('art_imgdiag_tests')
596    self._checker.check_art_test_executable('art_libartbase_tests')
597    self._checker.check_art_test_executable('art_libdexfile_support_tests')
598    self._checker.check_art_test_executable('art_libdexfile_tests')
599    self._checker.check_art_test_executable('art_libprofile_tests')
600    self._checker.check_art_test_executable('art_oatdump_tests')
601    self._checker.check_art_test_executable('art_odrefresh_tests', MULTILIB_FIRST)
602    self._checker.check_art_test_executable('art_profman_tests')
603    self._checker.check_art_test_executable('art_runtime_tests')
604    self._checker.check_art_test_executable('art_sigchain_tests')
605
606    # Check ART test tools.
607    self._checker.check_executable('signal_dumper')
608
609    # Check ART jar files which are needed for gtests.
610    self._checker.check_art_test_data('art-gtest-jars-AbstractMethod.jar')
611    self._checker.check_art_test_data('art-gtest-jars-ArrayClassWithUnresolvedComponent.dex')
612    self._checker.check_art_test_data('art-gtest-jars-MyClassNatives.jar')
613    self._checker.check_art_test_data('art-gtest-jars-Main.jar')
614    self._checker.check_art_test_data('art-gtest-jars-ProtoCompare.jar')
615    self._checker.check_art_test_data('art-gtest-jars-Transaction.jar')
616    self._checker.check_art_test_data('art-gtest-jars-VerifierDepsMulti.dex')
617    self._checker.check_art_test_data('art-gtest-jars-Nested.jar')
618    self._checker.check_art_test_data('art-gtest-jars-MyClass.jar')
619    self._checker.check_art_test_data('art-gtest-jars-ManyMethods.jar')
620    self._checker.check_art_test_data('art-gtest-jars-GetMethodSignature.jar')
621    self._checker.check_art_test_data('art-gtest-jars-Lookup.jar')
622    self._checker.check_art_test_data('art-gtest-jars-Instrumentation.jar')
623    self._checker.check_art_test_data('art-gtest-jars-MainUncompressedAligned.jar')
624    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderD.jar')
625    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderC.jar')
626    self._checker.check_art_test_data('art-gtest-jars-ErroneousA.jar')
627    self._checker.check_art_test_data('art-gtest-jars-HiddenApiSignatures.jar')
628    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderB.jar')
629    self._checker.check_art_test_data('art-gtest-jars-LinkageTest.dex')
630    self._checker.check_art_test_data('art-gtest-jars-MethodTypes.jar')
631    self._checker.check_art_test_data('art-gtest-jars-ErroneousInit.jar')
632    self._checker.check_art_test_data('art-gtest-jars-VerifierDeps.dex')
633    self._checker.check_art_test_data('art-gtest-jars-StringLiterals.jar')
634    self._checker.check_art_test_data('art-gtest-jars-XandY.jar')
635    self._checker.check_art_test_data('art-gtest-jars-ExceptionHandle.jar')
636    self._checker.check_art_test_data('art-gtest-jars-ImageLayoutB.jar')
637    self._checker.check_art_test_data('art-gtest-jars-Interfaces.jar')
638    self._checker.check_art_test_data('art-gtest-jars-IMTB.jar')
639    self._checker.check_art_test_data('art-gtest-jars-Extension2.jar')
640    self._checker.check_art_test_data('art-gtest-jars-Extension1.jar')
641    self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressedAligned.jar')
642    self._checker.check_art_test_data('art-gtest-jars-ErroneousB.jar')
643    self._checker.check_art_test_data('art-gtest-jars-MultiDexModifiedSecondary.jar')
644    self._checker.check_art_test_data('art-gtest-jars-NonStaticLeafMethods.jar')
645    self._checker.check_art_test_data('art-gtest-jars-DefaultMethods.jar')
646    self._checker.check_art_test_data('art-gtest-jars-MultiDexUncompressedAligned.jar')
647    self._checker.check_art_test_data('art-gtest-jars-StaticsFromCode.jar')
648    self._checker.check_art_test_data('art-gtest-jars-ProfileTestMultiDex.jar')
649    self._checker.check_art_test_data('art-gtest-jars-VerifySoftFailDuringClinit.dex')
650    self._checker.check_art_test_data('art-gtest-jars-MainStripped.jar')
651    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderA.jar')
652    self._checker.check_art_test_data('art-gtest-jars-StaticLeafMethods.jar')
653    self._checker.check_art_test_data('art-gtest-jars-MultiDex.jar')
654    self._checker.check_art_test_data('art-gtest-jars-Packages.jar')
655    self._checker.check_art_test_data('art-gtest-jars-ProtoCompare2.jar')
656    self._checker.check_art_test_data('art-gtest-jars-Statics.jar')
657    self._checker.check_art_test_data('art-gtest-jars-AllFields.jar')
658    self._checker.check_art_test_data('art-gtest-jars-IMTA.jar')
659    self._checker.check_art_test_data('art-gtest-jars-ImageLayoutA.jar')
660    self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressed.jar')
661    self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexTestDex.jar')
662    self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexPublicSdkDex.dex')
663    self._checker.check_art_test_data('art-gtest-jars-SuperWithAccessChecks.dex')
664
665    # Fuzzer cases
666    self._checker.check_art_test_data('dex_verification_fuzzer_corpus.zip')
667    self._checker.check_art_test_data('class_verification_fuzzer_corpus.zip')
668
669
670class NoSuperfluousFilesChecker:
671  def __init__(self, checker):
672    self._checker = checker
673
674  def __str__(self):
675    return 'No superfluous files checker'
676
677  def run(self):
678    self._checker.check_no_superfluous_files()
679
680
681class List:
682  def __init__(self, provider, print_size=False):
683    self._provider = provider
684    self._print_size = print_size
685
686  def print_list(self):
687
688    def print_list_rec(path):
689      apex_map = self._provider.read_dir(path)
690      if apex_map is None:
691        return
692      apex_map = dict(apex_map)
693      if '.' in apex_map:
694        del apex_map['.']
695      if '..' in apex_map:
696        del apex_map['..']
697      for (_, val) in sorted(apex_map.items()):
698        val_path = os.path.join(path, val.name)
699        if self._print_size:
700          if val.size < 0:
701            print('[    n/a    ]  %s' % val_path)
702          else:
703            print('[%11d]  %s' % (val.size, val_path))
704        else:
705          print(val_path)
706        if val.is_dir:
707          print_list_rec(val_path)
708
709    print_list_rec('')
710
711
712class Tree:
713  def __init__(self, provider, title, print_size=False):
714    print('%s' % title)
715    self._provider = provider
716    self._has_next_list = []
717    self._print_size = print_size
718
719  @staticmethod
720  def get_vertical(has_next_list):
721    string = ''
722    for v in has_next_list:
723      string += '%s   ' % ('│' if v else ' ')
724    return string
725
726  @staticmethod
727  def get_last_vertical(last):
728    return '└── ' if last else '├── '
729
730  def print_tree(self):
731
732    def print_tree_rec(path):
733      apex_map = self._provider.read_dir(path)
734      if apex_map is None:
735        return
736      apex_map = dict(apex_map)
737      if '.' in apex_map:
738        del apex_map['.']
739      if '..' in apex_map:
740        del apex_map['..']
741      key_list = list(sorted(apex_map.keys()))
742      for i, key in enumerate(key_list):
743        prev = self.get_vertical(self._has_next_list)
744        last = self.get_last_vertical(i == len(key_list) - 1)
745        val = apex_map[key]
746        if self._print_size:
747          if val.size < 0:
748            print('%s%s[    n/a    ]  %s' % (prev, last, val.name))
749          else:
750            print('%s%s[%11d]  %s' % (prev, last, val.size, val.name))
751        else:
752          print('%s%s%s' % (prev, last, val.name))
753        if val.is_dir:
754          self._has_next_list.append(i < len(key_list) - 1)
755          val_path = os.path.join(path, val.name)
756          print_tree_rec(val_path)
757          self._has_next_list.pop()
758
759    print_tree_rec('')
760
761
762# Note: do not sys.exit early, for __del__ cleanup.
763def art_apex_test_main(test_args):
764  if test_args.list and test_args.tree:
765    logging.error('Both of --list and --tree set')
766    return 1
767  if test_args.size and not (test_args.list or test_args.tree):
768    logging.error('--size set but neither --list nor --tree set')
769    return 1
770  if not test_args.flattened and not test_args.tmpdir:
771    logging.error('Need a tmpdir.')
772    return 1
773  if not test_args.flattened:
774    if not test_args.deapexer:
775      logging.error('Need deapexer.')
776      return 1
777    if not test_args.debugfs:
778      logging.error('Need debugfs.')
779      return 1
780    if not test_args.fsckerofs:
781      logging.error('Need fsck.erofs.')
782      return 1
783
784  if test_args.flavor == FLAVOR_AUTO:
785    logging.warning('--flavor=auto, trying to autodetect. This may be incorrect!')
786    # The order of flavors in the list below matters, as the release tag (empty string) will
787    # match any package name.
788    for flavor in [ FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_RELEASE ]:
789      flavor_tag = flavor
790      # Special handling for the release flavor, whose name is no longer part of the Release ART
791      # APEX file name (`com.android.art.capex` / `com.android.art`).
792      if flavor == FLAVOR_RELEASE:
793        flavor_tag = ''
794      flavor_pattern = '*.%s*' % flavor_tag
795      if fnmatch.fnmatch(test_args.apex, flavor_pattern):
796        test_args.flavor = flavor
797        logging.warning('  Detected %s flavor', flavor)
798        break
799    if test_args.flavor == FLAVOR_AUTO:
800      logging.error('  Could not detect APEX flavor, neither %s, %s nor %s for \'%s\'',
801                  FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, test_args.apex)
802      return 1
803
804  apex_dir = test_args.apex
805  if not test_args.flattened:
806    # Extract the apex. It would be nice to use the output from "deapexer list"
807    # to avoid this work, but it doesn't provide info about executable bits.
808    apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs,
809                            test_args.fsckerofs, test_args.tmpdir)
810  apex_provider = TargetApexProvider(apex_dir)
811
812  if test_args.tree:
813    Tree(apex_provider, test_args.apex, test_args.size).print_tree()
814    return 0
815  if test_args.list:
816    List(apex_provider, test_args.size).print_list()
817    return 0
818
819  checkers = []
820  if test_args.bitness == BITNESS_AUTO:
821    logging.warning('--bitness=auto, trying to autodetect. This may be incorrect!')
822    has_32 = apex_provider.get('lib') is not None
823    has_64 = apex_provider.get('lib64') is not None
824    if has_32 and has_64:
825      logging.warning('  Detected multilib')
826      test_args.bitness = BITNESS_MULTILIB
827    elif has_32:
828      logging.warning('  Detected 32-only')
829      test_args.bitness = BITNESS_32
830    elif has_64:
831      logging.warning('  Detected 64-only')
832      test_args.bitness = BITNESS_64
833    else:
834      logging.error('  Could not detect bitness, neither lib nor lib64 contained.')
835      List(apex_provider).print_list()
836      return 1
837
838  if test_args.bitness == BITNESS_32:
839    base_checker = Arch32Checker(apex_provider)
840  elif test_args.bitness == BITNESS_64:
841    base_checker = Arch64Checker(apex_provider)
842  else:
843    assert test_args.bitness == BITNESS_MULTILIB
844    base_checker = MultilibChecker(apex_provider)
845
846  checkers.append(ReleaseChecker(base_checker))
847  if test_args.flavor == FLAVOR_DEBUG or test_args.flavor == FLAVOR_TESTING:
848    checkers.append(DebugChecker(base_checker))
849  if test_args.flavor == FLAVOR_TESTING:
850    checkers.append(TestingChecker(base_checker))
851
852  # This checker must be last.
853  checkers.append(NoSuperfluousFilesChecker(base_checker))
854
855  failed = False
856  for checker in checkers:
857    logging.info('%s...', checker)
858    checker.run()
859    if base_checker.error_count() > 0:
860      logging.error('%s FAILED', checker)
861      failed = True
862    else:
863      logging.info('%s SUCCEEDED', checker)
864    base_checker.reset_errors()
865
866  return 1 if failed else 0
867
868
869def art_apex_test_default(test_parser):
870  if 'ANDROID_PRODUCT_OUT' not in os.environ:
871    logging.error('No-argument use requires ANDROID_PRODUCT_OUT')
872    sys.exit(1)
873  product_out = os.environ['ANDROID_PRODUCT_OUT']
874  if 'ANDROID_HOST_OUT' not in os.environ:
875    logging.error('No-argument use requires ANDROID_HOST_OUT')
876    sys.exit(1)
877  host_out = os.environ['ANDROID_HOST_OUT']
878
879  test_args = test_parser.parse_args(['unused'])  # For consistency.
880  test_args.debugfs = '%s/bin/debugfs' % host_out
881  test_args.fsckerofs = '%s/bin/fsck.erofs' % host_out
882  test_args.tmpdir = '.'
883  test_args.tree = False
884  test_args.list = False
885  test_args.bitness = BITNESS_AUTO
886  failed = False
887
888  if not os.path.exists(test_args.debugfs):
889    logging.error('Cannot find debugfs (default path %s). Please build it, e.g., m debugfs',
890                  test_args.debugfs)
891    sys.exit(1)
892
893  # TODO: Add support for flattened APEX packages.
894  configs = [
895    {'name': 'com.android.art.capex',         'flavor': FLAVOR_RELEASE},
896    {'name': 'com.android.art.debug.capex',   'flavor': FLAVOR_DEBUG},
897    # Note: The Testing ART APEX is not a Compressed APEX.
898    {'name': 'com.android.art.testing.apex',  'flavor': FLAVOR_TESTING},
899  ]
900
901  for config in configs:
902    logging.info(config['name'])
903    test_args.apex = '%s/system/apex/%s' % (product_out, config['name'])
904    if not os.path.exists(test_args.apex):
905      failed = True
906      logging.error('Cannot find APEX %s. Please build it first.', test_args.apex)
907      continue
908    test_args.flavor = config['flavor']
909    failed = art_apex_test_main(test_args) != 0
910
911  if failed:
912    sys.exit(1)
913
914
915if __name__ == '__main__':
916  parser = argparse.ArgumentParser(description='Check integrity of an ART APEX.')
917
918  parser.add_argument('apex', help='APEX file input')
919
920  parser.add_argument('--flattened', help='Check as flattened (target) APEX', action='store_true')
921
922  parser.add_argument('--flavor', help='Check as FLAVOR APEX', choices=FLAVORS_ALL,
923                      default=FLAVOR_AUTO)
924
925  parser.add_argument('--list', help='List all files', action='store_true')
926  parser.add_argument('--tree', help='Print directory tree', action='store_true')
927  parser.add_argument('--size', help='Print file sizes', action='store_true')
928
929  parser.add_argument('--tmpdir', help='Directory for temp files')
930  parser.add_argument('--deapexer', help='Path to deapexer')
931  parser.add_argument('--debugfs', help='Path to debugfs')
932  parser.add_argument('--fsckerofs', help='Path to fsck.erofs')
933
934  parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL,
935                      default=BITNESS_AUTO)
936
937  if len(sys.argv) == 1:
938    art_apex_test_default(parser)
939  else:
940    args = parser.parse_args()
941
942    if args is None:
943      sys.exit(1)
944
945    exit_code = art_apex_test_main(args)
946    sys.exit(exit_code)
947