Rework class loader context implementation.

The old representation consisted of a list of libraries (UsesLibraries),
a list of optional libraries (OptionalUsesLibraries) and a mapping from
library name to its build/install paths (LibraryPaths). The separation
into lists and map was necessary because of special handling of
compatibility libraries, which is now unified with normal libraries.

The new representation is a mapping from target SDK version to a tree
structure ClassLoaderContext. Each node of the tree represents a library
and contains library name, build/install paths and a slice of
subcontexts for dependencies. The same library may occur in the tree
multiple times in case it is a dependency of multiple libraries. The
order in which libraries are added matters (the resulting tree shape may
be different).

Test results have to be updated, as the resulting <uses-library> list is
reodered (previously it was a sorted list of map keys, and now it is
formed by a depth-first preorder traversal of the class loader tree).

Test: lunch aosp_cf_x86_phone-userdebug && m
Bug: 132357300
Bug: 168686456
Change-Id: I11be8cd2967f004fd58753d7c5fb99fed179cd63
This commit is contained in:
Ulya Trafimovich 2020-11-03 15:15:46 +00:00
parent 366c7d3a74
commit 8cbc5d269b
14 changed files with 351 additions and 361 deletions

View file

@ -16,7 +16,7 @@ package dexpreopt
import ( import (
"fmt" "fmt"
"path/filepath" "strconv"
"strings" "strings"
"android/soong/android" "android/soong/android"
@ -49,39 +49,39 @@ var CompatUsesLibs = android.CopyOf(CompatUsesLibs29)
const UnknownInstallLibraryPath = "error" const UnknownInstallLibraryPath = "error"
const AnySdkVersion int = 9999 // should go last in class loader context // AnySdkVersion means that the class loader context is needed regardless of the targetSdkVersion
// of the app. The numeric value affects the key order in the map and, as a result, the order of
// arguments passed to construct_context.py (high value means that the unconditional context goes
// last). We use the converntional "current" SDK level (10000), but any big number would do as well.
const AnySdkVersion int = android.FutureApiLevelInt
// LibraryPath contains paths to the library DEX jar on host and on device. // ClassLoaderContext is a tree of libraries used by the dexpreopted module with their dependencies.
type LibraryPath struct { // The context is used by dex2oat to compile the module and recorded in the AOT-compiled files, so
Host android.Path // that it can be checked agains the run-time class loader context on device. If there is a mismatch
// at runtime, AOT-compiled code is rejected.
type ClassLoaderContext struct {
// The name of the library (same as the name of the module that contains it).
Name string
// On-host build path to the library dex file (used in dex2oat argument --class-loader-context).
Host android.Path
// On-device install path (used in dex2oat argument --stored-class-loader-context).
Device string Device string
// Nested class loader subcontexts for dependencies.
Subcontexts []*ClassLoaderContext
} }
// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar. // ClassLoaderContextMap is a map from SDK version to a class loader context.
type LibraryPaths map[string]*LibraryPath // There is a special entry with key AnySdkVersion that stores unconditional class loader context.
// Other entries store conditional contexts that should be added for some apps that have
// targetSdkVersion in the manifest lower than the key SDK version.
type ClassLoaderContextMap map[int][]*ClassLoaderContext
type classLoaderContext struct { // Add class loader context for the given library to the map entry for the given SDK version.
// Library names func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
Names []string hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
// The class loader context using paths in the build.
Host android.Paths
// The class loader context using paths as they will be on the device.
Target []string
}
// A map of class loader contexts for each SDK version.
// A map entry for "any" version contains libraries that are unconditionally added to class loader
// context. Map entries for existing versions contains libraries that were in the default classpath
// until that API version, and should be added to class loader context if and only if the
// targetSdkVersion in the manifest or APK is less than that API version.
type classLoaderContextMap map[int]*classLoaderContext
// Add a new library path to the map, unless a path for this library already exists.
// If necessary, check that the build and install paths exist.
func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext, lib string,
hostPath, installPath android.Path, strict bool) error {
// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is // If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
// not found. However, this is likely to result is disabling dexpreopt, as it won't be // not found. However, this is likely to result is disabling dexpreopt, as it won't be
@ -89,263 +89,254 @@ func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext
strict = strict && !ctx.Config().AllowMissingDependencies() strict = strict && !ctx.Config().AllowMissingDependencies()
if hostPath == nil && strict { if hostPath == nil && strict {
return fmt.Errorf("unknown build path to <uses-library> '%s'", lib) return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
} }
devicePath := UnknownInstallLibraryPath
if installPath == nil { if installPath == nil {
if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) { if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
// Assume that compatibility libraries are installed in /system/framework. // Assume that compatibility libraries are installed in /system/framework.
installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar") installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
} else if strict { } else if strict {
return fmt.Errorf("unknown install path to <uses-library> '%s'", lib) return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
}
}
// Add a library only if the build and install path to it is known.
if _, present := libPaths[lib]; !present {
var devicePath string
if installPath != nil {
devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
} else { } else {
// For some stub libraries the only known thing is the name of their implementation // For some stub libraries the only known thing is the name of their implementation
// library, but the library itself is unavailable (missing or part of a prebuilt). In // library, but the library itself is unavailable (missing or part of a prebuilt). In
// such cases we still need to add the library to <uses-library> tags in the manifest, // such cases we still need to add the library to <uses-library> tags in the manifest,
// but we cannot use if for dexpreopt. // but we cannot use it for dexpreopt.
devicePath = UnknownInstallLibraryPath
} }
libPaths[lib] = &LibraryPath{hostPath, devicePath}
} }
if installPath != nil {
devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
}
subcontexts := nestedClcMap[AnySdkVersion]
// If the library with this name is already present as one of the unconditional top-level
// components, do not re-add it.
for _, clc := range clcMap[sdkVer] {
if clc.Name == lib {
return nil
}
}
clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
Name: lib,
Host: hostPath,
Device: devicePath,
Subcontexts: subcontexts,
})
return nil return nil
} }
// Wrapper around addLibraryPath that does error reporting. // Wrapper around addContext that reports errors.
func (libPaths LibraryPaths) addLibraryPathOrReportError(ctx android.ModuleInstallPathContext, lib string, func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
hostPath, installPath android.Path, strict bool) { hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
err := libPaths.addLibraryPath(ctx, lib, hostPath, installPath, strict) err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
if err != nil { if err != nil {
ctx.ModuleErrorf(err.Error())
android.ReportPathErrorf(ctx, err.Error()) android.ReportPathErrorf(ctx, err.Error())
} }
} }
// Add a new library path to the map. Enforce checks that the library paths exist. // Add class loader context. Fail on unknown build/install paths.
func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleInstallPathContext, lib string, hostPath, installPath android.Path) { func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
libPaths.addLibraryPathOrReportError(ctx, lib, hostPath, installPath, true) hostPath, installPath android.Path) {
clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
} }
// Add a new library path to the map, if the library exists (name is not nil). // Add class loader context if the library exists. Don't fail on unknown build/install paths.
// Don't enforce checks that the library paths exist. Some libraries may be missing from the build, func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
// but their names still need to be added to <uses-library> tags in the manifest. hostPath, installPath android.Path) {
func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleInstallPathContext, lib *string, hostPath, installPath android.Path) {
if lib != nil { if lib != nil {
libPaths.addLibraryPathOrReportError(ctx, *lib, hostPath, installPath, false) clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
} }
} }
// Add library paths from the second map to the first map (do not override existing entries). // Add class loader context for the given SDK version. Fail on unknown build/install paths.
func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) { func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
for lib, path := range otherPaths { lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
if _, present := libPaths[lib]; !present {
libPaths[lib] = path clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
}
}
} }
func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext { // Merge the other class loader context map into this one, do not override existing entries.
if _, ok := m[sdkVer]; !ok { func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap) {
m[sdkVer] = &classLoaderContext{} for sdkVer, otherClcs := range otherClcMap {
} for _, otherClc := range otherClcs {
return m[sdkVer] alreadyHave := false
} for _, clc := range clcMap[sdkVer] {
if clc.Name == otherClc.Name {
func (clc *classLoaderContext) addLib(lib string, hostPath android.Path, targetPath string) { alreadyHave = true
clc.Names = append(clc.Names, lib) break
clc.Host = append(clc.Host, hostPath) }
clc.Target = append(clc.Target, targetPath) }
} if !alreadyHave {
clcMap[sdkVer] = append(clcMap[sdkVer], otherClc)
func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig,
libs ...string) (bool, error) {
clc := m.getValue(sdkVer)
for _, lib := range libs {
if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
clc.addLib(lib, p.Host, p.Device)
} else {
if sdkVer == AnySdkVersion {
// Fail the build if dexpreopt doesn't know paths to one of the <uses-library>
// dependencies. In the future we may need to relax this and just disable dexpreopt.
return false, fmt.Errorf("dexpreopt cannot find path for <uses-library> '%s'", lib)
} else {
// No error for compatibility libraries, as Soong doesn't know if they are needed
// (this depends on the targetSdkVersion in the manifest).
return false, nil
} }
} }
} }
return true, nil
} }
func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) { // List of libraries in the unconditional class loader context, excluding dependencies of shared libraries.
clc := m.getValue(sdkVer) func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
for _, lib := range libs { if clcMap != nil {
clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar")) // compatibility libraries (those in conditional context) are not added to <uses-library> tags
ulibs = usesLibsRec(clcMap[AnySdkVersion])
ulibs = android.FirstUniqueStrings(ulibs)
} }
return ulibs
} }
func (m classLoaderContextMap) usesLibs() []string { func usesLibsRec(clcs []*ClassLoaderContext) (ulibs []string) {
if clc, ok := m[AnySdkVersion]; ok { for _, clc := range clcs {
return clc.Names ulibs = append(ulibs, clc.Name)
ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...)
} }
return nil return ulibs
}
// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat
// command for the dexpreopted module. There are three possible cases:
//
// 1. System server jars. They have a special class loader context that includes other system
// server jars.
//
// 2. Library jars or APKs which have precise list of their <uses-library> libs. Their class loader
// context includes build and on-device paths to these libs. In some cases it may happen that
// the path to a <uses-library> is unknown (e.g. the dexpreopted module may depend on stubs
// library, whose implementation library is missing from the build altogether). In such case
// dexpreopting with the <uses-library> is impossible, and dexpreopting without it is pointless,
// as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
// such cases the function returns nil, which disables dexpreopt.
//
// 3. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
// the unsafe &-classpath workaround that means empty class loader context and absence of runtime
// check that the class loader context provided by the PackageManager agrees with the stored
// class loader context recorded in the .odex file.
//
func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) (*classLoaderContextMap, error) {
classLoaderContexts := make(classLoaderContextMap)
systemServerJars := NonUpdatableSystemServerJars(ctx, global)
if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
// System server jars should be dexpreopted together: class loader context of each jar
// should include all preceding jars on the system server classpath.
classLoaderContexts.addSystemServerLibs(AnySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
} else if module.EnforceUsesLibraries {
// Unconditional class loader context.
usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
if ok, err := classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...); !ok {
return nil, err
}
// Conditional class loader context for API version < 28.
const httpLegacy = "org.apache.http.legacy"
if ok, err := classLoaderContexts.addLibs(ctx, 28, module, httpLegacy); !ok {
return nil, err
}
// Conditional class loader context for API version < 29.
usesLibs29 := []string{
"android.hidl.base-V1.0-java",
"android.hidl.manager-V1.0-java",
}
if ok, err := classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...); !ok {
return nil, err
}
// Conditional class loader context for API version < 30.
if ok, err := classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...); !ok {
return nil, err
}
} else {
// Pass special class loader context to skip the classpath and collision check.
// This will get removed once LOCAL_USES_LIBRARIES is enforced.
// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
// to the &.
}
fixConditionalClassLoaderContext(classLoaderContexts)
return &classLoaderContexts, nil
} }
// Now that the full unconditional context is known, reconstruct conditional context. // Now that the full unconditional context is known, reconstruct conditional context.
// Apply filters for individual libraries, mirroring what the PackageManager does when it // Apply filters for individual libraries, mirroring what the PackageManager does when it
// constructs class loader context on device. // constructs class loader context on device.
// //
// TODO(b/132357300): // TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
// - remove android.hidl.manager and android.hidl.base unless the app is a system app.
// //
func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) { func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
usesLibs := clcMap.usesLibs() usesLibs := clcMap.UsesLibs()
for sdkVer, clc := range clcMap { for sdkVer, clcs := range clcMap {
if sdkVer == AnySdkVersion { if sdkVer == AnySdkVersion {
continue continue
} }
clcMap[sdkVer] = &classLoaderContext{} fixedClcs := []*ClassLoaderContext{}
for i, lib := range clc.Names { for _, clc := range clcs {
if android.InList(lib, usesLibs) { if android.InList(clc.Name, usesLibs) {
// skip compatibility libraries that are already included in unconditional context // skip compatibility libraries that are already included in unconditional context
} else if lib == AndroidTestMock && !android.InList("android.test.runner", usesLibs) { } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
// android.test.mock is only needed as a compatibility library (in conditional class // android.test.mock is only needed as a compatibility library (in conditional class
// loader context) if android.test.runner is used, otherwise skip it // loader context) if android.test.runner is used, otherwise skip it
} else { } else {
clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i]) fixedClcs = append(fixedClcs, clc)
} }
clcMap[sdkVer] = fixedClcs
} }
} }
} }
// Return the class loader context as a string and a slice of build paths for all dependencies. // Return true if all build/install library paths are valid (including recursive subcontexts),
func computeClassLoaderContext(ctx android.PathContext, clcMap classLoaderContextMap) (clcStr string, paths android.Paths) { // otherwise return false. A build path is valid if it's not nil. An install path is valid if it's
for _, ver := range android.SortedIntKeys(clcMap) { // not equal to a special "error" value.
clc := clcMap.getValue(ver) func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) {
for sdkVer, clcs := range clcMap {
clcLen := len(clc.Names) if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil {
if clcLen != len(clc.Host) || clcLen != len(clc.Target) { return valid, err
android.ReportPathErrorf(ctx, "ill-formed class loader context")
} }
}
return true, nil
}
var hostClc, targetClc []string func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) {
var hostPaths android.Paths for _, clc := range clcs {
if clc.Host == nil || clc.Device == UnknownInstallLibraryPath {
for i := 0; i < clcLen; i++ { if sdkVer == AnySdkVersion {
hostStr := "PCL[" + clc.Host[i].String() + "]" // Return error if dexpreopt doesn't know paths to one of the <uses-library>
targetStr := "PCL[" + clc.Target[i] + "]" // dependencies. In the future we may need to relax this and just disable dexpreopt.
return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name)
hostClc = append(hostClc, hostStr) } else {
targetClc = append(targetClc, targetStr) // No error for compatibility libraries, as Soong doesn't know if they are needed
hostPaths = append(hostPaths, clc.Host[i]) // (this depends on the targetSdkVersion in the manifest), but the CLC is invalid.
return false, nil
}
} }
if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil {
return valid, err
}
}
return true, nil
}
// Return the class loader context as a string, and a slice of build paths for all dependencies.
// Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
// Return the resulting string and a slice of on-host build paths to all library dependencies.
func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order
sdkVerStr := fmt.Sprintf("%d", sdkVer)
if sdkVer == AnySdkVersion {
sdkVerStr = "any" // a special keyword that means any SDK version
}
hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
if hostPaths != nil { if hostPaths != nil {
sdkVerStr := fmt.Sprintf("%d", ver) clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
if ver == AnySdkVersion { clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
sdkVerStr = "any" // a special keyword that means any SDK version
}
clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, strings.Join(hostClc, "#"))
clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, strings.Join(targetClc, "#"))
paths = append(paths, hostPaths...)
} }
paths = append(paths, hostPaths...)
} }
return clcStr, android.FirstUniquePaths(paths)
return clcStr, paths
} }
func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
var paths android.Paths
var clcsHost, clcsTarget []string
for _, clc := range clcs {
subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
if subPaths != nil {
subClcHost = "{" + subClcHost + "}"
subClcTarget = "{" + subClcTarget + "}"
}
clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
paths = append(paths, clc.Host)
paths = append(paths, subPaths...)
}
clcHost := strings.Join(clcsHost, "#")
clcTarget := strings.Join(clcsTarget, "#")
return clcHost, clcTarget, paths
}
// Paths to a <uses-library> on host and on device.
type jsonLibraryPath struct { type jsonLibraryPath struct {
Host string Host string
Device string Device string
} }
type jsonLibraryPaths map[string]jsonLibraryPath // Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
// Make implementation.
type jsonClassLoaderContext map[string]jsonLibraryPath
// convert JSON map of library paths to LibraryPaths // A map from SDK version (represented with a JSON string) to JSON class loader context.
func constructLibraryPaths(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths { type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
m := LibraryPaths{}
for lib, path := range paths { // Convert JSON class loader context map to ClassLoaderContextMap.
m[lib] = &LibraryPath{ func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
constructPath(ctx, path.Host), clcMap := make(ClassLoaderContextMap)
path.Device, for sdkVerStr, clc := range jClcMap {
sdkVer, ok := strconv.Atoi(sdkVerStr)
if ok != nil {
if sdkVerStr == "any" {
sdkVer = AnySdkVersion
} else {
android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
}
}
for lib, path := range clc {
clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
Name: lib,
Host: constructPath(ctx, path.Host),
Device: path.Device,
Subcontexts: nil,
})
} }
} }
return m return clcMap
} }

