Reland "Android: Move dep-related code to dep_utils.py"
This reverts commit 1a61670568a764168c353c3e4a91f53fdcb72984.
Reason for revert: angle is adding //tools/android in
https://crrev.com/c/4582331
Original change's description:
> Revert "Android: Move dep-related code to dep_utils.py"
>
> This reverts commit 4ba710110b52161201898c376e50cc62418e0052.
>
> Reason for revert: https://anglebug.com/8178
>
> Original change's description:
> > Android: Move dep-related code to dep_utils.py
> >
> > This CL mostly moves code from lookup_dep.py -> dep_utils.py so that it
> > is accessible by //build scripts. This paves the way to simplifying the
> > code in subsequent CLs and making use of it in bytecode_processor.py.
> >
> > Bug: 1099522
> > Change-Id: Ia197e113a26b550a2a080c589ee23200fafe3767
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4567009
> > Commit-Queue: Mohamed Heikal <mheikal@chromium.org>
> > Reviewed-by: Mohamed Heikal <mheikal@chromium.org>
> > Auto-Submit: Peter Wen <wnwen@chromium.org>
> > Commit-Queue: Peter Wen <wnwen@chromium.org>
> > Cr-Commit-Position: refs/heads/main@{#1149807}
>
> Bug: 1099522
> Bug: angleproject:8178
> Change-Id: Id840469247b903de9ed29b016339cca3342c2bf2
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4573447
> Auto-Submit: Roman Lavrov <romanl@google.com>
> Reviewed-by: Mohamed Heikal <mheikal@chromium.org>
> Commit-Queue: Andrew Grieve <agrieve@chromium.org>
> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> Reviewed-by: Andrew Grieve <agrieve@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1150305}
Bug: 1099522
Bug: angleproject:8178
Change-Id: I6ed2d0a66851cafe030c433150b1886a828a6226
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4582810
Commit-Queue: Peter Wen <wnwen@chromium.org>
Auto-Submit: Peter Wen <wnwen@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/main@{#1152609}
diff --git a/build/android/gyp/bytecode_processor.py b/build/android/gyp/bytecode_processor.py
index ea9470c..127e218 100755
--- a/build/android/gyp/bytecode_processor.py
+++ b/build/android/gyp/bytecode_processor.py
@@ -13,8 +13,8 @@
import sys
from typing import Dict, List
-import javac_output_processor
from util import build_utils
+from util import dep_utils
from util import jar_utils
from util import server_utils
import action_helpers # build_utils adds //build to sys.path.
@@ -215,7 +215,7 @@
args.full_classpath_jars = action_helpers.parse_gn_list(
args.full_classpath_jars)
args.full_classpath_gn_targets = [
- javac_output_processor.ReplaceGmsPackageIfNeeded(t)
+ dep_utils.ReplaceGmsPackageIfNeeded(t)
for t in action_helpers.parse_gn_list(args.full_classpath_gn_targets)
]
args.missing_classes_allowlist = action_helpers.parse_gn_list(
diff --git a/build/android/gyp/bytecode_processor.pydeps b/build/android/gyp/bytecode_processor.pydeps
index 1e6956a..78db0c4 100644
--- a/build/android/gyp/bytecode_processor.pydeps
+++ b/build/android/gyp/bytecode_processor.pydeps
@@ -9,13 +9,6 @@
../../../third_party/catapult/devil/devil/android/sdk/version_codes.py
../../../third_party/catapult/devil/devil/constants/__init__.py
../../../third_party/catapult/devil/devil/constants/exit_codes.py
-../../../third_party/colorama/src/colorama/__init__.py
-../../../third_party/colorama/src/colorama/ansi.py
-../../../third_party/colorama/src/colorama/ansitowin32.py
-../../../third_party/colorama/src/colorama/initialise.py
-../../../third_party/colorama/src/colorama/win32.py
-../../../third_party/colorama/src/colorama/winterm.py
-../../../tools/android/modularization/convenience/lookup_dep.py
../../../tools/android/modularization/gn/dep_operations.py
../../../tools/android/modularization/gn/json_gn_editor.py
../../../tools/android/modularization/gn/utils.py
@@ -28,8 +21,8 @@
../pylib/__init__.py
../pylib/constants/__init__.py
bytecode_processor.py
-javac_output_processor.py
util/__init__.py
util/build_utils.py
+util/dep_utils.py
util/jar_utils.py
util/server_utils.py
diff --git a/build/android/gyp/compile_java.pydeps b/build/android/gyp/compile_java.pydeps
index 85da8c9..65cbd13 100644
--- a/build/android/gyp/compile_java.pydeps
+++ b/build/android/gyp/compile_java.pydeps
@@ -15,7 +15,6 @@
../../../third_party/colorama/src/colorama/initialise.py
../../../third_party/colorama/src/colorama/win32.py
../../../third_party/colorama/src/colorama/winterm.py
-../../../tools/android/modularization/convenience/lookup_dep.py
../../action_helpers.py
../../gn_helpers.py
../../print_python_deps.py
@@ -27,6 +26,7 @@
javac_output_processor.py
util/__init__.py
util/build_utils.py
+util/dep_utils.py
util/jar_info_utils.py
util/jar_utils.py
util/md5_check.py
diff --git a/build/android/gyp/compile_kt.pydeps b/build/android/gyp/compile_kt.pydeps
index bac5962..223647d 100644
--- a/build/android/gyp/compile_kt.pydeps
+++ b/build/android/gyp/compile_kt.pydeps
@@ -15,7 +15,6 @@
../../../third_party/colorama/src/colorama/initialise.py
../../../third_party/colorama/src/colorama/win32.py
../../../third_party/colorama/src/colorama/winterm.py
-../../../tools/android/modularization/convenience/lookup_dep.py
../../action_helpers.py
../../gn_helpers.py
../../print_python_deps.py
@@ -28,6 +27,7 @@
javac_output_processor.py
util/__init__.py
util/build_utils.py
+util/dep_utils.py
util/jar_info_utils.py
util/jar_utils.py
util/md5_check.py
diff --git a/build/android/gyp/javac_output_processor.py b/build/android/gyp/javac_output_processor.py
index 6faf5de5..db58845 100755
--- a/build/android/gyp/javac_output_processor.py
+++ b/build/android/gyp/javac_output_processor.py
@@ -5,62 +5,19 @@
# found in the LICENSE file.
"""Contains helper class for processing javac output."""
-import dataclasses
import os
import pathlib
import re
import sys
import traceback
-from typing import List
from util import build_utils
+from util import dep_utils
sys.path.insert(
0,
os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src'))
import colorama
-sys.path.insert(
- 0,
- os.path.join(build_utils.DIR_SOURCE_ROOT, 'tools', 'android',
- 'modularization', 'convenience'))
-import lookup_dep
-
-
-def ReplaceGmsPackageIfNeeded(target_name: str) -> str:
- if target_name.startswith(
- ('//third_party/android_deps:google_play_services_',
- '//clank/third_party/google3:google_play_services_')):
- return f'$google_play_services_package:{target_name.split(":")[1]}'
- return target_name
-
-
-def _DisambiguateDeps(class_entries: List[lookup_dep.ClassEntry]):
- def filter_if_not_empty(entries, filter_func):
- filtered_entries = [e for e in entries if filter_func(e)]
- return filtered_entries or entries
-
- # When some deps are preferred, ignore all other potential deps.
- class_entries = filter_if_not_empty(class_entries, lambda e: e.preferred_dep)
-
- # E.g. javax_annotation_jsr250_api_java.
- class_entries = filter_if_not_empty(class_entries,
- lambda e: 'jsr' in e.target)
-
- # Avoid suggesting subtargets when regular targets exist.
- class_entries = filter_if_not_empty(class_entries,
- lambda e: '__' not in e.target)
-
- # Swap out GMS package names if needed.
- class_entries = [
- dataclasses.replace(e, target=ReplaceGmsPackageIfNeeded(e.target))
- for e in class_entries
- ]
-
- # Convert to dict and then use list to get the keys back to remove dups and
- # keep order the same as before.
- class_entries = list({e: True for e in class_entries})
-
- return class_entries
class JavacOutputProcessor:
@@ -181,7 +138,7 @@
return
if self._class_lookup_index is None:
- self._class_lookup_index = lookup_dep.ClassLookupIndex(
+ self._class_lookup_index = dep_utils.ClassLookupIndex(
pathlib.Path(os.getcwd()),
should_build=False,
)
@@ -192,7 +149,7 @@
if not suggested_deps:
return
- suggested_deps = _DisambiguateDeps(suggested_deps)
+ suggested_deps = dep_utils.DisambiguateDeps(suggested_deps)
suggested_deps_str = ', '.join(s.target for s in suggested_deps)
if len(suggested_deps) > 1:
diff --git a/build/android/gyp/turbine.pydeps b/build/android/gyp/turbine.pydeps
index dca0c69..cfe15c5c 100644
--- a/build/android/gyp/turbine.pydeps
+++ b/build/android/gyp/turbine.pydeps
@@ -15,7 +15,6 @@
../../../third_party/colorama/src/colorama/initialise.py
../../../third_party/colorama/src/colorama/win32.py
../../../third_party/colorama/src/colorama/winterm.py
-../../../tools/android/modularization/convenience/lookup_dep.py
../../action_helpers.py
../../gn_helpers.py
../../print_python_deps.py
@@ -28,6 +27,7 @@
turbine.py
util/__init__.py
util/build_utils.py
+util/dep_utils.py
util/jar_info_utils.py
util/jar_utils.py
util/md5_check.py
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index d5dbb3a..ad0515b 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -12,7 +12,6 @@
import json
import logging
import os
-import pipes
import re
import shlex
import shutil
@@ -21,7 +20,6 @@
import sys
import tempfile
import textwrap
-import time
import zipfile
sys.path.append(os.path.join(os.path.dirname(__file__),
diff --git a/build/android/gyp/util/dep_utils.py b/build/android/gyp/util/dep_utils.py
new file mode 100644
index 0000000..1220991a
--- /dev/null
+++ b/build/android/gyp/util/dep_utils.py
@@ -0,0 +1,265 @@
+# 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.
+"""Methods for managing deps based on build_config.json files."""
+
+from __future__ import annotations
+import collections
+
+import dataclasses
+import json
+import logging
+import os
+import pathlib
+import subprocess
+import sys
+from typing import Dict, Iterator, List, Set
+
+from util import jar_utils
+
+_SRC_PATH = pathlib.Path(__file__).resolve().parents[4]
+
+sys.path.append(str(_SRC_PATH / 'build/android'))
+# Import list_java_targets so that the dependency is found by print_python_deps.
+import list_java_targets
+
+
+@dataclasses.dataclass(frozen=True)
+class ClassEntry:
+ """An assignment of a Java class to a build target."""
+ full_class_name: str
+ target: str
+ preferred_dep: bool
+
+ def __lt__(self, other: 'ClassEntry'):
+ # Prefer canonical targets first.
+ if self.preferred_dep and not other.preferred_dep:
+ return True
+ # Prefer targets without __ in the name. Usually double underscores are used
+ # for internal subtargets and not top level targets.
+ if '__' not in self.target and '__' in other.target:
+ return True
+ # Prefer shorter target names first since they are usually the correct ones.
+ if len(self.target) < len(other.target):
+ return True
+ if len(self.target) > len(other.target):
+ return False
+ # Use string comparison to get a stable ordering of equal-length names.
+ return self.target < other.target
+
+
+@dataclasses.dataclass
+class BuildConfig:
+ """Container for information from a build config."""
+ target_name: str
+ relpath: str
+ is_group: bool
+ preferred_dep: bool
+ dependent_config_paths: List[str]
+ full_class_names: Set[str]
+
+ def all_dependent_configs(
+ self,
+ path_to_configs: Dict[str, 'BuildConfig'],
+ ) -> Iterator['BuildConfig']:
+ for path in self.dependent_config_paths:
+ dep_build_config = path_to_configs.get(path)
+ # This can happen when a java group depends on non-java targets.
+ if dep_build_config is None:
+ continue
+ yield dep_build_config
+ if dep_build_config.is_group:
+ yield from dep_build_config.all_dependent_configs(path_to_configs)
+
+
+class ClassLookupIndex:
+ """A map from full Java class to its build targets.
+
+ A class might be in multiple targets if it's bytecode rewritten."""
+ def __init__(self, abs_build_output_dir: pathlib.Path, should_build: bool):
+ self._abs_build_output_dir = abs_build_output_dir
+ self._should_build = should_build
+ self._class_index = self._index_root()
+
+ def match(self, search_string: str) -> List[ClassEntry]:
+ """Get class/target entries where the class matches search_string"""
+ # Priority 1: Exact full matches
+ if search_string in self._class_index:
+ return self._entries_for(search_string)
+
+ # Priority 2: Match full class name (any case), if it's a class name
+ matches = []
+ lower_search_string = search_string.lower()
+ if '.' not in lower_search_string:
+ for full_class_name in self._class_index:
+ package_and_class = full_class_name.rsplit('.', 1)
+ if len(package_and_class) < 2:
+ continue
+ class_name = package_and_class[1]
+ class_lower = class_name.lower()
+ if class_lower == lower_search_string:
+ matches.extend(self._entries_for(full_class_name))
+ if matches:
+ return matches
+
+ # Priority 3: Match anything
+ for full_class_name in self._class_index:
+ if lower_search_string in full_class_name.lower():
+ matches.extend(self._entries_for(full_class_name))
+
+ return matches
+
+ def _entries_for(self, class_name) -> List[ClassEntry]:
+ return sorted(self._class_index[class_name])
+
+ def _index_root(self) -> Dict[str, Set[ClassEntry]]:
+ """Create the class to target index."""
+ logging.debug('Running list_java_targets.py...')
+ list_java_targets_command = [
+ 'build/android/list_java_targets.py', '--gn-labels',
+ '--print-build-config-paths',
+ f'--output-directory={self._abs_build_output_dir}'
+ ]
+ if self._should_build:
+ list_java_targets_command += ['--build']
+
+ list_java_targets_run = subprocess.run(list_java_targets_command,
+ cwd=_SRC_PATH,
+ capture_output=True,
+ text=True,
+ check=True)
+ logging.debug('... done.')
+
+ # Parse output of list_java_targets.py into BuildConfig objects.
+ path_to_build_config: Dict[str, BuildConfig] = {}
+ target_lines = list_java_targets_run.stdout.splitlines()
+ for target_line in target_lines:
+ # Skip empty lines
+ if not target_line:
+ continue
+
+ target_line_parts = target_line.split(': ')
+ assert len(target_line_parts) == 2, target_line_parts
+ target_name, build_config_path = target_line_parts
+
+ if not os.path.exists(build_config_path):
+ assert not self._should_build
+ continue
+
+ with open(build_config_path) as build_config_contents:
+ build_config_json: Dict = json.load(build_config_contents)
+ deps_info = build_config_json['deps_info']
+
+ # Checking the library type here instead of in list_java_targets.py avoids
+ # reading each .build_config file twice.
+ if deps_info['type'] not in ('java_library', 'group'):
+ continue
+
+ relpath = os.path.relpath(build_config_path, self._abs_build_output_dir)
+ preferred_dep = bool(deps_info.get('preferred_dep'))
+ is_group = bool(deps_info.get('type') == 'group')
+ dependent_config_paths = deps_info.get('deps_configs', [])
+ full_class_names = self._compute_full_class_names_for_build_config(
+ deps_info)
+ build_config = BuildConfig(relpath=relpath,
+ target_name=target_name,
+ is_group=is_group,
+ preferred_dep=preferred_dep,
+ dependent_config_paths=dependent_config_paths,
+ full_class_names=full_class_names)
+ path_to_build_config[relpath] = build_config
+
+ # From GN's perspective, depending on a java group is the same as depending
+ # on all of its deps directly, since groups are collapsed in
+ # write_build_config.py. Thus, collect all the java files in a java group's
+ # deps (recursing into other java groups) and set that as the java group's
+ # list of classes.
+ for build_config in path_to_build_config.values():
+ if build_config.is_group:
+ for dep_build_config in build_config.all_dependent_configs(
+ path_to_build_config):
+ build_config.full_class_names.update(
+ dep_build_config.full_class_names)
+
+ class_index = collections.defaultdict(set)
+ for build_config in path_to_build_config.values():
+ for full_class_name in build_config.full_class_names:
+ class_index[full_class_name].add(
+ ClassEntry(full_class_name=full_class_name,
+ target=build_config.target_name,
+ preferred_dep=build_config.preferred_dep))
+
+ return class_index
+
+ def _compute_full_class_names_for_build_config(self,
+ deps_info: Dict) -> Set[str]:
+ """Returns set of fully qualified class names for build config."""
+
+ full_class_names = set()
+
+ # Read the location of the target_sources_file from the build_config
+ sources_path = deps_info.get('target_sources_file')
+ if sources_path:
+ # Read the target_sources_file, indexing the classes found
+ with open(self._abs_build_output_dir / sources_path) as sources_contents:
+ for source_line in sources_contents:
+ source_path = pathlib.Path(source_line.strip())
+ java_class = jar_utils.parse_full_java_class(source_path)
+ if java_class:
+ full_class_names.add(java_class)
+
+ # |unprocessed_jar_path| is set for prebuilt targets. (ex:
+ # android_aar_prebuilt())
+ # |unprocessed_jar_path| might be set but not exist if not all targets have
+ # been built.
+ unprocessed_jar_path = deps_info.get('unprocessed_jar_path')
+ if unprocessed_jar_path:
+ abs_unprocessed_jar_path = (self._abs_build_output_dir /
+ unprocessed_jar_path)
+ if abs_unprocessed_jar_path.exists():
+ # Normalize path but do not follow symlink if .jar is symlink.
+ abs_unprocessed_jar_path = (abs_unprocessed_jar_path.parent.resolve() /
+ abs_unprocessed_jar_path.name)
+
+ full_class_names.update(
+ jar_utils.extract_full_class_names_from_jar(
+ self._abs_build_output_dir, abs_unprocessed_jar_path))
+
+ return full_class_names
+
+
+def ReplaceGmsPackageIfNeeded(target_name: str) -> str:
+ if target_name.startswith(
+ ('//third_party/android_deps:google_play_services_',
+ '//clank/third_party/google3:google_play_services_')):
+ return f'$google_play_services_package:{target_name.split(":")[1]}'
+ return target_name
+
+
+def DisambiguateDeps(class_entries: List[ClassEntry]):
+ def filter_if_not_empty(entries, filter_func):
+ filtered_entries = [e for e in entries if filter_func(e)]
+ return filtered_entries or entries
+
+ # When some deps are preferred, ignore all other potential deps.
+ class_entries = filter_if_not_empty(class_entries, lambda e: e.preferred_dep)
+
+ # E.g. javax_annotation_jsr250_api_java.
+ class_entries = filter_if_not_empty(class_entries,
+ lambda e: 'jsr' in e.target)
+
+ # Avoid suggesting subtargets when regular targets exist.
+ class_entries = filter_if_not_empty(class_entries,
+ lambda e: '__' not in e.target)
+
+ # Swap out GMS package names if needed.
+ class_entries = [
+ dataclasses.replace(e, target=ReplaceGmsPackageIfNeeded(e.target))
+ for e in class_entries
+ ]
+
+ # Convert to dict and then use list to get the keys back to remove dups and
+ # keep order the same as before.
+ class_entries = list({e: True for e in class_entries})
+
+ return class_entries
diff --git a/tools/android/modularization/convenience/lookup_dep.py b/tools/android/modularization/convenience/lookup_dep.py
index 5b5adc20..ee72cf9 100755
--- a/tools/android/modularization/convenience/lookup_dep.py
+++ b/tools/android/modularization/convenience/lookup_dep.py
@@ -14,30 +14,18 @@
Find build target with class FooUtil:
tools/android/modularization/convenience/lookup_dep.py FooUtil
'''
-
-from __future__ import annotations
-
import argparse
-import collections
-import dataclasses
-import json
import logging
-import os
import pathlib
-import subprocess
import sys
-from typing import Dict, Iterator, List, Set
_SRC_DIR = pathlib.Path(__file__).resolve().parents[4]
sys.path.append(str(_SRC_DIR / 'build/android'))
from pylib import constants
-# Import list_java_targets so that the dependency is found by print_python_deps.
-import list_java_targets
-
sys.path.append(str(_SRC_DIR / 'build/android/gyp'))
-from util import jar_utils
+from util import dep_utils
def main():
@@ -71,7 +59,7 @@
abs_out_dir: pathlib.Path = pathlib.Path(
constants.GetOutDirectory()).resolve()
- index = ClassLookupIndex(abs_out_dir, arguments.build)
+ index = dep_utils.ClassLookupIndex(abs_out_dir, arguments.build)
matches = {c: index.match(c) for c in arguments.classes}
if not arguments.build:
@@ -81,7 +69,7 @@
arguments.build = True
break
if arguments.build:
- index = ClassLookupIndex(abs_out_dir, True)
+ index = dep_utils.ClassLookupIndex(abs_out_dir, True)
matches = {c: index.match(c) for c in arguments.classes}
if not arguments.build:
@@ -105,210 +93,5 @@
print()
-@dataclasses.dataclass(frozen=True)
-class ClassEntry:
- """An assignment of a Java class to a build target."""
- full_class_name: str
- target: str
- preferred_dep: bool
-
- def __lt__(self, other: 'ClassEntry'):
- # Prefer canonical targets first.
- if self.preferred_dep and not other.preferred_dep:
- return True
- # Prefer targets without __ in the name. Usually double underscores are used
- # for internal subtargets and not top level targets.
- if '__' not in self.target and '__' in other.target:
- return True
- # Prefer shorter target names first since they are usually the correct ones.
- if len(self.target) < len(other.target):
- return True
- elif len(self.target) > len(other.target):
- return False
- # Use string comparison to get a stable ordering of equal-length names.
- return self.target < other.target
-
-
-@dataclasses.dataclass
-class BuildConfig:
- """Container for information from a build config."""
- target_name: str
- relpath: str
- is_group: bool
- preferred_dep: bool
- dependent_config_paths: List[str]
- full_class_names: Set[str]
-
- def all_dependent_configs(
- self,
- path_to_configs: Dict[str, 'BuildConfig'],
- ) -> Iterator['BuildConfig']:
- for path in self.dependent_config_paths:
- dep_build_config = path_to_configs.get(path)
- # This can happen when a java group depends on non-java targets.
- if dep_build_config is None:
- continue
- yield dep_build_config
- if dep_build_config.is_group:
- yield from dep_build_config.all_dependent_configs(path_to_configs)
-
-
-class ClassLookupIndex:
- """A map from full Java class to its build targets.
-
- A class might be in multiple targets if it's bytecode rewritten."""
-
- def __init__(self, abs_build_output_dir: pathlib.Path, should_build: bool):
- self._abs_build_output_dir = abs_build_output_dir
- self._should_build = should_build
- self._class_index = self._index_root()
-
- def match(self, search_string: str) -> List[ClassEntry]:
- """Get class/target entries where the class matches search_string"""
- # Priority 1: Exact full matches
- if search_string in self._class_index:
- return self._entries_for(search_string)
-
- # Priority 2: Match full class name (any case), if it's a class name
- matches = []
- lower_search_string = search_string.lower()
- if '.' not in lower_search_string:
- for full_class_name in self._class_index:
- package_and_class = full_class_name.rsplit('.', 1)
- if len(package_and_class) < 2:
- continue
- class_name = package_and_class[1]
- class_lower = class_name.lower()
- if class_lower == lower_search_string:
- matches.extend(self._entries_for(full_class_name))
- if matches:
- return matches
-
- # Priority 3: Match anything
- for full_class_name in self._class_index:
- if lower_search_string in full_class_name.lower():
- matches.extend(self._entries_for(full_class_name))
-
- return matches
-
- def _entries_for(self, class_name) -> List[ClassEntry]:
- return sorted(self._class_index[class_name])
-
- def _index_root(self) -> Dict[str, Set[ClassEntry]]:
- """Create the class to target index."""
- logging.debug('Running list_java_targets.py...')
- list_java_targets_command = [
- sys.executable, 'build/android/list_java_targets.py', '--gn-labels',
- '--print-build-config-paths',
- f'--output-directory={self._abs_build_output_dir}'
- ]
- if self._should_build:
- list_java_targets_command += ['--build']
-
- list_java_targets_run = subprocess.run(list_java_targets_command,
- cwd=_SRC_DIR,
- capture_output=True,
- text=True,
- check=True)
- logging.debug('... done.')
-
- # Parse output of list_java_targets.py into BuildConfig objects.
- path_to_build_config: Dict[str, BuildConfig] = {}
- target_lines = list_java_targets_run.stdout.splitlines()
- for target_line in target_lines:
- # Skip empty lines
- if not target_line:
- continue
-
- target_line_parts = target_line.split(': ')
- assert len(target_line_parts) == 2, target_line_parts
- target_name, build_config_path = target_line_parts
-
- if not os.path.exists(build_config_path):
- assert not self._should_build
- continue
-
- with open(build_config_path) as build_config_contents:
- build_config_json: Dict = json.load(build_config_contents)
- deps_info = build_config_json['deps_info']
-
- # Checking the library type here instead of in list_java_targets.py avoids
- # reading each .build_config file twice.
- if deps_info['type'] not in ('java_library', 'group'):
- continue
-
- relpath = os.path.relpath(build_config_path, self._abs_build_output_dir)
- preferred_dep = bool(deps_info.get('preferred_dep'))
- is_group = bool(deps_info.get('type') == 'group')
- dependent_config_paths = deps_info.get('deps_configs', [])
- full_class_names = self._compute_full_class_names_for_build_config(
- deps_info)
- build_config = BuildConfig(relpath=relpath,
- target_name=target_name,
- is_group=is_group,
- preferred_dep=preferred_dep,
- dependent_config_paths=dependent_config_paths,
- full_class_names=full_class_names)
- path_to_build_config[relpath] = build_config
-
- # From GN's perspective, depending on a java group is the same as depending
- # on all of its deps directly, since groups are collapsed in
- # write_build_config.py. Thus, collect all the java files in a java group's
- # deps (recursing into other java groups) and set that as the java group's
- # list of classes.
- for build_config in path_to_build_config.values():
- if build_config.is_group:
- for dep_build_config in build_config.all_dependent_configs(
- path_to_build_config):
- build_config.full_class_names.update(
- dep_build_config.full_class_names)
-
- class_index = collections.defaultdict(set)
- for build_config in path_to_build_config.values():
- for full_class_name in build_config.full_class_names:
- class_index[full_class_name].add(
- ClassEntry(full_class_name=full_class_name,
- target=build_config.target_name,
- preferred_dep=build_config.preferred_dep))
-
- return class_index
-
- def _compute_full_class_names_for_build_config(self,
- deps_info: Dict) -> Set[str]:
- """Returns set of fully qualified class names for build config."""
-
- full_class_names = set()
-
- # Read the location of the target_sources_file from the build_config
- sources_path = deps_info.get('target_sources_file')
- if sources_path:
- # Read the target_sources_file, indexing the classes found
- with open(self._abs_build_output_dir / sources_path) as sources_contents:
- for source_line in sources_contents:
- source_path = pathlib.Path(source_line.strip())
- java_class = jar_utils.parse_full_java_class(source_path)
- if java_class:
- full_class_names.add(java_class)
-
- # |unprocessed_jar_path| is set for prebuilt targets. (ex:
- # android_aar_prebuilt())
- # |unprocessed_jar_path| might be set but not exist if not all targets have
- # been built.
- unprocessed_jar_path = deps_info.get('unprocessed_jar_path')
- if unprocessed_jar_path:
- abs_unprocessed_jar_path = (self._abs_build_output_dir /
- unprocessed_jar_path)
- if abs_unprocessed_jar_path.exists():
- # Normalize path but do not follow symlink if .jar is symlink.
- abs_unprocessed_jar_path = (abs_unprocessed_jar_path.parent.resolve() /
- abs_unprocessed_jar_path.name)
-
- full_class_names.update(
- jar_utils.extract_full_class_names_from_jar(
- self._abs_build_output_dir, abs_unprocessed_jar_path))
-
- return full_class_names
-
-
if __name__ == '__main__':
main()