• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
27import zipfile
28
29logging.basicConfig(format='%(message)s')
30
31# Flavors of ART APEX package.
32FLAVOR_RELEASE = 'release'
33FLAVOR_DEBUG = 'debug'
34FLAVOR_TESTING = 'testing'
35FLAVOR_AUTO = 'auto'
36FLAVORS_ALL = [FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_AUTO]
37
38# Bitness options for APEX package
39BITNESS_32 = '32'
40BITNESS_64 = '64'
41BITNESS_MULTILIB = 'multilib'
42BITNESS_AUTO = 'auto'
43BITNESS_ALL = [BITNESS_32, BITNESS_64, BITNESS_MULTILIB, BITNESS_AUTO]
44
45# Architectures supported by APEX packages.
46ARCHS_32 = ["arm", "x86"]
47ARCHS_64 = ["arm64", "x86_64"]
48
49# Multilib options
50MULTILIB_32 = '32'
51MULTILIB_64 = '64'
52MULTILIB_BOTH = 'both'
53MULTILIB_FIRST = 'first'
54
55# Directory containing ART tests within an ART APEX (if the package includes
56# any). ART test executables are installed in `bin/art/<arch>`. Segregating
57# tests by architecture is useful on devices supporting more than one
58# architecture, as it permits testing all of them using a single ART APEX
59# package.
60ART_TEST_DIR = 'bin/art'
61
62
63# Test if a given variable is set to a string "true".
64def isEnvTrue(var):
65  return var in os.environ and os.environ[var] == 'true'
66
67
68def extract_apex(apex_path, deapexer_path, debugfs_path, 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                         'extract', apex_path, extract_path],
75                        stdout=subprocess.DEVNULL)
76  return extract_path
77
78
79class FSObject:
80  def __init__(self, name, is_dir, is_exec, is_symlink, size):
81    self.name = name
82    self.is_dir = is_dir
83    self.is_exec = is_exec
84    self.is_symlink = is_symlink
85    self.size = size
86
87  def __str__(self):
88    return '%s(dir=%r,exec=%r,symlink=%r,size=%d)' \
89             % (self.name, self.is_dir, self.is_exec, self.is_symlink, self.size)
90
91
92class TargetApexProvider:
93  def __init__(self, apex):
94    self._folder_cache = {}
95    self._apex = apex
96
97  def get(self, path):
98    apex_dir, name = os.path.split(path)
99    if not apex_dir:
100      apex_dir = '.'
101    apex_map = self.read_dir(apex_dir)
102    return apex_map[name] if name in apex_map else None
103
104  def read_dir(self, apex_dir):
105    if apex_dir in self._folder_cache:
106      return self._folder_cache[apex_dir]
107    apex_map = {}
108    dirname = os.path.join(self._apex, apex_dir)
109    if os.path.exists(dirname):
110      for basename in os.listdir(dirname):
111        filepath = os.path.join(dirname, basename)
112        is_dir = os.path.isdir(filepath)
113        is_exec = os.access(filepath, os.X_OK)
114        is_symlink = os.path.islink(filepath)
115        if is_symlink:
116          # Report the length of the symlink's target's path as file size, like `ls`.
117          size = len(os.readlink(filepath))
118        else:
119          size = os.path.getsize(filepath)
120        apex_map[basename] = FSObject(basename, is_dir, is_exec, is_symlink, size)
121    self._folder_cache[apex_dir] = apex_map
122    return apex_map
123
124
125class HostApexProvider:
126  def __init__(self, apex, tmpdir):
127    self._tmpdir = tmpdir
128    self._folder_cache = {}
129    self._payload = os.path.join(self._tmpdir, 'apex_payload.zip')
130    # Extract payload to tmpdir.
131    apex_zip = zipfile.ZipFile(apex)
132    apex_zip.extract('apex_payload.zip', tmpdir)
133
134  def __del__(self):
135    # Delete temps.
136    if os.path.exists(self._payload):
137      os.remove(self._payload)
138
139  def get(self, path):
140    apex_dir, name = os.path.split(path)
141    if not apex_dir:
142      apex_dir = ''
143    apex_map = self.read_dir(apex_dir)
144    return apex_map[name] if name in apex_map else None
145
146  def read_dir(self, apex_dir):
147    if apex_dir in self._folder_cache:
148      return self._folder_cache[apex_dir]
149    if not self._folder_cache:
150      self.parse_zip()
151    if apex_dir in self._folder_cache:
152      return self._folder_cache[apex_dir]
153    return {}
154
155  def parse_zip(self):
156    apex_zip = zipfile.ZipFile(self._payload)
157    infos = apex_zip.infolist()
158    for zipinfo in infos:
159      path = zipinfo.filename
160
161      # Assume no empty file is stored.
162      assert path
163
164      def get_octal(val, index):
165        return (val >> (index * 3)) & 0x7
166
167      def bits_is_exec(val):
168        # TODO: Enforce group/other, too?
169        return get_octal(val, 2) & 1 == 1
170
171      is_zipinfo = True
172      while path:
173        apex_dir, base = os.path.split(path)
174        # TODO: If directories are stored, base will be empty.
175
176        if apex_dir not in self._folder_cache:
177          self._folder_cache[apex_dir] = {}
178        dir_map = self._folder_cache[apex_dir]
179        if base not in dir_map:
180          if is_zipinfo:
181            bits = (zipinfo.external_attr >> 16) & 0xFFFF
182            is_dir = get_octal(bits, 4) == 4
183            is_symlink = get_octal(bits, 4) == 2
184            is_exec = bits_is_exec(bits)
185            size = zipinfo.file_size
186          else:
187            is_exec = False  # Seems we can't get this easily?
188            is_symlink = False
189            is_dir = True
190            # Use a negative value as an indicator of undefined/unknown size.
191            size = -1
192          dir_map[base] = FSObject(base, is_dir, is_exec, is_symlink, size)
193        is_zipinfo = False
194        path = apex_dir
195
196
197# DO NOT USE DIRECTLY! This is an "abstract" base class.
198class Checker:
199  def __init__(self, provider):
200    self._provider = provider
201    self._errors = 0
202    self._expected_file_globs = set()
203
204  def fail(self, msg, *fail_args):
205    self._errors += 1
206    logging.error(msg, *fail_args)
207
208  def error_count(self):
209    return self._errors
210
211  def reset_errors(self):
212    self._errors = 0
213
214  def is_file(self, path):
215    fs_object = self._provider.get(path)
216    if fs_object is None:
217      return False, 'Could not find %s'
218    if fs_object.is_dir:
219      return False, '%s is a directory'
220    return True, ''
221
222  def is_dir(self, path):
223    fs_object = self._provider.get(path)
224    if fs_object is None:
225      return False, 'Could not find %s'
226    if not fs_object.is_dir:
227      return False, '%s is not a directory'
228    return True, ''
229
230  def check_file(self, path):
231    ok, msg = self.is_file(path)
232    if not ok:
233      self.fail(msg, path)
234    self._expected_file_globs.add(path)
235    return ok
236
237  def check_executable(self, filename):
238    path = 'bin/%s' % filename
239    if not self.check_file(path):
240      return
241    if not self._provider.get(path).is_exec:
242      self.fail('%s is not executable', path)
243
244  def check_executable_symlink(self, filename):
245    path = 'bin/%s' % filename
246    fs_object = self._provider.get(path)
247    if fs_object is None:
248      self.fail('Could not find %s', path)
249      return
250    if fs_object.is_dir:
251      self.fail('%s is a directory', path)
252      return
253    if not fs_object.is_symlink:
254      self.fail('%s is not a symlink', path)
255    self._expected_file_globs.add(path)
256
257  def arch_dirs_for_path(self, path, multilib=None):
258    # Look for target-specific subdirectories for the given directory path.
259    # This is needed because the list of build targets is not propagated
260    # to this script.
261    #
262    # TODO(b/123602136): Pass build target information to this script and fix
263    # all places where this function in used (or similar workarounds).
264    dirs = []
265    for arch in self.possible_archs(multilib):
266      dir = '%s/%s' % (path, arch)
267      found, _ = self.is_dir(dir)
268      if found:
269        dirs.append(dir)
270    return dirs
271
272  def check_art_test_executable(self, filename, multilib=None):
273    dirs = self.arch_dirs_for_path(ART_TEST_DIR, multilib)
274    if not dirs:
275      self.fail('ART test binary missing: %s', filename)
276    for dir in dirs:
277      test_path = '%s/%s' % (dir, filename)
278      self._expected_file_globs.add(test_path)
279      if not self._provider.get(test_path).is_exec:
280        self.fail('%s is not executable', test_path)
281
282  def check_art_test_data(self, filename):
283    dirs = self.arch_dirs_for_path(ART_TEST_DIR)
284    if not dirs:
285      self.fail('ART test data missing: %s', filename)
286    for dir in dirs:
287      if not self.check_file('%s/%s' % (dir, filename)):
288        return
289
290  def check_single_library(self, filename):
291    lib_path = 'lib/%s' % filename
292    lib64_path = 'lib64/%s' % filename
293    lib_is_file, _ = self.is_file(lib_path)
294    if lib_is_file:
295      self._expected_file_globs.add(lib_path)
296    lib64_is_file, _ = self.is_file(lib64_path)
297    if lib64_is_file:
298      self._expected_file_globs.add(lib64_path)
299    if not lib_is_file and not lib64_is_file:
300      self.fail('Library missing: %s', filename)
301
302  def check_dexpreopt(self, basename):
303    dirs = self.arch_dirs_for_path('javalib')
304    for dir in dirs:
305      for ext in ['art', 'oat', 'vdex']:
306        self.check_file('%s/%s.%s' % (dir, basename, ext))
307
308  def check_java_library(self, basename):
309    return self.check_file('javalib/%s.jar' % basename)
310
311  def ignore_path(self, path_glob):
312    self._expected_file_globs.add(path_glob)
313
314  def check_optional_art_test_executable(self, filename):
315    for arch in self.possible_archs():
316      self.ignore_path('%s/%s/%s' % (ART_TEST_DIR, arch, filename))
317
318  def check_no_superfluous_files(self, dir_path):
319    paths = []
320    for name in sorted(self._provider.read_dir(dir_path).keys()):
321      if name not in ('.', '..'):
322        paths.append(os.path.join(dir_path, name))
323    expected_paths = set()
324    dir_prefix = dir_path + '/'
325    for path_glob in self._expected_file_globs:
326      expected_paths |= set(fnmatch.filter(paths, path_glob))
327      # If there are globs in subdirectories of dir_path we want to match their
328      # path segments at this directory level.
329      if path_glob.startswith(dir_prefix):
330        subpath = path_glob[len(dir_prefix):]
331        subpath_first_segment, _, _ = subpath.partition('/')
332        expected_paths |= set(fnmatch.filter(paths, dir_prefix + subpath_first_segment))
333    for unexpected_path in set(paths) - expected_paths:
334      self.fail('Unexpected file \'%s\'', unexpected_path)
335
336  # Just here for docs purposes, even if it isn't good Python style.
337
338  def check_symlinked_multilib_executable(self, filename):
339    """Check bin/filename32, and/or bin/filename64, with symlink bin/filename."""
340    raise NotImplementedError
341
342  def check_symlinked_first_executable(self, filename):
343    """Check bin/filename32, and/or bin/filename64, with symlink bin/filename."""
344    raise NotImplementedError
345
346  def check_native_library(self, basename):
347    """Check lib/basename.so, and/or lib64/basename.so."""
348    raise NotImplementedError
349
350  def check_optional_native_library(self, basename_glob):
351    """Allow lib/basename.so and/or lib64/basename.so to exist."""
352    raise NotImplementedError
353
354  def check_prefer64_library(self, basename):
355    """Check lib64/basename.so, or lib/basename.so on 32 bit only."""
356    raise NotImplementedError
357
358  def possible_archs(self, multilib=None):
359    """Returns names of possible archs."""
360    raise NotImplementedError
361
362class Arch32Checker(Checker):
363  def check_symlinked_multilib_executable(self, filename):
364    self.check_executable('%s32' % filename)
365    self.check_executable_symlink(filename)
366
367  def check_symlinked_first_executable(self, filename):
368    self.check_executable('%s32' % filename)
369    self.check_executable_symlink(filename)
370
371  def check_native_library(self, basename):
372    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
373    # the precision of this test?
374    self.check_file('lib/%s.so' % basename)
375
376  def check_optional_native_library(self, basename_glob):
377    self.ignore_path('lib/%s.so' % basename_glob)
378
379  def check_prefer64_library(self, basename):
380    self.check_native_library(basename)
381
382  def possible_archs(self, multilib=None):
383    return ARCHS_32
384
385class Arch64Checker(Checker):
386  def check_symlinked_multilib_executable(self, filename):
387    self.check_executable('%s64' % filename)
388    self.check_executable_symlink(filename)
389
390  def check_symlinked_first_executable(self, filename):
391    self.check_executable('%s64' % filename)
392    self.check_executable_symlink(filename)
393
394  def check_native_library(self, basename):
395    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
396    # the precision of this test?
397    self.check_file('lib64/%s.so' % basename)
398
399  def check_optional_native_library(self, basename_glob):
400    self.ignore_path('lib64/%s.so' % basename_glob)
401
402  def check_prefer64_library(self, basename):
403    self.check_native_library(basename)
404
405  def possible_archs(self, multilib=None):
406    return ARCHS_64
407
408
409class MultilibChecker(Checker):
410  def check_symlinked_multilib_executable(self, filename):
411    self.check_executable('%s32' % filename)
412    self.check_executable('%s64' % filename)
413    self.check_executable_symlink(filename)
414
415  def check_symlinked_first_executable(self, filename):
416    self.check_executable('%s64' % filename)
417    self.check_executable_symlink(filename)
418
419  def check_native_library(self, basename):
420    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
421    # the precision of this test?
422    self.check_file('lib/%s.so' % basename)
423    self.check_file('lib64/%s.so' % basename)
424
425  def check_optional_native_library(self, basename_glob):
426    self.ignore_path('lib/%s.so' % basename_glob)
427    self.ignore_path('lib64/%s.so' % basename_glob)
428
429  def check_prefer64_library(self, basename):
430    self.check_file('lib64/%s.so' % basename)
431
432  def possible_archs(self, multilib=None):
433    if multilib is None or multilib == MULTILIB_BOTH:
434      return ARCHS_32 + ARCHS_64
435    if multilib == MULTILIB_FIRST or multilib == MULTILIB_64:
436      return ARCHS_64
437    elif multilib == MULTILIB_32:
438      return ARCHS_32
439    self.fail('Unrecognized multilib option "%s"', multilib)
440
441
442class ReleaseChecker:
443  def __init__(self, checker):
444    self._checker = checker
445
446  def __str__(self):
447    return 'Release Checker'
448
449  def run(self):
450    # Check the Protocol Buffers APEX manifest.
451    self._checker.check_file('apex_manifest.pb')
452
453    # Check binaries for ART.
454    self._checker.check_executable('dexdump')
455    self._checker.check_executable('dexlist')
456    self._checker.check_executable('dexoptanalyzer')
457    self._checker.check_executable('profman')
458    self._checker.check_symlinked_multilib_executable('dalvikvm')
459
460    # Check exported libraries for ART.
461    self._checker.check_native_library('libdexfile')
462    self._checker.check_native_library('libnativebridge')
463    self._checker.check_native_library('libnativehelper')
464    self._checker.check_native_library('libnativeloader')
465
466    # Check internal libraries for ART.
467    self._checker.check_native_library('libadbconnection')
468    self._checker.check_native_library('libart')
469    self._checker.check_native_library('libart-compiler')
470    self._checker.check_native_library('libart-dexlayout')
471    self._checker.check_native_library('libart-disassembler')
472    self._checker.check_native_library('libartbase')
473    self._checker.check_native_library('libartpalette')
474    self._checker.check_native_library('libartservice')
475    self._checker.check_native_library('libarttools')
476    self._checker.check_native_library('libdt_fd_forward')
477    self._checker.check_native_library('libopenjdkjvm')
478    self._checker.check_native_library('libopenjdkjvmti')
479    self._checker.check_native_library('libprofile')
480    self._checker.check_native_library('libsigchain')
481
482    # Check Java libraries for Managed Core Library.
483    self._checker.check_java_library('apache-xml')
484    self._checker.check_java_library('bouncycastle')
485    self._checker.check_java_library('core-libart')
486    self._checker.check_java_library('core-oj')
487    self._checker.check_java_library('okhttp')
488    if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'):
489      # In coverage builds jacoco is added to the list of ART apex jars.
490      self._checker.check_java_library('jacocoagent')
491
492    # Check internal native libraries for Managed Core Library.
493    self._checker.check_native_library('libjavacore')
494    self._checker.check_native_library('libopenjdk')
495
496    # Check internal native library dependencies.
497    #
498    # Any internal dependency not listed here will cause a failure in
499    # NoSuperfluousLibrariesChecker. Internal dependencies are generally just
500    # implementation details, but in the release package we want to track them
501    # because a) they add to the package size and the RAM usage (in particular
502    # if the library is also present in /system or another APEX and hence might
503    # get loaded twice through linker namespace separation), and b) we need to
504    # catch invalid dependencies on /system or other APEXes that should go
505    # through an exported library with stubs (b/128708192 tracks implementing a
506    # better approach for that).
507    self._checker.check_native_library('libbacktrace')
508    self._checker.check_native_library('libbase')
509    self._checker.check_native_library('libc++')
510    self._checker.check_native_library('libdt_socket')
511    self._checker.check_native_library('libjdwp')
512    self._checker.check_native_library('liblz4')
513    self._checker.check_native_library('liblzma')
514    self._checker.check_native_library('libnpt')
515    self._checker.check_native_library('libunwindstack')
516    self._checker.check_native_library('libziparchive')
517
518    # Allow extra dependencies that appear in ASAN builds.
519    self._checker.check_optional_native_library('libclang_rt.asan*')
520    self._checker.check_optional_native_library('libclang_rt.hwasan*')
521    self._checker.check_optional_native_library('libclang_rt.ubsan*')
522
523    # Check dexpreopt files for libcore bootclasspath jars.
524    self._checker.check_dexpreopt('boot')
525    self._checker.check_dexpreopt('boot-apache-xml')
526    self._checker.check_dexpreopt('boot-bouncycastle')
527    self._checker.check_dexpreopt('boot-core-libart')
528    self._checker.check_dexpreopt('boot-okhttp')
529    if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'):
530      # In coverage builds the ART boot image includes jacoco.
531      self._checker.check_dexpreopt('boot-jacocoagent')
532
533class ReleaseTargetChecker:
534  def __init__(self, checker):
535    self._checker = checker
536
537  def __str__(self):
538    return 'Release (Target) Checker'
539
540  def run(self):
541    # We don't check for the presence of the JSON APEX manifest (file
542    # `apex_manifest.json`, only present in target APEXes), as it is only
543    # included for compatibility reasons with Android Q and will likely be
544    # removed in Android R.
545
546    # Check binaries for ART.
547    self._checker.check_executable('artd')
548    self._checker.check_executable('oatdump')
549    self._checker.check_executable("odrefresh")
550    self._checker.check_symlinked_multilib_executable('dex2oat')
551
552    # Check internal libraries for ART.
553    self._checker.check_native_library('libperfetto_hprof')
554    self._checker.check_prefer64_library('artd-aidl-ndk')
555
556    # Check internal Java libraries
557    self._checker.check_java_library("service-art")
558
559    # Check exported native libraries for Managed Core Library.
560    self._checker.check_native_library('libandroidio')
561
562    # Check internal native library dependencies.
563    self._checker.check_native_library('libcrypto')
564    self._checker.check_native_library('libexpat')
565
566
567class ReleaseHostChecker:
568  def __init__(self, checker):
569    self._checker = checker
570
571  def __str__(self):
572    return 'Release (Host) Checker'
573
574  def run(self):
575    # Check binaries for ART.
576    self._checker.check_executable('hprof-conv')
577    self._checker.check_symlinked_first_executable('dex2oatd')
578    self._checker.check_symlinked_first_executable('dex2oat')
579
580    # Check exported native libraries for Managed Core Library.
581    self._checker.check_native_library('libicu')
582    self._checker.check_native_library('libandroidio')
583
584    # Check internal libraries for Managed Core Library.
585    self._checker.check_native_library('libexpat-host')
586    self._checker.check_native_library('libz-host')
587
588
589class DebugChecker:
590  def __init__(self, checker):
591    self._checker = checker
592
593  def __str__(self):
594    return 'Debug Checker'
595
596  def run(self):
597    # Check binaries for ART.
598    self._checker.check_executable('dexdiag')
599    self._checker.check_executable('dexanalyze')
600    self._checker.check_executable('dexlayout')
601    self._checker.check_symlinked_multilib_executable('imgdiag')
602
603    # Check debug binaries for ART.
604    self._checker.check_executable('dexlayoutd')
605    self._checker.check_executable('dexoptanalyzerd')
606    self._checker.check_symlinked_multilib_executable('imgdiagd')
607    self._checker.check_executable('profmand')
608
609    # Check exported libraries for ART.
610    self._checker.check_native_library('libdexfiled')
611
612    # Check internal libraries for ART.
613    self._checker.check_native_library('libadbconnectiond')
614    self._checker.check_native_library('libartbased')
615    self._checker.check_native_library('libartd')
616    self._checker.check_native_library('libartd-compiler')
617    self._checker.check_native_library('libartd-dexlayout')
618    self._checker.check_native_library('libartd-disassembler')
619    self._checker.check_native_library('libopenjdkjvmd')
620    self._checker.check_native_library('libopenjdkjvmtid')
621    self._checker.check_native_library('libprofiled')
622
623    # Check internal libraries for Managed Core Library.
624    self._checker.check_native_library('libopenjdkd')
625
626
627class DebugTargetChecker:
628  def __init__(self, checker):
629    self._checker = checker
630
631  def __str__(self):
632    return 'Debug (Target) Checker'
633
634  def run(self):
635    # Check ART debug binaries.
636    self._checker.check_executable('oatdumpd')
637    self._checker.check_symlinked_multilib_executable('dex2oatd')
638
639    # Check ART internal libraries.
640    self._checker.check_native_library('libperfetto_hprofd')
641
642    # Check internal native library dependencies.
643    #
644    # Like in the release package, we check that we don't get other dependencies
645    # besides those listed here. In this case the concern is not bloat, but
646    # rather that we don't get behavioural differences between user (release)
647    # and userdebug/eng builds, which could happen if the debug package has
648    # duplicate library instances where releases don't. In other words, it's
649    # uncontroversial to add debug-only dependencies, as long as they don't make
650    # assumptions on having a single global state (ideally they should have
651    # double_loadable:true, cf. go/double_loadable). Also, like in the release
652    # package we need to look out for dependencies that should go through
653    # exported library stubs (until b/128708192 is fixed).
654    self._checker.check_prefer64_library('libmeminfo')
655    self._checker.check_prefer64_library('libprocinfo')
656
657
658class TestingTargetChecker:
659  def __init__(self, checker):
660    self._checker = checker
661
662  def __str__(self):
663    return 'Testing (Target) Checker'
664
665  def run(self):
666    # Check ART test binaries.
667    self._checker.check_art_test_executable('art_cmdline_tests')
668    self._checker.check_art_test_executable('art_compiler_tests')
669    self._checker.check_art_test_executable('art_dex2oat_tests')
670    self._checker.check_art_test_executable('art_dexanalyze_tests')
671    self._checker.check_art_test_executable('art_dexdiag_tests')
672    self._checker.check_art_test_executable('art_dexdump_tests')
673    self._checker.check_art_test_executable('art_dexlayout_tests')
674    self._checker.check_art_test_executable('art_dexlist_tests')
675    self._checker.check_art_test_executable('art_dexoptanalyzer_tests')
676    self._checker.check_art_test_executable('art_imgdiag_tests')
677    self._checker.check_art_test_executable('art_libartbase_tests')
678    self._checker.check_art_test_executable('art_libartpalette_tests')
679    self._checker.check_art_test_executable('art_libartservice_tests')
680    self._checker.check_art_test_executable('art_libarttools_tests')
681    self._checker.check_art_test_executable('art_libdexfile_support_tests')
682    self._checker.check_art_test_executable('art_libdexfile_tests')
683    self._checker.check_art_test_executable('art_libprofile_tests')
684    self._checker.check_art_test_executable('art_oatdump_tests')
685    self._checker.check_art_test_executable('art_odrefresh_tests')
686    self._checker.check_art_test_executable('art_profman_tests')
687    self._checker.check_art_test_executable('art_runtime_compiler_tests')
688    self._checker.check_art_test_executable('art_runtime_tests')
689    self._checker.check_art_test_executable('art_sigchain_tests')
690
691    # Check ART test (internal) libraries.
692    self._checker.check_native_library('libartd-gtest')
693    self._checker.check_native_library('libartd-simulator-container')
694    self._checker.check_native_library('libartbased-testing')
695
696    # Check ART test tools.
697    self._checker.check_executable('signal_dumper')
698
699    # Check ART jar files which are needed for gtests.
700    self._checker.check_art_test_data('art-gtest-jars-AbstractMethod.jar')
701    self._checker.check_art_test_data('art-gtest-jars-MyClassNatives.jar')
702    self._checker.check_art_test_data('art-gtest-jars-Main.jar')
703    self._checker.check_art_test_data('art-gtest-jars-ProtoCompare.jar')
704    self._checker.check_art_test_data('art-gtest-jars-Transaction.jar')
705    self._checker.check_art_test_data('art-gtest-jars-VerifierDepsMulti.dex')
706    self._checker.check_art_test_data('art-gtest-jars-Nested.jar')
707    self._checker.check_art_test_data('art-gtest-jars-MyClass.jar')
708    self._checker.check_art_test_data('art-gtest-jars-ManyMethods.jar')
709    self._checker.check_art_test_data('art-gtest-jars-GetMethodSignature.jar')
710    self._checker.check_art_test_data('art-gtest-jars-Lookup.jar')
711    self._checker.check_art_test_data('art-gtest-jars-Instrumentation.jar')
712    self._checker.check_art_test_data('art-gtest-jars-MainUncompressedAligned.jar')
713    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderD.jar')
714    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderC.jar')
715    self._checker.check_art_test_data('art-gtest-jars-ErroneousA.jar')
716    self._checker.check_art_test_data('art-gtest-jars-HiddenApiSignatures.jar')
717    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderB.jar')
718    self._checker.check_art_test_data('art-gtest-jars-LinkageTest.dex')
719    self._checker.check_art_test_data('art-gtest-jars-MethodTypes.jar')
720    self._checker.check_art_test_data('art-gtest-jars-ErroneousInit.jar')
721    self._checker.check_art_test_data('art-gtest-jars-VerifierDeps.dex')
722    self._checker.check_art_test_data('art-gtest-jars-StringLiterals.jar')
723    self._checker.check_art_test_data('art-gtest-jars-XandY.jar')
724    self._checker.check_art_test_data('art-gtest-jars-ExceptionHandle.jar')
725    self._checker.check_art_test_data('art-gtest-jars-ImageLayoutB.jar')
726    self._checker.check_art_test_data('art-gtest-jars-Interfaces.jar')
727    self._checker.check_art_test_data('art-gtest-jars-IMTB.jar')
728    self._checker.check_art_test_data('art-gtest-jars-Extension2.jar')
729    self._checker.check_art_test_data('art-gtest-jars-Extension1.jar')
730    self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressedAligned.jar')
731    self._checker.check_art_test_data('art-gtest-jars-ErroneousB.jar')
732    self._checker.check_art_test_data('art-gtest-jars-MultiDexModifiedSecondary.jar')
733    self._checker.check_art_test_data('art-gtest-jars-NonStaticLeafMethods.jar')
734    self._checker.check_art_test_data('art-gtest-jars-DefaultMethods.jar')
735    self._checker.check_art_test_data('art-gtest-jars-MultiDexUncompressedAligned.jar')
736    self._checker.check_art_test_data('art-gtest-jars-StaticsFromCode.jar')
737    self._checker.check_art_test_data('art-gtest-jars-ProfileTestMultiDex.jar')
738    self._checker.check_art_test_data('art-gtest-jars-VerifySoftFailDuringClinit.dex')
739    self._checker.check_art_test_data('art-gtest-jars-MainStripped.jar')
740    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderA.jar')
741    self._checker.check_art_test_data('art-gtest-jars-StaticLeafMethods.jar')
742    self._checker.check_art_test_data('art-gtest-jars-MultiDex.jar')
743    self._checker.check_art_test_data('art-gtest-jars-Packages.jar')
744    self._checker.check_art_test_data('art-gtest-jars-ProtoCompare2.jar')
745    self._checker.check_art_test_data('art-gtest-jars-Statics.jar')
746    self._checker.check_art_test_data('art-gtest-jars-AllFields.jar')
747    self._checker.check_art_test_data('art-gtest-jars-IMTA.jar')
748    self._checker.check_art_test_data('art-gtest-jars-ImageLayoutA.jar')
749    self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressed.jar')
750    self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexTestDex.jar')
751    self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexPublicSdkDex.dex')
752
753
754class NoSuperfluousBinariesChecker:
755  def __init__(self, checker):
756    self._checker = checker
757
758  def __str__(self):
759    return 'No superfluous binaries checker'
760
761  def run(self):
762    self._checker.check_no_superfluous_files('bin')
763
764
765class NoSuperfluousLibrariesChecker:
766  def __init__(self, checker):
767    self._checker = checker
768
769  def __str__(self):
770    return 'No superfluous libraries checker'
771
772  def run(self):
773    self._checker.check_no_superfluous_files('javalib')
774    self._checker.check_no_superfluous_files('lib')
775    self._checker.check_no_superfluous_files('lib64')
776
777
778class NoSuperfluousArtTestsChecker:
779  def __init__(self, checker):
780    self._checker = checker
781
782  def __str__(self):
783    return 'No superfluous ART tests checker'
784
785  def run(self):
786    for arch in self._checker.possible_archs():
787      self._checker.check_no_superfluous_files('%s/%s' % (ART_TEST_DIR, arch))
788
789
790class List:
791  def __init__(self, provider, print_size=False):
792    self._provider = provider
793    self._print_size = print_size
794
795  def print_list(self):
796
797    def print_list_rec(path):
798      apex_map = self._provider.read_dir(path)
799      if apex_map is None:
800        return
801      apex_map = dict(apex_map)
802      if '.' in apex_map:
803        del apex_map['.']
804      if '..' in apex_map:
805        del apex_map['..']
806      for (_, val) in sorted(apex_map.items()):
807        val_path = os.path.join(path, val.name)
808        if self._print_size:
809          if val.size < 0:
810            print('[    n/a    ]  %s' % val_path)
811          else:
812            print('[%11d]  %s' % (val.size, val_path))
813        else:
814          print(val_path)
815        if val.is_dir:
816          print_list_rec(val_path)
817
818    print_list_rec('')
819
820
821class Tree:
822  def __init__(self, provider, title, print_size=False):
823    print('%s' % title)
824    self._provider = provider
825    self._has_next_list = []
826    self._print_size = print_size
827
828  @staticmethod
829  def get_vertical(has_next_list):
830    string = ''
831    for v in has_next_list:
832      string += '%s   ' % ('│' if v else ' ')
833    return string
834
835  @staticmethod
836  def get_last_vertical(last):
837    return '└── ' if last else '├── '
838
839  def print_tree(self):
840
841    def print_tree_rec(path):
842      apex_map = self._provider.read_dir(path)
843      if apex_map is None:
844        return
845      apex_map = dict(apex_map)
846      if '.' in apex_map:
847        del apex_map['.']
848      if '..' in apex_map:
849        del apex_map['..']
850      key_list = list(sorted(apex_map.keys()))
851      for i, key in enumerate(key_list):
852        prev = self.get_vertical(self._has_next_list)
853        last = self.get_last_vertical(i == len(key_list) - 1)
854        val = apex_map[key]
855        if self._print_size:
856          if val.size < 0:
857            print('%s%s[    n/a    ]  %s' % (prev, last, val.name))
858          else:
859            print('%s%s[%11d]  %s' % (prev, last, val.size, val.name))
860        else:
861          print('%s%s%s' % (prev, last, val.name))
862        if val.is_dir:
863          self._has_next_list.append(i < len(key_list) - 1)
864          val_path = os.path.join(path, val.name)
865          print_tree_rec(val_path)
866          self._has_next_list.pop()
867
868    print_tree_rec('')
869
870
871# Note: do not sys.exit early, for __del__ cleanup.
872def art_apex_test_main(test_args):
873  if test_args.host and test_args.flattened:
874    logging.error("Both of --host and --flattened set")
875    return 1
876  if test_args.list and test_args.tree:
877    logging.error("Both of --list and --tree set")
878    return 1
879  if test_args.size and not (test_args.list or test_args.tree):
880    logging.error("--size set but neither --list nor --tree set")
881    return 1
882  if not test_args.flattened and not test_args.tmpdir:
883    logging.error("Need a tmpdir.")
884    return 1
885  if not test_args.flattened and not test_args.host:
886    if not test_args.deapexer:
887      logging.error("Need deapexer.")
888      return 1
889    if not test_args.debugfs:
890      logging.error("Need debugfs.")
891      return 1
892
893  if test_args.host:
894    # Host APEX.
895    if test_args.flavor not in [FLAVOR_DEBUG, FLAVOR_AUTO]:
896      logging.error("Using option --host with non-Debug APEX")
897      return 1
898    # Host APEX is always a debug flavor (for now).
899    test_args.flavor = FLAVOR_DEBUG
900  else:
901    # Device APEX.
902    if test_args.flavor == FLAVOR_AUTO:
903      logging.warning('--flavor=auto, trying to autodetect. This may be incorrect!')
904      # The order of flavors in the list below matters, as the release tag (empty string) will
905      # match any package name.
906      for flavor in [ FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_RELEASE ]:
907        flavor_tag = flavor
908        # Special handling for the release flavor, whose name is no longer part of the Release ART
909        # APEX file name (`com.android.art.capex` / `com.android.art`).
910        if flavor == FLAVOR_RELEASE:
911          flavor_tag = ''
912        flavor_pattern = '*.%s*' % flavor_tag
913        if fnmatch.fnmatch(test_args.apex, flavor_pattern):
914          test_args.flavor = flavor
915          logging.warning('  Detected %s flavor', flavor)
916          break
917      if test_args.flavor == FLAVOR_AUTO:
918        logging.error('  Could not detect APEX flavor, neither %s, %s nor %s for \'%s\'',
919                    FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, test_args.apex)
920        return 1
921
922  try:
923    if test_args.host:
924      apex_provider = HostApexProvider(test_args.apex, test_args.tmpdir)
925    else:
926      apex_dir = test_args.apex
927      if not test_args.flattened:
928        # Extract the apex. It would be nice to use the output from "deapexer list"
929        # to avoid this work, but it doesn't provide info about executable bits.
930        apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs,
931                                test_args.tmpdir)
932      apex_provider = TargetApexProvider(apex_dir)
933  except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
934    logging.error('Failed to create provider: %s', e)
935    return 1
936
937  if test_args.tree:
938    Tree(apex_provider, test_args.apex, test_args.size).print_tree()
939    return 0
940  if test_args.list:
941    List(apex_provider, test_args.size).print_list()
942    return 0
943
944  checkers = []
945  if test_args.bitness == BITNESS_AUTO:
946    logging.warning('--bitness=auto, trying to autodetect. This may be incorrect!')
947    has_32 = apex_provider.get('lib') is not None
948    has_64 = apex_provider.get('lib64') is not None
949    if has_32 and has_64:
950      logging.warning('  Detected multilib')
951      test_args.bitness = BITNESS_MULTILIB
952    elif has_32:
953      logging.warning('  Detected 32-only')
954      test_args.bitness = BITNESS_32
955    elif has_64:
956      logging.warning('  Detected 64-only')
957      test_args.bitness = BITNESS_64
958    else:
959      logging.error('  Could not detect bitness, neither lib nor lib64 contained.')
960      List(apex_provider).print_list()
961      return 1
962
963  if test_args.bitness == BITNESS_32:
964    base_checker = Arch32Checker(apex_provider)
965  elif test_args.bitness == BITNESS_64:
966    base_checker = Arch64Checker(apex_provider)
967  else:
968    assert test_args.bitness == BITNESS_MULTILIB
969    base_checker = MultilibChecker(apex_provider)
970
971  checkers.append(ReleaseChecker(base_checker))
972  if test_args.host:
973    checkers.append(ReleaseHostChecker(base_checker))
974  else:
975    checkers.append(ReleaseTargetChecker(base_checker))
976  if test_args.flavor == FLAVOR_DEBUG or test_args.flavor == FLAVOR_TESTING:
977    checkers.append(DebugChecker(base_checker))
978    if not test_args.host:
979      checkers.append(DebugTargetChecker(base_checker))
980  if test_args.flavor == FLAVOR_TESTING:
981    checkers.append(TestingTargetChecker(base_checker))
982
983  # These checkers must be last.
984  checkers.append(NoSuperfluousBinariesChecker(base_checker))
985  checkers.append(NoSuperfluousArtTestsChecker(base_checker))
986  if not test_args.host:
987    # We only care about superfluous libraries on target, where their absence
988    # can be vital to ensure they get picked up from the right package.
989    checkers.append(NoSuperfluousLibrariesChecker(base_checker))
990
991  failed = False
992  for checker in checkers:
993    logging.info('%s...', checker)
994    checker.run()
995    if base_checker.error_count() > 0:
996      logging.error('%s FAILED', checker)
997      failed = True
998    else:
999      logging.info('%s SUCCEEDED', checker)
1000    base_checker.reset_errors()
1001
1002  return 1 if failed else 0
1003
1004
1005def art_apex_test_default(test_parser):
1006  if 'ANDROID_PRODUCT_OUT' not in os.environ:
1007    logging.error('No-argument use requires ANDROID_PRODUCT_OUT')
1008    sys.exit(1)
1009  product_out = os.environ['ANDROID_PRODUCT_OUT']
1010  if 'ANDROID_HOST_OUT' not in os.environ:
1011    logging.error('No-argument use requires ANDROID_HOST_OUT')
1012    sys.exit(1)
1013  host_out = os.environ['ANDROID_HOST_OUT']
1014
1015  test_args = test_parser.parse_args(['unused'])  # For consistency.
1016  test_args.debugfs = '%s/bin/debugfs' % host_out
1017  test_args.tmpdir = '.'
1018  test_args.tree = False
1019  test_args.list = False
1020  test_args.bitness = BITNESS_AUTO
1021  failed = False
1022
1023  if not os.path.exists(test_args.debugfs):
1024    logging.error("Cannot find debugfs (default path %s). Please build it, e.g., m debugfs",
1025                  test_args.debugfs)
1026    sys.exit(1)
1027
1028  # TODO: Add host support.
1029  # TODO: Add support for flattened APEX packages.
1030  configs = [
1031    {'name': 'com.android.art.capex',         'flavor': FLAVOR_RELEASE, 'host': False},
1032    {'name': 'com.android.art.debug.capex',   'flavor': FLAVOR_DEBUG,   'host': False},
1033    # Note: The Testing ART APEX is not a Compressed APEX.
1034    {'name': 'com.android.art.testing.apex',  'flavor': FLAVOR_TESTING, 'host': False},
1035  ]
1036
1037  for config in configs:
1038    logging.info(config['name'])
1039    # TODO: Host will need different path.
1040    test_args.apex = '%s/system/apex/%s' % (product_out, config['name'])
1041    if not os.path.exists(test_args.apex):
1042      failed = True
1043      logging.error("Cannot find APEX %s. Please build it first.", test_args.apex)
1044      continue
1045    test_args.flavor = config['flavor']
1046    test_args.host = config['host']
1047    failed = art_apex_test_main(test_args) != 0
1048
1049  if failed:
1050    sys.exit(1)
1051
1052
1053if __name__ == "__main__":
1054  parser = argparse.ArgumentParser(description='Check integrity of an ART APEX.')
1055
1056  parser.add_argument('apex', help='APEX file input')
1057
1058  parser.add_argument('--host', help='Check as host APEX', action='store_true')
1059
1060  parser.add_argument('--flattened', help='Check as flattened (target) APEX', action='store_true')
1061
1062  parser.add_argument('--flavor', help='Check as FLAVOR APEX', choices=FLAVORS_ALL,
1063                      default=FLAVOR_AUTO)
1064
1065  parser.add_argument('--list', help='List all files', action='store_true')
1066  parser.add_argument('--tree', help='Print directory tree', action='store_true')
1067  parser.add_argument('--size', help='Print file sizes', action='store_true')
1068
1069  parser.add_argument('--tmpdir', help='Directory for temp files')
1070  parser.add_argument('--deapexer', help='Path to deapexer')
1071  parser.add_argument('--debugfs', help='Path to debugfs')
1072
1073  parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL,
1074                      default=BITNESS_AUTO)
1075
1076  if len(sys.argv) == 1:
1077    art_apex_test_default(parser)
1078  else:
1079    args = parser.parse_args()
1080
1081    if args is None:
1082      sys.exit(1)
1083
1084    exit_code = art_apex_test_main(args)
1085    sys.exit(exit_code)
1086