naiveproxy/src/build/gn_ast/jni_refactor.py
2024-07-27 08:55:30 +08:00

156 lines
5.2 KiB
Python

# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Refactors BUILD.gn files for our Annotation Processor -> .srcjar migration.
1) Finds all generate_jni() targets
2) Finds all android_library() targets with that use ":jni_processor"
3) Compares lists of sources between them
4) Removes the annotation_processor_deps entry
5) Adds the generate_jni target as a srcjar_dep
6) Updates visibility of generate_jni to allow the dep
This script has already done its job, but is left as an example of using gn_ast.
"""
import argparse
import sys
import gn_ast
_PROCESSOR_DEP = '//base/android/jni_generator:jni_processor'
class RefactorException(Exception):
pass
def find_processor_assignment(target):
for assignment in target.block.find_assignments(
'annotation_processor_deps'):
processors = assignment.list_value.literals
if _PROCESSOR_DEP in processors:
return assignment
return None
def find_all_sources(target, build_file):
ret = []
def helper(assignments):
for assignment in assignments:
if assignment.operation not in ('=', '+='):
raise RefactorException(
f'{target.name}: sources has a {assignment.operation}.')
value = assignment.value
if value.is_identifier():
helper(build_file.block.find_assignments(value.node_value))
elif not value.is_list():
raise RefactorException(f'{target.name}: sources not a list.')
else:
ret.extend(value.literals)
helper(target.block.find_assignments('sources'))
return ret
def find_matching_jni_target(library_target, jni_target_to_sources,
build_file):
all_sources = set(find_all_sources(library_target, build_file))
matches = []
for jni_target_name, jni_sources in jni_target_to_sources.items():
if all(s in all_sources for s in jni_sources):
matches.append(jni_target_name)
if len(matches) == 1:
return matches[0]
if len(matches) > 1:
raise RefactorException(
f'{library_target.name}: Matched multiple generate_jni().')
if jni_target_to_sources:
raise RefactorException(
f'{library_target.name}: No matching generate_jni().')
raise RefactorException('No sources found for generate_jni().')
def fix_visibility(target):
for assignment in target.block.find_assignments('visibility'):
if not assignment.value.is_list():
continue
list_value = assignment.list_value
for value in list(list_value.literals):
if value.startswith(':'):
list_value.remove_literal(value)
list_value.add_literal(':*')
def refactor(lib_target, jni_target):
assignments = lib_target.block.find_assignments('srcjar_deps')
srcjar_deps = assignments[0] if assignments else None
if srcjar_deps is None:
srcjar_deps = gn_ast.AssignmentWrapper.create_list('srcjar_deps')
first_source_assignment = lib_target.block.find_assignments(
'sources')[0]
lib_target.block.add_child(srcjar_deps, before=first_source_assignment)
elif not srcjar_deps.value.is_list():
raise RefactorException(
f'{lib_target.name}: srcjar_deps is not a list.')
srcjar_deps.list_value.add_literal(f':{jni_target.name}')
processor_assignment = find_processor_assignment(lib_target)
processors = processor_assignment.list_value.literals
if len(processors) == 1:
lib_target.block.remove_child(processor_assignment.node)
else:
processor_assignment.list_value.remove_literal(_PROCESSOR_DEP)
fix_visibility(jni_target)
def analyze(build_file):
targets = build_file.targets
jni_targets = [t for t in targets if t.type == 'generate_jni']
lib_targets = [t for t in targets if find_processor_assignment(t)]
if len(jni_targets) == 0 and len(lib_targets) == 0:
return
# Match up target when there are only one, even when targets use variables
# for list values.
if len(jni_targets) == 1 and len(lib_targets) == 1:
refactor(lib_targets[0], jni_targets[0])
return
jni_target_to_sources = {
t.name: find_all_sources(t, build_file)
for t in jni_targets
}
for lib_target in lib_targets:
jni_target_name = find_matching_jni_target(lib_target,
jni_target_to_sources,
build_file)
jni_target = build_file.targets_by_name[jni_target_name]
refactor(lib_target, jni_target)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('path')
args = parser.parse_args()
try:
build_file = gn_ast.BuildFile.from_file(args.path)
analyze(build_file)
if build_file.write_changes():
print(f'{args.path}: Changes applied.')
else:
print(f'{args.path}: No changes necessary.')
except RefactorException as e:
print(f'{args.path}: {e}')
sys.exit(1)
except Exception:
print('Failure on', args.path)
raise
if __name__ == '__main__':
main()