View file

@ -37,91 +37,74 @@ func TestCLC(t *testing.T) {
// ├── b // ├── b
// ├── c // ├── c
// ├── d // ├── d
// ├── a2 // │   ├── a2
// ├── b2 // │   ├── b2
// ── c2 // │   └── c2
// ├── a1 // │   ├── a1
// ── b1 // │   └── b1
// ├── f // ├── f
// ├── a3 // ├── a3
// └── b3 // └── b3
// //
ctx := testContext() ctx := testContext()
lp := make(LibraryPaths) m := make(ClassLoaderContextMap)
lp.AddLibraryPath(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a")) m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
lp.AddLibraryPath(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b")) m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
// "Maybe" variant in the good case: add as usual. // "Maybe" variant in the good case: add as usual.
c := "c" c := "c"
lp.MaybeAddLibraryPath(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c")) m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
// "Maybe" variant in the bad case: don't add library with unknown name, keep going. // "Maybe" variant in the bad case: don't add library with unknown name, keep going.
lp.MaybeAddLibraryPath(ctx, nil, nil, nil) m.MaybeAddContext(ctx, nil, nil, nil)
// Add some libraries with nested subcontexts. // Add some libraries with nested subcontexts.
lp1 := make(LibraryPaths) m1 := make(ClassLoaderContextMap)
lp1.AddLibraryPath(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1")) m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
lp1.AddLibraryPath(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1")) m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
lp2 := make(LibraryPaths) m2 := make(ClassLoaderContextMap)
lp2.AddLibraryPath(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2")) m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
lp2.AddLibraryPath(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2")) m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
lp2.AddLibraryPath(ctx, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2")) m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
lp2.AddLibraryPaths(lp1)
lp.AddLibraryPath(ctx, "d", buildPath(ctx, "d"), installPath(ctx, "d")) m3 := make(ClassLoaderContextMap)
lp.AddLibraryPaths(lp2) m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
lp3 := make(LibraryPaths) m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
lp3.AddLibraryPath(ctx, "f", buildPath(ctx, "f"), installPath(ctx, "f")) // When the same library is both in conditional and unconditional context, it should be removed
lp3.AddLibraryPath(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3")) // from conditional context.
lp3.AddLibraryPath(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3")) m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
lp.AddLibraryPaths(lp3) m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
m.AddContextMap(m3)
// Compatibility libraries with unknown install paths get default paths. // Compatibility libraries with unknown install paths get default paths.
lp.AddLibraryPath(ctx, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil) m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
lp.AddLibraryPath(ctx, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil) m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
lp.AddLibraryPath(ctx, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil)
module := testSystemModuleConfig(ctx, "test")
module.LibraryPaths = lp
m := make(classLoaderContextMap)
valid := true
ok, err := m.addLibs(ctx, AnySdkVersion, module, "a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3")
valid = valid && ok && err == nil
// Add compatibility libraries to conditional CLC for SDK level 29.
ok, err = m.addLibs(ctx, 29, module, AndroidHidlManager, AndroidHidlBase)
valid = valid && ok && err == nil
// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only // Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
// needed as a compatibility library if "android.test.runner" is in CLC as well. // needed as a compatibility library if "android.test.runner" is in CLC as well.
ok, err = m.addLibs(ctx, 30, module, AndroidTestMock) m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
valid = valid && ok && err == nil
// When the same library is both in conditional and unconditional context, it should be removed valid, validationError := validateClassLoaderContext(m)
// from conditional context.
ok, err = m.addLibs(ctx, 42, module, "f")
valid = valid && ok && err == nil
fixConditionalClassLoaderContext(m) fixClassLoaderContext(m)
var haveStr string var haveStr string
var havePaths android.Paths var havePaths android.Paths
var haveUsesLibs []string var haveUsesLibs []string
if valid { if valid && validationError == nil {
haveStr, havePaths = computeClassLoaderContext(ctx, m) haveStr, havePaths = ComputeClassLoaderContext(m)
haveUsesLibs = m.usesLibs() haveUsesLibs = m.UsesLibs()
} }
// Test that validation is successful (all paths are known). // Test that validation is successful (all paths are known).
t.Run("validate", func(t *testing.T) { t.Run("validate", func(t *testing.T) {
if !valid { if !(valid && validationError == nil) {
t.Errorf("invalid class loader context") t.Errorf("invalid class loader context")
} }
}) })
@ -135,14 +118,14 @@ func TestCLC(t *testing.T) {
"PCL[/system/framework/" + AndroidHidlManager + ".jar]#" + "PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
"PCL[/system/framework/" + AndroidHidlBase + ".jar]" + "PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
" --host-context-for-sdk any " + " --host-context-for-sdk any " +
"PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]#" + "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]" +
"PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]#" + "{PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]" +
"PCL[out/a1.jar]#PCL[out/b1.jar]#" + "{PCL[out/a1.jar]#PCL[out/b1.jar]}}#" +
"PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" + "PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" +
" --target-context-for-sdk any " + " --target-context-for-sdk any " +
"PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]#" + "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" +
"PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]#" + "{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" +
"PCL[/system/a1.jar]#PCL[/system/b1.jar]#" + "{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" +
"PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]" "PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
if wantStr != haveStr { if wantStr != haveStr {
t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr) t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
@ -175,32 +158,40 @@ func TestCLC(t *testing.T) {
// Test that an unexpected unknown build path causes immediate error. // Test that an unexpected unknown build path causes immediate error.
func TestCLCUnknownBuildPath(t *testing.T) { func TestCLCUnknownBuildPath(t *testing.T) {
ctx := testContext() ctx := testContext()
lp := make(LibraryPaths) m := make(ClassLoaderContextMap)
err := lp.addLibraryPath(ctx, "a", nil, nil, true) err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
checkError(t, err, "unknown build path to <uses-library> 'a'") checkError(t, err, "unknown build path to <uses-library> \"a\"")
} }
// Test that an unexpected unknown install path causes immediate error. // Test that an unexpected unknown install path causes immediate error.
func TestCLCUnknownInstallPath(t *testing.T) { func TestCLCUnknownInstallPath(t *testing.T) {
ctx := testContext() ctx := testContext()
lp := make(LibraryPaths) m := make(ClassLoaderContextMap)
err := lp.addLibraryPath(ctx, "a", buildPath(ctx, "a"), nil, true) err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
checkError(t, err, "unknown install path to <uses-library> 'a'") checkError(t, err, "unknown install path to <uses-library> \"a\"")
} }
func TestCLCMaybeAdd(t *testing.T) { func TestCLCMaybeAdd(t *testing.T) {
ctx := testContext() ctx := testContext()
lp := make(LibraryPaths) m := make(ClassLoaderContextMap)
a := "a" a := "a"
lp.MaybeAddLibraryPath(ctx, &a, nil, nil) m.MaybeAddContext(ctx, &a, nil, nil)
module := testSystemModuleConfig(ctx, "test") // The library should be added to <uses-library> tags by the manifest_fixer.
module.LibraryPaths = lp t.Run("maybe add", func(t *testing.T) {
haveUsesLibs := m.UsesLibs()
wantUsesLibs := []string{"a"}
if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
}
})
m := make(classLoaderContextMap) // But class loader context in such cases should raise an error on validation.
_, err := m.addLibs(ctx, AnySdkVersion, module, "a") t.Run("validate", func(t *testing.T) {
checkError(t, err, "dexpreopt cannot find path for <uses-library> 'a'") _, err := validateClassLoaderContext(m)
checkError(t, err, "invalid path for <uses-library> \"a\"")
})
} }
func checkError(t *testing.T, have error, want string) { func checkError(t *testing.T, have error, want string) {

View file

@ -114,10 +114,8 @@ type ModuleConfig struct {
ProfileIsTextListing bool ProfileIsTextListing bool
ProfileBootListing android.OptionalPath ProfileBootListing android.OptionalPath
EnforceUsesLibraries bool EnforceUsesLibraries bool
OptionalUsesLibraries []string ClassLoaderContexts ClassLoaderContextMap
UsesLibraries []string
LibraryPaths LibraryPaths
Archs []android.ArchType Archs []android.ArchType
DexPreoptImages []android.Path DexPreoptImages []android.Path
@ -265,7 +263,7 @@ func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, err
DexPath string DexPath string
ManifestPath string ManifestPath string
ProfileClassListing string ProfileClassListing string
LibraryPaths jsonLibraryPaths ClassLoaderContexts jsonClassLoaderContextMap
DexPreoptImages []string DexPreoptImages []string
DexPreoptImageLocations []string DexPreoptImageLocations []string
PreoptBootClassPathDexFiles []string PreoptBootClassPathDexFiles []string
@ -283,7 +281,7 @@ func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, err
config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath) config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath) config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing)) config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
config.ModuleConfig.LibraryPaths = constructLibraryPaths(ctx, config.LibraryPaths) config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts)
config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages) config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles) config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)

View file

@ -81,16 +81,18 @@ func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConf
} }
if !dexpreoptDisabled(ctx, global, module) { if !dexpreoptDisabled(ctx, global, module) {
if clc, err := genClassLoaderContext(ctx, global, module); err != nil { if valid, err := validateClassLoaderContext(module.ClassLoaderContexts); err != nil {
android.ReportPathErrorf(ctx, err.Error()) android.ReportPathErrorf(ctx, err.Error())
} else if clc != nil { } else if valid {
fixClassLoaderContext(module.ClassLoaderContexts)
appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) && appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
!module.NoCreateAppImage !module.NoCreateAppImage
generateDM := shouldGenerateDM(module, global) generateDM := shouldGenerateDM(module, global)
for archIdx, _ := range module.Archs { for archIdx, _ := range module.Archs {
dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, *clc, profile, appImage, generateDM) dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
} }
} }
} }
@ -197,8 +199,8 @@ func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig,
} }
func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig, func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap, module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
profile android.WritablePath, appImage bool, generateDM bool) { appImage bool, generateDM bool) {
arch := module.Archs[archIdx] arch := module.Archs[archIdx]
@ -235,6 +237,16 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g
rule.Command().FlagWithOutput("rm -f ", odexPath) rule.Command().FlagWithOutput("rm -f ", odexPath)
if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 { if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
// System server jars should be dexpreopted together: class loader context of each jar
// should include all preceding jars on the system server classpath.
var clcHost android.Paths
var clcTarget []string
for _, lib := range systemServerJars[:jarIndex] {
clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
clcTarget = append(clcTarget, filepath.Join("/system/framework", lib+".jar"))
}
// Copy the system server jar to a predefined location where dex2oat will find it. // Copy the system server jar to a predefined location where dex2oat will find it.
dexPathHost := SystemServerDexJarHostPath(ctx, module.Name) dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String())) rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
@ -242,11 +254,11 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g
checkSystemServerOrder(ctx, jarIndex) checkSystemServerOrder(ctx, jarIndex)
clc := classLoaderContexts[AnySdkVersion]
rule.Command(). rule.Command().
Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]"). Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clcHost.Strings(), ":") + "]").
Implicits(clc.Host). Implicits(clcHost).
Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]") Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
} else if module.EnforceUsesLibraries { } else if module.EnforceUsesLibraries {
// Generate command that saves target SDK version in a shell variable. // Generate command that saves target SDK version in a shell variable.
if module.ManifestPath != nil { if module.ManifestPath != nil {
@ -266,13 +278,15 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g
} }
// Generate command that saves host and target class loader context in shell variables. // Generate command that saves host and target class loader context in shell variables.
clc, paths := computeClassLoaderContext(ctx, classLoaderContexts) clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
cmd := rule.Command(). cmd := rule.Command().
Text(`eval "$(`).Tool(globalSoong.ConstructContext). Text(`eval "$(`).Tool(globalSoong.ConstructContext).
Text(` --target-sdk-version ${target_sdk_version}`). Text(` --target-sdk-version ${target_sdk_version}`).
Text(clc).Implicits(paths) Text(clc).Implicits(paths)
cmd.Text(`)"`) cmd.Text(`)"`)
} else { } else {
// Other libraries or APKs for which the exact <uses-library> list is unknown.
// Pass special class loader context to skip the classpath and collision check. // Pass special class loader context to skip the classpath and collision check.
// This will get removed once LOCAL_USES_LIBRARIES is enforced. // This will get removed once LOCAL_USES_LIBRARIES is enforced.
// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default // Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default

View file

@ -44,9 +44,7 @@ func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleCo
ProfileClassListing: android.OptionalPath{}, ProfileClassListing: android.OptionalPath{},
ProfileIsTextListing: false, ProfileIsTextListing: false,
EnforceUsesLibraries: false, EnforceUsesLibraries: false,
OptionalUsesLibraries: nil, ClassLoaderContexts: nil,
UsesLibraries: nil,
LibraryPaths: nil,
Archs: []android.ArchType{android.Arm}, Archs: []android.ArchType{android.Arm},
DexPreoptImages: android.Paths{android.PathForTesting("system/framework/arm/boot.art")}, DexPreoptImages: android.Paths{android.PathForTesting("system/framework/arm/boot.art")},
DexPreoptImagesDeps: []android.OutputPaths{android.OutputPaths{}}, DexPreoptImagesDeps: []android.OutputPaths{android.OutputPaths{}},

View file

@ -109,7 +109,7 @@ type aapt struct {
useEmbeddedNativeLibs bool useEmbeddedNativeLibs bool
useEmbeddedDex bool useEmbeddedDex bool
usesNonSdkApis bool usesNonSdkApis bool
sdkLibraries dexpreopt.LibraryPaths sdkLibraries dexpreopt.ClassLoaderContextMap
hasNoCode bool hasNoCode bool
LoggingParent string LoggingParent string
resourceFiles android.Paths resourceFiles android.Paths
@ -392,7 +392,7 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, ex
// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, transitiveStaticLibManifests android.Paths, func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, transitiveStaticLibManifests android.Paths,
staticRRODirs []rroDir, assets, deps android.Paths, flags []string, sdkLibraries dexpreopt.LibraryPaths) { staticRRODirs []rroDir, assets, deps android.Paths, flags []string, sdkLibraries dexpreopt.ClassLoaderContextMap) {
var sharedLibs android.Paths var sharedLibs android.Paths
@ -401,7 +401,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati
sharedLibs = append(sharedLibs, sdkDep.jars...) sharedLibs = append(sharedLibs, sdkDep.jars...)
} }
sdkLibraries = make(dexpreopt.LibraryPaths) sdkLibraries = make(dexpreopt.ClassLoaderContextMap)
ctx.VisitDirectDeps(func(module android.Module) { ctx.VisitDirectDeps(func(module android.Module) {
var exportPackage android.Path var exportPackage android.Path
@ -411,7 +411,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati
} }
if dep, ok := module.(Dependency); ok { if dep, ok := module.(Dependency); ok {
sdkLibraries.AddLibraryPaths(dep.ExportedSdkLibs()) sdkLibraries.AddContextMap(dep.ExportedSdkLibs())
} }
switch ctx.OtherModuleDependencyTag(module) { switch ctx.OtherModuleDependencyTag(module) {
@ -426,7 +426,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati
// (including the java_sdk_library) itself then append any implicit sdk library // (including the java_sdk_library) itself then append any implicit sdk library
// names to the list of sdk libraries to be added to the manifest. // names to the list of sdk libraries to be added to the manifest.
if component, ok := module.(SdkLibraryComponentDependency); ok { if component, ok := module.(SdkLibraryComponentDependency); ok {
sdkLibraries.MaybeAddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(), sdkLibraries.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(),
component.DexJarBuildPath(), component.DexJarInstallPath()) component.DexJarBuildPath(), component.DexJarInstallPath())
} }
@ -439,7 +439,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati
transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...) transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
transitiveStaticLibs = append(transitiveStaticLibs, exportPackage) transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...) transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...)
sdkLibraries.AddLibraryPaths(aarDep.ExportedSdkLibs()) sdkLibraries.AddContextMap(aarDep.ExportedSdkLibs())
if aarDep.ExportedAssets().Valid() { if aarDep.ExportedAssets().Valid() {
assets = append(assets, aarDep.ExportedAssets().Path()) assets = append(assets, aarDep.ExportedAssets().Path())
} }
@ -827,7 +827,7 @@ func (a *AARImport) AidlIncludeDirs() android.Paths {
return nil return nil
} }
func (a *AARImport) ExportedSdkLibs() dexpreopt.LibraryPaths { func (a *AARImport) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
return nil return nil
} }

