diff options
author | 2023-09-27 04:15:08 +0000 | |
---|---|---|
committer | 2023-09-27 04:15:08 +0000 | |
commit | f964854d1fc7f74ee9b27e6b844c999848e1571f (patch) | |
tree | 13da3a9c9e57e2dbdacbfca3f3f1acb85aec5e17 | |
parent | 07615e3062473c97556ce9f4f7dca70cae3a20f4 (diff) | |
parent | e09b6f3aa096d315bdb922e3ced6b33dad7376b7 (diff) |
Merge "enforce_permission_counter: Find targets automatically" into main
-rw-r--r-- | tools/lint/fix/soong_lint_fix.py | 163 | ||||
-rw-r--r-- | tools/lint/utils/enforce_permission_counter.py | 73 |
2 files changed, 136 insertions, 100 deletions
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py index 4a2e37e51d71..2e82beb24b56 100644 --- a/tools/lint/fix/soong_lint_fix.py +++ b/tools/lint/fix/soong_lint_fix.py @@ -14,6 +14,7 @@ import argparse import json +import functools import os import shutil import subprocess @@ -28,6 +29,7 @@ SOONG_UI = "build/soong/soong_ui.bash" PATH_PREFIX = "out/soong/.intermediates" PATH_SUFFIX = "android_common/lint" FIX_ZIP = "suggested-fixes.zip" +MODULE_JAVA_DEPS = "out/soong/module_bp_java_deps.json" class SoongModule: @@ -49,11 +51,26 @@ class SoongModule: print(f"Found module {partial_path}/{self._name}.") self._path = f"{PATH_PREFIX}/{partial_path}/{self._name}/{PATH_SUFFIX}" + def find_java_deps(self, module_java_deps): + """Finds the dependencies of a Java module in the loaded module_bp_java_deps.json. + + Returns: + A list of module names. + """ + if self._name not in module_java_deps: + raise Exception(f"Module {self._name} not found!") + + return module_java_deps[self._name]["dependencies"] + @property def name(self): return self._name @property + def path(self): + return self._path + + @property def lint_report(self): return f"{self._path}/lint-report.txt" @@ -62,52 +79,25 @@ class SoongModule: return f"{self._path}/{FIX_ZIP}" -class SoongLintFix: +class SoongLintWrapper: """ - This class creates a command line tool that will apply lint fixes to the - platform via the necessary combination of soong and shell commands. + This class wraps the necessary calls to Soong and/or shell commands to lint + platform modules and apply suggested fixes if desired. - It breaks up these operations into a few "private" methods that are - intentionally exposed so experimental code can tweak behavior. - - The entry point, `run`, will apply lint fixes using the intermediate - `suggested-fixes` directory that soong creates during its invocation of - lint. - - Basic usage: - ``` - from soong_lint_fix import SoongLintFix - - opts = SoongLintFixOptions() - opts.parse_args(sys.argv) - SoongLintFix(opts).run() - ``` + It breaks up these operations into a few methods that are available to + sub-classes (see SoongLintFix for an example). """ - def __init__(self, opts): - self._opts = opts + def __init__(self, check=None, lint_module=None): + self._check = check + self._lint_module = lint_module self._kwargs = None - self._modules = [] - - def run(self): - """ - Run the script - """ - self._setup() - self._find_modules() - self._lint() - - if not self._opts.no_fix: - self._fix() - - if self._opts.print: - self._print() def _setup(self): env = os.environ.copy() - if self._opts.check: - env["ANDROID_LINT_CHECK"] = self._opts.check - if self._opts.lint_module: - env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._opts.lint_module + if self._check: + env["ANDROID_LINT_CHECK"] = self._check + if self._lint_module: + env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._lint_module self._kwargs = { "env": env, @@ -117,7 +107,10 @@ class SoongLintFix: os.chdir(ANDROID_BUILD_TOP) - print("Refreshing soong modules...") + @functools.cached_property + def _module_info(self): + """Returns the JSON content of module-info.json.""" + print("Refreshing Soong modules...") try: os.mkdir(ANDROID_PRODUCT_OUT) except OSError: @@ -125,19 +118,54 @@ class SoongLintFix: subprocess.call(f"{SOONG_UI} --make-mode {PRODUCT_OUT}/module-info.json", **self._kwargs) print("done.") - - def _find_modules(self): with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f: - module_info = json.load(f) + return json.load(f) - for module_name in self._opts.modules: - module = SoongModule(module_name) - module.find(module_info) - self._modules.append(module) + def _find_module(self, module_name): + """Returns a SoongModule from a module name. - def _lint(self): + Ensures that the module is known to Soong. + """ + module = SoongModule(module_name) + module.find(self._module_info) + return module + + def _find_modules(self, module_names): + modules = [] + for module_name in module_names: + modules.append(self._find_module(module_name)) + return modules + + @functools.cached_property + def _module_java_deps(self): + """Returns the JSON content of module_bp_java_deps.json.""" + print("Refreshing Soong Java deps...") + subprocess.call(f"{SOONG_UI} --make-mode {MODULE_JAVA_DEPS}", **self._kwargs) + print("done.") + + with open(f"{MODULE_JAVA_DEPS}") as f: + return json.load(f) + + def _find_module_java_deps(self, module): + """Returns a list a dependencies for a module. + + Args: + module: A SoongModule. + + Returns: + A list of SoongModule. + """ + deps = [] + dep_names = module.find_java_deps(self._module_java_deps) + for dep_name in dep_names: + dep = SoongModule(dep_name) + dep.find(self._module_info) + deps.append(dep) + return deps + + def _lint(self, modules): print("Cleaning up any old lint results...") - for module in self._modules: + for module in modules: try: os.remove(f"{module.lint_report}") os.remove(f"{module.suggested_fixes}") @@ -145,13 +173,13 @@ class SoongLintFix: pass print("done.") - target = " ".join([ module.lint_report for module in self._modules ]) + target = " ".join([ module.lint_report for module in modules ]) print(f"Generating {target}") subprocess.call(f"{SOONG_UI} --make-mode {target}", **self._kwargs) print("done.") - def _fix(self): - for module in self._modules: + def _fix(self, modules): + for module in modules: print(f"Copying suggested fixes for {module.name} to the tree...") with zipfile.ZipFile(f"{module.suggested_fixes}") as zip: for name in zip.namelist(): @@ -161,13 +189,40 @@ class SoongLintFix: shutil.copyfileobj(src, dst) print("done.") - def _print(self): - for module in self._modules: + def _print(self, modules): + for module in modules: print(f"### lint-report.txt {module.name} ###", end="\n\n") with open(module.lint_report, "r") as f: print(f.read()) +class SoongLintFix(SoongLintWrapper): + """ + Basic usage: + ``` + from soong_lint_fix import SoongLintFix + + opts = SoongLintFixOptions() + opts.parse_args() + SoongLintFix(opts).run() + ``` + """ + def __init__(self, opts): + super().__init__(check=opts.check, lint_module=opts.lint_module) + self._opts = opts + + def run(self): + self._setup() + modules = self._find_modules(self._opts.modules) + self._lint(modules) + + if not self._opts.no_fix: + self._fix(modules) + + if self._opts.print: + self._print(modules) + + class SoongLintFixOptions: """Options for SoongLintFix""" diff --git a/tools/lint/utils/enforce_permission_counter.py b/tools/lint/utils/enforce_permission_counter.py index b5c2ffe9885e..a4c00f79b63f 100644 --- a/tools/lint/utils/enforce_permission_counter.py +++ b/tools/lint/utils/enforce_permission_counter.py @@ -16,57 +16,38 @@ import re import soong_lint_fix -# Libraries that constitute system_server. -# It is non-trivial to keep in sync with services/Android.bp as some -# module are post-processed (e.g, services.core). -TARGETS = [ - "services.core.unboosted", - "services.accessibility", - "services.appprediction", - "services.appwidget", - "services.autofill", - "services.backup", - "services.companion", - "services.contentcapture", - "services.contentsuggestions", - "services.coverage", - "services.devicepolicy", - "services.midi", - "services.musicsearch", - "services.net", - "services.people", - "services.print", - "services.profcollect", - "services.restrictions", - "services.searchui", - "services.smartspace", - "services.systemcaptions", - "services.translation", - "services.texttospeech", - "services.usage", - "services.usb", - "services.voiceinteraction", - "services.wallpapereffectsgeneration", - "services.wifi", -] +CHECK = "AnnotatedAidlCounter" +LINT_MODULE = "AndroidUtilsLintChecker" - -class EnforcePermissionMigratedCounter: +class EnforcePermissionMigratedCounter(soong_lint_fix.SoongLintWrapper): """Wrapper around lint_fix to count the number of AIDL methods annotated.""" - def run(self): - opts = soong_lint_fix.SoongLintFixOptions() - opts.check = "AnnotatedAidlCounter" - opts.lint_module = "AndroidUtilsLintChecker" - opts.no_fix = True - opts.modules = TARGETS - self.linter = soong_lint_fix.SoongLintFix(opts) - self.linter.run() - self.parse_lint_reports() + def __init__(self): + super().__init__(check=CHECK, lint_module=LINT_MODULE) + + def run(self): + self._setup() + + # Analyze the dependencies of the "services" module and the module + # "services.core.unboosted". + service_module = self._find_module("services") + dep_modules = self._find_module_java_deps(service_module) + \ + [self._find_module("services.core.unboosted")] + + # Skip dependencies that are not services. Skip the "services.core" + # module which is analyzed via "services.core.unboosted". + modules = [] + for module in dep_modules: + if "frameworks/base/services" not in module.path: + continue + if module.name == "services.core": + continue + modules.append(module) + + self._lint(modules) - def parse_lint_reports(self): counts = { "unannotated": 0, "enforced": 0, "notRequired": 0 } - for module in self.linter._modules: + for module in modules: with open(module.lint_report, "r") as f: content = f.read() keys = dict(re.findall(r'(\w+)=(\d+)', content)) |