1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6777b538SAndroid Build Coastguard Worker# 3*6777b538SAndroid Build Coastguard Worker# Copyright 2013 The Chromium Authors 4*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 5*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 6*6777b538SAndroid Build Coastguard Worker"""Runs Android's lint tool.""" 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard Workerimport argparse 9*6777b538SAndroid Build Coastguard Workerimport logging 10*6777b538SAndroid Build Coastguard Workerimport os 11*6777b538SAndroid Build Coastguard Workerimport shutil 12*6777b538SAndroid Build Coastguard Workerimport sys 13*6777b538SAndroid Build Coastguard Workerimport time 14*6777b538SAndroid Build Coastguard Workerfrom xml.dom import minidom 15*6777b538SAndroid Build Coastguard Workerfrom xml.etree import ElementTree 16*6777b538SAndroid Build Coastguard Worker 17*6777b538SAndroid Build Coastguard Workerfrom util import build_utils 18*6777b538SAndroid Build Coastguard Workerfrom util import manifest_utils 19*6777b538SAndroid Build Coastguard Workerfrom util import server_utils 20*6777b538SAndroid Build Coastguard Workerimport action_helpers # build_utils adds //build to sys.path. 21*6777b538SAndroid Build Coastguard Worker 22*6777b538SAndroid Build Coastguard Worker_LINT_MD_URL = 'https://chromium.googlesource.com/chromium/src/+/main/build/android/docs/lint.md' # pylint: disable=line-too-long 23*6777b538SAndroid Build Coastguard Worker 24*6777b538SAndroid Build Coastguard Worker# These checks are not useful for chromium. 25*6777b538SAndroid Build Coastguard Worker_DISABLED_ALWAYS = [ 26*6777b538SAndroid Build Coastguard Worker "AppCompatResource", # Lint does not correctly detect our appcompat lib. 27*6777b538SAndroid Build Coastguard Worker "Assert", # R8 --force-enable-assertions is used to enable java asserts. 28*6777b538SAndroid Build Coastguard Worker "InflateParams", # Null is ok when inflating views for dialogs. 29*6777b538SAndroid Build Coastguard Worker "InlinedApi", # Constants are copied so they are always available. 30*6777b538SAndroid Build Coastguard Worker "LintBaseline", # Don't warn about using baseline.xml files. 31*6777b538SAndroid Build Coastguard Worker "LintBaselineFixed", # We dont care if baseline.xml has unused entries. 32*6777b538SAndroid Build Coastguard Worker "MissingInflatedId", # False positives https://crbug.com/1394222 33*6777b538SAndroid Build Coastguard Worker "MissingApplicationIcon", # False positive for non-production targets. 34*6777b538SAndroid Build Coastguard Worker "NetworkSecurityConfig", # Breaks on library certificates b/269783280. 35*6777b538SAndroid Build Coastguard Worker "ObsoleteLintCustomCheck", # We have no control over custom lint checks. 36*6777b538SAndroid Build Coastguard Worker "StringFormatCount", # Has false-positives. 37*6777b538SAndroid Build Coastguard Worker "SwitchIntDef", # Many C++ enums are not used at all in java. 38*6777b538SAndroid Build Coastguard Worker "Typos", # Strings are committed in English first and later translated. 39*6777b538SAndroid Build Coastguard Worker "VisibleForTests", # Does not recognize "ForTesting" methods. 40*6777b538SAndroid Build Coastguard Worker "UniqueConstants", # Chromium enums allow aliases. 41*6777b538SAndroid Build Coastguard Worker "UnusedAttribute", # Chromium apks have various minSdkVersion values. 42*6777b538SAndroid Build Coastguard Worker] 43*6777b538SAndroid Build Coastguard Worker 44*6777b538SAndroid Build Coastguard Worker# These checks are not useful for test targets and adds an unnecessary burden 45*6777b538SAndroid Build Coastguard Worker# to suppress them. 46*6777b538SAndroid Build Coastguard Worker_DISABLED_FOR_TESTS = [ 47*6777b538SAndroid Build Coastguard Worker # We should not require test strings.xml files to explicitly add 48*6777b538SAndroid Build Coastguard Worker # translatable=false since they are not translated and not used in 49*6777b538SAndroid Build Coastguard Worker # production. 50*6777b538SAndroid Build Coastguard Worker "MissingTranslation", 51*6777b538SAndroid Build Coastguard Worker # Test strings.xml files often have simple names and are not translatable, 52*6777b538SAndroid Build Coastguard Worker # so it may conflict with a production string and cause this error. 53*6777b538SAndroid Build Coastguard Worker "Untranslatable", 54*6777b538SAndroid Build Coastguard Worker # Test targets often use the same strings target and resources target as the 55*6777b538SAndroid Build Coastguard Worker # production targets but may not use all of them. 56*6777b538SAndroid Build Coastguard Worker "UnusedResources", 57*6777b538SAndroid Build Coastguard Worker # TODO(wnwen): Turn this back on since to crash it would require running on 58*6777b538SAndroid Build Coastguard Worker # a device with all the various minSdkVersions. 59*6777b538SAndroid Build Coastguard Worker # Real NewApi violations crash the app, so the only ones that lint catches 60*6777b538SAndroid Build Coastguard Worker # but tests still succeed are false positives. 61*6777b538SAndroid Build Coastguard Worker "NewApi", 62*6777b538SAndroid Build Coastguard Worker # Tests should be allowed to access these methods/classes. 63*6777b538SAndroid Build Coastguard Worker "VisibleForTests", 64*6777b538SAndroid Build Coastguard Worker] 65*6777b538SAndroid Build Coastguard Worker 66*6777b538SAndroid Build Coastguard Worker_RES_ZIP_DIR = 'RESZIPS' 67*6777b538SAndroid Build Coastguard Worker_SRCJAR_DIR = 'SRCJARS' 68*6777b538SAndroid Build Coastguard Worker_AAR_DIR = 'AARS' 69*6777b538SAndroid Build Coastguard Worker 70*6777b538SAndroid Build Coastguard Worker 71*6777b538SAndroid Build Coastguard Workerdef _SrcRelative(path): 72*6777b538SAndroid Build Coastguard Worker """Returns relative path to top-level src dir.""" 73*6777b538SAndroid Build Coastguard Worker return os.path.relpath(path, build_utils.DIR_SOURCE_ROOT) 74*6777b538SAndroid Build Coastguard Worker 75*6777b538SAndroid Build Coastguard Worker 76*6777b538SAndroid Build Coastguard Workerdef _GenerateProjectFile(android_manifest, 77*6777b538SAndroid Build Coastguard Worker android_sdk_root, 78*6777b538SAndroid Build Coastguard Worker cache_dir, 79*6777b538SAndroid Build Coastguard Worker partials_dir, 80*6777b538SAndroid Build Coastguard Worker sources=None, 81*6777b538SAndroid Build Coastguard Worker classpath=None, 82*6777b538SAndroid Build Coastguard Worker srcjar_sources=None, 83*6777b538SAndroid Build Coastguard Worker resource_sources=None, 84*6777b538SAndroid Build Coastguard Worker custom_lint_jars=None, 85*6777b538SAndroid Build Coastguard Worker custom_annotation_zips=None, 86*6777b538SAndroid Build Coastguard Worker android_sdk_version=None, 87*6777b538SAndroid Build Coastguard Worker baseline_path=None): 88*6777b538SAndroid Build Coastguard Worker project = ElementTree.Element('project') 89*6777b538SAndroid Build Coastguard Worker root = ElementTree.SubElement(project, 'root') 90*6777b538SAndroid Build Coastguard Worker # Run lint from output directory: crbug.com/1115594 91*6777b538SAndroid Build Coastguard Worker root.set('dir', os.getcwd()) 92*6777b538SAndroid Build Coastguard Worker sdk = ElementTree.SubElement(project, 'sdk') 93*6777b538SAndroid Build Coastguard Worker # Lint requires that the sdk path be an absolute path. 94*6777b538SAndroid Build Coastguard Worker sdk.set('dir', os.path.abspath(android_sdk_root)) 95*6777b538SAndroid Build Coastguard Worker if baseline_path is not None: 96*6777b538SAndroid Build Coastguard Worker baseline = ElementTree.SubElement(project, 'baseline') 97*6777b538SAndroid Build Coastguard Worker baseline.set('file', baseline_path) 98*6777b538SAndroid Build Coastguard Worker cache = ElementTree.SubElement(project, 'cache') 99*6777b538SAndroid Build Coastguard Worker cache.set('dir', cache_dir) 100*6777b538SAndroid Build Coastguard Worker main_module = ElementTree.SubElement(project, 'module') 101*6777b538SAndroid Build Coastguard Worker main_module.set('name', 'main') 102*6777b538SAndroid Build Coastguard Worker main_module.set('android', 'true') 103*6777b538SAndroid Build Coastguard Worker main_module.set('library', 'false') 104*6777b538SAndroid Build Coastguard Worker # Required to make lint-resources.xml be written to a per-target path. 105*6777b538SAndroid Build Coastguard Worker # https://crbug.com/1515070 and b/324598620 106*6777b538SAndroid Build Coastguard Worker main_module.set('partial-results-dir', partials_dir) 107*6777b538SAndroid Build Coastguard Worker if android_sdk_version: 108*6777b538SAndroid Build Coastguard Worker main_module.set('compile_sdk_version', android_sdk_version) 109*6777b538SAndroid Build Coastguard Worker manifest = ElementTree.SubElement(main_module, 'manifest') 110*6777b538SAndroid Build Coastguard Worker manifest.set('file', android_manifest) 111*6777b538SAndroid Build Coastguard Worker if srcjar_sources: 112*6777b538SAndroid Build Coastguard Worker for srcjar_file in srcjar_sources: 113*6777b538SAndroid Build Coastguard Worker src = ElementTree.SubElement(main_module, 'src') 114*6777b538SAndroid Build Coastguard Worker src.set('file', srcjar_file) 115*6777b538SAndroid Build Coastguard Worker # Cannot add generated="true" since then lint does not scan them, and 116*6777b538SAndroid Build Coastguard Worker # we get "UnusedResources" lint errors when resources are used only by 117*6777b538SAndroid Build Coastguard Worker # generated files. 118*6777b538SAndroid Build Coastguard Worker if sources: 119*6777b538SAndroid Build Coastguard Worker for source in sources: 120*6777b538SAndroid Build Coastguard Worker src = ElementTree.SubElement(main_module, 'src') 121*6777b538SAndroid Build Coastguard Worker src.set('file', source) 122*6777b538SAndroid Build Coastguard Worker # Cannot set test="true" since we sometimes put Test.java files beside 123*6777b538SAndroid Build Coastguard Worker # non-test files, which lint does not allow: 124*6777b538SAndroid Build Coastguard Worker # "Test sources cannot be in the same source root as production files" 125*6777b538SAndroid Build Coastguard Worker if classpath: 126*6777b538SAndroid Build Coastguard Worker for file_path in classpath: 127*6777b538SAndroid Build Coastguard Worker classpath_element = ElementTree.SubElement(main_module, 'classpath') 128*6777b538SAndroid Build Coastguard Worker classpath_element.set('file', file_path) 129*6777b538SAndroid Build Coastguard Worker if resource_sources: 130*6777b538SAndroid Build Coastguard Worker for resource_file in resource_sources: 131*6777b538SAndroid Build Coastguard Worker resource = ElementTree.SubElement(main_module, 'resource') 132*6777b538SAndroid Build Coastguard Worker resource.set('file', resource_file) 133*6777b538SAndroid Build Coastguard Worker if custom_lint_jars: 134*6777b538SAndroid Build Coastguard Worker for lint_jar in custom_lint_jars: 135*6777b538SAndroid Build Coastguard Worker lint = ElementTree.SubElement(main_module, 'lint-checks') 136*6777b538SAndroid Build Coastguard Worker lint.set('file', lint_jar) 137*6777b538SAndroid Build Coastguard Worker if custom_annotation_zips: 138*6777b538SAndroid Build Coastguard Worker for annotation_zip in custom_annotation_zips: 139*6777b538SAndroid Build Coastguard Worker annotation = ElementTree.SubElement(main_module, 'annotations') 140*6777b538SAndroid Build Coastguard Worker annotation.set('file', annotation_zip) 141*6777b538SAndroid Build Coastguard Worker return project 142*6777b538SAndroid Build Coastguard Worker 143*6777b538SAndroid Build Coastguard Worker 144*6777b538SAndroid Build Coastguard Workerdef _RetrieveBackportedMethods(backported_methods_path): 145*6777b538SAndroid Build Coastguard Worker with open(backported_methods_path) as f: 146*6777b538SAndroid Build Coastguard Worker methods = f.read().splitlines() 147*6777b538SAndroid Build Coastguard Worker # Methods look like: 148*6777b538SAndroid Build Coastguard Worker # java/util/Set#of(Ljava/lang/Object;)Ljava/util/Set; 149*6777b538SAndroid Build Coastguard Worker # But error message looks like: 150*6777b538SAndroid Build Coastguard Worker # Call requires API level R (current min is 21): java.util.Set#of [NewApi] 151*6777b538SAndroid Build Coastguard Worker methods = (m.replace('/', '\\.') for m in methods) 152*6777b538SAndroid Build Coastguard Worker methods = (m[:m.index('(')] for m in methods) 153*6777b538SAndroid Build Coastguard Worker return sorted(set(methods)) 154*6777b538SAndroid Build Coastguard Worker 155*6777b538SAndroid Build Coastguard Worker 156*6777b538SAndroid Build Coastguard Workerdef _GenerateConfigXmlTree(orig_config_path, backported_methods): 157*6777b538SAndroid Build Coastguard Worker if orig_config_path: 158*6777b538SAndroid Build Coastguard Worker root_node = ElementTree.parse(orig_config_path).getroot() 159*6777b538SAndroid Build Coastguard Worker else: 160*6777b538SAndroid Build Coastguard Worker root_node = ElementTree.fromstring('<lint/>') 161*6777b538SAndroid Build Coastguard Worker 162*6777b538SAndroid Build Coastguard Worker issue_node = ElementTree.SubElement(root_node, 'issue') 163*6777b538SAndroid Build Coastguard Worker issue_node.attrib['id'] = 'NewApi' 164*6777b538SAndroid Build Coastguard Worker ignore_node = ElementTree.SubElement(issue_node, 'ignore') 165*6777b538SAndroid Build Coastguard Worker ignore_node.attrib['regexp'] = '|'.join(backported_methods) 166*6777b538SAndroid Build Coastguard Worker return root_node 167*6777b538SAndroid Build Coastguard Worker 168*6777b538SAndroid Build Coastguard Worker 169*6777b538SAndroid Build Coastguard Workerdef _GenerateAndroidManifest(original_manifest_path, extra_manifest_paths, 170*6777b538SAndroid Build Coastguard Worker min_sdk_version, android_sdk_version): 171*6777b538SAndroid Build Coastguard Worker # Set minSdkVersion in the manifest to the correct value. 172*6777b538SAndroid Build Coastguard Worker doc, manifest, app_node = manifest_utils.ParseManifest(original_manifest_path) 173*6777b538SAndroid Build Coastguard Worker 174*6777b538SAndroid Build Coastguard Worker # TODO(crbug.com/1126301): Should this be done using manifest merging? 175*6777b538SAndroid Build Coastguard Worker # Add anything in the application node of the extra manifests to the main 176*6777b538SAndroid Build Coastguard Worker # manifest to prevent unused resource errors. 177*6777b538SAndroid Build Coastguard Worker for path in extra_manifest_paths: 178*6777b538SAndroid Build Coastguard Worker _, _, extra_app_node = manifest_utils.ParseManifest(path) 179*6777b538SAndroid Build Coastguard Worker for node in extra_app_node: 180*6777b538SAndroid Build Coastguard Worker app_node.append(node) 181*6777b538SAndroid Build Coastguard Worker 182*6777b538SAndroid Build Coastguard Worker uses_sdk = manifest.find('./uses-sdk') 183*6777b538SAndroid Build Coastguard Worker if uses_sdk is None: 184*6777b538SAndroid Build Coastguard Worker uses_sdk = ElementTree.Element('uses-sdk') 185*6777b538SAndroid Build Coastguard Worker manifest.insert(0, uses_sdk) 186*6777b538SAndroid Build Coastguard Worker uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE, 187*6777b538SAndroid Build Coastguard Worker min_sdk_version) 188*6777b538SAndroid Build Coastguard Worker uses_sdk.set('{%s}targetSdkVersion' % manifest_utils.ANDROID_NAMESPACE, 189*6777b538SAndroid Build Coastguard Worker android_sdk_version) 190*6777b538SAndroid Build Coastguard Worker return doc 191*6777b538SAndroid Build Coastguard Worker 192*6777b538SAndroid Build Coastguard Worker 193*6777b538SAndroid Build Coastguard Workerdef _WriteXmlFile(root, path): 194*6777b538SAndroid Build Coastguard Worker logging.info('Writing xml file %s', path) 195*6777b538SAndroid Build Coastguard Worker build_utils.MakeDirectory(os.path.dirname(path)) 196*6777b538SAndroid Build Coastguard Worker with action_helpers.atomic_output(path) as f: 197*6777b538SAndroid Build Coastguard Worker # Although we can write it just with ElementTree.tostring, using minidom 198*6777b538SAndroid Build Coastguard Worker # makes it a lot easier to read as a human (also on code search). 199*6777b538SAndroid Build Coastguard Worker f.write( 200*6777b538SAndroid Build Coastguard Worker minidom.parseString(ElementTree.tostring( 201*6777b538SAndroid Build Coastguard Worker root, encoding='utf-8')).toprettyxml(indent=' ').encode('utf-8')) 202*6777b538SAndroid Build Coastguard Worker 203*6777b538SAndroid Build Coastguard Worker 204*6777b538SAndroid Build Coastguard Workerdef _RunLint(custom_lint_jar_path, 205*6777b538SAndroid Build Coastguard Worker lint_jar_path, 206*6777b538SAndroid Build Coastguard Worker backported_methods_path, 207*6777b538SAndroid Build Coastguard Worker config_path, 208*6777b538SAndroid Build Coastguard Worker manifest_path, 209*6777b538SAndroid Build Coastguard Worker extra_manifest_paths, 210*6777b538SAndroid Build Coastguard Worker sources, 211*6777b538SAndroid Build Coastguard Worker classpath, 212*6777b538SAndroid Build Coastguard Worker cache_dir, 213*6777b538SAndroid Build Coastguard Worker android_sdk_version, 214*6777b538SAndroid Build Coastguard Worker aars, 215*6777b538SAndroid Build Coastguard Worker srcjars, 216*6777b538SAndroid Build Coastguard Worker min_sdk_version, 217*6777b538SAndroid Build Coastguard Worker resource_sources, 218*6777b538SAndroid Build Coastguard Worker resource_zips, 219*6777b538SAndroid Build Coastguard Worker android_sdk_root, 220*6777b538SAndroid Build Coastguard Worker lint_gen_dir, 221*6777b538SAndroid Build Coastguard Worker baseline, 222*6777b538SAndroid Build Coastguard Worker testonly_target=False, 223*6777b538SAndroid Build Coastguard Worker warnings_as_errors=False): 224*6777b538SAndroid Build Coastguard Worker logging.info('Lint starting') 225*6777b538SAndroid Build Coastguard Worker if not cache_dir: 226*6777b538SAndroid Build Coastguard Worker # Use per-target cache directory when --cache-dir is not used. 227*6777b538SAndroid Build Coastguard Worker cache_dir = os.path.join(lint_gen_dir, 'cache') 228*6777b538SAndroid Build Coastguard Worker # Lint complains if the directory does not exist. 229*6777b538SAndroid Build Coastguard Worker # When --create-cache is used, ninja will create this directory because the 230*6777b538SAndroid Build Coastguard Worker # stamp file is created within it. 231*6777b538SAndroid Build Coastguard Worker os.makedirs(cache_dir, exist_ok=True) 232*6777b538SAndroid Build Coastguard Worker 233*6777b538SAndroid Build Coastguard Worker if baseline and not os.path.exists(baseline): 234*6777b538SAndroid Build Coastguard Worker # Generating new baselines is only done locally, and requires more memory to 235*6777b538SAndroid Build Coastguard Worker # avoid OOMs. 236*6777b538SAndroid Build Coastguard Worker creating_baseline = True 237*6777b538SAndroid Build Coastguard Worker lint_xmx = '4G' 238*6777b538SAndroid Build Coastguard Worker else: 239*6777b538SAndroid Build Coastguard Worker creating_baseline = False 240*6777b538SAndroid Build Coastguard Worker lint_xmx = '2G' 241*6777b538SAndroid Build Coastguard Worker 242*6777b538SAndroid Build Coastguard Worker # Lint requires this directory to exist and be cleared. 243*6777b538SAndroid Build Coastguard Worker # See b/324598620 244*6777b538SAndroid Build Coastguard Worker partials_dir = os.path.join(lint_gen_dir, 'partials') 245*6777b538SAndroid Build Coastguard Worker shutil.rmtree(partials_dir, ignore_errors=True) 246*6777b538SAndroid Build Coastguard Worker os.makedirs(partials_dir) 247*6777b538SAndroid Build Coastguard Worker 248*6777b538SAndroid Build Coastguard Worker # All paths in lint are based off of relative paths from root with root as the 249*6777b538SAndroid Build Coastguard Worker # prefix. Path variable substitution is based off of prefix matching so custom 250*6777b538SAndroid Build Coastguard Worker # path variables need to match exactly in order to show up in baseline files. 251*6777b538SAndroid Build Coastguard Worker # e.g. lint_path=path/to/output/dir/../../file/in/src 252*6777b538SAndroid Build Coastguard Worker root_path = os.getcwd() # This is usually the output directory. 253*6777b538SAndroid Build Coastguard Worker pathvar_src = os.path.join( 254*6777b538SAndroid Build Coastguard Worker root_path, os.path.relpath(build_utils.DIR_SOURCE_ROOT, start=root_path)) 255*6777b538SAndroid Build Coastguard Worker 256*6777b538SAndroid Build Coastguard Worker cmd = build_utils.JavaCmd(xmx=lint_xmx) + [ 257*6777b538SAndroid Build Coastguard Worker '-cp', 258*6777b538SAndroid Build Coastguard Worker '{}:{}'.format(lint_jar_path, custom_lint_jar_path), 259*6777b538SAndroid Build Coastguard Worker 'org.chromium.build.CustomLint', 260*6777b538SAndroid Build Coastguard Worker '--sdk-home', 261*6777b538SAndroid Build Coastguard Worker android_sdk_root, 262*6777b538SAndroid Build Coastguard Worker '--jdk-home', 263*6777b538SAndroid Build Coastguard Worker build_utils.JAVA_HOME, 264*6777b538SAndroid Build Coastguard Worker '--path-variables', 265*6777b538SAndroid Build Coastguard Worker f'SRC={pathvar_src}', 266*6777b538SAndroid Build Coastguard Worker '--offline', 267*6777b538SAndroid Build Coastguard Worker '--quiet', # Silences lint's "." progress updates. 268*6777b538SAndroid Build Coastguard Worker '--stacktrace', # Prints full stacktraces for internal lint errors. 269*6777b538SAndroid Build Coastguard Worker '--disable', 270*6777b538SAndroid Build Coastguard Worker ','.join(_DISABLED_ALWAYS), 271*6777b538SAndroid Build Coastguard Worker ] 272*6777b538SAndroid Build Coastguard Worker 273*6777b538SAndroid Build Coastguard Worker if testonly_target: 274*6777b538SAndroid Build Coastguard Worker cmd.extend(['--disable', ','.join(_DISABLED_FOR_TESTS)]) 275*6777b538SAndroid Build Coastguard Worker 276*6777b538SAndroid Build Coastguard Worker if not manifest_path: 277*6777b538SAndroid Build Coastguard Worker manifest_path = os.path.join(build_utils.DIR_SOURCE_ROOT, 'build', 278*6777b538SAndroid Build Coastguard Worker 'android', 'AndroidManifest.xml') 279*6777b538SAndroid Build Coastguard Worker 280*6777b538SAndroid Build Coastguard Worker logging.info('Generating config.xml') 281*6777b538SAndroid Build Coastguard Worker backported_methods = _RetrieveBackportedMethods(backported_methods_path) 282*6777b538SAndroid Build Coastguard Worker config_xml_node = _GenerateConfigXmlTree(config_path, backported_methods) 283*6777b538SAndroid Build Coastguard Worker generated_config_path = os.path.join(lint_gen_dir, 'config.xml') 284*6777b538SAndroid Build Coastguard Worker _WriteXmlFile(config_xml_node, generated_config_path) 285*6777b538SAndroid Build Coastguard Worker cmd.extend(['--config', generated_config_path]) 286*6777b538SAndroid Build Coastguard Worker 287*6777b538SAndroid Build Coastguard Worker logging.info('Generating Android manifest file') 288*6777b538SAndroid Build Coastguard Worker android_manifest_tree = _GenerateAndroidManifest(manifest_path, 289*6777b538SAndroid Build Coastguard Worker extra_manifest_paths, 290*6777b538SAndroid Build Coastguard Worker min_sdk_version, 291*6777b538SAndroid Build Coastguard Worker android_sdk_version) 292*6777b538SAndroid Build Coastguard Worker # Just use a hardcoded name, since we may have different target names (and 293*6777b538SAndroid Build Coastguard Worker # thus different manifest_paths) using the same lint baseline. Eg. 294*6777b538SAndroid Build Coastguard Worker # trichrome_chrome_bundle and trichrome_chrome_32_64_bundle. 295*6777b538SAndroid Build Coastguard Worker lint_android_manifest_path = os.path.join(lint_gen_dir, 'AndroidManifest.xml') 296*6777b538SAndroid Build Coastguard Worker _WriteXmlFile(android_manifest_tree.getroot(), lint_android_manifest_path) 297*6777b538SAndroid Build Coastguard Worker 298*6777b538SAndroid Build Coastguard Worker resource_root_dir = os.path.join(lint_gen_dir, _RES_ZIP_DIR) 299*6777b538SAndroid Build Coastguard Worker # These are zip files with generated resources (e. g. strings from GRD). 300*6777b538SAndroid Build Coastguard Worker logging.info('Extracting resource zips') 301*6777b538SAndroid Build Coastguard Worker for resource_zip in resource_zips: 302*6777b538SAndroid Build Coastguard Worker # Use a consistent root and name rather than a temporary file so that 303*6777b538SAndroid Build Coastguard Worker # suppressions can be local to the lint target and the resource target. 304*6777b538SAndroid Build Coastguard Worker resource_dir = os.path.join(resource_root_dir, resource_zip) 305*6777b538SAndroid Build Coastguard Worker shutil.rmtree(resource_dir, True) 306*6777b538SAndroid Build Coastguard Worker os.makedirs(resource_dir) 307*6777b538SAndroid Build Coastguard Worker resource_sources.extend( 308*6777b538SAndroid Build Coastguard Worker build_utils.ExtractAll(resource_zip, path=resource_dir)) 309*6777b538SAndroid Build Coastguard Worker 310*6777b538SAndroid Build Coastguard Worker logging.info('Extracting aars') 311*6777b538SAndroid Build Coastguard Worker aar_root_dir = os.path.join(lint_gen_dir, _AAR_DIR) 312*6777b538SAndroid Build Coastguard Worker custom_lint_jars = [] 313*6777b538SAndroid Build Coastguard Worker custom_annotation_zips = [] 314*6777b538SAndroid Build Coastguard Worker if aars: 315*6777b538SAndroid Build Coastguard Worker for aar in aars: 316*6777b538SAndroid Build Coastguard Worker # Use relative source for aar files since they are not generated. 317*6777b538SAndroid Build Coastguard Worker aar_dir = os.path.join(aar_root_dir, 318*6777b538SAndroid Build Coastguard Worker os.path.splitext(_SrcRelative(aar))[0]) 319*6777b538SAndroid Build Coastguard Worker shutil.rmtree(aar_dir, True) 320*6777b538SAndroid Build Coastguard Worker os.makedirs(aar_dir) 321*6777b538SAndroid Build Coastguard Worker aar_files = build_utils.ExtractAll(aar, path=aar_dir) 322*6777b538SAndroid Build Coastguard Worker for f in aar_files: 323*6777b538SAndroid Build Coastguard Worker if f.endswith('lint.jar'): 324*6777b538SAndroid Build Coastguard Worker custom_lint_jars.append(f) 325*6777b538SAndroid Build Coastguard Worker elif f.endswith('annotations.zip'): 326*6777b538SAndroid Build Coastguard Worker custom_annotation_zips.append(f) 327*6777b538SAndroid Build Coastguard Worker 328*6777b538SAndroid Build Coastguard Worker logging.info('Extracting srcjars') 329*6777b538SAndroid Build Coastguard Worker srcjar_root_dir = os.path.join(lint_gen_dir, _SRCJAR_DIR) 330*6777b538SAndroid Build Coastguard Worker srcjar_sources = [] 331*6777b538SAndroid Build Coastguard Worker if srcjars: 332*6777b538SAndroid Build Coastguard Worker for srcjar in srcjars: 333*6777b538SAndroid Build Coastguard Worker # Use path without extensions since otherwise the file name includes 334*6777b538SAndroid Build Coastguard Worker # .srcjar and lint treats it as a srcjar. 335*6777b538SAndroid Build Coastguard Worker srcjar_dir = os.path.join(srcjar_root_dir, os.path.splitext(srcjar)[0]) 336*6777b538SAndroid Build Coastguard Worker shutil.rmtree(srcjar_dir, True) 337*6777b538SAndroid Build Coastguard Worker os.makedirs(srcjar_dir) 338*6777b538SAndroid Build Coastguard Worker # Sadly lint's srcjar support is broken since it only considers the first 339*6777b538SAndroid Build Coastguard Worker # srcjar. Until we roll a lint version with that fixed, we need to extract 340*6777b538SAndroid Build Coastguard Worker # it ourselves. 341*6777b538SAndroid Build Coastguard Worker srcjar_sources.extend(build_utils.ExtractAll(srcjar, path=srcjar_dir)) 342*6777b538SAndroid Build Coastguard Worker 343*6777b538SAndroid Build Coastguard Worker logging.info('Generating project file') 344*6777b538SAndroid Build Coastguard Worker project_file_root = _GenerateProjectFile( 345*6777b538SAndroid Build Coastguard Worker lint_android_manifest_path, android_sdk_root, cache_dir, partials_dir, 346*6777b538SAndroid Build Coastguard Worker sources, classpath, srcjar_sources, resource_sources, custom_lint_jars, 347*6777b538SAndroid Build Coastguard Worker custom_annotation_zips, android_sdk_version, baseline) 348*6777b538SAndroid Build Coastguard Worker 349*6777b538SAndroid Build Coastguard Worker project_xml_path = os.path.join(lint_gen_dir, 'project.xml') 350*6777b538SAndroid Build Coastguard Worker _WriteXmlFile(project_file_root, project_xml_path) 351*6777b538SAndroid Build Coastguard Worker cmd += ['--project', project_xml_path] 352*6777b538SAndroid Build Coastguard Worker 353*6777b538SAndroid Build Coastguard Worker # This filter is necessary for JDK11. 354*6777b538SAndroid Build Coastguard Worker stderr_filter = build_utils.FilterReflectiveAccessJavaWarnings 355*6777b538SAndroid Build Coastguard Worker stdout_filter = lambda x: build_utils.FilterLines(x, 'No issues found') 356*6777b538SAndroid Build Coastguard Worker 357*6777b538SAndroid Build Coastguard Worker start = time.time() 358*6777b538SAndroid Build Coastguard Worker logging.debug('Lint command %s', ' '.join(cmd)) 359*6777b538SAndroid Build Coastguard Worker failed = False 360*6777b538SAndroid Build Coastguard Worker 361*6777b538SAndroid Build Coastguard Worker if creating_baseline and not warnings_as_errors: 362*6777b538SAndroid Build Coastguard Worker # Allow error code 6 when creating a baseline: ERRNO_CREATED_BASELINE 363*6777b538SAndroid Build Coastguard Worker fail_func = lambda returncode, _: returncode not in (0, 6) 364*6777b538SAndroid Build Coastguard Worker else: 365*6777b538SAndroid Build Coastguard Worker fail_func = lambda returncode, _: returncode != 0 366*6777b538SAndroid Build Coastguard Worker 367*6777b538SAndroid Build Coastguard Worker try: 368*6777b538SAndroid Build Coastguard Worker build_utils.CheckOutput(cmd, 369*6777b538SAndroid Build Coastguard Worker print_stdout=True, 370*6777b538SAndroid Build Coastguard Worker stdout_filter=stdout_filter, 371*6777b538SAndroid Build Coastguard Worker stderr_filter=stderr_filter, 372*6777b538SAndroid Build Coastguard Worker fail_on_output=warnings_as_errors, 373*6777b538SAndroid Build Coastguard Worker fail_func=fail_func) 374*6777b538SAndroid Build Coastguard Worker except build_utils.CalledProcessError as e: 375*6777b538SAndroid Build Coastguard Worker failed = True 376*6777b538SAndroid Build Coastguard Worker # Do not output the python stacktrace because it is lengthy and is not 377*6777b538SAndroid Build Coastguard Worker # relevant to the actual lint error. 378*6777b538SAndroid Build Coastguard Worker sys.stderr.write(e.output) 379*6777b538SAndroid Build Coastguard Worker finally: 380*6777b538SAndroid Build Coastguard Worker # When not treating warnings as errors, display the extra footer. 381*6777b538SAndroid Build Coastguard Worker is_debug = os.environ.get('LINT_DEBUG', '0') != '0' 382*6777b538SAndroid Build Coastguard Worker 383*6777b538SAndroid Build Coastguard Worker end = time.time() - start 384*6777b538SAndroid Build Coastguard Worker logging.info('Lint command took %ss', end) 385*6777b538SAndroid Build Coastguard Worker if not is_debug: 386*6777b538SAndroid Build Coastguard Worker shutil.rmtree(aar_root_dir, ignore_errors=True) 387*6777b538SAndroid Build Coastguard Worker shutil.rmtree(resource_root_dir, ignore_errors=True) 388*6777b538SAndroid Build Coastguard Worker shutil.rmtree(srcjar_root_dir, ignore_errors=True) 389*6777b538SAndroid Build Coastguard Worker os.unlink(project_xml_path) 390*6777b538SAndroid Build Coastguard Worker shutil.rmtree(partials_dir, ignore_errors=True) 391*6777b538SAndroid Build Coastguard Worker 392*6777b538SAndroid Build Coastguard Worker if failed: 393*6777b538SAndroid Build Coastguard Worker print('- For more help with lint in Chrome:', _LINT_MD_URL) 394*6777b538SAndroid Build Coastguard Worker if is_debug: 395*6777b538SAndroid Build Coastguard Worker print('- DEBUG MODE: Here is the project.xml: {}'.format( 396*6777b538SAndroid Build Coastguard Worker _SrcRelative(project_xml_path))) 397*6777b538SAndroid Build Coastguard Worker else: 398*6777b538SAndroid Build Coastguard Worker print('- Run with LINT_DEBUG=1 to enable lint configuration debugging') 399*6777b538SAndroid Build Coastguard Worker sys.exit(1) 400*6777b538SAndroid Build Coastguard Worker 401*6777b538SAndroid Build Coastguard Worker logging.info('Lint completed') 402*6777b538SAndroid Build Coastguard Worker 403*6777b538SAndroid Build Coastguard Worker 404*6777b538SAndroid Build Coastguard Workerdef _ParseArgs(argv): 405*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 406*6777b538SAndroid Build Coastguard Worker action_helpers.add_depfile_arg(parser) 407*6777b538SAndroid Build Coastguard Worker parser.add_argument('--target-name', help='Fully qualified GN target name.') 408*6777b538SAndroid Build Coastguard Worker parser.add_argument('--skip-build-server', 409*6777b538SAndroid Build Coastguard Worker action='store_true', 410*6777b538SAndroid Build Coastguard Worker help='Avoid using the build server.') 411*6777b538SAndroid Build Coastguard Worker parser.add_argument('--use-build-server', 412*6777b538SAndroid Build Coastguard Worker action='store_true', 413*6777b538SAndroid Build Coastguard Worker help='Always use the build server.') 414*6777b538SAndroid Build Coastguard Worker parser.add_argument('--lint-jar-path', 415*6777b538SAndroid Build Coastguard Worker required=True, 416*6777b538SAndroid Build Coastguard Worker help='Path to the lint jar.') 417*6777b538SAndroid Build Coastguard Worker parser.add_argument('--custom-lint-jar-path', 418*6777b538SAndroid Build Coastguard Worker required=True, 419*6777b538SAndroid Build Coastguard Worker help='Path to our custom lint jar.') 420*6777b538SAndroid Build Coastguard Worker parser.add_argument('--backported-methods', 421*6777b538SAndroid Build Coastguard Worker help='Path to backported methods file created by R8.') 422*6777b538SAndroid Build Coastguard Worker parser.add_argument('--cache-dir', 423*6777b538SAndroid Build Coastguard Worker help='Path to the directory in which the android cache ' 424*6777b538SAndroid Build Coastguard Worker 'directory tree should be stored.') 425*6777b538SAndroid Build Coastguard Worker parser.add_argument('--config-path', help='Path to lint suppressions file.') 426*6777b538SAndroid Build Coastguard Worker parser.add_argument('--lint-gen-dir', 427*6777b538SAndroid Build Coastguard Worker required=True, 428*6777b538SAndroid Build Coastguard Worker help='Path to store generated xml files.') 429*6777b538SAndroid Build Coastguard Worker parser.add_argument('--stamp', help='Path to stamp upon success.') 430*6777b538SAndroid Build Coastguard Worker parser.add_argument('--android-sdk-version', 431*6777b538SAndroid Build Coastguard Worker help='Version (API level) of the Android SDK used for ' 432*6777b538SAndroid Build Coastguard Worker 'building.') 433*6777b538SAndroid Build Coastguard Worker parser.add_argument('--min-sdk-version', 434*6777b538SAndroid Build Coastguard Worker required=True, 435*6777b538SAndroid Build Coastguard Worker help='Minimal SDK version to lint against.') 436*6777b538SAndroid Build Coastguard Worker parser.add_argument('--android-sdk-root', 437*6777b538SAndroid Build Coastguard Worker required=True, 438*6777b538SAndroid Build Coastguard Worker help='Lint needs an explicit path to the android sdk.') 439*6777b538SAndroid Build Coastguard Worker parser.add_argument('--testonly', 440*6777b538SAndroid Build Coastguard Worker action='store_true', 441*6777b538SAndroid Build Coastguard Worker help='If set, some checks like UnusedResources will be ' 442*6777b538SAndroid Build Coastguard Worker 'disabled since they are not helpful for test ' 443*6777b538SAndroid Build Coastguard Worker 'targets.') 444*6777b538SAndroid Build Coastguard Worker parser.add_argument('--create-cache', 445*6777b538SAndroid Build Coastguard Worker action='store_true', 446*6777b538SAndroid Build Coastguard Worker help='Whether this invocation is just warming the cache.') 447*6777b538SAndroid Build Coastguard Worker parser.add_argument('--warnings-as-errors', 448*6777b538SAndroid Build Coastguard Worker action='store_true', 449*6777b538SAndroid Build Coastguard Worker help='Treat all warnings as errors.') 450*6777b538SAndroid Build Coastguard Worker parser.add_argument('--sources', 451*6777b538SAndroid Build Coastguard Worker help='A list of files containing java and kotlin source ' 452*6777b538SAndroid Build Coastguard Worker 'files.') 453*6777b538SAndroid Build Coastguard Worker parser.add_argument('--aars', help='GN list of included aars.') 454*6777b538SAndroid Build Coastguard Worker parser.add_argument('--srcjars', help='GN list of included srcjars.') 455*6777b538SAndroid Build Coastguard Worker parser.add_argument('--manifest-path', 456*6777b538SAndroid Build Coastguard Worker help='Path to original AndroidManifest.xml') 457*6777b538SAndroid Build Coastguard Worker parser.add_argument('--extra-manifest-paths', 458*6777b538SAndroid Build Coastguard Worker action='append', 459*6777b538SAndroid Build Coastguard Worker help='GYP-list of manifest paths to merge into the ' 460*6777b538SAndroid Build Coastguard Worker 'original AndroidManifest.xml') 461*6777b538SAndroid Build Coastguard Worker parser.add_argument('--resource-sources', 462*6777b538SAndroid Build Coastguard Worker default=[], 463*6777b538SAndroid Build Coastguard Worker action='append', 464*6777b538SAndroid Build Coastguard Worker help='GYP-list of resource sources files, similar to ' 465*6777b538SAndroid Build Coastguard Worker 'java sources files, but for resource files.') 466*6777b538SAndroid Build Coastguard Worker parser.add_argument('--resource-zips', 467*6777b538SAndroid Build Coastguard Worker default=[], 468*6777b538SAndroid Build Coastguard Worker action='append', 469*6777b538SAndroid Build Coastguard Worker help='GYP-list of resource zips, zip files of generated ' 470*6777b538SAndroid Build Coastguard Worker 'resource files.') 471*6777b538SAndroid Build Coastguard Worker parser.add_argument('--classpath', 472*6777b538SAndroid Build Coastguard Worker help='List of jars to add to the classpath.') 473*6777b538SAndroid Build Coastguard Worker parser.add_argument('--baseline', 474*6777b538SAndroid Build Coastguard Worker help='Baseline file to ignore existing errors and fail ' 475*6777b538SAndroid Build Coastguard Worker 'on new errors.') 476*6777b538SAndroid Build Coastguard Worker 477*6777b538SAndroid Build Coastguard Worker args = parser.parse_args(build_utils.ExpandFileArgs(argv)) 478*6777b538SAndroid Build Coastguard Worker args.sources = action_helpers.parse_gn_list(args.sources) 479*6777b538SAndroid Build Coastguard Worker args.aars = action_helpers.parse_gn_list(args.aars) 480*6777b538SAndroid Build Coastguard Worker args.srcjars = action_helpers.parse_gn_list(args.srcjars) 481*6777b538SAndroid Build Coastguard Worker args.resource_sources = action_helpers.parse_gn_list(args.resource_sources) 482*6777b538SAndroid Build Coastguard Worker args.extra_manifest_paths = action_helpers.parse_gn_list( 483*6777b538SAndroid Build Coastguard Worker args.extra_manifest_paths) 484*6777b538SAndroid Build Coastguard Worker args.resource_zips = action_helpers.parse_gn_list(args.resource_zips) 485*6777b538SAndroid Build Coastguard Worker args.classpath = action_helpers.parse_gn_list(args.classpath) 486*6777b538SAndroid Build Coastguard Worker 487*6777b538SAndroid Build Coastguard Worker if args.baseline: 488*6777b538SAndroid Build Coastguard Worker assert os.path.basename(args.baseline) == 'lint-baseline.xml', ( 489*6777b538SAndroid Build Coastguard Worker 'The baseline file needs to be named "lint-baseline.xml" in order for ' 490*6777b538SAndroid Build Coastguard Worker 'the autoroller to find and update it whenever lint is rolled to a new ' 491*6777b538SAndroid Build Coastguard Worker 'version.') 492*6777b538SAndroid Build Coastguard Worker 493*6777b538SAndroid Build Coastguard Worker return args 494*6777b538SAndroid Build Coastguard Worker 495*6777b538SAndroid Build Coastguard Worker 496*6777b538SAndroid Build Coastguard Workerdef main(): 497*6777b538SAndroid Build Coastguard Worker build_utils.InitLogging('LINT_DEBUG') 498*6777b538SAndroid Build Coastguard Worker args = _ParseArgs(sys.argv[1:]) 499*6777b538SAndroid Build Coastguard Worker 500*6777b538SAndroid Build Coastguard Worker # TODO(wnwen): Consider removing lint cache now that there are only two lint 501*6777b538SAndroid Build Coastguard Worker # invocations. 502*6777b538SAndroid Build Coastguard Worker # Avoid parallelizing cache creation since lint runs without the cache defeat 503*6777b538SAndroid Build Coastguard Worker # the purpose of creating the cache in the first place. 504*6777b538SAndroid Build Coastguard Worker if (not args.create_cache and not args.skip_build_server 505*6777b538SAndroid Build Coastguard Worker and server_utils.MaybeRunCommand(name=args.target_name, 506*6777b538SAndroid Build Coastguard Worker argv=sys.argv, 507*6777b538SAndroid Build Coastguard Worker stamp_file=args.stamp, 508*6777b538SAndroid Build Coastguard Worker force=args.use_build_server)): 509*6777b538SAndroid Build Coastguard Worker return 510*6777b538SAndroid Build Coastguard Worker 511*6777b538SAndroid Build Coastguard Worker sources = [] 512*6777b538SAndroid Build Coastguard Worker for sources_file in args.sources: 513*6777b538SAndroid Build Coastguard Worker sources.extend(build_utils.ReadSourcesList(sources_file)) 514*6777b538SAndroid Build Coastguard Worker resource_sources = [] 515*6777b538SAndroid Build Coastguard Worker for resource_sources_file in args.resource_sources: 516*6777b538SAndroid Build Coastguard Worker resource_sources.extend(build_utils.ReadSourcesList(resource_sources_file)) 517*6777b538SAndroid Build Coastguard Worker 518*6777b538SAndroid Build Coastguard Worker possible_depfile_deps = (args.srcjars + args.resource_zips + sources + 519*6777b538SAndroid Build Coastguard Worker resource_sources + [ 520*6777b538SAndroid Build Coastguard Worker args.baseline, 521*6777b538SAndroid Build Coastguard Worker args.manifest_path, 522*6777b538SAndroid Build Coastguard Worker ]) 523*6777b538SAndroid Build Coastguard Worker depfile_deps = [p for p in possible_depfile_deps if p] 524*6777b538SAndroid Build Coastguard Worker 525*6777b538SAndroid Build Coastguard Worker _RunLint(args.custom_lint_jar_path, 526*6777b538SAndroid Build Coastguard Worker args.lint_jar_path, 527*6777b538SAndroid Build Coastguard Worker args.backported_methods, 528*6777b538SAndroid Build Coastguard Worker args.config_path, 529*6777b538SAndroid Build Coastguard Worker args.manifest_path, 530*6777b538SAndroid Build Coastguard Worker args.extra_manifest_paths, 531*6777b538SAndroid Build Coastguard Worker sources, 532*6777b538SAndroid Build Coastguard Worker args.classpath, 533*6777b538SAndroid Build Coastguard Worker args.cache_dir, 534*6777b538SAndroid Build Coastguard Worker args.android_sdk_version, 535*6777b538SAndroid Build Coastguard Worker args.aars, 536*6777b538SAndroid Build Coastguard Worker args.srcjars, 537*6777b538SAndroid Build Coastguard Worker args.min_sdk_version, 538*6777b538SAndroid Build Coastguard Worker resource_sources, 539*6777b538SAndroid Build Coastguard Worker args.resource_zips, 540*6777b538SAndroid Build Coastguard Worker args.android_sdk_root, 541*6777b538SAndroid Build Coastguard Worker args.lint_gen_dir, 542*6777b538SAndroid Build Coastguard Worker args.baseline, 543*6777b538SAndroid Build Coastguard Worker testonly_target=args.testonly, 544*6777b538SAndroid Build Coastguard Worker warnings_as_errors=args.warnings_as_errors) 545*6777b538SAndroid Build Coastguard Worker logging.info('Creating stamp file') 546*6777b538SAndroid Build Coastguard Worker build_utils.Touch(args.stamp) 547*6777b538SAndroid Build Coastguard Worker 548*6777b538SAndroid Build Coastguard Worker if args.depfile: 549*6777b538SAndroid Build Coastguard Worker action_helpers.write_depfile(args.depfile, args.stamp, depfile_deps) 550*6777b538SAndroid Build Coastguard Worker 551*6777b538SAndroid Build Coastguard Worker 552*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 553*6777b538SAndroid Build Coastguard Worker sys.exit(main()) 554