View file

@ -43,8 +43,9 @@ var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
"args", "libs") "args", "libs")
// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries dexpreopt.LibraryPaths, func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { sdkLibraries dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
var args []string var args []string
if isLibrary { if isLibrary {
@ -70,7 +71,7 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext
args = append(args, "--use-embedded-dex") args = append(args, "--use-embedded-dex")
} }
for _, usesLib := range android.SortedStringKeys(sdkLibraries) { for _, usesLib := range sdkLibraries.UsesLibs() {
if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) { if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) {
args = append(args, "--optional-uses-library", usesLib) args = append(args, "--optional-uses-library", usesLib)
} else { } else {

View file

@ -115,7 +115,7 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries {
entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile) entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
} }
entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", android.SortedStringKeys(library.exportedSdkLibs)...) entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.exportedSdkLibs.UsesLibs()...)
if len(library.additionalCheckedModules) != 0 { if len(library.additionalCheckedModules) != 0 {
entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...) entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)

View file

@ -601,7 +601,7 @@ func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath
return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk") return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
} }
func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.LibraryPaths) android.Path { func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.ClassLoaderContextMap) android.Path {
a.dexpreopter.installPath = a.installPath(ctx) a.dexpreopter.installPath = a.installPath(ctx)
if a.dexProperties.Uncompress_dex == nil { if a.dexProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module. // If the value was not force-set by the user, use reasonable default based on the module.
@ -609,12 +609,10 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreop
} }
a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx) a.dexpreopter.classLoaderContexts.AddContextMap(sdkLibs)
a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
a.dexpreopter.libraryPaths.AddLibraryPaths(sdkLibs)
a.dexpreopter.manifestFile = a.mergedManifestFile a.dexpreopter.manifestFile = a.mergedManifestFile
a.exportedSdkLibs = make(dexpreopt.LibraryPaths) a.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap)
if ctx.ModuleName() != "framework-res" { if ctx.ModuleName() != "framework-res" {
a.Module.compile(ctx, a.aaptSrcJar) a.Module.compile(ctx, a.aaptSrcJar)
@ -791,7 +789,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
a.usesLibrary.freezeEnforceUsesLibraries() a.usesLibrary.freezeEnforceUsesLibraries()
// Add implicit SDK libraries to <uses-library> list. // Add implicit SDK libraries to <uses-library> list.
for _, usesLib := range android.SortedStringKeys(a.aapt.sdkLibraries) { for _, usesLib := range a.aapt.sdkLibraries.UsesLibs() {
a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs)) a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs))
} }
@ -1540,9 +1538,7 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext
a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed) dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
if a.dexpreopter.uncompressedDex { if a.dexpreopter.uncompressedDex {
@ -1976,17 +1972,18 @@ func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []s
return optionalUsesLibs return optionalUsesLibs
} }
// usesLibraryPaths returns a map of module names of shared library dependencies to the paths // Returns a map of module names of shared library dependencies to the paths
// to their dex jars on host and on device. // to their dex jars on host and on device.
func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) dexpreopt.LibraryPaths { func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
usesLibPaths := make(dexpreopt.LibraryPaths) clcMap := make(dexpreopt.ClassLoaderContextMap)
if !ctx.Config().UnbundledBuild() { if !ctx.Config().UnbundledBuild() {
ctx.VisitDirectDeps(func(m android.Module) { ctx.VisitDirectDeps(func(m android.Module) {
if _, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok { if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
dep := ctx.OtherModuleName(m) dep := ctx.OtherModuleName(m)
if lib, ok := m.(Dependency); ok { if lib, ok := m.(Dependency); ok {
usesLibPaths.AddLibraryPath(ctx, dep, lib.DexJarBuildPath(), lib.DexJarInstallPath()) clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ExportedSdkLibs())
} else if ctx.Config().AllowMissingDependencies() { } else if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{dep}) ctx.AddMissingDependencies([]string{dep})
} else { } else {
@ -1996,7 +1993,7 @@ func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) dexpreopt.Libr
}) })
} }
return usesLibPaths return clcMap
} }
// enforceUsesLibraries returns true of <uses-library> tags should be checked against uses_libs and optional_uses_libs // enforceUsesLibraries returns true of <uses-library> tags should be checked against uses_libs and optional_uses_libs

