Spracherkennung für: .md vermutete Sprache: Python {Python[80] Fortran[50] R[35]} [Methode: maximale Elemente, drei Dimensionen]
## Dexpreopt implementation
### Introduction
All dexpreopted Java code falls into three categories:
- bootclasspath
- system server
- apps
and libraries
Dexpreopt implementation
for bootclasspath libraries (boot images)
is located
in
[soong/java] (see e.g. [soong/java/dexpreopt_bootjars.go]),
and install rules
are
in [make/core/dex_preopt.mk].
Dexpreopt implementation
for system server, libraries
and apps
is located
in
[soong/dexpreopt].
For the rest of this section we focus primarily on it (
and
not boot images).
Dexpeopt implementation
is split across the Soong part
and the Make part. The
core logic
is in Soong,
and Make only generates configs
and scripts to
pass
information to Soong.
### Global and module dexpreopt.config
The build system generates a
global JSON dexpreopt config that
is populated
from
product variables. This
is static configuration that
is passed to both Soong
and
Make. The `$OUT/soong/dexpreopt.config` file
is generated
in
[make/core/dex_preopt_config.mk]. Soong reads it
in [soong/dexpreopt/config.go]
and makes a device-specific copy (this
is needed to ensure incremental build
correctness). The
global config contains lists of bootclasspath jars, system
server jars, dex2oat options,
global switches that enable
and disable parts of
dexpreopt
and so on.
The build system also generates a module config
for each dexpreopted package. It
contains package-specific configuration that
is derived
from the
global
configuration
and Android.bp
or Android.mk module
for the package.
Module configs
for Make packages are generated
in
[make/core/dex_preopt_odex_install.mk]; they are materialized
as per-package
JSON dexpreopt.config files.
Module configs
in Soong are
not materialized
as dexpreopt.config files
and exist
as Go structures
in memory, unless it
is necessary to materialize them
as a file
for dependent Make packages
or for post-dexpreopting. Module configs are defined
in [soong/dexpreopt/config.go].
### Dexpreopt in Soong
The Soong implementation of dexpreopt consists roughly of the following steps:
- Read
global dexpreopt config passed
from Make ([soong/dexpreopt/config.go]).
- Construct a static boot image config ([soong/java/dexpreopt_config.go]).
- During dependency mutator
pass,
for each suitable module:
- add uses-library dependencies (e.g.
for apps: [soong/java/app.go:deps])
- During rule generation
pass,
for each suitable module:
- compute transitive uses-library dependency closure
([soong/java/java.go:addCLCFromDep])
- construct CLC
from the dependency closure
([soong/dexpreopt/class_loader_context.go])
- construct module config
with CLC, boot image locations, etc.
([soong/java/dexpreopt.go])
- generate build rules to verify build-time CLC against the manifest (e.g.
for apps: [soong/java/app.go:verifyUsesLibraries])
- generate dexpreopt build rule ([soong/dexpreopt/dexpreopt.go])
- At the end of rule generation
pass:
- generate build rules
for boot images ([soong/java/dexpreopt_bootjars.go],
[soong/java/bootclasspath_fragment.go]
and
[soong/java/platform_bootclasspath.go])
### Dexpreopt in Make - dexpreopt_gen
In order to reuse the same dexpreopt implementation
for both Soong
and Make
packages, part of Soong
is compiled into a standalone binary dexpreopt_gen. It
runs during the Ninja stage of the build
and generates shell scripts
with
dexpreopt build rules
for Make packages,
and then executes them.
This setup causes many inconveniences. To name a few:
- Errors
in the build rules are only revealed at the late stage of the build.
- These rules are
not tested by the presubmit builds that run `m nothing` on
many build targets/products.
- It
is impossible to find dexpreopt build rules
in the generated Ninja files.
However all these issues are a lesser evil compared to having a duplicate
dexpreopt implementation
in Make. Also note that it would be problematic to
reimplement the logic
in Make anyway, because Android.mk modules are
not
processed
in the order of uses-library dependencies
and propagating dependency
information
from one module to another would require a similar workaround
with
a script.
Dexpreopt
for Make packages involves a few steps:
- At Soong phase (during `m nothing`), see dexpreopt_gen:
- generate build rules
for dexpreopt_gen binary
- At Make/Kati phase (during `m nothing`), see
[make/core/dex_preopt_odex_install.mk]:
- generate build rules
for module dexpreopt.config
- generate build rules
for merging dependency dexpreopt.config files (see
[make/core/dex_preopt_config_merger.py])
- generate build rules
for dexpreopt_gen invocation
- generate build rules
for executing dexpreopt.sh scripts
- At Ninja phase (during `m`):
- generate dexpreopt.config files
- execute dexpreopt_gen rules (generate dexpreopt.sh scripts)
- execute dexpreopt.sh scripts (this runs the actual dexpreopt)
The Make/Kati phase adds all the necessary dependencies that trigger
dexpreopt_gen
and dexpreopt.sh rules. The real dexpreopt command (dex2oat
invocation that will be executed to AOT-compile a package)
is in the
dexpreopt.sh script, which
is generated close to the end of the build.
### Indirect build rules
The process described above
for Make packages involves
"indirect build rules",
i.e. build rules that are generated
not at the time when the build system
is
created (which
is a small step at the very beginning of the build triggered
with
`m nothing`), but at the time when the actual build
is done (`m` phase).
Some build systems, such
as Make, allow modifications of the build graph during
the build. Other build systems, such
as Soong, have a clear separation into the
first
"generation phase" (this
is when build rules are created)
and the second
"build phase" (this
is when the build rules are executed),
and they do
not allow
modifications of the dependency graph during the second phase. The Soong
approach
is better
from performance standpoint, because
with the Make approach
there are no guarantees regarding the time of the build --- recursive build
graph modfications
continue until fixpoint. However the Soong approach
is also
more restictive,
as it can only generate build rules
from the information that
is passed to the build system via
global configuration, Android.bp files
or
encoded
in the Go code. Any other information (such
as the contents of the Java
manifest files) are
not accessible
and cannot be used to generate build rules.
Hence the need
for the
"indirect build rules": during the generation phase only
stubs of the build rules are generated,
and the real rules are generated by the
stub rules during the build phase (
and executed immediately). Note that the
build system still has to add all the necessary dependencies during the
generation phase, because it will
not be possible to change build order during
the build phase.
Indirect buils rules are used
in a couple of places
in dexpreopt:
- [soong/scripts/manifest_check.py]: first to extract targetSdkVersion
from the
manifest,
and later to extract `<uses-library/>` tags
from the manifest
and
compare them to the uses-library list known to the build system
- [soong/scripts/construct_context.py]: to trim compatibility libraries
in CLC
- [make/core/dex_preopt_config_merger.py]: to merge information
from
dexpreopt.config files
for uses-library dependencies into the dependent
's
dexpreopt.config file (mostly the CLC)
- autogenerated dexpreopt.sh scripts: to call dexpreopt_gen
### Consistency check - manifest_check.py
Because the information
from the manifests has to be duplicated
in the
Android.bp/Android.mk files, there
is a danger that it may get out of sync. To
guard against that, the build system generates a rule that verifies
uses-libraries: checks the metadata
in the build files against the contents of a
manifest. The manifest can be available
as a source file,
or as part of a
prebuilt APK.
The check
is implemented
in [soong/scripts/manifest_check.py].
It
is possible to turn off the check globally
for a product by setting
`PRODUCT_BROKEN_VERIFY_USES_LIBRARIES :=
true`
in a product makefile,
or for a
particular build by setting `RELAX_USES_LIBRARY_CHECK=
true`.
### Compatibility libraries - construct_context.py
Compatibility libraries are libraries that didn’t exist prior to a certain SDK
version (say, `N`), but classes
in them were
in the bootclasspath jars, etc.,
and in version `N` they have been separated into a standalone uses-library.
Compatibility libraries should only be
in the CLC of an app
if its
`targetSdkVersion`
in the manifest
is less than `N`.
Currently compatibility libraries only affect apps (but
not other libraries).
The build system cannot see `targetSdkVersion` of an app at the time it
generates dexpreopt build rules, so it doesn
't know whether to add compatibility
libaries to CLC
or not.
As a workaround, the build system includes all
compatibility libraries regardless of the app version,
and appends some extra
logic to the dexpreopt rule that will extract `targetSdkVersion`
from the
manifest
and filter CLC based on that version during Ninja stage of the build,
immediately before executing the dexpreopt command (see the
soong/scripts/construct_context.py script).
As of the time of writing (January
2022), there are the following compatibility
libraries:
- org.apache.http.legacy (SDK
28)
- android.hidl.base-V1.
0-java (SDK
29)
- android.hidl.manager-V1.
0-java (SDK
29)
- android.test.base (SDK
30)
- android.test.mock (SDK
30)
### Manifest fixer
Sometimes uses-library tags are missing
from the source manifest of a
library/app. This may happen
for example
if one of the transitive dependencies
of the library/app starts using another uses-library,
and the library/app
's
manifest isn
't updated to include it.
Soong can compute some of the missing uses-library tags
for a given library/app
automatically
as SDK libraries
in the transitive dependency closure of the
library/app. The closure
is needed because a library/app may depend on a static
library that may
in turn depend on an SDK library (possibly transitively via
another library).
Not all uses-library tags can be computed
in this way, because some of the
uses-library dependencies are
not SDK libraries,
or they are
not reachable via
transitive dependency closure. But when possible, allowing Soong to calculate
the manifest entries
is less prone to errors
and simplifies maintenance.
For
example, consider a situation when many apps use some static library that adds a
new uses-library dependency -- all the apps will have to be updated. That
is
difficult to maintain.
There
is also a manifest merger, because sometimes the final manifest of an app
is merged
from a few dependency manifests, so the final manifest installed on
devices contains a superset of uses-library tags of the source manifest of the
app.
[make/core/dex_preopt.mk]:
https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt.mk
[make/core/dex_preopt_config.mk]:
https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt_config.mk
[make/core/dex_preopt_config_merger.py]:
https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt_config_merger.py
[make/core/dex_preopt_odex_install.mk]:
https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt_odex_install.mk
[soong/dexpreopt]:
https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt
[soong/dexpreopt/class_loader_context.go]:
https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt/class_loader_context.go
[soong/dexpreopt/config.go]:
https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt/config.go
[soong/dexpreopt/dexpreopt.go]:
https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt/dexpreopt.go
[soong/java]:
https://cs.android.com/android/platform/superproject/+/main:build/soong/java
[soong/java/app.go:deps]:
https://cs.android.com/android/platform/superproject/+/main:build/soong/java/app.go?q=%22func%
20\(u%
20*usesLibrary\)%
20deps%
22
[soong/java/app.go:verifyUsesLibraries]:
https://cs.android.com/android/platform/superproject/+/main:build/soong/java/app.go?q=%22func%
20\(u%
20*usesLibrary\)%
20ve
rifyUsesLibraries%22
[soong/java/bootclasspath_fragment.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/bootclasspath_fragment.go
[soong/java/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/dexpreopt.go
[soong/java/dexpreopt_bootjars.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/dexpreopt_bootjars.go
[soong/java/dexpreopt_config.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/dexpreopt_config.go
[soong/java/java.go:addCLCFromDep]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/java.go?q=%22func%20addCLCfromDep%22
[soong/java/platform_bootclasspath.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/platform_bootclasspath.go
[soong/scripts/construct_context.py]: https://cs.android.com/android/platform/superproject/+/main:build/soong/scripts/construct_context.py
[soong/scripts/manifest_check.py]: https://cs.android.com/android/platform/superproject/+/main:build/soong/scripts/manifest_check.py