Loading tools/lint/fix/soong_lint_fix.py +109 −54 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import argparse import json import functools import os import shutil import subprocess Loading @@ -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: Loading @@ -49,10 +51,25 @@ 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" Loading @@ -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, Loading @@ -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: Loading @@ -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: def _find_module(self, module_name): """Returns a SoongModule from a module name. Ensures that the module is known to Soong. """ module = SoongModule(module_name) module.find(module_info) self._modules.append(module) 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 _lint(self): 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}") Loading @@ -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(): Loading @@ -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""" Loading tools/lint/utils/enforce_permission_counter.py +27 −46 Original line number Diff line number Diff line Loading @@ -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)) Loading Loading
tools/lint/fix/soong_lint_fix.py +109 −54 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import argparse import json import functools import os import shutil import subprocess Loading @@ -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: Loading @@ -49,10 +51,25 @@ 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" Loading @@ -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, Loading @@ -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: Loading @@ -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: def _find_module(self, module_name): """Returns a SoongModule from a module name. Ensures that the module is known to Soong. """ module = SoongModule(module_name) module.find(module_info) self._modules.append(module) 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 _lint(self): 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}") Loading @@ -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(): Loading @@ -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""" Loading
tools/lint/utils/enforce_permission_counter.py +27 −46 Original line number Diff line number Diff line Loading @@ -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)) Loading