View file

@ -2815,11 +2815,11 @@ func TestUsesLibraries(t *testing.T) {
// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs // Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs
cmd = app.Rule("dexpreopt").RuleParams.Command cmd = app.Rule("dexpreopt").RuleParams.Command
w := `--target-context-for-sdk any ` + w := `--target-context-for-sdk any ` +
`PCL[/system/framework/foo.jar]#` +
`PCL[/system/framework/quuz.jar]#` +
`PCL[/system/framework/qux.jar]#` + `PCL[/system/framework/qux.jar]#` +
`PCL[/system/framework/runtime-library.jar]#` + `PCL[/system/framework/quuz.jar]#` +
`PCL[/system/framework/bar.jar]` `PCL[/system/framework/foo.jar]#` +
`PCL[/system/framework/bar.jar]#` +
`PCL[/system/framework/runtime-library.jar]`
if !strings.Contains(cmd, w) { if !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd) t.Errorf("wanted %q in %q", w, cmd)
} }

View file

@ -163,7 +163,7 @@ func (d *DeviceHostConverter) AidlIncludeDirs() android.Paths {
return nil return nil
} }
func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.LibraryPaths { func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
return nil return nil
} }

View file

@ -33,11 +33,9 @@ type dexpreopter struct {
isTest bool isTest bool
isPresignedPrebuilt bool isPresignedPrebuilt bool
manifestFile android.Path manifestFile android.Path
usesLibs []string enforceUsesLibs bool
optionalUsesLibs []string classLoaderContexts dexpreopt.ClassLoaderContextMap
enforceUsesLibs bool
libraryPaths dexpreopt.LibraryPaths
builtInstalled string builtInstalled string
} }
@ -193,10 +191,8 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo
ProfileIsTextListing: profileIsTextListing, ProfileIsTextListing: profileIsTextListing,
ProfileBootListing: profileBootListing, ProfileBootListing: profileBootListing,
EnforceUsesLibraries: d.enforceUsesLibs, EnforceUsesLibraries: d.enforceUsesLibs,
OptionalUsesLibraries: d.optionalUsesLibs, ClassLoaderContexts: d.classLoaderContexts,
UsesLibraries: d.usesLibs,
LibraryPaths: d.libraryPaths,
Archs: archs, Archs: archs,
DexPreoptImages: images, DexPreoptImages: images,

View file

@ -416,8 +416,8 @@ type Module struct {
// manifest file to use instead of properties.Manifest // manifest file to use instead of properties.Manifest
overrideManifest android.OptionalPath overrideManifest android.OptionalPath
// map of SDK libs exported by this java module to their build and install paths // map of SDK version to class loader context
exportedSdkLibs dexpreopt.LibraryPaths exportedSdkLibs dexpreopt.ClassLoaderContextMap
// list of plugins that this java module is exporting // list of plugins that this java module is exporting
exportedPluginJars android.Paths exportedPluginJars android.Paths
@ -509,7 +509,7 @@ type Dependency interface {
ImplementationJars() android.Paths ImplementationJars() android.Paths
ResourceJars() android.Paths ResourceJars() android.Paths
AidlIncludeDirs() android.Paths AidlIncludeDirs() android.Paths
ExportedSdkLibs() dexpreopt.LibraryPaths ExportedSdkLibs() dexpreopt.ClassLoaderContextMap
ExportedPlugins() (android.Paths, []string) ExportedPlugins() (android.Paths, []string)
SrcJarArgs() ([]string, android.Paths) SrcJarArgs() ([]string, android.Paths)
BaseModuleName() string BaseModuleName() string
@ -1027,7 +1027,8 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps {
case libTag: case libTag:
deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
// names of sdk libs that are directly depended are exported // names of sdk libs that are directly depended are exported
j.exportedSdkLibs.MaybeAddLibraryPath(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath()) j.exportedSdkLibs.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(),
dep.DexJarBuildPath(), dep.DexJarInstallPath())
case staticLibTag: case staticLibTag:
ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
} }
@ -1038,7 +1039,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps {
case libTag, instrumentationForTag: case libTag, instrumentationForTag:
deps.classpath = append(deps.classpath, dep.HeaderJars()...) deps.classpath = append(deps.classpath, dep.HeaderJars()...)
// sdk lib names from dependencies are re-exported // sdk lib names from dependencies are re-exported
j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs()) j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs())
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
pluginJars, pluginClasses := dep.ExportedPlugins() pluginJars, pluginClasses := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...) addPlugins(&deps, pluginJars, pluginClasses...)
@ -1050,7 +1051,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps {
deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...) deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...) deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...)
// sdk lib names from dependencies are re-exported // sdk lib names from dependencies are re-exported
j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs()) j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs())
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
pluginJars, pluginClasses := dep.ExportedPlugins() pluginJars, pluginClasses := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...) addPlugins(&deps, pluginJars, pluginClasses...)
@ -1902,7 +1903,7 @@ func (j *Module) AidlIncludeDirs() android.Paths {
return j.exportAidlIncludeDirs return j.exportAidlIncludeDirs
} }
func (j *Module) ExportedSdkLibs() dexpreopt.LibraryPaths { func (j *Module) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
return j.exportedSdkLibs return j.exportedSdkLibs
} }
@ -2041,7 +2042,7 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
} }
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
j.exportedSdkLibs = make(dexpreopt.LibraryPaths) j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap)
j.compile(ctx, nil) j.compile(ctx, nil)
// Collect the module directory for IDE info in java/jdeps.go. // Collect the module directory for IDE info in java/jdeps.go.
@ -2061,11 +2062,12 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// add the name of that java_sdk_library to the exported sdk libs to make sure // add the name of that java_sdk_library to the exported sdk libs to make sure
// that, if necessary, a <uses-library> element for that java_sdk_library is // that, if necessary, a <uses-library> element for that java_sdk_library is
// added to the Android manifest. // added to the Android manifest.
j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath()) j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
j.DexJarBuildPath(), j.DexJarInstallPath())
// A non-SDK library may provide a <uses-library> (the name may be different from the module name). // A non-SDK library may provide a <uses-library> (the name may be different from the module name).
if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" { if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" {
j.exportedSdkLibs.AddLibraryPath(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath()) j.exportedSdkLibs.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath())
} }
j.distFiles = j.GenerateTaggedDistFiles(ctx) j.distFiles = j.GenerateTaggedDistFiles(ctx)
@ -2644,7 +2646,7 @@ type Import struct {
dexJarFile android.Path dexJarFile android.Path
combinedClasspathFile android.Path combinedClasspathFile android.Path
exportedSdkLibs dexpreopt.LibraryPaths exportedSdkLibs dexpreopt.ClassLoaderContextMap
exportAidlIncludeDirs android.Paths exportAidlIncludeDirs android.Paths
hideApexVariantFromMake bool hideApexVariantFromMake bool
@ -2719,7 +2721,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
TransformJetifier(ctx, outputFile, inputFile) TransformJetifier(ctx, outputFile, inputFile)
} }
j.combinedClasspathFile = outputFile j.combinedClasspathFile = outputFile
j.exportedSdkLibs = make(dexpreopt.LibraryPaths) j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap)
var flags javaBuilderFlags var flags javaBuilderFlags
@ -2733,7 +2735,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
case libTag, staticLibTag: case libTag, staticLibTag:
flags.classpath = append(flags.classpath, dep.HeaderJars()...) flags.classpath = append(flags.classpath, dep.HeaderJars()...)
// sdk lib names from dependencies are re-exported // sdk lib names from dependencies are re-exported
j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs()) j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs())
case bootClasspathTag: case bootClasspathTag:
flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...) flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...)
} }
@ -2742,7 +2744,8 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
case libTag: case libTag:
flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
// names of sdk libs that are directly depended are exported // names of sdk libs that are directly depended are exported
j.exportedSdkLibs.AddLibraryPath(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath()) j.exportedSdkLibs.AddContext(ctx, otherName,
dep.DexJarBuildPath(), dep.DexJarInstallPath())
} }
} }
}) })
@ -2757,7 +2760,8 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// add the name of that java_sdk_library to the exported sdk libs to make sure // add the name of that java_sdk_library to the exported sdk libs to make sure
// that, if necessary, a <uses-library> element for that java_sdk_library is // that, if necessary, a <uses-library> element for that java_sdk_library is
// added to the Android manifest. // added to the Android manifest.
j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile) j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
outputFile, installFile)
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs) j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
@ -2839,7 +2843,7 @@ func (j *Import) AidlIncludeDirs() android.Paths {
return j.exportAidlIncludeDirs return j.exportAidlIncludeDirs
} }
func (j *Import) ExportedSdkLibs() dexpreopt.LibraryPaths { func (j *Import) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
return j.exportedSdkLibs return j.exportedSdkLibs
} }

View file

@ -1593,8 +1593,8 @@ func TestJavaSdkLibrary(t *testing.T) {
// test if baz has exported SDK lib names foo and bar to qux // test if baz has exported SDK lib names foo and bar to qux
qux := ctx.ModuleForTests("qux", "android_common") qux := ctx.ModuleForTests("qux", "android_common")
if quxLib, ok := qux.Module().(*Library); ok { if quxLib, ok := qux.Module().(*Library); ok {
sdkLibs := android.SortedStringKeys(quxLib.ExportedSdkLibs()) sdkLibs := quxLib.ExportedSdkLibs().UsesLibs()
if w := []string{"bar", "foo", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) { if w := []string{"foo", "bar", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) {
t.Errorf("qux should export %q but exports %q", w, sdkLibs) t.Errorf("qux should export %q but exports %q", w, sdkLibs)
} }
} }