[automerger skipped] Minimal license feature. am: 459beab694 -s ours
am skip reason: skip tag Change-Id Ic78672cd25da13aa9078a48b06446f66780c792d with SHA-1 58d85b87bc is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/13928045
Change-Id: If527f71987f23aee63aa0a1209572479672398c5
diff --git a/Android.bp b/Android.bp
index 93e706d..c9ed95b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,12 +40,14 @@
],
srcs: [
"android/androidmk.go",
+ "android/apex.go",
"android/api_levels.go",
"android/arch.go",
"android/config.go",
"android/defaults.go",
"android/defs.go",
"android/expand.go",
+ "android/filegroup.go",
"android/hooks.go",
"android/license.go",
"android/makevars.go",
@@ -53,33 +55,46 @@
"android/mutator.go",
"android/namespace.go",
"android/neverallow.go",
+ "android/notices.go",
"android/onceper.go",
+ "android/override_module.go",
"android/package.go",
"android/package_ctx.go",
+ "android/path_properties.go",
"android/paths.go",
"android/prebuilt.go",
+ "android/prebuilt_etc.go",
"android/proto.go",
"android/register.go",
+ "android/rule_builder.go",
+ "android/sh_binary.go",
"android/singleton.go",
"android/testing.go",
"android/util.go",
"android/variable.go",
+ "android/vts_config.go",
"android/writedocs.go",
// Lock down environment access last
"android/env.go",
],
testSrcs: [
+ "android/arch_test.go",
"android/config_test.go",
"android/expand_test.go",
"android/license_test.go",
"android/namespace_test.go",
"android/neverallow_test.go",
+ "android/onceper_test.go",
+ "android/path_properties_test.go",
"android/package_test.go",
"android/paths_test.go",
"android/prebuilt_test.go",
+ "android/prebuilt_etc_test.go",
+ "android/rule_builder_test.go",
"android/util_test.go",
"android/variable_test.go",
+ "android/vts_config_test.go",
],
}
@@ -88,19 +103,23 @@
pkgPath: "android/soong/cc/config",
deps: [
"soong-android",
+ "soong-remoteexec",
],
srcs: [
"cc/config/clang.go",
"cc/config/global.go",
"cc/config/tidy.go",
"cc/config/toolchain.go",
+ "cc/config/vndk.go",
"cc/config/arm_device.go",
"cc/config/arm64_device.go",
+ "cc/config/arm64_fuchsia_device.go",
"cc/config/mips_device.go",
"cc/config/mips64_device.go",
"cc/config/x86_device.go",
"cc/config/x86_64_device.go",
+ "cc/config/x86_64_fuchsia_device.go",
"cc/config/x86_darwin_host.go",
"cc/config/x86_linux_host.go",
@@ -122,6 +141,7 @@
"soong-android",
"soong-cc-config",
"soong-genrule",
+ "soong-tradefed",
],
srcs: [
"cc/androidmk.go",
@@ -135,18 +155,20 @@
"cc/pgo.go",
"cc/prebuilt.go",
"cc/proto.go",
- "cc/relocation_packer.go",
"cc/rs.go",
"cc/sanitize.go",
"cc/sabi.go",
"cc/stl.go",
"cc/strip.go",
+ "cc/sysprop.go",
"cc/tidy.go",
"cc/util.go",
"cc/vndk.go",
"cc/vndk_prebuilt.go",
+ "cc/xom.go",
"cc/cmakelists.go",
+ "cc/compdb.go",
"cc/compiler.go",
"cc/installer.go",
"cc/linker.go",
@@ -169,12 +191,18 @@
"cc/genrule.go",
"cc/vendor_public_library.go",
+
+ "cc/testing.go",
],
testSrcs: [
"cc/cc_test.go",
"cc/gen_test.go",
+ "cc/genrule_test.go",
"cc/library_test.go",
+ "cc/prebuilt_test.go",
+ "cc/proto_test.go",
"cc/test_data_test.go",
+ "cc/util_test.go",
],
pluginFor: ["soong_build"],
}
@@ -190,9 +218,11 @@
"soong-shared",
],
srcs: [
- "genrule/filegroup.go",
"genrule/genrule.go",
],
+ testSrcs: [
+ "genrule/genrule_test.go",
+ ],
pluginFor: ["soong_build"],
}
@@ -217,31 +247,56 @@
"blueprint-pathtools",
"soong",
"soong-android",
+ "soong-cc",
+ "soong-dexpreopt",
"soong-genrule",
"soong-java-config",
+ "soong-tradefed",
],
srcs: [
"java/aapt2.go",
"java/aar.go",
+ "java/android_manifest.go",
"java/android_resources.go",
"java/androidmk.go",
"java/app_builder.go",
"java/app.go",
"java/builder.go",
+ "java/device_host_converter.go",
"java/dex.go",
+ "java/dexpreopt.go",
+ "java/dexpreopt_bootjars.go",
+ "java/dexpreopt_config.go",
"java/droiddoc.go",
"java/gen.go",
"java/genrule.go",
+ "java/hiddenapi.go",
+ "java/hiddenapi_singleton.go",
"java/jacoco.go",
"java/java.go",
+ "java/jdeps.go",
"java/java_resources.go",
+ "java/kotlin.go",
+ "java/plugin.go",
+ "java/prebuilt_apis.go",
"java/proto.go",
+ "java/sdk.go",
+ "java/sdk_library.go",
"java/support_libraries.go",
"java/system_modules.go",
+ "java/testing.go",
+ "java/tradefed.go",
],
testSrcs: [
"java/app_test.go",
+ "java/device_host_converter_test.go",
+ "java/dexpreopt_test.go",
+ "java/dexpreopt_bootjars_test.go",
"java/java_test.go",
+ "java/jdeps_test.go",
+ "java/kotlin_test.go",
+ "java/plugin_test.go",
+ "java/sdk_test.go",
],
pluginFor: ["soong_build"],
}
@@ -252,6 +307,7 @@
deps: [
"blueprint-proptools",
"soong-android",
+ "soong-remoteexec",
],
srcs: [
"java/config/config.go",
@@ -267,6 +323,7 @@
deps: [
"blueprint",
"soong-android",
+ "soong-tradefed",
],
srcs: [
"python/androidmk.go",
@@ -275,6 +332,7 @@
"python/defaults.go",
"python/installer.go",
"python/library.go",
+ "python/proto.go",
"python/python.go",
"python/test.go",
],
@@ -292,6 +350,95 @@
],
}
+bootstrap_go_package {
+ name: "soong-tradefed",
+ pkgPath: "android/soong/tradefed",
+ deps: [
+ "blueprint",
+ "soong-android",
+ ],
+ srcs: [
+ "tradefed/autogen.go",
+ "tradefed/config.go",
+ "tradefed/makevars.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
+bootstrap_go_package {
+ name: "soong-xml",
+ pkgPath: "android/soong/xml",
+ deps: [
+ "blueprint",
+ "blueprint-pathtools",
+ "soong",
+ "soong-android",
+ ],
+ srcs: [
+ "xml/xml.go",
+ ],
+ testSrcs: [
+ "xml/xml_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
+bootstrap_go_package {
+ name: "soong-apex",
+ pkgPath: "android/soong/apex",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-cc",
+ "soong-java",
+ "soong-python",
+ ],
+ srcs: [
+ "apex/apex.go",
+ "apex/key.go",
+ ],
+ testSrcs: [
+ "apex/apex_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
+bootstrap_go_package {
+ name: "soong-sysprop",
+ pkgPath: "android/soong/sysprop",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-cc",
+ "soong-java",
+ ],
+ srcs: [
+ "sysprop/sysprop_library.go",
+ ],
+ testSrcs: [
+ "sysprop/sysprop_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
+bootstrap_go_package {
+ name: "soong-remoteexec",
+ pkgPath: "android/soong/remoteexec",
+ deps: [
+ "blueprint",
+ "soong-android",
+ ],
+ srcs: [
+ "remoteexec/remoteexec.go",
+ ],
+ testSrcs: [
+ "remoteexec/remoteexec_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
//
// Defaults to enable various configurations of host bionic
//
@@ -317,9 +464,20 @@
name: "libatomic",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ recovery_available: true,
+
arch: {
arm: {
- instruction_set: "arm",
+ src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/arm-linux-androideabi/lib/libatomic.a",
+ },
+ arm64: {
+ src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/aarch64-linux-android/lib64/libatomic.a",
+ },
+ x86: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/x86_64-linux-android/lib/libatomic.a",
+ },
+ x86_64: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/x86_64-linux-android/lib64/libatomic.a",
},
},
}
@@ -328,10 +486,132 @@
name: "libgcc",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ recovery_available: true,
+
arch: {
arm: {
- instruction_set: "arm",
+ src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a",
},
+ arm64: {
+ src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a",
+ },
+ x86: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcc.a",
+ },
+ x86_64: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcc.a",
+ },
+ },
+}
+
+toolchain_library {
+ name: "libgcc_stripped",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ recovery_available: true,
+
+ arch: {
+ arm: {
+ src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a",
+ strip: {
+ keep_symbols_list: [
+ // unwind-arm.o
+ "_Unwind_Complete",
+ "_Unwind_DeleteException",
+ "_Unwind_GetCFA",
+ "_Unwind_VRS_Get",
+ "_Unwind_VRS_Pop",
+ "_Unwind_VRS_Set",
+ "__aeabi_unwind_cpp_pr0",
+ "__aeabi_unwind_cpp_pr1",
+ "__aeabi_unwind_cpp_pr2",
+ "__gnu_Unwind_Backtrace",
+ "__gnu_Unwind_ForcedUnwind",
+ "__gnu_Unwind_RaiseException",
+ "__gnu_Unwind_Resume",
+ "__gnu_Unwind_Resume_or_Rethrow",
+
+ // libunwind.o
+ "_Unwind_Backtrace",
+ "_Unwind_ForcedUnwind",
+ "_Unwind_RaiseException",
+ "_Unwind_Resume",
+ "_Unwind_Resume_or_Rethrow",
+ "___Unwind_Backtrace",
+ "___Unwind_ForcedUnwind",
+ "___Unwind_RaiseException",
+ "___Unwind_Resume",
+ "___Unwind_Resume_or_Rethrow",
+ "__gnu_Unwind_Restore_VFP",
+ "__gnu_Unwind_Restore_VFP_D",
+ "__gnu_Unwind_Restore_VFP_D_16_to_31",
+ "__gnu_Unwind_Restore_WMMXC",
+ "__gnu_Unwind_Restore_WMMXD",
+ "__gnu_Unwind_Save_VFP",
+ "__gnu_Unwind_Save_VFP_D",
+ "__gnu_Unwind_Save_VFP_D_16_to_31",
+ "__gnu_Unwind_Save_WMMXC",
+ "__gnu_Unwind_Save_WMMXD",
+ "__restore_core_regs",
+ "restore_core_regs",
+
+ // pr-support.o
+ "_Unwind_GetDataRelBase",
+ "_Unwind_GetLanguageSpecificData",
+ "_Unwind_GetRegionStart",
+ "_Unwind_GetTextRelBase",
+ "__gnu_unwind_execute",
+ "__gnu_unwind_frame",
+ ],
+ use_gnu_strip: true,
+ },
+ },
+ arm64: {
+ src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a",
+ },
+ x86: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcc.a",
+
+ },
+ x86_64: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcc.a",
+ },
+ },
+ strip: {
+ keep_symbols_list: [
+ // unwind-dw2.o
+ "_Unwind_Backtrace",
+ "_Unwind_DeleteException",
+ "_Unwind_FindEnclosingFunction",
+ "_Unwind_ForcedUnwind",
+ "_Unwind_GetCFA",
+ "_Unwind_GetDataRelBase",
+ "_Unwind_GetGR",
+ "_Unwind_GetIP",
+ "_Unwind_GetIPInfo",
+ "_Unwind_GetLanguageSpecificData",
+ "_Unwind_GetRegionStart",
+ "_Unwind_GetTextRelBase",
+ "_Unwind_RaiseException",
+ "_Unwind_Resume",
+ "_Unwind_Resume_or_Rethrow",
+ "_Unwind_SetGR",
+ "_Unwind_SetIP",
+ "__frame_state_for",
+
+ // unwind-dw2-fde-dip.o
+ "_Unwind_Find_FDE",
+ "__deregister_frame",
+ "__deregister_frame_info",
+ "__deregister_frame_info_bases",
+ "__register_frame",
+ "__register_frame_info",
+ "__register_frame_info_bases",
+ "__register_frame_info_table",
+ "__register_frame_info_table_bases",
+ "__register_frame_table",
+ ],
+ use_gnu_strip: true,
},
}
@@ -343,15 +623,32 @@
windows: {
enabled: true,
},
+ windows_x86: {
+ src: "prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib32/libwinpthread.a",
+ },
+ windows_x86_64: {
+ src: "prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib/libwinpthread.a",
+ },
},
+ notice: ":mingw-libwinpthread-notice",
}
toolchain_library {
name: "libgcov",
defaults: ["linux_bionic_supported"],
+
arch: {
arm: {
- instruction_set: "arm",
+ src: "prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/lib/gcc/arm-linux-androideabi/4.9.x/libgcov.a",
+ },
+ arm64: {
+ src: "prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/lib/gcc/aarch64-linux-android/4.9.x/libgcov.a",
+ },
+ x86: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/32/libgcov.a",
+ },
+ x86_64: {
+ src: "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/lib/gcc/x86_64-linux-android/4.9.x/libgcov.a",
},
},
}
@@ -359,6 +656,7 @@
kernel_headers {
name: "device_kernel_headers",
vendor: true,
+ recovery_available: true,
}
cc_genrule {
@@ -383,7 +681,7 @@
}
cc_genrule {
- name: "host_bionic_linker_script",
+ name: "host_bionic_linker_flags",
host_supported: true,
device_supported: false,
target: {
@@ -398,7 +696,7 @@
},
},
tools: ["extract_linker"],
- cmd: "$(location) -T $(out) $(in)",
+ cmd: "$(location) -f $(out) $(in)",
srcs: [":linker"],
- out: ["linker.script"],
+ out: ["linker.flags"],
}
diff --git a/OWNERS b/OWNERS
index 121a441..8bb5c30 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,11 +1,12 @@
-ccross@android.com
-dwillemsen@google.com
-nanzhang@google.com
-
+per-file * = asmundak@google.com
per-file * = ccross@android.com
per-file * = dwillemsen@google.com
-per-file * = nanzhang@google.com
-per-file *gen_stub_libs.py = danalbert@google.com
-per-file ndk_*.go = danalbert@google.com
-per-file clang.go = srhines@google.com
-per-file global.go = srhines@google.com
+per-file * = eakammer@google.com
+per-file * = jungjw@google.com
+per-file * = patricearruda@google.com
+per-file * = paulduffin@google.com
+
+per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com
+per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
+per-file tidy.go = srhines@google.com, chh@google.com
+per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
diff --git a/README.md b/README.md
index b234c71..2957940 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,10 @@
replaces Android.mk files with Android.bp files, which are JSON-like simple
declarative descriptions of modules to build.
+See [Simple Build
+Configuration](https://source.android.com/compatibility/tests/development/blueprints)
+on source.android.com to read how Soong is configured for testing.
+
## Android.bp file format
By design, Android.bp files are very simple. There are no conditionals or
@@ -30,7 +34,15 @@
all Android.bp files.
For a list of valid module types and their properties see
-[$OUT_DIR/soong/.bootstrap/docs/soong_build.html](https://go/Android.bp).
+[$OUT_DIR/soong/docs/soong_build.html](https://ci.android.com/builds/latest/branches/aosp-build-tools/targets/linux/view/soong_build.html).
+
+### Globs
+
+Properties that take a list of files can also take glob patterns. Glob
+patterns can contain the normal Unix wildcard `*`, for example "*.java". Glob
+patterns can also contain a single `**` wildcard as a path element, which will
+match zero or more path elements. For example, `java/**/*.java` will match
+`java/Main.java` and `java/com/android/Main.java`.
### Variables
@@ -175,6 +187,7 @@
* [Best Practices](docs/best_practices.md)
* [Build Performance](docs/perf.md)
* [Generating CLion Projects](docs/clion.md)
+* [Generating YouCompleteMe/VSCode compile\_commands.json file](docs/compdb.md)
* Make-specific documentation: [build/make/README.md](https://android.googlesource.com/platform/build/+/master/README.md)
## FAQ
@@ -208,6 +221,18 @@
or [external/llvm/soong/llvm.go](https://android.googlesource.com/platform/external/llvm/+/master/soong/llvm.go)
for examples of more complex conditionals on product variables or environment variables.
+## Developing for Soong
+
+To load Soong code in a Go-aware IDE, create a directory outside your android tree and then:
+```bash
+apt install bindfs
+export GOPATH=<path to the directory you created>
+build/soong/scripts/setup_go_workspace_for_soong.sh
+```
+
+This will bind mount the Soong source directories into the directory in the layout expected by
+the IDE.
+
## Contact
Email android-building@googlegroups.com (external) for any questions, or see
diff --git a/android/androidmk.go b/android/androidmk.go
index 81de415..bd49e4c 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -25,6 +25,7 @@
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/bootstrap"
)
func init() {
@@ -39,6 +40,7 @@
type AndroidMkData struct {
Class string
SubName string
+ DistFile OptionalPath
OutputFile OptionalPath
Disabled bool
Include string
@@ -64,13 +66,15 @@
return
}
- var androidMkModulesList []Module
+ var androidMkModulesList []blueprint.Module
- ctx.VisitAllModules(func(module Module) {
+ ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
androidMkModulesList = append(androidMkModulesList, module)
})
- sort.Sort(AndroidModulesByName{androidMkModulesList, ctx})
+ sort.SliceStable(androidMkModulesList, func(i, j int) bool {
+ return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j])
+ })
transMk := PathForOutput(ctx, "Android"+String(ctx.Config().productVariables.Make_suffix)+".mk")
if ctx.Failed() {
@@ -88,7 +92,7 @@
})
}
-func translateAndroidMk(ctx SingletonContext, mkFile string, mods []Module) error {
+func translateAndroidMk(ctx SingletonContext, mkFile string, mods []blueprint.Module) error {
buf := &bytes.Buffer{}
fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
@@ -101,8 +105,8 @@
return err
}
- if ctx.PrimaryModule(mod) == mod {
- type_stats[ctx.ModuleType(mod)] += 1
+ if amod, ok := mod.(Module); ok && ctx.PrimaryModule(amod) == amod {
+ type_stats[ctx.ModuleType(amod)] += 1
}
}
@@ -141,10 +145,36 @@
}
func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
- provider, ok := mod.(AndroidMkDataProvider)
- if !ok {
+ defer func() {
+ if r := recover(); r != nil {
+ panic(fmt.Errorf("%s in translateAndroidMkModule for module %s variant %s",
+ r, ctx.ModuleName(mod), ctx.ModuleSubDir(mod)))
+ }
+ }()
+
+ switch x := mod.(type) {
+ case AndroidMkDataProvider:
+ return translateAndroidModule(ctx, w, mod, x)
+ case bootstrap.GoBinaryTool:
+ return translateGoBinaryModule(ctx, w, mod, x)
+ default:
return nil
}
+}
+
+func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
+ goBinary bootstrap.GoBinaryTool) error {
+
+ name := ctx.ModuleName(mod)
+ fmt.Fprintln(w, ".PHONY:", name)
+ fmt.Fprintln(w, name+":", goBinary.InstallPath())
+ fmt.Fprintln(w, "")
+
+ return nil
+}
+
+func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
+ provider AndroidMkDataProvider) error {
name := provider.BaseModuleName()
amod := mod.(Module).base()
@@ -169,7 +199,7 @@
data.Include = "$(BUILD_PREBUILT)"
}
- data.Required = amod.commonProperties.Required
+ data.Required = append(data.Required, amod.commonProperties.Required...)
// Make does not understand LinuxBionic
if amod.Os() == LinuxBionic {
@@ -188,11 +218,50 @@
}
- if amod.Arch().ArchType != ctx.Config().Targets[amod.Os().Class][0].Arch.ArchType {
+ if amod.Arch().ArchType != ctx.Config().Targets[amod.Os()][0].Arch.ArchType {
prefix = "2ND_" + prefix
}
}
+ if len(amod.commonProperties.Dist.Targets) > 0 {
+ distFile := data.DistFile
+ if !distFile.Valid() {
+ distFile = data.OutputFile
+ }
+ if distFile.Valid() {
+ dest := filepath.Base(distFile.String())
+
+ if amod.commonProperties.Dist.Dest != nil {
+ var err error
+ dest, err = validateSafePath(*amod.commonProperties.Dist.Dest)
+ if err != nil {
+ // This was checked in ModuleBase.GenerateBuildActions
+ panic(err)
+ }
+ }
+
+ if amod.commonProperties.Dist.Suffix != nil {
+ ext := filepath.Ext(dest)
+ suffix := *amod.commonProperties.Dist.Suffix
+ dest = strings.TrimSuffix(dest, ext) + suffix + ext
+ }
+
+ if amod.commonProperties.Dist.Dir != nil {
+ var err error
+ dest, err = validateSafePath(*amod.commonProperties.Dist.Dir, dest)
+ if err != nil {
+ // This was checked in ModuleBase.GenerateBuildActions
+ panic(err)
+ }
+ }
+
+ goals := strings.Join(amod.commonProperties.Dist.Targets, " ")
+ fmt.Fprintln(&data.preamble, ".PHONY:", goals)
+ fmt.Fprintf(&data.preamble, "$(call dist-for-goals,%s,%s:%s)\n",
+ goals, distFile.String(), dest)
+ }
+ }
+
fmt.Fprintln(&data.preamble, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(&data.preamble, "LOCAL_PATH :=", filepath.Dir(ctx.BlueprintFile(mod)))
fmt.Fprintln(&data.preamble, "LOCAL_MODULE :=", name+data.SubName)
@@ -227,6 +296,9 @@
if len(amod.commonProperties.Init_rc) > 0 {
fmt.Fprintln(&data.preamble, "LOCAL_INIT_RC := ", strings.Join(amod.commonProperties.Init_rc, " "))
}
+ if len(amod.commonProperties.Vintf_fragments) > 0 {
+ fmt.Fprintln(&data.preamble, "LOCAL_VINTF_FRAGMENTS := ", strings.Join(amod.commonProperties.Vintf_fragments, " "))
+ }
if Bool(amod.commonProperties.Proprietary) {
fmt.Fprintln(&data.preamble, "LOCAL_PROPRIETARY_MODULE := true")
}
@@ -239,12 +311,16 @@
if Bool(amod.commonProperties.Product_specific) {
fmt.Fprintln(&data.preamble, "LOCAL_PRODUCT_MODULE := true")
}
+ if Bool(amod.commonProperties.Product_services_specific) {
+ fmt.Fprintln(&data.preamble, "LOCAL_PRODUCT_SERVICES_MODULE := true")
+ }
if amod.commonProperties.Owner != nil {
fmt.Fprintln(&data.preamble, "LOCAL_MODULE_OWNER :=", *amod.commonProperties.Owner)
}
- if amod.commonProperties.Notice != nil {
- fmt.Fprintln(&data.preamble, "LOCAL_NOTICE_FILE :=", "$(LOCAL_PATH)/"+*amod.commonProperties.Notice)
- }
+ }
+
+ if amod.noticeFile.Valid() {
+ fmt.Fprintln(&data.preamble, "LOCAL_NOTICE_FILE :=", amod.noticeFile.String())
}
if host {
diff --git a/android/apex.go b/android/apex.go
new file mode 100644
index 0000000..17df762
--- /dev/null
+++ b/android/apex.go
@@ -0,0 +1,236 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "sort"
+ "sync"
+
+ "github.com/google/blueprint"
+)
+
+// ApexModule is the interface that a module type is expected to implement if
+// the module has to be built differently depending on whether the module
+// is destined for an apex or not (installed to one of the regular partitions).
+//
+// Native shared libraries are one such module type; when it is built for an
+// APEX, it should depend only on stable interfaces such as NDK, stable AIDL,
+// or C APIs from other APEXs.
+//
+// A module implementing this interface will be mutated into multiple
+// variations by apex.apexMutator if it is directly or indirectly included
+// in one or more APEXs. Specifically, if a module is included in apex.foo and
+// apex.bar then three apex variants are created: platform, apex.foo and
+// apex.bar. The platform variant is for the regular partitions
+// (e.g., /system or /vendor, etc.) while the other two are for the APEXs,
+// respectively.
+type ApexModule interface {
+ Module
+ apexModuleBase() *ApexModuleBase
+
+ // Marks that this module should be built for the APEX of the specified name.
+ // Call this before apex.apexMutator is run.
+ BuildForApex(apexName string)
+
+ // Returns the name of APEX that this module will be built for. Empty string
+ // is returned when 'IsForPlatform() == true'. Note that a module can be
+ // included in multiple APEXes, in which case, the module is mutated into
+ // multiple modules each of which for an APEX. This method returns the
+ // name of the APEX that a variant module is for.
+ // Call this after apex.apexMutator is run.
+ ApexName() string
+
+ // Tests whether this module will be built for the platform or not.
+ // This is a shortcut for ApexName() == ""
+ IsForPlatform() bool
+
+ // Tests if this module could have APEX variants. APEX variants are
+ // created only for the modules that returns true here. This is useful
+ // for not creating APEX variants for certain types of shared libraries
+ // such as NDK stubs.
+ CanHaveApexVariants() bool
+
+ // Tests if this module can be installed to APEX as a file. For example,
+ // this would return true for shared libs while return false for static
+ // libs.
+ IsInstallableToApex() bool
+
+ // Mutate this module into one or more variants each of which is built
+ // for an APEX marked via BuildForApex().
+ CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module
+
+ // Sets the name of the apex variant of this module. Called inside
+ // CreateApexVariations.
+ setApexName(apexName string)
+}
+
+type ApexProperties struct {
+ // Name of the apex variant that this module is mutated into
+ ApexName string `blueprint:"mutated"`
+}
+
+// Provides default implementation for the ApexModule interface. APEX-aware
+// modules are expected to include this struct and call InitApexModule().
+type ApexModuleBase struct {
+ ApexProperties ApexProperties
+
+ canHaveApexVariants bool
+
+ apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator
+ apexVariations []string
+}
+
+func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase {
+ return m
+}
+
+func (m *ApexModuleBase) BuildForApex(apexName string) {
+ m.apexVariationsLock.Lock()
+ defer m.apexVariationsLock.Unlock()
+ if !InList(apexName, m.apexVariations) {
+ m.apexVariations = append(m.apexVariations, apexName)
+ }
+}
+
+func (m *ApexModuleBase) ApexName() string {
+ return m.ApexProperties.ApexName
+}
+
+func (m *ApexModuleBase) IsForPlatform() bool {
+ return m.ApexProperties.ApexName == ""
+}
+
+func (m *ApexModuleBase) setApexName(apexName string) {
+ m.ApexProperties.ApexName = apexName
+}
+
+func (m *ApexModuleBase) CanHaveApexVariants() bool {
+ return m.canHaveApexVariants
+}
+
+func (m *ApexModuleBase) IsInstallableToApex() bool {
+ // should be overriden if needed
+ return false
+}
+
+func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module {
+ if len(m.apexVariations) > 0 {
+ sort.Strings(m.apexVariations)
+ variations := []string{""} // Original variation for platform
+ variations = append(variations, m.apexVariations...)
+
+ modules := mctx.CreateVariations(variations...)
+ for i, m := range modules {
+ if i == 0 {
+ continue
+ }
+ m.(ApexModule).setApexName(variations[i])
+ }
+ return modules
+ }
+ return nil
+}
+
+var apexData OncePer
+var apexNamesMapMutex sync.Mutex
+var apexNamesKey = NewOnceKey("apexNames")
+
+// This structure maintains the global mapping in between modules and APEXes.
+// Examples:
+//
+// apexNamesMap()["foo"]["bar"] == true: module foo is directly depended on by APEX bar
+// apexNamesMap()["foo"]["bar"] == false: module foo is indirectly depended on by APEX bar
+// apexNamesMap()["foo"]["bar"] doesn't exist: foo is not built for APEX bar
+func apexNamesMap() map[string]map[string]bool {
+ return apexData.Once(apexNamesKey, func() interface{} {
+ return make(map[string]map[string]bool)
+ }).(map[string]map[string]bool)
+}
+
+// Update the map to mark that a module named moduleName is directly or indirectly
+// depended on by an APEX named apexName. Directly depending means that a module
+// is explicitly listed in the build definition of the APEX via properties like
+// native_shared_libs, java_libs, etc.
+func UpdateApexDependency(apexName string, moduleName string, directDep bool) {
+ apexNamesMapMutex.Lock()
+ defer apexNamesMapMutex.Unlock()
+ apexNames, ok := apexNamesMap()[moduleName]
+ if !ok {
+ apexNames = make(map[string]bool)
+ apexNamesMap()[moduleName] = apexNames
+ }
+ apexNames[apexName] = apexNames[apexName] || directDep
+}
+
+// Tests whether a module named moduleName is directly depended on by an APEX
+// named apexName.
+func DirectlyInApex(apexName string, moduleName string) bool {
+ apexNamesMapMutex.Lock()
+ defer apexNamesMapMutex.Unlock()
+ if apexNames, ok := apexNamesMap()[moduleName]; ok {
+ return apexNames[apexName]
+ }
+ return false
+}
+
+type hostContext interface {
+ Host() bool
+}
+
+// Tests whether a module named moduleName is directly depended on by any APEX.
+func DirectlyInAnyApex(ctx hostContext, moduleName string) bool {
+ if ctx.Host() {
+ // Host has no APEX.
+ return false
+ }
+ apexNamesMapMutex.Lock()
+ defer apexNamesMapMutex.Unlock()
+ if apexNames, ok := apexNamesMap()[moduleName]; ok {
+ for an := range apexNames {
+ if apexNames[an] {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// Tests whether a module named module is depended on (including both
+// direct and indirect dependencies) by any APEX.
+func InAnyApex(moduleName string) bool {
+ apexNamesMapMutex.Lock()
+ defer apexNamesMapMutex.Unlock()
+ apexNames, ok := apexNamesMap()[moduleName]
+ return ok && len(apexNames) > 0
+}
+
+func GetApexesForModule(moduleName string) []string {
+ ret := []string{}
+ apexNamesMapMutex.Lock()
+ defer apexNamesMapMutex.Unlock()
+ if apexNames, ok := apexNamesMap()[moduleName]; ok {
+ for an := range apexNames {
+ ret = append(ret, an)
+ }
+ }
+ return ret
+}
+
+func InitApexModule(m ApexModule) {
+ base := m.apexModuleBase()
+ base.canHaveApexVariants = true
+
+ m.AddProperties(&base.ApexProperties)
+}
diff --git a/android/api_levels.go b/android/api_levels.go
index b1b954c..2f70f62 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -16,6 +16,7 @@
import (
"encoding/json"
+ "strconv"
)
func init() {
@@ -50,28 +51,51 @@
return PathForOutput(ctx, "api_levels.json")
}
-func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
- baseApiLevel := 9000
- apiLevelsMap := map[string]int{
- "G": 9,
- "I": 14,
- "J": 16,
- "J-MR1": 17,
- "J-MR2": 18,
- "K": 19,
- "L": 21,
- "L-MR1": 22,
- "M": 23,
- "N": 24,
- "N-MR1": 25,
- "O": 26,
- "O-MR1": 27,
- "P": 28,
- }
- for i, codename := range ctx.Config().PlatformVersionCombinedCodenames() {
- apiLevelsMap[codename] = baseApiLevel + i
- }
+var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
+func getApiLevelsMap(config Config) map[string]int {
+ return config.Once(apiLevelsMapKey, func() interface{} {
+ baseApiLevel := 9000
+ apiLevelsMap := map[string]int{
+ "G": 9,
+ "I": 14,
+ "J": 16,
+ "J-MR1": 17,
+ "J-MR2": 18,
+ "K": 19,
+ "L": 21,
+ "L-MR1": 22,
+ "M": 23,
+ "N": 24,
+ "N-MR1": 25,
+ "O": 26,
+ "O-MR1": 27,
+ "P": 28,
+ "Q": 29,
+ }
+ for i, codename := range config.PlatformVersionCombinedCodenames() {
+ apiLevelsMap[codename] = baseApiLevel + i
+ }
+
+ return apiLevelsMap
+ }).(map[string]int)
+}
+
+// Converts an API level string into its numeric form.
+// * Codenames are decoded.
+// * Numeric API levels are simply converted.
+// * "minimum" and "current" are not currently handled since the former is
+// NDK specific and the latter has inconsistent meaning.
+func ApiStrToNum(ctx BaseContext, apiLevel string) (int, error) {
+ num, ok := getApiLevelsMap(ctx.Config())[apiLevel]
+ if ok {
+ return num, nil
+ }
+ return strconv.Atoi(apiLevel)
+}
+
+func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
+ apiLevelsMap := getApiLevelsMap(ctx.Config())
apiLevelsJson := GetApiLevelsJson(ctx)
createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
}
diff --git a/android/arch.go b/android/arch.go
index 88f9f65..957a659 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -15,9 +15,11 @@
package android
import (
+ "encoding"
"fmt"
"reflect"
"runtime"
+ "strconv"
"strings"
"github.com/google/blueprint/proptools"
@@ -103,36 +105,419 @@
}
*/
-var archVariants = map[ArchType][]string{}
-var archFeatures = map[ArchType][]string{}
-var archFeatureMap = map[ArchType]map[string][]string{}
-
-func RegisterArchVariants(arch ArchType, variants ...string) {
- checkCalledFromInit()
- archVariants[arch] = append(archVariants[arch], variants...)
+var archVariants = map[ArchType][]string{
+ Arm: {
+ "armv7-a",
+ "armv7-a-neon",
+ "armv8-a",
+ "armv8-2a",
+ "cortex-a7",
+ "cortex-a8",
+ "cortex-a9",
+ "cortex-a15",
+ "cortex-a53",
+ "cortex-a53-a57",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "krait",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ Arm64: {
+ "armv8_a",
+ "armv8_2a",
+ "cortex-a53",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ Mips: {
+ "mips32_fp",
+ "mips32r2_fp",
+ "mips32r2_fp_xburst",
+ "mips32r2dsp_fp",
+ "mips32r2dspr2_fp",
+ "mips32r6",
+ },
+ Mips64: {
+ "mips64r2",
+ "mips64r6",
+ },
+ X86: {
+ "amberlake",
+ "atom",
+ "broadwell",
+ "haswell",
+ "icelake",
+ "ivybridge",
+ "kabylake",
+ "sandybridge",
+ "silvermont",
+ "skylake",
+ "stoneyridge",
+ "tigerlake",
+ "whiskeylake",
+ "x86_64",
+ },
+ X86_64: {
+ "amberlake",
+ "broadwell",
+ "haswell",
+ "icelake",
+ "ivybridge",
+ "kabylake",
+ "sandybridge",
+ "silvermont",
+ "skylake",
+ "stoneyridge",
+ "tigerlake",
+ "whiskeylake",
+ },
}
-func RegisterArchFeatures(arch ArchType, features ...string) {
- checkCalledFromInit()
- archFeatures[arch] = append(archFeatures[arch], features...)
+var archFeatures = map[ArchType][]string{
+ Arm: {
+ "neon",
+ },
+ Mips: {
+ "dspr2",
+ "rev6",
+ "msa",
+ },
+ Mips64: {
+ "rev6",
+ "msa",
+ },
+ X86: {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ "movbe",
+ },
+ X86_64: {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ },
}
-func RegisterArchVariantFeatures(arch ArchType, variant string, features ...string) {
+var archFeatureMap = map[ArchType]map[string][]string{
+ Arm: {
+ "armv7-a-neon": {
+ "neon",
+ },
+ "armv8-a": {
+ "neon",
+ },
+ "armv8-2a": {
+ "neon",
+ },
+ },
+ Mips: {
+ "mips32r2dspr2_fp": {
+ "dspr2",
+ },
+ "mips32r6": {
+ "rev6",
+ },
+ },
+ Mips64: {
+ "mips64r6": {
+ "rev6",
+ },
+ },
+ X86: {
+ "amberlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "atom": {
+ "ssse3",
+ "movbe",
+ },
+ "broadwell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "haswell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ "movbe",
+ },
+ "icelake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "ivybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "kabylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "sandybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ "silvermont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ "movbe",
+ },
+ "skylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "stoneyridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "popcnt",
+ "movbe",
+ },
+ "tigerlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "whiskeylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "x86_64": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ },
+ X86_64: {
+ "amberlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "broadwell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "haswell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "icelake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "ivybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "kabylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "sandybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ "silvermont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ },
+ "skylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "stoneyridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "popcnt",
+ },
+ "tigerlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "whiskeylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ },
+}
+
+var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
+
+func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
checkCalledFromInit()
- if variant != "" && !InList(variant, archVariants[arch]) {
- panic(fmt.Errorf("Invalid variant %q for arch %q", variant, arch))
- }
for _, feature := range features {
if !InList(feature, archFeatures[arch]) {
- panic(fmt.Errorf("Invalid feature %q for arch %q variant %q", feature, arch, variant))
+ panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
}
}
- if archFeatureMap[arch] == nil {
- archFeatureMap[arch] = make(map[string][]string)
+ if defaultArchFeatureMap[os] == nil {
+ defaultArchFeatureMap[os] = make(map[ArchType][]string)
}
- archFeatureMap[arch][variant] = features
+ defaultArchFeatureMap[os][arch] = features
}
// An Arch indicates a single CPU architecture.
@@ -176,6 +561,23 @@
return a.Name
}
+var _ encoding.TextMarshaler = ArchType{}
+
+func (a ArchType) MarshalText() ([]byte, error) {
+ return []byte(strconv.Quote(a.String())), nil
+}
+
+var _ encoding.TextUnmarshaler = &ArchType{}
+
+func (a *ArchType) UnmarshalText(text []byte) error {
+ if u, ok := archTypeMap[string(text)]; ok {
+ *a = u
+ return nil
+ }
+
+ return fmt.Errorf("unknown ArchType %q", text)
+}
+
var BuildOs = func() OsType {
switch runtime.GOOS {
case "linux":
@@ -194,16 +596,18 @@
NoOsType OsType
Linux = NewOsType("linux_glibc", Host, false)
Darwin = NewOsType("darwin", Host, false)
- LinuxBionic = NewOsType("linux_bionic", Host, true)
+ LinuxBionic = NewOsType("linux_bionic", Host, false)
Windows = NewOsType("windows", HostCross, true)
Android = NewOsType("android", Device, false)
+ Fuchsia = NewOsType("fuchsia", Device, false)
osArchTypeMap = map[OsType][]ArchType{
Linux: []ArchType{X86, X86_64},
LinuxBionic: []ArchType{X86_64},
- Darwin: []ArchType{X86, X86_64},
+ Darwin: []ArchType{X86_64},
Windows: []ArchType{X86, X86_64},
Android: []ArchType{Arm, Arm64, Mips, Mips64, X86, X86_64},
+ Fuchsia: []ArchType{Arm64, X86_64},
}
)
@@ -288,6 +692,30 @@
return target.Os.String() + "_" + target.Arch.String()
}
+// archMutator splits a module into a variant for each Target requested by the module. Target selection
+// for a module is in three levels, OsClass, mulitlib, and then Target.
+// OsClass selection is determined by:
+// - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
+// whether the module type can compile for host, device or both.
+// - The host_supported and device_supported properties on the module.
+// If host is supported for the module, the Host and HostCross OsClasses are are selected. If device is supported
+// for the module, the Device OsClass is selected.
+// Within each selected OsClass, the multilib selection is determined by:
+// - The compile_multilib property if it set (which may be overriden by target.android.compile_multlib or
+// target.host.compile_multilib).
+// - The default multilib passed to InitAndroidArchModule if compile_multilib was not set.
+// Valid multilib values include:
+// "both": compile for all Targets supported by the OsClass (generally x86_64 and x86, or arm64 and arm).
+// "first": compile for only a single preferred Target supported by the OsClass. This is generally x86_64 or arm64,
+// but may be arm for a 32-bit only build or a build with TARGET_PREFER_32_BIT=true set.
+// "32": compile for only a single 32-bit Target supported by the OsClass.
+// "64": compile for only a single 64-bit Target supported by the OsClass.
+// "common": compile a for a single Target that will work on all Targets suported by the OsClass (for example Java).
+//
+// Once the list of Targets is determined, the module is split into a variant for each Target.
+//
+// Modules can be initialized with InitAndroidMultiTargetsArchModule, in which case they will be split by OsClass,
+// but will have a common Target that is expected to handle all other selected Targets via ctx.MultiTargets().
func archMutator(mctx BottomUpMutatorContext) {
var module Module
var ok bool
@@ -295,53 +723,66 @@
return
}
- if !module.base().ArchSpecific() {
+ base := module.base()
+
+ if !base.ArchSpecific() {
return
}
- osClasses := module.base().OsClassSupported()
-
var moduleTargets []Target
+ moduleMultiTargets := make(map[int][]Target)
primaryModules := make(map[int]bool)
+ osClasses := base.OsClassSupported()
- for _, class := range osClasses {
- targets := mctx.Config().Targets[class]
- if len(targets) == 0 {
+ for _, os := range osTypeList {
+ supportedClass := false
+ for _, osClass := range osClasses {
+ if os.Class == osClass {
+ supportedClass = true
+ }
+ }
+ if !supportedClass {
continue
}
- var multilib string
- switch class {
- case Device:
- multilib = String(module.base().commonProperties.Target.Android.Compile_multilib)
- case Host, HostCross:
- multilib = String(module.base().commonProperties.Target.Host.Compile_multilib)
+
+ osTargets := mctx.Config().Targets[os]
+ if len(osTargets) == 0 {
+ continue
}
- if multilib == "" {
- multilib = String(module.base().commonProperties.Compile_multilib)
+
+ // only the primary arch in the recovery partition
+ if os == Android && module.InstallInRecovery() {
+ osTargets = []Target{osTargets[0]}
}
- if multilib == "" {
- multilib = module.base().commonProperties.Default_multilib
+
+ prefer32 := false
+ if base.prefer32 != nil {
+ prefer32 = base.prefer32(mctx, base, os.Class)
}
- var prefer32 bool
- switch class {
- case Device:
- prefer32 = mctx.Config().DevicePrefer32BitExecutables()
- case HostCross:
- // Windows builds always prefer 32-bit
- prefer32 = true
- }
- targets, err := decodeMultilib(multilib, targets, prefer32)
+
+ multilib, extraMultilib := decodeMultilib(base, os.Class)
+ targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
if err != nil {
mctx.ModuleErrorf("%s", err.Error())
}
+
+ var multiTargets []Target
+ if extraMultilib != "" {
+ multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
+ if err != nil {
+ mctx.ModuleErrorf("%s", err.Error())
+ }
+ }
+
if len(targets) > 0 {
primaryModules[len(moduleTargets)] = true
+ moduleMultiTargets[len(moduleTargets)] = multiTargets
moduleTargets = append(moduleTargets, targets...)
}
}
if len(moduleTargets) == 0 {
- module.base().commonProperties.Enabled = boolPtr(false)
+ base.commonProperties.Enabled = boolPtr(false)
return
}
@@ -353,22 +794,41 @@
modules := mctx.CreateVariations(targetNames...)
for i, m := range modules {
- m.(Module).base().SetTarget(moduleTargets[i], primaryModules[i])
+ m.(Module).base().SetTarget(moduleTargets[i], moduleMultiTargets[i], primaryModules[i])
m.(Module).base().setArchProperties(mctx)
}
}
-func filterArchStruct(prop reflect.Type) (reflect.Type, bool) {
- var fields []reflect.StructField
-
- ptr := prop.Kind() == reflect.Ptr
- if ptr {
- prop = prop.Elem()
+func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib string) {
+ switch class {
+ case Device:
+ multilib = String(base.commonProperties.Target.Android.Compile_multilib)
+ case Host, HostCross:
+ multilib = String(base.commonProperties.Target.Host.Compile_multilib)
+ }
+ if multilib == "" {
+ multilib = String(base.commonProperties.Compile_multilib)
+ }
+ if multilib == "" {
+ multilib = base.commonProperties.Default_multilib
}
- for i := 0; i < prop.NumField(); i++ {
- field := prop.Field(i)
+ if base.commonProperties.UseTargetVariants {
+ return multilib, ""
+ } else {
+ // For app modules a single arch variant will be created per OS class which is expected to handle all the
+ // selected arches. Return the common-type as multilib and any Android.bp provided multilib as extraMultilib
+ if multilib == base.commonProperties.Default_multilib {
+ multilib = "first"
+ }
+ return base.commonProperties.Default_multilib, multilib
+ }
+}
+
+func filterArchStructFields(fields []reflect.StructField) (filteredFields []reflect.StructField, filtered bool) {
+ for _, field := range fields {
if !proptools.HasTag(field, "android", "arch_variant") {
+ filtered = true
continue
}
@@ -386,15 +846,17 @@
// Recurse into structs
switch field.Type.Kind() {
case reflect.Struct:
- var ok bool
- field.Type, ok = filterArchStruct(field.Type)
- if !ok {
+ var subFiltered bool
+ field.Type, subFiltered = filterArchStruct(field.Type)
+ filtered = filtered || subFiltered
+ if field.Type == nil {
continue
}
case reflect.Ptr:
if field.Type.Elem().Kind() == reflect.Struct {
- nestedType, ok := filterArchStruct(field.Type.Elem())
- if !ok {
+ nestedType, subFiltered := filterArchStruct(field.Type.Elem())
+ filtered = filtered || subFiltered
+ if nestedType == nil {
continue
}
field.Type = reflect.PtrTo(nestedType)
@@ -403,112 +865,205 @@
panic("Interfaces are not supported in arch_variant properties")
}
- fields = append(fields, field)
- }
- if len(fields) == 0 {
- return nil, false
+ filteredFields = append(filteredFields, field)
}
- ret := reflect.StructOf(fields)
+ return filteredFields, filtered
+}
+
+// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a reflect.Type
+// that only contains the fields in the original type that have an `android:"arch_variant"` struct tag, and a bool
+// that is true if the new struct type has fewer fields than the original type. If there are no fields in the
+// original type with the struct tag it returns nil and true.
+func filterArchStruct(prop reflect.Type) (filteredProp reflect.Type, filtered bool) {
+ var fields []reflect.StructField
+
+ ptr := prop.Kind() == reflect.Ptr
+ if ptr {
+ prop = prop.Elem()
+ }
+
+ for i := 0; i < prop.NumField(); i++ {
+ fields = append(fields, prop.Field(i))
+ }
+
+ filteredFields, filtered := filterArchStructFields(fields)
+
+ if len(filteredFields) == 0 {
+ return nil, true
+ }
+
+ if !filtered {
+ if ptr {
+ return reflect.PtrTo(prop), false
+ }
+ return prop, false
+ }
+
+ ret := reflect.StructOf(filteredFields)
if ptr {
ret = reflect.PtrTo(ret)
}
+
return ret, true
}
-func createArchType(props reflect.Type) reflect.Type {
- props, ok := filterArchStruct(props)
- if !ok {
+// filterArchStruct takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a list of
+// reflect.Type that only contains the fields in the original type that have an `android:"arch_variant"` struct tag,
+// and a bool that is true if the new struct type has fewer fields than the original type. If there are no fields in
+// the original type with the struct tag it returns nil and true. Each returned struct type will have a maximum of
+// 10 top level fields in it to attempt to avoid hitting the reflect.StructOf name length limit, although the limit
+// can still be reached with a single struct field with many fields in it.
+func filterArchStructSharded(prop reflect.Type) (filteredProp []reflect.Type, filtered bool) {
+ var fields []reflect.StructField
+
+ ptr := prop.Kind() == reflect.Ptr
+ if ptr {
+ prop = prop.Elem()
+ }
+
+ for i := 0; i < prop.NumField(); i++ {
+ fields = append(fields, prop.Field(i))
+ }
+
+ fields, filtered = filterArchStructFields(fields)
+ if !filtered {
+ if ptr {
+ return []reflect.Type{reflect.PtrTo(prop)}, false
+ }
+ return []reflect.Type{prop}, false
+ }
+
+ if len(fields) == 0 {
+ return nil, true
+ }
+
+ shards := shardFields(fields, 10)
+
+ for _, shard := range shards {
+ s := reflect.StructOf(shard)
+ if ptr {
+ s = reflect.PtrTo(s)
+ }
+ filteredProp = append(filteredProp, s)
+ }
+
+ return filteredProp, true
+}
+
+func shardFields(fields []reflect.StructField, shardSize int) [][]reflect.StructField {
+ ret := make([][]reflect.StructField, 0, (len(fields)+shardSize-1)/shardSize)
+ for len(fields) > shardSize {
+ ret = append(ret, fields[0:shardSize])
+ fields = fields[shardSize:]
+ }
+ if len(fields) > 0 {
+ ret = append(ret, fields)
+ }
+ return ret
+}
+
+// createArchType takes a reflect.Type that is either a struct or a pointer to a struct, and returns a list of
+// reflect.Type that contains the arch-variant properties inside structs for each architecture, os, target, multilib,
+// etc.
+func createArchType(props reflect.Type) []reflect.Type {
+ propShards, _ := filterArchStructSharded(props)
+ if len(propShards) == 0 {
return nil
}
- variantFields := func(names []string) []reflect.StructField {
- ret := make([]reflect.StructField, len(names))
+ var ret []reflect.Type
+ for _, props := range propShards {
- for i, name := range names {
- ret[i].Name = name
- ret[i].Type = props
- }
+ variantFields := func(names []string) []reflect.StructField {
+ ret := make([]reflect.StructField, len(names))
- return ret
- }
-
- archFields := make([]reflect.StructField, len(archTypeList))
- for i, arch := range archTypeList {
- variants := []string{}
-
- for _, archVariant := range archVariants[arch] {
- archVariant := variantReplacer.Replace(archVariant)
- variants = append(variants, proptools.FieldNameForProperty(archVariant))
- }
- for _, feature := range archFeatures[arch] {
- feature := variantReplacer.Replace(feature)
- variants = append(variants, proptools.FieldNameForProperty(feature))
- }
-
- fields := variantFields(variants)
-
- fields = append([]reflect.StructField{reflect.StructField{
- Name: "BlueprintEmbed",
- Type: props,
- Anonymous: true,
- }}, fields...)
-
- archFields[i] = reflect.StructField{
- Name: arch.Field,
- Type: reflect.StructOf(fields),
- }
- }
- archType := reflect.StructOf(archFields)
-
- multilibType := reflect.StructOf(variantFields([]string{"Lib32", "Lib64"}))
-
- targets := []string{
- "Host",
- "Android64",
- "Android32",
- "Bionic",
- "Linux",
- "Not_windows",
- "Arm_on_x86",
- "Arm_on_x86_64",
- }
- for _, os := range osTypeList {
- targets = append(targets, os.Field)
-
- for _, archType := range osArchTypeMap[os] {
- targets = append(targets, os.Field+"_"+archType.Name)
-
- if os.Linux() {
- target := "Linux_" + archType.Name
- if !InList(target, targets) {
- targets = append(targets, target)
- }
+ for i, name := range names {
+ ret[i].Name = name
+ ret[i].Type = props
}
- if os.Bionic() {
- target := "Bionic_" + archType.Name
- if !InList(target, targets) {
- targets = append(targets, target)
+
+ return ret
+ }
+
+ archFields := make([]reflect.StructField, len(archTypeList))
+ for i, arch := range archTypeList {
+ variants := []string{}
+
+ for _, archVariant := range archVariants[arch] {
+ archVariant := variantReplacer.Replace(archVariant)
+ variants = append(variants, proptools.FieldNameForProperty(archVariant))
+ }
+ for _, feature := range archFeatures[arch] {
+ feature := variantReplacer.Replace(feature)
+ variants = append(variants, proptools.FieldNameForProperty(feature))
+ }
+
+ fields := variantFields(variants)
+
+ fields = append([]reflect.StructField{{
+ Name: "BlueprintEmbed",
+ Type: props,
+ Anonymous: true,
+ }}, fields...)
+
+ archFields[i] = reflect.StructField{
+ Name: arch.Field,
+ Type: reflect.StructOf(fields),
+ }
+ }
+ archType := reflect.StructOf(archFields)
+
+ multilibType := reflect.StructOf(variantFields([]string{"Lib32", "Lib64"}))
+
+ targets := []string{
+ "Host",
+ "Android64",
+ "Android32",
+ "Bionic",
+ "Linux",
+ "Not_windows",
+ "Arm_on_x86",
+ "Arm_on_x86_64",
+ }
+ for _, os := range osTypeList {
+ targets = append(targets, os.Field)
+
+ for _, archType := range osArchTypeMap[os] {
+ targets = append(targets, os.Field+"_"+archType.Name)
+
+ if os.Linux() {
+ target := "Linux_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
+ if os.Bionic() {
+ target := "Bionic_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
}
}
}
- }
- targetType := reflect.StructOf(variantFields(targets))
- return reflect.StructOf([]reflect.StructField{
- reflect.StructField{
- Name: "Arch",
- Type: archType,
- },
- reflect.StructField{
- Name: "Multilib",
- Type: multilibType,
- },
- reflect.StructField{
- Name: "Target",
- Type: targetType,
- },
- })
+ targetType := reflect.StructOf(variantFields(targets))
+ ret = append(ret, reflect.StructOf([]reflect.StructField{
+ {
+ Name: "Arch",
+ Type: archType,
+ },
+ {
+ Name: "Multilib",
+ Type: multilibType,
+ },
+ {
+ Name: "Target",
+ Type: targetType,
+ },
+ }))
+ }
+ return ret
}
var archPropTypeMap OncePer
@@ -533,21 +1088,16 @@
propertiesValue.Interface()))
}
- archPropType := archPropTypeMap.Once(t, func() interface{} {
+ archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
return createArchType(t)
- })
+ }).([]reflect.Type)
- if archPropType != nil {
- base.archProperties = append(base.archProperties, reflect.New(archPropType.(reflect.Type)).Interface())
- } else {
- base.archProperties = append(base.archProperties, nil)
+ var archProperties []interface{}
+ for _, t := range archPropTypes {
+ archProperties = append(archProperties, reflect.New(t).Interface())
}
- }
-
- for _, asp := range base.archProperties {
- if asp != nil {
- m.AddProperties(asp)
- }
+ base.archProperties = append(base.archProperties, archProperties)
+ m.AddProperties(archProperties...)
}
base.customizableProperties = m.GetProperties()
@@ -602,203 +1152,205 @@
if a.archProperties[i] == nil {
continue
}
- archProps := reflect.ValueOf(a.archProperties[i]).Elem()
+ for _, archProperties := range a.archProperties[i] {
+ archPropValues := reflect.ValueOf(archProperties).Elem()
- archProp := archProps.FieldByName("Arch")
- multilibProp := archProps.FieldByName("Multilib")
- targetProp := archProps.FieldByName("Target")
+ archProp := archPropValues.FieldByName("Arch")
+ multilibProp := archPropValues.FieldByName("Multilib")
+ targetProp := archPropValues.FieldByName("Target")
- var field string
- var prefix string
+ var field string
+ var prefix string
- // Handle arch-specific properties in the form:
- // arch: {
- // arm64: {
- // key: value,
- // },
- // },
- t := arch.ArchType
-
- if arch.ArchType != Common {
- field := proptools.FieldNameForProperty(t.Name)
- prefix := "arch." + t.Name
- archStruct := a.appendProperties(ctx, genProps, archProp, field, prefix)
-
- // Handle arch-variant-specific properties in the form:
+ // Handle arch-specific properties in the form:
// arch: {
- // variant: {
+ // arm64: {
// key: value,
// },
// },
- v := variantReplacer.Replace(arch.ArchVariant)
- if v != "" {
- field := proptools.FieldNameForProperty(v)
- prefix := "arch." + t.Name + "." + v
- a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ t := arch.ArchType
+
+ if arch.ArchType != Common {
+ field := proptools.FieldNameForProperty(t.Name)
+ prefix := "arch." + t.Name
+ archStruct := a.appendProperties(ctx, genProps, archProp, field, prefix)
+
+ // Handle arch-variant-specific properties in the form:
+ // arch: {
+ // variant: {
+ // key: value,
+ // },
+ // },
+ v := variantReplacer.Replace(arch.ArchVariant)
+ if v != "" {
+ field := proptools.FieldNameForProperty(v)
+ prefix := "arch." + t.Name + "." + v
+ a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ }
+
+ // Handle cpu-variant-specific properties in the form:
+ // arch: {
+ // variant: {
+ // key: value,
+ // },
+ // },
+ if arch.CpuVariant != arch.ArchVariant {
+ c := variantReplacer.Replace(arch.CpuVariant)
+ if c != "" {
+ field := proptools.FieldNameForProperty(c)
+ prefix := "arch." + t.Name + "." + c
+ a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ }
+ }
+
+ // Handle arch-feature-specific properties in the form:
+ // arch: {
+ // feature: {
+ // key: value,
+ // },
+ // },
+ for _, feature := range arch.ArchFeatures {
+ field := proptools.FieldNameForProperty(feature)
+ prefix := "arch." + t.Name + "." + feature
+ a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ }
+
+ // Handle multilib-specific properties in the form:
+ // multilib: {
+ // lib32: {
+ // key: value,
+ // },
+ // },
+ field = proptools.FieldNameForProperty(t.Multilib)
+ prefix = "multilib." + t.Multilib
+ a.appendProperties(ctx, genProps, multilibProp, field, prefix)
}
- // Handle cpu-variant-specific properties in the form:
- // arch: {
- // variant: {
+ // Handle host-specific properties in the form:
+ // target: {
+ // host: {
// key: value,
// },
// },
- if arch.CpuVariant != arch.ArchVariant {
- c := variantReplacer.Replace(arch.CpuVariant)
- if c != "" {
- field := proptools.FieldNameForProperty(c)
- prefix := "arch." + t.Name + "." + c
- a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ if os.Class == Host || os.Class == HostCross {
+ field = "Host"
+ prefix = "target.host"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ }
+
+ // Handle target OS generalities of the form:
+ // target: {
+ // bionic: {
+ // key: value,
+ // },
+ // bionic_x86: {
+ // key: value,
+ // },
+ // }
+ if os.Linux() {
+ field = "Linux"
+ prefix = "target.linux"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+
+ if arch.ArchType != Common {
+ field = "Linux_" + arch.ArchType.Name
+ prefix = "target.linux_" + arch.ArchType.Name
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
}
}
- // Handle arch-feature-specific properties in the form:
- // arch: {
- // feature: {
+ if os.Bionic() {
+ field = "Bionic"
+ prefix = "target.bionic"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+
+ if arch.ArchType != Common {
+ field = "Bionic_" + t.Name
+ prefix = "target.bionic_" + t.Name
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ }
+ }
+
+ // Handle target OS properties in the form:
+ // target: {
+ // linux_glibc: {
+ // key: value,
+ // },
+ // not_windows: {
+ // key: value,
+ // },
+ // linux_glibc_x86: {
+ // key: value,
+ // },
+ // linux_glibc_arm: {
+ // key: value,
+ // },
+ // android {
+ // key: value,
+ // },
+ // android_arm {
+ // key: value,
+ // },
+ // android_x86 {
// key: value,
// },
// },
- for _, feature := range arch.ArchFeatures {
- field := proptools.FieldNameForProperty(feature)
- prefix := "arch." + t.Name + "." + feature
- a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ field = os.Field
+ prefix = "target." + os.Name
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+
+ if arch.ArchType != Common {
+ field = os.Field + "_" + t.Name
+ prefix = "target." + os.Name + "_" + t.Name
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
}
- // Handle multilib-specific properties in the form:
- // multilib: {
- // lib32: {
+ if (os.Class == Host || os.Class == HostCross) && os != Windows {
+ field := "Not_windows"
+ prefix := "target.not_windows"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ }
+
+ // Handle 64-bit device properties in the form:
+ // target {
+ // android64 {
+ // key: value,
+ // },
+ // android32 {
// key: value,
// },
// },
- field = proptools.FieldNameForProperty(t.Multilib)
- prefix = "multilib." + t.Multilib
- a.appendProperties(ctx, genProps, multilibProp, field, prefix)
- }
+ // WARNING: this is probably not what you want to use in your blueprints file, it selects
+ // options for all targets on a device that supports 64-bit binaries, not just the targets
+ // that are being compiled for 64-bit. Its expected use case is binaries like linker and
+ // debuggerd that need to know when they are a 32-bit process running on a 64-bit device
+ if os.Class == Device {
+ if ctx.Config().Android64() {
+ field := "Android64"
+ prefix := "target.android64"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ } else {
+ field := "Android32"
+ prefix := "target.android32"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ }
- // Handle host-specific properties in the form:
- // target: {
- // host: {
- // key: value,
- // },
- // },
- if os.Class == Host || os.Class == HostCross {
- field = "Host"
- prefix = "target.host"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- }
-
- // Handle target OS generalities of the form:
- // target: {
- // bionic: {
- // key: value,
- // },
- // bionic_x86: {
- // key: value,
- // },
- // }
- if os.Linux() {
- field = "Linux"
- prefix = "target.linux"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
-
- if arch.ArchType != Common {
- field = "Linux_" + arch.ArchType.Name
- prefix = "target.linux_" + arch.ArchType.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- }
- }
-
- if os.Bionic() {
- field = "Bionic"
- prefix = "target.bionic"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
-
- if arch.ArchType != Common {
- field = "Bionic_" + t.Name
- prefix = "target.bionic_" + t.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- }
- }
-
- // Handle target OS properties in the form:
- // target: {
- // linux_glibc: {
- // key: value,
- // },
- // not_windows: {
- // key: value,
- // },
- // linux_glibc_x86: {
- // key: value,
- // },
- // linux_glibc_arm: {
- // key: value,
- // },
- // android {
- // key: value,
- // },
- // android_arm {
- // key: value,
- // },
- // android_x86 {
- // key: value,
- // },
- // },
- field = os.Field
- prefix = "target." + os.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
-
- if arch.ArchType != Common {
- field = os.Field + "_" + t.Name
- prefix = "target." + os.Name + "_" + t.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- }
-
- if (os.Class == Host || os.Class == HostCross) && os != Windows {
- field := "Not_windows"
- prefix := "target.not_windows"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- }
-
- // Handle 64-bit device properties in the form:
- // target {
- // android64 {
- // key: value,
- // },
- // android32 {
- // key: value,
- // },
- // },
- // WARNING: this is probably not what you want to use in your blueprints file, it selects
- // options for all targets on a device that supports 64-bit binaries, not just the targets
- // that are being compiled for 64-bit. Its expected use case is binaries like linker and
- // debuggerd that need to know when they are a 32-bit process running on a 64-bit device
- if os.Class == Device {
- if ctx.Config().Android64() {
- field := "Android64"
- prefix := "target.android64"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- } else {
- field := "Android32"
- prefix := "target.android32"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- }
-
- if (arch.ArchType == X86 && (hasArmAbi(arch) ||
- hasArmAndroidArch(ctx.Config().Targets[Device]))) ||
- (arch.ArchType == Arm &&
- hasX86AndroidArch(ctx.Config().Targets[Device])) {
- field := "Arm_on_x86"
- prefix := "target.arm_on_x86"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
- }
- if (arch.ArchType == X86_64 && (hasArmAbi(arch) ||
- hasArmAndroidArch(ctx.Config().Targets[Device]))) ||
- (arch.ArchType == Arm &&
- hasX8664AndroidArch(ctx.Config().Targets[Device])) {
- field := "Arm_on_x86_64"
- prefix := "target.arm_on_x86_64"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ if (arch.ArchType == X86 && (hasArmAbi(arch) ||
+ hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
+ (arch.ArchType == Arm &&
+ hasX86AndroidArch(ctx.Config().Targets[Android])) {
+ field := "Arm_on_x86"
+ prefix := "target.arm_on_x86"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ }
+ if (arch.ArchType == X86_64 && (hasArmAbi(arch) ||
+ hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
+ (arch.ArchType == Arm &&
+ hasX8664AndroidArch(ctx.Config().Targets[Android])) {
+ field := "Arm_on_x86_64"
+ prefix := "target.arm_on_x86_64"
+ a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ }
}
}
}
@@ -820,24 +1372,24 @@
}
// Convert the arch product variables into a list of targets for each os class structs
-func decodeTargetProductVariables(config *config) (map[OsClass][]Target, error) {
+func decodeTargetProductVariables(config *config) (map[OsType][]Target, error) {
variables := config.productVariables
- targets := make(map[OsClass][]Target)
+ targets := make(map[OsType][]Target)
var targetErr error
- addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi *[]string) {
+ addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi []string) {
if targetErr != nil {
return
}
- arch, err := decodeArch(archName, archVariant, cpuVariant, abi)
+ arch, err := decodeArch(os, archName, archVariant, cpuVariant, abi)
if err != nil {
targetErr = err
return
}
- targets[os.Class] = append(targets[os.Class],
+ targets[os] = append(targets[os],
Target{
Os: os,
Arch: arch,
@@ -854,17 +1406,17 @@
addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil)
}
- if config.Host_bionic != nil && *config.Host_bionic {
+ if Bool(config.Host_bionic) {
addTarget(LinuxBionic, "x86_64", nil, nil, nil)
}
- if variables.CrossHost != nil && *variables.CrossHost != "" {
+ if String(variables.CrossHost) != "" {
crossHostOs := osByName(*variables.CrossHost)
if crossHostOs == NoOsType {
return nil, fmt.Errorf("Unknown cross host OS %q", *variables.CrossHost)
}
- if variables.CrossHostArch == nil || *variables.CrossHostArch == "" {
+ if String(variables.CrossHostArch) == "" {
return nil, fmt.Errorf("No cross-host primary architecture set")
}
@@ -876,7 +1428,12 @@
}
if variables.DeviceArch != nil && *variables.DeviceArch != "" {
- addTarget(Android, *variables.DeviceArch, variables.DeviceArchVariant,
+ var target = Android
+ if Bool(variables.Fuchsia) {
+ target = Fuchsia
+ }
+
+ addTarget(target, *variables.DeviceArch, variables.DeviceArchVariant,
variables.DeviceCpuVariant, variables.DeviceAbi)
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
@@ -884,7 +1441,7 @@
variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
variables.DeviceSecondaryAbi)
- deviceArches := targets[Device]
+ deviceArches := targets[Android]
if deviceArches[0].Arch.ArchType.Multilib == deviceArches[1].Arch.ArchType.Multilib {
deviceArches[1].Arch.Native = false
}
@@ -955,20 +1512,24 @@
{"arm", "armv7-a-neon", "cortex-a15", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a72", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "cortex-a73", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "cortex-a75", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "denver", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a76", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "kryo385", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "exynos-m1", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "exynos-m2", []string{"armeabi-v7a"}},
{"arm64", "armv8-a", "cortex-a53", []string{"arm64-v8a"}},
+ {"arm64", "armv8-a", "cortex-a72", []string{"arm64-v8a"}},
{"arm64", "armv8-a", "cortex-a73", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "denver64", []string{"arm64-v8a"}},
{"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
{"arm64", "armv8-a", "exynos-m1", []string{"arm64-v8a"}},
{"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
{"arm64", "armv8-2a", "cortex-a75", []string{"arm64-v8a"}},
+ {"arm64", "armv8-2a", "cortex-a76", []string{"arm64-v8a"}},
+ {"arm64", "armv8-2a", "kryo385", []string{"arm64-v8a"}},
{"mips", "mips32-fp", "", []string{"mips"}},
{"mips", "mips32r2-fp", "", []string{"mips"}},
{"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
@@ -984,12 +1545,14 @@
{"x86", "ivybridge", "", []string{"x86"}},
{"x86", "sandybridge", "", []string{"x86"}},
{"x86", "silvermont", "", []string{"x86"}},
+ {"x86", "stoneyridge", "", []string{"x86"}},
{"x86", "x86_64", "", []string{"x86"}},
{"x86_64", "", "", []string{"x86_64"}},
{"x86_64", "haswell", "", []string{"x86_64"}},
{"x86_64", "ivybridge", "", []string{"x86_64"}},
{"x86_64", "sandybridge", "", []string{"x86_64"}},
{"x86_64", "silvermont", "", []string{"x86_64"}},
+ {"x86_64", "stoneyridge", "", []string{"x86_64"}},
}
}
@@ -1002,12 +1565,12 @@
}
}
-func decodeArchSettings(archConfigs []archConfig) ([]Target, error) {
+func decodeArchSettings(os OsType, archConfigs []archConfig) ([]Target, error) {
var ret []Target
for _, config := range archConfigs {
- arch, err := decodeArch(config.arch, &config.archVariant,
- &config.cpuVariant, &config.abi)
+ arch, err := decodeArch(os, config.arch, &config.archVariant,
+ &config.cpuVariant, config.abi)
if err != nil {
return nil, err
}
@@ -1022,7 +1585,7 @@
}
// Convert a set of strings from product variables into a single Arch struct
-func decodeArch(arch string, archVariant, cpuVariant *string, abi *[]string) (Arch, error) {
+func decodeArch(os OsType, arch string, archVariant, cpuVariant *string, abi []string) (Arch, error) {
stringPtr := func(p *string) string {
if p != nil {
return *p
@@ -1030,13 +1593,6 @@
return ""
}
- slicePtr := func(p *[]string) []string {
- if p != nil {
- return *p
- }
- return nil
- }
-
archType, ok := archTypeMap[arch]
if !ok {
return Arch{}, fmt.Errorf("unknown arch %q", arch)
@@ -1046,7 +1602,7 @@
ArchType: archType,
ArchVariant: stringPtr(archVariant),
CpuVariant: stringPtr(cpuVariant),
- Abi: slicePtr(abi),
+ Abi: abi,
Native: true,
}
@@ -1065,8 +1621,14 @@
}
}
- if featureMap, ok := archFeatureMap[archType]; ok {
- a.ArchFeatures = featureMap[a.ArchVariant]
+ if a.ArchVariant == "" {
+ if featureMap, ok := defaultArchFeatureMap[os]; ok {
+ a.ArchFeatures = featureMap[archType]
+ }
+ } else {
+ if featureMap, ok := archFeatureMap[archType]; ok {
+ a.ArchFeatures = featureMap[a.ArchVariant]
+ }
}
return a, nil
@@ -1096,18 +1658,18 @@
return ret
}
-func preferTargets(targets []Target, filters ...string) []Target {
+func firstTarget(targets []Target, filters ...string) []Target {
for _, filter := range filters {
buildTargets := filterMultilibTargets(targets, filter)
if len(buildTargets) > 0 {
- return buildTargets
+ return buildTargets[:1]
}
}
return nil
}
// Use the module multilib setting to select one or more targets from a target list
-func decodeMultilib(multilib string, targets []Target, prefer32 bool) ([]Target, error) {
+func decodeMultilibTargets(multilib string, targets []Target, prefer32 bool) ([]Target, error) {
buildTargets := []Target{}
switch multilib {
@@ -1116,9 +1678,9 @@
case "common_first":
buildTargets = getCommonTargets(targets)
if prefer32 {
- buildTargets = append(buildTargets, preferTargets(targets, "lib32", "lib64")...)
+ buildTargets = append(buildTargets, firstTarget(targets, "lib32", "lib64")...)
} else {
- buildTargets = append(buildTargets, preferTargets(targets, "lib64", "lib32")...)
+ buildTargets = append(buildTargets, firstTarget(targets, "lib64", "lib32")...)
}
case "both":
if prefer32 {
@@ -1134,12 +1696,15 @@
buildTargets = filterMultilibTargets(targets, "lib64")
case "first":
if prefer32 {
- buildTargets = preferTargets(targets, "lib32", "lib64")
+ buildTargets = firstTarget(targets, "lib32", "lib64")
} else {
- buildTargets = preferTargets(targets, "lib64", "lib32")
+ buildTargets = firstTarget(targets, "lib64", "lib32")
}
case "prefer32":
- buildTargets = preferTargets(targets, "lib32", "lib64")
+ buildTargets = filterMultilibTargets(targets, "lib32")
+ if len(buildTargets) == 0 {
+ buildTargets = filterMultilibTargets(targets, "lib64")
+ }
default:
return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", or "prefer32" found %q`,
multilib)
diff --git a/android/arch_test.go b/android/arch_test.go
new file mode 100644
index 0000000..0589e6c
--- /dev/null
+++ b/android/arch_test.go
@@ -0,0 +1,232 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "reflect"
+ "testing"
+)
+
+type Named struct {
+ A *string `android:"arch_variant"`
+ B *string
+}
+
+type NamedAllFiltered struct {
+ A *string
+}
+
+type NamedNoneFiltered struct {
+ A *string `android:"arch_variant"`
+}
+
+func TestFilterArchStruct(t *testing.T) {
+ tests := []struct {
+ name string
+ in interface{}
+ out interface{}
+ filtered bool
+ }{
+ // Property tests
+ {
+ name: "basic",
+ in: &struct {
+ A *string `android:"arch_variant"`
+ B *string
+ }{},
+ out: &struct {
+ A *string
+ }{},
+ filtered: true,
+ },
+ {
+ name: "all filtered",
+ in: &struct {
+ A *string
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "none filtered",
+ in: &struct {
+ A *string `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A *string `android:"arch_variant"`
+ }{},
+ filtered: false,
+ },
+
+ // Sub-struct tests
+ {
+ name: "substruct",
+ in: &struct {
+ A struct {
+ A *string `android:"arch_variant"`
+ B *string
+ } `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "substruct all filtered",
+ in: &struct {
+ A struct {
+ A *string
+ } `android:"arch_variant"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "substruct none filtered",
+ in: &struct {
+ A struct {
+ A *string `android:"arch_variant"`
+ } `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A struct {
+ A *string `android:"arch_variant"`
+ } `android:"arch_variant"`
+ }{},
+ filtered: false,
+ },
+
+ // Named sub-struct tests
+ {
+ name: "named substruct",
+ in: &struct {
+ A Named `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "substruct all filtered",
+ in: &struct {
+ A NamedAllFiltered `android:"arch_variant"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "substruct none filtered",
+ in: &struct {
+ A NamedNoneFiltered `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A NamedNoneFiltered `android:"arch_variant"`
+ }{},
+ filtered: false,
+ },
+
+ // Pointer to sub-struct tests
+ {
+ name: "pointer substruct",
+ in: &struct {
+ A *struct {
+ A *string `android:"arch_variant"`
+ B *string
+ } `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A *struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "pointer substruct all filtered",
+ in: &struct {
+ A *struct {
+ A *string
+ } `android:"arch_variant"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "pointer substruct none filtered",
+ in: &struct {
+ A *struct {
+ A *string `android:"arch_variant"`
+ } `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A *struct {
+ A *string `android:"arch_variant"`
+ } `android:"arch_variant"`
+ }{},
+ filtered: false,
+ },
+
+ // Pointer to named sub-struct tests
+ {
+ name: "pointer named substruct",
+ in: &struct {
+ A *Named `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A *struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "pointer substruct all filtered",
+ in: &struct {
+ A *NamedAllFiltered `android:"arch_variant"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "pointer substruct none filtered",
+ in: &struct {
+ A *NamedNoneFiltered `android:"arch_variant"`
+ }{},
+ out: &struct {
+ A *NamedNoneFiltered `android:"arch_variant"`
+ }{},
+ filtered: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ out, filtered := filterArchStruct(reflect.TypeOf(test.in))
+ if filtered != test.filtered {
+ t.Errorf("expected filtered %v, got %v", test.filtered, filtered)
+ }
+ expected := reflect.TypeOf(test.out)
+ if out != expected {
+ t.Errorf("expected type %v, got %v", expected, out)
+ }
+ })
+ }
+}
diff --git a/android/config.go b/android/config.go
index fafed6b..2e0c247 100644
--- a/android/config.go
+++ b/android/config.go
@@ -41,7 +41,6 @@
// config file. These will be included in the config struct.
type FileConfigurableOptions struct {
Mega_device *bool `json:",omitempty"`
- Ndk_abis *bool `json:",omitempty"`
Host_bionic *bool `json:",omitempty"`
}
@@ -90,8 +89,9 @@
ConfigFileName string
ProductVariablesFileName string
- Targets map[OsClass][]Target
- BuildOsVariant string
+ Targets map[OsType][]Target
+ BuildOsVariant string
+ BuildOsCommonVariant string
deviceConfig *deviceConfig
@@ -108,8 +108,7 @@
captureBuild bool // true for tests, saves build parameters for each module
ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
- useOpenJDK9 bool // Use OpenJDK9, but possibly target 1.8
- targetOpenJDK9 bool // Use OpenJDK9 and target 1.9
+ targetOpenJDK9 bool // Target 1.9
stopBefore bootstrap.StopBefore
@@ -201,12 +200,15 @@
func TestConfig(buildDir string, env map[string]string) Config {
config := &config{
productVariables: productVariables{
- DeviceName: stringPtr("test_device"),
- Platform_sdk_version: intPtr(26),
- AAPTConfig: &[]string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
- AAPTPreferredConfig: stringPtr("xhdpi"),
- AAPTCharacteristics: stringPtr("nosdcard"),
- AAPTPrebuiltDPI: &[]string{"xhdpi", "xxhdpi"},
+ DeviceName: stringPtr("test_device"),
+ Platform_sdk_version: intPtr(26),
+ DeviceSystemSdkVersions: []string{"14", "15"},
+ Platform_systemsdk_versions: []string{"25", "26"},
+ AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+ AAPTPreferredConfig: stringPtr("xhdpi"),
+ AAPTCharacteristics: stringPtr("nosdcard"),
+ AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
+ UncompressPrivAppDex: boolPtr(true),
},
buildDir: buildDir,
@@ -225,22 +227,41 @@
return Config{config}
}
+func TestArchConfigFuchsia(buildDir string, env map[string]string) Config {
+ testConfig := TestConfig(buildDir, env)
+ config := testConfig.config
+
+ config.Targets = map[OsType][]Target{
+ Fuchsia: []Target{
+ {Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Native: true}},
+ },
+ BuildOs: []Target{
+ {BuildOs, Arch{ArchType: X86_64}},
+ },
+ }
+
+ return testConfig
+}
+
// TestConfig returns a Config object suitable for using for tests that need to run the arch mutator
func TestArchConfig(buildDir string, env map[string]string) Config {
testConfig := TestConfig(buildDir, env)
config := testConfig.config
- config.Targets = map[OsClass][]Target{
- Device: []Target{
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true}},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true}},
+ config.Targets = map[OsType][]Target{
+ Android: []Target{
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}},
},
- Host: []Target{
+ BuildOs: []Target{
{BuildOs, Arch{ArchType: X86_64}},
{BuildOs, Arch{ArchType: X86}},
},
}
+ config.BuildOsVariant = config.Targets[BuildOs][0].String()
+ config.BuildOsCommonVariant = getCommonTargets(config.Targets[BuildOs])[0].String()
+
return testConfig
}
@@ -297,20 +318,21 @@
var archConfig []archConfig
if Bool(config.Mega_device) {
archConfig = getMegaDeviceConfig()
- } else if Bool(config.Ndk_abis) {
+ } else if config.NdkAbis() {
archConfig = getNdkAbisConfig()
}
if archConfig != nil {
- deviceTargets, err := decodeArchSettings(archConfig)
+ androidTargets, err := decodeArchSettings(Android, archConfig)
if err != nil {
return Config{}, err
}
- targets[Device] = deviceTargets
+ targets[Android] = androidTargets
}
config.Targets = targets
- config.BuildOsVariant = targets[Host][0].String()
+ config.BuildOsVariant = targets[BuildOs][0].String()
+ config.BuildOsCommonVariant = getCommonTargets(targets[BuildOs])[0].String()
if err := config.fromEnv(); err != nil {
return Config{}, err
@@ -321,22 +343,13 @@
func (c *config) fromEnv() error {
switch c.Getenv("EXPERIMENTAL_USE_OPENJDK9") {
- case "":
- if c.Getenv("RUN_ERROR_PRONE") != "true" {
- // Use OpenJDK9, but target 1.8
- c.useOpenJDK9 = true
- }
- case "false":
- // Use OpenJDK8
- case "1.8":
- // Use OpenJDK9, but target 1.8
- c.useOpenJDK9 = true
+ case "", "1.8":
+ // Nothing, we always use OpenJDK9
case "true":
// Use OpenJDK9 and target 1.9
- c.useOpenJDK9 = true
c.targetOpenJDK9 = true
default:
- return fmt.Errorf(`Invalid value for EXPERIMENTAL_USE_OPENJDK9, should be "", "false", "1.8", or "true"`)
+ return fmt.Errorf(`Invalid value for EXPERIMENTAL_USE_OPENJDK9, should be "", "1.8", or "true"`)
}
return nil
@@ -358,6 +371,10 @@
var _ bootstrap.ConfigBlueprintToolLocation = (*config)(nil)
+func (c *config) HostToolPath(ctx PathContext, tool string) Path {
+ return PathForOutput(ctx, "host", c.PrebuiltOS(), "bin", tool)
+}
+
// HostSystemTool looks for non-hermetic tools from the system we're running on.
// Generally shouldn't be used, but useful to find the XCode SDK, etc.
func (c *config) HostSystemTool(name string) string {
@@ -461,11 +478,12 @@
return *c.productVariables.DeviceName
}
-func (c *config) ResourceOverlays() []string {
- if c.productVariables.ResourceOverlays == nil {
- return nil
- }
- return *c.productVariables.ResourceOverlays
+func (c *config) DeviceResourceOverlays() []string {
+ return c.productVariables.DeviceResourceOverlays
+}
+
+func (c *config) ProductResourceOverlays() []string {
+ return c.productVariables.ProductResourceOverlays
}
func (c *config) PlatformVersionName() string {
@@ -484,8 +502,24 @@
return String(c.productVariables.Platform_sdk_codename)
}
+func (c *config) PlatformSecurityPatch() string {
+ return String(c.productVariables.Platform_security_patch)
+}
+
+func (c *config) PlatformPreviewSdkVersion() string {
+ return String(c.productVariables.Platform_preview_sdk_version)
+}
+
+func (c *config) PlatformMinSupportedTargetSdkVersion() string {
+ return String(c.productVariables.Platform_min_supported_target_sdk_version)
+}
+
+func (c *config) PlatformBaseOS() string {
+ return String(c.productVariables.Platform_base_os)
+}
+
func (c *config) MinSupportedSdkVersion() int {
- return 14
+ return 16
}
func (c *config) DefaultAppTargetSdkInt() int {
@@ -530,7 +564,7 @@
}
func (c *config) ProductAAPTConfig() []string {
- return stringSlice(c.productVariables.AAPTConfig)
+ return c.productVariables.AAPTConfig
}
func (c *config) ProductAAPTPreferredConfig() string {
@@ -542,7 +576,7 @@
}
func (c *config) ProductAAPTPrebuiltDPI() []string {
- return stringSlice(c.productVariables.AAPTPrebuiltDPI)
+ return c.productVariables.AAPTPrebuiltDPI
}
func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath {
@@ -564,6 +598,19 @@
}
}
+func (c *config) ApexKeyDir(ctx ModuleContext) SourcePath {
+ // TODO(b/121224311): define another variable such as TARGET_APEX_KEY_OVERRIDE
+ defaultCert := String(c.productVariables.DefaultAppCertificate)
+ if defaultCert == "" || filepath.Dir(defaultCert) == "build/target/product/security" {
+ // When defaultCert is unset or is set to the testkeys path, use the APEX keys
+ // that is under the module dir
+ return pathForModuleSrc(ctx)
+ } else {
+ // If not, APEX keys are under the specified directory
+ return PathForSource(ctx, filepath.Dir(defaultCert))
+ }
+}
+
func (c *config) AllowMissingDependencies() bool {
return Bool(c.productVariables.Allow_missing_dependencies)
}
@@ -572,6 +619,14 @@
return Bool(c.productVariables.Unbundled_build)
}
+func (c *config) UnbundledBuildUsePrebuiltSdks() bool {
+ return Bool(c.productVariables.Unbundled_build) && !Bool(c.productVariables.Unbundled_build_sdks_from_source)
+}
+
+func (c *config) Fuchsia() bool {
+ return Bool(c.productVariables.Fuchsia)
+}
+
func (c *config) IsPdkBuild() bool {
return Bool(c.productVariables.Pdk)
}
@@ -580,10 +635,26 @@
return Bool(c.productVariables.MinimizeJavaDebugInfo) && !Bool(c.productVariables.Eng)
}
+func (c *config) Debuggable() bool {
+ return Bool(c.productVariables.Debuggable)
+}
+
+func (c *config) Eng() bool {
+ return Bool(c.productVariables.Eng)
+}
+
+func (c *config) DevicePrefer32BitApps() bool {
+ return Bool(c.productVariables.DevicePrefer32BitApps)
+}
+
func (c *config) DevicePrefer32BitExecutables() bool {
return Bool(c.productVariables.DevicePrefer32BitExecutables)
}
+func (c *config) DevicePrimaryArchType() ArchType {
+ return c.Targets[Android][0].Arch.ArchType
+}
+
func (c *config) SkipDeviceInstall() bool {
return c.EmbeddedInMake()
}
@@ -617,8 +688,20 @@
}
}
+func (c *config) DisableScudo() bool {
+ return Bool(c.productVariables.DisableScudo)
+}
+
+func (c *config) EnableXOM() bool {
+ if c.productVariables.EnableXOM == nil {
+ return true
+ } else {
+ return Bool(c.productVariables.EnableXOM)
+ }
+}
+
func (c *config) Android64() bool {
- for _, t := range c.Targets[Device] {
+ for _, t := range c.Targets[Android] {
if t.Arch.ArchType.Multilib == "lib64" {
return true
}
@@ -627,17 +710,32 @@
return false
}
-func (c *config) UseD8Desugar() bool {
- return !c.IsEnvFalse("USE_D8_DESUGAR")
-}
-
func (c *config) UseGoma() bool {
return Bool(c.productVariables.UseGoma)
}
-// Returns true if OpenJDK9 prebuilts are being used
-func (c *config) UseOpenJDK9() bool {
- return c.useOpenJDK9
+func (c *config) UseRBE() bool {
+ return Bool(c.productVariables.UseRBE)
+}
+
+func (c *config) UseRBEJAVAC() bool {
+ return Bool(c.productVariables.UseRBEJAVAC)
+}
+
+func (c *config) UseRBER8() bool {
+ return Bool(c.productVariables.UseRBER8)
+}
+
+func (c *config) UseRBED8() bool {
+ return Bool(c.productVariables.UseRBED8)
+}
+
+func (c *config) UseRemoteBuild() bool {
+ return c.UseGoma() || c.UseRBE()
+}
+
+func (c *config) RunErrorProne() bool {
+ return c.IsEnvTrue("RUN_ERROR_PRONE")
}
// Returns true if -source 1.9 -target 1.9 is being passed to javac
@@ -662,8 +760,8 @@
func (c *config) LibartImgDeviceBaseAddress() string {
archType := Common
- if len(c.Targets[Device]) > 0 {
- archType = c.Targets[Device][0].Arch.ArchType
+ if len(c.Targets[Android]) > 0 {
+ archType = c.Targets[Android][0].Arch.ArchType
}
switch archType {
default:
@@ -680,10 +778,10 @@
func (c *config) EnforceRROForModule(name string) bool {
enforceList := c.productVariables.EnforceRROTargets
if enforceList != nil {
- if len(*enforceList) == 1 && (*enforceList)[0] == "*" {
+ if len(enforceList) == 1 && (enforceList)[0] == "*" {
return true
}
- return InList(name, *enforceList)
+ return InList(name, enforceList)
}
return false
}
@@ -691,7 +789,7 @@
func (c *config) EnforceRROExcludedOverlay(path string) bool {
excluded := c.productVariables.EnforceRROExcludedOverlays
if excluded != nil {
- for _, exclude := range *excluded {
+ for _, exclude := range excluded {
if strings.HasPrefix(path, exclude) {
return true
}
@@ -708,9 +806,29 @@
return Bool(c.productVariables.HostStaticBinaries)
}
+func (c *config) UncompressPrivAppDex() bool {
+ return Bool(c.productVariables.UncompressPrivAppDex)
+}
+
+func (c *config) ModulesLoadedByPrivilegedModules() []string {
+ return c.productVariables.ModulesLoadedByPrivilegedModules
+}
+
+func (c *config) BootJars() []string {
+ return c.productVariables.BootJars
+}
+
+func (c *config) DexpreoptGlobalConfig() string {
+ return String(c.productVariables.DexpreoptGlobalConfig)
+}
+
+func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
+ return ExistentPathForSource(ctx, "frameworks", "base").Valid()
+}
+
func (c *deviceConfig) Arches() []Arch {
var arches []Arch
- for _, target := range c.config.Targets[Device] {
+ for _, target := range c.config.Targets[Android] {
arches = append(arches, target.Arch)
}
return arches
@@ -743,11 +861,12 @@
return c.config.productVariables.ExtraVndkVersions
}
+func (c *deviceConfig) VndkUseCoreVariant() bool {
+ return Bool(c.config.productVariables.VndkUseCoreVariant)
+}
+
func (c *deviceConfig) SystemSdkVersions() []string {
- if c.config.productVariables.DeviceSystemSdkVersions == nil {
- return nil
- }
- return *c.config.productVariables.DeviceSystemSdkVersions
+ return c.config.productVariables.DeviceSystemSdkVersions
}
func (c *deviceConfig) PlatformSystemSdkVersions() []string {
@@ -768,6 +887,13 @@
return "product"
}
+func (c *deviceConfig) ProductServicesPath() string {
+ if c.config.productVariables.ProductServicesPath != nil {
+ return *c.config.productVariables.ProductServicesPath
+ }
+ return "product_services"
+}
+
func (c *deviceConfig) BtConfigIncludeDir() string {
return String(c.config.productVariables.BtConfigIncludeDir)
}
@@ -783,12 +909,12 @@
func (c *deviceConfig) CoverageEnabledForPath(path string) bool {
coverage := false
if c.config.productVariables.CoveragePaths != nil {
- if PrefixInList(path, *c.config.productVariables.CoveragePaths) {
+ if InList("*", c.config.productVariables.CoveragePaths) || PrefixInList(path, c.config.productVariables.CoveragePaths) {
coverage = true
}
}
if coverage && c.config.productVariables.CoverageExcludePaths != nil {
- if PrefixInList(path, *c.config.productVariables.CoverageExcludePaths) {
+ if PrefixInList(path, c.config.productVariables.CoverageExcludePaths) {
coverage = false
}
}
@@ -799,25 +925,99 @@
return c.config.productVariables.PgoAdditionalProfileDirs
}
+func (c *deviceConfig) VendorSepolicyDirs() []string {
+ return c.config.productVariables.BoardVendorSepolicyDirs
+}
+
+func (c *deviceConfig) OdmSepolicyDirs() []string {
+ return c.config.productVariables.BoardOdmSepolicyDirs
+}
+
+func (c *deviceConfig) PlatPublicSepolicyDirs() []string {
+ return c.config.productVariables.BoardPlatPublicSepolicyDirs
+}
+
+func (c *deviceConfig) PlatPrivateSepolicyDirs() []string {
+ return c.config.productVariables.BoardPlatPrivateSepolicyDirs
+}
+
+func (c *deviceConfig) OverrideManifestPackageNameFor(name string) (manifestName string, overridden bool) {
+ return findOverrideValue(c.config.productVariables.ManifestPackageNameOverrides, name,
+ "invalid override rule %q in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES should be <module_name>:<manifest_name>")
+}
+
+func (c *deviceConfig) OverrideCertificateFor(name string) (certificatePath string, overridden bool) {
+ return findOverrideValue(c.config.productVariables.CertificateOverrides, name,
+ "invalid override rule %q in PRODUCT_CERTIFICATE_OVERRIDES should be <module_name>:<certificate_module_name>")
+}
+
+func (c *deviceConfig) OverridePackageNameFor(name string) string {
+ newName, overridden := findOverrideValue(
+ c.config.productVariables.PackageNameOverrides,
+ name,
+ "invalid override rule %q in PRODUCT_PACKAGE_NAME_OVERRIDES should be <module_name>:<package_name>")
+ if overridden {
+ return newName
+ }
+ return name
+}
+
+func findOverrideValue(overrides []string, name string, errorMsg string) (newValue string, overridden bool) {
+ if overrides == nil || len(overrides) == 0 {
+ return "", false
+ }
+ for _, o := range overrides {
+ split := strings.Split(o, ":")
+ if len(split) != 2 {
+ // This shouldn't happen as this is first checked in make, but just in case.
+ panic(fmt.Errorf(errorMsg, o))
+ }
+ if matchPattern(split[0], name) {
+ return substPattern(split[0], split[1], name), true
+ }
+ }
+ return "", false
+}
+
+// SecondArchIsTranslated returns true if the primary device arch is X86 or X86_64 and the device also has an arch
+// that is Arm or Arm64.
+func (c *config) SecondArchIsTranslated() bool {
+ deviceTargets := c.Targets[Android]
+ if len(deviceTargets) < 2 {
+ return false
+ }
+
+ arch := deviceTargets[0].Arch
+
+ return (arch.ArchType == X86 || arch.ArchType == X86_64) && hasArmAndroidArch(deviceTargets)
+}
+
func (c *config) IntegerOverflowDisabledForPath(path string) bool {
if c.productVariables.IntegerOverflowExcludePaths == nil {
return false
}
- return PrefixInList(path, *c.productVariables.IntegerOverflowExcludePaths)
+ return PrefixInList(path, c.productVariables.IntegerOverflowExcludePaths)
}
func (c *config) CFIDisabledForPath(path string) bool {
if c.productVariables.CFIExcludePaths == nil {
return false
}
- return PrefixInList(path, *c.productVariables.CFIExcludePaths)
+ return PrefixInList(path, c.productVariables.CFIExcludePaths)
}
func (c *config) CFIEnabledForPath(path string) bool {
if c.productVariables.CFIIncludePaths == nil {
return false
}
- return PrefixInList(path, *c.productVariables.CFIIncludePaths)
+ return PrefixInList(path, c.productVariables.CFIIncludePaths)
+}
+
+func (c *config) XOMDisabledForPath(path string) bool {
+ if c.productVariables.XOMExcludePaths == nil {
+ return false
+ }
+ return PrefixInList(path, c.productVariables.XOMExcludePaths)
}
func (c *config) VendorConfig(name string) VendorConfig {
@@ -838,10 +1038,46 @@
return ok
}
-func stringSlice(s *[]string) []string {
- if s != nil {
- return *s
- } else {
- return nil
- }
+func (c *config) NdkAbis() bool {
+ return Bool(c.productVariables.Ndk_abis)
+}
+
+func (c *config) ExcludeDraftNdkApis() bool {
+ return Bool(c.productVariables.Exclude_draft_ndk_apis)
+}
+
+func (c *config) FlattenApex() bool {
+ return Bool(c.productVariables.FlattenApex)
+}
+
+func (c *config) EnforceSystemCertificate() bool {
+ return Bool(c.productVariables.EnforceSystemCertificate)
+}
+
+func (c *config) EnforceSystemCertificateWhitelist() []string {
+ return c.productVariables.EnforceSystemCertificateWhitelist
+}
+
+func (c *config) ProductHiddenAPIStubs() []string {
+ return c.productVariables.ProductHiddenAPIStubs
+}
+
+func (c *config) ProductHiddenAPIStubsSystem() []string {
+ return c.productVariables.ProductHiddenAPIStubsSystem
+}
+
+func (c *config) ProductHiddenAPIStubsTest() []string {
+ return c.productVariables.ProductHiddenAPIStubsTest
+}
+
+func (c *deviceConfig) TargetFSConfigGen() []string {
+ return c.config.productVariables.TargetFSConfigGen
+}
+
+func (c *deviceConfig) DeviceArch() string {
+ return String(c.config.productVariables.DeviceArch)
+}
+
+func (c *deviceConfig) DeviceSecondaryArch() string {
+ return String(c.config.productVariables.DeviceSecondaryArch)
}
diff --git a/android/config_test.go b/android/config_test.go
index 72942eb..274d59f 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -50,7 +50,7 @@
}
type configType struct {
- populateMe *bool `json:"omitempty"`
+ PopulateMe *bool `json:"omitempty"`
}
func (c *configType) SetDefaultConfig() {
@@ -60,7 +60,7 @@
func TestValidateConfigAnnotations(t *testing.T) {
config := configType{}
err := validateConfigAnnotations(&config)
- expectedError := `Field configType.populateMe has tag json:"omitempty" which specifies to change its json field name to "omitempty".
+ expectedError := `Field configType.PopulateMe has tag json:"omitempty" which specifies to change its json field name to "omitempty".
Did you mean to use an annotation of ",omitempty"?
(Alternatively, to change the json name of the field, rename the field in source instead.)`
if err.Error() != expectedError {
diff --git a/android/defaults.go b/android/defaults.go
index c704529..d4fbf48 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -131,11 +131,16 @@
func defaultsMutator(ctx TopDownMutatorContext) {
if defaultable, ok := ctx.Module().(Defaultable); ok && len(defaultable.defaults().Defaults) > 0 {
var defaultsList []Defaults
+ seen := make(map[Defaults]bool)
+
ctx.WalkDeps(func(module, parent Module) bool {
if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
if defaults, ok := module.(Defaults); ok {
- defaultsList = append(defaultsList, defaults)
- return len(defaults.defaults().Defaults) > 0
+ if !seen[defaults] {
+ seen[defaults] = true
+ defaultsList = append(defaultsList, defaults)
+ return len(defaults.defaults().Defaults) > 0
+ }
} else {
ctx.PropertyErrorf("defaults", "module %s is not an defaults module",
ctx.OtherModuleName(module))
diff --git a/android/defs.go b/android/defs.go
index cd8b4e3..4890c66 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -20,7 +20,7 @@
)
var (
- pctx = NewPackageContext("android/soong/common")
+ pctx = NewPackageContext("android/soong/android")
cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
Config.CpPreserveSymlinksFlags)
diff --git a/genrule/filegroup.go b/android/filegroup.go
similarity index 63%
rename from genrule/filegroup.go
rename to android/filegroup.go
index 2cff5fe..ec522fc 100644
--- a/genrule/filegroup.go
+++ b/android/filegroup.go
@@ -12,24 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package genrule
+package android
import (
- "android/soong/android"
"io"
"strings"
"text/template"
)
func init() {
- android.RegisterModuleType("filegroup", FileGroupFactory)
+ RegisterModuleType("filegroup", FileGroupFactory)
}
type fileGroupProperties struct {
// srcs lists files that will be included in this filegroup
- Srcs []string
+ Srcs []string `android:"path"`
- Exclude_srcs []string
+ Exclude_srcs []string `android:"path"`
// The base path to the files. May be used by other modules to determine which portion
// of the path to use. For example, when a filegroup is used as data in a cc_test rule,
@@ -43,34 +42,33 @@
}
type fileGroup struct {
- android.ModuleBase
+ ModuleBase
properties fileGroupProperties
- srcs android.Paths
+ srcs Paths
}
-var _ android.SourceFileProducer = (*fileGroup)(nil)
+var _ SourceFileProducer = (*fileGroup)(nil)
-// filegroup modules contain a list of files, and can be used to export files across package
-// boundaries. filegroups (and genrules) can be referenced from srcs properties of other modules
-// using the syntax ":module".
-func FileGroupFactory() android.Module {
+// filegroup contains a list of files that are referenced by other modules
+// properties (such as "srcs") using the syntax ":<name>". filegroup are
+// also be used to export files across package boundaries.
+func FileGroupFactory() Module {
module := &fileGroup{}
module.AddProperties(&module.properties)
- android.InitAndroidModule(module)
+ InitAndroidModule(module)
return module
}
-func (fg *fileGroup) DepsMutator(ctx android.BottomUpMutatorContext) {
- android.ExtractSourcesDeps(ctx, fg.properties.Srcs)
- android.ExtractSourcesDeps(ctx, fg.properties.Exclude_srcs)
+func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
+ fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
+
+ if fg.properties.Path != nil {
+ fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
+ }
}
-func (fg *fileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- fg.srcs = ctx.ExpandSourcesSubDir(fg.properties.Srcs, fg.properties.Exclude_srcs, String(fg.properties.Path))
-}
-
-func (fg *fileGroup) Srcs() android.Paths {
- return append(android.Paths{}, fg.srcs...)
+func (fg *fileGroup) Srcs() Paths {
+ return append(Paths{}, fg.srcs...)
}
var androidMkTemplate = template.Must(template.New("filegroup").Parse(`
@@ -81,9 +79,9 @@
.KATI_READONLY := {{.makeVar}}
`))
-func (fg *fileGroup) AndroidMk() android.AndroidMkData {
- return android.AndroidMkData{
- Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+func (fg *fileGroup) AndroidMk() AndroidMkData {
+ return AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
if makeVar := String(fg.properties.Export_to_make_var); makeVar != "" {
androidMkTemplate.Execute(w, map[string]string{
"makeVar": makeVar,
diff --git a/android/hooks.go b/android/hooks.go
index 57560d2..6b2468d 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -123,7 +123,7 @@
install []func(InstallHookContext)
}
-func loadHookMutator(ctx TopDownMutatorContext) {
+func LoadHookMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(Module); ok {
// Cast through *androidTopDownMutatorContext because AppendProperties is implemented
// on *androidTopDownMutatorContext but not exposed through TopDownMutatorContext
diff --git a/android/makevars.go b/android/makevars.go
index 3094a48..c011ea6 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -20,7 +20,10 @@
"io/ioutil"
"os"
"strconv"
+ "strings"
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
@@ -37,7 +40,21 @@
type MakeVarsContext interface {
Config() Config
DeviceConfig() DeviceConfig
- SingletonContext() SingletonContext
+ AddNinjaFileDeps(deps ...string)
+ Fs() pathtools.FileSystem
+
+ ModuleName(module blueprint.Module) string
+ ModuleDir(module blueprint.Module) string
+ ModuleSubDir(module blueprint.Module) string
+ ModuleType(module blueprint.Module) string
+ BlueprintFile(module blueprint.Module) string
+
+ ModuleErrorf(module blueprint.Module, format string, args ...interface{})
+ Errorf(format string, args ...interface{})
+ Failed() bool
+
+ VisitAllModules(visit func(Module))
+ VisitAllModulesIf(pred func(Module) bool, visit func(Module))
// Verify the make variable matches the Soong version, fail the build
// if it does not. If the make variable is empty, just set it.
@@ -65,18 +82,35 @@
CheckRaw(name, value string)
}
+var _ PathContext = MakeVarsContext(nil)
+
type MakeVarsProvider func(ctx MakeVarsContext)
func RegisterMakeVarsProvider(pctx PackageContext, provider MakeVarsProvider) {
makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
}
-///////////////////////////////////////////////////////////////////////////////
+// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
+type SingletonMakeVarsProvider interface {
+ Singleton
-func init() {
- RegisterSingletonType("makevars", makeVarsSingletonFunc)
+ // MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
+ MakeVars(ctx MakeVarsContext)
}
+// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to the list of
+// MakeVarsProviders to run.
+func registerSingletonMakeVarsProvider(singleton SingletonMakeVarsProvider) {
+ makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, SingletonmakeVarsProviderAdapter(singleton)})
+}
+
+// SingletonmakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
+func SingletonmakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
+ return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
func makeVarsSingletonFunc() Singleton {
return &makeVarsSingleton{}
}
@@ -91,8 +125,8 @@
var makeVarsProviders []makeVarsProvider
type makeVarsContext struct {
+ SingletonContext
config Config
- ctx SingletonContext
pctx PackageContext
vars []makeVarsVariable
}
@@ -120,9 +154,8 @@
vars := []makeVarsVariable{}
for _, provider := range makeVarsProviders {
mctx := &makeVarsContext{
- config: ctx.Config(),
- ctx: ctx,
- pctx: provider.pctx,
+ SingletonContext: ctx,
+ pctx: provider.pctx,
}
provider.call(mctx)
@@ -152,7 +185,7 @@
func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
buf := &bytes.Buffer{}
- fmt.Fprintln(buf, `# Autogenerated file
+ fmt.Fprint(buf, `# Autogenerated file
# Compares SOONG_$(1) against $(1), and warns if they are not equal.
#
@@ -200,7 +233,7 @@
fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
}
- fmt.Fprintln(buf, `
+ fmt.Fprint(buf, `
ifneq ($(my_check_failed),false)
$(error Soong variable check failed)
endif
@@ -228,20 +261,20 @@
return buf.Bytes()
}
-func (c *makeVarsContext) Config() Config {
- return c.config
-}
-
func (c *makeVarsContext) DeviceConfig() DeviceConfig {
- return DeviceConfig{c.config.deviceConfig}
+ return DeviceConfig{c.Config().deviceConfig}
}
-func (c *makeVarsContext) SingletonContext() SingletonContext {
- return c.ctx
-}
+var ninjaDescaper = strings.NewReplacer("$$", "$")
func (c *makeVarsContext) Eval(ninjaStr string) (string, error) {
- return c.ctx.Eval(c.pctx, ninjaStr)
+ s, err := c.SingletonContext.Eval(c.pctx, ninjaStr)
+ if err != nil {
+ return "", err
+ }
+ // SingletonContext.Eval returns an exapnded string that is valid for a ninja file, de-escape $$ to $ for use
+ // in a Makefile
+ return ninjaDescaper.Replace(s), nil
}
func (c *makeVarsContext) addVariableRaw(name, value string, strict, sort bool) {
@@ -256,7 +289,7 @@
func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
value, err := c.Eval(ninjaStr)
if err != nil {
- c.ctx.Errorf(err.Error())
+ c.SingletonContext.Errorf(err.Error())
}
c.addVariableRaw(name, value, strict, sort)
}
diff --git a/android/module.go b/android/module.go
index 2d0d5b8..7f4e308 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path"
"path/filepath"
"sort"
"strings"
@@ -23,6 +24,7 @@
"github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
)
var (
@@ -57,11 +59,13 @@
type androidBaseContext interface {
Target() Target
TargetPrimary() bool
+ MultiTargets() []Target
Arch() Arch
Os() OsType
Host() bool
Device() bool
Darwin() bool
+ Fuchsia() bool
Windows() bool
Debug() bool
PrimaryArch() bool
@@ -69,6 +73,7 @@
DeviceSpecific() bool
SocSpecific() bool
ProductSpecific() bool
+ ProductServicesSpecific() bool
AConfig() Config
DeviceConfig() DeviceConfig
}
@@ -83,6 +88,7 @@
type BaseModuleContext interface {
ModuleName() string
ModuleDir() string
+ ModuleType() string
Config() Config
ContainsProperty(name string) bool
@@ -111,19 +117,20 @@
ExpandSources(srcFiles, excludes []string) Paths
ExpandSource(srcFile, prop string) Path
ExpandOptionalSource(srcFile *string, prop string) OptionalPath
- ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths
Glob(globPattern string, excludes []string) Paths
GlobFiles(globPattern string, excludes []string) Paths
InstallExecutable(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
InstallFile(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath
+ InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath
CheckbuildFile(srcPath Path)
AddMissingDependencies(deps []string)
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
RequiredModuleNames() []string
@@ -143,9 +150,12 @@
VisitDirectDeps(visit func(Module))
VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
+ // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
VisitDepsDepthFirst(visit func(Module))
+ // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
WalkDeps(visit func(Module, Module) bool)
+ WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool)
Variable(pctx PackageContext, name, value string)
Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
@@ -176,13 +186,17 @@
Target() Target
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
SkipInstall()
ExportedToMake() bool
+ NoticeFile() OptionalPath
AddProperties(props ...interface{})
GetProperties() []interface{}
BuildParamsForTests() []BuildParams
+ RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
+ VariablesForTests() map[string]string
}
type nameProperties struct {
@@ -191,8 +205,6 @@
}
type commonProperties struct {
- Tags []string
-
// emit build rules for this module
Enabled *bool `android:"arch_variant"`
@@ -214,7 +226,8 @@
}
}
- Default_multilib string `blueprint:"mutated"`
+ UseTargetVariants bool `blueprint:"mutated"`
+ Default_multilib string `blueprint:"mutated"`
// whether this is a proprietary vendor module, and should be installed into /vendor
Proprietary *bool
@@ -242,18 +255,48 @@
// /system/product if product partition does not exist).
Product_specific *bool
+ // whether this module provides services owned by the OS provider to the core platform. When set
+ // to true, it is installed into /product_services (or /system/product_services if
+ // product_services partition does not exist).
+ Product_services_specific *bool
+
+ // Whether this module is installed to recovery partition
+ Recovery *bool
+
// init.rc files to be installed if this module is installed
- Init_rc []string
+ Init_rc []string `android:"path"`
+
+ // VINTF manifest fragments to be installed if this module is installed
+ Vintf_fragments []string `android:"path"`
// names of other modules to install if this module is installed
Required []string `android:"arch_variant"`
// relative path to a file to include in the list of notices for the device
- Notice *string
+ Notice *string `android:"path"`
+
+ Dist struct {
+ // copy the output of this module to the $DIST_DIR when `dist` is specified on the
+ // command line and any of these targets are also on the command line, or otherwise
+ // built
+ Targets []string `android:"arch_variant"`
+
+ // The name of the output artifact. This defaults to the basename of the output of
+ // the module.
+ Dest *string `android:"arch_variant"`
+
+ // The directory within the dist directory to store the artifact. Defaults to the
+ // top level directory ("").
+ Dir *string `android:"arch_variant"`
+
+ // A suffix to add to the artifact file name (before any extension).
+ Suffix *string `android:"arch_variant"`
+ } `android:"arch_variant"`
// Set by TargetMutator
- CompileTarget Target `blueprint:"mutated"`
- CompilePrimary bool `blueprint:"mutated"`
+ CompileTarget Target `blueprint:"mutated"`
+ CompileMultiTargets []Target `blueprint:"mutated"`
+ CompilePrimary bool `blueprint:"mutated"`
// Set by InitAndroidModule
HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
@@ -265,7 +308,10 @@
}
type hostAndDeviceProperties struct {
- Host_supported *bool
+ // If set to true, build a variant of the module for the host. Defaults to false.
+ Host_supported *bool
+
+ // If set to true, build a variant of the module for the device. Defaults to true.
Device_supported *bool
}
@@ -283,11 +329,25 @@
const (
_ HostOrDeviceSupported = iota
+
+ // Host and HostCross are built by default. Device is not supported.
HostSupported
+
+ // Host is built by default. HostCross and Device are not supported.
HostSupportedNoCross
+
+ // Device is built by default. Host and HostCross are not supported.
DeviceSupported
+
+ // Device is built by default. Host and HostCross are supported.
HostAndDeviceSupported
+
+ // Host, HostCross, and Device are built by default.
HostAndDeviceDefault
+
+ // Nothing is supported. This is not exposed to the user, but used to mark a
+ // host only module as unsupported when the module type is not supported on
+ // the host OS. E.g. benchmarks are supported on Linux but not Darwin.
NeitherHostNorDeviceSupported
)
@@ -298,6 +358,7 @@
deviceSpecificModule
socSpecificModule
productSpecificModule
+ productServicesSpecificModule
)
func (k moduleKind) String() string {
@@ -310,6 +371,8 @@
return "soc-specific"
case productSpecificModule:
return "product-specific"
+ case productServicesSpecificModule:
+ return "productservices-specific"
default:
panic(fmt.Errorf("unknown module kind %d", k))
}
@@ -323,6 +386,8 @@
&base.nameProperties,
&base.commonProperties,
&base.variableProperties)
+ base.generalProperties = m.GetProperties()
+ base.customizableProperties = m.GetProperties()
}
func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
@@ -332,6 +397,7 @@
base.commonProperties.HostOrDeviceSupported = hod
base.commonProperties.Default_multilib = string(defaultMultilib)
base.commonProperties.ArchSpecific = true
+ base.commonProperties.UseTargetVariants = true
switch hod {
case HostAndDeviceSupported, HostAndDeviceDefault:
@@ -341,6 +407,11 @@
InitArchModule(m)
}
+func InitAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
+ InitAndroidArchModule(m, hod, defaultMultilib)
+ m.base().commonProperties.UseTargetVariants = false
+}
+
// A ModuleBase object contains the properties that are common to all Android
// modules. It should be included as an anonymous field in every module
// struct definition. InitAndroidModule should then be called from the module's
@@ -393,12 +464,13 @@
variableProperties variableProperties
hostAndDeviceProperties hostAndDeviceProperties
generalProperties []interface{}
- archProperties []interface{}
+ archProperties [][]interface{}
customizableProperties []interface{}
noAddressSanitizer bool
installFiles Paths
checkbuildFiles Paths
+ noticeFile OptionalPath
// Used by buildTargetSingleton to create checkbuild and per-directory build targets
// Only set on the final variant of each module
@@ -412,8 +484,14 @@
// For tests
buildParams []BuildParams
+ ruleParams map[blueprint.Rule]blueprint.RuleParams
+ variables map[string]string
+
+ prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool
}
+func (a *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
+
func (a *ModuleBase) AddProperties(props ...interface{}) {
a.registerProps = append(a.registerProps, props...)
}
@@ -426,6 +504,18 @@
return a.buildParams
}
+func (a *ModuleBase) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
+ return a.ruleParams
+}
+
+func (a *ModuleBase) VariablesForTests() map[string]string {
+ return a.variables
+}
+
+func (a *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool) {
+ a.prefer32 = prefer32
+}
+
// Name returns the name of the module. It may be overridden by individual module types, for
// example prebuilts will prepend prebuilt_ to the name.
func (a *ModuleBase) Name() string {
@@ -441,8 +531,9 @@
return a
}
-func (a *ModuleBase) SetTarget(target Target, primary bool) {
+func (a *ModuleBase) SetTarget(target Target, multiTargets []Target, primary bool) {
a.commonProperties.CompileTarget = target
+ a.commonProperties.CompileMultiTargets = multiTargets
a.commonProperties.CompilePrimary = primary
}
@@ -454,6 +545,10 @@
return a.commonProperties.CompilePrimary
}
+func (a *ModuleBase) MultiTargets() []Target {
+ return a.commonProperties.CompileMultiTargets
+}
+
func (a *ModuleBase) Os() OsType {
return a.Target().Os
}
@@ -478,9 +573,11 @@
return []OsClass{Host}
case DeviceSupported:
return []OsClass{Device}
- case HostAndDeviceSupported:
+ case HostAndDeviceSupported, HostAndDeviceDefault:
var supported []OsClass
- if Bool(a.hostAndDeviceProperties.Host_supported) {
+ if Bool(a.hostAndDeviceProperties.Host_supported) ||
+ (a.commonProperties.HostOrDeviceSupported == HostAndDeviceDefault &&
+ a.hostAndDeviceProperties.Host_supported == nil) {
supported = append(supported, Host, HostCross)
}
if a.hostAndDeviceProperties.Device_supported == nil ||
@@ -500,6 +597,53 @@
*a.hostAndDeviceProperties.Device_supported)
}
+func (a *ModuleBase) Platform() bool {
+ return !a.DeviceSpecific() && !a.SocSpecific() && !a.ProductSpecific() && !a.ProductServicesSpecific()
+}
+
+func (a *ModuleBase) DeviceSpecific() bool {
+ return Bool(a.commonProperties.Device_specific)
+}
+
+func (a *ModuleBase) SocSpecific() bool {
+ return Bool(a.commonProperties.Vendor) || Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Soc_specific)
+}
+
+func (a *ModuleBase) ProductSpecific() bool {
+ return Bool(a.commonProperties.Product_specific)
+}
+
+func (a *ModuleBase) ProductServicesSpecific() bool {
+ return Bool(a.commonProperties.Product_services_specific)
+}
+
+func (m *ModuleBase) PartitionTag(config DeviceConfig) string {
+ partition := "system"
+ if m.SocSpecific() {
+ // A SoC-specific module could be on the vendor partition at
+ // "vendor" or the system partition at "system/vendor".
+ if config.VendorPath() == "vendor" {
+ partition = "vendor"
+ }
+ } else if m.DeviceSpecific() {
+ // A device-specific module could be on the odm partition at
+ // "odm", the vendor partition at "vendor/odm", or the system
+ // partition at "system/vendor/odm".
+ if config.OdmPath() == "odm" {
+ partition = "odm"
+ } else if strings.HasPrefix(config.OdmPath(), "vendor/") {
+ partition = "vendor"
+ }
+ } else if m.ProductSpecific() {
+ // A product-specific module could be on the product partition
+ // at "product" or the system partition at "system/product".
+ if config.ProductPath() == "product" {
+ partition = "product"
+ }
+ }
+ return partition
+}
+
func (a *ModuleBase) Enabled() bool {
if a.commonProperties.Enabled == nil {
return !a.Os().DefaultDisabled
@@ -519,6 +663,7 @@
ctx blueprint.ModuleContext) Paths {
result := Paths{}
+ // TODO(ccross): we need to use WalkDeps and have some way to know which dependencies require installation
ctx.VisitDepsDepthFirstIf(isFileInstaller,
func(m blueprint.Module) {
fileInstaller := m.(fileInstaller)
@@ -545,6 +690,18 @@
return false
}
+func (p *ModuleBase) InstallInRecovery() bool {
+ return Bool(p.commonProperties.Recovery)
+}
+
+func (a *ModuleBase) Owner() string {
+ return String(a.commonProperties.Owner)
+}
+
+func (a *ModuleBase) NoticeFile() OptionalPath {
+ return a.noticeFile
+}
+
func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
allInstalledFiles := Paths{}
allCheckbuildFiles := Paths{}
@@ -605,17 +762,11 @@
var socSpecific = Bool(a.commonProperties.Vendor) || Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Soc_specific)
var deviceSpecific = Bool(a.commonProperties.Device_specific)
var productSpecific = Bool(a.commonProperties.Product_specific)
+ var productServicesSpecific = Bool(a.commonProperties.Product_services_specific)
- if ((socSpecific || deviceSpecific) && productSpecific) || (socSpecific && deviceSpecific) {
- msg := "conflicting value set here"
- if productSpecific {
- ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
- if deviceSpecific {
- ctx.PropertyErrorf("device_specific", msg)
- }
- } else {
- ctx.PropertyErrorf("device_specific", "a module cannot be specific to SoC and device at the same time.")
- }
+ msg := "conflicting value set here"
+ if socSpecific && deviceSpecific {
+ ctx.PropertyErrorf("device_specific", "a module cannot be specific to SoC and device at the same time.")
if Bool(a.commonProperties.Vendor) {
ctx.PropertyErrorf("vendor", msg)
}
@@ -627,8 +778,36 @@
}
}
+ if productSpecific && productServicesSpecific {
+ ctx.PropertyErrorf("product_specific", "a module cannot be specific to product and product_services at the same time.")
+ ctx.PropertyErrorf("product_services_specific", msg)
+ }
+
+ if (socSpecific || deviceSpecific) && (productSpecific || productServicesSpecific) {
+ if productSpecific {
+ ctx.PropertyErrorf("product_specific", "a module cannot be specific to SoC or device and product at the same time.")
+ } else {
+ ctx.PropertyErrorf("product_services_specific", "a module cannot be specific to SoC or device and product_services at the same time.")
+ }
+ if deviceSpecific {
+ ctx.PropertyErrorf("device_specific", msg)
+ } else {
+ if Bool(a.commonProperties.Vendor) {
+ ctx.PropertyErrorf("vendor", msg)
+ }
+ if Bool(a.commonProperties.Proprietary) {
+ ctx.PropertyErrorf("proprietary", msg)
+ }
+ if Bool(a.commonProperties.Soc_specific) {
+ ctx.PropertyErrorf("soc_specific", msg)
+ }
+ }
+ }
+
if productSpecific {
return productSpecificModule
+ } else if productServicesSpecific {
+ return productServicesSpecificModule
} else if deviceSpecific {
return deviceSpecificModule
} else if socSpecific {
@@ -642,6 +821,7 @@
return androidBaseContextImpl{
target: a.commonProperties.CompileTarget,
targetPrimary: a.commonProperties.CompilePrimary,
+ multiTargets: a.commonProperties.CompileMultiTargets,
kind: determineModuleKind(a, ctx),
config: ctx.Config().(Config),
}
@@ -655,6 +835,11 @@
installDeps: a.computeInstallDeps(blueprintCtx),
installFiles: a.installFiles,
missingDeps: blueprintCtx.GetMissingDependencies(),
+ variables: make(map[string]string),
+ }
+
+ if ctx.config.captureBuild {
+ ctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams)
}
desc := "//" + ctx.ModuleDir() + ":" + ctx.ModuleName() + " "
@@ -674,7 +859,34 @@
}
ctx.Variable(pctx, "moduleDescSuffix", s)
+ // Some common property checks for properties that will be used later in androidmk.go
+ if a.commonProperties.Dist.Dest != nil {
+ _, err := validateSafePath(*a.commonProperties.Dist.Dest)
+ if err != nil {
+ ctx.PropertyErrorf("dist.dest", "%s", err.Error())
+ }
+ }
+ if a.commonProperties.Dist.Dir != nil {
+ _, err := validateSafePath(*a.commonProperties.Dist.Dir)
+ if err != nil {
+ ctx.PropertyErrorf("dist.dir", "%s", err.Error())
+ }
+ }
+ if a.commonProperties.Dist.Suffix != nil {
+ if strings.Contains(*a.commonProperties.Dist.Suffix, "/") {
+ ctx.PropertyErrorf("dist.suffix", "Suffix may not contain a '/' character.")
+ }
+ }
+
if a.Enabled() {
+ notice := proptools.StringDefault(a.commonProperties.Notice, "NOTICE")
+ if m := SrcIsModule(notice); m != "" {
+ a.noticeFile = ctx.ExpandOptionalSource(¬ice, "notice")
+ } else {
+ noticePath := filepath.Join(ctx.ModuleDir(), notice)
+ a.noticeFile = ExistentPathForSource(ctx, noticePath)
+ }
+
a.module.GenerateAndroidBuildActions(ctx)
if ctx.Failed() {
return
@@ -692,10 +904,13 @@
}
a.buildParams = ctx.buildParams
+ a.ruleParams = ctx.ruleParams
+ a.variables = ctx.variables
}
type androidBaseContextImpl struct {
target Target
+ multiTargets []Target
targetPrimary bool
debug bool
kind moduleKind
@@ -713,6 +928,8 @@
// For tests
buildParams []BuildParams
+ ruleParams map[blueprint.Rule]blueprint.RuleParams
+ variables map[string]string
}
func (a *androidModuleContext) ninjaError(desc string, outputs []string, err error) {
@@ -766,17 +983,40 @@
bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
}
+ bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs)
+ bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs)
+ bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs)
+ bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits)
+ bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly)
+ bparams.Depfile = proptools.NinjaEscapeList([]string{bparams.Depfile})[0]
+
return bparams
}
func (a *androidModuleContext) Variable(pctx PackageContext, name, value string) {
+ if a.config.captureBuild {
+ a.variables[name] = value
+ }
+
a.ModuleContext.Variable(pctx.PackageContext, name, value)
}
func (a *androidModuleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
argNames ...string) blueprint.Rule {
- return a.ModuleContext.Rule(pctx.PackageContext, name, params, argNames...)
+ if (a.config.UseGoma() || a.config.UseRBE()) && params.Pool == nil {
+ // When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
+ // jobs to the local parallelism value
+ params.Pool = localPool
+ }
+
+ rule := a.ModuleContext.Rule(pctx.PackageContext, name, params, argNames...)
+
+ if a.config.captureBuild {
+ a.ruleParams[rule] = params
+ }
+
+ return rule
}
func (a *androidModuleContext) Build(pctx PackageContext, params BuildParams) {
@@ -830,6 +1070,39 @@
return aModule
}
+func (a *androidModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) {
+ type dep struct {
+ mod blueprint.Module
+ tag blueprint.DependencyTag
+ }
+ var deps []dep
+ a.VisitDirectDepsBlueprint(func(m blueprint.Module) {
+ if aModule, _ := m.(Module); aModule != nil && aModule.base().BaseModuleName() == name {
+ returnedTag := a.ModuleContext.OtherModuleDependencyTag(aModule)
+ if tag == nil || returnedTag == tag {
+ deps = append(deps, dep{aModule, returnedTag})
+ }
+ }
+ })
+ if len(deps) == 1 {
+ return deps[0].mod, deps[0].tag
+ } else if len(deps) >= 2 {
+ panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
+ name, a.ModuleName()))
+ } else {
+ return nil, nil
+ }
+}
+
+func (a *androidModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
+ m, _ := a.getDirectDepInternal(name, tag)
+ return m
+}
+
+func (a *androidModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) {
+ return a.getDirectDepInternal(name, nil)
+}
+
func (a *androidModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
a.ModuleContext.VisitDirectDeps(visit)
}
@@ -892,6 +1165,10 @@
})
}
+func (a *androidModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
+ a.ModuleContext.WalkDeps(visit)
+}
+
func (a *androidModuleContext) WalkDeps(visit func(Module, Module) bool) {
a.ModuleContext.WalkDeps(func(child, parent blueprint.Module) bool {
childAndroidModule := a.validateAndroidModule(child)
@@ -926,6 +1203,10 @@
return a.targetPrimary
}
+func (a *androidBaseContextImpl) MultiTargets() []Target {
+ return a.multiTargets
+}
+
func (a *androidBaseContextImpl) Arch() Arch {
return a.target.Arch
}
@@ -946,6 +1227,10 @@
return a.target.Os == Darwin
}
+func (a *androidBaseContextImpl) Fuchsia() bool {
+ return a.target.Os == Fuchsia
+}
+
func (a *androidBaseContextImpl) Windows() bool {
return a.target.Os == Windows
}
@@ -955,10 +1240,10 @@
}
func (a *androidBaseContextImpl) PrimaryArch() bool {
- if len(a.config.Targets[a.target.Os.Class]) <= 1 {
+ if len(a.config.Targets[a.target.Os]) <= 1 {
return true
}
- return a.target.Arch.ArchType == a.config.Targets[a.target.Os.Class][0].Arch.ArchType
+ return a.target.Arch.ArchType == a.config.Targets[a.target.Os][0].Arch.ArchType
}
func (a *androidBaseContextImpl) AConfig() Config {
@@ -985,6 +1270,20 @@
return a.kind == productSpecificModule
}
+func (a *androidBaseContextImpl) ProductServicesSpecific() bool {
+ return a.kind == productServicesSpecificModule
+}
+
+// Makes this module a platform module, i.e. not specific to soc, device,
+// product, or product_services.
+func (a *ModuleBase) MakeAsPlatform() {
+ a.commonProperties.Vendor = boolPtr(false)
+ a.commonProperties.Proprietary = boolPtr(false)
+ a.commonProperties.Soc_specific = boolPtr(false)
+ a.commonProperties.Product_specific = boolPtr(false)
+ a.commonProperties.Product_services_specific = boolPtr(false)
+}
+
func (a *androidModuleContext) InstallInData() bool {
return a.module.InstallInData()
}
@@ -993,11 +1292,22 @@
return a.module.InstallInSanitizerDir()
}
+func (a *androidModuleContext) InstallInRecovery() bool {
+ return a.module.InstallInRecovery()
+}
+
func (a *androidModuleContext) skipInstall(fullInstallPath OutputPath) bool {
if a.module.base().commonProperties.SkipInstall {
return true
}
+ // We'll need a solution for choosing which of modules with the same name in different
+ // namespaces to install. For now, reuse the list of namespaces exported to Make as the
+ // list of namespaces to install in a Soong-only build.
+ if !a.module.base().commonProperties.NamespaceExportedToMake {
+ return true
+ }
+
if a.Device() {
if a.Config().SkipDeviceInstall() {
return true
@@ -1063,6 +1373,10 @@
if !a.skipInstall(fullInstallPath) {
+ relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String())
+ if err != nil {
+ panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
+ }
a.Build(pctx, BuildParams{
Rule: Symlink,
Description: "install symlink " + fullInstallPath.Base(),
@@ -1070,7 +1384,7 @@
OrderOnly: Paths{srcPath},
Default: !a.Config().EmbeddedInMake(),
Args: map[string]string{
- "fromPath": srcPath.String(),
+ "fromPath": relPath,
},
})
@@ -1080,6 +1394,28 @@
return fullInstallPath
}
+// installPath/name -> absPath where absPath might be a path that is available only at runtime
+// (e.g. /apex/...)
+func (a *androidModuleContext) InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath {
+ fullInstallPath := installPath.Join(a, name)
+ a.module.base().hooks.runInstallHooks(a, fullInstallPath, true)
+
+ if !a.skipInstall(fullInstallPath) {
+ a.Build(pctx, BuildParams{
+ Rule: Symlink,
+ Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
+ Output: fullInstallPath,
+ Default: !a.Config().EmbeddedInMake(),
+ Args: map[string]string{
+ "fromPath": absPath,
+ },
+ })
+
+ a.installFiles = append(a.installFiles, fullInstallPath)
+ }
+ return fullInstallPath
+}
+
func (a *androidModuleContext) CheckbuildFile(srcPath Path) {
a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
}
@@ -1122,6 +1458,8 @@
// Adds necessary dependencies to satisfy filegroup or generated sources modules listed in srcFiles
// using ":module" syntax, if any.
+//
+// Deprecated: tag the property with `android:"path"` instead.
func ExtractSourcesDeps(ctx BottomUpMutatorContext, srcFiles []string) {
var deps []string
set := make(map[string]bool)
@@ -1142,6 +1480,8 @@
// Adds necessary dependencies to satisfy filegroup or generated sources modules specified in s
// using ":module" syntax, if any.
+//
+// Deprecated: tag the property with `android:"path"` instead.
func ExtractSourceDeps(ctx BottomUpMutatorContext, s *string) {
if s != nil {
if m := SrcIsModule(*s); m != "" {
@@ -1154,97 +1494,36 @@
Srcs() Paths
}
-// Returns a list of paths expanded from globs and modules referenced using ":module" syntax.
-// ExtractSourcesDeps must have already been called during the dependency resolution phase.
-func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
- return ctx.ExpandSourcesSubDir(srcFiles, excludes, "")
+type HostToolProvider interface {
+ HostToolPath() OptionalPath
}
-// Returns a single path expanded from globs and modules referenced using ":module" syntax.
-// ExtractSourceDeps must have already been called during the dependency resolution phase.
+// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must
+// be tagged with `android:"path" to support automatic source module dependency resolution.
+//
+// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead.
+func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
+ return PathsForModuleSrcExcludes(ctx, srcFiles, excludes)
+}
+
+// Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must
+// be tagged with `android:"path" to support automatic source module dependency resolution.
+//
+// Deprecated: use PathForModuleSrc instead.
func (ctx *androidModuleContext) ExpandSource(srcFile, prop string) Path {
- srcFiles := ctx.ExpandSourcesSubDir([]string{srcFile}, nil, "")
- if len(srcFiles) == 1 {
- return srcFiles[0]
- } else {
- ctx.PropertyErrorf(prop, "module providing %s must produce exactly one file", prop)
- return nil
- }
+ return PathForModuleSrc(ctx, srcFile)
}
// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if
-// the srcFile is non-nil.
-// ExtractSourceDeps must have already been called during the dependency resolution phase.
+// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module
+// dependency resolution.
func (ctx *androidModuleContext) ExpandOptionalSource(srcFile *string, prop string) OptionalPath {
if srcFile != nil {
- return OptionalPathForPath(ctx.ExpandSource(*srcFile, prop))
+ return OptionalPathForPath(PathForModuleSrc(ctx, *srcFile))
}
return OptionalPath{}
}
-func (ctx *androidModuleContext) ExpandSourcesSubDir(srcFiles, excludes []string, subDir string) Paths {
- prefix := PathForModuleSrc(ctx).String()
-
- var expandedExcludes []string
- if excludes != nil {
- expandedExcludes = make([]string, 0, len(excludes))
- }
-
- for _, e := range excludes {
- if m := SrcIsModule(e); m != "" {
- module := ctx.GetDirectDepWithTag(m, SourceDepTag)
- if module == nil {
- // Error will have been handled by ExtractSourcesDeps
- continue
- }
- if srcProducer, ok := module.(SourceFileProducer); ok {
- expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...)
- } else {
- ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
- }
- } else {
- expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
- }
- }
- expandedSrcFiles := make(Paths, 0, len(srcFiles))
- for _, s := range srcFiles {
- if m := SrcIsModule(s); m != "" {
- module := ctx.GetDirectDepWithTag(m, SourceDepTag)
- if module == nil {
- // Error will have been handled by ExtractSourcesDeps
- continue
- }
- if srcProducer, ok := module.(SourceFileProducer); ok {
- moduleSrcs := srcProducer.Srcs()
- for _, e := range expandedExcludes {
- for j, ms := range moduleSrcs {
- if ms.String() == e {
- moduleSrcs = append(moduleSrcs[:j], moduleSrcs[j+1:]...)
- }
- }
- }
- expandedSrcFiles = append(expandedSrcFiles, moduleSrcs...)
- } else {
- ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
- }
- } else if pathtools.IsGlob(s) {
- globbedSrcFiles := ctx.GlobFiles(filepath.Join(prefix, s), expandedExcludes)
- for i, s := range globbedSrcFiles {
- globbedSrcFiles[i] = s.(ModuleSrcPath).WithSubDir(ctx, subDir)
- }
- expandedSrcFiles = append(expandedSrcFiles, globbedSrcFiles...)
- } else {
- p := PathForModuleSrc(ctx, s).WithSubDir(ctx, subDir)
- j := findStringInSlice(p.String(), expandedExcludes)
- if j == -1 {
- expandedSrcFiles = append(expandedSrcFiles, p)
- }
-
- }
- }
- return expandedSrcFiles
-}
-
func (ctx *androidModuleContext) RequiredModuleNames() []string {
return ctx.module.base().commonProperties.Required
}
@@ -1412,23 +1691,26 @@
}
}
-type AndroidModulesByName struct {
- slice []Module
- ctx interface {
- ModuleName(blueprint.Module) string
- ModuleSubDir(blueprint.Module) string
- }
+// Collect information for opening IDE project files in java/jdeps.go.
+type IDEInfo interface {
+ IDEInfo(ideInfo *IdeInfo)
+ BaseModuleName() string
}
-func (s AndroidModulesByName) Len() int { return len(s.slice) }
-func (s AndroidModulesByName) Less(i, j int) bool {
- mi, mj := s.slice[i], s.slice[j]
- ni, nj := s.ctx.ModuleName(mi), s.ctx.ModuleName(mj)
-
- if ni != nj {
- return ni < nj
- } else {
- return s.ctx.ModuleSubDir(mi) < s.ctx.ModuleSubDir(mj)
- }
+// Extract the base module name from the Import name.
+// Often the Import name has a prefix "prebuilt_".
+// Remove the prefix explicitly if needed
+// until we find a better solution to get the Import name.
+type IDECustomizedModuleName interface {
+ IDECustomizedModuleName() string
}
-func (s AndroidModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
+
+type IdeInfo struct {
+ Deps []string `json:"dependencies,omitempty"`
+ Srcs []string `json:"srcs,omitempty"`
+ Aidl_include_dirs []string `json:"aidl_include_dirs,omitempty"`
+ Jarjar_rules []string `json:"jarjar_rules,omitempty"`
+ Jars []string `json:"jars,omitempty"`
+ Classes []string `json:"class,omitempty"`
+ Installed_paths []string `json:"installed,omitempty"`
+}
diff --git a/android/mutator.go b/android/mutator.go
index 644a86b..31a3789 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -74,12 +74,13 @@
var preArch = []RegisterMutatorFunc{
func(ctx RegisterMutatorsContext) {
- ctx.TopDown("load_hooks", loadHookMutator).Parallel()
+ ctx.TopDown("load_hooks", LoadHookMutator).Parallel()
},
RegisterNamespaceMutator,
registerPackageRenamer,
RegisterPrebuiltsPreArchMutators,
RegisterDefaultsPreArchMutators,
+ RegisterOverridePreArchMutators,
}
func registerArchMutator(ctx RegisterMutatorsContext) {
@@ -92,6 +93,7 @@
}
var postDeps = []RegisterMutatorFunc{
+ registerPathDepsMutator,
RegisterPrebuiltsPostDepsMutators,
registerNeverallowMutator,
}
@@ -133,11 +135,15 @@
VisitDepsDepthFirst(visit func(Module))
VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
WalkDeps(visit func(Module, Module) bool)
+ // GetWalkPath is supposed to be called in visit function passed in WalkDeps()
+ // and returns a top-down dependency path from a start module to current child module.
+ GetWalkPath() []Module
}
type androidTopDownMutatorContext struct {
blueprint.TopDownMutatorContext
androidBaseContextImpl
+ walkPath []Module
}
type AndroidBottomUpMutator func(BottomUpMutatorContext)
@@ -206,7 +212,7 @@
}
func depsMutator(ctx BottomUpMutatorContext) {
- if m, ok := ctx.Module().(Module); ok {
+ if m, ok := ctx.Module().(Module); ok && m.Enabled() {
m.DepsMutator(ctx)
}
}
@@ -283,10 +289,16 @@
}
func (a *androidTopDownMutatorContext) WalkDeps(visit func(Module, Module) bool) {
+ a.walkPath = []Module{a.Module()}
a.TopDownMutatorContext.WalkDeps(func(child, parent blueprint.Module) bool {
childAndroidModule, _ := child.(Module)
parentAndroidModule, _ := parent.(Module)
if childAndroidModule != nil && parentAndroidModule != nil {
+ // record walkPath before visit
+ for a.walkPath[len(a.walkPath)-1] != parentAndroidModule {
+ a.walkPath = a.walkPath[0 : len(a.walkPath)-1]
+ }
+ a.walkPath = append(a.walkPath, childAndroidModule)
return visit(childAndroidModule, parentAndroidModule)
} else {
return false
@@ -294,6 +306,10 @@
})
}
+func (a *androidTopDownMutatorContext) GetWalkPath() []Module {
+ return a.walkPath
+}
+
func (a *androidTopDownMutatorContext) AppendProperties(props ...interface{}) {
for _, p := range props {
err := proptools.AppendMatchingProperties(a.Module().base().customizableProperties,
diff --git a/android/namespace.go b/android/namespace.go
index 0230524..50bdcba 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -94,10 +94,8 @@
}
func NewNameResolver(namespaceExportFilter func(*Namespace) bool) *NameResolver {
- namespacesByDir := sync.Map{}
-
r := &NameResolver{
- namespacesByDir: namespacesByDir,
+ namespacesByDir: sync.Map{},
namespaceExportFilter: namespaceExportFilter,
}
r.rootNamespace = r.newNamespace(".")
@@ -357,18 +355,19 @@
var _ blueprint.Namespace = (*Namespace)(nil)
+type namespaceProperties struct {
+ // a list of namespaces that contain modules that will be referenced
+ // by modules in this namespace.
+ Imports []string `android:"path"`
+}
+
type NamespaceModule struct {
ModuleBase
namespace *Namespace
resolver *NameResolver
- properties struct {
- Imports []string
- }
-}
-
-func (n *NamespaceModule) DepsMutator(context BottomUpMutatorContext) {
+ properties namespaceProperties
}
func (n *NamespaceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -381,6 +380,16 @@
return *n.nameProperties.Name
}
+// soong_namespace provides a scope to modules in an Android.bp file to prevent
+// module name conflicts with other defined modules in different Android.bp
+// files. Once soong_namespace has been defined in an Android.bp file, the
+// namespacing is applied to all modules that follow the soong_namespace in
+// the current Android.bp file, as well as modules defined in Android.bp files
+// in subdirectories. An Android.bp file in a subdirectory can define its own
+// soong_namespace which is applied to all its modules and as well as modules
+// defined in subdirectories Android.bp files. Modules in a soong_namespace are
+// visible to Make by listing the namespace path in PRODUCT_SOONG_NAMESPACES
+// make variable in a makefile.
func NamespaceFactory() Module {
module := &NamespaceModule{}
diff --git a/android/neverallow.go b/android/neverallow.go
index 8fba4b9..b90fe43 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -45,33 +45,108 @@
ctx.BottomUp("neverallow", neverallowMutator).Parallel()
}
-var neverallows = []*rule{
- neverallow().
- in("vendor", "device").
- with("vndk.enabled", "true").
- without("vendor", "true").
- because("the VNDK can never contain a library that is device dependent."),
- neverallow().
- with("vndk.enabled", "true").
- without("vendor", "true").
- without("owner", "").
- because("a VNDK module can never have an owner."),
- neverallow().notIn("libcore").with("no_standard_libs", "true"),
+var neverallows = createNeverAllows()
- // TODO(b/67974785): always enforce the manifest
- neverallow().
- without("name", "libhidltransport").
- with("product_variables.enforce_vintf_manifest.cflags", "*").
- because("manifest enforcement should be independent of ."),
+func createNeverAllows() []*rule {
+ rules := []*rule{}
+ rules = append(rules, createTrebleRules()...)
+ rules = append(rules, createLibcoreRules()...)
+ rules = append(rules, createMediaRules()...)
+ rules = append(rules, createJavaDeviceForHostRules()...)
+ return rules
+}
- // TODO(b/67975799): vendor code should always use /vendor/bin/sh
- neverallow().
- without("name", "libc_bionic_ndk").
- with("product_variables.treble_linker_namespaces.cflags", "*").
- because("nothing should care if linker namespaces are enabled or not"),
+func createTrebleRules() []*rule {
+ return []*rule{
+ neverallow().
+ in("vendor", "device").
+ with("vndk.enabled", "true").
+ without("vendor", "true").
+ because("the VNDK can never contain a library that is device dependent."),
+ neverallow().
+ with("vndk.enabled", "true").
+ without("vendor", "true").
+ without("owner", "").
+ because("a VNDK module can never have an owner."),
- // Example:
- // *neverallow().with("Srcs", "main.cpp"),
+ // TODO(b/67974785): always enforce the manifest
+ neverallow().
+ without("name", "libhidltransport-impl-internal").
+ with("product_variables.enforce_vintf_manifest.cflags", "*").
+ because("manifest enforcement should be independent of ."),
+
+ // TODO(b/67975799): vendor code should always use /vendor/bin/sh
+ neverallow().
+ without("name", "libc_bionic_ndk").
+ with("product_variables.treble_linker_namespaces.cflags", "*").
+ because("nothing should care if linker namespaces are enabled or not"),
+
+ // Example:
+ // *neverallow().with("Srcs", "main.cpp"))
+ }
+}
+
+func createLibcoreRules() []*rule {
+ var coreLibraryProjects = []string{
+ "libcore",
+ "external/apache-harmony",
+ "external/apache-xml",
+ "external/bouncycastle",
+ "external/conscrypt",
+ "external/icu",
+ "external/okhttp",
+ "external/wycheproof",
+ }
+
+ var coreModules = []string{
+ "core-all",
+ "core-oj",
+ "core-libart",
+ "okhttp",
+ "bouncycastle",
+ "conscrypt",
+ "apache-xml",
+ }
+
+ // Core library constraints. Prevent targets adding dependencies on core
+ // library internals, which could lead to compatibility issues with the ART
+ // mainline module. They should use core.platform.api.stubs instead.
+ rules := []*rule{
+ neverallow().
+ notIn(append(coreLibraryProjects, "development")...).
+ with("no_standard_libs", "true"),
+ }
+
+ for _, m := range coreModules {
+ r := neverallow().
+ notIn(coreLibraryProjects...).
+ with("libs", m).
+ because("Only core libraries projects can depend on " + m)
+ rules = append(rules, r)
+ }
+ return rules
+}
+
+func createMediaRules() []*rule {
+ return []*rule{
+ neverallow().
+ with("libs", "updatable-media").
+ because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
+ }
+}
+
+func createJavaDeviceForHostRules() []*rule {
+ javaDeviceForHostProjectsWhitelist := []string{
+ "external/robolectric-shadows",
+ "framework/layoutlib",
+ }
+
+ return []*rule{
+ neverallow().
+ notIn(javaDeviceForHostProjectsWhitelist...).
+ moduleType("java_device_for_host", "java_host_for_device").
+ because("java_device_for_host can only be used in whitelisted projects"),
+ }
}
func neverallowMutator(ctx BottomUpMutatorContext) {
@@ -88,6 +163,10 @@
continue
}
+ if !n.appliesToModuleType(ctx.ModuleType()) {
+ continue
+ }
+
if !n.appliesToProperties(properties) {
continue
}
@@ -108,6 +187,9 @@
paths []string
unlessPaths []string
+ moduleTypes []string
+ unlessModuleTypes []string
+
props []ruleProperty
unlessProps []ruleProperty
}
@@ -115,14 +197,27 @@
func neverallow() *rule {
return &rule{}
}
+
func (r *rule) in(path ...string) *rule {
r.paths = append(r.paths, cleanPaths(path)...)
return r
}
+
func (r *rule) notIn(path ...string) *rule {
r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
return r
}
+
+func (r *rule) moduleType(types ...string) *rule {
+ r.moduleTypes = append(r.moduleTypes, types...)
+ return r
+}
+
+func (r *rule) notModuleType(types ...string) *rule {
+ r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
+ return r
+}
+
func (r *rule) with(properties, value string) *rule {
r.props = append(r.props, ruleProperty{
fields: fieldNamesForProperties(properties),
@@ -130,6 +225,7 @@
})
return r
}
+
func (r *rule) without(properties, value string) *rule {
r.unlessProps = append(r.unlessProps, ruleProperty{
fields: fieldNamesForProperties(properties),
@@ -137,6 +233,7 @@
})
return r
}
+
func (r *rule) because(reason string) *rule {
r.reason = reason
return r
@@ -150,6 +247,12 @@
for _, v := range r.unlessPaths {
s += " -dir:" + v + "*"
}
+ for _, v := range r.moduleTypes {
+ s += " type:" + v
+ }
+ for _, v := range r.unlessModuleTypes {
+ s += " -type:" + v
+ }
for _, v := range r.props {
s += " " + strings.Join(v.fields, ".") + "=" + v.value
}
@@ -168,6 +271,10 @@
return includePath && !excludePath
}
+func (r *rule) appliesToModuleType(moduleType string) bool {
+ return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
+}
+
func (r *rule) appliesToProperties(properties []interface{}) bool {
includeProps := hasAllProperties(properties, r.props)
excludeProps := hasAnyProperty(properties, r.unlessProps)
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index a278365..6fd2cd7 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -91,21 +91,6 @@
},
expectedError: "manifest enforcement should be independent",
},
- {
- name: "libhidltransport enforce_vintf_manifest.cflags",
- fs: map[string][]byte{
- "Blueprints": []byte(`
- cc_library {
- name: "libhidltransport",
- product_variables: {
- enforce_vintf_manifest: {
- cflags: ["-DSHOULD_NOT_EXIST"],
- },
- },
- }`),
- },
- expectedError: "",
- },
{
name: "no treble_linker_namespaces.cflags",
@@ -137,6 +122,39 @@
},
expectedError: "",
},
+ {
+ name: "dependency on core-libart",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ java_library {
+ name: "needs_core_libart",
+ libs: ["core-libart"],
+ }`),
+ },
+ expectedError: "Only core libraries projects can depend on core-libart",
+ },
+ {
+ name: "dependency on updatable-media",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ java_library {
+ name: "needs_updatable_media",
+ libs: ["updatable-media"],
+ }`),
+ },
+ expectedError: "updatable-media includes private APIs. Use updatable_media_stubs instead.",
+ },
+ {
+ name: "java_device_for_host",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ java_device_for_host {
+ name: "device_for_host",
+ libs: ["core-libart"],
+ }`),
+ },
+ expectedError: "java_device_for_host can only be used in whitelisted projects",
+ },
}
func TestNeverallow(t *testing.T) {
@@ -164,6 +182,8 @@
func testNeverallow(t *testing.T, config Config, fs map[string][]byte) (*TestContext, []error) {
ctx := NewTestContext()
ctx.RegisterModuleType("cc_library", ModuleFactoryAdaptor(newMockCcLibraryModule))
+ ctx.RegisterModuleType("java_library", ModuleFactoryAdaptor(newMockJavaLibraryModule))
+ ctx.RegisterModuleType("java_device_for_host", ModuleFactoryAdaptor(newMockJavaLibraryModule))
ctx.PostDepsMutators(registerNeverallowMutator)
ctx.Register()
@@ -178,7 +198,7 @@
return ctx, errs
}
-type mockProperties struct {
+type mockCcLibraryProperties struct {
Vendor_available *bool
Vndk struct {
@@ -200,7 +220,7 @@
type mockCcLibraryModule struct {
ModuleBase
- properties mockProperties
+ properties mockCcLibraryProperties
}
func newMockCcLibraryModule() Module {
@@ -210,8 +230,24 @@
return m
}
-func (p *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
}
-func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
+type mockJavaLibraryProperties struct {
+ Libs []string
+}
+
+type mockJavaLibraryModule struct {
+ ModuleBase
+ properties mockJavaLibraryProperties
+}
+
+func newMockJavaLibraryModule() Module {
+ m := &mockJavaLibraryModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidModule(m)
+ return m
+}
+
+func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
}
diff --git a/android/notices.go b/android/notices.go
new file mode 100644
index 0000000..dbb88fc
--- /dev/null
+++ b/android/notices.go
@@ -0,0 +1,87 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "path/filepath"
+
+ "github.com/google/blueprint"
+)
+
+func init() {
+ pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
+ pctx.SourcePathVariable("generate_notice", "build/make/tools/generate-notice-files.py")
+
+ pctx.HostBinToolVariable("minigzip", "minigzip")
+}
+
+var (
+ mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{
+ Command: `${merge_notices} --output $out $in`,
+ CommandDeps: []string{"${merge_notices}"},
+ Description: "merge notice files into $out",
+ })
+
+ generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
+ Command: `rm -rf $tmpDir $$(dirname $out) && ` +
+ `mkdir -p $tmpDir $$(dirname $out) && ` +
+ `${generate_notice} --text-output $tmpDir/NOTICE.txt --html-output $tmpDir/NOTICE.html -t "$title" -s $inputDir && ` +
+ `${minigzip} -c $tmpDir/NOTICE.html > $out`,
+ CommandDeps: []string{"${generate_notice}", "${minigzip}"},
+ Description: "produce notice file $out",
+ }, "tmpDir", "title", "inputDir")
+)
+
+func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) {
+ ctx.Build(pctx, BuildParams{
+ Rule: mergeNoticesRule,
+ Description: "merge notices",
+ Inputs: noticePaths,
+ Output: mergedNotice,
+ })
+}
+
+func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string,
+ noticePaths []Path) ModuleOutPath {
+ // Merge all NOTICE files into one.
+ // TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
+ //
+ // generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
+ // about input NOTICE file paths.
+ // 1. Their relative paths to the src root become their NOTICE index titles. We want to use
+ // on-device paths as titles, and so output the merged NOTICE file the corresponding location.
+ // 2. They must end with .txt extension. Otherwise, they're ignored.
+ noticeRelPath := InstallPathToOnDevicePath(ctx, installPath.Join(ctx, installFilename+".txt"))
+ mergedNotice := PathForModuleOut(ctx, filepath.Join("NOTICE_FILES/src", noticeRelPath))
+ MergeNotices(ctx, mergedNotice, noticePaths)
+
+ // Transform the merged NOTICE file into a gzipped HTML file.
+ noticeOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+ tmpDir := PathForModuleOut(ctx, "NOTICE_tmp")
+ title := "Notices for " + ctx.ModuleName()
+ ctx.Build(pctx, BuildParams{
+ Rule: generateNoticeRule,
+ Description: "generate notice output",
+ Input: mergedNotice,
+ Output: noticeOutput,
+ Args: map[string]string{
+ "tmpDir": tmpDir.String(),
+ "title": title,
+ "inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(),
+ },
+ })
+
+ return noticeOutput
+}
diff --git a/android/onceper.go b/android/onceper.go
index f19f75c..5ad17fa 100644
--- a/android/onceper.go
+++ b/android/onceper.go
@@ -20,50 +20,66 @@
)
type OncePer struct {
- values sync.Map
- valuesLock sync.Mutex
+ values sync.Map
}
-type valueMap map[interface{}]interface{}
+type onceValueWaiter chan bool
+
+func (once *OncePer) maybeWaitFor(key OnceKey, value interface{}) interface{} {
+ if wait, isWaiter := value.(onceValueWaiter); isWaiter {
+ // The entry in the map is a placeholder waiter because something else is constructing the value
+ // wait until the waiter is signalled, then load the real value.
+ <-wait
+ value, _ = once.values.Load(key)
+ if _, isWaiter := value.(onceValueWaiter); isWaiter {
+ panic(fmt.Errorf("Once() waiter completed but key is still not valid"))
+ }
+ }
+
+ return value
+}
// Once computes a value the first time it is called with a given key per OncePer, and returns the
// value without recomputing when called with the same key. key must be hashable.
-func (once *OncePer) Once(key interface{}, value func() interface{}) interface{} {
+func (once *OncePer) Once(key OnceKey, value func() interface{}) interface{} {
// Fast path: check if the key is already in the map
if v, ok := once.values.Load(key); ok {
- return v
+ return once.maybeWaitFor(key, v)
}
- // Slow path: lock so that we don't call the value function twice concurrently
- once.valuesLock.Lock()
- defer once.valuesLock.Unlock()
-
- // Check again with the lock held
- if v, ok := once.values.Load(key); ok {
- return v
+ // Slow path: create a OnceValueWrapper and attempt to insert it
+ waiter := make(onceValueWaiter)
+ if v, loaded := once.values.LoadOrStore(key, waiter); loaded {
+ // Got a value, something else inserted its own waiter or a constructed value
+ return once.maybeWaitFor(key, v)
}
- // Still not in the map, call the value function and store it
+ // The waiter is inserted, call the value constructor, store it, and signal the waiter
v := value()
once.values.Store(key, v)
+ close(waiter)
return v
}
-func (once *OncePer) Get(key interface{}) interface{} {
+// Get returns the value previously computed with Once for a given key. If Once has not been called for the given
+// key Get will panic.
+func (once *OncePer) Get(key OnceKey) interface{} {
v, ok := once.values.Load(key)
if !ok {
panic(fmt.Errorf("Get() called before Once()"))
}
- return v
+ return once.maybeWaitFor(key, v)
}
-func (once *OncePer) OnceStringSlice(key interface{}, value func() []string) []string {
+// OnceStringSlice is the same as Once, but returns the value cast to a []string
+func (once *OncePer) OnceStringSlice(key OnceKey, value func() []string) []string {
return once.Once(key, func() interface{} { return value() }).([]string)
}
-func (once *OncePer) Once2StringSlice(key interface{}, value func() ([]string, []string)) ([]string, []string) {
+// OnceStringSlice is the same as Once, but returns two values cast to []string
+func (once *OncePer) Once2StringSlice(key OnceKey, value func() ([]string, []string)) ([]string, []string) {
type twoStringSlice [2][]string
s := once.Once(key, func() interface{} {
var s twoStringSlice
@@ -72,3 +88,21 @@
}).(twoStringSlice)
return s[0], s[1]
}
+
+// OnceKey is an opaque type to be used as the key in calls to Once.
+type OnceKey struct {
+ key interface{}
+}
+
+// NewOnceKey returns an opaque OnceKey object for the provided key. Two calls to NewOnceKey with the same key string
+// DO NOT produce the same OnceKey object.
+func NewOnceKey(key string) OnceKey {
+ return OnceKey{&key}
+}
+
+// NewCustomOnceKey returns an opaque OnceKey object for the provided key. The key can be any type that is valid as the
+// key in a map, i.e. comparable. Two calls to NewCustomOnceKey with key values that compare equal will return OnceKey
+// objects that access the same value stored with Once.
+func NewCustomOnceKey(key interface{}) OnceKey {
+ return OnceKey{key}
+}
diff --git a/android/onceper_test.go b/android/onceper_test.go
new file mode 100644
index 0000000..95303ba
--- /dev/null
+++ b/android/onceper_test.go
@@ -0,0 +1,177 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "testing"
+ "time"
+)
+
+func TestOncePer_Once(t *testing.T) {
+ once := OncePer{}
+ key := NewOnceKey("key")
+
+ a := once.Once(key, func() interface{} { return "a" }).(string)
+ b := once.Once(key, func() interface{} { return "b" }).(string)
+
+ if a != "a" {
+ t.Errorf(`first call to Once should return "a": %q`, a)
+ }
+
+ if b != "a" {
+ t.Errorf(`second call to Once with the same key should return "a": %q`, b)
+ }
+}
+
+func TestOncePer_Once_wait(t *testing.T) {
+ once := OncePer{}
+ key := NewOnceKey("key")
+
+ ch := make(chan bool)
+
+ go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
+ <-ch
+ a := once.Once(key, func() interface{} { return "bar" }).(string)
+
+ if a != "foo" {
+ t.Errorf("expect %q, got %q", "foo", a)
+ }
+}
+
+func TestOncePer_Get(t *testing.T) {
+ once := OncePer{}
+ key := NewOnceKey("key")
+
+ a := once.Once(key, func() interface{} { return "a" }).(string)
+ b := once.Get(key).(string)
+
+ if a != "a" {
+ t.Errorf(`first call to Once should return "a": %q`, a)
+ }
+
+ if b != "a" {
+ t.Errorf(`Get with the same key should return "a": %q`, b)
+ }
+}
+
+func TestOncePer_Get_panic(t *testing.T) {
+ once := OncePer{}
+ key := NewOnceKey("key")
+
+ defer func() {
+ p := recover()
+
+ if p == nil {
+ t.Error("call to Get for unused key should panic")
+ }
+ }()
+
+ once.Get(key)
+}
+
+func TestOncePer_Get_wait(t *testing.T) {
+ once := OncePer{}
+ key := NewOnceKey("key")
+
+ ch := make(chan bool)
+
+ go once.Once(key, func() interface{} { close(ch); time.Sleep(100 * time.Millisecond); return "foo" })
+ <-ch
+ a := once.Get(key).(string)
+
+ if a != "foo" {
+ t.Errorf("expect %q, got %q", "foo", a)
+ }
+}
+
+func TestOncePer_OnceStringSlice(t *testing.T) {
+ once := OncePer{}
+ key := NewOnceKey("key")
+
+ a := once.OnceStringSlice(key, func() []string { return []string{"a"} })
+ b := once.OnceStringSlice(key, func() []string { return []string{"a"} })
+
+ if a[0] != "a" {
+ t.Errorf(`first call to OnceStringSlice should return ["a"]: %q`, a)
+ }
+
+ if b[0] != "a" {
+ t.Errorf(`second call to OnceStringSlice with the same key should return ["a"]: %q`, b)
+ }
+}
+
+func TestOncePer_Once2StringSlice(t *testing.T) {
+ once := OncePer{}
+ key := NewOnceKey("key")
+
+ a, b := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"a"}, []string{"b"} })
+ c, d := once.Once2StringSlice(key, func() ([]string, []string) { return []string{"c"}, []string{"d"} })
+
+ if a[0] != "a" || b[0] != "b" {
+ t.Errorf(`first call to Once2StringSlice should return ["a"], ["b"]: %q, %q`, a, b)
+ }
+
+ if c[0] != "a" || d[0] != "b" {
+ t.Errorf(`second call to Once2StringSlice with the same key should return ["a"], ["b"]: %q, %q`, c, d)
+ }
+}
+
+func TestNewOnceKey(t *testing.T) {
+ once := OncePer{}
+ key1 := NewOnceKey("key")
+ key2 := NewOnceKey("key")
+
+ a := once.Once(key1, func() interface{} { return "a" }).(string)
+ b := once.Once(key2, func() interface{} { return "b" }).(string)
+
+ if a != "a" {
+ t.Errorf(`first call to Once should return "a": %q`, a)
+ }
+
+ if b != "b" {
+ t.Errorf(`second call to Once with the NewOnceKey from same string should return "b": %q`, b)
+ }
+}
+
+func TestNewCustomOnceKey(t *testing.T) {
+ type key struct {
+ key string
+ }
+ once := OncePer{}
+ key1 := NewCustomOnceKey(key{"key"})
+ key2 := NewCustomOnceKey(key{"key"})
+
+ a := once.Once(key1, func() interface{} { return "a" }).(string)
+ b := once.Once(key2, func() interface{} { return "b" }).(string)
+
+ if a != "a" {
+ t.Errorf(`first call to Once should return "a": %q`, a)
+ }
+
+ if b != "a" {
+ t.Errorf(`second call to Once with the NewCustomOnceKey from equal key should return "a": %q`, b)
+ }
+}
+
+func TestOncePerReentrant(t *testing.T) {
+ once := OncePer{}
+ key1 := NewOnceKey("key")
+ key2 := NewOnceKey("key")
+
+ a := once.Once(key1, func() interface{} { return once.Once(key2, func() interface{} { return "a" }) })
+ if a != "a" {
+ t.Errorf(`reentrant Once should return "a": %q`, a)
+ }
+}
diff --git a/android/override_module.go b/android/override_module.go
new file mode 100644
index 0000000..ba66182
--- /dev/null
+++ b/android/override_module.go
@@ -0,0 +1,209 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+// This file contains all the foundation components for override modules and their base module
+// types. Override modules are a kind of opposite of default modules in that they override certain
+// properties of an existing base module whereas default modules provide base module data to be
+// overridden. However, unlike default and defaultable module pairs, both override and overridable
+// modules generate and output build actions, and it is up to product make vars to decide which one
+// to actually build and install in the end. In other words, default modules and defaultable modules
+// can be compared to abstract classes and concrete classes in C++ and Java. By the same analogy,
+// both override and overridable modules act like concrete classes.
+//
+// There is one more crucial difference from the logic perspective. Unlike default pairs, most Soong
+// actions happen in the base (overridable) module by creating a local variant for each override
+// module based on it.
+
+import (
+ "sync"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+// Interface for override module types, e.g. override_android_app, override_apex
+type OverrideModule interface {
+ Module
+
+ getOverridingProperties() []interface{}
+ setOverridingProperties(properties []interface{})
+
+ getOverrideModuleProperties() *OverrideModuleProperties
+}
+
+// Base module struct for override module types
+type OverrideModuleBase struct {
+ moduleProperties OverrideModuleProperties
+
+ overridingProperties []interface{}
+}
+
+type OverrideModuleProperties struct {
+ // Name of the base module to be overridden
+ Base *string
+
+ // TODO(jungjw): Add an optional override_name bool flag.
+}
+
+func (o *OverrideModuleBase) getOverridingProperties() []interface{} {
+ return o.overridingProperties
+}
+
+func (o *OverrideModuleBase) setOverridingProperties(properties []interface{}) {
+ o.overridingProperties = properties
+}
+
+func (o *OverrideModuleBase) getOverrideModuleProperties() *OverrideModuleProperties {
+ return &o.moduleProperties
+}
+
+func InitOverrideModule(m OverrideModule) {
+ m.setOverridingProperties(m.GetProperties())
+
+ m.AddProperties(m.getOverrideModuleProperties())
+}
+
+// Interface for overridable module types, e.g. android_app, apex
+type OverridableModule interface {
+ setOverridableProperties(prop []interface{})
+
+ addOverride(o OverrideModule)
+ getOverrides() []OverrideModule
+
+ override(ctx BaseModuleContext, o OverrideModule)
+
+ setOverridesProperty(overridesProperties *[]string)
+}
+
+// Base module struct for overridable module types
+type OverridableModuleBase struct {
+ // List of OverrideModules that override this base module
+ overrides []OverrideModule
+ // Used to parallelize registerOverrideMutator executions. Note that only addOverride locks this
+ // mutex. It is because addOverride and getOverride are used in different mutators, and so are
+ // guaranteed to be not mixed. (And, getOverride only reads from overrides, and so don't require
+ // mutex locking.)
+ overridesLock sync.Mutex
+
+ overridableProperties []interface{}
+
+ // If an overridable module has a property to list other modules that itself overrides, it should
+ // set this to a pointer to the property through the InitOverridableModule function, so that
+ // override information is propagated and aggregated correctly.
+ overridesProperty *[]string
+}
+
+func InitOverridableModule(m OverridableModule, overridesProperty *[]string) {
+ m.setOverridableProperties(m.(Module).GetProperties())
+ m.setOverridesProperty(overridesProperty)
+}
+
+func (b *OverridableModuleBase) setOverridableProperties(prop []interface{}) {
+ b.overridableProperties = prop
+}
+
+func (b *OverridableModuleBase) addOverride(o OverrideModule) {
+ b.overridesLock.Lock()
+ b.overrides = append(b.overrides, o)
+ b.overridesLock.Unlock()
+}
+
+// Should NOT be used in the same mutator as addOverride.
+func (b *OverridableModuleBase) getOverrides() []OverrideModule {
+ return b.overrides
+}
+
+func (b *OverridableModuleBase) setOverridesProperty(overridesProperty *[]string) {
+ b.overridesProperty = overridesProperty
+}
+
+// Overrides a base module with the given OverrideModule.
+func (b *OverridableModuleBase) override(ctx BaseModuleContext, o OverrideModule) {
+ // Adds the base module to the overrides property, if exists, of the overriding module. See the
+ // comment on OverridableModuleBase.overridesProperty for details.
+ if b.overridesProperty != nil {
+ *b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
+ }
+ for _, p := range b.overridableProperties {
+ for _, op := range o.getOverridingProperties() {
+ if proptools.TypeEqual(p, op) {
+ err := proptools.AppendProperties(p, op, nil)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
+ }
+ }
+ }
+}
+
+// Mutators for override/overridable modules. All the fun happens in these functions. It is critical
+// to keep them in this order and not put any order mutators between them.
+func RegisterOverridePreArchMutators(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
+ ctx.TopDown("register_override", registerOverrideMutator).Parallel()
+ ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
+}
+
+type overrideBaseDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var overrideBaseDepTag overrideBaseDependencyTag
+
+// Adds dependency on the base module to the overriding module so that they can be visited in the
+// next phase.
+func overrideModuleDepsMutator(ctx BottomUpMutatorContext) {
+ if module, ok := ctx.Module().(OverrideModule); ok {
+ ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)
+ }
+}
+
+// Visits the base module added as a dependency above, checks the module type, and registers the
+// overriding module.
+func registerOverrideMutator(ctx TopDownMutatorContext) {
+ ctx.VisitDirectDepsWithTag(overrideBaseDepTag, func(base Module) {
+ if o, ok := base.(OverridableModule); ok {
+ o.addOverride(ctx.Module().(OverrideModule))
+ } else {
+ ctx.PropertyErrorf("base", "unsupported base module type")
+ }
+ })
+}
+
+// Now, goes through all overridable modules, finds all modules overriding them, creates a local
+// variant for each of them, and performs the actual overriding operation by calling override().
+func performOverrideMutator(ctx BottomUpMutatorContext) {
+ if b, ok := ctx.Module().(OverridableModule); ok {
+ overrides := b.getOverrides()
+ if len(overrides) == 0 {
+ return
+ }
+ variants := make([]string, len(overrides)+1)
+ // The first variant is for the original, non-overridden, base module.
+ variants[0] = ""
+ for i, o := range overrides {
+ variants[i+1] = o.(Module).Name()
+ }
+ mods := ctx.CreateLocalVariations(variants...)
+ for i, o := range overrides {
+ mods[i+1].(OverridableModule).override(ctx, o)
+ }
+ }
+}
diff --git a/android/package_ctx.go b/android/package_ctx.go
index e228bba..1663469 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -104,7 +104,8 @@
}
// RuleFunc wraps blueprint.PackageContext.RuleFunc, converting the interface{} config
-// argument to a Context that supports Config().
+// argument to a Context that supports Config(), and provides a default Pool if none is
+// specified.
func (p PackageContext) RuleFunc(name string,
f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule {
@@ -114,6 +115,11 @@
if len(ctx.errors) > 0 {
return params, ctx.errors[0]
}
+ if (ctx.Config().UseGoma() || ctx.Config().UseRBE()) && params.Pool == nil {
+ // When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by
+ // goma/RBE, restrict jobs to the local parallelism value
+ params.Pool = localPool
+ }
return params, nil
}, argNames...)
}
@@ -124,7 +130,11 @@
// package-scoped variable's initialization.
func (p PackageContext) SourcePathVariable(name, path string) blueprint.Variable {
return p.VariableFunc(name, func(ctx PackageVarContext) string {
- return safePathForSource(ctx, path).String()
+ p, err := safePathForSource(ctx, path)
+ if err != nil {
+ ctx.Errorf("%s", err.Error())
+ }
+ return p.String()
})
}
@@ -136,7 +146,10 @@
return p.VariableFunc(name, func(ctx PackageVarContext) string {
var ret []string
for _, path := range paths {
- p := safePathForSource(ctx, path)
+ p, err := safePathForSource(ctx, path)
+ if err != nil {
+ ctx.Errorf("%s", err.Error())
+ }
ret = append(ret, p.String())
}
return strings.Join(ret, separator)
@@ -150,7 +163,10 @@
// as part of a package-scoped variable's initialization.
func (p PackageContext) SourcePathVariableWithEnvOverride(name, path, env string) blueprint.Variable {
return p.VariableFunc(name, func(ctx PackageVarContext) string {
- p := safePathForSource(ctx, path)
+ p, err := safePathForSource(ctx, path)
+ if err != nil {
+ ctx.Errorf("%s", err.Error())
+ }
return ctx.Config().GetenvWithDefault(env, p.String())
})
}
@@ -224,29 +240,45 @@
})
}
-// AndroidStaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified
+// AndroidStaticRule is an alias for StaticRule.
func (p PackageContext) AndroidStaticRule(name string, params blueprint.RuleParams,
argNames ...string) blueprint.Rule {
- return p.AndroidRuleFunc(name, func(PackageRuleContext) blueprint.RuleParams {
- return params
- }, argNames...)
-}
-
-// AndroidGomaStaticRule wraps blueprint.StaticRule but uses goma's parallelism if goma is enabled
-func (p PackageContext) AndroidGomaStaticRule(name string, params blueprint.RuleParams,
- argNames ...string) blueprint.Rule {
return p.StaticRule(name, params, argNames...)
}
-func (p PackageContext) AndroidRuleFunc(name string,
- f func(PackageRuleContext) blueprint.RuleParams, argNames ...string) blueprint.Rule {
- return p.RuleFunc(name, func(ctx PackageRuleContext) blueprint.RuleParams {
- params := f(ctx)
- if ctx.Config().UseGoma() && params.Pool == nil {
+// StaticRule wraps blueprint.StaticRule and provides a default Pool if none is specified.
+func (p PackageContext) StaticRule(name string, params blueprint.RuleParams,
+ argNames ...string) blueprint.Rule {
+ return p.RuleFunc(name, func(PackageRuleContext) blueprint.RuleParams {
+ return params
+ }, argNames...)
+}
+
+// RemoteRuleSupports configures rules with whether they have Goma and/or RBE support.
+type RemoteRuleSupports struct {
+ Goma bool
+ RBE bool
+}
+
+// AndroidRemoteStaticRule wraps blueprint.StaticRule but uses goma or RBE's parallelism if goma or RBE are enabled
+// and the appropriate SUPPORTS_* flag is set.
+func (p PackageContext) AndroidRemoteStaticRule(name string, supports RemoteRuleSupports, params blueprint.RuleParams,
+ argNames ...string) blueprint.Rule {
+
+ return p.PackageContext.RuleFunc(name, func(config interface{}) (blueprint.RuleParams, error) {
+ ctx := &configErrorWrapper{p, config.(Config), nil}
+ if ctx.Config().UseGoma() && !supports.Goma {
// When USE_GOMA=true is set and the rule is not supported by goma, restrict jobs to the
// local parallelism value
params.Pool = localPool
}
- return params
+
+ if ctx.Config().UseRBE() && !supports.RBE {
+ // When USE_RBE=true is set and the rule is not supported by RBE, restrict jobs to the
+ // local parallelism value
+ params.Pool = localPool
+ }
+
+ return params, nil
}, argNames...)
}
diff --git a/android/path_properties.go b/android/path_properties.go
new file mode 100644
index 0000000..1a12290
--- /dev/null
+++ b/android/path_properties.go
@@ -0,0 +1,123 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func registerPathDepsMutator(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("pathdeps", pathDepsMutator).Parallel()
+}
+
+// The pathDepsMutator automatically adds dependencies on any module that is listed with ":module" syntax in a
+// property that is tagged with android:"path".
+func pathDepsMutator(ctx BottomUpMutatorContext) {
+ m := ctx.Module().(Module)
+ if m == nil {
+ return
+ }
+
+ props := m.base().generalProperties
+
+ for _, ps := range props {
+ pathProperties := pathPropertiesForPropertyStruct(ctx, ps)
+ pathProperties = FirstUniqueStrings(pathProperties)
+
+ var deps []string
+ for _, s := range pathProperties {
+ if m := SrcIsModule(s); m != "" {
+ deps = append(deps, m)
+ }
+ }
+
+ ctx.AddDependency(ctx.Module(), SourceDepTag, deps...)
+ }
+}
+
+// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with android:"path" to extract
+// all their values from a property struct, returning them as a single slice of strings..
+func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string {
+ v := reflect.ValueOf(ps)
+ if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
+ panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
+ }
+ if v.IsNil() {
+ return nil
+ }
+ v = v.Elem()
+
+ pathPropertyIndexes := pathPropertyIndexesForPropertyStruct(ps)
+
+ var ret []string
+
+ for _, i := range pathPropertyIndexes {
+ sv := fieldByIndex(v, i)
+ if !sv.IsValid() {
+ continue
+ }
+
+ if sv.Kind() == reflect.Ptr {
+ if sv.IsNil() {
+ continue
+ }
+ sv = sv.Elem()
+ }
+ switch sv.Kind() {
+ case reflect.String:
+ ret = append(ret, sv.String())
+ case reflect.Slice:
+ ret = append(ret, sv.Interface().([]string)...)
+ default:
+ panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
+ v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
+ }
+ }
+
+ return ret
+}
+
+// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when traversing a nil pointer
+// to a struct.
+func fieldByIndex(v reflect.Value, index []int) reflect.Value {
+ if len(index) == 1 {
+ return v.Field(index[0])
+ }
+ for _, x := range index {
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return reflect.Value{}
+ }
+ v = v.Elem()
+ }
+ v = v.Field(x)
+ }
+ return v
+}
+
+var pathPropertyIndexesCache OncePer
+
+// pathPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in property struct type that
+// are tagged with android:"path". Each index is a []int suitable for passing to reflect.Value.FieldByIndex. The value
+// is cached in a global cache by type.
+func pathPropertyIndexesForPropertyStruct(ps interface{}) [][]int {
+ key := NewCustomOnceKey(reflect.TypeOf(ps))
+ return pathPropertyIndexesCache.Once(key, func() interface{} {
+ return proptools.PropertyIndexesWithTag(ps, "android", "path")
+ }).([][]int)
+}
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
new file mode 100644
index 0000000..ecc2d21
--- /dev/null
+++ b/android/path_properties_test.go
@@ -0,0 +1,137 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "io/ioutil"
+ "os"
+ "reflect"
+ "testing"
+)
+
+type pathDepsMutatorTestModule struct {
+ ModuleBase
+ props struct {
+ Foo string `android:"path"`
+ Bar []string `android:"path,arch_variant"`
+ Baz *string `android:"path"`
+ Qux string
+ }
+
+ sourceDeps []string
+}
+
+func pathDepsMutatorTestModuleFactory() Module {
+ module := &pathDepsMutatorTestModule{}
+ module.AddProperties(&module.props)
+ InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+ return module
+}
+
+func (p *pathDepsMutatorTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ ctx.VisitDirectDepsWithTag(SourceDepTag, func(dep Module) {
+ p.sourceDeps = append(p.sourceDeps, ctx.OtherModuleName(dep))
+ })
+}
+
+func TestPathDepsMutator(t *testing.T) {
+ tests := []struct {
+ name string
+ bp string
+ deps []string
+ }{
+ {
+ name: "all",
+ bp: `
+ test {
+ name: "foo",
+ foo: ":a",
+ bar: [":b"],
+ baz: ":c",
+ qux: ":d",
+ }`,
+ deps: []string{"a", "b", "c"},
+ },
+ {
+ name: "arch variant",
+ bp: `
+ test {
+ name: "foo",
+ arch: {
+ arm64: {
+ bar: [":a"],
+ },
+ arm: {
+ bar: [":b"],
+ },
+ },
+ bar: [":c"],
+ }`,
+ deps: []string{"c", "a"},
+ },
+ }
+
+ buildDir, err := ioutil.TempDir("", "soong_path_properties_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ config := TestArchConfig(buildDir, nil)
+ ctx := NewTestArchContext()
+
+ ctx.RegisterModuleType("test", ModuleFactoryAdaptor(pathDepsMutatorTestModuleFactory))
+ ctx.RegisterModuleType("filegroup", ModuleFactoryAdaptor(FileGroupFactory))
+
+ bp := test.bp + `
+ filegroup {
+ name: "a",
+ }
+
+ filegroup {
+ name: "b",
+ }
+
+ filegroup {
+ name: "c",
+ }
+
+ filegroup {
+ name: "d",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ ctx.Register()
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ m := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*pathDepsMutatorTestModule)
+
+ if g, w := m.sourceDeps, test.deps; !reflect.DeepEqual(g, w) {
+ t.Errorf("want deps %q, got %q", w, g)
+ }
+ })
+ }
+}
diff --git a/android/paths.go b/android/paths.go
index 91dd9a6..8cc7057 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -47,6 +47,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
}
var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -216,19 +217,139 @@
return ret
}
-// PathsForModuleSrc returns Paths rooted from the module's local source
-// directory
+// PathsForModuleSrc returns Paths rooted from the module's local source directory. It expands globs and references
+// to SourceFileProducer modules using the ":name" syntax. Properties passed as the paths argument must have been
+// annotated with struct tag `android:"path"` so that dependencies on SourceFileProducer modules will have already
+// been handled by the path_properties mutator. If ctx.Config().AllowMissingDependencies() is true, then any missing
+// SourceFileProducer dependencies will cause the module to be marked as having missing dependencies.
func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
- ret := make(Paths, len(paths))
- for i, path := range paths {
- ret[i] = PathForModuleSrc(ctx, path)
+ return PathsForModuleSrcExcludes(ctx, paths, nil)
+}
+
+// PathsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding paths listed in
+// the excludes arguments. It expands globs and references to SourceFileProducer modules in both paths and excludes
+// using the ":name" syntax. Properties passed as the paths or excludes argument must have been annotated with struct
+// tag `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
+// path_properties mutator. If ctx.Config().AllowMissingDependencies() is true, then any missing SourceFileProducer
+// dependencies will cause the module to be marked as having missing dependencies.
+func PathsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) Paths {
+ ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
+ if ctx.Config().AllowMissingDependencies() {
+ ctx.AddMissingDependencies(missingDeps)
+ } else {
+ for _, m := range missingDeps {
+ ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
+ }
}
return ret
}
+// PathsAndMissingDepsForModuleSrcExcludes returns Paths rooted from the module's local source directory, excluding
+// paths listed in the excludes arguments, and a list of missing dependencies. It expands globs and references to
+// SourceFileProducer modules in both paths and excludes using the ":name" syntax. Properties passed as the paths or
+// excludes argument must have been annotated with struct tag `android:"path"` so that dependencies on
+// SourceFileProducer modules will have already been handled by the path_properties mutator. If
+// ctx.Config().AllowMissingDependencies() is true, then any missing SourceFileProducer dependencies will be returned,
+// and they will NOT cause the module to be marked as having missing dependencies.
+func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleContext, paths, excludes []string) (Paths, []string) {
+ prefix := pathForModuleSrc(ctx).String()
+
+ var expandedExcludes []string
+ if excludes != nil {
+ expandedExcludes = make([]string, 0, len(excludes))
+ }
+
+ var missingExcludeDeps []string
+
+ for _, e := range excludes {
+ if m := SrcIsModule(e); m != "" {
+ module := ctx.GetDirectDepWithTag(m, SourceDepTag)
+ if module == nil {
+ missingExcludeDeps = append(missingExcludeDeps, m)
+ continue
+ }
+ if srcProducer, ok := module.(SourceFileProducer); ok {
+ expandedExcludes = append(expandedExcludes, srcProducer.Srcs().Strings()...)
+ } else {
+ ctx.ModuleErrorf("srcs dependency %q is not a source file producing module", m)
+ }
+ } else {
+ expandedExcludes = append(expandedExcludes, filepath.Join(prefix, e))
+ }
+ }
+
+ if paths == nil {
+ return nil, missingExcludeDeps
+ }
+
+ var missingDeps []string
+
+ expandedSrcFiles := make(Paths, 0, len(paths))
+ for _, s := range paths {
+ srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes)
+ if depErr, ok := err.(missingDependencyError); ok {
+ missingDeps = append(missingDeps, depErr.missingDeps...)
+ } else if err != nil {
+ reportPathError(ctx, err)
+ }
+ expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
+ }
+
+ return expandedSrcFiles, append(missingDeps, missingExcludeDeps...)
+}
+
+type missingDependencyError struct {
+ missingDeps []string
+}
+
+func (e missingDependencyError) Error() string {
+ return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
+}
+
+func expandOneSrcPath(ctx ModuleContext, s string, expandedExcludes []string) (Paths, error) {
+ if m := SrcIsModule(s); m != "" {
+ module := ctx.GetDirectDepWithTag(m, SourceDepTag)
+ if module == nil {
+ return nil, missingDependencyError{[]string{m}}
+ }
+ if srcProducer, ok := module.(SourceFileProducer); ok {
+ moduleSrcs := srcProducer.Srcs()
+ for _, e := range expandedExcludes {
+ for j := 0; j < len(moduleSrcs); j++ {
+ if moduleSrcs[j].String() == e {
+ moduleSrcs = append(moduleSrcs[:j], moduleSrcs[j+1:]...)
+ j--
+ }
+ }
+ }
+ return moduleSrcs, nil
+ } else {
+ return nil, fmt.Errorf("path dependency %q is not a source file producing module", m)
+ }
+ } else if pathtools.IsGlob(s) {
+ paths := ctx.GlobFiles(pathForModuleSrc(ctx, s).String(), expandedExcludes)
+ return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
+ } else {
+ p := pathForModuleSrc(ctx, s)
+ if exists, _, err := ctx.Fs().Exists(p.String()); err != nil {
+ reportPathErrorf(ctx, "%s: %s", p, err.Error())
+ } else if !exists {
+ reportPathErrorf(ctx, "module source path %q does not exist", p)
+ }
+
+ j := findStringInSlice(p.String(), expandedExcludes)
+ if j >= 0 {
+ return nil, nil
+ }
+ return Paths{p}, nil
+ }
+}
+
// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
// source directory, but strip the local source directory from the beginning of
// each string. If incDirs is false, strip paths with a trailing '/' from the list.
+// It intended for use in globs that only list files that exist, so it allows '$' in
+// filenames.
func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string, incDirs bool) Paths {
prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
if prefix == "./" {
@@ -241,18 +362,27 @@
}
path := filepath.Clean(p)
if !strings.HasPrefix(path, prefix) {
- reportPathErrorf(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
+ reportPathErrorf(ctx, "Path %q is not in module source directory %q", p, prefix)
continue
}
- ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
+
+ srcPath, err := safePathForSource(ctx, ctx.ModuleDir(), path[len(prefix):])
+ if err != nil {
+ reportPathError(ctx, err)
+ continue
+ }
+
+ srcPath.basePath.rel = srcPath.path
+
+ ret = append(ret, srcPath)
}
return ret
}
// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
-// local source directory. If none are provided, use the default if it exists.
+// local source directory. If input is nil, use the default if it exists. If input is empty, returns nil.
func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
- if len(input) > 0 {
+ if input != nil {
return PathsForModuleSrc(ctx, input)
}
// Use Glob so that if the default doesn't exist, a dependency is added so that when it
@@ -483,29 +613,19 @@
// safePathForSource is for paths that we expect are safe -- only for use by go
// code that is embedding ninja variables in paths
-func safePathForSource(ctx PathContext, path string) SourcePath {
- p, err := validateSafePath(path)
- if err != nil {
- reportPathError(ctx, err)
- }
+func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
+ p, err := validateSafePath(pathComponents...)
ret := SourcePath{basePath{p, ctx.Config(), ""}}
-
- abs, err := filepath.Abs(ret.String())
if err != nil {
- reportPathError(ctx, err)
- return ret
- }
- buildroot, err := filepath.Abs(ctx.Config().buildDir)
- if err != nil {
- reportPathError(ctx, err)
- return ret
- }
- if strings.HasPrefix(abs, buildroot) {
- reportPathErrorf(ctx, "source path %s is in output", abs)
- return ret
+ return ret, err
}
- return ret
+ // absolute path already checked by validateSafePath
+ if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
+ return ret, fmt.Errorf("source path %q is in output", ret.String())
+ }
+
+ return ret, err
}
// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
@@ -516,20 +636,9 @@
return ret, err
}
- abs, err := filepath.Abs(ret.String())
- if err != nil {
- return ret, err
- }
- buildroot, err := filepath.Abs(ctx.Config().buildDir)
- if err != nil {
- return ret, err
- }
- if strings.HasPrefix(abs, buildroot) {
- return ret, fmt.Errorf("source path %s is in output", abs)
- }
-
- if pathtools.IsGlob(ret.String()) {
- return ret, fmt.Errorf("path may not contain a glob: %s", ret.String())
+ // absolute path already checked by validatePath
+ if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
+ return ret, fmt.Errorf("source path %q is in output", ret.String())
}
return ret, nil
@@ -548,7 +657,7 @@
var deps []string
// We cannot add build statements in this context, so we fall back to
// AddNinjaFileDeps
- files, deps, err = pathtools.Glob(path.String(), nil)
+ files, deps, err = pathtools.Glob(path.String(), nil, pathtools.FollowSymlinks)
ctx.AddNinjaFileDeps(deps...)
}
@@ -568,6 +677,10 @@
reportPathError(ctx, err)
}
+ if pathtools.IsGlob(path.String()) {
+ reportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
+ }
+
if modCtx, ok := ctx.(ModuleContext); ok && ctx.Config().AllowMissingDependencies() {
exists, err := existsWithDependencies(ctx, path)
if err != nil {
@@ -579,7 +692,7 @@
} else if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
reportPathErrorf(ctx, "%s: %s", path, err.Error())
} else if !exists {
- reportPathErrorf(ctx, "source path %s does not exist", path)
+ reportPathErrorf(ctx, "source path %q does not exist", path)
}
return path
}
@@ -594,6 +707,11 @@
return OptionalPath{}
}
+ if pathtools.IsGlob(path.String()) {
+ reportPathErrorf(ctx, "path may not contain a glob: %s", path.String())
+ return OptionalPath{}
+ }
+
exists, err := existsWithDependencies(ctx, path)
if err != nil {
reportPathError(ctx, err)
@@ -619,13 +737,20 @@
return p.withRel(path)
}
+// join is like Join but does less path validation.
+func (p SourcePath) join(ctx PathContext, paths ...string) SourcePath {
+ path, err := validateSafePath(paths...)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+ return p.withRel(path)
+}
+
// OverlayPath returns the overlay for `path' if it exists. This assumes that the
// SourcePath is the path to a resource overlay directory.
func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
var relDir string
- if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
- relDir = moduleSrcPath.path
- } else if srcPath, ok := path.(SourcePath); ok {
+ if srcPath, ok := path.(SourcePath); ok {
relDir = srcPath.path
} else {
reportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
@@ -644,11 +769,7 @@
if len(paths) == 0 {
return OptionalPath{}
}
- relPath, err := filepath.Rel(p.config.srcDir, paths[0])
- if err != nil {
- reportPathError(ctx, err)
- return OptionalPath{}
- }
+ relPath := Rel(ctx, p.config.srcDir, paths[0])
return OptionalPathForPath(PathForSource(ctx, relPath))
}
@@ -662,6 +783,11 @@
return p
}
+func (p OutputPath) WithoutRel() OutputPath {
+ p.basePath.rel = filepath.Base(p.basePath.path)
+ return p
+}
+
var _ Path = OutputPath{}
// PathForOutput joins the provided paths and returns an OutputPath that is
@@ -675,6 +801,15 @@
return OutputPath{basePath{path, ctx.Config(), ""}}
}
+// PathsForOutput returns Paths rooted from buildDir
+func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
+ ret := make(WritablePaths, len(paths))
+ for i, path := range paths {
+ ret[i] = PathForOutput(ctx, path)
+ }
+ return ret
+}
+
func (p OutputPath) writablePath() {}
func (p OutputPath) String() string {
@@ -695,6 +830,28 @@
return p.withRel(path)
}
+// ReplaceExtension creates a new OutputPath with the extension replaced with ext.
+func (p OutputPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
+ if strings.Contains(ext, "/") {
+ reportPathErrorf(ctx, "extension %q cannot contain /", ext)
+ }
+ ret := PathForOutput(ctx, pathtools.ReplaceExtension(p.path, ext))
+ ret.rel = pathtools.ReplaceExtension(p.rel, ext)
+ return ret
+}
+
+// InSameDir creates a new OutputPath from the directory of the current OutputPath joined with the elements in paths.
+func (p OutputPath) InSameDir(ctx PathContext, paths ...string) OutputPath {
+ path, err := validatePath(paths...)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+
+ ret := PathForOutput(ctx, filepath.Dir(p.path), path)
+ ret.rel = filepath.Join(filepath.Dir(p.rel), path)
+ return ret
+}
+
// PathForIntermediates returns an OutputPath representing the top-level
// intermediates directory.
func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
@@ -705,81 +862,75 @@
return PathForOutput(ctx, ".intermediates", path)
}
-// DistPath is a Path representing a file path rooted from the dist directory
-type DistPath struct {
- basePath
-}
+var _ genPathProvider = SourcePath{}
+var _ objPathProvider = SourcePath{}
+var _ resPathProvider = SourcePath{}
-func (p DistPath) withRel(rel string) DistPath {
- p.basePath = p.basePath.withRel(rel)
- return p
-}
-
-var _ Path = DistPath{}
-
-// PathForDist joins the provided paths and returns a DistPath that is
-// validated to not escape the dist dir.
-// On error, it will return a usable, but invalid DistPath, and report a ModuleError.
-func PathForDist(ctx PathContext, pathComponents ...string) DistPath {
- path, err := validatePath(pathComponents...)
+// PathForModuleSrc returns a Path representing the paths... under the
+// module's local source directory.
+func PathForModuleSrc(ctx ModuleContext, pathComponents ...string) Path {
+ p, err := validatePath(pathComponents...)
if err != nil {
reportPathError(ctx, err)
}
- return DistPath{basePath{path, ctx.Config(), ""}}
-}
-
-func (p DistPath) writablePath() {}
-
-func (p DistPath) Valid() bool {
- return p.config.productVariables.DistDir != nil && *p.config.productVariables.DistDir != ""
-}
-
-func (p DistPath) String() string {
- if !p.Valid() {
- panic("Requesting an invalid path")
+ paths, err := expandOneSrcPath(ctx, p, nil)
+ if err != nil {
+ if depErr, ok := err.(missingDependencyError); ok {
+ if ctx.Config().AllowMissingDependencies() {
+ ctx.AddMissingDependencies(depErr.missingDeps)
+ } else {
+ ctx.ModuleErrorf(`%s, is the property annotated with android:"path"?`, depErr.Error())
+ }
+ } else {
+ reportPathError(ctx, err)
+ }
+ return nil
+ } else if len(paths) == 0 {
+ reportPathErrorf(ctx, "%q produced no files, expected exactly one", p)
+ return nil
+ } else if len(paths) > 1 {
+ reportPathErrorf(ctx, "%q produced %d files, expected exactly one", p, len(paths))
}
- return filepath.Join(*p.config.productVariables.DistDir, p.path)
+ return paths[0]
}
-func (p DistPath) RelPathString() string {
- return p.path
-}
-
-// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
-type ModuleSrcPath struct {
- SourcePath
-}
-
-var _ Path = ModuleSrcPath{}
-var _ genPathProvider = ModuleSrcPath{}
-var _ objPathProvider = ModuleSrcPath{}
-var _ resPathProvider = ModuleSrcPath{}
-
-// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
-// module's local source directory.
-func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
+func pathForModuleSrc(ctx ModuleContext, paths ...string) SourcePath {
p, err := validatePath(paths...)
if err != nil {
reportPathError(ctx, err)
}
- srcPath, err := pathForSource(ctx, ctx.ModuleDir(), p)
+ path, err := pathForSource(ctx, ctx.ModuleDir(), p)
if err != nil {
reportPathError(ctx, err)
}
- path := ModuleSrcPath{srcPath}
path.basePath.rel = p
- if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
- reportPathErrorf(ctx, "%s: %s", path, err.Error())
- } else if !exists {
- reportPathErrorf(ctx, "module source path %s does not exist", path)
- }
-
return path
}
+// PathsWithModuleSrcSubDir takes a list of Paths and returns a new list of Paths where Rel() on each path
+// will return the path relative to subDir in the module's source directory. If any input paths are not located
+// inside subDir then a path error will be reported.
+func PathsWithModuleSrcSubDir(ctx ModuleContext, paths Paths, subDir string) Paths {
+ paths = append(Paths(nil), paths...)
+ subDirFullPath := pathForModuleSrc(ctx, subDir)
+ for i, path := range paths {
+ rel := Rel(ctx, subDirFullPath.String(), path.String())
+ paths[i] = subDirFullPath.join(ctx, rel)
+ }
+ return paths
+}
+
+// PathWithModuleSrcSubDir takes a Path and returns a Path where Rel() will return the path relative to subDir in the
+// module's source directory. If the input path is not located inside subDir then a path error will be reported.
+func PathWithModuleSrcSubDir(ctx ModuleContext, path Path, subDir string) Path {
+ subDirFullPath := pathForModuleSrc(ctx, subDir)
+ rel := Rel(ctx, subDirFullPath.String(), path.String())
+ return subDirFullPath.Join(ctx, rel)
+}
+
// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
// valid path if p is non-nil.
func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
@@ -789,31 +940,19 @@
return OptionalPathForPath(PathForModuleSrc(ctx, *p))
}
-func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
+func (p SourcePath) genPathWithExt(ctx ModuleContext, subdir, ext string) ModuleGenPath {
return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
-func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+func (p SourcePath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
}
-func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
+func (p SourcePath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
// TODO: Use full directory if the new ctx is not the current ctx?
return PathForModuleRes(ctx, p.path, name)
}
-func (p ModuleSrcPath) WithSubDir(ctx ModuleContext, subdir string) ModuleSrcPath {
- subdir = PathForModuleSrc(ctx, subdir).String()
- var err error
- rel, err := filepath.Rel(subdir, p.path)
- if err != nil {
- ctx.ModuleErrorf("source file %q is not under path %q", p.path, subdir)
- return p
- }
- p.rel = rel
- return p
-}
-
// ModuleOutPath is a Path representing a module's output directory.
type ModuleOutPath struct {
OutputPath
@@ -825,37 +964,40 @@
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
}
-// PathForVndkRefDump returns an OptionalPath representing the path of the reference
-// abi dump for the given module. This is not guaranteed to be valid.
-func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNdk, isSourceDump bool) OptionalPath {
+// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
+// reference abi dump for the given module. This is not guaranteed to be valid.
+func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
+ isLlndk, isGzip bool) OptionalPath {
+
arches := ctx.DeviceConfig().Arches()
+ if len(arches) == 0 {
+ panic("device build with no primary arch")
+ }
currentArch := ctx.Arch()
archNameAndVariant := currentArch.ArchType.String()
if currentArch.ArchVariant != "" {
archNameAndVariant += "_" + currentArch.ArchVariant
}
- var sourceOrBinaryDir string
- var vndkOrNdkDir string
- var ext string
- if isSourceDump {
- ext = ".lsdump.gz"
- sourceOrBinaryDir = "source-based"
+
+ var dirName string
+ if isLlndk {
+ dirName = "ndk"
} else {
- ext = ".bdump.gz"
- sourceOrBinaryDir = "binary-based"
+ dirName = "vndk"
}
- if vndkOrNdk {
- vndkOrNdkDir = "vndk"
- } else {
- vndkOrNdkDir = "ndk"
- }
- if len(arches) == 0 {
- panic("device build with no primary arch")
- }
+
binderBitness := ctx.DeviceConfig().BinderBitness()
- refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" + binderBitness + "/" +
- archNameAndVariant + "/" + sourceOrBinaryDir + "/" + fileName + ext
- return ExistentPathForSource(ctx, refDumpFileStr)
+
+ var ext string
+ if isGzip {
+ ext = ".lsdump.gz"
+ } else {
+ ext = ".lsdump"
+ }
+
+ return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
+ version, binderBitness, archNameAndVariant, "source-based",
+ fileName+ext)
}
// PathForModuleOut returns a Path representing the paths... under the module's
@@ -945,22 +1087,7 @@
func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
var outPaths []string
if ctx.Device() {
- var partition string
- if ctx.InstallInData() {
- partition = "data"
- } else if ctx.SocSpecific() {
- partition = ctx.DeviceConfig().VendorPath()
- } else if ctx.DeviceSpecific() {
- partition = ctx.DeviceConfig().OdmPath()
- } else if ctx.ProductSpecific() {
- partition = ctx.DeviceConfig().ProductPath()
- } else {
- partition = "system"
- }
-
- if ctx.InstallInSanitizerDir() {
- partition = "data/asan/" + partition
- }
+ partition := modulePartition(ctx)
outPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
} else {
switch ctx.Os() {
@@ -980,6 +1107,36 @@
return PathForOutput(ctx, outPaths...)
}
+func InstallPathToOnDevicePath(ctx PathContext, path OutputPath) string {
+ rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
+
+ return "/" + rel
+}
+
+func modulePartition(ctx ModuleInstallPathContext) string {
+ var partition string
+ if ctx.InstallInData() {
+ partition = "data"
+ } else if ctx.InstallInRecovery() {
+ // the layout of recovery partion is the same as that of system partition
+ partition = "recovery/root/system"
+ } else if ctx.SocSpecific() {
+ partition = ctx.DeviceConfig().VendorPath()
+ } else if ctx.DeviceSpecific() {
+ partition = ctx.DeviceConfig().OdmPath()
+ } else if ctx.ProductSpecific() {
+ partition = ctx.DeviceConfig().ProductPath()
+ } else if ctx.ProductServicesSpecific() {
+ partition = ctx.DeviceConfig().ProductServicesPath()
+ } else {
+ partition = "system"
+ }
+ if ctx.InstallInSanitizerDir() {
+ partition = "data/asan/" + partition
+ }
+ return partition
+}
+
// validateSafePath validates a path that we trust (may contain ninja variables).
// Ensures that each path component does not attempt to leave its component.
func validateSafePath(pathComponents ...string) (string, error) {
@@ -1031,6 +1188,14 @@
return p.path
}
+type testWritablePath struct {
+ testPath
+}
+
+func (p testPath) writablePath() {}
+
+// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from
+// within tests.
func PathForTesting(paths ...string) Path {
p, err := validateSafePath(paths...)
if err != nil {
@@ -1039,7 +1204,8 @@
return testPath{basePath{path: p, rel: p}}
}
-func PathsForTesting(strs []string) Paths {
+// PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests.
+func PathsForTesting(strs ...string) Paths {
p := make(Paths, len(strs))
for i, s := range strs {
p[i] = PathForTesting(s)
@@ -1047,3 +1213,70 @@
return p
}
+
+// WritablePathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be
+// used from within tests.
+func WritablePathForTesting(paths ...string) WritablePath {
+ p, err := validateSafePath(paths...)
+ if err != nil {
+ panic(err)
+ }
+ return testWritablePath{testPath{basePath{path: p, rel: p}}}
+}
+
+// WritablePathsForTesting returns a Path constructed from each element in strs. It should only be used from within
+// tests.
+func WritablePathsForTesting(strs ...string) WritablePaths {
+ p := make(WritablePaths, len(strs))
+ for i, s := range strs {
+ p[i] = WritablePathForTesting(s)
+ }
+
+ return p
+}
+
+type testPathContext struct {
+ config Config
+ fs pathtools.FileSystem
+}
+
+func (x *testPathContext) Fs() pathtools.FileSystem { return x.fs }
+func (x *testPathContext) Config() Config { return x.config }
+func (x *testPathContext) AddNinjaFileDeps(...string) {}
+
+// PathContextForTesting returns a PathContext that can be used in tests, for example to create an OutputPath with
+// PathForOutput.
+func PathContextForTesting(config Config, fs map[string][]byte) PathContext {
+ return &testPathContext{
+ config: config,
+ fs: pathtools.MockFs(fs),
+ }
+}
+
+// Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if
+// targetPath is not inside basePath.
+func Rel(ctx PathContext, basePath string, targetPath string) string {
+ rel, isRel := MaybeRel(ctx, basePath, targetPath)
+ if !isRel {
+ reportPathErrorf(ctx, "path %q is not under path %q", targetPath, basePath)
+ return ""
+ }
+ return rel
+}
+
+// MaybeRel performs the same function as filepath.Rel, but reports errors to a PathContext, and returns false if
+// targetPath is not inside basePath.
+func MaybeRel(ctx PathContext, basePath string, targetPath string) (string, bool) {
+ // filepath.Rel returns an error if one path is absolute and the other is not, handle that case first.
+ if filepath.IsAbs(basePath) != filepath.IsAbs(targetPath) {
+ return "", false
+ }
+ rel, err := filepath.Rel(basePath, targetPath)
+ if err != nil {
+ reportPathError(ctx, err)
+ return "", false
+ } else if rel == ".." || strings.HasPrefix(rel, "../") || strings.HasPrefix(rel, "/") {
+ return "", false
+ }
+ return rel, true
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index cd9fbfd..b52d713 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -17,11 +17,14 @@
import (
"errors"
"fmt"
+ "io/ioutil"
+ "os"
"reflect"
"strings"
"testing"
"github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
)
type strsTestCase struct {
@@ -201,6 +204,7 @@
inData bool
inSanitizerDir bool
+ inRecovery bool
}
func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
@@ -221,6 +225,10 @@
return m.inSanitizerDir
}
+func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
+ return m.inRecovery
+}
+
func TestPathForModuleInstall(t *testing.T) {
testConfig := TestConfig("", nil)
@@ -287,6 +295,17 @@
in: []string{"bin", "my_test"},
out: "target/product/test_device/product/bin/my_test",
},
+ {
+ name: "product_services binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productServicesSpecificModule,
+ },
+ },
+ in: []string{"bin", "my_test"},
+ out: "target/product/test_device/product_services/bin/my_test",
+ },
{
name: "system native test binary",
@@ -337,6 +356,19 @@
},
{
+ name: "product_services native test binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productServicesSpecificModule,
+ },
+ inData: true,
+ },
+ in: []string{"nativetest", "my_test"},
+ out: "target/product/test_device/data/nativetest/my_test",
+ },
+
+ {
name: "sanitized system binary",
ctx: &moduleInstallPathContextImpl{
androidBaseContextImpl: androidBaseContextImpl{
@@ -385,6 +417,19 @@
},
{
+ name: "sanitized product_services binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productServicesSpecificModule,
+ },
+ inSanitizerDir: true,
+ },
+ in: []string{"bin", "my_test"},
+ out: "target/product/test_device/data/asan/product_services/bin/my_test",
+ },
+
+ {
name: "sanitized system native test binary",
ctx: &moduleInstallPathContextImpl{
androidBaseContextImpl: androidBaseContextImpl{
@@ -435,6 +480,19 @@
in: []string{"nativetest", "my_test"},
out: "target/product/test_device/data/asan/data/nativetest/my_test",
},
+ {
+ name: "sanitized product_services native test binary",
+ ctx: &moduleInstallPathContextImpl{
+ androidBaseContextImpl: androidBaseContextImpl{
+ target: deviceTarget,
+ kind: productServicesSpecificModule,
+ },
+ inData: true,
+ inSanitizerDir: true,
+ },
+ in: []string{"nativetest", "my_test"},
+ out: "target/product/test_device/data/asan/data/nativetest/my_test",
+ },
}
for _, tc := range testCases {
@@ -451,15 +509,27 @@
}
func TestDirectorySortedPaths(t *testing.T) {
+ config := TestConfig("out", nil)
+
+ ctx := PathContextForTesting(config, map[string][]byte{
+ "a.txt": nil,
+ "a/txt": nil,
+ "a/b/c": nil,
+ "a/b/d": nil,
+ "b": nil,
+ "b/b.txt": nil,
+ "a/a.txt": nil,
+ })
+
makePaths := func() Paths {
return Paths{
- PathForTesting("a.txt"),
- PathForTesting("a/txt"),
- PathForTesting("a/b/c"),
- PathForTesting("a/b/d"),
- PathForTesting("b"),
- PathForTesting("b/b.txt"),
- PathForTesting("a/a.txt"),
+ PathForSource(ctx, "a.txt"),
+ PathForSource(ctx, "a/txt"),
+ PathForSource(ctx, "a/b/c"),
+ PathForSource(ctx, "a/b/d"),
+ PathForSource(ctx, "b"),
+ PathForSource(ctx, "b/b.txt"),
+ PathForSource(ctx, "a/a.txt"),
}
}
@@ -518,3 +588,454 @@
t.Errorf("FilesInDirectory(b):\n %#v\n != \n %#v", inA.Strings(), expectedA)
}
}
+
+func TestMaybeRel(t *testing.T) {
+ testCases := []struct {
+ name string
+ base string
+ target string
+ out string
+ isRel bool
+ }{
+ {
+ name: "normal",
+ base: "a/b/c",
+ target: "a/b/c/d",
+ out: "d",
+ isRel: true,
+ },
+ {
+ name: "parent",
+ base: "a/b/c/d",
+ target: "a/b/c",
+ isRel: false,
+ },
+ {
+ name: "not relative",
+ base: "a/b",
+ target: "c/d",
+ isRel: false,
+ },
+ {
+ name: "abs1",
+ base: "/a",
+ target: "a",
+ isRel: false,
+ },
+ {
+ name: "abs2",
+ base: "a",
+ target: "/a",
+ isRel: false,
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ ctx := &configErrorWrapper{}
+ out, isRel := MaybeRel(ctx, testCase.base, testCase.target)
+ if len(ctx.errors) > 0 {
+ t.Errorf("MaybeRel(..., %s, %s) reported unexpected errors %v",
+ testCase.base, testCase.target, ctx.errors)
+ }
+ if isRel != testCase.isRel || out != testCase.out {
+ t.Errorf("MaybeRel(..., %s, %s) want %v, %v got %v, %v",
+ testCase.base, testCase.target, testCase.out, testCase.isRel, out, isRel)
+ }
+ })
+ }
+}
+
+func TestPathForSource(t *testing.T) {
+ testCases := []struct {
+ name string
+ buildDir string
+ src string
+ err string
+ }{
+ {
+ name: "normal",
+ buildDir: "out",
+ src: "a/b/c",
+ },
+ {
+ name: "abs",
+ buildDir: "out",
+ src: "/a/b/c",
+ err: "is outside directory",
+ },
+ {
+ name: "in out dir",
+ buildDir: "out",
+ src: "out/a/b/c",
+ err: "is in output",
+ },
+ }
+
+ funcs := []struct {
+ name string
+ f func(ctx PathContext, pathComponents ...string) (SourcePath, error)
+ }{
+ {"pathForSource", pathForSource},
+ {"safePathForSource", safePathForSource},
+ }
+
+ for _, f := range funcs {
+ t.Run(f.name, func(t *testing.T) {
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ testConfig := TestConfig(test.buildDir, nil)
+ ctx := &configErrorWrapper{config: testConfig}
+ _, err := f.f(ctx, test.src)
+ if len(ctx.errors) > 0 {
+ t.Fatalf("unexpected errors %v", ctx.errors)
+ }
+ if err != nil {
+ if test.err == "" {
+ t.Fatalf("unexpected error %q", err.Error())
+ } else if !strings.Contains(err.Error(), test.err) {
+ t.Fatalf("incorrect error, want substring %q got %q", test.err, err.Error())
+ }
+ } else {
+ if test.err != "" {
+ t.Fatalf("missing error %q", test.err)
+ }
+ }
+ })
+ }
+ })
+ }
+}
+
+type pathForModuleSrcTestModule struct {
+ ModuleBase
+ props struct {
+ Srcs []string `android:"path"`
+ Exclude_srcs []string `android:"path"`
+
+ Src *string `android:"path"`
+
+ Module_handles_missing_deps bool
+ }
+
+ src string
+ rel string
+
+ srcs []string
+ rels []string
+
+ missingDeps []string
+}
+
+func pathForModuleSrcTestModuleFactory() Module {
+ module := &pathForModuleSrcTestModule{}
+ module.AddProperties(&module.props)
+ InitAndroidModule(module)
+ return module
+}
+
+func (p *pathForModuleSrcTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ var srcs Paths
+ if p.props.Module_handles_missing_deps {
+ srcs, p.missingDeps = PathsAndMissingDepsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs)
+ } else {
+ srcs = PathsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs)
+ }
+ p.srcs = srcs.Strings()
+
+ for _, src := range srcs {
+ p.rels = append(p.rels, src.Rel())
+ }
+
+ if p.props.Src != nil {
+ src := PathForModuleSrc(ctx, *p.props.Src)
+ if src != nil {
+ p.src = src.String()
+ p.rel = src.Rel()
+ }
+ }
+
+ if !p.props.Module_handles_missing_deps {
+ p.missingDeps = ctx.GetMissingDependencies()
+ }
+}
+
+type pathForModuleSrcTestCase struct {
+ name string
+ bp string
+ srcs []string
+ rels []string
+ src string
+ rel string
+}
+
+func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSrcTestCase) {
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ config := TestConfig(buildDir, nil)
+ ctx := NewTestContext()
+
+ ctx.RegisterModuleType("test", ModuleFactoryAdaptor(pathForModuleSrcTestModuleFactory))
+ ctx.RegisterModuleType("filegroup", ModuleFactoryAdaptor(FileGroupFactory))
+
+ fgBp := `
+ filegroup {
+ name: "a",
+ srcs: ["src/a"],
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "fg/Android.bp": []byte(fgBp),
+ "foo/Android.bp": []byte(test.bp),
+ "fg/src/a": nil,
+ "foo/src/b": nil,
+ "foo/src/c": nil,
+ "foo/src/d": nil,
+ "foo/src/e/e": nil,
+ "foo/src_special/$": nil,
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ ctx.Register()
+ _, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ m := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
+
+ if g, w := m.srcs, test.srcs; !reflect.DeepEqual(g, w) {
+ t.Errorf("want srcs %q, got %q", w, g)
+ }
+
+ if g, w := m.rels, test.rels; !reflect.DeepEqual(g, w) {
+ t.Errorf("want rels %q, got %q", w, g)
+ }
+
+ if g, w := m.src, test.src; g != w {
+ t.Errorf("want src %q, got %q", w, g)
+ }
+
+ if g, w := m.rel, test.rel; g != w {
+ t.Errorf("want rel %q, got %q", w, g)
+ }
+ })
+ }
+}
+
+func TestPathsForModuleSrc(t *testing.T) {
+ tests := []pathForModuleSrcTestCase{
+ {
+ name: "path",
+ bp: `
+ test {
+ name: "foo",
+ srcs: ["src/b"],
+ }`,
+ srcs: []string{"foo/src/b"},
+ rels: []string{"src/b"},
+ },
+ {
+ name: "glob",
+ bp: `
+ test {
+ name: "foo",
+ srcs: [
+ "src/*",
+ "src/e/*",
+ ],
+ }`,
+ srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"},
+ rels: []string{"src/b", "src/c", "src/d", "src/e/e"},
+ },
+ {
+ name: "recursive glob",
+ bp: `
+ test {
+ name: "foo",
+ srcs: ["src/**/*"],
+ }`,
+ srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"},
+ rels: []string{"src/b", "src/c", "src/d", "src/e/e"},
+ },
+ {
+ name: "filegroup",
+ bp: `
+ test {
+ name: "foo",
+ srcs: [":a"],
+ }`,
+ srcs: []string{"fg/src/a"},
+ rels: []string{"src/a"},
+ },
+ {
+ name: "special characters glob",
+ bp: `
+ test {
+ name: "foo",
+ srcs: ["src_special/*"],
+ }`,
+ srcs: []string{"foo/src_special/$"},
+ rels: []string{"src_special/$"},
+ },
+ }
+
+ buildDir, err := ioutil.TempDir("", "soong_paths_for_module_src_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ testPathForModuleSrc(t, buildDir, tests)
+}
+
+func TestPathForModuleSrc(t *testing.T) {
+ tests := []pathForModuleSrcTestCase{
+ {
+ name: "path",
+ bp: `
+ test {
+ name: "foo",
+ src: "src/b",
+ }`,
+ src: "foo/src/b",
+ rel: "src/b",
+ },
+ {
+ name: "glob",
+ bp: `
+ test {
+ name: "foo",
+ src: "src/e/*",
+ }`,
+ src: "foo/src/e/e",
+ rel: "src/e/e",
+ },
+ {
+ name: "filegroup",
+ bp: `
+ test {
+ name: "foo",
+ src: ":a",
+ }`,
+ src: "fg/src/a",
+ rel: "src/a",
+ },
+ {
+ name: "special characters glob",
+ bp: `
+ test {
+ name: "foo",
+ src: "src_special/*",
+ }`,
+ src: "foo/src_special/$",
+ rel: "src_special/$",
+ },
+ }
+
+ buildDir, err := ioutil.TempDir("", "soong_path_for_module_src_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ testPathForModuleSrc(t, buildDir, tests)
+}
+
+func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) {
+ buildDir, err := ioutil.TempDir("", "soong_paths_for_module_src_allow_missing_dependencies_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ config := TestConfig(buildDir, nil)
+ config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+
+ ctx := NewTestContext()
+ ctx.SetAllowMissingDependencies(true)
+
+ ctx.RegisterModuleType("test", ModuleFactoryAdaptor(pathForModuleSrcTestModuleFactory))
+
+ bp := `
+ test {
+ name: "foo",
+ srcs: [":a"],
+ exclude_srcs: [":b"],
+ src: ":c",
+ }
+
+ test {
+ name: "bar",
+ srcs: [":d"],
+ exclude_srcs: [":e"],
+ module_handles_missing_deps: true,
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ ctx.Register()
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ foo := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
+
+ if g, w := foo.missingDeps, []string{"a", "b", "c"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("want foo missing deps %q, got %q", w, g)
+ }
+
+ if g, w := foo.srcs, []string{}; !reflect.DeepEqual(g, w) {
+ t.Errorf("want foo srcs %q, got %q", w, g)
+ }
+
+ if g, w := foo.src, ""; g != w {
+ t.Errorf("want foo src %q, got %q", w, g)
+ }
+
+ bar := ctx.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule)
+
+ if g, w := bar.missingDeps, []string{"d", "e"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("want bar missing deps %q, got %q", w, g)
+ }
+
+ if g, w := bar.srcs, []string{}; !reflect.DeepEqual(g, w) {
+ t.Errorf("want bar srcs %q, got %q", w, g)
+ }
+}
+
+func ExampleOutputPath_ReplaceExtension() {
+ ctx := &configErrorWrapper{
+ config: TestConfig("out", nil),
+ }
+ p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
+ p2 := p.ReplaceExtension(ctx, "oat")
+ fmt.Println(p, p2)
+ fmt.Println(p.Rel(), p2.Rel())
+
+ // Output:
+ // out/system/framework/boot.art out/system/framework/boot.oat
+ // boot.art boot.oat
+}
+
+func ExampleOutputPath_FileInSameDir() {
+ ctx := &configErrorWrapper{
+ config: TestConfig("out", nil),
+ }
+ p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
+ p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex")
+ fmt.Println(p, p2)
+ fmt.Println(p.Rel(), p2.Rel())
+
+ // Output:
+ // out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex
+ // boot.art oat/arm/boot.vdex
+}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 9356aab..3be10f7 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -18,6 +18,7 @@
"fmt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
// This file implements common functionality for handling modules that may exist as prebuilts,
@@ -42,6 +43,7 @@
properties PrebuiltProperties
module Module
srcs *[]string
+ src *string
}
func (p *Prebuilt) Name(name string) string {
@@ -49,17 +51,31 @@
}
func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
- if len(*p.srcs) == 0 {
- ctx.PropertyErrorf("srcs", "missing prebuilt source file")
- return nil
- }
+ if p.srcs != nil {
+ if len(*p.srcs) == 0 {
+ ctx.PropertyErrorf("srcs", "missing prebuilt source file")
+ return nil
+ }
- if len(*p.srcs) > 1 {
- ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
- return nil
- }
+ if len(*p.srcs) > 1 {
+ ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
+ return nil
+ }
- return PathForModuleSrc(ctx, (*p.srcs)[0])
+ // Return the singleton source after expanding any filegroup in the
+ // sources.
+ return PathForModuleSrc(ctx, (*p.srcs)[0])
+ } else {
+ if proptools.String(p.src) == "" {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ return nil
+ }
+ return PathForModuleSrc(ctx, *p.src)
+ }
+}
+
+func (p *Prebuilt) UsePrebuilt() bool {
+ return p.properties.UsePrebuilt
}
func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
@@ -68,23 +84,29 @@
p.srcs = srcs
}
+func InitSingleSourcePrebuiltModule(module PrebuiltInterface, src *string) {
+ p := module.Prebuilt()
+ module.AddProperties(&p.properties)
+ p.src = src
+}
+
type PrebuiltInterface interface {
Module
Prebuilt() *Prebuilt
}
func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
- ctx.BottomUp("prebuilts", prebuiltMutator).Parallel()
+ ctx.BottomUp("prebuilts", PrebuiltMutator).Parallel()
}
func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
ctx.TopDown("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
- ctx.BottomUp("prebuilt_replace", PrebuiltReplaceMutator).Parallel()
+ ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
}
-// prebuiltMutator ensures that there is always a module with an undecorated name, and marks
+// PrebuiltMutator ensures that there is always a module with an undecorated name, and marks
// prebuilt modules that have both a prebuilt and a source module.
-func prebuiltMutator(ctx BottomUpMutatorContext) {
+func PrebuiltMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
name := m.base().BaseModuleName()
@@ -102,7 +124,7 @@
func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
- if p.srcs == nil {
+ if p.srcs == nil && p.src == nil {
panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
}
if !p.properties.SourceExists {
@@ -119,10 +141,12 @@
}
}
-// PrebuiltReplaceMutator replaces dependencies on the source module with dependencies on the
-// prebuilt when both modules exist and the prebuilt should be used. When the prebuilt should not
-// be used, disable installing it.
-func PrebuiltReplaceMutator(ctx BottomUpMutatorContext) {
+// PrebuiltPostDepsMutator does two operations. It replace dependencies on the
+// source module with dependencies on the prebuilt when both modules exist and
+// the prebuilt should be used. When the prebuilt should not be used, disable
+// installing it. Secondly, it also adds a sourcegroup to any filegroups found
+// in the prebuilt's 'Srcs' property.
+func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
name := m.base().BaseModuleName()
@@ -139,7 +163,11 @@
// usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt
// will be used if it is marked "prefer" or if the source module is disabled.
func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool {
- if len(*p.srcs) == 0 {
+ if p.srcs != nil && len(*p.srcs) == 0 {
+ return false
+ }
+
+ if p.src != nil && *p.src == "" {
return false
}
@@ -150,3 +178,7 @@
return source == nil || !source.Enabled()
}
+
+func (p *Prebuilt) SourceExists() bool {
+ return p.properties.SourceExists
+}
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
new file mode 100644
index 0000000..8f23d78
--- /dev/null
+++ b/android/prebuilt_etc.go
@@ -0,0 +1,259 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "io"
+ "strings"
+)
+
+// TODO(jungw): Now that it handles more than the ones in etc/, consider renaming this file.
+
+func init() {
+ RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
+ RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
+ RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
+ RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
+
+ PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("prebuilt_etc", prebuiltEtcMutator).Parallel()
+ })
+}
+
+type prebuiltEtcProperties struct {
+ // Source file of this prebuilt.
+ Src *string `android:"path,arch_variant"`
+
+ // optional subdirectory under which this file is installed into
+ Sub_dir *string `android:"arch_variant"`
+
+ // optional name for the installed file. If unspecified, name of the module is used as the file name
+ Filename *string `android:"arch_variant"`
+
+ // when set to true, and filename property is not set, the name for the installed file
+ // is the same as the file name of the source file.
+ Filename_from_src *bool `android:"arch_variant"`
+
+ // Make this module available when building for recovery.
+ Recovery_available *bool
+
+ InRecovery bool `blueprint:"mutated"`
+
+ // Whether this module is directly installable to one of the partitions. Default: true.
+ Installable *bool
+}
+
+type PrebuiltEtc struct {
+ ModuleBase
+
+ properties prebuiltEtcProperties
+
+ sourceFilePath Path
+ outputFilePath OutputPath
+ // The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
+ installDirBase string
+ installDirPath OutputPath
+ additionalDependencies *Paths
+}
+
+func (p *PrebuiltEtc) inRecovery() bool {
+ return p.properties.InRecovery || p.ModuleBase.InstallInRecovery()
+}
+
+func (p *PrebuiltEtc) onlyInRecovery() bool {
+ return p.ModuleBase.InstallInRecovery()
+}
+
+func (p *PrebuiltEtc) InstallInRecovery() bool {
+ return p.inRecovery()
+}
+
+func (p *PrebuiltEtc) DepsMutator(ctx BottomUpMutatorContext) {
+ if p.properties.Src == nil {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ }
+}
+
+func (p *PrebuiltEtc) SourceFilePath(ctx ModuleContext) Path {
+ return PathForModuleSrc(ctx, String(p.properties.Src))
+}
+
+// This allows other derivative modules (e.g. prebuilt_etc_xml) to perform
+// additional steps (like validating the src) before the file is installed.
+func (p *PrebuiltEtc) SetAdditionalDependencies(paths Paths) {
+ p.additionalDependencies = &paths
+}
+
+func (p *PrebuiltEtc) OutputFile() OutputPath {
+ return p.outputFilePath
+}
+
+func (p *PrebuiltEtc) SubDir() string {
+ return String(p.properties.Sub_dir)
+}
+
+func (p *PrebuiltEtc) Installable() bool {
+ return p.properties.Installable == nil || Bool(p.properties.Installable)
+}
+
+func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx ModuleContext) {
+ p.sourceFilePath = PathForModuleSrc(ctx, String(p.properties.Src))
+ filename := String(p.properties.Filename)
+ filename_from_src := Bool(p.properties.Filename_from_src)
+ if filename == "" {
+ if filename_from_src {
+ filename = p.sourceFilePath.Base()
+ } else {
+ filename = ctx.ModuleName()
+ }
+ } else if filename_from_src {
+ ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+ return
+ }
+ p.outputFilePath = PathForModuleOut(ctx, filename).OutputPath
+ p.installDirPath = PathForModuleInstall(ctx, p.installDirBase, String(p.properties.Sub_dir))
+
+ // This ensures that outputFilePath has the correct name for others to
+ // use, as the source file may have a different name.
+ ctx.Build(pctx, BuildParams{
+ Rule: Cp,
+ Output: p.outputFilePath,
+ Input: p.sourceFilePath,
+ })
+}
+
+func (p *PrebuiltEtc) AndroidMk() AndroidMkData {
+ return AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
+ nameSuffix := ""
+ if p.inRecovery() && !p.onlyInRecovery() {
+ nameSuffix = ".recovery"
+ }
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+ fmt.Fprintln(w, "LOCAL_MODULE :=", name+nameSuffix)
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
+ if p.commonProperties.Owner != nil {
+ fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", *p.commonProperties.Owner)
+ }
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional")
+ if p.Host() {
+ fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+ }
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", p.outputFilePath.String())
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+ fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", p.outputFilePath.Base())
+ fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !p.Installable())
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(data.Required, " "))
+ fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", p.Arch().ArchType.String())
+ if p.additionalDependencies != nil {
+ fmt.Fprint(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=")
+ for _, path := range *p.additionalDependencies {
+ fmt.Fprint(w, " "+path.String())
+ }
+ fmt.Fprintln(w, "")
+ }
+ fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+ },
+ }
+}
+
+func InitPrebuiltEtcModule(p *PrebuiltEtc) {
+ p.AddProperties(&p.properties)
+}
+
+// prebuilt_etc is for a prebuilt artifact that is installed in
+// <partition>/etc/<sub_dir> directory.
+func PrebuiltEtcFactory() Module {
+ module := &PrebuiltEtc{installDirBase: "etc"}
+ InitPrebuiltEtcModule(module)
+ // This module is device-only
+ InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
+ return module
+}
+
+// prebuilt_etc_host is for a host prebuilt artifact that is installed in
+// $(HOST_OUT)/etc/<sub_dir> directory.
+func PrebuiltEtcHostFactory() Module {
+ module := &PrebuiltEtc{installDirBase: "etc"}
+ InitPrebuiltEtcModule(module)
+ // This module is host-only
+ InitAndroidArchModule(module, HostSupported, MultilibCommon)
+ return module
+}
+
+// prebuilt_usr_share is for a prebuilt artifact that is installed in
+// <partition>/usr/share/<sub_dir> directory.
+func PrebuiltUserShareFactory() Module {
+ module := &PrebuiltEtc{installDirBase: "usr/share"}
+ InitPrebuiltEtcModule(module)
+ // This module is device-only
+ InitAndroidArchModule(module, DeviceSupported, MultilibFirst)
+ return module
+}
+
+// prebuild_usr_share_host is for a host prebuilt artifact that is installed in
+// $(HOST_OUT)/usr/share/<sub_dir> directory.
+func PrebuiltUserShareHostFactory() Module {
+ module := &PrebuiltEtc{installDirBase: "usr/share"}
+ InitPrebuiltEtcModule(module)
+ // This module is host-only
+ InitAndroidArchModule(module, HostSupported, MultilibCommon)
+ return module
+}
+
+const (
+ // coreMode is the variant for modules to be installed to system.
+ coreMode = "core"
+
+ // recoveryMode means a module to be installed to recovery image.
+ recoveryMode = "recovery"
+)
+
+// prebuiltEtcMutator creates the needed variants to install the module to
+// system or recovery.
+func prebuiltEtcMutator(mctx BottomUpMutatorContext) {
+ m, ok := mctx.Module().(*PrebuiltEtc)
+ if !ok || m.Host() {
+ return
+ }
+
+ var coreVariantNeeded bool = true
+ var recoveryVariantNeeded bool = false
+ if Bool(m.properties.Recovery_available) {
+ recoveryVariantNeeded = true
+ }
+
+ if m.ModuleBase.InstallInRecovery() {
+ recoveryVariantNeeded = true
+ coreVariantNeeded = false
+ }
+
+ var variants []string
+ if coreVariantNeeded {
+ variants = append(variants, coreMode)
+ }
+ if recoveryVariantNeeded {
+ variants = append(variants, recoveryMode)
+ }
+ mod := mctx.CreateVariations(variants...)
+ for i, v := range variants {
+ if v == recoveryMode {
+ m := mod[i].(*PrebuiltEtc)
+ m.properties.InRecovery = true
+ }
+ }
+}
diff --git a/android/prebuilt_etc_test.go b/android/prebuilt_etc_test.go
new file mode 100644
index 0000000..e0ade7e
--- /dev/null
+++ b/android/prebuilt_etc_test.go
@@ -0,0 +1,231 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "bufio"
+ "bytes"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func testPrebuiltEtc(t *testing.T, bp string) (*TestContext, Config) {
+ config, buildDir := setUp(t)
+ defer tearDown(buildDir)
+ ctx := NewTestArchContext()
+ ctx.RegisterModuleType("prebuilt_etc", ModuleFactoryAdaptor(PrebuiltEtcFactory))
+ ctx.RegisterModuleType("prebuilt_etc_host", ModuleFactoryAdaptor(PrebuiltEtcHostFactory))
+ ctx.RegisterModuleType("prebuilt_usr_share", ModuleFactoryAdaptor(PrebuiltUserShareFactory))
+ ctx.RegisterModuleType("prebuilt_usr_share_host", ModuleFactoryAdaptor(PrebuiltUserShareHostFactory))
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("prebuilt_etc", prebuiltEtcMutator).Parallel()
+ })
+ ctx.Register()
+ mockFiles := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "foo.conf": nil,
+ "bar.conf": nil,
+ "baz.conf": nil,
+ }
+ ctx.MockFileSystem(mockFiles)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ return ctx, config
+}
+
+func setUp(t *testing.T) (config Config, buildDir string) {
+ buildDir, err := ioutil.TempDir("", "soong_prebuilt_etc_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ config = TestArchConfig(buildDir, nil)
+ return
+}
+
+func tearDown(buildDir string) {
+ os.RemoveAll(buildDir)
+}
+
+func TestPrebuiltEtcVariants(t *testing.T) {
+ ctx, _ := testPrebuiltEtc(t, `
+ prebuilt_etc {
+ name: "foo.conf",
+ src: "foo.conf",
+ }
+ prebuilt_etc {
+ name: "bar.conf",
+ src: "bar.conf",
+ recovery_available: true,
+ }
+ prebuilt_etc {
+ name: "baz.conf",
+ src: "baz.conf",
+ recovery: true,
+ }
+ `)
+
+ foo_variants := ctx.ModuleVariantsForTests("foo.conf")
+ if len(foo_variants) != 1 {
+ t.Errorf("expected 1, got %#v", foo_variants)
+ }
+
+ bar_variants := ctx.ModuleVariantsForTests("bar.conf")
+ if len(bar_variants) != 2 {
+ t.Errorf("expected 2, got %#v", bar_variants)
+ }
+
+ baz_variants := ctx.ModuleVariantsForTests("baz.conf")
+ if len(baz_variants) != 1 {
+ t.Errorf("expected 1, got %#v", bar_variants)
+ }
+}
+
+func TestPrebuiltEtcOutputPath(t *testing.T) {
+ ctx, _ := testPrebuiltEtc(t, `
+ prebuilt_etc {
+ name: "foo.conf",
+ src: "foo.conf",
+ filename: "foo.installed.conf",
+ }
+ `)
+
+ p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+ if p.outputFilePath.Base() != "foo.installed.conf" {
+ t.Errorf("expected foo.installed.conf, got %q", p.outputFilePath.Base())
+ }
+}
+
+func TestPrebuiltEtcGlob(t *testing.T) {
+ ctx, _ := testPrebuiltEtc(t, `
+ prebuilt_etc {
+ name: "my_foo",
+ src: "foo.*",
+ }
+ prebuilt_etc {
+ name: "my_bar",
+ src: "bar.*",
+ filename_from_src: true,
+ }
+ `)
+
+ p := ctx.ModuleForTests("my_foo", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+ if p.outputFilePath.Base() != "my_foo" {
+ t.Errorf("expected my_foo, got %q", p.outputFilePath.Base())
+ }
+
+ p = ctx.ModuleForTests("my_bar", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+ if p.outputFilePath.Base() != "bar.conf" {
+ t.Errorf("expected bar.conf, got %q", p.outputFilePath.Base())
+ }
+}
+
+func TestPrebuiltEtcAndroidMk(t *testing.T) {
+ ctx, _ := testPrebuiltEtc(t, `
+ prebuilt_etc {
+ name: "foo",
+ src: "foo.conf",
+ owner: "abc",
+ filename_from_src: true,
+ }
+ `)
+
+ data := AndroidMkData{}
+ data.Required = append(data.Required, "modA", "moduleB")
+
+ expected := map[string]string{
+ "LOCAL_MODULE": "foo",
+ "LOCAL_MODULE_CLASS": "ETC",
+ "LOCAL_MODULE_OWNER": "abc",
+ "LOCAL_INSTALLED_MODULE_STEM": "foo.conf",
+ "LOCAL_REQUIRED_MODULES": "modA moduleB",
+ }
+
+ mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+ buf := &bytes.Buffer{}
+ mod.AndroidMk().Custom(buf, "foo", "", "", data)
+ for k, expected := range expected {
+ found := false
+ scanner := bufio.NewScanner(bytes.NewReader(buf.Bytes()))
+ for scanner.Scan() {
+ line := scanner.Text()
+ tok := strings.Split(line, " := ")
+ if tok[0] == k {
+ found = true
+ if tok[1] != expected {
+ t.Errorf("Incorrect %s '%s', expected '%s'", k, tok[1], expected)
+ }
+ }
+ }
+
+ if !found {
+ t.Errorf("No %s defined, saw %s", k, buf.String())
+ }
+ }
+}
+
+func TestPrebuiltEtcHost(t *testing.T) {
+ ctx, _ := testPrebuiltEtc(t, `
+ prebuilt_etc_host {
+ name: "foo.conf",
+ src: "foo.conf",
+ }
+ `)
+
+ buildOS := BuildOs.String()
+ p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
+ if !p.Host() {
+ t.Errorf("host bit is not set for a prebuilt_etc_host module.")
+ }
+}
+
+func TestPrebuiltUserShareInstallDirPath(t *testing.T) {
+ ctx, _ := testPrebuiltEtc(t, `
+ prebuilt_usr_share {
+ name: "foo.conf",
+ src: "foo.conf",
+ sub_dir: "bar",
+ }
+ `)
+
+ p := ctx.ModuleForTests("foo.conf", "android_arm64_armv8-a_core").Module().(*PrebuiltEtc)
+ expected := "target/product/test_device/system/usr/share/bar"
+ if p.installDirPath.RelPathString() != expected {
+ t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+ }
+}
+
+func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) {
+ ctx, config := testPrebuiltEtc(t, `
+ prebuilt_usr_share_host {
+ name: "foo.conf",
+ src: "foo.conf",
+ sub_dir: "bar",
+ }
+ `)
+
+ buildOS := BuildOs.String()
+ p := ctx.ModuleForTests("foo.conf", buildOS+"_common").Module().(*PrebuiltEtc)
+ expected := filepath.Join("host", config.PrebuiltOS(), "usr", "share", "bar")
+ if p.installDirPath.RelPathString() != expected {
+ t.Errorf("expected %q, got %q", expected, p.installDirPath.RelPathString())
+ }
+}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 69ce16a..e182641 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -41,7 +41,7 @@
prebuilt {
name: "bar",
prefer: false,
- srcs: ["prebuilt"],
+ srcs: ["prebuilt_file"],
}`,
prebuilt: true,
},
@@ -51,7 +51,7 @@
prebuilt {
name: "bar",
prefer: true,
- srcs: ["prebuilt"],
+ srcs: ["prebuilt_file"],
}`,
prebuilt: true,
},
@@ -65,7 +65,7 @@
prebuilt {
name: "bar",
prefer: false,
- srcs: ["prebuilt"],
+ srcs: ["prebuilt_file"],
}`,
prebuilt: false,
},
@@ -79,7 +79,7 @@
prebuilt {
name: "bar",
prefer: true,
- srcs: ["prebuilt"],
+ srcs: ["prebuilt_file"],
}`,
prebuilt: true,
},
@@ -109,6 +109,20 @@
}`,
prebuilt: false,
},
+ {
+ name: "prebuilt file from filegroup preferred",
+ modules: `
+ filegroup {
+ name: "fg",
+ srcs: ["prebuilt_file"],
+ }
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: [":fg"],
+ }`,
+ prebuilt: true,
+ },
}
func TestPrebuilts(t *testing.T) {
@@ -125,14 +139,17 @@
ctx := NewTestContext()
ctx.PreArchMutators(RegisterPrebuiltsPreArchMutators)
ctx.PostDepsMutators(RegisterPrebuiltsPostDepsMutators)
+ ctx.RegisterModuleType("filegroup", ModuleFactoryAdaptor(FileGroupFactory))
ctx.RegisterModuleType("prebuilt", ModuleFactoryAdaptor(newPrebuiltModule))
ctx.RegisterModuleType("source", ModuleFactoryAdaptor(newSourceModule))
ctx.Register()
ctx.MockFileSystem(map[string][]byte{
+ "prebuilt_file": nil,
+ "source_file": nil,
"Blueprints": []byte(`
source {
name: "foo",
- deps: ["bar"],
+ deps: [":bar"],
}
` + test.modules),
})
@@ -157,21 +174,45 @@
}
})
+ deps := foo.Module().(*sourceModule).deps
+ if deps == nil || len(deps) != 1 {
+ t.Errorf("deps does not have single path, but is %v", deps)
+ }
+ var usingSourceFile, usingPrebuiltFile bool
+ if deps[0].String() == "source_file" {
+ usingSourceFile = true
+ }
+ if deps[0].String() == "prebuilt_file" {
+ usingPrebuiltFile = true
+ }
+
if test.prebuilt {
if !dependsOnPrebuiltModule {
t.Errorf("doesn't depend on prebuilt module")
}
+ if !usingPrebuiltFile {
+ t.Errorf("doesn't use prebuilt_file")
+ }
if dependsOnSourceModule {
t.Errorf("depends on source module")
}
+ if usingSourceFile {
+ t.Errorf("using source_file")
+ }
} else {
if dependsOnPrebuiltModule {
t.Errorf("depends on prebuilt module")
}
+ if usingPrebuiltFile {
+ t.Errorf("using prebuilt_file")
+ }
if !dependsOnSourceModule {
- t.Errorf("doens't depend on source module")
+ t.Errorf("doesn't depend on source module")
+ }
+ if !usingSourceFile {
+ t.Errorf("doesn't use source_file")
}
}
})
@@ -182,8 +223,9 @@
ModuleBase
prebuilt Prebuilt
properties struct {
- Srcs []string
+ Srcs []string `android:"path"`
}
+ src Path
}
func newPrebuiltModule() Module {
@@ -198,22 +240,28 @@
return p.prebuilt.Name(p.ModuleBase.Name())
}
-func (p *prebuiltModule) DepsMutator(ctx BottomUpMutatorContext) {
-}
-
-func (p *prebuiltModule) GenerateAndroidBuildActions(ModuleContext) {
+func (p *prebuiltModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ if len(p.properties.Srcs) >= 1 {
+ p.src = p.prebuilt.SingleSourcePath(ctx)
+ }
}
func (p *prebuiltModule) Prebuilt() *Prebuilt {
return &p.prebuilt
}
+func (p *prebuiltModule) Srcs() Paths {
+ return Paths{p.src}
+}
+
type sourceModule struct {
ModuleBase
properties struct {
- Deps []string
+ Deps []string `android:"path"`
}
dependsOnSourceModule, dependsOnPrebuiltModule bool
+ deps Paths
+ src Path
}
func newSourceModule() Module {
@@ -224,10 +272,15 @@
}
func (s *sourceModule) DepsMutator(ctx BottomUpMutatorContext) {
- for _, d := range s.properties.Deps {
- ctx.AddDependency(ctx.Module(), nil, d)
- }
+ // s.properties.Deps are annotated with android:path, so they are
+ // automatically added to the dependency by pathDeps mutator
}
func (s *sourceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ s.deps = PathsForModuleSrc(ctx, s.properties.Deps)
+ s.src = PathForModuleSrc(ctx, "source_file")
+}
+
+func (s *sourceModule) Srcs() Paths {
+ return Paths{s.src}
}
diff --git a/android/proto.go b/android/proto.go
index 801837e..5247c68 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -14,6 +14,13 @@
package android
+import (
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
// TODO(ccross): protos are often used to communicate between multiple modules. If the only
// way to convert a proto to source is to reference it as a source file, and external modules cannot
// reference source files in other modules, then every module that owns a proto file will need to
@@ -22,36 +29,72 @@
// and then external modules could depend on the proto module but use their own settings to
// generate the source.
-func ProtoFlags(ctx ModuleContext, p *ProtoProperties) []string {
- protoFlags := []string{}
+type ProtoFlags struct {
+ Flags []string
+ CanonicalPathFromRoot bool
+ Dir ModuleGenPath
+ SubDir ModuleGenPath
+ OutTypeFlag string
+ OutParams []string
+ Deps Paths
+}
+
+type protoDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+var ProtoPluginDepTag = protoDependencyTag{name: "plugin"}
+
+func ProtoDeps(ctx BottomUpMutatorContext, p *ProtoProperties) {
+ if String(p.Proto.Plugin) != "" && String(p.Proto.Type) != "" {
+ ctx.ModuleErrorf("only one of proto.type and proto.plugin can be specified.")
+ }
+
+ if plugin := String(p.Proto.Plugin); plugin != "" {
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Config().BuildOsVariant},
+ }, ProtoPluginDepTag, "protoc-gen-"+plugin)
+ }
+}
+
+func GetProtoFlags(ctx ModuleContext, p *ProtoProperties) ProtoFlags {
+ var flags []string
+ var deps Paths
if len(p.Proto.Local_include_dirs) > 0 {
localProtoIncludeDirs := PathsForModuleSrc(ctx, p.Proto.Local_include_dirs)
- protoFlags = append(protoFlags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I"))
+ flags = append(flags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I"))
}
if len(p.Proto.Include_dirs) > 0 {
rootProtoIncludeDirs := PathsForSource(ctx, p.Proto.Include_dirs)
- protoFlags = append(protoFlags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I"))
+ flags = append(flags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I"))
}
- return protoFlags
-}
+ ctx.VisitDirectDepsWithTag(ProtoPluginDepTag, func(dep Module) {
+ if hostTool, ok := dep.(HostToolProvider); !ok || !hostTool.HostToolPath().Valid() {
+ ctx.PropertyErrorf("proto.plugin", "module %q is not a host tool provider",
+ ctx.OtherModuleName(dep))
+ } else {
+ plugin := String(p.Proto.Plugin)
+ deps = append(deps, hostTool.HostToolPath().Path())
+ flags = append(flags, "--plugin=protoc-gen-"+plugin+"="+hostTool.HostToolPath().String())
+ }
+ })
-func ProtoCanonicalPathFromRoot(ctx ModuleContext, p *ProtoProperties) bool {
- if p.Proto.Canonical_path_from_root == nil {
- return true
+ var protoOutFlag string
+ if plugin := String(p.Proto.Plugin); plugin != "" {
+ protoOutFlag = "--" + plugin + "_out"
}
- return *p.Proto.Canonical_path_from_root
-}
-// ProtoDir returns the module's "gen/proto" directory
-func ProtoDir(ctx ModuleContext) ModuleGenPath {
- return PathForModuleGen(ctx, "proto")
-}
-
-// ProtoSubDir returns the module's "gen/proto/path/to/module" directory
-func ProtoSubDir(ctx ModuleContext) ModuleGenPath {
- return PathForModuleGen(ctx, "proto", ctx.ModuleDir())
+ return ProtoFlags{
+ Flags: flags,
+ Deps: deps,
+ OutTypeFlag: protoOutFlag,
+ CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, true),
+ Dir: PathForModuleGen(ctx, "proto"),
+ SubDir: PathForModuleGen(ctx, "proto", ctx.ModuleDir()),
+ }
}
type ProtoProperties struct {
@@ -59,6 +102,9 @@
// Proto generator type. C++: full or lite. Java: micro, nano, stream, or lite.
Type *string `android:"arch_variant"`
+ // Proto plugin to use as the generator. Must be a cc_binary_host module.
+ Plugin *string `android:"arch_variant"`
+
// list of directories that will be added to the protoc include paths.
Include_dirs []string
@@ -76,3 +122,28 @@
Canonical_path_from_root *bool
} `android:"arch_variant"`
}
+
+func ProtoRule(ctx ModuleContext, rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
+ outDir WritablePath, depFile WritablePath, outputs WritablePaths) {
+
+ var protoBase string
+ if flags.CanonicalPathFromRoot {
+ protoBase = "."
+ } else {
+ rel := protoFile.Rel()
+ protoBase = strings.TrimSuffix(protoFile.String(), rel)
+ }
+
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "aprotoc")).
+ FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
+ FlagWithDepFile("--dependency_out=", depFile).
+ FlagWithArg("-I ", protoBase).
+ Flags(flags.Flags).
+ Input(protoFile).
+ Implicits(deps).
+ ImplicitOutputs(outputs)
+
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).Flag(depFile.String())
+}
diff --git a/android/register.go b/android/register.go
index 6c88af1..d79982a 100644
--- a/android/register.go
+++ b/android/register.go
@@ -20,7 +20,7 @@
type moduleType struct {
name string
- factory blueprint.ModuleFactory
+ factory ModuleFactory
}
var moduleTypes []moduleType
@@ -40,8 +40,6 @@
parallel bool
}
-var mutators []*mutator
-
type ModuleFactory func() Module
// ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module
@@ -60,12 +58,15 @@
func SingletonFactoryAdaptor(factory SingletonFactory) blueprint.SingletonFactory {
return func() blueprint.Singleton {
singleton := factory()
- return singletonAdaptor{singleton}
+ if makevars, ok := singleton.(SingletonMakeVarsProvider); ok {
+ registerSingletonMakeVarsProvider(makevars)
+ }
+ return &singletonAdaptor{Singleton: singleton}
}
}
func RegisterModuleType(name string, factory ModuleFactory) {
- moduleTypes = append(moduleTypes, moduleType{name, ModuleFactoryAdaptor(factory)})
+ moduleTypes = append(moduleTypes, moduleType{name, factory})
}
func RegisterSingletonType(name string, factory SingletonFactory) {
@@ -90,7 +91,7 @@
}
for _, t := range moduleTypes {
- ctx.RegisterModuleType(t.name, t.factory)
+ ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
}
for _, t := range singletons {
@@ -99,5 +100,17 @@
registerMutators(ctx.Context, preArch, preDeps, postDeps)
+ // Register makevars after other singletons so they can export values through makevars
+ ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc))
+
+ // Register env last so that it can track all used environment variables
ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton))
}
+
+func ModuleTypeFactories() map[string]ModuleFactory {
+ ret := make(map[string]ModuleFactory)
+ for _, t := range moduleTypes {
+ ret[t.name] = t.factory
+ }
+ return ret
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
new file mode 100644
index 0000000..2d0fac1
--- /dev/null
+++ b/android/rule_builder.go
@@ -0,0 +1,518 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
+// graph.
+type RuleBuilder struct {
+ commands []*RuleBuilderCommand
+ installs RuleBuilderInstalls
+ temporariesSet map[WritablePath]bool
+ restat bool
+ missingDeps []string
+}
+
+// NewRuleBuilder returns a newly created RuleBuilder.
+func NewRuleBuilder() *RuleBuilder {
+ return &RuleBuilder{
+ temporariesSet: make(map[WritablePath]bool),
+ }
+}
+
+// RuleBuilderInstall is a tuple of install from and to locations.
+type RuleBuilderInstall struct {
+ From Path
+ To string
+}
+
+type RuleBuilderInstalls []RuleBuilderInstall
+
+// String returns the RuleBuilderInstalls in the form used by $(call copy-many-files) in Make, a space separated
+// list of from:to tuples.
+func (installs RuleBuilderInstalls) String() string {
+ sb := strings.Builder{}
+ for i, install := range installs {
+ if i != 0 {
+ sb.WriteRune(' ')
+ }
+ sb.WriteString(install.From.String())
+ sb.WriteRune(':')
+ sb.WriteString(install.To)
+ }
+ return sb.String()
+}
+
+// MissingDeps adds modules to the list of missing dependencies. If MissingDeps
+// is called with a non-empty input, any call to Build will result in a rule
+// that will print an error listing the missing dependencies and fail.
+// MissingDeps should only be called if Config.AllowMissingDependencies() is
+// true.
+func (r *RuleBuilder) MissingDeps(missingDeps []string) {
+ r.missingDeps = append(r.missingDeps, missingDeps...)
+}
+
+// Restat marks the rule as a restat rule, which will be passed to ModuleContext.Rule in BuildParams.Restat.
+func (r *RuleBuilder) Restat() *RuleBuilder {
+ r.restat = true
+ return r
+}
+
+// Install associates an output of the rule with an install location, which can be retrieved later using
+// RuleBuilder.Installs.
+func (r *RuleBuilder) Install(from Path, to string) {
+ r.installs = append(r.installs, RuleBuilderInstall{from, to})
+}
+
+// Command returns a new RuleBuilderCommand for the rule. The commands will be ordered in the rule by when they were
+// created by this method. That can be mutated through their methods in any order, as long as the mutations do not
+// race with any call to Build.
+func (r *RuleBuilder) Command() *RuleBuilderCommand {
+ command := &RuleBuilderCommand{}
+ r.commands = append(r.commands, command)
+ return command
+}
+
+// Temporary marks an output of a command as an intermediate file that will be used as an input to another command
+// in the same rule, and should not be listed in Outputs.
+func (r *RuleBuilder) Temporary(path WritablePath) {
+ r.temporariesSet[path] = true
+}
+
+// DeleteTemporaryFiles adds a command to the rule that deletes any outputs that have been marked using Temporary
+// when the rule runs. DeleteTemporaryFiles should be called after all calls to Temporary.
+func (r *RuleBuilder) DeleteTemporaryFiles() {
+ var temporariesList WritablePaths
+
+ for intermediate := range r.temporariesSet {
+ temporariesList = append(temporariesList, intermediate)
+ }
+
+ sort.Slice(temporariesList, func(i, j int) bool {
+ return temporariesList[i].String() < temporariesList[j].String()
+ })
+
+ r.Command().Text("rm").Flag("-f").Outputs(temporariesList)
+}
+
+// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take input paths, such
+// as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or RuleBuilderCommand.FlagWithInput. Inputs to a command
+// that are also outputs of another command in the same RuleBuilder are filtered out.
+func (r *RuleBuilder) Inputs() Paths {
+ outputs := r.outputSet()
+
+ inputs := make(map[string]Path)
+ for _, c := range r.commands {
+ for _, input := range c.inputs {
+ if _, isOutput := outputs[input.String()]; !isOutput {
+ inputs[input.String()] = input
+ }
+ }
+ }
+
+ var inputList Paths
+ for _, input := range inputs {
+ inputList = append(inputList, input)
+ }
+
+ sort.Slice(inputList, func(i, j int) bool {
+ return inputList[i].String() < inputList[j].String()
+ })
+
+ return inputList
+}
+
+func (r *RuleBuilder) outputSet() map[string]WritablePath {
+ outputs := make(map[string]WritablePath)
+ for _, c := range r.commands {
+ for _, output := range c.outputs {
+ outputs[output.String()] = output
+ }
+ }
+ return outputs
+}
+
+// Outputs returns the list of paths that were passed to the RuleBuilderCommand methods that take output paths, such
+// as RuleBuilderCommand.Output, RuleBuilderCommand.ImplicitOutput, or RuleBuilderCommand.FlagWithInput.
+func (r *RuleBuilder) Outputs() WritablePaths {
+ outputs := r.outputSet()
+
+ var outputList WritablePaths
+ for _, output := range outputs {
+ if !r.temporariesSet[output] {
+ outputList = append(outputList, output)
+ }
+ }
+
+ sort.Slice(outputList, func(i, j int) bool {
+ return outputList[i].String() < outputList[j].String()
+ })
+
+ return outputList
+}
+
+// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
+// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
+func (r *RuleBuilder) DepFiles() WritablePaths {
+ var depFiles WritablePaths
+
+ for _, c := range r.commands {
+ for _, depFile := range c.depFiles {
+ depFiles = append(depFiles, depFile)
+ }
+ }
+
+ return depFiles
+}
+
+// Installs returns the list of tuples passed to Install.
+func (r *RuleBuilder) Installs() RuleBuilderInstalls {
+ return append(RuleBuilderInstalls(nil), r.installs...)
+}
+
+func (r *RuleBuilder) toolsSet() map[string]Path {
+ tools := make(map[string]Path)
+ for _, c := range r.commands {
+ for _, tool := range c.tools {
+ tools[tool.String()] = tool
+ }
+ }
+
+ return tools
+}
+
+// Tools returns the list of paths that were passed to the RuleBuilderCommand.Tool method.
+func (r *RuleBuilder) Tools() Paths {
+ toolsSet := r.toolsSet()
+
+ var toolsList Paths
+ for _, tool := range toolsSet {
+ toolsList = append(toolsList, tool)
+ }
+
+ sort.Slice(toolsList, func(i, j int) bool {
+ return toolsList[i].String() < toolsList[j].String()
+ })
+
+ return toolsList
+}
+
+// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
+func (r *RuleBuilder) Commands() []string {
+ var commands []string
+ for _, c := range r.commands {
+ commands = append(commands, string(c.buf))
+ }
+ return commands
+}
+
+// BuilderContext is a subset of ModuleContext and SingletonContext.
+type BuilderContext interface {
+ PathContext
+ Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule
+ Build(PackageContext, BuildParams)
+}
+
+var _ BuilderContext = ModuleContext(nil)
+var _ BuilderContext = SingletonContext(nil)
+
+func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
+ return (&RuleBuilderCommand{}).
+ Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).
+ Flags(depFiles.Strings())
+}
+
+// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
+// Outputs.
+func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
+ name = ninjaNameEscape(name)
+
+ if len(r.missingDeps) > 0 {
+ ctx.Build(pctx, BuildParams{
+ Rule: ErrorRule,
+ Outputs: r.Outputs(),
+ Description: desc,
+ Args: map[string]string{
+ "error": "missing dependencies: " + strings.Join(r.missingDeps, ", "),
+ },
+ })
+ return
+ }
+
+ tools := r.Tools()
+ commands := r.Commands()
+
+ var depFile WritablePath
+ var depFormat blueprint.Deps
+ if depFiles := r.DepFiles(); len(depFiles) > 0 {
+ depFile = depFiles[0]
+ depFormat = blueprint.DepsGCC
+ if len(depFiles) > 1 {
+ // Add a command locally that merges all depfiles together into the first depfile.
+ cmd := r.depFileMergerCmd(ctx, depFiles)
+ commands = append(commands, string(cmd.buf))
+ tools = append(tools, cmd.tools...)
+ }
+ }
+
+ // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
+ // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
+ // doesn't matter.
+ var output WritablePath
+ var implicitOutputs WritablePaths
+ if outputs := r.Outputs(); len(outputs) > 0 {
+ output = outputs[0]
+ implicitOutputs = outputs[1:]
+ }
+
+ if len(commands) > 0 {
+ ctx.Build(pctx, BuildParams{
+ Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
+ Command: strings.Join(proptools.NinjaEscapeList(commands), " && "),
+ CommandDeps: tools.Strings(),
+ Restat: r.restat,
+ }),
+ Implicits: r.Inputs(),
+ Output: output,
+ ImplicitOutputs: implicitOutputs,
+ Depfile: depFile,
+ Deps: depFormat,
+ Description: desc,
+ })
+ }
+}
+
+// RuleBuilderCommand is a builder for a command in a command line. It can be mutated by its methods to add to the
+// command and track dependencies. The methods mutate the RuleBuilderCommand in place, as well as return the
+// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
+// space as a separator from the previous method.
+type RuleBuilderCommand struct {
+ buf []byte
+ inputs Paths
+ outputs WritablePaths
+ depFiles WritablePaths
+ tools Paths
+}
+
+// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
+// rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
+ if len(c.buf) > 0 {
+ c.buf = append(c.buf, ' ')
+ }
+ c.buf = append(c.buf, text...)
+ return c
+}
+
+// Textf adds the specified formatted text to the command line. The text should not contain input or output paths or
+// the rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
+ return c.Text(fmt.Sprintf(format, a...))
+}
+
+// Flag adds the specified raw text to the command line. The text should not contain input or output paths or the
+// rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) Flag(flag string) *RuleBuilderCommand {
+ return c.Text(flag)
+}
+
+// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
+// rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
+ for _, flag := range flags {
+ c.Text(flag)
+ }
+ return c
+}
+
+// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
+// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
+// outputs.
+func (c *RuleBuilderCommand) FlagWithArg(flag, arg string) *RuleBuilderCommand {
+ return c.Text(flag + arg)
+}
+
+// FlagForEachArg adds the specified flag joined with each argument to the command line. The result is identical to
+// calling FlagWithArg for argument.
+func (c *RuleBuilderCommand) FlagForEachArg(flag string, args []string) *RuleBuilderCommand {
+ for _, arg := range args {
+ c.FlagWithArg(flag, arg)
+ }
+ return c
+}
+
+// FlagWithList adds the specified flag and list of arguments to the command line, with the arguments joined by sep
+// and no separator between the flag and arguments. The flag and arguments should not contain input or output paths or
+// the rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) FlagWithList(flag string, list []string, sep string) *RuleBuilderCommand {
+ return c.Text(flag + strings.Join(list, sep))
+}
+
+// Tool adds the specified tool path to the command line. The path will be also added to the dependencies returned by
+// RuleBuilder.Tools.
+func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
+ c.tools = append(c.tools, path)
+ return c.Text(path.String())
+}
+
+// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
+// RuleBuilder.Inputs.
+func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
+ c.inputs = append(c.inputs, path)
+ return c.Text(path.String())
+}
+
+// Inputs adds the specified input paths to the command line, separated by spaces. The paths will also be added to the
+// dependencies returned by RuleBuilder.Inputs.
+func (c *RuleBuilderCommand) Inputs(paths Paths) *RuleBuilderCommand {
+ for _, path := range paths {
+ c.Input(path)
+ }
+ return c
+}
+
+// Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
+// command line.
+func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
+ c.inputs = append(c.inputs, path)
+ return c
+}
+
+// Implicits adds the specified input paths to the dependencies returned by RuleBuilder.Inputs without modifying the
+// command line.
+func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
+ c.inputs = append(c.inputs, paths...)
+ return c
+}
+
+// Output adds the specified output path to the command line. The path will also be added to the outputs returned by
+// RuleBuilder.Outputs.
+func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
+ c.outputs = append(c.outputs, path)
+ return c.Text(path.String())
+}
+
+// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
+// the outputs returned by RuleBuilder.Outputs.
+func (c *RuleBuilderCommand) Outputs(paths WritablePaths) *RuleBuilderCommand {
+ for _, path := range paths {
+ c.Output(path)
+ }
+ return c
+}
+
+// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
+// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
+// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
+func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
+ c.depFiles = append(c.depFiles, path)
+ return c.Text(path.String())
+}
+
+// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
+// the command line.
+func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
+ c.outputs = append(c.outputs, path)
+ return c
+}
+
+// ImplicitOutputs adds the specified output paths to the dependencies returned by RuleBuilder.Outputs without modifying
+// the command line.
+func (c *RuleBuilderCommand) ImplicitOutputs(paths WritablePaths) *RuleBuilderCommand {
+ c.outputs = append(c.outputs, paths...)
+ return c
+}
+
+// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
+// the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles
+// are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
+// depfiles together.
+func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
+ c.depFiles = append(c.depFiles, path)
+ return c
+}
+
+// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
+// will also be added to the dependencies returned by RuleBuilder.Inputs.
+func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
+ c.inputs = append(c.inputs, path)
+ return c.Text(flag + path.String())
+}
+
+// FlagWithInputList adds the specified flag and input paths to the command line, with the inputs joined by sep
+// and no separator between the flag and inputs. The input paths will also be added to the dependencies returned by
+// RuleBuilder.Inputs.
+func (c *RuleBuilderCommand) FlagWithInputList(flag string, paths Paths, sep string) *RuleBuilderCommand {
+ c.inputs = append(c.inputs, paths...)
+ return c.FlagWithList(flag, paths.Strings(), sep)
+}
+
+// FlagForEachInput adds the specified flag joined with each input path to the command line. The input paths will also
+// be added to the dependencies returned by RuleBuilder.Inputs. The result is identical to calling FlagWithInput for
+// each input path.
+func (c *RuleBuilderCommand) FlagForEachInput(flag string, paths Paths) *RuleBuilderCommand {
+ for _, path := range paths {
+ c.FlagWithInput(flag, path)
+ }
+ return c
+}
+
+// FlagWithOutput adds the specified flag and output path to the command line, with no separator between them. The path
+// will also be added to the outputs returned by RuleBuilder.Outputs.
+func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
+ c.outputs = append(c.outputs, path)
+ return c.Text(flag + path.String())
+}
+
+// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
+// will also be added to the outputs returned by RuleBuilder.Outputs.
+func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
+ c.depFiles = append(c.depFiles, path)
+ return c.Text(flag + path.String())
+}
+
+// String returns the command line.
+func (c *RuleBuilderCommand) String() string {
+ return string(c.buf)
+}
+
+func ninjaNameEscape(s string) string {
+ b := []byte(s)
+ escaped := false
+ for i, c := range b {
+ valid := (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '_') ||
+ (c == '-') ||
+ (c == '.')
+ if !valid {
+ b[i] = '_'
+ escaped = true
+ }
+ }
+ if escaped {
+ s = string(b)
+ }
+ return s
+}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
new file mode 100644
index 0000000..7bad025
--- /dev/null
+++ b/android/rule_builder_test.go
@@ -0,0 +1,420 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func pathContext() PathContext {
+ return PathContextForTesting(TestConfig("out", nil),
+ map[string][]byte{
+ "ld": nil,
+ "a.o": nil,
+ "b.o": nil,
+ "cp": nil,
+ "a": nil,
+ "b": nil,
+ "ls": nil,
+ "turbine": nil,
+ "java": nil,
+ })
+}
+
+func ExampleRuleBuilder() {
+ rule := NewRuleBuilder()
+
+ ctx := pathContext()
+
+ rule.Command().
+ Tool(PathForSource(ctx, "ld")).
+ Inputs(PathsForTesting("a.o", "b.o")).
+ FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
+ rule.Command().Text("echo success")
+
+ // To add the command to the build graph:
+ // rule.Build(pctx, ctx, "link", "link")
+
+ fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
+ fmt.Printf("tools: %q\n", rule.Tools())
+ fmt.Printf("inputs: %q\n", rule.Inputs())
+ fmt.Printf("outputs: %q\n", rule.Outputs())
+
+ // Output:
+ // commands: "ld a.o b.o -o out/linked && echo success"
+ // tools: ["ld"]
+ // inputs: ["a.o" "b.o"]
+ // outputs: ["out/linked"]
+}
+
+func ExampleRuleBuilder_Temporary() {
+ rule := NewRuleBuilder()
+
+ ctx := pathContext()
+
+ rule.Command().
+ Tool(PathForSource(ctx, "cp")).
+ Input(PathForSource(ctx, "a")).
+ Output(PathForOutput(ctx, "b"))
+ rule.Command().
+ Tool(PathForSource(ctx, "cp")).
+ Input(PathForOutput(ctx, "b")).
+ Output(PathForOutput(ctx, "c"))
+ rule.Temporary(PathForOutput(ctx, "b"))
+
+ fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
+ fmt.Printf("tools: %q\n", rule.Tools())
+ fmt.Printf("inputs: %q\n", rule.Inputs())
+ fmt.Printf("outputs: %q\n", rule.Outputs())
+
+ // Output:
+ // commands: "cp a out/b && cp out/b out/c"
+ // tools: ["cp"]
+ // inputs: ["a"]
+ // outputs: ["out/c"]
+}
+
+func ExampleRuleBuilder_DeleteTemporaryFiles() {
+ rule := NewRuleBuilder()
+
+ ctx := pathContext()
+
+ rule.Command().
+ Tool(PathForSource(ctx, "cp")).
+ Input(PathForSource(ctx, "a")).
+ Output(PathForOutput(ctx, "b"))
+ rule.Command().
+ Tool(PathForSource(ctx, "cp")).
+ Input(PathForOutput(ctx, "b")).
+ Output(PathForOutput(ctx, "c"))
+ rule.Temporary(PathForOutput(ctx, "b"))
+ rule.DeleteTemporaryFiles()
+
+ fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
+ fmt.Printf("tools: %q\n", rule.Tools())
+ fmt.Printf("inputs: %q\n", rule.Inputs())
+ fmt.Printf("outputs: %q\n", rule.Outputs())
+
+ // Output:
+ // commands: "cp a out/b && cp out/b out/c && rm -f out/b"
+ // tools: ["cp"]
+ // inputs: ["a"]
+ // outputs: ["out/c"]
+}
+
+func ExampleRuleBuilder_Installs() {
+ rule := NewRuleBuilder()
+
+ ctx := pathContext()
+
+ out := PathForOutput(ctx, "linked")
+
+ rule.Command().
+ Tool(PathForSource(ctx, "ld")).
+ Inputs(PathsForTesting("a.o", "b.o")).
+ FlagWithOutput("-o ", out)
+ rule.Install(out, "/bin/linked")
+ rule.Install(out, "/sbin/linked")
+
+ fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
+
+ // Output:
+ // rule.Installs().String() = "out/linked:/bin/linked out/linked:/sbin/linked"
+}
+
+func ExampleRuleBuilderCommand() {
+ rule := NewRuleBuilder()
+
+ ctx := pathContext()
+
+ // chained
+ rule.Command().
+ Tool(PathForSource(ctx, "ld")).
+ Inputs(PathsForTesting("a.o", "b.o")).
+ FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
+
+ // unchained
+ cmd := rule.Command()
+ cmd.Tool(PathForSource(ctx, "ld"))
+ cmd.Inputs(PathsForTesting("a.o", "b.o"))
+ cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
+
+ // mixed:
+ cmd = rule.Command().Tool(PathForSource(ctx, "ld"))
+ cmd.Inputs(PathsForTesting("a.o", "b.o"))
+ cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked"))
+}
+
+func ExampleRuleBuilderCommand_Flag() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "ls")).Flag("-l"))
+ // Output:
+ // ls -l
+}
+
+func ExampleRuleBuilderCommand_Flags() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
+ // Output:
+ // ls -l -a
+}
+
+func ExampleRuleBuilderCommand_FlagWithArg() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "ls")).
+ FlagWithArg("--sort=", "time"))
+ // Output:
+ // ls --sort=time
+}
+
+func ExampleRuleBuilderCommand_FlagForEachArg() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "ls")).
+ FlagForEachArg("--sort=", []string{"time", "size"}))
+ // Output:
+ // ls --sort=time --sort=size
+}
+
+func ExampleRuleBuilderCommand_FlagForEachInput() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "turbine")).
+ FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
+ // Output:
+ // turbine --classpath a.jar --classpath b.jar
+}
+
+func ExampleRuleBuilderCommand_FlagWithInputList() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "java")).
+ FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
+ // Output:
+ // java -classpath=a.jar:b.jar
+}
+
+func ExampleRuleBuilderCommand_FlagWithInput() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "java")).
+ FlagWithInput("-classpath=", PathForSource(ctx, "a")))
+ // Output:
+ // java -classpath=a
+}
+
+func ExampleRuleBuilderCommand_FlagWithList() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "ls")).
+ FlagWithList("--sort=", []string{"time", "size"}, ","))
+ // Output:
+ // ls --sort=time,size
+}
+
+func TestRuleBuilder(t *testing.T) {
+ rule := NewRuleBuilder()
+
+ fs := map[string][]byte{
+ "dep_fixer": nil,
+ "input": nil,
+ "Implicit": nil,
+ "Input": nil,
+ "Tool": nil,
+ "input2": nil,
+ "tool2": nil,
+ "input3": nil,
+ }
+
+ ctx := PathContextForTesting(TestConfig("out", nil), fs)
+
+ cmd := rule.Command().
+ DepFile(PathForOutput(ctx, "DepFile")).
+ Flag("Flag").
+ FlagWithArg("FlagWithArg=", "arg").
+ FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
+ FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
+ FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
+ Implicit(PathForSource(ctx, "Implicit")).
+ ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
+ ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
+ Input(PathForSource(ctx, "Input")).
+ Output(PathForOutput(ctx, "Output")).
+ Text("Text").
+ Tool(PathForSource(ctx, "Tool"))
+
+ rule.Command().
+ Text("command2").
+ DepFile(PathForOutput(ctx, "depfile2")).
+ Input(PathForSource(ctx, "input2")).
+ Output(PathForOutput(ctx, "output2")).
+ Tool(PathForSource(ctx, "tool2"))
+
+ // Test updates to the first command after the second command has been started
+ cmd.Text("after command2")
+ // Test updating a command when the previous update did not replace the cmd variable
+ cmd.Text("old cmd")
+
+ // Test a command that uses the output of a previous command as an input
+ rule.Command().
+ Text("command3").
+ Input(PathForSource(ctx, "input3")).
+ Input(PathForOutput(ctx, "output2")).
+ Output(PathForOutput(ctx, "output3"))
+
+ wantCommands := []string{
+ "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd",
+ "command2 out/depfile2 input2 out/output2 tool2",
+ "command3 input3 out/output2 out/output3",
+ }
+
+ wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
+
+ wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
+ wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "Output", "output", "output2", "output3"})
+ wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
+ wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
+
+ if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
+ t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
+ }
+}
+
+func testRuleBuilderFactory() Module {
+ module := &testRuleBuilderModule{}
+ module.AddProperties(&module.properties)
+ InitAndroidModule(module)
+ return module
+}
+
+type testRuleBuilderModule struct {
+ ModuleBase
+ properties struct {
+ Src string
+ }
+}
+
+func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ in := PathForSource(ctx, t.properties.Src)
+ out := PathForModuleOut(ctx, ctx.ModuleName())
+
+ testRuleBuilder_Build(ctx, in, out)
+}
+
+type testRuleBuilderSingleton struct{}
+
+func testRuleBuilderSingletonFactory() Singleton {
+ return &testRuleBuilderSingleton{}
+}
+
+func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
+ in := PathForSource(ctx, "bar")
+ out := PathForOutput(ctx, "baz")
+ testRuleBuilder_Build(ctx, in, out)
+}
+
+func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) {
+ rule := NewRuleBuilder()
+
+ rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out)
+
+ rule.Restat()
+
+ rule.Build(pctx, ctx, "rule", "desc")
+}
+
+func TestRuleBuilder_Build(t *testing.T) {
+ buildDir, err := ioutil.TempDir("", "soong_test_rule_builder")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ bp := `
+ rule_builder_test {
+ name: "foo",
+ src: "bar",
+ }
+ `
+
+ config := TestConfig(buildDir, nil)
+ ctx := NewTestContext()
+ ctx.MockFileSystem(map[string][]byte{
+ "Android.bp": []byte(bp),
+ "bar": nil,
+ "cp": nil,
+ })
+ ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory))
+ ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory))
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ check := func(t *testing.T, params TestingBuildParams, wantOutput string) {
+ if len(params.RuleParams.CommandDeps) != 1 || params.RuleParams.CommandDeps[0] != "cp" {
+ t.Errorf("want RuleParams.CommandDeps = [%q], got %q", "cp", params.RuleParams.CommandDeps)
+ }
+
+ if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" {
+ t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
+ }
+
+ if params.Output.String() != wantOutput {
+ t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
+ }
+
+ if !params.RuleParams.Restat {
+ t.Errorf("want RuleParams.Restat = true, got %v", params.RuleParams.Restat)
+ }
+ }
+
+ t.Run("module", func(t *testing.T) {
+ check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
+ filepath.Join(buildDir, ".intermediates", "foo", "foo"))
+ })
+ t.Run("singleton", func(t *testing.T) {
+ check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
+ filepath.Join(buildDir, "baz"))
+ })
+}
diff --git a/android/sh_binary.go b/android/sh_binary.go
new file mode 100644
index 0000000..cf415c5
--- /dev/null
+++ b/android/sh_binary.go
@@ -0,0 +1,180 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "io"
+ "strings"
+)
+
+// sh_binary is for shell scripts (and batch files) that are installed as
+// executable files into .../bin/
+//
+// Do not use them for prebuilt C/C++/etc files. Use cc_prebuilt_binary
+// instead.
+
+func init() {
+ RegisterModuleType("sh_binary", ShBinaryFactory)
+ RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
+ RegisterModuleType("sh_test", ShTestFactory)
+}
+
+type shBinaryProperties struct {
+ // Source file of this prebuilt.
+ Src *string `android:"path,arch_variant"`
+
+ // optional subdirectory under which this file is installed into
+ Sub_dir *string `android:"arch_variant"`
+
+ // optional name for the installed file. If unspecified, name of the module is used as the file name
+ Filename *string `android:"arch_variant"`
+
+ // when set to true, and filename property is not set, the name for the installed file
+ // is the same as the file name of the source file.
+ Filename_from_src *bool `android:"arch_variant"`
+
+ // Whether this module is directly installable to one of the partitions. Default: true.
+ Installable *bool
+}
+
+type TestProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+
+ // the name of the test configuration (for example "AndroidTest.xml") that should be
+ // installed with the module.
+ Test_config *string `android:"arch_variant"`
+}
+
+type ShBinary struct {
+ ModuleBase
+
+ properties shBinaryProperties
+
+ sourceFilePath Path
+ outputFilePath OutputPath
+}
+
+type ShTest struct {
+ ShBinary
+
+ testProperties TestProperties
+}
+
+func (s *ShBinary) DepsMutator(ctx BottomUpMutatorContext) {
+ if s.properties.Src == nil {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ }
+}
+
+func (s *ShBinary) SourceFilePath(ctx ModuleContext) Path {
+ return PathForModuleSrc(ctx, String(s.properties.Src))
+}
+
+func (s *ShBinary) OutputFile() OutputPath {
+ return s.outputFilePath
+}
+
+func (s *ShBinary) SubDir() string {
+ return String(s.properties.Sub_dir)
+}
+
+func (s *ShBinary) Installable() bool {
+ return s.properties.Installable == nil || Bool(s.properties.Installable)
+}
+
+func (s *ShBinary) GenerateAndroidBuildActions(ctx ModuleContext) {
+ s.sourceFilePath = PathForModuleSrc(ctx, String(s.properties.Src))
+ filename := String(s.properties.Filename)
+ filename_from_src := Bool(s.properties.Filename_from_src)
+ if filename == "" {
+ if filename_from_src {
+ filename = s.sourceFilePath.Base()
+ } else {
+ filename = ctx.ModuleName()
+ }
+ } else if filename_from_src {
+ ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+ return
+ }
+ s.outputFilePath = PathForModuleOut(ctx, filename).OutputPath
+
+ // This ensures that outputFilePath has the correct name for others to
+ // use, as the source file may have a different name.
+ ctx.Build(pctx, BuildParams{
+ Rule: CpExecutable,
+ Output: s.outputFilePath,
+ Input: s.sourceFilePath,
+ })
+}
+
+func (s *ShBinary) AndroidMk() AndroidMkData {
+ return AndroidMkData{
+ Class: "EXECUTABLES",
+ OutputFile: OptionalPathForPath(s.outputFilePath),
+ Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+ Extra: []AndroidMkExtraFunc{
+ func(w io.Writer, outputFile Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", String(s.properties.Sub_dir))
+ fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX :=")
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", s.outputFilePath.Rel())
+ },
+ },
+ }
+}
+
+func (s *ShTest) AndroidMk() AndroidMkData {
+ data := s.ShBinary.AndroidMk()
+ data.Class = "NATIVE_TESTS"
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(s.testProperties.Test_suites, " "))
+ fmt.Fprintln(w, "LOCAL_TEST_CONFIG :=", String(s.testProperties.Test_config))
+ })
+ return data
+}
+
+func InitShBinaryModule(s *ShBinary) {
+ s.AddProperties(&s.properties)
+}
+
+// sh_binary is for a shell script or batch file to be installed as an
+// executable binary to <partition>/bin.
+func ShBinaryFactory() Module {
+ module := &ShBinary{}
+ InitShBinaryModule(module)
+ InitAndroidArchModule(module, HostAndDeviceSupported, MultilibFirst)
+ return module
+}
+
+// sh_binary_host is for a shell script to be installed as an executable binary
+// to $(HOST_OUT)/bin.
+func ShBinaryHostFactory() Module {
+ module := &ShBinary{}
+ InitShBinaryModule(module)
+ InitAndroidArchModule(module, HostSupported, MultilibFirst)
+ return module
+}
+
+func ShTestFactory() Module {
+ module := &ShTest{}
+ InitShBinaryModule(&module.ShBinary)
+ module.AddProperties(&module.testProperties)
+
+ InitAndroidArchModule(module, HostAndDeviceSupported, MultilibFirst)
+ return module
+}
diff --git a/android/singleton.go b/android/singleton.go
index f577b0a..0266d77 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -22,6 +22,7 @@
// SingletonContext
type SingletonContext interface {
Config() Config
+ DeviceConfig() DeviceConfig
ModuleName(module blueprint.Module) string
ModuleDir(module blueprint.Module) string
@@ -48,9 +49,12 @@
// are expanded in the scope of the PackageContext.
Eval(pctx PackageContext, ninjaStr string) (string, error)
+ VisitAllModulesBlueprint(visit func(blueprint.Module))
VisitAllModules(visit func(Module))
VisitAllModulesIf(pred func(Module) bool, visit func(Module))
+ // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
VisitDepsDepthFirst(module Module, visit func(Module))
+ // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
visit func(Module))
@@ -72,10 +76,31 @@
type singletonAdaptor struct {
Singleton
+
+ buildParams []BuildParams
+ ruleParams map[blueprint.Rule]blueprint.RuleParams
}
-func (s singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) {
- s.Singleton.GenerateBuildActions(singletonContextAdaptor{ctx})
+var _ testBuildProvider = (*singletonAdaptor)(nil)
+
+func (s *singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) {
+ sctx := &singletonContextAdaptor{SingletonContext: ctx}
+ if sctx.Config().captureBuild {
+ sctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams)
+ }
+
+ s.Singleton.GenerateBuildActions(sctx)
+
+ s.buildParams = sctx.buildParams
+ s.ruleParams = sctx.ruleParams
+}
+
+func (s *singletonAdaptor) BuildParamsForTests() []BuildParams {
+ return s.buildParams
+}
+
+func (s *singletonAdaptor) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
+ return s.ruleParams
}
type Singleton interface {
@@ -84,31 +109,50 @@
type singletonContextAdaptor struct {
blueprint.SingletonContext
+
+ buildParams []BuildParams
+ ruleParams map[blueprint.Rule]blueprint.RuleParams
}
-func (s singletonContextAdaptor) Config() Config {
+func (s *singletonContextAdaptor) Config() Config {
return s.SingletonContext.Config().(Config)
}
-func (s singletonContextAdaptor) Variable(pctx PackageContext, name, value string) {
+func (s *singletonContextAdaptor) DeviceConfig() DeviceConfig {
+ return DeviceConfig{s.Config().deviceConfig}
+}
+
+func (s *singletonContextAdaptor) Variable(pctx PackageContext, name, value string) {
s.SingletonContext.Variable(pctx.PackageContext, name, value)
}
-func (s singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
- return s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...)
+func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
+ if (s.Config().UseGoma() || s.Config().UseRBE()) && params.Pool == nil {
+ // When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict
+ // jobs to the local parallelism value
+ params.Pool = localPool
+ }
+ rule := s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...)
+ if s.Config().captureBuild {
+ s.ruleParams[rule] = params
+ }
+ return rule
}
-func (s singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) {
+func (s *singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) {
+ if s.Config().captureBuild {
+ s.buildParams = append(s.buildParams, params)
+ }
bparams := convertBuildParams(params)
s.SingletonContext.Build(pctx.PackageContext, bparams)
}
-func (s singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
+func (s *singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value)
}
-func (s singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
+func (s *singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
return s.SingletonContext.Eval(pctx.PackageContext, ninjaStr)
}
@@ -136,30 +180,34 @@
}
}
-func (s singletonContextAdaptor) VisitAllModules(visit func(Module)) {
+func (s *singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
+ s.SingletonContext.VisitAllModules(visit)
+}
+
+func (s *singletonContextAdaptor) VisitAllModules(visit func(Module)) {
s.SingletonContext.VisitAllModules(visitAdaptor(visit))
}
-func (s singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
+func (s *singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
}
-func (s singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
+func (s *singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
s.SingletonContext.VisitDepsDepthFirst(module, visitAdaptor(visit))
}
-func (s singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
+func (s *singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
s.SingletonContext.VisitDepsDepthFirstIf(module, predAdaptor(pred), visitAdaptor(visit))
}
-func (s singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) {
+func (s *singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) {
s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
}
-func (s singletonContextAdaptor) PrimaryModule(module Module) Module {
+func (s *singletonContextAdaptor) PrimaryModule(module Module) Module {
return s.SingletonContext.PrimaryModule(module).(Module)
}
-func (s singletonContextAdaptor) FinalModule(module Module) Module {
+func (s *singletonContextAdaptor) FinalModule(module Module) Module {
return s.SingletonContext.FinalModule(module).(Module)
}
diff --git a/android/testing.go b/android/testing.go
index f5d33e1..0ec5af5 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -31,12 +31,14 @@
nameResolver := NewNameResolver(namespaceExportFilter)
ctx := &TestContext{
- Context: blueprint.NewContext(),
+ Context: &Context{blueprint.NewContext()},
NameResolver: nameResolver,
}
ctx.SetNameInterface(nameResolver)
+ ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator)
+
return ctx
}
@@ -47,7 +49,7 @@
}
type TestContext struct {
- *blueprint.Context
+ *Context
preArch, preDeps, postDeps []RegisterMutatorFunc
NameResolver *NameResolver
}
@@ -65,7 +67,7 @@
}
func (ctx *TestContext) Register() {
- registerMutators(ctx.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
+ registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton))
}
@@ -92,6 +94,34 @@
return TestingModule{module}
}
+func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
+ var variants []string
+ ctx.VisitAllModules(func(m blueprint.Module) {
+ if ctx.ModuleName(m) == name {
+ variants = append(variants, ctx.ModuleSubDir(m))
+ }
+ })
+ return variants
+}
+
+// SingletonForTests returns a TestingSingleton for the singleton registered with the given name.
+func (ctx *TestContext) SingletonForTests(name string) TestingSingleton {
+ allSingletonNames := []string{}
+ for _, s := range ctx.Singletons() {
+ n := ctx.SingletonName(s)
+ if n == name {
+ return TestingSingleton{
+ singleton: s.(*singletonAdaptor).Singleton,
+ provider: s.(testBuildProvider),
+ }
+ }
+ allSingletonNames = append(allSingletonNames, n)
+ }
+
+ panic(fmt.Errorf("failed to find singleton %q."+
+ "\nall singletons: %v", name, allSingletonNames))
+}
+
// MockFileSystem causes the Context to replace all reads with accesses to the provided map of
// filenames to contents stored as a byte slice.
func (ctx *TestContext) MockFileSystem(files map[string][]byte) {
@@ -111,48 +141,200 @@
ctx.Context.MockFileSystem(files)
}
-type TestingModule struct {
- module Module
+type testBuildProvider interface {
+ BuildParamsForTests() []BuildParams
+ RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
}
-func (m TestingModule) Module() Module {
- return m.module
+type TestingBuildParams struct {
+ BuildParams
+ RuleParams blueprint.RuleParams
}
-func (m TestingModule) Rule(rule string) BuildParams {
- for _, p := range m.module.BuildParamsForTests() {
+func newTestingBuildParams(provider testBuildProvider, bparams BuildParams) TestingBuildParams {
+ return TestingBuildParams{
+ BuildParams: bparams,
+ RuleParams: provider.RuleParamsForTests()[bparams.Rule],
+ }
+}
+
+func maybeBuildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
+ for _, p := range provider.BuildParamsForTests() {
if strings.Contains(p.Rule.String(), rule) {
- return p
+ return newTestingBuildParams(provider, p)
}
}
- panic(fmt.Errorf("couldn't find rule %q", rule))
+ return TestingBuildParams{}
}
-func (m TestingModule) Description(desc string) BuildParams {
- for _, p := range m.module.BuildParamsForTests() {
+func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
+ p := maybeBuildParamsFromRule(provider, rule)
+ if p.Rule == nil {
+ panic(fmt.Errorf("couldn't find rule %q", rule))
+ }
+ return p
+}
+
+func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
+ for _, p := range provider.BuildParamsForTests() {
if p.Description == desc {
- return p
+ return newTestingBuildParams(provider, p)
}
}
- panic(fmt.Errorf("couldn't find description %q", desc))
+ return TestingBuildParams{}
}
-func (m TestingModule) Output(file string) BuildParams {
+func buildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
+ p := maybeBuildParamsFromDescription(provider, desc)
+ if p.Rule == nil {
+ panic(fmt.Errorf("couldn't find description %q", desc))
+ }
+ return p
+}
+
+func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) {
var searchedOutputs []string
- for _, p := range m.module.BuildParamsForTests() {
+ for _, p := range provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
+ outputs = append(outputs, p.ImplicitOutputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
for _, f := range outputs {
if f.String() == file || f.Rel() == file {
- return p
+ return newTestingBuildParams(provider, p), nil
}
searchedOutputs = append(searchedOutputs, f.Rel())
}
}
- panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
- file, searchedOutputs))
+ return TestingBuildParams{}, searchedOutputs
+}
+
+func buildParamsFromOutput(provider testBuildProvider, file string) TestingBuildParams {
+ p, searchedOutputs := maybeBuildParamsFromOutput(provider, file)
+ if p.Rule == nil {
+ panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
+ file, searchedOutputs))
+ }
+ return p
+}
+
+func allOutputs(provider testBuildProvider) []string {
+ var outputFullPaths []string
+ for _, p := range provider.BuildParamsForTests() {
+ outputs := append(WritablePaths(nil), p.Outputs...)
+ outputs = append(outputs, p.ImplicitOutputs...)
+ if p.Output != nil {
+ outputs = append(outputs, p.Output)
+ }
+ outputFullPaths = append(outputFullPaths, outputs.Strings()...)
+ }
+ return outputFullPaths
+}
+
+// TestingModule is wrapper around an android.Module that provides methods to find information about individual
+// ctx.Build parameters for verification in tests.
+type TestingModule struct {
+ module Module
+}
+
+// Module returns the Module wrapped by the TestingModule.
+func (m TestingModule) Module() Module {
+ return m.module
+}
+
+// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
+// BuildParams if no rule is found.
+func (m TestingModule) MaybeRule(rule string) TestingBuildParams {
+ return maybeBuildParamsFromRule(m.module, rule)
+}
+
+// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
+func (m TestingModule) Rule(rule string) TestingBuildParams {
+ return buildParamsFromRule(m.module, rule)
+}
+
+// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
+// BuildParams if no rule is found.
+func (m TestingModule) MaybeDescription(desc string) TestingBuildParams {
+ return maybeBuildParamsFromDescription(m.module, desc)
+}
+
+// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
+// found.
+func (m TestingModule) Description(desc string) TestingBuildParams {
+ return buildParamsFromDescription(m.module, desc)
+}
+
+// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Returns an empty BuildParams if no rule is found.
+func (m TestingModule) MaybeOutput(file string) TestingBuildParams {
+ p, _ := maybeBuildParamsFromOutput(m.module, file)
+ return p
+}
+
+// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Panics if no rule is found.
+func (m TestingModule) Output(file string) TestingBuildParams {
+ return buildParamsFromOutput(m.module, file)
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (m TestingModule) AllOutputs() []string {
+ return allOutputs(m.module)
+}
+
+// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
+// ctx.Build parameters for verification in tests.
+type TestingSingleton struct {
+ singleton Singleton
+ provider testBuildProvider
+}
+
+// Singleton returns the Singleton wrapped by the TestingSingleton.
+func (s TestingSingleton) Singleton() Singleton {
+ return s.singleton
+}
+
+// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
+// BuildParams if no rule is found.
+func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams {
+ return maybeBuildParamsFromRule(s.provider, rule)
+}
+
+// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
+func (s TestingSingleton) Rule(rule string) TestingBuildParams {
+ return buildParamsFromRule(s.provider, rule)
+}
+
+// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
+// BuildParams if no rule is found.
+func (s TestingSingleton) MaybeDescription(desc string) TestingBuildParams {
+ return maybeBuildParamsFromDescription(s.provider, desc)
+}
+
+// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
+// found.
+func (s TestingSingleton) Description(desc string) TestingBuildParams {
+ return buildParamsFromDescription(s.provider, desc)
+}
+
+// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Returns an empty BuildParams if no rule is found.
+func (s TestingSingleton) MaybeOutput(file string) TestingBuildParams {
+ p, _ := maybeBuildParamsFromOutput(s.provider, file)
+ return p
+}
+
+// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Panics if no rule is found.
+func (s TestingSingleton) Output(file string) TestingBuildParams {
+ return buildParamsFromOutput(s.provider, file)
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (s TestingSingleton) AllOutputs() []string {
+ return allOutputs(s.provider)
}
func FailIfErrored(t *testing.T, errs []error) {
diff --git a/android/util.go b/android/util.go
index e9b9764..f9dce6f 100644
--- a/android/util.go
+++ b/android/util.go
@@ -15,11 +15,18 @@
package android
import (
+ "fmt"
+ "regexp"
"runtime"
"sort"
"strings"
)
+// CopyOf returns a new slice that has the same contents as s.
+func CopyOf(s []string) []string {
+ return append([]string(nil), s...)
+}
+
func JoinWithPrefix(strs []string, prefix string) string {
if len(strs) == 0 {
return ""
@@ -159,12 +166,17 @@
panic("not called from an init func")
}
- if funcName == "init" || strings.HasPrefix(funcName, "init·") {
+ if funcName == "init" || strings.HasPrefix(funcName, "init·") ||
+ strings.HasPrefix(funcName, "init.") {
return
}
}
}
+// A regex to find a package path within a function name. It finds the shortest string that is
+// followed by '.' and doesn't have any '/'s left.
+var pkgPathRe = regexp.MustCompile(`^(.*?)\.([^/]+)$`)
+
// callerName returns the package path and function name of the calling
// function. The skip argument has the same meaning as the skip argument of
// runtime.Callers.
@@ -175,25 +187,13 @@
return "", "", false
}
- f := runtime.FuncForPC(pc[0])
- fullName := f.Name()
-
- lastDotIndex := strings.LastIndex(fullName, ".")
- if lastDotIndex == -1 {
- panic("unable to distinguish function name from package")
+ f := runtime.FuncForPC(pc[0]).Name()
+ s := pkgPathRe.FindStringSubmatch(f)
+ if len(s) < 3 {
+ panic(fmt.Errorf("failed to extract package path and function name from %q", f))
}
- if fullName[lastDotIndex-1] == ')' {
- // The caller is a method on some type, so it's name looks like
- // "pkg/path.(type).method". We need to go back one dot farther to get
- // to the package name.
- lastDotIndex = strings.LastIndex(fullName[:lastDotIndex], ".")
- }
-
- pkgPath = fullName[:lastDotIndex]
- funcName = fullName[lastDotIndex+1:]
- ok = true
- return
+ return s[1], s[2], true
}
func GetNumericSdkVersion(v string) string {
@@ -202,3 +202,44 @@
}
return v
}
+
+// copied from build/kati/strutil.go
+func substPattern(pat, repl, str string) string {
+ ps := strings.SplitN(pat, "%", 2)
+ if len(ps) != 2 {
+ if str == pat {
+ return repl
+ }
+ return str
+ }
+ in := str
+ trimed := str
+ if ps[0] != "" {
+ trimed = strings.TrimPrefix(in, ps[0])
+ if trimed == in {
+ return str
+ }
+ }
+ in = trimed
+ if ps[1] != "" {
+ trimed = strings.TrimSuffix(in, ps[1])
+ if trimed == in {
+ return str
+ }
+ }
+
+ rs := strings.SplitN(repl, "%", 2)
+ if len(rs) != 2 {
+ return repl
+ }
+ return rs[0] + trimed + rs[1]
+}
+
+// copied from build/kati/strutil.go
+func matchPattern(pat, str string) bool {
+ i := strings.IndexByte(pat, '%')
+ if i < 0 {
+ return pat == str
+ }
+ return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:])
+}
diff --git a/android/util_test.go b/android/util_test.go
index 1c791b2..2e5eb07 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -15,6 +15,7 @@
package android
import (
+ "fmt"
"reflect"
"testing"
)
@@ -359,3 +360,47 @@
})
}
}
+
+func ExampleCopyOf() {
+ a := []string{"1", "2", "3"}
+ b := CopyOf(a)
+ a[0] = "-1"
+ fmt.Printf("a = %q\n", a)
+ fmt.Printf("b = %q\n", b)
+
+ // Output:
+ // a = ["-1" "2" "3"]
+ // b = ["1" "2" "3"]
+}
+
+func ExampleCopyOf_append() {
+ a := make([]string, 1, 2)
+ a[0] = "foo"
+
+ fmt.Println("Without CopyOf:")
+ b := append(a, "bar")
+ c := append(a, "baz")
+ fmt.Printf("a = %q\n", a)
+ fmt.Printf("b = %q\n", b)
+ fmt.Printf("c = %q\n", c)
+
+ a = make([]string, 1, 2)
+ a[0] = "foo"
+
+ fmt.Println("With CopyOf:")
+ b = append(CopyOf(a), "bar")
+ c = append(CopyOf(a), "baz")
+ fmt.Printf("a = %q\n", a)
+ fmt.Printf("b = %q\n", b)
+ fmt.Printf("c = %q\n", c)
+
+ // Output:
+ // Without CopyOf:
+ // a = ["foo"]
+ // b = ["foo" "baz"]
+ // c = ["foo" "baz"]
+ // With CopyOf:
+ // a = ["foo"]
+ // b = ["foo" "bar"]
+ // c = ["foo" "baz"]
+}
diff --git a/android/variable.go b/android/variable.go
index f4aaec7..a88fc9d 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -42,14 +42,10 @@
Enabled *bool `android:"arch_variant"`
} `android:"arch_variant"`
- Brillo struct {
- Cflags []string
- Version_script *string `android:"arch_variant"`
- } `android:"arch_variant"`
-
Malloc_not_svelte struct {
- Cflags []string
- }
+ Cflags []string `android:"arch_variant"`
+ Shared_libs []string `android:"arch_variant"`
+ } `android:"arch_variant"`
Safestack struct {
Cflags []string `android:"arch_variant"`
@@ -59,12 +55,19 @@
Cflags []string
}
- Device_uses_hwc2 struct {
+ Override_rs_driver struct {
Cflags []string
}
- Override_rs_driver struct {
- Cflags []string
+ // Product_is_iot is true for Android Things devices.
+ Product_is_iot struct {
+ Cflags []string
+ Enabled bool
+ Exclude_srcs []string
+ Init_rc []string
+ Shared_libs []string
+ Srcs []string
+ Static_libs []string
}
// treble_linker_namespaces is true when the system/vendor linker namespace separation is
@@ -85,6 +88,7 @@
Cflags []string
Cppflags []string
Init_rc []string
+ Required []string
}
// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
@@ -92,6 +96,12 @@
Eng struct {
Cflags []string
Cppflags []string
+ Lto struct {
+ Never *bool
+ }
+ Sanitize struct {
+ Address *bool
+ }
}
Pdk struct {
@@ -127,27 +137,31 @@
BuildNumberFromFile *string `json:",omitempty"`
DateFromFile *string `json:",omitempty"`
- Platform_version_name *string `json:",omitempty"`
- Platform_sdk_version *int `json:",omitempty"`
- Platform_sdk_codename *string `json:",omitempty"`
- Platform_sdk_final *bool `json:",omitempty"`
- Platform_version_active_codenames []string `json:",omitempty"`
- Platform_version_future_codenames []string `json:",omitempty"`
- Platform_vndk_version *string `json:",omitempty"`
- Platform_systemsdk_versions []string `json:",omitempty"`
+ Platform_version_name *string `json:",omitempty"`
+ Platform_sdk_version *int `json:",omitempty"`
+ Platform_sdk_codename *string `json:",omitempty"`
+ Platform_sdk_final *bool `json:",omitempty"`
+ Platform_version_active_codenames []string `json:",omitempty"`
+ Platform_version_future_codenames []string `json:",omitempty"`
+ Platform_vndk_version *string `json:",omitempty"`
+ Platform_systemsdk_versions []string `json:",omitempty"`
+ Platform_security_patch *string `json:",omitempty"`
+ Platform_preview_sdk_version *string `json:",omitempty"`
+ Platform_min_supported_target_sdk_version *string `json:",omitempty"`
+ Platform_base_os *string `json:",omitempty"`
- DeviceName *string `json:",omitempty"`
- DeviceArch *string `json:",omitempty"`
- DeviceArchVariant *string `json:",omitempty"`
- DeviceCpuVariant *string `json:",omitempty"`
- DeviceAbi *[]string `json:",omitempty"`
- DeviceVndkVersion *string `json:",omitempty"`
- DeviceSystemSdkVersions *[]string `json:",omitempty"`
+ DeviceName *string `json:",omitempty"`
+ DeviceArch *string `json:",omitempty"`
+ DeviceArchVariant *string `json:",omitempty"`
+ DeviceCpuVariant *string `json:",omitempty"`
+ DeviceAbi []string `json:",omitempty"`
+ DeviceVndkVersion *string `json:",omitempty"`
+ DeviceSystemSdkVersions []string `json:",omitempty"`
- DeviceSecondaryArch *string `json:",omitempty"`
- DeviceSecondaryArchVariant *string `json:",omitempty"`
- DeviceSecondaryCpuVariant *string `json:",omitempty"`
- DeviceSecondaryAbi *[]string `json:",omitempty"`
+ DeviceSecondaryArch *string `json:",omitempty"`
+ DeviceSecondaryArchVariant *string `json:",omitempty"`
+ DeviceSecondaryCpuVariant *string `json:",omitempty"`
+ DeviceSecondaryAbi []string `json:",omitempty"`
HostArch *string `json:",omitempty"`
HostSecondaryArch *string `json:",omitempty"`
@@ -156,56 +170,73 @@
CrossHostArch *string `json:",omitempty"`
CrossHostSecondaryArch *string `json:",omitempty"`
- ResourceOverlays *[]string `json:",omitempty"`
- EnforceRROTargets *[]string `json:",omitempty"`
- EnforceRROExcludedOverlays *[]string `json:",omitempty"`
+ DeviceResourceOverlays []string `json:",omitempty"`
+ ProductResourceOverlays []string `json:",omitempty"`
+ EnforceRROTargets []string `json:",omitempty"`
+ EnforceRROExcludedOverlays []string `json:",omitempty"`
- AAPTCharacteristics *string `json:",omitempty"`
- AAPTConfig *[]string `json:",omitempty"`
- AAPTPreferredConfig *string `json:",omitempty"`
- AAPTPrebuiltDPI *[]string `json:",omitempty"`
+ AAPTCharacteristics *string `json:",omitempty"`
+ AAPTConfig []string `json:",omitempty"`
+ AAPTPreferredConfig *string `json:",omitempty"`
+ AAPTPrebuiltDPI []string `json:",omitempty"`
DefaultAppCertificate *string `json:",omitempty"`
AppsDefaultVersionName *string `json:",omitempty"`
- Allow_missing_dependencies *bool `json:",omitempty"`
- Unbundled_build *bool `json:",omitempty"`
- Brillo *bool `json:",omitempty"`
- Malloc_not_svelte *bool `json:",omitempty"`
- Safestack *bool `json:",omitempty"`
- HostStaticBinaries *bool `json:",omitempty"`
- Binder32bit *bool `json:",omitempty"`
- UseGoma *bool `json:",omitempty"`
- Debuggable *bool `json:",omitempty"`
- Eng *bool `json:",omitempty"`
- Device_uses_hwc2 *bool `json:",omitempty"`
- Treble_linker_namespaces *bool `json:",omitempty"`
- Sepolicy_split *bool `json:",omitempty"`
- Enforce_vintf_manifest *bool `json:",omitempty"`
- Pdk *bool `json:",omitempty"`
- Uml *bool `json:",omitempty"`
- Use_lmkd_stats_log *bool `json:",omitempty"`
- Arc *bool `json:",omitempty"`
- MinimizeJavaDebugInfo *bool `json:",omitempty"`
+ Allow_missing_dependencies *bool `json:",omitempty"`
+ Unbundled_build *bool `json:",omitempty"`
+ Unbundled_build_sdks_from_source *bool `json:",omitempty"`
+ Malloc_not_svelte *bool `json:",omitempty"`
+ Safestack *bool `json:",omitempty"`
+ HostStaticBinaries *bool `json:",omitempty"`
+ Binder32bit *bool `json:",omitempty"`
+ UseGoma *bool `json:",omitempty"`
+ UseRBE *bool `json:",omitempty"`
+ UseRBEJAVAC *bool `json:",omitempty"`
+ UseRBER8 *bool `json:",omitempty"`
+ UseRBED8 *bool `json:",omitempty"`
+ Debuggable *bool `json:",omitempty"`
+ Eng *bool `json:",omitempty"`
+ Treble_linker_namespaces *bool `json:",omitempty"`
+ Enforce_vintf_manifest *bool `json:",omitempty"`
+ Pdk *bool `json:",omitempty"`
+ Uml *bool `json:",omitempty"`
+ Use_lmkd_stats_log *bool `json:",omitempty"`
+ Arc *bool `json:",omitempty"`
+ MinimizeJavaDebugInfo *bool `json:",omitempty"`
- IntegerOverflowExcludePaths *[]string `json:",omitempty"`
+ Check_elf_files *bool `json:",omitempty"`
- EnableCFI *bool `json:",omitempty"`
- CFIExcludePaths *[]string `json:",omitempty"`
- CFIIncludePaths *[]string `json:",omitempty"`
+ UncompressPrivAppDex *bool `json:",omitempty"`
+ ModulesLoadedByPrivilegedModules []string `json:",omitempty"`
- VendorPath *string `json:",omitempty"`
- OdmPath *string `json:",omitempty"`
- ProductPath *string `json:",omitempty"`
+ BootJars []string `json:",omitempty"`
+
+ IntegerOverflowExcludePaths []string `json:",omitempty"`
+
+ EnableCFI *bool `json:",omitempty"`
+ CFIExcludePaths []string `json:",omitempty"`
+ CFIIncludePaths []string `json:",omitempty"`
+
+ DisableScudo *bool `json:",omitempty"`
+
+ EnableXOM *bool `json:",omitempty"`
+ XOMExcludePaths []string `json:",omitempty"`
+
+ VendorPath *string `json:",omitempty"`
+ OdmPath *string `json:",omitempty"`
+ ProductPath *string `json:",omitempty"`
+ ProductServicesPath *string `json:",omitempty"`
ClangTidy *bool `json:",omitempty"`
TidyChecks *string `json:",omitempty"`
- NativeCoverage *bool `json:",omitempty"`
- CoveragePaths *[]string `json:",omitempty"`
- CoverageExcludePaths *[]string `json:",omitempty"`
+ NativeCoverage *bool `json:",omitempty"`
+ CoveragePaths []string `json:",omitempty"`
+ CoverageExcludePaths []string `json:",omitempty"`
+ DevicePrefer32BitApps *bool `json:",omitempty"`
DevicePrefer32BitExecutables *bool `json:",omitempty"`
HostPrefer32BitExecutables *bool `json:",omitempty"`
@@ -220,8 +251,11 @@
Override_rs_driver *string `json:",omitempty"`
+ Product_is_iot *bool `json:",omitempty"`
+
+ Fuchsia *bool `json:",omitempty"`
+
DeviceKernelHeaders []string `json:",omitempty"`
- DistDir *string `json:",omitempty"`
ExtraVndkVersions []string `json:",omitempty"`
@@ -229,7 +263,34 @@
PgoAdditionalProfileDirs []string `json:",omitempty"`
+ VndkUseCoreVariant *bool `json:",omitempty"`
+
+ BoardVendorSepolicyDirs []string `json:",omitempty"`
+ BoardOdmSepolicyDirs []string `json:",omitempty"`
+ BoardPlatPublicSepolicyDirs []string `json:",omitempty"`
+ BoardPlatPrivateSepolicyDirs []string `json:",omitempty"`
+
VendorVars map[string]map[string]string `json:",omitempty"`
+
+ Ndk_abis *bool `json:",omitempty"`
+ Exclude_draft_ndk_apis *bool `json:",omitempty"`
+
+ FlattenApex *bool `json:",omitempty"`
+
+ DexpreoptGlobalConfig *string `json:",omitempty"`
+
+ ManifestPackageNameOverrides []string `json:",omitempty"`
+ CertificateOverrides []string `json:",omitempty"`
+ PackageNameOverrides []string `json:",omitempty"`
+
+ EnforceSystemCertificate *bool `json:",omitempty"`
+ EnforceSystemCertificateWhitelist []string `json:",omitempty"`
+
+ ProductHiddenAPIStubs []string `json:",omitempty"`
+ ProductHiddenAPIStubsSystem []string `json:",omitempty"`
+ ProductHiddenAPIStubsTest []string `json:",omitempty"`
+
+ TargetFSConfigGen []string `json:",omitempty"`
}
func boolPtr(v bool) *bool {
@@ -256,16 +317,16 @@
DeviceArch: stringPtr("arm64"),
DeviceArchVariant: stringPtr("armv8-a"),
DeviceCpuVariant: stringPtr("generic"),
- DeviceAbi: &[]string{"arm64-v8a"},
+ DeviceAbi: []string{"arm64-v8a"},
DeviceSecondaryArch: stringPtr("arm"),
DeviceSecondaryArchVariant: stringPtr("armv8-a"),
DeviceSecondaryCpuVariant: stringPtr("generic"),
- DeviceSecondaryAbi: &[]string{"armeabi-v7a", "armeabi"},
+ DeviceSecondaryAbi: []string{"armeabi-v7a", "armeabi"},
- AAPTConfig: &[]string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+ AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
AAPTPreferredConfig: stringPtr("xhdpi"),
AAPTCharacteristics: stringPtr("nosdcard"),
- AAPTPrebuiltDPI: &[]string{"xhdpi", "xxhdpi"},
+ AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
Malloc_not_svelte: boolPtr(true),
Safestack: boolPtr(false),
diff --git a/android/vts_config.go b/android/vts_config.go
new file mode 100644
index 0000000..c44b3a3
--- /dev/null
+++ b/android/vts_config.go
@@ -0,0 +1,69 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "io"
+)
+
+func init() {
+ RegisterModuleType("vts_config", VtsConfigFactory)
+}
+
+type vtsConfigProperties struct {
+ // Override the default (AndroidTest.xml) test manifest file name.
+ Test_config *string
+}
+
+type VtsConfig struct {
+ ModuleBase
+ properties vtsConfigProperties
+ OutputFilePath OutputPath
+}
+
+func (me *VtsConfig) GenerateAndroidBuildActions(ctx ModuleContext) {
+ me.OutputFilePath = PathForModuleOut(ctx, me.BaseModuleName()).OutputPath
+}
+
+func (me *VtsConfig) AndroidMk() AndroidMkData {
+ androidMkData := AndroidMkData{
+ Class: "FAKE",
+ Include: "$(BUILD_SYSTEM)/android_vts_host_config.mk",
+ OutputFile: OptionalPathForPath(me.OutputFilePath),
+ }
+ if me.properties.Test_config != nil {
+ androidMkData.Extra = []AndroidMkExtraFunc{
+ func(w io.Writer, outputFile Path) {
+ fmt.Fprintf(w, "LOCAL_TEST_CONFIG := %s\n",
+ *me.properties.Test_config)
+ },
+ }
+ }
+ return androidMkData
+}
+
+func InitVtsConfigModule(me *VtsConfig) {
+ me.AddProperties(&me.properties)
+}
+
+// vts_config generates a Vendor Test Suite (VTS) configuration file from the
+// <test_config> xml file and stores it in a subdirectory of $(HOST_OUT).
+func VtsConfigFactory() Module {
+ module := &VtsConfig{}
+ InitVtsConfigModule(module)
+ InitAndroidArchModule(module /*TODO: or HostAndDeviceSupported? */, HostSupported, MultilibFirst)
+ return module
+}
diff --git a/android/vts_config_test.go b/android/vts_config_test.go
new file mode 100644
index 0000000..7d4c9b1
--- /dev/null
+++ b/android/vts_config_test.go
@@ -0,0 +1,61 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func testVtsConfig(test *testing.T, bpFileContents string) *TestContext {
+ buildDir, err := ioutil.TempDir("", "soong_vts_config_test")
+ if err != nil {
+ test.Fatal(err)
+ }
+
+ config := TestArchConfig(buildDir, nil)
+ defer func() { os.RemoveAll(buildDir) }()
+
+ ctx := NewTestArchContext()
+ ctx.RegisterModuleType("vts_config", ModuleFactoryAdaptor(VtsConfigFactory))
+ ctx.Register()
+ mockFiles := map[string][]byte{
+ "Android.bp": []byte(bpFileContents),
+ }
+ ctx.MockFileSystem(mockFiles)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(test, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(test, errs)
+ return ctx
+}
+
+func TestVtsConfig(t *testing.T) {
+ ctx := testVtsConfig(t, `
+vts_config { name: "plain"}
+vts_config { name: "with_manifest", test_config: "manifest.xml" }
+`)
+
+ variants := ctx.ModuleVariantsForTests("plain")
+ if len(variants) > 1 {
+ t.Errorf("expected 1, got %d", len(variants))
+ }
+ expectedOutputFilename := ctx.ModuleForTests(
+ "plain", variants[0]).Module().(*VtsConfig).OutputFilePath.Base()
+ if expectedOutputFilename != "plain" {
+ t.Errorf("expected plain, got %q", expectedOutputFilename)
+ }
+}
diff --git a/android/writedocs.go b/android/writedocs.go
index 9737030..7262ad8 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -53,15 +53,19 @@
primaryBuilder := primaryBuilderPath(ctx)
soongDocs := ctx.Rule(pctx, "soongDocs",
blueprint.RuleParams{
- Command: fmt.Sprintf("%s --soong_docs %s %s",
+ Command: fmt.Sprintf("rm -f ${outDir}/* && %s --soong_docs %s %s",
primaryBuilder.String(), docsFile.String(), strings.Join(os.Args[1:], " ")),
CommandDeps: []string{primaryBuilder.String()},
Description: fmt.Sprintf("%s docs $out", primaryBuilder.Base()),
- })
+ },
+ "outDir")
ctx.Build(pctx, BuildParams{
Rule: soongDocs,
Output: docsFile,
+ Args: map[string]string{
+ "outDir": PathForOutput(ctx, "docs").String(),
+ },
})
// Add a phony target for building the documentation
diff --git a/androidmk/Android.bp b/androidmk/Android.bp
index 442452f..1d939b0 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -44,6 +44,6 @@
],
testSrcs: [
"parser/make_strings_test.go",
+ "parser/parser_test.go",
],
}
-
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 5cb4869..52bcf9c 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -42,18 +42,24 @@
var rewriteProperties = map[string](func(variableAssignmentContext) error){
// custom functions
+ "LOCAL_32_BIT_ONLY": local32BitOnly,
"LOCAL_AIDL_INCLUDES": localAidlIncludes,
+ "LOCAL_ASSET_DIR": localizePathList("asset_dirs"),
"LOCAL_C_INCLUDES": localIncludeDirs,
"LOCAL_EXPORT_C_INCLUDE_DIRS": exportIncludeDirs,
+ "LOCAL_JARJAR_RULES": localizePath("jarjar_rules"),
"LOCAL_LDFLAGS": ldflags,
"LOCAL_MODULE_CLASS": prebuiltClass,
"LOCAL_MODULE_STEM": stem,
"LOCAL_MODULE_HOST_OS": hostOs,
+ "LOCAL_RESOURCE_DIR": localizePathList("resource_dirs"),
"LOCAL_SANITIZE": sanitize(""),
"LOCAL_SANITIZE_DIAG": sanitize("diag."),
+ "LOCAL_STRIP_MODULE": strip(),
"LOCAL_CFLAGS": cflags,
"LOCAL_UNINSTALLABLE_MODULE": invert("installable"),
"LOCAL_PROGUARD_ENABLED": proguardEnabled,
+ "LOCAL_MODULE_PATH": prebuiltModulePath,
// composite functions
"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
@@ -67,6 +73,11 @@
"LOCAL_BUILT_MODULE_STEM": skip,
"LOCAL_USE_AAPT2": skip, // Always enabled in Soong
"LOCAL_JAR_EXCLUDE_FILES": skip, // Soong never excludes files from jars
+
+ "LOCAL_ANNOTATION_PROCESSOR_CLASSES": skip, // Soong gets the processor classes from the plugin
+ "LOCAL_CTS_TEST_PACKAGE": skip, // Obsolete
+ "LOCAL_JACK_ENABLED": skip, // Obselete
+ "LOCAL_JACK_FLAGS": skip, // Obselete
}
// adds a group of properties all having the same type
@@ -81,13 +92,12 @@
map[string]string{
"LOCAL_MODULE": "name",
"LOCAL_CXX_STL": "stl",
- "LOCAL_STRIP_MODULE": "strip",
"LOCAL_MULTILIB": "compile_multilib",
"LOCAL_ARM_MODE_HACK": "instruction_set",
"LOCAL_SDK_VERSION": "sdk_version",
+ "LOCAL_MIN_SDK_VERSION": "min_sdk_version",
"LOCAL_NDK_STL_VARIANT": "stl",
"LOCAL_JAR_MANIFEST": "manifest",
- "LOCAL_JARJAR_RULES": "jarjar_rules",
"LOCAL_CERTIFICATE": "certificate",
"LOCAL_PACKAGE_NAME": "name",
"LOCAL_MODULE_RELATIVE_PATH": "relative_install_path",
@@ -100,6 +110,7 @@
"LOCAL_MANIFEST_FILE": "manifest",
"LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING": "dex_preopt.profile",
+ "LOCAL_TEST_CONFIG": "test_config",
})
addStandardProperties(bpparser.ListType,
map[string]string{
@@ -125,6 +136,7 @@
"LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": "export_shared_lib_headers",
"LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": "export_static_lib_headers",
"LOCAL_INIT_RC": "init_rc",
+ "LOCAL_VINTF_FRAGMENTS": "vintf_fragments",
"LOCAL_TIDY_FLAGS": "tidy_flags",
// TODO: This is comma-separated, not space-separated
"LOCAL_TIDY_CHECKS": "tidy_checks",
@@ -132,51 +144,64 @@
"LOCAL_RENDERSCRIPT_FLAGS": "renderscript.flags",
"LOCAL_JAVA_RESOURCE_DIRS": "java_resource_dirs",
- "LOCAL_RESOURCE_DIR": "resource_dirs",
"LOCAL_JAVACFLAGS": "javacflags",
"LOCAL_ERROR_PRONE_FLAGS": "errorprone.javacflags",
"LOCAL_DX_FLAGS": "dxflags",
"LOCAL_JAVA_LIBRARIES": "libs",
"LOCAL_STATIC_JAVA_LIBRARIES": "static_libs",
+ "LOCAL_JNI_SHARED_LIBRARIES": "jni_libs",
"LOCAL_AAPT_FLAGS": "aaptflags",
"LOCAL_PACKAGE_SPLITS": "package_splits",
"LOCAL_COMPATIBILITY_SUITE": "test_suites",
+ "LOCAL_OVERRIDES_PACKAGES": "overrides",
- "LOCAL_ANNOTATION_PROCESSORS": "annotation_processors",
- "LOCAL_ANNOTATION_PROCESSOR_CLASSES": "annotation_processor_classes",
+ "LOCAL_ANNOTATION_PROCESSORS": "plugins",
"LOCAL_PROGUARD_FLAGS": "optimize.proguard_flags",
- "LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flag_files",
+ "LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flags_files",
// These will be rewritten to libs/static_libs by bpfix, after their presence is used to convert
// java_library_static to android_library.
"LOCAL_SHARED_ANDROID_LIBRARIES": "android_libs",
"LOCAL_STATIC_ANDROID_LIBRARIES": "android_static_libs",
+ "LOCAL_ADDITIONAL_CERTIFICATES": "additional_certificates",
+
+ // Jacoco filters:
+ "LOCAL_JACK_COVERAGE_INCLUDE_FILTER": "jacoco.include_filter",
+ "LOCAL_JACK_COVERAGE_EXCLUDE_FILTER": "jacoco.exclude_filter",
})
addStandardProperties(bpparser.BoolType,
map[string]string{
// Bool properties
- "LOCAL_IS_HOST_MODULE": "host",
- "LOCAL_CLANG": "clang",
- "LOCAL_FORCE_STATIC_EXECUTABLE": "static_executable",
- "LOCAL_NATIVE_COVERAGE": "native_coverage",
- "LOCAL_NO_CRT": "nocrt",
- "LOCAL_ALLOW_UNDEFINED_SYMBOLS": "allow_undefined_symbols",
- "LOCAL_RTTI_FLAG": "rtti",
- "LOCAL_NO_STANDARD_LIBRARIES": "no_standard_libs",
- "LOCAL_PACK_MODULE_RELOCATIONS": "pack_relocations",
- "LOCAL_TIDY": "tidy",
- "LOCAL_PROPRIETARY_MODULE": "proprietary",
- "LOCAL_VENDOR_MODULE": "vendor",
- "LOCAL_ODM_MODULE": "device_specific",
- "LOCAL_PRODUCT_MODULE": "product_specific",
- "LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
- "LOCAL_PRIVILEGED_MODULE": "privileged",
+ "LOCAL_IS_HOST_MODULE": "host",
+ "LOCAL_CLANG": "clang",
+ "LOCAL_FORCE_STATIC_EXECUTABLE": "static_executable",
+ "LOCAL_NATIVE_COVERAGE": "native_coverage",
+ "LOCAL_NO_CRT": "nocrt",
+ "LOCAL_ALLOW_UNDEFINED_SYMBOLS": "allow_undefined_symbols",
+ "LOCAL_RTTI_FLAG": "rtti",
+ "LOCAL_NO_STANDARD_LIBRARIES": "no_standard_libs",
+ "LOCAL_PACK_MODULE_RELOCATIONS": "pack_relocations",
+ "LOCAL_TIDY": "tidy",
+ "LOCAL_USE_CLANG_LLD": "use_clang_lld",
+ "LOCAL_PROPRIETARY_MODULE": "proprietary",
+ "LOCAL_VENDOR_MODULE": "vendor",
+ "LOCAL_ODM_MODULE": "device_specific",
+ "LOCAL_PRODUCT_MODULE": "product_specific",
+ "LOCAL_PRODUCT_SERVICES_MODULE": "product_services_specific",
+ "LOCAL_EXPORT_PACKAGE_RESOURCES": "export_package_resources",
+ "LOCAL_PRIVILEGED_MODULE": "privileged",
+ "LOCAL_AAPT_INCLUDE_ALL_RESOURCES": "aapt_include_all_resources",
+ "LOCAL_USE_EMBEDDED_NATIVE_LIBS": "use_embedded_native_libs",
+ "LOCAL_USE_EMBEDDED_DEX": "use_embedded_dex",
"LOCAL_DEX_PREOPT": "dex_preopt.enabled",
"LOCAL_DEX_PREOPT_APP_IMAGE": "dex_preopt.app_image",
"LOCAL_DEX_PREOPT_GENERATE_PROFILE": "dex_preopt.profile_guided",
+
+ "LOCAL_PRIVATE_PLATFORM_APIS": "platform_apis",
+ "LOCAL_JETIFIER_ENABLED": "jetifier",
})
}
@@ -350,10 +375,82 @@
return splitAndAssign(ctx, classifyLocalOrGlobalPath, map[string]string{"global": "export_include_dirs", "local": "export_include_dirs"})
}
+func local32BitOnly(ctx variableAssignmentContext) error {
+ val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.BoolType)
+ if err != nil {
+ return err
+ }
+ if val.(*bpparser.Bool).Value {
+ thirtyTwo := &bpparser.String{
+ Value: "32",
+ }
+ setVariable(ctx.file, false, ctx.prefix, "compile_multilib", thirtyTwo, true)
+ }
+ return nil
+}
+
func localAidlIncludes(ctx variableAssignmentContext) error {
return splitAndAssign(ctx, classifyLocalOrGlobalPath, map[string]string{"global": "aidl.include_dirs", "local": "aidl.local_include_dirs"})
}
+func localizePathList(attribute string) func(ctx variableAssignmentContext) error {
+ return func(ctx variableAssignmentContext) error {
+ paths, err := localizePaths(ctx)
+ if err == nil {
+ err = setVariable(ctx.file, ctx.append, ctx.prefix, attribute, paths, true)
+ }
+ return err
+ }
+}
+
+func localizePath(attribute string) func(ctx variableAssignmentContext) error {
+ return func(ctx variableAssignmentContext) error {
+ paths, err := localizePaths(ctx)
+ if err == nil {
+ pathList, ok := paths.(*bpparser.List)
+ if !ok {
+ panic("Expected list")
+ }
+ switch len(pathList.Values) {
+ case 0:
+ err = setVariable(ctx.file, ctx.append, ctx.prefix, attribute, &bpparser.List{}, true)
+ case 1:
+ err = setVariable(ctx.file, ctx.append, ctx.prefix, attribute, pathList.Values[0], true)
+ default:
+ err = fmt.Errorf("Expected single value for %s", attribute)
+ }
+ }
+ return err
+ }
+}
+
+// Convert the "full" paths (that is, from the top of the source tree) to the relative one
+// (from the directory containing the blueprint file) and set given attribute to it.
+// This is needed for some of makefile variables (e.g., LOCAL_RESOURCE_DIR).
+// At the moment only the paths of the `$(LOCAL_PATH)/foo/bar` format can be converted
+// (to `foo/bar` in this case) as we cannot convert a literal path without
+// knowing makefiles's location in the source tree. We just issue a warning in the latter case.
+func localizePaths(ctx variableAssignmentContext) (bpparser.Expression, error) {
+ bpvalue, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
+ var result bpparser.Expression
+ if err != nil {
+ return result, err
+ }
+ classifiedPaths, err := splitBpList(bpvalue, classifyLocalOrGlobalPath)
+ if err != nil {
+ return result, err
+ }
+ for pathClass, path := range classifiedPaths {
+ switch pathClass {
+ case "local":
+ result = path
+ default:
+ err = fmt.Errorf("Only $(LOCAL_PATH)/.. values are allowed")
+ }
+ }
+ return result, err
+}
+
func stem(ctx variableAssignmentContext) error {
val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.StringType)
if err != nil {
@@ -455,10 +552,33 @@
}
}
+func strip() func(ctx variableAssignmentContext) error {
+ return func(ctx variableAssignmentContext) error {
+ val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.StringType)
+ if err != nil {
+ return err
+ }
+
+ if _, ok := val.(*bpparser.String); !ok {
+ return fmt.Errorf("unsupported strip expression")
+ }
+
+ bpTrue := &bpparser.Bool{
+ Value: true,
+ }
+ v := val.(*bpparser.String).Value
+ sub := (map[string]string{"false": "none", "true": "all", "keep_symbols": "keep_symbols"})[v]
+ if sub == "" {
+ return fmt.Errorf("unexpected strip option: %s", v)
+ }
+ return setVariable(ctx.file, false, ctx.prefix, "strip."+sub, bpTrue, true)
+ }
+}
+
func prebuiltClass(ctx variableAssignmentContext) error {
class := ctx.mkvalue.Value(ctx.file.scope)
- if v, ok := prebuiltTypes[class]; ok {
- ctx.file.scope.Set("BUILD_PREBUILT", v)
+ if _, ok := prebuiltTypes[class]; ok {
+ ctx.file.scope.Set("BUILD_PREBUILT", class)
} else {
// reset to default
ctx.file.scope.Set("BUILD_PREBUILT", "prebuilt")
@@ -466,6 +586,55 @@
return nil
}
+func makeBlueprintStringAssignment(file *bpFile, prefix string, suffix string, value string) error {
+ val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString(value, mkparser.NoPos), bpparser.StringType)
+ if err == nil {
+ err = setVariable(file, false, prefix, suffix, val, true)
+ }
+ return err
+}
+
+// If variable is a literal variable name, return the name, otherwise return ""
+func varLiteralName(variable mkparser.Variable) string {
+ if len(variable.Name.Variables) == 0 {
+ return variable.Name.Strings[0]
+ }
+ return ""
+}
+
+func prebuiltModulePath(ctx variableAssignmentContext) error {
+ // Cannot handle appending
+ if ctx.append {
+ return fmt.Errorf("Cannot handle appending to LOCAL_MODULE_PATH")
+ }
+ // Analyze value in order to set the correct values for the 'device_specific',
+ // 'product_specific', 'product_services_specific' 'vendor'/'soc_specific',
+ // 'product_services_specific' attribute. Two cases are allowed:
+ // $(VAR)/<literal-value>
+ // $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/<literal-value>
+ // The last case is equivalent to $(TARGET_OUT_VENDOR)/<literal-value>
+ // Map the variable name if present to `local_module_path_var`
+ // Map literal-path to local_module_path_fixed
+ varname := ""
+ fixed := ""
+ val := ctx.mkvalue
+ if len(val.Variables) == 1 && varLiteralName(val.Variables[0]) != "" && len(val.Strings) == 2 && val.Strings[0] == "" {
+ fixed = val.Strings[1]
+ varname = val.Variables[0].Name.Strings[0]
+ } else if len(val.Variables) == 2 && varLiteralName(val.Variables[0]) == "PRODUCT_OUT" && varLiteralName(val.Variables[1]) == "TARGET_COPY_OUT_VENDOR" &&
+ len(val.Strings) == 3 && val.Strings[0] == "" && val.Strings[1] == "/" {
+ fixed = val.Strings[2]
+ varname = "TARGET_OUT_VENDOR"
+ } else {
+ return fmt.Errorf("LOCAL_MODULE_PATH value should start with $(<some-varaible>)/ or $(PRODUCT_OUT)/$(TARGET_COPY_VENDOR)/")
+ }
+ err := makeBlueprintStringAssignment(ctx.file, "local_module_path", "var", varname)
+ if err == nil && fixed != "" {
+ err = makeBlueprintStringAssignment(ctx.file, "local_module_path", "fixed", fixed)
+ }
+ return err
+}
+
func ldflags(ctx variableAssignmentContext) error {
val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
if err != nil {
@@ -706,27 +875,31 @@
true: "product_variables.pdk"},
}
-func mydir(args []string) string {
- return "."
+func mydir(args []string) []string {
+ return []string{"."}
}
-func allFilesUnder(wildcard string) func(args []string) string {
- return func(args []string) string {
- dir := ""
+func allFilesUnder(wildcard string) func(args []string) []string {
+ return func(args []string) []string {
+ dirs := []string{""}
if len(args) > 0 {
- dir = strings.TrimSpace(args[0])
+ dirs = strings.Fields(args[0])
}
- return fmt.Sprintf("%s/**/"+wildcard, dir)
+ paths := make([]string, len(dirs))
+ for i := range paths {
+ paths[i] = fmt.Sprintf("%s/**/"+wildcard, dirs[i])
+ }
+ return paths
}
}
-func allSubdirJavaFiles(args []string) string {
- return "**/*.java"
+func allSubdirJavaFiles(args []string) []string {
+ return []string{"**/*.java"}
}
-func includeIgnored(args []string) string {
- return include_ignored
+func includeIgnored(args []string) []string {
+ return []string{include_ignored}
}
var moduleTypes = map[string]string{
@@ -742,11 +915,17 @@
"BUILD_NATIVE_BENCHMARK": "cc_benchmark",
"BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host",
- "BUILD_JAVA_LIBRARY": "java_library",
- "BUILD_STATIC_JAVA_LIBRARY": "java_library_static",
+ "BUILD_JAVA_LIBRARY": "java_library_installable", // will be rewritten to java_library by bpfix
+ "BUILD_STATIC_JAVA_LIBRARY": "java_library",
"BUILD_HOST_JAVA_LIBRARY": "java_library_host",
"BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
"BUILD_PACKAGE": "android_app",
+
+ "BUILD_CTS_EXECUTABLE": "cc_binary", // will be further massaged by bpfix depending on the output path
+ "BUILD_CTS_SUPPORT_PACKAGE": "cts_support_package", // will be rewritten to android_test by bpfix
+ "BUILD_CTS_PACKAGE": "cts_package", // will be rewritten to android_test by bpfix
+ "BUILD_CTS_TARGET_JAVA_LIBRARY": "cts_target_java_library", // will be rewritten to java_library by bpfix
+ "BUILD_CTS_HOST_JAVA_LIBRARY": "cts_host_java_library", // will be rewritten to java_library_host by bpfix
}
var prebuiltTypes = map[string]string{
@@ -754,10 +933,24 @@
"STATIC_LIBRARIES": "cc_prebuilt_library_static",
"EXECUTABLES": "cc_prebuilt_binary",
"JAVA_LIBRARIES": "java_import",
+ "ETC": "prebuilt_etc",
}
var soongModuleTypes = map[string]bool{}
+var includePathToModule = map[string]string{
+ "test/vts/tools/build/Android.host_config.mk": "vts_config",
+ // The rest will be populated dynamically in androidScope below
+}
+
+func mapIncludePath(path string) (string, bool) {
+ if path == clear_vars || path == include_ignored {
+ return path, true
+ }
+ module, ok := includePathToModule[path]
+ return module, ok
+}
+
func androidScope() mkparser.Scope {
globalScope := mkparser.NewScope(nil)
globalScope.Set("CLEAR_VARS", clear_vars)
@@ -773,12 +966,16 @@
globalScope.SetFunc("all-named-subdir-makefiles", includeIgnored)
globalScope.SetFunc("all-subdir-makefiles", includeIgnored)
- for k, v := range moduleTypes {
- globalScope.Set(k, v)
- soongModuleTypes[v] = true
+ // The scope maps each known variable to a path, and then includePathToModule maps a path
+ // to a module. We don't care what the actual path value is so long as the value in scope
+ // is mapped, so we might as well use variable name as key, too.
+ for varName, moduleName := range moduleTypes {
+ path := varName
+ globalScope.Set(varName, path)
+ includePathToModule[path] = moduleName
}
- for _, v := range prebuiltTypes {
- soongModuleTypes[v] = true
+ for varName, moduleName := range prebuiltTypes {
+ includePathToModule[varName] = moduleName
}
return globalScope
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go
index b6a973c..d2a84d1 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/cmd/androidmk/androidmk.go
@@ -169,20 +169,21 @@
handleAssignment(file, x, assignmentCond)
case *mkparser.Directive:
switch x.Name {
- case "include":
- val := x.Args.Value(file.scope)
- switch {
- case soongModuleTypes[val]:
- handleModuleConditionals(file, x, conds)
- makeModule(file, val)
- case val == clear_vars:
+ case "include", "-include":
+ module, ok := mapIncludePath(x.Args.Value(file.scope))
+ if !ok {
+ file.errorf(x, "unsupported include")
+ continue
+ }
+ switch module {
+ case clear_vars:
resetModule(file)
- case val == include_ignored:
+ case include_ignored:
// subdirs are already automatically included in Soong
continue
default:
- file.errorf(x, "unsupported include")
- continue
+ handleModuleConditionals(file, x, conds)
+ makeModule(file, module)
}
case "ifeq", "ifneq", "ifdef", "ifndef":
args := x.Args.Dump()
@@ -207,7 +208,7 @@
file.errorf(x, "missing if before else")
continue
} else if conds[len(conds)-1] == nil {
- file.errorf(x, "else from unsupported contitional")
+ file.errorf(x, "else from unsupported conditional")
continue
}
conds[len(conds)-1].eq = !conds[len(conds)-1].eq
@@ -216,7 +217,7 @@
file.errorf(x, "missing if before endif")
continue
} else if conds[len(conds)-1] == nil {
- file.errorf(x, "endif from unsupported contitional")
+ file.errorf(x, "endif from unsupported conditional")
conds = conds[:len(conds)-1]
} else {
if assignmentCond == conds[len(conds)-1] {
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 37e2427..f2dc6ff 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -112,6 +112,25 @@
}`,
},
{
+ desc: "Convert to local path",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res $(LOCAL_PATH)/res2
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/asset
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ resource_dirs: [
+ "res",
+ "res2",
+ ],
+ asset_dirs: ["asset"],
+ jarjar_rules: "jarjar-rules.txt",
+}`,
+ },
+ {
desc: "LOCAL_MODULE_STEM",
in: `
include $(CLEAR_VARS)
@@ -331,7 +350,7 @@
`,
},
{
- desc: "Keep LOCAL_MODULE_TAGS non-optional",
+ desc: "Warn for LOCAL_MODULE_TAGS non-optional",
in: `
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := debug
@@ -340,7 +359,68 @@
expected: `
cc_library_shared {
- tags: ["debug"],
+ // WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+ // force installation for -userdebug and -eng builds.
+}
+`,
+ },
+ {
+ desc: "Custom warning for LOCAL_MODULE_TAGS tests",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := debug tests
+include $(BUILD_SHARED_LIBRARY)
+`,
+
+ expected: `
+cc_library_shared {
+ // WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+ // force installation for -userdebug and -eng builds.
+ // WARNING: Module tags are not supported in Soong.
+ // To make a shared library only for tests, use the "cc_test_library" module
+ // type. If you don't use gtest, set "gtest: false".
+}
+`,
+ },
+ {
+ desc: "Ignore LOCAL_MODULE_TAGS tests for cc_test",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_NATIVE_TEST)
+`,
+
+ expected: `
+cc_test {
+}
+`,
+ },
+ {
+ desc: "Convert LOCAL_MODULE_TAGS tests to java_test",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_HOST_JAVA_LIBRARY)
+`,
+
+ expected: `
+java_test {
+}
+
+android_test {
+}
+
+java_test_host {
}
`,
},
@@ -424,9 +504,9 @@
// # b==false
// echo end
//
-// ANDROIDMK TRANSLATION ERROR: endif from unsupported contitional
+// ANDROIDMK TRANSLATION ERROR: endif from unsupported conditional
// endif
-// ANDROIDMK TRANSLATION ERROR: endif from unsupported contitional
+// ANDROIDMK TRANSLATION ERROR: endif from unsupported conditional
// endif
`,
},
@@ -451,7 +531,7 @@
LOCAL_PROGUARD_ENABLED := obfuscation optimization
# Custom
LOCAL_PROGUARD_ENABLED := custom
- include $(BUILD_JAVA_LIBRARY)
+ include $(BUILD_STATIC_JAVA_LIBRARY)
`,
expected: `
java_library {
@@ -474,11 +554,64 @@
`,
},
{
+ desc: "java library",
+ in: `
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := a.java
+ include $(BUILD_STATIC_JAVA_LIBRARY)
+
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := b.java
+ include $(BUILD_JAVA_LIBRARY)
+
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := c.java
+ LOCAL_UNINSTALLABLE_MODULE := true
+ include $(BUILD_JAVA_LIBRARY)
+
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := d.java
+ LOCAL_UNINSTALLABLE_MODULE := false
+ include $(BUILD_JAVA_LIBRARY)
+
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(call all-java-files-under, src gen)
+ include $(BUILD_STATIC_JAVA_LIBRARY)
+ `,
+ expected: `
+ java_library {
+ srcs: ["a.java"],
+ }
+
+ java_library {
+ installable: true,
+ srcs: ["b.java"],
+ }
+
+ java_library {
+ installable: false,
+ srcs: ["c.java"],
+ }
+
+ java_library {
+ installable: true,
+ srcs: ["d.java"],
+ }
+
+ java_library {
+ srcs: [
+ "src/**/*.java",
+ "gen/**/*.java",
+ ],
+ }
+ `,
+ },
+ {
desc: "errorprone options for java library",
in: `
include $(CLEAR_VARS)
LOCAL_ERROR_PRONE_FLAGS := -Xep:AsyncCallableReturnsNull:ERROR -Xep:AsyncFunctionReturnsNull:ERROR
- include $(BUILD_JAVA_LIBRARY)
+ include $(BUILD_STATIC_JAVA_LIBRARY)
`,
expected: `
java_library {
@@ -498,12 +631,14 @@
LOCAL_SRC_FILES := test.jar
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_STATIC_ANDROID_LIBRARIES :=
+ LOCAL_JETIFIER_ENABLED := true
include $(BUILD_PREBUILT)
`,
expected: `
java_import {
jars: ["test.jar"],
+ jetifier: true,
}
`,
},
@@ -528,13 +663,15 @@
in: `
include $(CLEAR_VARS)
LOCAL_SRC_FILES := test.java
- LOCAL_RESOURCE_DIR := res
+ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+ LOCAL_JACK_COVERAGE_INCLUDE_FILTER := foo.*
include $(BUILD_STATIC_JAVA_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := test.java
LOCAL_STATIC_LIBRARIES := foo
LOCAL_STATIC_ANDROID_LIBRARIES := bar
+ LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := bar.*
include $(BUILD_STATIC_JAVA_LIBRARY)
include $(CLEAR_VARS)
@@ -552,6 +689,9 @@
android_library {
srcs: ["test.java"],
resource_dirs: ["res"],
+ jacoco: {
+ include_filter: ["foo.*"],
+ },
}
android_library {
@@ -560,6 +700,9 @@
"foo",
"bar",
],
+ jacoco: {
+ exclude_filter: ["bar.*"],
+ },
}
android_library {
@@ -570,7 +713,7 @@
],
}
- java_library_static {
+ java_library {
srcs: ["test.java"],
static_libs: [],
}
@@ -589,6 +732,386 @@
}
`,
},
+ {
+ desc: "LOCAL_STRIP_MODULE",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtest
+LOCAL_STRIP_MODULE := false
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtest2
+LOCAL_STRIP_MODULE := true
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtest3
+LOCAL_STRIP_MODULE := keep_symbols
+include $(BUILD_SHARED_LIBRARY)
+`,
+ expected: `
+cc_library_shared {
+ name: "libtest",
+ strip: {
+ none: true,
+ }
+}
+
+cc_library_shared {
+ name: "libtest2",
+ strip: {
+ all: true,
+ }
+}
+
+cc_library_shared {
+ name: "libtest3",
+ strip: {
+ keep_symbols: true,
+ }
+}
+`,
+ },
+ {
+ desc: "BUILD_CTS_SUPPORT_PACKAGE",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FooTest
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+`,
+ expected: `
+android_test {
+ name: "FooTest",
+ defaults: ["cts_support_defaults"],
+ test_suites: ["cts"],
+
+}
+`,
+ },
+ {
+ desc: "BUILD_CTS_PACKAGE",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FooTest
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_CTS_TEST_PACKAGE := foo.bar
+include $(BUILD_CTS_PACKAGE)
+`,
+ expected: `
+android_test {
+ name: "FooTest",
+ defaults: ["cts_defaults"],
+ test_suites: ["cts"],
+
+}
+`,
+ },
+ {
+ desc: "BUILD_CTS_*_JAVA_LIBRARY",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foolib
+include $(BUILD_CTS_TARGET_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := foolib-host
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+`,
+ expected: `
+java_library {
+ name: "foolib",
+ defaults: ["cts_defaults"],
+}
+
+java_library_host {
+ name: "foolib-host",
+ defaults: ["cts_defaults"],
+}
+`,
+ },
+ {
+ desc: "LOCAL_ANNOTATION_PROCESSORS",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foolib
+LOCAL_ANNOTATION_PROCESSORS := bar
+LOCAL_ANNOTATION_PROCESSOR_CLASSES := com.bar
+include $(BUILD_STATIC_JAVA_LIBRARY)
+`,
+ expected: `
+java_library {
+ name: "foolib",
+ plugins: ["bar"],
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_ETC",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_SRC_FILES := mymod
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ src: "mymod",
+ sub_dir: "foo/bar",
+
+}
+`,
+ },
+
+ {
+ desc: "prebuilt_etc_PRODUCT_OUT/system/etc",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/etc/foo/bar
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+
+ src: "etc.test1",
+ sub_dir: "foo/bar",
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_ODM/etc",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ device_specific: true,
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_PRODUCT/etc",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/etc/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ product_specific: true,
+
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_PRODUCT_ETC",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ product_specific: true,
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES/etc",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES)/etc/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ product_services_specific: true,
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES_ETC",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ product_services_specific: true,
+
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_VENDOR/etc",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ proprietary: true,
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_PRODUCT_OUT/TARGET_COPY_OUT_VENDOR/etc",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/etc/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ proprietary: true,
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_OUT_VENDOR_ETC",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ proprietary: true,
+
+}
+`,
+ },
+ {
+ desc: "prebuilt_etc_TARGET_RECOVERY_ROOT_OUT/system/etc",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc.test1
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/foo/bar
+include $(BUILD_PREBUILT)
+`,
+ expected: `
+prebuilt_etc {
+ name: "etc.test1",
+ sub_dir: "foo/bar",
+ recovery: true,
+
+}
+`,
+ },
+ {
+ desc: "vts_config",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := vtsconf
+include test/vts/tools/build/Android.host_config.mk
+`,
+ expected: `
+vts_config {
+ name: "vtsconf",
+}
+`,
+ },
+ {
+ desc: "comment with ESC",
+ in: `
+# Comment line 1 \
+# Comment line 2
+`,
+ expected: `
+// Comment line 1 \
+// Comment line 2
+`,
+ },
+ {
+ desc: "Merge with variable reference",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_STATIC_ANDROID_LIBRARIES := $(FOO)
+LOCAL_STATIC_JAVA_LIBRARIES := javalib
+LOCAL_JAVA_RESOURCE_DIRS := $(FOO)
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+android_app {
+ name: "foo",
+ static_libs: FOO,
+ static_libs: ["javalib"],
+ java_resource_dirs: FOO,
+}
+`,
+ },
+ {
+ desc: "LOCAL_JACK_ENABLED and LOCAL_JACK_FLAGS skipped",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_JACK_ENABLED := incremental
+LOCAL_JACK_FLAGS := --multi-dex native
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+
+}
+ `,
+ },
}
func TestEndToEnd(t *testing.T) {
diff --git a/androidmk/cmd/androidmk/values.go b/androidmk/cmd/androidmk/values.go
index d240a01..90f2e74 100644
--- a/androidmk/cmd/androidmk/values.go
+++ b/androidmk/cmd/androidmk/values.go
@@ -29,6 +29,14 @@
}
}
+func stringListToStringValueList(list []string) []bpparser.Expression {
+ valList := make([]bpparser.Expression, len(list))
+ for i, l := range list {
+ valList[i] = stringToStringValue(l)
+ }
+ return valList
+}
+
func addValues(val1, val2 bpparser.Expression) (bpparser.Expression, error) {
if val1 == nil {
return val2, nil
@@ -63,7 +71,10 @@
for i, s := range ms.Strings[1:] {
if ret, ok := ms.Variables[i].EvalFunction(scope); ok {
- val, err = addValues(val, stringToStringValue(ret))
+ if len(ret) > 1 {
+ return nil, fmt.Errorf("Unexpected list value %s", ms.Dump())
+ }
+ val, err = addValues(val, stringToStringValue(ret[0]))
} else {
name := ms.Variables[i].Name
if !name.Const() {
@@ -125,9 +136,7 @@
for _, f := range fields {
if len(f.Variables) == 1 && f.Strings[0] == "" && f.Strings[1] == "" {
if ret, ok := f.Variables[0].EvalFunction(scope); ok {
- listValue.Values = append(listValue.Values, &bpparser.String{
- Value: ret,
- })
+ listValue.Values = append(listValue.Values, stringListToStringValueList(ret)...)
} else {
// Variable by itself, variable is probably a list
if !f.Variables[0].Name.Const() {
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index e6885a8..4b782a2 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -90,10 +90,10 @@
if len(ms.Strings) == 0 {
return ""
} else {
- ret := ms.Strings[0]
+ ret := unescape(ms.Strings[0])
for i := range ms.Strings[1:] {
ret += ms.Variables[i].Value(scope)
- ret += ms.Strings[i+1]
+ ret += unescape(ms.Strings[i+1])
}
return ret
}
@@ -125,6 +125,16 @@
}
func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
+ return ms.splitNFunc(n, func(s string, n int) []string {
+ return splitAnyN(s, sep, n)
+ })
+}
+
+func (ms *MakeString) Words() []*MakeString {
+ return ms.splitNFunc(-1, splitWords)
+}
+
+func (ms *MakeString) splitNFunc(n int, splitFunc func(s string, n int) []string) []*MakeString {
ret := []*MakeString{}
curMs := SimpleMakeString("", ms.Pos())
@@ -133,7 +143,7 @@
var s string
for i, s = range ms.Strings {
if n != 0 {
- split := splitAnyN(s, sep, n)
+ split := splitFunc(s, n)
if n != -1 {
if len(split) > n {
panic("oops!")
@@ -156,7 +166,9 @@
}
}
- ret = append(ret, curMs)
+ if !curMs.Empty() {
+ ret = append(ret, curMs)
+ }
return ret
}
@@ -206,3 +218,64 @@
ret = append(ret, s)
return ret
}
+
+func splitWords(s string, n int) []string {
+ ret := []string{}
+ preserve := ""
+ for n == -1 || n > 1 {
+ index := strings.IndexAny(s, " \t")
+ if index == 0 && len(preserve) == 0 {
+ s = s[1:]
+ } else if index >= 0 {
+ escapeCount := 0
+ for i := index - 1; i >= 0; i-- {
+ if s[i] != '\\' {
+ break
+ }
+ escapeCount += 1
+ }
+
+ if escapeCount%2 == 1 {
+ preserve += s[0 : index+1]
+ s = s[index+1:]
+ continue
+ }
+
+ ret = append(ret, preserve+s[0:index])
+ s = s[index+1:]
+ preserve = ""
+ if n > 0 {
+ n--
+ }
+ } else {
+ break
+ }
+ }
+ if preserve != "" || s != "" || len(ret) == 0 {
+ ret = append(ret, preserve+s)
+ }
+ return ret
+}
+
+func unescape(s string) string {
+ ret := ""
+ for {
+ index := strings.IndexByte(s, '\\')
+ if index < 0 {
+ break
+ }
+
+ if index+1 == len(s) {
+ break
+ }
+
+ switch s[index+1] {
+ case ' ', '\\', '#', ':', '*', '[', '|', '\t', '\n', '\r':
+ ret += s[:index] + s[index+1:index+2]
+ default:
+ ret += s[:index+2]
+ }
+ s = s[index+2:]
+ }
+ return ret + s
+}
diff --git a/androidmk/parser/make_strings_test.go b/androidmk/parser/make_strings_test.go
index 8ad3d74..6995e89 100644
--- a/androidmk/parser/make_strings_test.go
+++ b/androidmk/parser/make_strings_test.go
@@ -99,6 +99,78 @@
}
}
+var valueTestCases = []struct {
+ in *MakeString
+ expected string
+}{
+ {
+ in: SimpleMakeString("a b", NoPos),
+ expected: "a b",
+ },
+ {
+ in: SimpleMakeString("a\\ \\\tb\\\\", NoPos),
+ expected: "a \tb\\",
+ },
+ {
+ in: SimpleMakeString("a\\b\\", NoPos),
+ expected: "a\\b\\",
+ },
+}
+
+func TestMakeStringValue(t *testing.T) {
+ for _, test := range valueTestCases {
+ got := test.in.Value(nil)
+ if got != test.expected {
+ t.Errorf("\nwith: %q\nwant: %q\n got: %q", test.in.Dump(), test.expected, got)
+ }
+ }
+}
+
+var splitWordsTestCases = []struct {
+ in *MakeString
+ expected []*MakeString
+}{
+ {
+ in: SimpleMakeString("", NoPos),
+ expected: []*MakeString{},
+ },
+ {
+ in: SimpleMakeString(" a b\\ c d", NoPos),
+ expected: []*MakeString{
+ SimpleMakeString("a", NoPos),
+ SimpleMakeString("b\\ c", NoPos),
+ SimpleMakeString("d", NoPos),
+ },
+ },
+ {
+ in: SimpleMakeString(" a\tb\\\t\\ c d ", NoPos),
+ expected: []*MakeString{
+ SimpleMakeString("a", NoPos),
+ SimpleMakeString("b\\\t\\ c", NoPos),
+ SimpleMakeString("d", NoPos),
+ },
+ },
+ {
+ in: SimpleMakeString(`a\\ b\\\ c d`, NoPos),
+ expected: []*MakeString{
+ SimpleMakeString(`a\\`, NoPos),
+ SimpleMakeString(`b\\\ c`, NoPos),
+ SimpleMakeString("d", NoPos),
+ },
+ },
+}
+
+func TestMakeStringWords(t *testing.T) {
+ for _, test := range splitWordsTestCases {
+ got := test.in.Words()
+ gotString := dumpArray(got)
+ expectedString := dumpArray(test.expected)
+ if gotString != expectedString {
+ t.Errorf("with:\n%q\nexpected:\n%s\ngot:\n%s", test.in.Dump(), expectedString, gotString)
+ }
+ }
+}
+
func dumpArray(a []*MakeString) string {
ret := make([]string, len(a))
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index 89ee308..86dabf9 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -35,6 +35,10 @@
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
}
+const builtinDollar = "__builtin_dollar"
+
+var builtinDollarName = SimpleMakeString(builtinDollar, NoPos)
+
func (p *parser) Parse() ([]Node, []error) {
defer func() {
if r := recover(); r != nil {
@@ -326,7 +330,11 @@
case '$':
var variable Variable
variable = p.parseVariable()
- value.appendVariable(variable)
+ if variable.Name == builtinDollarName {
+ value.appendString("$")
+ } else {
+ value.appendVariable(variable)
+ }
case scanner.EOF:
break loop
case '(':
@@ -357,7 +365,8 @@
case '{':
return p.parseBracketedVariable('{', '}', pos)
case '$':
- name = SimpleMakeString("__builtin_dollar", NoPos)
+ name = builtinDollarName
+ p.accept(p.tok)
case scanner.EOF:
p.errorf("expected variable name, found %s",
scanner.TokenString(p.tok))
@@ -457,6 +466,8 @@
case '=':
p.parseAssignment("=", target, prerequisites)
return nil, true
+ case scanner.EOF:
+ // do nothing
default:
p.errorf("unexpected token %s after rule prerequisites", scanner.TokenString(p.tok))
}
@@ -474,10 +485,12 @@
case '\\':
p.parseEscape()
if p.tok == '\n' {
- comment += "\n"
- } else {
- comment += "\\" + p.scanner.TokenText()
+ // Special case: '\' does not "escape" newline in comment (b/127521510)
+ comment += "\\"
+ p.accept(p.tok)
+ break loop
}
+ comment += "\\" + p.scanner.TokenText()
p.accept(p.tok)
case '\n':
p.accept('\n')
diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go
new file mode 100644
index 0000000..f562c29
--- /dev/null
+++ b/androidmk/parser/parser_test.go
@@ -0,0 +1,61 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package parser
+
+import (
+ "bytes"
+ "testing"
+)
+
+var parserTestCases = []struct {
+ name string
+ in string
+ out []Node
+}{
+ {
+ name: "Escaped $",
+ in: `a$$ b: c`,
+ out: []Node{
+ &Rule{
+ Target: SimpleMakeString("a$ b", NoPos),
+ Prerequisites: SimpleMakeString("c", NoPos),
+ },
+ },
+ },
+}
+
+func TestParse(t *testing.T) {
+ for _, test := range parserTestCases {
+ t.Run(test.name, func(t *testing.T) {
+ p := NewParser(test.name, bytes.NewBufferString(test.in))
+ got, errs := p.Parse()
+
+ if len(errs) != 0 {
+ t.Fatalf("Unexpected errors while parsing: %v", errs)
+ }
+
+ if len(got) != len(test.out) {
+ t.Fatalf("length mismatch, expected %d nodes, got %d", len(test.out), len(got))
+ }
+
+ for i := range got {
+ if got[i].Dump() != test.out[i].Dump() {
+ t.Errorf("incorrect node %d:\nexpected: %#v (%s)\n got: %#v (%s)",
+ i, test.out[i], test.out[i].Dump(), got[i], got[i].Dump())
+ }
+ }
+ })
+ }
+}
diff --git a/androidmk/parser/scope.go b/androidmk/parser/scope.go
index 7a514fa..8111c89 100644
--- a/androidmk/parser/scope.go
+++ b/androidmk/parser/scope.go
@@ -21,13 +21,13 @@
type Scope interface {
Get(name string) string
Set(name, value string)
- Call(name string, args []string) string
- SetFunc(name string, f func([]string) string)
+ Call(name string, args []string) []string
+ SetFunc(name string, f func([]string) []string)
}
type scope struct {
variables map[string]string
- functions map[string]func([]string) string
+ functions map[string]func([]string) []string
parent Scope
}
@@ -47,22 +47,22 @@
s.variables[name] = value
}
-func (s *scope) Call(name string, args []string) string {
+func (s *scope) Call(name string, args []string) []string {
if f, ok := s.functions[name]; ok {
return f(args)
}
- return "<func:'" + name + "' unset>"
+ return []string{"<func:'" + name + "' unset>"}
}
-func (s *scope) SetFunc(name string, f func([]string) string) {
+func (s *scope) SetFunc(name string, f func([]string) []string) {
s.functions[name] = f
}
func NewScope(parent Scope) Scope {
return &scope{
variables: make(map[string]string),
- functions: make(map[string]func([]string) string),
+ functions: make(map[string]func([]string) []string),
parent: parent,
}
}
@@ -71,10 +71,10 @@
func init() {
builtinScope := make(map[string]string)
- builtinScope["__builtin_dollar"] = "$"
+ builtinScope[builtinDollar] = "$"
}
-func (v Variable) EvalFunction(scope Scope) (string, bool) {
+func (v Variable) EvalFunction(scope Scope) ([]string, bool) {
f := v.Name.SplitN(" \t", 2)
if len(f) > 1 && f[0].Const() {
fname := f[0].Value(nil)
@@ -88,17 +88,20 @@
if fname == "call" {
return scope.Call(argVals[0], argVals[1:]), true
} else {
- return "__builtin_func:" + fname + " " + strings.Join(argVals, " "), true
+ return []string{"__builtin_func:" + fname + " " + strings.Join(argVals, " ")}, true
}
}
}
- return "", false
+ return []string{""}, false
}
func (v Variable) Value(scope Scope) string {
if ret, ok := v.EvalFunction(scope); ok {
- return ret
+ if len(ret) > 1 {
+ panic("Expected a single value, but instead got a list")
+ }
+ return ret[0]
}
if scope == nil {
panic("Cannot take the value of a variable in a nil scope")
diff --git a/apex/OWNERS b/apex/OWNERS
new file mode 100644
index 0000000..a382ae8
--- /dev/null
+++ b/apex/OWNERS
@@ -0,0 +1 @@
+per-file * = jiyong@google.com
\ No newline at end of file
diff --git a/apex/apex.go b/apex/apex.go
new file mode 100644
index 0000000..4dca961
--- /dev/null
+++ b/apex/apex.go
@@ -0,0 +1,1586 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/java"
+ "android/soong/python"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/bootstrap"
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ pctx = android.NewPackageContext("android/apex")
+
+ // Create a canned fs config file where all files and directories are
+ // by default set to (uid/gid/mode) = (1000/1000/0644)
+ // TODO(b/113082813) make this configurable using config.fs syntax
+ generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
+ Command: `echo '/ 1000 1000 0755' > ${out} && ` +
+ `echo '/apex_manifest.json 1000 1000 0644' >> ${out} && ` +
+ `echo ${ro_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 1000 1000 0644"}' >> ${out} && ` +
+ `echo ${exec_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 0 2000 0755"}' >> ${out}`,
+ Description: "fs_config ${out}",
+ }, "ro_paths", "exec_paths")
+
+ // TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
+ // against the binary policy using sefcontext_compiler -p <policy>.
+
+ // TODO(b/114327326): automate the generation of file_contexts
+ apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{
+ Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+ `(${copy_commands}) && ` +
+ `APEXER_TOOL_PATH=${tool_path} ` +
+ `${apexer} --force --manifest ${manifest} ` +
+ `--file_contexts ${file_contexts} ` +
+ `--canned_fs_config ${canned_fs_config} ` +
+ `--payload_type image ` +
+ `--key ${key} ${opt_flags} ${image_dir} ${out} `,
+ CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
+ "${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
+ "${soong_zip}", "${zipalign}", "${aapt2}"},
+ Description: "APEX ${image_dir} => ${out}",
+ }, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key", "opt_flags")
+
+ zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
+ Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+ `(${copy_commands}) && ` +
+ `APEXER_TOOL_PATH=${tool_path} ` +
+ `${apexer} --force --manifest ${manifest} ` +
+ `--payload_type zip ` +
+ `${image_dir} ${out} `,
+ CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
+ Description: "ZipAPEX ${image_dir} => ${out}",
+ }, "tool_path", "image_dir", "copy_commands", "manifest")
+
+ apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
+ blueprint.RuleParams{
+ Command: `${aapt2} convert --output-format proto $in -o $out`,
+ CommandDeps: []string{"${aapt2}"},
+ })
+
+ apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{
+ Command: `${zip2zip} -i $in -o $out ` +
+ `apex_payload.img:apex/${abi}.img ` +
+ `apex_manifest.json:root/apex_manifest.json ` +
+ `AndroidManifest.xml:manifest/AndroidManifest.xml ` +
+ `assets/NOTICE.html.gz:assets/NOTICE.html.gz`,
+ CommandDeps: []string{"${zip2zip}"},
+ Description: "app bundle",
+ }, "abi")
+
+ extractMatchingApex = pctx.StaticRule(
+ "extractMatchingApex",
+ blueprint.RuleParams{
+ Command: `rm -rf "$out" && ` +
+ `${extract_apks} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
+ `-sdk-version=${sdk-version} -abis=${abis} -screen-densities=all -extract-single ` +
+ `${in}`,
+ CommandDeps: []string{"${extract_apks}"},
+ },
+ "abis", "allow-prereleased", "sdk-version")
+)
+
+var imageApexSuffix = ".apex"
+var zipApexSuffix = ".zipapex"
+
+var imageApexType = "image"
+var zipApexType = "zip"
+
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+var (
+ sharedLibTag = dependencyTag{name: "sharedLib"}
+ executableTag = dependencyTag{name: "executable"}
+ javaLibTag = dependencyTag{name: "javaLib"}
+ prebuiltTag = dependencyTag{name: "prebuilt"}
+ keyTag = dependencyTag{name: "key"}
+ certificateTag = dependencyTag{name: "certificate"}
+)
+
+func init() {
+ pctx.Import("android/soong/android")
+ pctx.Import("android/soong/java")
+ pctx.HostBinToolVariable("apexer", "apexer")
+ // ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
+ // projects, and hence cannot built 'aapt2'. Use the SDK prebuilt instead.
+ hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
+ pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
+ if !ctx.Config().FrameworksBaseDirExists(ctx) {
+ return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool)
+ } else {
+ return pctx.HostBinToolPath(ctx, tool).String()
+ }
+ })
+ }
+ hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2")
+ pctx.HostBinToolVariable("avbtool", "avbtool")
+ pctx.HostBinToolVariable("e2fsdroid", "e2fsdroid")
+ pctx.HostBinToolVariable("merge_zips", "merge_zips")
+ pctx.HostBinToolVariable("mke2fs", "mke2fs")
+ pctx.HostBinToolVariable("resize2fs", "resize2fs")
+ pctx.HostBinToolVariable("sefcontext_compile", "sefcontext_compile")
+ pctx.HostBinToolVariable("soong_zip", "soong_zip")
+ pctx.HostBinToolVariable("zip2zip", "zip2zip")
+ pctx.HostBinToolVariable("zipalign", "zipalign")
+ pctx.HostBinToolVariable("extract_apks", "extract_apks")
+
+ android.RegisterModuleType("apex", apexBundleFactory)
+ android.RegisterModuleType("apex_test", testApexBundleFactory)
+ android.RegisterModuleType("apex_defaults", defaultsFactory)
+ android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
+ android.RegisterModuleType("apex_set", apexSetFactory)
+
+ android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("apex_deps", apexDepsMutator)
+ ctx.BottomUp("apex", apexMutator)
+ })
+}
+
+// Mark the direct and transitive dependencies of apex bundles so that they
+// can be built for the apex bundles.
+func apexDepsMutator(mctx android.TopDownMutatorContext) {
+ if a, ok := mctx.Module().(*apexBundle); ok {
+ apexBundleName := mctx.ModuleName()
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ depName := mctx.OtherModuleName(child)
+ // If the parent is apexBundle, this child is directly depended.
+ _, directDep := parent.(*apexBundle)
+ if a.installable() && !a.testApex {
+ // TODO(b/123892969): Workaround for not having any way to annotate test-apexs
+ // non-installable apex's cannot be installed and so should not prevent libraries from being
+ // installed to the system.
+ android.UpdateApexDependency(apexBundleName, depName, directDep)
+ }
+
+ if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() {
+ am.BuildForApex(apexBundleName)
+ return true
+ } else {
+ return false
+ }
+ })
+ }
+}
+
+// Create apex variations if a module is included in APEX(s).
+func apexMutator(mctx android.BottomUpMutatorContext) {
+ if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
+ am.CreateApexVariations(mctx)
+ } else if _, ok := mctx.Module().(*apexBundle); ok {
+ // apex bundle itself is mutated so that it and its modules have same
+ // apex variant.
+ apexBundleName := mctx.ModuleName()
+ mctx.CreateVariations(apexBundleName)
+ }
+}
+
+type apexNativeDependencies struct {
+ // List of native libraries
+ Native_shared_libs []string
+ // List of native executables
+ Binaries []string
+}
+type apexMultilibProperties struct {
+ // Native dependencies whose compile_multilib is "first"
+ First apexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "both"
+ Both apexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "prefer32"
+ Prefer32 apexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "32"
+ Lib32 apexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "64"
+ Lib64 apexNativeDependencies
+}
+
+type apexBundleProperties struct {
+ // Json manifest file describing meta info of this APEX bundle. Default:
+ // "apex_manifest.json"
+ Manifest *string `android:"path"`
+
+ // AndroidManifest.xml file used for the zip container of this APEX bundle.
+ // If unspecified, a default one is automatically generated.
+ AndroidManifest *string `android:"path"`
+
+ // Canonical name of the APEX bundle in the manifest file.
+ // If unspecified, defaults to the value of name
+ Apex_name *string
+
+ // Determines the file contexts file for setting security context to each file in this APEX bundle.
+ // Specifically, when this is set to <value>, /system/sepolicy/apex/<value>_file_contexts file is
+ // used.
+ // Default: <name_of_this_module>
+ File_contexts *string
+
+ // List of native shared libs that are embedded inside this APEX bundle
+ Native_shared_libs []string
+
+ // List of native executables that are embedded inside this APEX bundle
+ Binaries []string
+
+ // List of java libraries that are embedded inside this APEX bundle
+ Java_libs []string
+
+ // List of prebuilt files that are embedded inside this APEX bundle
+ Prebuilts []string
+
+ // Name of the apex_key module that provides the private key to sign APEX
+ Key *string
+
+ // The type of APEX to build. Controls what the APEX payload is. Either
+ // 'image', 'zip' or 'both'. Default: 'image'.
+ Payload_type *string
+
+ // The name of a certificate in the default certificate directory, blank to use the default product certificate,
+ // or an android_app_certificate module name in the form ":module".
+ Certificate *string
+
+ // Whether this APEX is installable to one of the partitions. Default: true.
+ Installable *bool
+
+ // For native libraries and binaries, use the vendor variant instead of the core (platform) variant.
+ // Default is false.
+ Use_vendor *bool
+
+ // For telling the apex to ignore special handling for system libraries such as bionic. Default is false.
+ Ignore_system_library_special_case *bool
+
+ Multilib apexMultilibProperties
+
+ // List of sanitizer names that this APEX is enabled for
+ SanitizerNames []string `blueprint:"mutated"`
+
+ PreventInstall bool `blueprint:"mutated"`
+
+ HideFromMake bool `blueprint:"mutated"`
+}
+
+type apexTargetBundleProperties struct {
+ Target struct {
+ // Multilib properties only for android.
+ Android struct {
+ Multilib apexMultilibProperties
+ }
+ // Multilib properties only for host.
+ Host struct {
+ Multilib apexMultilibProperties
+ }
+ // Multilib properties only for host linux_bionic.
+ Linux_bionic struct {
+ Multilib apexMultilibProperties
+ }
+ // Multilib properties only for host linux_glibc.
+ Linux_glibc struct {
+ Multilib apexMultilibProperties
+ }
+ }
+}
+
+type apexFileClass int
+
+const (
+ etc apexFileClass = iota
+ nativeSharedLib
+ nativeExecutable
+ shBinary
+ pyBinary
+ goBinary
+ javaSharedLib
+)
+
+type apexPackaging int
+
+const (
+ imageApex apexPackaging = iota
+ zipApex
+ both
+)
+
+func (a apexPackaging) image() bool {
+ switch a {
+ case imageApex, both:
+ return true
+ }
+ return false
+}
+
+func (a apexPackaging) zip() bool {
+ switch a {
+ case zipApex, both:
+ return true
+ }
+ return false
+}
+
+func (a apexPackaging) suffix() string {
+ switch a {
+ case imageApex:
+ return imageApexSuffix
+ case zipApex:
+ return zipApexSuffix
+ case both:
+ panic(fmt.Errorf("must be either zip or image"))
+ default:
+ panic(fmt.Errorf("unkonwn APEX type %d", a))
+ }
+}
+
+func (a apexPackaging) name() string {
+ switch a {
+ case imageApex:
+ return imageApexType
+ case zipApex:
+ return zipApexType
+ case both:
+ panic(fmt.Errorf("must be either zip or image"))
+ default:
+ panic(fmt.Errorf("unkonwn APEX type %d", a))
+ }
+}
+
+func (class apexFileClass) NameInMake() string {
+ switch class {
+ case etc:
+ return "ETC"
+ case nativeSharedLib:
+ return "SHARED_LIBRARIES"
+ case nativeExecutable, shBinary, pyBinary, goBinary:
+ return "EXECUTABLES"
+ case javaSharedLib:
+ return "JAVA_LIBRARIES"
+ default:
+ panic(fmt.Errorf("unkonwn class %d", class))
+ }
+}
+
+type apexFile struct {
+ builtFile android.Path
+ moduleName string
+ installDir string
+ class apexFileClass
+ module android.Module
+ symlinks []string
+}
+
+type apexBundle struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ properties apexBundleProperties
+ targetProperties apexTargetBundleProperties
+
+ apexTypes apexPackaging
+
+ bundleModuleFile android.WritablePath
+ outputFiles map[apexPackaging]android.WritablePath
+ installDir android.OutputPath
+
+ public_key_file android.Path
+ private_key_file android.Path
+
+ container_certificate_file android.Path
+ container_private_key_file android.Path
+
+ // list of files to be included in this apex
+ filesInfo []apexFile
+
+ // list of module names that this APEX is depending on
+ externalDeps []string
+
+ flattened bool
+
+ testApex bool
+}
+
+func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
+ native_shared_libs []string, binaries []string, arch string, imageVariation string) {
+ // Use *FarVariation* to be able to depend on modules having
+ // conflicting variations with this module. This is required since
+ // arch variant of an APEX bundle is 'common' but it is 'arm' or 'arm64'
+ // for native shared libs.
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: arch},
+ {Mutator: "image", Variation: imageVariation},
+ {Mutator: "link", Variation: "shared"},
+ {Mutator: "version", Variation: ""}, // "" is the non-stub variant
+ }, sharedLibTag, native_shared_libs...)
+
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: arch},
+ {Mutator: "image", Variation: imageVariation},
+ }, executableTag, binaries...)
+}
+
+func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
+ if ctx.Os().Class == android.Device {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Android.Multilib, nil)
+ } else {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Host.Multilib, nil)
+ if ctx.Os().Bionic() {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_bionic.Multilib, nil)
+ } else {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_glibc.Multilib, nil)
+ }
+ }
+}
+
+func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
+
+ targets := ctx.MultiTargets()
+ config := ctx.DeviceConfig()
+
+ a.combineProperties(ctx)
+
+ has32BitTarget := false
+ for _, target := range targets {
+ if target.Arch.ArchType.Multilib == "lib32" {
+ has32BitTarget = true
+ }
+ }
+ for i, target := range targets {
+ // When multilib.* is omitted for native_shared_libs, it implies
+ // multilib.both.
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: target.String()},
+ {Mutator: "image", Variation: a.getImageVariation(config)},
+ {Mutator: "link", Variation: "shared"},
+ }, sharedLibTag, a.properties.Native_shared_libs...)
+
+ // Add native modules targetting both ABIs
+ addDependenciesForNativeModules(ctx,
+ a.properties.Multilib.Both.Native_shared_libs,
+ a.properties.Multilib.Both.Binaries, target.String(),
+ a.getImageVariation(config))
+
+ isPrimaryAbi := i == 0
+ if isPrimaryAbi {
+ // When multilib.* is omitted for binaries, it implies
+ // multilib.first.
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: target.String()},
+ {Mutator: "image", Variation: a.getImageVariation(config)},
+ }, executableTag, a.properties.Binaries...)
+
+ // Add native modules targetting the first ABI
+ addDependenciesForNativeModules(ctx,
+ a.properties.Multilib.First.Native_shared_libs,
+ a.properties.Multilib.First.Binaries, target.String(),
+ a.getImageVariation(config))
+
+ // When multilib.* is omitted for prebuilts, it implies multilib.first.
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: target.String()},
+ }, prebuiltTag, a.properties.Prebuilts...)
+ }
+
+ switch target.Arch.ArchType.Multilib {
+ case "lib32":
+ // Add native modules targetting 32-bit ABI
+ addDependenciesForNativeModules(ctx,
+ a.properties.Multilib.Lib32.Native_shared_libs,
+ a.properties.Multilib.Lib32.Binaries, target.String(),
+ a.getImageVariation(config))
+
+ addDependenciesForNativeModules(ctx,
+ a.properties.Multilib.Prefer32.Native_shared_libs,
+ a.properties.Multilib.Prefer32.Binaries, target.String(),
+ a.getImageVariation(config))
+ case "lib64":
+ // Add native modules targetting 64-bit ABI
+ addDependenciesForNativeModules(ctx,
+ a.properties.Multilib.Lib64.Native_shared_libs,
+ a.properties.Multilib.Lib64.Binaries, target.String(),
+ a.getImageVariation(config))
+
+ if !has32BitTarget {
+ addDependenciesForNativeModules(ctx,
+ a.properties.Multilib.Prefer32.Native_shared_libs,
+ a.properties.Multilib.Prefer32.Binaries, target.String(),
+ a.getImageVariation(config))
+ }
+ }
+
+ }
+
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: "android_common"},
+ }, javaLibTag, a.properties.Java_libs...)
+
+ if String(a.properties.Key) == "" {
+ ctx.ModuleErrorf("key is missing")
+ return
+ }
+ ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
+
+ cert := android.SrcIsModule(a.getCertString(ctx))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+}
+
+func (a *apexBundle) getCertString(ctx android.BaseContext) string {
+ certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
+ if overridden {
+ return ":" + certificate
+ }
+ return String(a.properties.Certificate)
+}
+
+func (a *apexBundle) Srcs() android.Paths {
+ if file, ok := a.outputFiles[imageApex]; ok {
+ return android.Paths{file}
+ } else {
+ return nil
+ }
+}
+
+func (a *apexBundle) installable() bool {
+ return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
+}
+
+func (a *apexBundle) getImageVariation(config android.DeviceConfig) string {
+ if config.VndkVersion() != "" && proptools.Bool(a.properties.Use_vendor) {
+ return "vendor"
+ } else {
+ return "core"
+ }
+}
+
+func (a *apexBundle) EnableSanitizer(sanitizerName string) {
+ if !android.InList(sanitizerName, a.properties.SanitizerNames) {
+ a.properties.SanitizerNames = append(a.properties.SanitizerNames, sanitizerName)
+ }
+}
+
+func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
+ if android.InList(sanitizerName, a.properties.SanitizerNames) {
+ return true
+ }
+
+ // Then follow the global setting
+ globalSanitizerNames := []string{}
+ if a.Host() {
+ globalSanitizerNames = ctx.Config().SanitizeHost()
+ } else {
+ arches := ctx.Config().SanitizeDeviceArch()
+ if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) {
+ globalSanitizerNames = ctx.Config().SanitizeDevice()
+ }
+ }
+ return android.InList(sanitizerName, globalSanitizerNames)
+}
+
+func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseContext) bool {
+ return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+}
+
+func (a *apexBundle) PreventInstall() {
+ a.properties.PreventInstall = true
+}
+
+func (a *apexBundle) HideFromMake() {
+ a.properties.HideFromMake = true
+}
+
+func getCopyManifestForNativeLibrary(cc *cc.Module, handleSpecialLibs bool) (fileToCopy android.Path, dirInApex string) {
+ // Decide the APEX-local directory by the multilib of the library
+ // In the future, we may query this to the module.
+ switch cc.Arch().ArchType.Multilib {
+ case "lib32":
+ dirInApex = "lib"
+ case "lib64":
+ dirInApex = "lib64"
+ }
+ dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
+ if !cc.Arch().Native {
+ dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
+ }
+ if handleSpecialLibs {
+ switch cc.Name() {
+ case "libc", "libm", "libdl":
+ // Special case for bionic libs. This is to prevent the bionic libs
+ // from being included in the search path /apex/com.android.apex/lib.
+ // This exclusion is required because bionic libs in the runtime APEX
+ // are available via the legacy paths /system/lib/libc.so, etc. By the
+ // init process, the bionic libs in the APEX are bind-mounted to the
+ // legacy paths and thus will be loaded into the default linker namespace.
+ // If the bionic libs are directly in /apex/com.android.apex/lib then
+ // the same libs will be again loaded to the runtime linker namespace,
+ // which will result double loading of bionic libs that isn't supported.
+ dirInApex = filepath.Join(dirInApex, "bionic")
+ }
+ }
+
+ fileToCopy = cc.OutputFile().Path()
+ return
+}
+
+func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
+ fileToCopy = cc.OutputFile().Path()
+ return
+}
+
+func getCopyManifestForPyBinary(py *python.Module) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = "bin"
+ fileToCopy = py.HostToolPath().Path()
+ return
+}
+func getCopyManifestForGoBinary(ctx android.ModuleContext, gb bootstrap.GoBinaryTool) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = "bin"
+ s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
+ if err != nil {
+ ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
+ return
+ }
+ fileToCopy = android.PathForOutput(ctx, s)
+ return
+}
+
+func getCopyManifestForShBinary(sh *android.ShBinary) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = filepath.Join("bin", sh.SubDir())
+ fileToCopy = sh.OutputFile()
+ return
+}
+
+func getCopyManifestForJavaLibrary(java *java.Library) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = "javalib"
+ fileToCopy = java.DexJarFile()
+ return
+}
+
+func getCopyManifestForPrebuiltEtc(prebuilt *android.PrebuiltEtc) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = filepath.Join("etc", prebuilt.SubDir())
+ fileToCopy = prebuilt.OutputFile()
+ return
+}
+
+func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ filesInfo := []apexFile{}
+
+ if a.properties.Payload_type == nil || *a.properties.Payload_type == "image" {
+ a.apexTypes = imageApex
+ } else if *a.properties.Payload_type == "zip" {
+ a.apexTypes = zipApex
+ } else if *a.properties.Payload_type == "both" {
+ a.apexTypes = both
+ } else {
+ ctx.PropertyErrorf("type", "%q is not one of \"image\", \"zip\", or \"both\".", *a.properties.Payload_type)
+ return
+ }
+
+ handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
+
+ ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
+ if _, ok := parent.(*apexBundle); ok {
+ // direct dependencies
+ depTag := ctx.OtherModuleDependencyTag(child)
+ depName := ctx.OtherModuleName(child)
+ switch depTag {
+ case sharedLibTag:
+ if cc, ok := child.(*cc.Module); ok {
+ fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
+ return true
+ } else {
+ ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName)
+ }
+ case executableTag:
+ if cc, ok := child.(*cc.Module); ok {
+ if !cc.Arch().Native {
+ // There is only one 'bin' directory so we shouldn't bother copying in
+ // native-bridge'd binaries and only use main ones.
+ return true
+ }
+ fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeExecutable, cc, cc.Symlinks()})
+ return true
+ } else if sh, ok := child.(*android.ShBinary); ok {
+ fileToCopy, dirInApex := getCopyManifestForShBinary(sh)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, shBinary, sh, nil})
+ } else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
+ fileToCopy, dirInApex := getCopyManifestForPyBinary(py)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, pyBinary, py, nil})
+ } else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
+ fileToCopy, dirInApex := getCopyManifestForGoBinary(ctx, gb)
+ // NB: Since go binaries are static we don't need the module for anything here, which is
+ // good since the go tool is a blueprint.Module not an android.Module like we would
+ // normally use.
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, goBinary, nil, nil})
+ } else {
+ ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
+ }
+ case javaLibTag:
+ if java, ok := child.(*java.Library); ok {
+ fileToCopy, dirInApex := getCopyManifestForJavaLibrary(java)
+ if fileToCopy == nil {
+ ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+ } else {
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, java, nil})
+ }
+ return true
+ } else {
+ ctx.PropertyErrorf("java_libs", "%q is not a java_library module", depName)
+ }
+ case prebuiltTag:
+ if prebuilt, ok := child.(*android.PrebuiltEtc); ok {
+ fileToCopy, dirInApex := getCopyManifestForPrebuiltEtc(prebuilt)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, etc, prebuilt, nil})
+ return true
+ } else {
+ ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
+ }
+ case keyTag:
+ if key, ok := child.(*apexKey); ok {
+ a.private_key_file = key.private_key_file
+ a.public_key_file = key.public_key_file
+ return false
+ } else {
+ ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
+ }
+ case certificateTag:
+ if dep, ok := child.(*java.AndroidAppCertificate); ok {
+ a.container_certificate_file = dep.Certificate.Pem
+ a.container_private_key_file = dep.Certificate.Key
+ return false
+ } else {
+ ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
+ }
+ }
+ } else {
+ // indirect dependencies
+ if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
+ if cc, ok := child.(*cc.Module); ok {
+ if !a.Host() && (cc.IsStubs() || cc.HasStubsVariants()) {
+ // If the dependency is a stubs lib, don't include it in this APEX,
+ // but make sure that the lib is installed on the device.
+ // In case no APEX is having the lib, the lib is installed to the system
+ // partition.
+ //
+ // Always include if we are a host-apex however since those won't have any
+ // system libraries.
+ if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.externalDeps) {
+ a.externalDeps = append(a.externalDeps, cc.Name())
+ }
+ // Don't track further
+ return false
+ }
+ depName := ctx.OtherModuleName(child)
+ fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
+ filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
+ return true
+ }
+ }
+ }
+ return false
+ })
+
+ a.flattened = ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
+ if a.private_key_file == nil {
+ ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
+ return
+ }
+
+ // remove duplicates in filesInfo
+ removeDup := func(filesInfo []apexFile) []apexFile {
+ encountered := make(map[android.Path]bool)
+ result := []apexFile{}
+ for _, f := range filesInfo {
+ if !encountered[f.builtFile] {
+ encountered[f.builtFile] = true
+ result = append(result, f)
+ }
+ }
+ return result
+ }
+ filesInfo = removeDup(filesInfo)
+
+ // to have consistent build rules
+ sort.Slice(filesInfo, func(i, j int) bool {
+ return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
+ })
+
+ // prepend the name of this APEX to the module names. These names will be the names of
+ // modules that will be defined if the APEX is flattened.
+ for i := range filesInfo {
+ filesInfo[i].moduleName = ctx.ModuleName() + "." + filesInfo[i].moduleName
+ }
+
+ a.installDir = android.PathForModuleInstall(ctx, "apex")
+ a.filesInfo = filesInfo
+
+ if a.apexTypes.zip() {
+ a.buildUnflattenedApex(ctx, zipApex)
+ }
+ if a.apexTypes.image() {
+ // Build rule for unflattened APEX is created even when ctx.Config().FlattenApex()
+ // is true. This is to support referencing APEX via ":<module_name" syntax
+ // in other modules. It is in AndroidMk where the selection of flattened
+ // or unflattened APEX is made.
+ a.buildUnflattenedApex(ctx, imageApex)
+ a.buildFlattenedApex(ctx)
+ }
+}
+
+func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
+ noticeFiles := []android.Path{}
+ for _, f := range a.filesInfo {
+ if f.module != nil {
+ notice := f.module.NoticeFile()
+ if notice.Valid() {
+ noticeFiles = append(noticeFiles, notice.Path())
+ }
+ }
+ }
+ // append the notice file specified in the apex module itself
+ if a.NoticeFile().Valid() {
+ noticeFiles = append(noticeFiles, a.NoticeFile().Path())
+ }
+
+ if len(noticeFiles) == 0 {
+ return android.OptionalPath{}
+ }
+
+ return android.OptionalPathForPath(
+ android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)))
+}
+
+func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType apexPackaging) {
+ cert := String(a.properties.Certificate)
+ if cert != "" && android.SrcIsModule(cert) == "" {
+ defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
+ a.container_certificate_file = defaultDir.Join(ctx, cert+".x509.pem")
+ a.container_private_key_file = defaultDir.Join(ctx, cert+".pk8")
+ } else if cert == "" {
+ pem, key := ctx.Config().DefaultAppCertificate(ctx)
+ a.container_certificate_file = pem
+ a.container_private_key_file = key
+ }
+
+ manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
+
+ var abis []string
+ for _, target := range ctx.MultiTargets() {
+ if len(target.Arch.Abi) > 0 {
+ abis = append(abis, target.Arch.Abi[0])
+ }
+ }
+
+ abis = android.FirstUniqueStrings(abis)
+
+ suffix := apexType.suffix()
+ unsignedOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+suffix+".unsigned")
+
+ filesToCopy := []android.Path{}
+ for _, f := range a.filesInfo {
+ filesToCopy = append(filesToCopy, f.builtFile)
+ }
+
+ copyCommands := []string{}
+ for i, src := range filesToCopy {
+ dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
+ dest_path := filepath.Join(android.PathForModuleOut(ctx, "image"+suffix).String(), dest)
+ copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
+ copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
+ for _, sym := range a.filesInfo[i].symlinks {
+ symlinkDest := filepath.Join(filepath.Dir(dest_path), sym)
+ copyCommands = append(copyCommands, "ln -s "+filepath.Base(dest)+" "+symlinkDest)
+ }
+ }
+ implicitInputs := append(android.Paths(nil), filesToCopy...)
+ implicitInputs = append(implicitInputs, manifest)
+
+ outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
+ prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
+
+ if apexType.image() {
+ // files and dirs that will be created in APEX
+ var readOnlyPaths []string
+ var executablePaths []string // this also includes dirs
+ for _, f := range a.filesInfo {
+ pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
+ if f.installDir == "bin" {
+ executablePaths = append(executablePaths, pathInApex)
+ for _, s := range f.symlinks {
+ executablePaths = append(executablePaths, filepath.Join("bin", s))
+ }
+ } else {
+ readOnlyPaths = append(readOnlyPaths, pathInApex)
+ }
+ dir := f.installDir
+ for !android.InList(dir, executablePaths) && dir != "" {
+ executablePaths = append(executablePaths, dir)
+ dir, _ = filepath.Split(dir) // move up to the parent
+ if len(dir) > 0 {
+ // remove trailing slash
+ dir = dir[:len(dir)-1]
+ }
+ }
+ }
+ sort.Strings(readOnlyPaths)
+ sort.Strings(executablePaths)
+ cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: generateFsConfig,
+ Output: cannedFsConfig,
+ Description: "generate fs config",
+ Args: map[string]string{
+ "ro_paths": strings.Join(readOnlyPaths, " "),
+ "exec_paths": strings.Join(executablePaths, " "),
+ },
+ })
+
+ fcName := proptools.StringDefault(a.properties.File_contexts, ctx.ModuleName())
+ fileContextsPath := "system/sepolicy/apex/" + fcName + "-file_contexts"
+ fileContextsOptionalPath := android.ExistentPathForSource(ctx, fileContextsPath)
+ if !fileContextsOptionalPath.Valid() {
+ ctx.ModuleErrorf("Cannot find file_contexts file: %q", fileContextsPath)
+ return
+ }
+ fileContexts := fileContextsOptionalPath.Path()
+
+ optFlags := []string{}
+
+ // Additional implicit inputs.
+ implicitInputs = append(implicitInputs, cannedFsConfig, fileContexts, a.private_key_file, a.public_key_file)
+ optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
+
+ manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
+ if overridden {
+ optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName)
+ }
+
+ if a.properties.AndroidManifest != nil {
+ androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
+ implicitInputs = append(implicitInputs, androidManifestFile)
+ optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
+ }
+
+ targetSdkVersion := ctx.Config().DefaultAppTargetSdk()
+ if targetSdkVersion == ctx.Config().PlatformSdkCodename() &&
+ ctx.Config().UnbundledBuild() &&
+ !ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
+ ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
+ apiFingerprint := java.ApiFingerprintPath(ctx)
+ targetSdkVersion += fmt.Sprintf(".$$(cat %s)", apiFingerprint.String())
+ implicitInputs = append(implicitInputs, apiFingerprint)
+ }
+ optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
+
+ noticeFile := a.buildNoticeFile(ctx, ctx.ModuleName()+suffix)
+ if noticeFile.Valid() {
+ // If there's a NOTICE file, embed it as an asset file in the APEX.
+ implicitInputs = append(implicitInputs, noticeFile.Path())
+ optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apexRule,
+ Implicits: implicitInputs,
+ Output: unsignedOutputFile,
+ Description: "apex (" + apexType.name() + ")",
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": manifest.String(),
+ "file_contexts": fileContexts.String(),
+ "canned_fs_config": cannedFsConfig.String(),
+ "key": a.private_key_file.String(),
+ "opt_flags": strings.Join(optFlags, " "),
+ },
+ })
+
+ apexProtoFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".pb"+suffix)
+ bundleModuleFile := android.PathForModuleOut(ctx, ctx.ModuleName()+suffix+"-base.zip")
+ a.bundleModuleFile = bundleModuleFile
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apexProtoConvertRule,
+ Input: unsignedOutputFile,
+ Output: apexProtoFile,
+ Description: "apex proto convert",
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apexBundleRule,
+ Input: apexProtoFile,
+ Output: a.bundleModuleFile,
+ Description: "apex bundle module",
+ Args: map[string]string{
+ "abi": strings.Join(abis, "."),
+ },
+ })
+ } else {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zipApexRule,
+ Implicits: implicitInputs,
+ Output: unsignedOutputFile,
+ Description: "apex (" + apexType.name() + ")",
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": manifest.String(),
+ },
+ })
+ }
+
+ a.outputFiles[apexType] = android.PathForModuleOut(ctx, ctx.ModuleName()+suffix)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: java.Signapk,
+ Description: "signapk",
+ Output: a.outputFiles[apexType],
+ Input: unsignedOutputFile,
+ Args: map[string]string{
+ "certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
+ "flags": "-a 4096", //alignment
+ },
+ })
+
+ // Install to $OUT/soong/{target,host}/.../apex
+ if a.installable() && (!ctx.Config().FlattenApex() || apexType.zip()) {
+ ctx.InstallFile(a.installDir, ctx.ModuleName()+suffix, a.outputFiles[apexType])
+ }
+}
+
+func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
+ if a.installable() {
+ // For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
+ // with other ordinary files.
+ manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
+
+ // rename to apex_manifest.json
+ copiedManifest := android.PathForModuleOut(ctx, "apex_manifest.json")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: manifest,
+ Output: copiedManifest,
+ })
+ a.filesInfo = append(a.filesInfo, apexFile{copiedManifest, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
+
+ // rename to apex_pubkey
+ copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: a.public_key_file,
+ Output: copiedPubkey,
+ })
+ a.filesInfo = append(a.filesInfo, apexFile{copiedPubkey, ctx.ModuleName() + ".apex_pubkey", ".", etc, nil, nil})
+
+ if ctx.Config().FlattenApex() {
+ for _, fi := range a.filesInfo {
+ dir := filepath.Join("apex", ctx.ModuleName(), fi.installDir)
+ target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
+ for _, sym := range fi.symlinks {
+ ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
+ }
+ }
+ }
+ }
+}
+
+func (a *apexBundle) AndroidMk() android.AndroidMkData {
+ if a.properties.HideFromMake {
+ return android.AndroidMkData{
+ Disabled: true,
+ }
+ }
+ writers := []android.AndroidMkData{}
+ if a.apexTypes.image() {
+ writers = append(writers, a.androidMkForType(imageApex))
+ }
+ if a.apexTypes.zip() {
+ writers = append(writers, a.androidMkForType(zipApex))
+ }
+ return android.AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ for _, data := range writers {
+ data.Custom(w, name, prefix, moduleDir, data)
+ }
+ }}
+}
+
+func (a *apexBundle) androidMkForFiles(w io.Writer, name, moduleDir string, apexType apexPackaging) []string {
+ moduleNames := []string{}
+
+ for _, fi := range a.filesInfo {
+ if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
+ continue
+ }
+ if !android.InList(fi.moduleName, moduleNames) {
+ moduleNames = append(moduleNames, fi.moduleName)
+ }
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+ fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
+ // /apex/<name>/{lib|framework|...}
+ pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex",
+ proptools.StringDefault(a.properties.Apex_name, name), fi.installDir)
+ if a.flattened && apexType.image() {
+ // /system/apex/<name>/{lib|framework|...}
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)",
+ a.installDir.RelPathString(), name, fi.installDir))
+ fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
+ if len(fi.symlinks) > 0 {
+ fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
+ }
+
+ if fi.module != nil && fi.module.NoticeFile().Valid() {
+ fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", fi.module.NoticeFile().Path().String())
+ }
+ } else {
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
+ }
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
+ if fi.module != nil {
+ archStr := fi.module.Target().Arch.ArchType.String()
+ host := false
+ switch fi.module.Target().Os.Class {
+ case android.Host:
+ if archStr != "common" {
+ fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
+ }
+ host = true
+ case android.HostCross:
+ if archStr != "common" {
+ fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+ }
+ host = true
+ case android.Device:
+ if archStr != "common" {
+ fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
+ }
+ }
+ if host {
+ makeOs := fi.module.Target().Os.String()
+ if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic {
+ makeOs = "linux"
+ }
+ fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
+ fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+ }
+ }
+ if fi.class == javaSharedLib {
+ javaModule := fi.module.(*java.Library)
+ // soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
+ // we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
+ // we will have foo.jar.jar
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.builtFile.Base(), ".jar"))
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", javaModule.ImplementationAndResourcesJars()[0].String())
+ fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", javaModule.HeaderJars()[0].String())
+ fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", fi.builtFile.String())
+ fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
+ fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
+ } else if fi.class == nativeSharedLib || fi.class == nativeExecutable {
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
+ if cc, ok := fi.module.(*cc.Module); ok {
+ if cc.UnstrippedOutputFile() != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", cc.UnstrippedOutputFile().String())
+ }
+ if cc.CoverageOutputFile().Valid() {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", cc.CoverageOutputFile().String())
+ }
+ }
+ fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
+ } else {
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
+ fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+ }
+ }
+ return moduleNames
+}
+
+func (a *apexBundle) androidMkForType(apexType apexPackaging) android.AndroidMkData {
+ return android.AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ moduleNames := []string{}
+ if a.installable() {
+ moduleNames = a.androidMkForFiles(w, name, moduleDir, apexType)
+ }
+
+ if a.flattened && apexType.image() {
+ // Only image APEXes can be flattened.
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+ fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ if len(moduleNames) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
+ }
+ fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+ } else {
+ // zip-apex is the less common type so have the name refer to the image-apex
+ // only and use {name}.zip if you want the zip-apex
+ if apexType == zipApex && a.apexTypes == both {
+ name = name + ".zip"
+ }
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+ fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFiles[apexType].String())
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
+ fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
+ if len(moduleNames) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
+ }
+ if len(a.externalDeps) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.externalDeps, " "))
+ }
+ fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+
+ if apexType == imageApex {
+ fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
+ }
+ }
+ }}
+}
+
+func testApexBundleFactory() android.Module {
+ return ApexBundleFactory( /*testApex*/ true)
+}
+
+func apexBundleFactory() android.Module {
+ return ApexBundleFactory( /*testApex*/ false)
+}
+
+func ApexBundleFactory(testApex bool) android.Module {
+ module := &apexBundle{
+ outputFiles: map[apexPackaging]android.WritablePath{},
+ testApex: testApex,
+ }
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.targetProperties)
+ module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
+ return class == android.Device && ctx.Config().DevicePrefer32BitExecutables()
+ })
+ android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+//
+// Defaults
+//
+type Defaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+func defaultsFactory() android.Module {
+ return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+ module := &Defaults{}
+
+ module.AddProperties(props...)
+ module.AddProperties(
+ &apexBundleProperties{},
+ &apexTargetBundleProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+ return module
+}
+
+//
+// Prebuilt APEX
+//
+type Prebuilt struct {
+ android.ModuleBase
+ prebuilt android.Prebuilt
+
+ properties PrebuiltProperties
+
+ inputApex android.Path
+ installDir android.OutputPath
+ installFilename string
+ outputApex android.WritablePath
+}
+
+type PrebuiltProperties struct {
+ // the path to the prebuilt .apex file to import.
+ Source string `blueprint:"mutated"`
+ ForceDisable bool `blueprint:"mutated"`
+
+ Src *string
+ Arch struct {
+ Arm struct {
+ Src *string
+ }
+ Arm64 struct {
+ Src *string
+ }
+ X86 struct {
+ Src *string
+ }
+ X86_64 struct {
+ Src *string
+ }
+ }
+
+ Installable *bool
+ // Optional name for the installed apex. If unspecified, name of the
+ // module is used as the file name
+ Filename *string
+}
+
+func (p *Prebuilt) installable() bool {
+ return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
+}
+
+func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // If the device is configured to use flattened APEX, force disable the prebuilt because
+ // the prebuilt is a non-flattened one.
+ forceDisable := ctx.Config().FlattenApex()
+
+ // Force disable the prebuilts when we are doing unbundled build. We do unbundled build
+ // to build the prebuilts themselves.
+ forceDisable = forceDisable || ctx.Config().UnbundledBuild()
+
+ // Force disable the prebuilts when coverage is enabled.
+ forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
+ forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
+
+ // b/137216042 don't use prebuilts when address sanitizer is on
+ forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
+ android.InList("hwaddress", ctx.Config().SanitizeDevice())
+
+ if forceDisable && p.prebuilt.SourceExists() {
+ p.properties.ForceDisable = true
+ return
+ }
+
+ // This is called before prebuilt_select and prebuilt_postdeps mutators
+ // The mutators requires that src to be set correctly for each arch so that
+ // arch variants are disabled when src is not provided for the arch.
+ if len(ctx.MultiTargets()) != 1 {
+ ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ return
+ }
+ var src string
+ switch ctx.MultiTargets()[0].Arch.ArchType {
+ case android.Arm:
+ src = String(p.properties.Arch.Arm.Src)
+ case android.Arm64:
+ src = String(p.properties.Arch.Arm64.Src)
+ case android.X86:
+ src = String(p.properties.Arch.X86.Src)
+ case android.X86_64:
+ src = String(p.properties.Arch.X86_64.Src)
+ default:
+ ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+ return
+ }
+ if src == "" {
+ src = String(p.properties.Src)
+ }
+ p.properties.Source = src
+}
+
+func (p *Prebuilt) Srcs() android.Paths {
+ return android.Paths{p.outputApex}
+}
+
+func (p *Prebuilt) InstallFilename() string {
+ return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
+}
+
+func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if p.properties.ForceDisable {
+ return
+ }
+
+ // TODO(jungjw): Check the key validity.
+ p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+ p.installDir = android.PathForModuleInstall(ctx, "apex")
+ p.installFilename = p.InstallFilename()
+ if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
+ ctx.ModuleErrorf("filename should end in %s for prebuilt_apex", imageApexSuffix)
+ }
+ p.outputApex = android.PathForModuleOut(ctx, p.installFilename)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: p.inputApex,
+ Output: p.outputApex,
+ })
+ if p.installable() {
+ ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
+ }
+}
+
+func (p *Prebuilt) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (p *Prebuilt) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *Prebuilt) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(p.inputApex),
+ Include: "$(BUILD_PREBUILT)",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", p.installFilename)
+ fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !p.installable())
+ },
+ },
+ }
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func PrebuiltFactory() android.Module {
+ module := &Prebuilt{}
+ module.AddProperties(&module.properties)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties.Source)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+type ApexSet struct {
+ android.ModuleBase
+ prebuilt android.Prebuilt
+
+ properties ApexSetProperties
+
+ installDir android.OutputPath
+ installFilename string
+ outputApex android.WritablePath
+}
+
+type ApexSetProperties struct {
+ // the .apks file path that contains prebuilt apex files to be extracted.
+ Set string
+
+ // whether the extracted apex file installable.
+ Installable *bool
+
+ // optional name for the installed apex. If unspecified, name of the
+ // module is used as the file name
+ Filename *string
+
+ // names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // apexes in this set use prerelease SDK version
+ Prerelease *bool
+}
+
+func (a *ApexSet) installable() bool {
+ return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
+}
+
+func (a *ApexSet) InstallFilename() string {
+ return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
+}
+
+func (a *ApexSet) Prebuilt() *android.Prebuilt {
+ return &a.prebuilt
+}
+
+func (a *ApexSet) Name() string {
+ return a.prebuilt.Name(a.ModuleBase.Name())
+}
+
+func (a *ApexSet) Overrides() []string {
+ return a.properties.Overrides
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func apexSetFactory() android.Module {
+ module := &ApexSet{}
+ module.AddProperties(&module.properties)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties.Set)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.installFilename = a.InstallFilename()
+ if !strings.HasSuffix(a.installFilename, imageApexSuffix) {
+ ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
+ }
+
+ apexSet := a.prebuilt.SingleSourcePath(ctx)
+ a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: extractMatchingApex,
+ Description: "Extract an apex from an apex set",
+ Inputs: android.Paths{apexSet},
+ Output: a.outputApex,
+ Args: map[string]string{
+ "abis": strings.Join(java.SupportedAbis(ctx), ","),
+ "allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
+ "sdk-version": ctx.Config().PlatformSdkVersion(),
+ },
+ })
+ a.installDir = android.PathForModuleInstall(ctx, "apex")
+ if a.installable() {
+ ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+ }
+}
+
+func (a *ApexSet) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(a.outputApex),
+ Include: "$(BUILD_PREBUILT)",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH := ", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", a.installFilename)
+ if !a.installable() {
+ fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+ }
+ fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.properties.Overrides, " "))
+ },
+ },
+ }
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
new file mode 100644
index 0000000..406ade8
--- /dev/null
+++ b/apex/apex_test.go
@@ -0,0 +1,1367 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/java"
+)
+
+var buildDir string
+
+type testCustomizer func(fs map[string][]byte, config android.Config)
+
+func testApex(t *testing.T, bp string, handlers ...testCustomizer) *android.TestContext {
+ var config android.Config
+ config, buildDir = setup(t)
+ for _, handler := range handlers {
+ tempFS := map[string][]byte{}
+ handler(tempFS, config)
+ }
+ defer teardown(buildDir)
+
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apexBundleFactory))
+ ctx.RegisterModuleType("apex_test", android.ModuleFactoryAdaptor(testApexBundleFactory))
+ ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apexKeyFactory))
+ ctx.RegisterModuleType("apex_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
+ ctx.RegisterModuleType("prebuilt_apex", android.ModuleFactoryAdaptor(PrebuiltFactory))
+ ctx.RegisterModuleType("apex_set", android.ModuleFactoryAdaptor(apexSetFactory))
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+
+ ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("apex_deps", apexDepsMutator)
+ ctx.BottomUp("apex", apexMutator)
+ ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
+ ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
+ })
+
+ ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+ ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(cc.LibrarySharedFactory))
+ ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
+ ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(cc.BinaryFactory))
+ ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
+ ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
+ ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+ ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory))
+ ctx.RegisterModuleType("sh_binary", android.ModuleFactoryAdaptor(android.ShBinaryFactory))
+ ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(java.AndroidAppCertificateFactory))
+ ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
+ ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
+ })
+ ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("image", cc.ImageMutator).Parallel()
+ ctx.BottomUp("link", cc.LinkageMutator).Parallel()
+ ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
+ ctx.BottomUp("version", cc.VersionMutator).Parallel()
+ ctx.BottomUp("begin", cc.BeginMutator).Parallel()
+ })
+ ctx.RegisterSingletonType("apex_keys_text", android.SingletonFactoryAdaptor(apexKeysTextFactory))
+
+ ctx.Register()
+
+ bp = bp + `
+ toolchain_library {
+ name: "libcompiler_rt-extras",
+ src: "",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ toolchain_library {
+ name: "libatomic",
+ src: "",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ toolchain_library {
+ name: "libgcc",
+ src: "",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ toolchain_library {
+ name: "libgcc_stripped",
+ src: "",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-aarch64-android",
+ src: "",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-arm-android",
+ src: "",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_object {
+ name: "crtbegin_so",
+ stl: "none",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_object {
+ name: "crtend_so",
+ stl: "none",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_object {
+ name: "crtbegin_static",
+ stl: "none",
+ }
+
+ cc_object {
+ name: "crtend_android",
+ stl: "none",
+ }
+
+ llndk_library {
+ name: "libc",
+ symbol_file: "",
+ }
+
+ llndk_library {
+ name: "libm",
+ symbol_file: "",
+ }
+
+ llndk_library {
+ name: "libdl",
+ symbol_file: "",
+ }
+ `
+
+ ctx.MockFileSystem(map[string][]byte{
+ "Android.bp": []byte(bp),
+ "build/target/product/security": nil,
+ "apex_manifest.json": nil,
+ "AndroidManifest.xml": nil,
+ "system/sepolicy/apex/myapex-file_contexts": nil,
+ "system/sepolicy/apex/myapex_keytest-file_contexts": nil,
+ "system/sepolicy/apex/otherapex-file_contexts": nil,
+ "mylib.cpp": nil,
+ "myprebuilt": nil,
+ "my_include": nil,
+ "vendor/foo/devkeys/test.x509.pem": nil,
+ "vendor/foo/devkeys/test.pk8": nil,
+ "testkey.x509.pem": nil,
+ "testkey.pk8": nil,
+ "testkey.override.x509.pem": nil,
+ "testkey.override.pk8": nil,
+ "vendor/foo/devkeys/testkey.avbpubkey": nil,
+ "vendor/foo/devkeys/testkey.pem": nil,
+ "NOTICE": nil,
+ "custom_notice": nil,
+ "testkey2.avbpubkey": nil,
+ "testkey2.pem": nil,
+ "myapex-arm64.apex": nil,
+ "myapex-arm.apex": nil,
+ "myapex.apks": nil,
+ "frameworks/base/api/current.txt": nil,
+ })
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ return ctx
+}
+
+func setup(t *testing.T) (config android.Config, buildDir string) {
+ buildDir, err := ioutil.TempDir("", "soong_apex_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ config = android.TestArchConfig(buildDir, nil)
+ config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
+ config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
+ config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
+ config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
+ config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
+ return
+}
+
+func teardown(buildDir string) {
+ os.RemoveAll(buildDir)
+}
+
+// ensure that 'result' contains 'expected'
+func ensureContains(t *testing.T, result string, expected string) {
+ if !strings.Contains(result, expected) {
+ t.Errorf("%q is not found in %q", expected, result)
+ }
+}
+
+// ensures that 'result' does not contain 'notExpected'
+func ensureNotContains(t *testing.T, result string, notExpected string) {
+ if strings.Contains(result, notExpected) {
+ t.Errorf("%q is found in %q", notExpected, result)
+ }
+}
+
+func ensureListContains(t *testing.T, result []string, expected string) {
+ if !android.InList(expected, result) {
+ t.Errorf("%q is not found in %v", expected, result)
+ }
+}
+
+func ensureListNotContains(t *testing.T, result []string, notExpected string) {
+ if android.InList(notExpected, result) {
+ t.Errorf("%q is found in %v", notExpected, result)
+ }
+}
+
+// Minimal test
+func TestBasicApex(t *testing.T) {
+ ctx := testApex(t, `
+ apex_defaults {
+ name: "myapex-defaults",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ multilib: {
+ both: {
+ binaries: ["foo",],
+ }
+ }
+ }
+
+ apex {
+ name: "myapex",
+ defaults: ["myapex-defaults"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ filegroup {
+ name: "myapex.manifest",
+ srcs: ["apex_manifest.json"],
+ }
+
+ filegroup {
+ name: "myapex.androidmanifest",
+ srcs: ["AndroidManifest.xml"],
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["mylib2"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_binary {
+ name: "foo",
+ srcs: ["mylib.cpp"],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ symlinks: ["foo_link_"],
+ symlink_preferred_arch: true,
+ system_shared_libs: [],
+ static_executable: true,
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ notice: "custom_notice",
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+
+ optFlags := apexRule.Args["opt_flags"]
+ ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
+ // Ensure that the NOTICE output is being packaged as an asset.
+ ensureContains(t, optFlags, "--assets_dir "+buildDir+"/.intermediates/myapex/android_common_myapex/NOTICE")
+
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that main rule creates an output
+ ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
+
+ // Ensure that apex variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+
+ // Ensure that apex variant is created for the indirect dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared_myapex")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+
+ // Ensure that the platform variant ends with _core_shared
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared")
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared")
+
+ // Ensure that all symlinks are present.
+ found_foo_link_64 := false
+ found_foo := false
+ for _, cmd := range strings.Split(copyCmds, " && ") {
+ if strings.HasPrefix(cmd, "ln -s foo64") {
+ if strings.HasSuffix(cmd, "bin/foo") {
+ found_foo = true
+ } else if strings.HasSuffix(cmd, "bin/foo_link_64") {
+ found_foo_link_64 = true
+ }
+ }
+ }
+ good := found_foo && found_foo_link_64
+ if !good {
+ t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds)
+ }
+
+ mergeNoticesRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("mergeNoticesRule")
+ noticeInputs := mergeNoticesRule.Inputs.Strings()
+ if len(noticeInputs) != 2 {
+ t.Errorf("number of input notice files: expected = 2, actual = %q", len(noticeInputs))
+ }
+ ensureListContains(t, noticeInputs, "NOTICE")
+ ensureListContains(t, noticeInputs, "custom_notice")
+}
+
+func TestBasicZipApex(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ payload_type: "zip",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["mylib2"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ zipApexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("zipApexRule")
+ copyCmds := zipApexRule.Args["copy_commands"]
+
+ // Ensure that main rule creates an output
+ ensureContains(t, zipApexRule.Output.String(), "myapex.zipapex.unsigned")
+
+ // Ensure that APEX variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+
+ // Ensure that APEX variant is created for the indirect dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared_myapex")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.zipapex/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.zipapex/lib64/mylib2.so")
+}
+
+func TestApexWithStubs(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib", "mylib3"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["mylib2", "mylib3"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ cflags: ["-include mylib.h"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ cc_library {
+ name: "mylib3",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["mylib4"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["10", "11", "12"],
+ },
+ }
+
+ cc_library {
+ name: "mylib4",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that direct non-stubs dep is always included
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+ // Ensure that indirect stubs dep is not included
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+
+ // Ensure that direct stubs dep is included
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
+
+ mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_shared_myapex").Rule("ld").Args["libFlags"]
+
+ // Ensure that mylib is linking with the latest version of stubs for mylib2
+ ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_core_shared_3_myapex/mylib2.so")
+ // ... and not linking to the non-stub (impl) variant of mylib2
+ ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_core_shared_myapex/mylib2.so")
+
+ // Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
+ ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_core_shared_myapex/mylib3.so")
+ // .. and not linking to the stubs variant of mylib3
+ ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_core_shared_12_myapex/mylib3.so")
+
+ // Ensure that stubs libs are built without -include flags
+ mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
+ ensureNotContains(t, mylib2Cflags, "-include ")
+
+ // Ensure that genstub is invoked with --apex
+ ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_core_static_3_myapex").Rule("genStubSrc").Args["flags"])
+}
+
+func TestApexWithExplicitStubsDependency(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libfoo#10"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libfoo",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libbar"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["10", "20", "30"],
+ },
+ }
+
+ cc_library {
+ name: "libbar",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that direct non-stubs dep is always included
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+
+ // Ensure that indirect stubs dep is not included
+ ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so")
+
+ // Ensure that dependency of stubs is not included
+ ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so")
+
+ mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_shared_myapex").Rule("ld").Args["libFlags"]
+
+ // Ensure that mylib is linking with version 10 of libfoo
+ ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_core_shared_10_myapex/libfoo.so")
+ // ... and not linking to the non-stub (impl) variant of libfoo
+ ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_core_shared_myapex/libfoo.so")
+
+ libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_core_shared_10_myapex").Rule("ld").Args["libFlags"]
+
+ // Ensure that libfoo stubs is not linking to libbar (since it is a stubs)
+ ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
+}
+
+func TestApexWithSystemLibsStubs(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libdl#27"],
+ stl: "none",
+ }
+
+ cc_library_shared {
+ name: "mylib_shared",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libdl#27"],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libc",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["27", "28", "29"],
+ },
+ }
+
+ cc_library {
+ name: "libm",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["27", "28", "29"],
+ },
+ }
+
+ cc_library {
+ name: "libdl",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["27", "28", "29"],
+ },
+ }
+
+ cc_library {
+ name: "libBootstrap",
+ srcs: ["mylib.cpp"],
+ stl: "none",
+ bootstrap: true,
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that mylib, libm, libdl are included.
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/bionic/libm.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/bionic/libdl.so")
+
+ // Ensure that libc is not included (since it has stubs and not listed in native_shared_libs)
+ ensureNotContains(t, copyCmds, "image.apex/lib64/bionic/libc.so")
+
+ mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_shared_myapex").Rule("ld").Args["libFlags"]
+ mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
+ mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_core_shared_myapex").Rule("cc").Args["cFlags"]
+
+ // For dependency to libc
+ // Ensure that mylib is linking with the latest version of stubs
+ ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_core_shared_29_myapex/libc.so")
+ // ... and not linking to the non-stub (impl) variant
+ ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_core_shared_myapex/libc.so")
+ // ... Cflags from stub is correctly exported to mylib
+ ensureContains(t, mylibCFlags, "__LIBC_API__=29")
+ ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29")
+
+ // For dependency to libm
+ // Ensure that mylib is linking with the non-stub (impl) variant
+ ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_core_shared_myapex/libm.so")
+ // ... and not linking to the stub variant
+ ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_core_shared_29_myapex/libm.so")
+ // ... and is not compiling with the stub
+ ensureNotContains(t, mylibCFlags, "__LIBM_API__=29")
+ ensureNotContains(t, mylibSharedCFlags, "__LIBM_API__=29")
+
+ // For dependency to libdl
+ // Ensure that mylib is linking with the specified version of stubs
+ ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_27_myapex/libdl.so")
+ // ... and not linking to the other versions of stubs
+ ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_28_myapex/libdl.so")
+ ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_29_myapex/libdl.so")
+ // ... and not linking to the non-stub (impl) variant
+ ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_core_shared_myapex/libdl.so")
+ // ... Cflags from stub is correctly exported to mylib
+ ensureContains(t, mylibCFlags, "__LIBDL_API__=27")
+ ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27")
+
+ // Ensure that libBootstrap is depending on the platform variant of bionic libs
+ libFlags := ctx.ModuleForTests("libBootstrap", "android_arm64_armv8-a_core_shared").Rule("ld").Args["libFlags"]
+ ensureContains(t, libFlags, "libc/android_arm64_armv8-a_core_shared/libc.so")
+ ensureContains(t, libFlags, "libm/android_arm64_armv8-a_core_shared/libm.so")
+ ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_core_shared/libdl.so")
+}
+
+func TestFilesInSubDir(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ binaries: ["mybin"],
+ prebuilts: ["myetc"],
+ compile_multilib: "both",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ prebuilt_etc {
+ name: "myetc",
+ src: "myprebuilt",
+ sub_dir: "foo/bar",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ relative_install_path: "foo/bar",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_binary {
+ name: "mybin",
+ srcs: ["mylib.cpp"],
+ relative_install_path: "foo/bar",
+ system_shared_libs: [],
+ static_executable: true,
+ stl: "none",
+ }
+ `)
+
+ generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("generateFsConfig")
+ dirs := strings.Split(generateFsRule.Args["exec_paths"], " ")
+
+ // Ensure that the subdirectories are all listed
+ ensureListContains(t, dirs, "etc")
+ ensureListContains(t, dirs, "etc/foo")
+ ensureListContains(t, dirs, "etc/foo/bar")
+ ensureListContains(t, dirs, "lib64")
+ ensureListContains(t, dirs, "lib64/foo")
+ ensureListContains(t, dirs, "lib64/foo/bar")
+ ensureListContains(t, dirs, "lib")
+ ensureListContains(t, dirs, "lib/foo")
+ ensureListContains(t, dirs, "lib/foo/bar")
+
+ ensureListContains(t, dirs, "bin")
+ ensureListContains(t, dirs, "bin/foo")
+ ensureListContains(t, dirs, "bin/foo/bar")
+}
+
+func TestUseVendor(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ use_vendor: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["mylib2"],
+ system_shared_libs: [],
+ vendor_available: true,
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ vendor_available: true,
+ stl: "none",
+ }
+ `)
+
+ inputsList := []string{}
+ for _, i := range ctx.ModuleForTests("myapex", "android_common_myapex").Module().BuildParamsForTests() {
+ for _, implicit := range i.Implicits {
+ inputsList = append(inputsList, implicit.String())
+ }
+ }
+ inputsString := strings.Join(inputsList, " ")
+
+ // ensure that the apex includes vendor variants of the direct and indirect deps
+ ensureContains(t, inputsString, "android_arm64_armv8-a_vendor_shared_myapex/mylib.so")
+ ensureContains(t, inputsString, "android_arm64_armv8-a_vendor_shared_myapex/mylib2.so")
+
+ // ensure that the apex does not include core variants
+ ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib.so")
+ ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib2.so")
+}
+
+func TestStaticLinking(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ cc_binary {
+ name: "not_in_apex",
+ srcs: ["mylib.cpp"],
+ static_libs: ["mylib"],
+ static_executable: true,
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ ldFlags := ctx.ModuleForTests("not_in_apex", "android_arm64_armv8-a_core").Rule("ld").Args["libFlags"]
+
+ // Ensure that not_in_apex is linking with the static variant of mylib
+ ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_core_static/mylib.a")
+}
+
+func TestKeys(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex_keytest",
+ key: "myapex.key",
+ certificate: ":myapex.certificate",
+ native_shared_libs: ["mylib"],
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ android_app_certificate {
+ name: "myapex.certificate",
+ certificate: "testkey",
+ }
+
+ android_app_certificate {
+ name: "myapex.certificate.override",
+ certificate: "testkey.override",
+ }
+
+ `)
+
+ // check the APEX keys
+ keys := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey)
+
+ if keys.public_key_file.String() != "vendor/foo/devkeys/testkey.avbpubkey" {
+ t.Errorf("public key %q is not %q", keys.public_key_file.String(),
+ "vendor/foo/devkeys/testkey.avbpubkey")
+ }
+ if keys.private_key_file.String() != "vendor/foo/devkeys/testkey.pem" {
+ t.Errorf("private key %q is not %q", keys.private_key_file.String(),
+ "vendor/foo/devkeys/testkey.pem")
+ }
+
+ // check the APK certs. It should be overridden to myapex.certificate.override
+ certs := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk").Args["certificates"]
+ if certs != "testkey.override.x509.pem testkey.override.pk8" {
+ t.Errorf("cert and private key %q are not %q", certs,
+ "testkey.override.509.pem testkey.override.pk8")
+ }
+}
+
+func TestMacro(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex {
+ name: "otherapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ // non-APEX variant does not have __ANDROID__APEX__ defined
+ mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static").Rule("cc").Args["cFlags"]
+ ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
+ ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+
+ // APEX variant has __ANDROID_APEX__=<apexname> defined
+ mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
+ ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
+ ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+
+ // APEX variant has __ANDROID_APEX__=<apexname> defined
+ mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_otherapex").Rule("cc").Args["cFlags"]
+ ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
+ ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+}
+
+func TestHeaderLibsDependency(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library_headers {
+ name: "mylib_headers",
+ export_include_dirs: ["my_include"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ header_libs: ["mylib_headers"],
+ export_header_lib_headers: ["mylib_headers"],
+ stubs: {
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ cc_library {
+ name: "otherlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ shared_libs: ["mylib"],
+ }
+ `)
+
+ cFlags := ctx.ModuleForTests("otherlib", "android_arm64_armv8-a_core_static").Rule("cc").Args["cFlags"]
+
+ // Ensure that the include path of the header lib is exported to 'otherlib'
+ ensureContains(t, cFlags, "-Imy_include")
+}
+
+func TestNonTestApex(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib_common"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib_common",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ module := ctx.ModuleForTests("myapex", "android_common_myapex")
+ apexRule := module.Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ if apex, ok := module.Module().(*apexBundle); !ok || apex.testApex {
+ t.Log("Apex was a test apex!")
+ t.Fail()
+ }
+ // Ensure that main rule creates an output
+ ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
+
+ // Ensure that apex variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared_myapex")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib_common.so")
+
+ // Ensure that the platform variant ends with _core_shared
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared")
+
+ if !android.InAnyApex("mylib_common") {
+ t.Log("Found mylib_common not in any apex!")
+ t.Fail()
+ }
+}
+
+func TestTestApex(t *testing.T) {
+ if android.InAnyApex("mylib_common_test") {
+ t.Fatal("mylib_common_test must not be used in any other tests since this checks that global state is not updated in an illegal way!")
+ }
+ ctx := testApex(t, `
+ apex_test {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib_common_test"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib_common_test",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ module := ctx.ModuleForTests("myapex", "android_common_myapex")
+ apexRule := module.Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ if apex, ok := module.Module().(*apexBundle); !ok || !apex.testApex {
+ t.Log("Apex was not a test apex!")
+ t.Fail()
+ }
+ // Ensure that main rule creates an output
+ ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
+
+ // Ensure that apex variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_core_shared_myapex")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib_common_test.so")
+
+ // Ensure that the platform variant ends with _core_shared
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_core_shared")
+
+ if android.InAnyApex("mylib_common_test") {
+ t.Log("Found mylib_common_test in some apex!")
+ t.Fail()
+ }
+}
+
+func TestApexWithTarget(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ multilib: {
+ first: {
+ native_shared_libs: ["mylib_common"],
+ }
+ },
+ target: {
+ android: {
+ multilib: {
+ first: {
+ native_shared_libs: ["mylib"],
+ }
+ }
+ },
+ host: {
+ multilib: {
+ first: {
+ native_shared_libs: ["mylib2"],
+ }
+ }
+ }
+ }
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mylib_common",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ compile_multilib: "first",
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ compile_multilib: "first",
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that main rule creates an output
+ ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")
+
+ // Ensure that apex variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared_myapex")
+ ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared_myapex")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib_common.so")
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+
+ // Ensure that the platform variant ends with _core_shared
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared")
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_core_shared")
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_core_shared")
+}
+
+func TestApexWithShBinary(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ binaries: ["myscript"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ sh_binary {
+ name: "myscript",
+ src: "mylib.cpp",
+ filename: "myscript.sh",
+ sub_dir: "script",
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ ensureContains(t, copyCmds, "image.apex/bin/script/myscript.sh")
+}
+
+func TestApexInProductPartition(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ product_specific: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ product_specific: true,
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ apex := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+ expected := "target/product/test_device/product/apex"
+ actual := apex.installDir.RelPathString()
+ if actual != expected {
+ t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
+ }
+}
+
+func TestApexKeyFromOtherModule(t *testing.T) {
+ ctx := testApex(t, `
+ apex_key {
+ name: "myapex.key",
+ public_key: ":my.avbpubkey",
+ private_key: ":my.pem",
+ product_specific: true,
+ }
+
+ filegroup {
+ name: "my.avbpubkey",
+ srcs: ["testkey2.avbpubkey"],
+ }
+
+ filegroup {
+ name: "my.pem",
+ srcs: ["testkey2.pem"],
+ }
+ `)
+
+ apex_key := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey)
+ expected_pubkey := "testkey2.avbpubkey"
+ actual_pubkey := apex_key.public_key_file.String()
+ if actual_pubkey != expected_pubkey {
+ t.Errorf("wrong public key path. expected %q. actual %q", expected_pubkey, actual_pubkey)
+ }
+ expected_privkey := "testkey2.pem"
+ actual_privkey := apex_key.private_key_file.String()
+ if actual_privkey != expected_privkey {
+ t.Errorf("wrong private key path. expected %q. actual %q", expected_privkey, actual_privkey)
+ }
+}
+
+func TestPrebuilt(t *testing.T) {
+ ctx := testApex(t, `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ }
+ `)
+
+ prebuilt := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+
+ expectedInput := "myapex-arm64.apex"
+ if prebuilt.inputApex.String() != expectedInput {
+ t.Errorf("inputApex invalid. expected: %q, actual: %q", expectedInput, prebuilt.inputApex.String())
+ }
+}
+
+func TestPrebuiltFilenameOverride(t *testing.T) {
+ ctx := testApex(t, `
+ prebuilt_apex {
+ name: "myapex",
+ src: "myapex-arm.apex",
+ filename: "notmyapex.apex",
+ }
+ `)
+
+ p := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+
+ expected := "notmyapex.apex"
+ if p.installFilename != expected {
+ t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename)
+ }
+}
+
+// TODO(jungjw): Move this to proptools
+func intPtr(i int) *int {
+ return &i
+}
+
+func TestApexSet(t *testing.T) {
+ ctx := testApex(t, `
+ apex_set {
+ name: "myapex",
+ set: "myapex.apks",
+ filename: "foo_v2.apex",
+ overrides: ["foo"],
+ }
+ `, func(fs map[string][]byte, config android.Config) {
+ config.TestProductVariables.Platform_sdk_version = intPtr(30)
+ config.TestProductVariables.DeviceArch = proptools.StringPtr("arm")
+ config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm64")
+ })
+
+ m := ctx.ModuleForTests("myapex", "android_common")
+
+ // Check extract_apks tool parameters.
+ extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+ actual := extractedApex.Args["abis"]
+ expected := "ARMEABI_V7A,ARM64_V8A"
+ if actual != expected {
+ t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
+ }
+ actual = extractedApex.Args["sdk-version"]
+ expected = "30"
+ if actual != expected {
+ t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
+ }
+}
+
+func TestApexKeysTxt(t *testing.T) {
+ ctx := testApex(t, `
+ prebuilt_apex {
+ name: "myapex",
+ prefer: true,
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ }
+
+ apex_set {
+ name: "myapex_set",
+ set: "myapex.apks",
+ filename: "myapex_set.apex",
+ overrides: ["myapex"],
+ }
+ `, func(fs map[string][]byte, config android.Config) {
+ config.TestProductVariables.Platform_sdk_version = intPtr(30)
+ config.TestProductVariables.DeviceArch = proptools.StringPtr("arm")
+ config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm64")
+ })
+
+ apexKeysText := ctx.SingletonForTests("apex_keys_text")
+ content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"]
+ ensureContains(t, content, `name="myapex_set.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"`)
+ ensureContains(t, content, `name="myapex.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"`)
+}
diff --git a/apex/key.go b/apex/key.go
new file mode 100644
index 0000000..ec33763
--- /dev/null
+++ b/apex/key.go
@@ -0,0 +1,196 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint/proptools"
+)
+
+var String = proptools.String
+
+func init() {
+ android.RegisterModuleType("apex_key", apexKeyFactory)
+ android.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+}
+
+type apexKey struct {
+ android.ModuleBase
+
+ properties apexKeyProperties
+
+ public_key_file android.Path
+ private_key_file android.Path
+
+ keyName string
+}
+
+type apexKeyProperties struct {
+ // Path or module to the public key file in avbpubkey format. Installed to the device.
+ // Base name of the file is used as the ID for the key.
+ Public_key *string `android:"path"`
+ // Path or module to the private key file in pem format. Used to sign APEXs.
+ Private_key *string `android:"path"`
+
+ // Whether this key is installable to one of the partitions. Defualt: true.
+ Installable *bool
+}
+
+func apexKeyFactory() android.Module {
+ module := &apexKey{}
+ module.AddProperties(&module.properties)
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (m *apexKey) installable() bool {
+ return false
+}
+
+func (m *apexKey) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // If the keys are from other modules (i.e. :module syntax) respect it.
+ // Otherwise, try to locate the key files in the default cert dir or
+ // in the local module dir
+ if android.SrcIsModule(String(m.properties.Public_key)) != "" {
+ m.public_key_file = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
+ } else {
+ m.public_key_file = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Public_key))
+ // If not found, fall back to the local key pairs
+ if !android.ExistentPathForSource(ctx, m.public_key_file.String()).Valid() {
+ m.public_key_file = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
+ }
+ }
+
+ if android.SrcIsModule(String(m.properties.Private_key)) != "" {
+ m.private_key_file = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
+ } else {
+ m.private_key_file = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Private_key))
+ if !android.ExistentPathForSource(ctx, m.private_key_file.String()).Valid() {
+ m.private_key_file = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
+ }
+ }
+
+ pubKeyName := m.public_key_file.Base()[0 : len(m.public_key_file.Base())-len(m.public_key_file.Ext())]
+ privKeyName := m.private_key_file.Base()[0 : len(m.private_key_file.Base())-len(m.private_key_file.Ext())]
+
+ if m.properties.Public_key != nil && m.properties.Private_key != nil && pubKeyName != privKeyName {
+ ctx.ModuleErrorf("public_key %q (keyname:%q) and private_key %q (keyname:%q) do not have same keyname",
+ m.public_key_file.String(), pubKeyName, m.private_key_file, privKeyName)
+ return
+ }
+ m.keyName = pubKeyName
+}
+
+////////////////////////////////////////////////////////////////////////
+// apex_keys_text
+type apexKeysText struct {
+ output android.OutputPath
+}
+
+func (s *apexKeysText) GenerateBuildActions(ctx android.SingletonContext) {
+ s.output = android.PathForOutput(ctx, "apexkeys.txt")
+ type apexKeyEntry struct {
+ name string
+ presigned bool
+ public_key string
+ private_key string
+ container_certificate string
+ container_private_key string
+ partition string
+ }
+ toString := func(e apexKeyEntry) string {
+ format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q\\n"
+ if e.presigned {
+ return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED")
+ } else {
+ return fmt.Sprintf(format, e.name, e.public_key, e.private_key, e.container_certificate, e.container_private_key)
+ }
+ }
+
+ apexKeyMap := make(map[string]apexKeyEntry)
+ ctx.VisitAllModules(func(module android.Module) {
+ if m, ok := module.(*apexBundle); ok && m.Enabled() && m.installable() {
+ apexKeyMap[m.Name()] = apexKeyEntry{
+ name: m.Name() + ".apex",
+ presigned: false,
+ public_key: m.public_key_file.String(),
+ private_key: m.private_key_file.String(),
+ container_certificate: m.container_certificate_file.String(),
+ container_private_key: m.container_private_key_file.String(),
+ partition: m.PartitionTag(ctx.DeviceConfig()),
+ }
+ }
+ })
+
+ // Find prebuilts and let them override apexBundle if they are preferred
+ ctx.VisitAllModules(func(module android.Module) {
+ if m, ok := module.(*Prebuilt); ok && m.Enabled() && m.installable() &&
+ m.Prebuilt().UsePrebuilt() {
+ apexKeyMap[m.BaseModuleName()] = apexKeyEntry{
+ name: m.InstallFilename(),
+ presigned: true,
+ partition: m.PartitionTag(ctx.DeviceConfig()),
+ }
+ }
+ })
+
+ // Find apex_set and let them override apexBundle or prebuilts. This is done in a separate pass
+ // so that apex_set are not overridden by prebuilts.
+ ctx.VisitAllModules(func(module android.Module) {
+ if m, ok := module.(*ApexSet); ok && m.Enabled() {
+ entry := apexKeyEntry{
+ name: m.InstallFilename(),
+ presigned: true,
+ partition: m.PartitionTag(ctx.DeviceConfig()),
+ }
+ apexKeyMap[m.BaseModuleName()] = entry
+ }
+ })
+
+ // iterating over map does not give consistent ordering in golang
+ var moduleNames []string
+ for key, _ := range apexKeyMap {
+ moduleNames = append(moduleNames, key)
+ }
+ sort.Strings(moduleNames)
+
+ var filecontent strings.Builder
+ for _, name := range moduleNames {
+ fmt.Fprintf(&filecontent, "%s", toString(apexKeyMap[name]))
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.WriteFile,
+ Description: "apexkeys.txt",
+ Output: s.output,
+ Args: map[string]string{
+ "content": filecontent.String(),
+ },
+ })
+}
+
+func apexKeysTextFactory() android.Singleton {
+ return &apexKeysText{}
+}
+
+func (s *apexKeysText) MakeVars(ctx android.MakeVarsContext) {
+ ctx.Strict("SOONG_APEX_KEYS_FILE", s.output.String())
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index fa1f3ff..0047636 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -33,7 +33,7 @@
var (
pctx = android.NewPackageContext("android/soong/bpf")
- cc = pctx.AndroidGomaStaticRule("cc",
+ cc = pctx.AndroidRemoteStaticRule("cc", android.RemoteRuleSupports{Goma: true},
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
@@ -44,7 +44,7 @@
)
type BpfProperties struct {
- Srcs []string
+ Srcs []string `android:"path"`
Cflags []string
Include_dirs []string
}
@@ -66,6 +66,7 @@
// The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
"-isystem bionic/libc/kernel/uapi/asm-arm64",
"-isystem bionic/libc/kernel/android/uapi",
+ "-I system/bpf/progs/include",
"-I " + ctx.ModuleDir(),
}
@@ -75,7 +76,7 @@
cflags = append(cflags, bpf.properties.Cflags...)
- srcs := ctx.ExpandSources(bpf.properties.Srcs, nil)
+ srcs := android.PathsForModuleSrc(ctx, bpf.properties.Srcs)
for _, src := range srcs {
obj := android.ObjPathWithExt(ctx, "", src, "o")
@@ -90,14 +91,10 @@
},
})
- bpf.objs = append(bpf.objs, obj)
+ bpf.objs = append(bpf.objs, obj.WithoutRel())
}
}
-func (bpf *bpf) DepsMutator(ctx android.BottomUpMutatorContext) {
- android.ExtractSourcesDeps(ctx, bpf.properties.Srcs)
-}
-
func (bpf *bpf) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
@@ -125,6 +122,14 @@
}
}
+// Implements SourceFileProducer interface so that the obj output can be used in the data property
+// of other modules.
+func (bpf *bpf) Srcs() android.Paths {
+ return bpf.objs
+}
+
+var _ android.SourceFileProducer = (*bpf)(nil)
+
func bpfFactory() android.Module {
module := &bpf{}
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
new file mode 100644
index 0000000..1d53e41
--- /dev/null
+++ b/bpf/bpf_test.go
@@ -0,0 +1,192 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bpf
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "android/soong/android"
+ cc2 "android/soong/cc"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "genrule_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func testContext(bp string) *android.TestContext {
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("bpf", android.ModuleFactoryAdaptor(bpfFactory))
+ ctx.RegisterModuleType("cc_test", android.ModuleFactoryAdaptor(cc2.TestFactory))
+ ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc2.LibraryFactory))
+ ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(cc2.LibraryStaticFactory))
+ ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc2.ObjectFactory))
+ ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc2.ToolchainLibraryFactory))
+ ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("link", cc2.LinkageMutator).Parallel()
+ })
+ ctx.Register()
+
+ // Add some modules that are required by the compiler and/or linker
+ bp = bp + `
+ toolchain_library {
+ name: "libatomic",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-arm-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-aarch64-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libgcc",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ cc_library {
+ name: "libc",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+
+ cc_library {
+ name: "libm",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+
+ cc_library {
+ name: "libdl",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+
+ cc_library {
+ name: "libgtest",
+ host_supported: true,
+ vendor_available: true,
+ }
+
+ cc_library {
+ name: "libgtest_main",
+ host_supported: true,
+ vendor_available: true,
+ }
+
+ cc_object {
+ name: "crtbegin_dynamic",
+ recovery_available: true,
+ vendor_available: true,
+ }
+
+ cc_object {
+ name: "crtend_android",
+ recovery_available: true,
+ vendor_available: true,
+ }
+
+ cc_object {
+ name: "crtbegin_so",
+ recovery_available: true,
+ vendor_available: true,
+ }
+
+ cc_object {
+ name: "crtend_so",
+ recovery_available: true,
+ vendor_available: true,
+ }
+ `
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "bpf.c": nil,
+ "BpfTest.cpp": nil,
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ return ctx
+}
+
+func TestBpfDataDependency(t *testing.T) {
+ config := android.TestArchConfig(buildDir, nil)
+ bp := `
+ bpf {
+ name: "bpf.o",
+ srcs: ["bpf.c"],
+ }
+
+ cc_test {
+ name: "vts_test_binary_bpf_module",
+ srcs: ["BpfTest.cpp"],
+ data: [":bpf.o"],
+ }
+ `
+
+ ctx := testContext(bp)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if errs == nil {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+ if errs != nil {
+ t.Fatal(errs)
+ }
+
+ // We only verify the above BP configuration is processed successfully since the data property
+ // value is not available for testing from this package.
+ // TODO(jungjw): Add a check for data or move this test to the cc package.
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index d5c318c..706c0ec 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -45,10 +45,63 @@
// A FixRequest specifies the details of which fixes to apply to an individual file
// A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go
type FixRequest struct {
- simplifyKnownRedundantVariables bool
- rewriteIncorrectAndroidmkPrebuilts bool
- rewriteIncorrectAndroidmkAndroidLibraries bool
- mergeMatchingModuleProperties bool
+ steps []fixStep
+}
+
+type fixStep struct {
+ name string
+ fix func(f *Fixer) error
+}
+
+var fixSteps = []fixStep{
+ {
+ name: "simplifyKnownRedundantVariables",
+ fix: runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther),
+ },
+ {
+ name: "rewriteIncorrectAndroidmkPrebuilts",
+ fix: rewriteIncorrectAndroidmkPrebuilts,
+ },
+ {
+ name: "rewriteCtsModuleTypes",
+ fix: rewriteCtsModuleTypes,
+ },
+ {
+ name: "rewriteIncorrectAndroidmkAndroidLibraries",
+ fix: rewriteIncorrectAndroidmkAndroidLibraries,
+ },
+ {
+ name: "rewriteTestModuleTypes",
+ fix: rewriteTestModuleTypes,
+ },
+ {
+ name: "rewriteAndroidmkJavaLibs",
+ fix: rewriteAndroidmkJavaLibs,
+ },
+ {
+ name: "rewriteJavaStaticLibs",
+ fix: rewriteJavaStaticLibs,
+ },
+ {
+ name: "rewritePrebuiltEtc",
+ fix: rewriteAndroidmkPrebuiltEtc,
+ },
+ {
+ name: "mergeMatchingModuleProperties",
+ fix: runPatchListMod(mergeMatchingModuleProperties),
+ },
+ {
+ name: "reorderCommonProperties",
+ fix: runPatchListMod(reorderCommonProperties),
+ },
+ {
+ name: "removeTags",
+ fix: runPatchListMod(removeTags),
+ },
+ {
+ name: "rewriteAndroidTest",
+ fix: rewriteAndroidTest,
+ },
}
func NewFixRequest() FixRequest {
@@ -56,11 +109,8 @@
}
func (r FixRequest) AddAll() (result FixRequest) {
- result = r
- result.simplifyKnownRedundantVariables = true
- result.rewriteIncorrectAndroidmkPrebuilts = true
- result.rewriteIncorrectAndroidmkAndroidLibraries = true
- result.mergeMatchingModuleProperties = true
+ result.steps = append([]fixStep(nil), r.steps...)
+ result.steps = append(result.steps, fixSteps...)
return result
}
@@ -103,7 +153,6 @@
i++
if i >= maxNumIterations {
return nil, fmt.Errorf("Applied fixes %d times and yet the tree continued to change. Is there an infinite loop?", i)
- break
}
}
return f.tree, err
@@ -144,28 +193,8 @@
}
func (f *Fixer) fixTreeOnce(config FixRequest) error {
- if config.simplifyKnownRedundantVariables {
- err := f.simplifyKnownPropertiesDuplicatingEachOther()
- if err != nil {
- return err
- }
- }
- if config.rewriteIncorrectAndroidmkPrebuilts {
- err := f.rewriteIncorrectAndroidmkPrebuilts()
- if err != nil {
- return err
- }
- }
-
- if config.rewriteIncorrectAndroidmkAndroidLibraries {
- err := f.rewriteIncorrectAndroidmkAndroidLibraries()
- if err != nil {
- return err
- }
- }
-
- if config.mergeMatchingModuleProperties {
- err := f.mergeMatchingModuleProperties()
+ for _, fix := range config.steps {
+ err := fix.fix(f)
if err != nil {
return err
}
@@ -173,12 +202,13 @@
return nil
}
-func (f *Fixer) simplifyKnownPropertiesDuplicatingEachOther() error {
+func simplifyKnownPropertiesDuplicatingEachOther(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
// remove from local_include_dirs anything in export_include_dirs
- return f.removeMatchingModuleListProperties("export_include_dirs", "local_include_dirs")
+ return removeMatchingModuleListProperties(mod, patchList,
+ "export_include_dirs", "local_include_dirs")
}
-func (f *Fixer) rewriteIncorrectAndroidmkPrebuilts() error {
+func rewriteIncorrectAndroidmkPrebuilts(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
if !ok {
@@ -187,6 +217,11 @@
if mod.Type != "java_import" {
continue
}
+ host, _ := getLiteralBoolPropertyValue(mod, "host")
+ if host {
+ mod.Type = "java_import_host"
+ removeProperty(mod, "host")
+ }
srcs, ok := getLiteralListProperty(mod, "srcs")
if !ok {
continue
@@ -214,7 +249,53 @@
return nil
}
-func (f *Fixer) rewriteIncorrectAndroidmkAndroidLibraries() error {
+func rewriteCtsModuleTypes(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ if mod.Type != "cts_support_package" && mod.Type != "cts_package" &&
+ mod.Type != "cts_target_java_library" &&
+ mod.Type != "cts_host_java_library" {
+
+ continue
+ }
+
+ var defStr string
+ switch mod.Type {
+ case "cts_support_package":
+ mod.Type = "android_test"
+ defStr = "cts_support_defaults"
+ case "cts_package":
+ mod.Type = "android_test"
+ defStr = "cts_defaults"
+ case "cts_target_java_library":
+ mod.Type = "java_library"
+ defStr = "cts_defaults"
+ case "cts_host_java_library":
+ mod.Type = "java_library_host"
+ defStr = "cts_defaults"
+ }
+
+ defaults := &parser.Property{
+ Name: "defaults",
+ Value: &parser.List{
+ Values: []parser.Expression{
+ &parser.String{
+ Value: defStr,
+ },
+ },
+ },
+ }
+ mod.Properties = append(mod.Properties, defaults)
+ }
+
+ return nil
+}
+
+func rewriteIncorrectAndroidmkAndroidLibraries(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
if !ok {
@@ -230,7 +311,7 @@
hasResourceDirs := hasNonEmptyLiteralListProperty(mod, "resource_dirs")
if hasAndroidLibraries || hasStaticAndroidLibraries || hasResourceDirs {
- if mod.Type == "java_library_static" {
+ if mod.Type == "java_library_static" || mod.Type == "java_library" {
mod.Type = "android_library"
}
}
@@ -249,42 +330,438 @@
return nil
}
-func (f *Fixer) mergeMatchingModuleProperties() error {
- // Make sure all the offsets are accurate
- buf, err := f.reparse()
- if err != nil {
- return err
- }
-
- var patchlist parser.PatchList
+// rewriteTestModuleTypes looks for modules that are identifiable as tests but for which Make doesn't have a separate
+// module class, and moves them to the appropriate Soong module type.
+func rewriteTestModuleTypes(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
if !ok {
continue
}
- err := mergeMatchingProperties(&mod.Properties, buf, &patchlist)
+ if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") {
+ continue
+ }
+
+ hasInstrumentationFor := hasNonEmptyLiteralStringProperty(mod, "instrumentation_for")
+ tags, _ := getLiteralListPropertyValue(mod, "tags")
+
+ var hasTestsTag bool
+ for _, tag := range tags {
+ if tag == "tests" {
+ hasTestsTag = true
+ }
+ }
+
+ isTest := hasInstrumentationFor || hasTestsTag
+
+ if isTest {
+ switch mod.Type {
+ case "android_app":
+ mod.Type = "android_test"
+ case "java_library", "java_library_installable":
+ mod.Type = "java_test"
+ case "java_library_host":
+ mod.Type = "java_test_host"
+ }
+ }
+ }
+
+ return nil
+}
+
+// rewriteJavaStaticLibs rewrites java_library_static into java_library
+func rewriteJavaStaticLibs(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ if mod.Type == "java_library_static" {
+ mod.Type = "java_library"
+ }
+ }
+
+ return nil
+}
+
+// rewriteAndroidmkJavaLibs rewrites java_library_installable into java_library plus installable: true
+func rewriteAndroidmkJavaLibs(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ if mod.Type != "java_library_installable" {
+ continue
+ }
+
+ mod.Type = "java_library"
+
+ _, hasInstallable := mod.GetProperty("installable")
+ if !hasInstallable {
+ prop := &parser.Property{
+ Name: "installable",
+ Value: &parser.Bool{
+ Value: true,
+ },
+ }
+ mod.Properties = append(mod.Properties, prop)
+ }
+ }
+
+ return nil
+}
+
+// Helper function to get the value of a string-valued property in a given compound property.
+func getStringProperty(prop *parser.Property, fieldName string) string {
+ if propsAsMap, ok := prop.Value.(*parser.Map); ok {
+ for _, propField := range propsAsMap.Properties {
+ if fieldName == propField.Name {
+ if propFieldAsString, ok := propField.Value.(*parser.String); ok {
+ return propFieldAsString.Value
+ } else {
+ return ""
+ }
+ }
+ }
+ }
+ return ""
+}
+
+// Create sub_dir: attribute for the given path
+func makePrebuiltEtcDestination(mod *parser.Module, path string) {
+ mod.Properties = append(mod.Properties, &parser.Property{
+ Name: "sub_dir",
+ Value: &parser.String{Value: path},
+ })
+}
+
+// Set the value of the given attribute to the error message
+func indicateAttributeError(mod *parser.Module, attributeName string, format string, a ...interface{}) error {
+ msg := fmt.Sprintf(format, a...)
+ mod.Properties = append(mod.Properties, &parser.Property{
+ Name: attributeName,
+ Value: &parser.String{Value: "ERROR: " + msg},
+ })
+ return errors.New(msg)
+}
+
+// If a variable is LOCAL_MODULE, get its value from the 'name' attribute.
+// This handles the statement
+// LOCAL_SRC_FILES := $(LOCAL_MODULE)
+// which occurs often.
+func resolveLocalModule(mod *parser.Module, val parser.Expression) parser.Expression {
+ if varLocalName, ok := val.(*parser.Variable); ok {
+ if varLocalName.Name == "LOCAL_MODULE" {
+ if v, ok := getLiteralStringProperty(mod, "name"); ok {
+ return v
+ }
+ }
+ }
+ return val
+}
+
+// A prefix to strip before setting 'filename' attribute and an array of boolean attributes to set.
+type filenamePrefixToFlags struct {
+ prefix string
+ flags []string
+}
+
+var localModulePathRewrite = map[string][]filenamePrefixToFlags{
+ "HOST_OUT": {{prefix: "/etc"}},
+ "PRODUCT_OUT": {{prefix: "/system/etc"}, {prefix: "/vendor/etc", flags: []string{"proprietary"}}},
+ "TARGET_OUT": {{prefix: "/etc"}},
+ "TARGET_OUT_ETC": {{prefix: ""}},
+ "TARGET_OUT_PRODUCT": {{prefix: "/etc", flags: []string{"product_specific"}}},
+ "TARGET_OUT_PRODUCT_ETC": {{prefix: "", flags: []string{"product_specific"}}},
+ "TARGET_OUT_ODM": {{prefix: "/etc", flags: []string{"device_specific"}}},
+ "TARGET_OUT_PRODUCT_SERVICES": {{prefix: "/etc", flags: []string{"product_services_specific"}}},
+ "TARGET_OUT_PRODUCT_SERVICES_ETC": {{prefix: "", flags: []string{"product_services_specific"}}},
+ "TARGET_OUT_VENDOR": {{prefix: "/etc", flags: []string{"proprietary"}}},
+ "TARGET_OUT_VENDOR_ETC": {{prefix: "", flags: []string{"proprietary"}}},
+ "TARGET_RECOVERY_ROOT_OUT": {{prefix: "/system/etc", flags: []string{"recovery"}}},
+}
+
+// rewriteAndroidPrebuiltEtc fixes prebuilt_etc rule
+func rewriteAndroidmkPrebuiltEtc(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ if mod.Type != "prebuilt_etc" && mod.Type != "prebuilt_etc_host" {
+ continue
+ }
+
+ // The rewriter converts LOCAL_SRC_FILES to `srcs` attribute. Convert
+ // it to 'src' attribute (which is where the file is installed). If the
+ // value 'srcs' is a list, we can convert it only if it contains a single
+ // element.
+ if srcs, ok := mod.GetProperty("srcs"); ok {
+ if srcList, ok := srcs.Value.(*parser.List); ok {
+ removeProperty(mod, "srcs")
+ if len(srcList.Values) == 1 {
+ mod.Properties = append(mod.Properties,
+ &parser.Property{Name: "src", NamePos: srcs.NamePos, ColonPos: srcs.ColonPos, Value: resolveLocalModule(mod, srcList.Values[0])})
+ } else if len(srcList.Values) > 1 {
+ indicateAttributeError(mod, "src", "LOCAL_SRC_FILES should contain at most one item")
+ }
+ } else if _, ok = srcs.Value.(*parser.Variable); ok {
+ removeProperty(mod, "srcs")
+ mod.Properties = append(mod.Properties,
+ &parser.Property{Name: "src", NamePos: srcs.NamePos, ColonPos: srcs.ColonPos, Value: resolveLocalModule(mod, srcs.Value)})
+ } else {
+ renameProperty(mod, "srcs", "src")
+ }
+ }
+
+ // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
+ // 'local_module_path'. Analyze its contents and create the correct sub_dir:,
+ // filename: and boolean attributes combination
+ const local_module_path = "local_module_path"
+ if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok {
+ removeProperty(mod, local_module_path)
+ prefixVariableName := getStringProperty(prop_local_module_path, "var")
+ path := getStringProperty(prop_local_module_path, "fixed")
+ if prefixRewrites, ok := localModulePathRewrite[prefixVariableName]; ok {
+ rewritten := false
+ for _, prefixRewrite := range prefixRewrites {
+ if path == prefixRewrite.prefix {
+ rewritten = true
+ } else if trimmedPath := strings.TrimPrefix(path, prefixRewrite.prefix+"/"); trimmedPath != path {
+ makePrebuiltEtcDestination(mod, trimmedPath)
+ rewritten = true
+ }
+ if rewritten {
+ for _, flag := range prefixRewrite.flags {
+ mod.Properties = append(mod.Properties, &parser.Property{Name: flag, Value: &parser.Bool{Value: true, Token: "true"}})
+ }
+ break
+ }
+ }
+ if !rewritten {
+ expectedPrefices := ""
+ sep := ""
+ for _, prefixRewrite := range prefixRewrites {
+ expectedPrefices += sep
+ sep = ", "
+ expectedPrefices += prefixRewrite.prefix
+ }
+ return indicateAttributeError(mod, "filename",
+ "LOCAL_MODULE_PATH value under $(%s) should start with %s", prefixVariableName, expectedPrefices)
+ }
+ if prefixVariableName == "HOST_OUT" {
+ mod.Type = "prebuilt_etc_host"
+ }
+ } else {
+ return indicateAttributeError(mod, "filename", "Cannot handle $(%s) for the prebuilt_etc", prefixVariableName)
+ }
+ }
+ }
+ return nil
+}
+
+func rewriteAndroidTest(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !(ok && mod.Type == "android_test") {
+ continue
+ }
+ // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
+ // 'local_module_path'. For the android_test module, it should be $(TARGET_OUT_DATA_APPS),
+ // that is, `local_module_path: { var: "TARGET_OUT_DATA_APPS"}`
+ const local_module_path = "local_module_path"
+ if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok {
+ removeProperty(mod, local_module_path)
+ prefixVariableName := getStringProperty(prop_local_module_path, "var")
+ path := getStringProperty(prop_local_module_path, "fixed")
+ if prefixVariableName == "TARGET_OUT_DATA_APPS" && path == "" {
+ continue
+ }
+ return indicateAttributeError(mod, "filename",
+ "Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the android_test")
+ }
+ }
+ return nil
+}
+
+func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error {
+ return func(f *Fixer) error {
+ // Make sure all the offsets are accurate
+ buf, err := f.reparse()
+ if err != nil {
+ return err
+ }
+
+ var patchlist parser.PatchList
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ err := modFunc(mod, buf, &patchlist)
+ if err != nil {
+ return err
+ }
+ }
+
+ newBuf := new(bytes.Buffer)
+ err = patchlist.Apply(bytes.NewReader(buf), newBuf)
+ if err != nil {
+ return err
+ }
+
+ // Save a copy of the buffer to print for errors below
+ bufCopy := append([]byte(nil), newBuf.Bytes()...)
+
+ newTree, err := parse(f.tree.Name, newBuf)
+ if err != nil {
+ return fmt.Errorf("Failed to parse: %v\nBuffer:\n%s", err, string(bufCopy))
+ }
+
+ f.tree = newTree
+
+ return nil
+ }
+}
+
+var commonPropertyPriorities = []string{
+ "name",
+ "defaults",
+ "device_supported",
+ "host_supported",
+ "installable",
+}
+
+func reorderCommonProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+ if len(mod.Properties) == 0 {
+ return nil
+ }
+
+ pos := mod.LBracePos.Offset + 1
+ stage := ""
+
+ for _, name := range commonPropertyPriorities {
+ idx := propertyIndex(mod.Properties, name)
+ if idx == -1 {
+ continue
+ }
+ if idx == 0 {
+ err := patchlist.Add(pos, pos, stage)
+ if err != nil {
+ return err
+ }
+ stage = ""
+
+ pos = mod.Properties[0].End().Offset + 1
+ mod.Properties = mod.Properties[1:]
+ continue
+ }
+
+ prop := mod.Properties[idx]
+ mod.Properties = append(mod.Properties[:idx], mod.Properties[idx+1:]...)
+
+ stage += string(buf[prop.Pos().Offset : prop.End().Offset+1])
+
+ err := patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
if err != nil {
return err
}
}
- newBuf := new(bytes.Buffer)
- err = patchlist.Apply(bytes.NewReader(buf), newBuf)
- if err != nil {
- return err
+ if stage != "" {
+ err := patchlist.Add(pos, pos, stage)
+ if err != nil {
+ return err
+ }
}
- newTree, err := parse(f.tree.Name, newBuf)
- if err != nil {
- return err
- }
-
- f.tree = newTree
-
return nil
}
+func removeTags(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+ prop, ok := mod.GetProperty("tags")
+ if !ok {
+ return nil
+ }
+ list, ok := prop.Value.(*parser.List)
+ if !ok {
+ return nil
+ }
+
+ replaceStr := ""
+
+ for _, item := range list.Values {
+ str, ok := item.(*parser.String)
+ if !ok {
+ replaceStr += fmt.Sprintf("// ERROR: Unable to parse tag %q\n", item)
+ continue
+ }
+
+ switch str.Value {
+ case "optional":
+ continue
+ case "debug":
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
+ // force installation for -userdebug and -eng builds.
+ `
+ case "eng":
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // Add this module to PRODUCT_PACKAGES_ENG in your product file if you want to
+ // force installation for -eng builds.
+ `
+ case "tests":
+ switch {
+ case strings.Contains(mod.Type, "cc_test"),
+ strings.Contains(mod.Type, "cc_library_static"),
+ strings.Contains(mod.Type, "java_test"),
+ mod.Type == "android_test":
+ continue
+ case strings.Contains(mod.Type, "cc_lib"):
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // To make a shared library only for tests, use the "cc_test_library" module
+ // type. If you don't use gtest, set "gtest: false".
+ `
+ case strings.Contains(mod.Type, "cc_bin"):
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // For native test binaries, use the "cc_test" module type. Some differences:
+ // - If you don't use gtest, set "gtest: false"
+ // - Binaries will be installed into /data/nativetest[64]/<name>/<name>
+ // - Both 32 & 64 bit versions will be built (as appropriate)
+ `
+ case strings.Contains(mod.Type, "java_lib"):
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // For JUnit or similar tests, use the "java_test" module type. A dependency on
+ // Junit will be added by default, if it is using some other runner, set "junit: false".
+ `
+ case mod.Type == "android_app":
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // For JUnit or instrumentataion app tests, use the "android_test" module type.
+ `
+ default:
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // In most cases, tests are now identified by their module type:
+ // cc_test, java_test, python_test
+ `
+ }
+ default:
+ replaceStr += fmt.Sprintf("// WARNING: Unknown module tag %q\n", str.Value)
+ }
+ }
+
+ return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
+}
+
+func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+ return mergeMatchingProperties(&mod.Properties, buf, patchlist)
+}
+
func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error {
seen := make(map[string]*parser.Property)
for i := 0; i < len(*properties); i++ {
@@ -309,6 +786,14 @@
}
func mergeProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error {
+ // The value of one of the properties may be a variable reference with no type assigned
+ // Bail out in this case. Soong will notice duplicate entries and will tell to merge them.
+ if _, isVar := a.Value.(*parser.Variable); isVar {
+ return nil
+ }
+ if _, isVar := b.Value.(*parser.Variable); isVar {
+ return nil
+ }
if a.Value.Type() != b.Value.Type() {
return fmt.Errorf("type mismatch when merging properties %q: %s and %s", a.Name, a.Value.Type(), b.Value.Type())
}
@@ -359,7 +844,7 @@
}
// removes from <items> every item present in <removals>
-func filterExpressionList(items *parser.List, removals *parser.List) {
+func filterExpressionList(patchList *parser.PatchList, items *parser.List, removals *parser.List) {
writeIndex := 0
for _, item := range items.Values {
included := true
@@ -376,28 +861,39 @@
if included {
items.Values[writeIndex] = item
writeIndex++
+ } else {
+ patchList.Add(item.Pos().Offset, item.End().Offset+2, "")
}
}
items.Values = items.Values[:writeIndex]
}
// Remove each modules[i].Properties[<legacyName>][j] that matches a modules[i].Properties[<canonicalName>][k]
-func (f *Fixer) removeMatchingModuleListProperties(canonicalName string, legacyName string) error {
- for _, def := range f.tree.Defs {
- mod, ok := def.(*parser.Module)
- if !ok {
- continue
- }
- legacyList, ok := getLiteralListProperty(mod, legacyName)
- if !ok {
- continue
- }
- canonicalList, ok := getLiteralListProperty(mod, canonicalName)
- if !ok {
- continue
- }
- filterExpressionList(legacyList, canonicalList)
+func removeMatchingModuleListProperties(mod *parser.Module, patchList *parser.PatchList, canonicalName string, legacyName string) error {
+ legacyProp, ok := mod.GetProperty(legacyName)
+ if !ok {
+ return nil
}
+ legacyList, ok := legacyProp.Value.(*parser.List)
+ if !ok || len(legacyList.Values) == 0 {
+ return nil
+ }
+ canonicalList, ok := getLiteralListProperty(mod, canonicalName)
+ if !ok {
+ return nil
+ }
+
+ localPatches := parser.PatchList{}
+ filterExpressionList(&localPatches, legacyList, canonicalList)
+
+ if len(legacyList.Values) == 0 {
+ patchList.Add(legacyProp.Pos().Offset, legacyProp.End().Offset+2, "")
+ } else {
+ for _, p := range localPatches {
+ patchList.Add(p.Start, p.End, p.Replacement)
+ }
+ }
+
return nil
}
@@ -406,6 +902,11 @@
return found && len(list.Values) > 0
}
+func hasNonEmptyLiteralStringProperty(mod *parser.Module, name string) bool {
+ s, found := getLiteralStringPropertyValue(mod, name)
+ return found && len(s) > 0
+}
+
func getLiteralListProperty(mod *parser.Module, name string) (list *parser.List, found bool) {
prop, ok := mod.GetProperty(name)
if !ok {
@@ -415,6 +916,67 @@
return list, ok
}
+func getLiteralListPropertyValue(mod *parser.Module, name string) (list []string, found bool) {
+ listValue, ok := getLiteralListProperty(mod, name)
+ if !ok {
+ return nil, false
+ }
+ for _, v := range listValue.Values {
+ stringValue, ok := v.(*parser.String)
+ if !ok {
+ return nil, false
+ }
+ list = append(list, stringValue.Value)
+ }
+
+ return list, true
+}
+
+func getLiteralStringProperty(mod *parser.Module, name string) (s *parser.String, found bool) {
+ prop, ok := mod.GetProperty(name)
+ if !ok {
+ return nil, false
+ }
+ s, ok = prop.Value.(*parser.String)
+ return s, ok
+}
+
+func getLiteralStringPropertyValue(mod *parser.Module, name string) (s string, found bool) {
+ stringValue, ok := getLiteralStringProperty(mod, name)
+ if !ok {
+ return "", false
+ }
+
+ return stringValue.Value, true
+}
+
+func getLiteralBoolProperty(mod *parser.Module, name string) (b *parser.Bool, found bool) {
+ prop, ok := mod.GetProperty(name)
+ if !ok {
+ return nil, false
+ }
+ b, ok = prop.Value.(*parser.Bool)
+ return b, ok
+}
+
+func getLiteralBoolPropertyValue(mod *parser.Module, name string) (s bool, found bool) {
+ boolValue, ok := getLiteralBoolProperty(mod, name)
+ if !ok {
+ return false, false
+ }
+
+ return boolValue.Value, true
+}
+
+func propertyIndex(props []*parser.Property, propertyName string) int {
+ for i, prop := range props {
+ if prop.Name == propertyName {
+ return i
+ }
+ }
+ return -1
+}
+
func renameProperty(mod *parser.Module, from, to string) {
for _, prop := range mod.Properties {
if prop.Name == from {
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 51708eb..459cd36 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -66,27 +66,31 @@
fixer := NewFixer(tree)
// apply simplifications
- err := fixer.simplifyKnownPropertiesDuplicatingEachOther()
+ err := runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)(fixer)
if len(errs) > 0 {
t.Fatal(err)
}
// lookup legacy property
mod := fixer.tree.Defs[0].(*parser.Module)
- _, found := mod.GetProperty("local_include_dirs")
- if !found {
- t.Fatalf("failed to include key local_include_dirs in parse tree")
+
+ expectedResultString := fmt.Sprintf("%q", expectedResult)
+ if expectedResult == nil {
+ expectedResultString = "unset"
}
// check that the value for the legacy property was updated to the correct value
errorHeader := fmt.Sprintf("\nFailed to correctly simplify key 'local_include_dirs' in the presence of 'export_include_dirs.'\n"+
"original local_include_dirs: %q\n"+
"original export_include_dirs: %q\n"+
- "expected result: %q\n"+
+ "expected result: %s\n"+
"actual result: ",
- local_include_dirs, export_include_dirs, expectedResult)
- result, ok := mod.GetProperty("local_include_dirs")
- if !ok {
+ local_include_dirs, export_include_dirs, expectedResultString)
+ result, found := mod.GetProperty("local_include_dirs")
+ if !found {
+ if expectedResult == nil {
+ return
+ }
t.Fatal(errorHeader + "property not found")
}
@@ -95,6 +99,10 @@
t.Fatalf("%sproperty is not a list: %v", errorHeader, listResult)
}
+ if expectedResult == nil {
+ t.Fatalf("%sproperty exists: %v", errorHeader, listResult)
+ }
+
actualExpressions := listResult.Values
actualValues := make([]string, 0)
for _, expr := range actualExpressions {
@@ -109,7 +117,7 @@
func TestSimplifyKnownVariablesDuplicatingEachOther(t *testing.T) {
// TODO use []Expression{} once buildTree above can support it (which is after b/38325146 is done)
- implFilterListTest(t, []string{"include"}, []string{"include"}, []string{})
+ implFilterListTest(t, []string{"include"}, []string{"include"}, nil)
implFilterListTest(t, []string{"include1"}, []string{"include2"}, []string{"include1"})
implFilterListTest(t, []string{"include1", "include2", "include3", "include4"}, []string{"include2"},
[]string{"include1", "include3", "include4"})
@@ -117,6 +125,49 @@
implFilterListTest(t, []string{}, []string{}, []string{})
}
+func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) {
+ expected, err := Reformat(out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ in, err = Reformat(in)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
+ if errs != nil {
+ t.Fatal(errs)
+ }
+
+ fixer := NewFixer(tree)
+
+ got := ""
+ prev := "foo"
+ passes := 0
+ for got != prev && passes < 10 {
+ err := innerTest(fixer)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ out, err := parser.Print(fixer.tree)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ prev = got
+ got = string(out)
+ passes++
+ }
+
+ if got != expected {
+ t.Errorf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
+ in, expected, got)
+ }
+}
+
func TestMergeMatchingProperties(t *testing.T) {
tests := []struct {
name string
@@ -199,47 +250,537 @@
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- expected, err := Reformat(test.out)
- if err != nil {
- t.Error(err)
- }
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return runPatchListMod(mergeMatchingModuleProperties)(fixer)
+ })
+ })
+ }
+}
- in, err := Reformat(test.in)
- if err != nil {
- t.Error(err)
- }
-
- tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
- if errs != nil {
- t.Fatal(errs)
- }
-
- fixer := NewFixer(tree)
-
- got := ""
- prev := "foo"
- passes := 0
- for got != prev && passes < 10 {
- err := fixer.mergeMatchingModuleProperties()
- if err != nil {
- t.Fatal(err)
+func TestReorderCommonProperties(t *testing.T) {
+ var tests = []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "empty",
+ in: `cc_library {}`,
+ out: `cc_library {}`,
+ },
+ {
+ name: "only priority",
+ in: `
+ cc_library {
+ name: "foo",
}
-
- out, err := parser.Print(fixer.tree)
- if err != nil {
- t.Fatal(err)
+ `,
+ out: `
+ cc_library {
+ name: "foo",
}
+ `,
+ },
+ {
+ name: "already in order",
+ in: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ }
+ `,
+ },
+ {
+ name: "reorder only priority",
+ in: `
+ cc_library {
+ defaults: ["bar"],
+ name: "foo",
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ }
+ `,
+ },
+ {
+ name: "reorder",
+ in: `
+ cc_library {
+ name: "foo",
+ srcs: ["a.c"],
+ host_supported: true,
+ defaults: ["bar"],
+ shared_libs: ["baz"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ defaults: ["bar"],
+ host_supported: true,
+ srcs: ["a.c"],
+ shared_libs: ["baz"],
+ }
+ `,
+ },
+ }
- prev = got
- got = string(out)
- passes++
- }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return runPatchListMod(reorderCommonProperties)(fixer)
+ })
+ })
+ }
+}
- if got != expected {
- t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
- test.name, in, expected, got)
- }
+func TestRemoveMatchingModuleListProperties(t *testing.T) {
+ var tests = []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "simple",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: ["a"],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "long",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: [
+ "a",
+ "b",
+ ],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ foo: [
+ "b",
+ ],
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "long fully removed",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: [
+ "a",
+ ],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "comment",
+ in: `
+ cc_library {
+ name: "foo",
+ // comment
+ foo: ["a"],
+
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+
+ // comment
+
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "inner comment",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: [
+ // comment
+ "a",
+ ],
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "eol comment",
+ in: `
+ cc_library {
+ name: "foo",
+ foo: ["a"], // comment
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+ // comment
+ bar: ["a"],
+ }
+ `,
+ },
+ {
+ name: "eol comment with blank lines",
+ in: `
+ cc_library {
+ name: "foo",
+
+ foo: ["a"], // comment
+
+ // bar
+ bar: ["a"],
+ }
+ `,
+ out: `
+ cc_library {
+ name: "foo",
+
+ // comment
+
+ // bar
+ bar: ["a"],
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return runPatchListMod(func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+ return removeMatchingModuleListProperties(mod, patchList, "bar", "foo")
+ })(fixer)
+ })
+ })
+ }
+}
+
+func TestReplaceJavaStaticLibs(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "static lib",
+ in: `
+ java_library_static {
+ name: "foo",
+ }
+ `,
+ out: `
+ java_library {
+ name: "foo",
+ }
+ `,
+ },
+ {
+ name: "java lib",
+ in: `
+ java_library {
+ name: "foo",
+ }
+ `,
+ out: `
+ java_library {
+ name: "foo",
+ }
+ `,
+ },
+ {
+ name: "java installable lib",
+ in: `
+ java_library {
+ name: "foo",
+ installable: true,
+ }
+ `,
+ out: `
+ java_library {
+ name: "foo",
+ installable: true,
+ }
+ `,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return rewriteJavaStaticLibs(fixer)
+ })
+ })
+ }
+}
+
+func TestRewritePrebuilts(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "jar srcs",
+ in: `
+ java_import {
+ name: "foo",
+ srcs: ["foo.jar"],
+ }
+ `,
+ out: `
+ java_import {
+ name: "foo",
+ jars: ["foo.jar"],
+ }
+ `,
+ },
+ {
+ name: "aar srcs",
+ in: `
+ java_import {
+ name: "foo",
+ srcs: ["foo.aar"],
+ installable: true,
+ }
+ `,
+ out: `
+ android_library_import {
+ name: "foo",
+ aars: ["foo.aar"],
+
+ }
+ `,
+ },
+ {
+ name: "host prebuilt",
+ in: `
+ java_import {
+ name: "foo",
+ srcs: ["foo.jar"],
+ host: true,
+ }
+ `,
+ out: `
+ java_import_host {
+ name: "foo",
+ jars: ["foo.jar"],
+
+ }
+ `,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return rewriteIncorrectAndroidmkPrebuilts(fixer)
+ })
+ })
+ }
+}
+
+func TestRewriteCtsModuleTypes(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "cts_support_package",
+ in: `
+ cts_support_package {
+ name: "foo",
+ }
+ `,
+ out: `
+ android_test {
+ name: "foo",
+ defaults: ["cts_support_defaults"],
+ }
+ `,
+ },
+ {
+ name: "cts_package",
+ in: `
+ cts_package {
+ name: "foo",
+ }
+ `,
+ out: `
+ android_test {
+ name: "foo",
+ defaults: ["cts_defaults"],
+ }
+ `,
+ },
+ {
+ name: "cts_target_java_library",
+ in: `
+ cts_target_java_library {
+ name: "foo",
+ }
+ `,
+ out: `
+ java_library {
+ name: "foo",
+ defaults: ["cts_defaults"],
+ }
+ `,
+ },
+ {
+ name: "cts_host_java_library",
+ in: `
+ cts_host_java_library {
+ name: "foo",
+ }
+ `,
+ out: `
+ java_library_host {
+ name: "foo",
+ defaults: ["cts_defaults"],
+ }
+ `,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, rewriteCtsModuleTypes)
+ })
+ }
+}
+
+func TestRewritePrebuiltEtc(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "prebuilt_etc src",
+ in: `
+ prebuilt_etc {
+ name: "foo",
+ srcs: ["bar"],
+ }
+ `,
+ out: `prebuilt_etc {
+ name: "foo",
+ src: "bar",
+ }
+ `,
+ },
+ {
+ name: "prebuilt_etc src",
+ in: `
+ prebuilt_etc {
+ name: "foo",
+ srcs: FOO,
+ }
+ `,
+ out: `prebuilt_etc {
+ name: "foo",
+ src: FOO,
+ }
+ `,
+ },
+ {
+ name: "prebuilt_etc src",
+ in: `
+ prebuilt_etc {
+ name: "foo",
+ srcs: ["bar", "baz"],
+ }
+ `,
+ out: `prebuilt_etc {
+ name: "foo",
+ src: "ERROR: LOCAL_SRC_FILES should contain at most one item",
+
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return rewriteAndroidmkPrebuiltEtc(fixer)
+ })
+ })
+ }
+}
+
+func TestRewriteAndroidTest(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "android_test valid module path",
+ in: `
+ android_test {
+ name: "foo",
+ local_module_path: {
+ var: "TARGET_OUT_DATA_APPS",
+ },
+ }
+ `,
+ out: `
+ android_test {
+ name: "foo",
+
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return rewriteAndroidTest(fixer)
+ })
})
}
}
diff --git a/bpfix/cmd/bpfix.go b/bpfix/cmd/bpfix.go
index 2fde383..ccdae16 100644
--- a/bpfix/cmd/bpfix.go
+++ b/bpfix/cmd/bpfix.go
@@ -65,7 +65,7 @@
if err != nil {
return err
}
- r := bytes.NewBuffer(src)
+ r := bytes.NewBuffer(append([]byte(nil), src...))
file, errs := parser.Parse(filename, r, parser.NewScope(nil))
if len(errs) > 0 {
for _, err := range errs {
diff --git a/build_test.bash b/build_test.bash
index 4c43224..ee979e7 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -27,6 +27,10 @@
# that's detected in the Go code, which skips calculating the startup time.
export TRACE_BEGIN_SOONG=$(date +%s%N)
+# Remove BUILD_NUMBER so that incremental builds on build servers don't
+# re-read makefiles every time.
+unset BUILD_NUMBER
+
export TOP=$(cd $(dirname ${BASH_SOURCE[0]})/../..; PWD= /bin/pwd)
cd "${TOP}"
source "${TOP}/build/soong/scripts/microfactory.bash"
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 9bcb783..02806f9 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -24,13 +24,20 @@
)
var (
- vendorSuffix = ".vendor"
+ vendorSuffix = ".vendor"
+ recoverySuffix = ".recovery"
)
type AndroidMkContext interface {
+ Name() string
Target() android.Target
subAndroidMk(*android.AndroidMkData, interface{})
+ Arch() android.Arch
+ Os() android.OsType
+ Host() bool
useVndk() bool
+ static() bool
+ inRecovery() bool
}
type subAndroidMkProvider interface {
@@ -50,7 +57,7 @@
}
func (c *Module) AndroidMk() android.AndroidMkData {
- if c.Properties.HideFromMake {
+ if c.Properties.HideFromMake || !c.IsForPlatform() {
return android.AndroidMkData{
Disabled: true,
}
@@ -58,23 +65,28 @@
ret := android.AndroidMkData{
OutputFile: c.outputFile,
+ // TODO(jiyong): add the APEXes providing shared libs to the required modules
+ // Currently, adding c.Properties.ApexesProvidingSharedLibs is causing multiple
+ // runtime APEXes (com.android.runtime.debug|release) to be installed. And this
+ // is breaking some older devices (like marlin) where system.img is small.
+ Required: c.Properties.AndroidMkRuntimeLibs,
+ Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
+
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
if len(c.Properties.Logtags) > 0 {
fmt.Fprintln(w, "LOCAL_LOGTAGS_FILES :=", strings.Join(c.Properties.Logtags, " "))
}
- fmt.Fprintln(w, "LOCAL_SANITIZE := never")
if len(c.Properties.AndroidMkSharedLibs) > 0 {
fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(c.Properties.AndroidMkSharedLibs, " "))
}
- if c.Target().Os == android.Android &&
- String(c.Properties.Sdk_version) != "" && !c.useVndk() {
- fmt.Fprintln(w, "LOCAL_SDK_VERSION := "+String(c.Properties.Sdk_version))
- fmt.Fprintln(w, "LOCAL_NDK_STL_VARIANT := none")
- } else {
- // These are already included in LOCAL_SHARED_LIBRARIES
- fmt.Fprintln(w, "LOCAL_CXX_STL := none")
+ if len(c.Properties.AndroidMkStaticLibs) > 0 {
+ fmt.Fprintln(w, "LOCAL_STATIC_LIBRARIES := "+strings.Join(c.Properties.AndroidMkStaticLibs, " "))
}
+ if len(c.Properties.AndroidMkWholeStaticLibs) > 0 {
+ fmt.Fprintln(w, "LOCAL_WHOLE_STATIC_LIBRARIES := "+strings.Join(c.Properties.AndroidMkWholeStaticLibs, " "))
+ }
+ fmt.Fprintln(w, "LOCAL_SOONG_LINK_TYPE :=", c.getMakeLinkType())
if c.useVndk() {
fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
}
@@ -97,6 +109,8 @@
// .vendor suffix is added only when we will have two variants: core and vendor.
// The suffix is not added for vendor-only module.
ret.SubName += vendorSuffix
+ } else if c.inRecovery() && !c.onlyInRecovery() {
+ ret.SubName += recoverySuffix
}
return ret
@@ -135,47 +149,24 @@
if library.static() {
ret.Class = "STATIC_LIBRARIES"
} else if library.shared() {
- ctx.subAndroidMk(ret, &library.stripper)
- ctx.subAndroidMk(ret, &library.relocationPacker)
-
ret.Class = "SHARED_LIBRARIES"
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_SOONG_TOC :=", library.toc().String())
+ if !library.buildStubs() {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
+ }
+ if len(library.Properties.Overrides) > 0 {
+ fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES := "+strings.Join(library.Properties.Overrides, " "))
+ }
+ if len(library.post_install_cmds) > 0 {
+ fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD := "+strings.Join(library.post_install_cmds, "&& "))
+ }
+ })
} else if library.header() {
- ret.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
- fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
- fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
- fmt.Fprintln(w, "LOCAL_MODULE :=", name+data.SubName)
-
- archStr := ctx.Target().Arch.ArchType.String()
- var host bool
- switch ctx.Target().Os.Class {
- case android.Host:
- fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH := ", archStr)
- host = true
- case android.HostCross:
- fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH := ", archStr)
- host = true
- case android.Device:
- fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH := ", archStr)
- }
-
- if host {
- makeOs := ctx.Target().Os.String()
- if ctx.Target().Os == android.Linux || ctx.Target().Os == android.LinuxBionic {
- makeOs = "linux"
- }
- fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
- fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
- } else if ctx.useVndk() {
- fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
- }
-
- library.androidMkWriteExportedFlags(w)
- fmt.Fprintln(w, "include $(BUILD_HEADER_LIBRARY)")
- }
-
- return
+ ret.Class = "HEADER_LIBRARIES"
}
+ ret.DistFile = library.distFile
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
library.androidMkWriteExportedFlags(w)
fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES := ")
@@ -187,40 +178,57 @@
}
}
- fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext())
+ _, _, ext := splitFileExt(outputFile.Base())
- fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
+ fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
if library.coverageOutputFile.Valid() {
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", library.coverageOutputFile.String())
}
+
+ if library.useCoreVariant {
+ fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+ fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
+ fmt.Fprintln(w, "LOCAL_VNDK_DEPEND_ON_CORE_VARIANT := true")
+ }
})
- if library.shared() {
+ if library.shared() && !library.buildStubs() {
ctx.subAndroidMk(ret, library.baseInstaller)
+ } else {
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+ if library.buildStubs() {
+ fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
+ }
+ })
+ }
+ if len(library.Properties.Stubs.Versions) > 0 &&
+ android.DirectlyInAnyApex(ctx, ctx.Name()) && !ctx.inRecovery() && !ctx.useVndk() &&
+ !ctx.static() {
+ if !library.buildStubs() {
+ ret.SubName = ".bootstrap"
+ }
}
}
func (object *objectLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ret.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
out := ret.OutputFile.Path()
+ varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, data.SubName)
- fmt.Fprintln(w, "\n$("+prefix+"OUT_INTERMEDIATE_LIBRARIES)/"+name+data.SubName+objectExtension+":", out.String())
- fmt.Fprintln(w, "\t$(copy-file-to-target)")
+ fmt.Fprintf(w, "\n%s := %s\n", varname, out.String())
+ fmt.Fprintln(w, ".KATI_READONLY: "+varname)
}
}
func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ctx.subAndroidMk(ret, binary.baseInstaller)
- ctx.subAndroidMk(ret, &binary.stripper)
ret.Class = "EXECUTABLES"
+ ret.DistFile = binary.distFile
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
- if Bool(binary.Properties.Static_executable) {
- fmt.Fprintln(w, "LOCAL_FORCE_STATIC_EXECUTABLE := true")
- }
-
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String())
if len(binary.symlinks) > 0 {
fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS := "+strings.Join(binary.symlinks, " "))
}
@@ -232,6 +240,9 @@
if len(binary.Properties.Overrides) > 0 {
fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES := "+strings.Join(binary.Properties.Overrides, " "))
}
+ if len(binary.post_install_cmds) > 0 {
+ fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD := "+strings.Join(binary.post_install_cmds, "&& "))
+ }
})
}
@@ -243,6 +254,10 @@
fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
strings.Join(benchmark.Properties.Test_suites, " "))
}
+ if benchmark.testConfig != nil {
+ fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", benchmark.testConfig.String())
+ }
+ fmt.Fprintln(w, "LOCAL_NATIVE_BENCHMARK := true")
})
androidMkWriteTestData(benchmark.data, ctx, ret)
@@ -260,6 +275,9 @@
fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
strings.Join(test.Properties.Test_suites, " "))
}
+ if test.testConfig != nil {
+ fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", test.testConfig.String())
+ }
})
androidMkWriteTestData(test.data, ctx, ret)
@@ -272,34 +290,8 @@
func (library *toolchainLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ret.Class = "STATIC_LIBRARIES"
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
- fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
- })
-}
-
-func (stripper *stripper) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
- // Make only supports stripping target modules
- if ctx.Target().Os != android.Android {
- return
- }
-
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if Bool(stripper.StripProperties.Strip.None) {
-
- fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
- } else if Bool(stripper.StripProperties.Strip.Keep_symbols) {
- fmt.Fprintln(w, "LOCAL_STRIP_MODULE := keep_symbols")
- } else {
- fmt.Fprintln(w, "LOCAL_STRIP_MODULE := mini-debug-info")
- }
- })
-}
-
-func (packer *relocationPacker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if packer.Properties.PackingRelocations {
- fmt.Fprintln(w, "LOCAL_PACK_MODULE_RELOCATIONS := true")
- }
+ _, suffix, _ := splitFileExt(outputFile.Base())
+ fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
})
}
@@ -313,8 +305,8 @@
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
path := installer.path.RelPathString()
dir, file := filepath.Split(path)
- stem := strings.TrimSuffix(file, filepath.Ext(file))
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file))
+ stem, suffix, _ := splitFileExt(file)
+ fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
})
@@ -326,33 +318,26 @@
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
path, file := filepath.Split(c.installPath.String())
- stem := strings.TrimSuffix(file, filepath.Ext(file))
- fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext())
+ stem, suffix, _ := splitFileExt(file)
+ fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
-
- // Prevent make from installing the libraries to obj/lib (since we have
- // dozens of libraries with the same name, they'll clobber each other
- // and the real versions of the libraries from the platform).
- fmt.Fprintln(w, "LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES := false")
})
}
func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ret.Class = "SHARED_LIBRARIES"
- ret.SubName = ".vendor"
+ ret.SubName = vendorSuffix
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
c.libraryDecorator.androidMkWriteExportedFlags(w)
+ _, _, ext := splitFileExt(outputFile.Base())
- fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext())
- fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
- fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
+ fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
- fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
+ fmt.Fprintln(w, "LOCAL_SOONG_TOC :=", c.toc().String())
})
}
@@ -366,12 +351,9 @@
path := c.path.RelPathString()
dir, file := filepath.Split(path)
- stem := strings.TrimSuffix(file, filepath.Ext(file))
- fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
- fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
- fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
- fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext())
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file))
+ stem, suffix, ext := splitFileExt(file)
+ fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
+ fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
})
@@ -379,13 +361,6 @@
func (c *ndkPrebuiltStlLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ret.Class = "SHARED_LIBRARIES"
-
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- // Prevent make from installing the libraries to obj/lib (since we have
- // dozens of libraries with the same name, they'll clobber each other
- // and the real versions of the libraries from the platform).
- fmt.Fprintln(w, "LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES := false")
- })
}
func (c *vendorPublicLibraryStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
@@ -394,11 +369,47 @@
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
c.libraryDecorator.androidMkWriteExportedFlags(w)
+ _, _, ext := splitFileExt(outputFile.Base())
- fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext())
- fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
- fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=")
+ fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
})
}
+
+func (p *prebuiltLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ if p.properties.Check_elf_files != nil {
+ fmt.Fprintln(w, "LOCAL_CHECK_ELF_FILES :=", *p.properties.Check_elf_files)
+ } else {
+ // soong_cc_prebuilt.mk does not include check_elf_file.mk by default
+ // because cc_library_shared and cc_binary use soong_cc_prebuilt.mk as well.
+ // In order to turn on prebuilt ABI checker, set `LOCAL_CHECK_ELF_FILES` to
+ // true if `p.properties.Check_elf_files` is not specified.
+ fmt.Fprintln(w, "LOCAL_CHECK_ELF_FILES := true")
+ }
+ })
+}
+
+func (p *prebuiltLibraryLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+ ctx.subAndroidMk(ret, p.libraryDecorator)
+ if p.shared() {
+ ctx.subAndroidMk(ret, &p.prebuiltLinker)
+ androidMkWriteAllowUndefinedSymbols(p.baseLinker, ret)
+ }
+}
+
+func (p *prebuiltBinaryLinker) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+ ctx.subAndroidMk(ret, p.binaryDecorator)
+ ctx.subAndroidMk(ret, &p.prebuiltLinker)
+ androidMkWriteAllowUndefinedSymbols(p.baseLinker, ret)
+}
+
+func androidMkWriteAllowUndefinedSymbols(linker *baseLinker, ret *android.AndroidMkData) {
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ allow := linker.Properties.Allow_undefined_symbols
+ if allow != nil {
+ fmt.Fprintln(w, "LOCAL_ALLOW_UNDEFINED_SYMBOLS :=", *allow)
+ }
+ })
+}
diff --git a/cc/binary.go b/cc/binary.go
index 9e7b70b..99fb795 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -15,6 +15,10 @@
package cc
import (
+ "path/filepath"
+
+ "github.com/google/blueprint"
+
"android/soong/android"
)
@@ -31,19 +35,13 @@
// if set, add an extra objcopy --prefix-symbols= step
Prefix_symbols *string
- // local file name to pass to the linker as --version_script
- Version_script *string `android:"arch_variant"`
-
// if set, install a symlink to the preferred architecture
- Symlink_preferred_arch *bool
+ Symlink_preferred_arch *bool `android:"arch_variant"`
// install symlinks to the binary. Symlink names will have the suffix and the binary
// extension (if any) appended
Symlinks []string `android:"arch_variant"`
- // do not pass -pie
- No_pie *bool `android:"arch_variant"`
-
DynamicLinker string `blueprint:"mutated"`
// Names of modules to be overridden. Listed modules can only be other binaries
@@ -55,17 +53,17 @@
}
func init() {
- android.RegisterModuleType("cc_binary", binaryFactory)
+ android.RegisterModuleType("cc_binary", BinaryFactory)
android.RegisterModuleType("cc_binary_host", binaryHostFactory)
}
-// Module factory for binaries
-func binaryFactory() android.Module {
+// cc_binary produces a binary that is runnable on a device.
+func BinaryFactory() android.Module {
module, _ := NewBinary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for host binaries
+// cc_binary_host produces a binary that is runnable on a host.
func binaryHostFactory() android.Module {
module, _ := NewBinary(android.HostSupported)
return module.Init()
@@ -84,11 +82,19 @@
toolPath android.OptionalPath
+ // Location of the linked, unstripped binary
+ unstrippedOutputFile android.Path
+
// Names of symlinks to be installed for use in LOCAL_MODULE_SYMLINKS
symlinks []string
// Output archive of gcno coverage information
coverageOutputFile android.OptionalPath
+
+ // Location of the file that should be copied to dist dir when requested
+ distFile android.OptionalPath
+
+ post_install_cmds []string
}
var _ linker = (*binaryDecorator)(nil)
@@ -157,7 +163,8 @@
}
if ctx.Os() == android.LinuxBionic && !binary.static() {
- deps.LinkerScript = "host_bionic_linker_script"
+ deps.DynamicLinker = "linker"
+ deps.LinkerFlagsFile = "host_bionic_linker_flags"
}
}
@@ -166,8 +173,6 @@
"from static libs or set static_executable: true")
}
- android.ExtractSourceDeps(ctx, binary.Properties.Version_script)
-
return deps
}
@@ -178,7 +183,7 @@
func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
module := newModule(hod, android.MultilibFirst)
binary := &binaryDecorator{
- baseLinker: NewBaseLinker(),
+ baseLinker: NewBaseLinker(module.sanitize),
baseInstaller: NewBaseInstaller("bin", "", InstallInSystem),
}
module.compiler = NewBaseCompiler()
@@ -195,7 +200,7 @@
if binary.Properties.Static_executable == nil && ctx.Config().HostStaticBinaries() {
binary.Properties.Static_executable = BoolPtr(true)
}
- } else {
+ } else if !ctx.Fuchsia() {
// Static executables are not supported on Darwin or Windows
binary.Properties.Static_executable = nil
}
@@ -213,12 +218,9 @@
func (binary *binaryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
flags = binary.baseLinker.linkerFlags(ctx, flags)
- if ctx.Host() && !binary.static() {
+ if ctx.Host() && !ctx.Windows() && !binary.static() {
if !ctx.Config().IsEnvTrue("DISABLE_HOST_PIE") {
flags.LdFlags = append(flags.LdFlags, "-pie")
- if ctx.Windows() {
- flags.LdFlags = append(flags.LdFlags, "-Wl,-e_mainCRTStartup")
- }
}
}
@@ -251,15 +253,28 @@
} else {
switch ctx.Os() {
case android.Android:
- flags.DynamicLinker = "/system/bin/linker"
+ if ctx.bootstrap() && !ctx.inRecovery() {
+ flags.DynamicLinker = "/system/bin/bootstrap/linker"
+ } else {
+ flags.DynamicLinker = "/system/bin/linker"
+ }
+ if flags.Toolchain.Is64Bit() {
+ flags.DynamicLinker += "64"
+ }
case android.LinuxBionic:
flags.DynamicLinker = ""
default:
ctx.ModuleErrorf("unknown dynamic linker")
}
- if flags.Toolchain.Is64Bit() {
- flags.DynamicLinker += "64"
- }
+ }
+
+ if ctx.Os() == android.LinuxBionic {
+ // Use the dlwrap entry point, but keep _start around so
+ // that it can be used by host_bionic_inject
+ flags.LdFlags = append(flags.LdFlags,
+ "-Wl,--entry=__dlwrap__start",
+ "-Wl,--undefined=_start",
+ )
}
}
@@ -270,7 +285,6 @@
"-Wl,--gc-sections",
"-Wl,-z,nocopyreloc",
)
-
}
} else {
if binary.static() {
@@ -287,42 +301,36 @@
func (binary *binaryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
- versionScript := ctx.ExpandOptionalSource(binary.Properties.Version_script, "version_script")
fileName := binary.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
outputFile := android.PathForModuleOut(ctx, fileName)
ret := outputFile
var linkerDeps android.Paths
- sharedLibs := deps.SharedLibs
- sharedLibs = append(sharedLibs, deps.LateSharedLibs...)
-
- if versionScript.Valid() {
- if ctx.Darwin() {
- ctx.PropertyErrorf("version_script", "Not supported on Darwin")
- } else {
- flags.LdFlags = append(flags.LdFlags, "-Wl,--version-script,"+versionScript.String())
- linkerDeps = append(linkerDeps, versionScript.Path())
- }
- }
-
- if deps.LinkerScript.Valid() {
- flags.LdFlags = append(flags.LdFlags, "-Wl,-T,"+deps.LinkerScript.String())
- linkerDeps = append(linkerDeps, deps.LinkerScript.Path())
+ if deps.LinkerFlagsFile.Valid() {
+ flags.LdFlags = append(flags.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
+ linkerDeps = append(linkerDeps, deps.LinkerFlagsFile.Path())
}
if flags.DynamicLinker != "" {
- flags.LdFlags = append(flags.LdFlags, " -Wl,-dynamic-linker,"+flags.DynamicLinker)
+ flags.LdFlags = append(flags.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
+ } else if ctx.toolchain().Bionic() && !binary.static() {
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--no-dynamic-linker")
}
builderFlags := flagsToBuilderFlags(flags)
if binary.stripper.needsStrip(ctx) {
+ if ctx.Darwin() {
+ builderFlags.stripUseGnuStrip = true
+ }
strippedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
binary.stripper.strip(ctx, outputFile, strippedOutputFile, builderFlags)
}
+ binary.unstrippedOutputFile = outputFile
+
if String(binary.Properties.Prefix_symbols) != "" {
afterPrefixSymbols := outputFile
outputFile = android.PathForModuleOut(ctx, "unprefixed", fileName)
@@ -330,14 +338,47 @@
flagsToBuilderFlags(flags), afterPrefixSymbols)
}
- if Bool(binary.baseLinker.Properties.Use_version_lib) && ctx.Host() {
- versionedOutputFile := outputFile
- outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
- binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ if Bool(binary.baseLinker.Properties.Use_version_lib) {
+ if ctx.Host() {
+ versionedOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
+ binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ } else {
+ versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
+ binary.distFile = android.OptionalPathForPath(versionedOutputFile)
+
+ if binary.stripper.needsStrip(ctx) {
+ out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
+ binary.distFile = android.OptionalPathForPath(out)
+ binary.stripper.strip(ctx, versionedOutputFile, out, builderFlags)
+ }
+
+ binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ }
}
- linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
- linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+ if ctx.Os() == android.LinuxBionic && !binary.static() {
+ injectedOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "prelinker", fileName)
+
+ if !deps.DynamicLinker.Valid() {
+ panic("Non-static host bionic modules must have a dynamic linker")
+ }
+
+ binary.injectHostBionicLinkerSymbols(ctx, outputFile, deps.DynamicLinker.Path(), injectedOutputFile)
+ }
+
+ var sharedLibs android.Paths
+ // Ignore shared libs for static executables.
+ if !binary.static() {
+ sharedLibs = deps.EarlySharedLibs
+ sharedLibs = append(sharedLibs, deps.SharedLibs...)
+ sharedLibs = append(sharedLibs, deps.LateSharedLibs...)
+ linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
+ linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
+ linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+ }
+
linkerDeps = append(linkerDeps, objs.tidyFiles...)
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
@@ -347,13 +388,10 @@
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
- binary.coverageOutputFile = TransformCoverageFilesToLib(ctx, objs, builderFlags, binary.getStem(ctx))
+ binary.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, binary.getStem(ctx))
- return ret
-}
-
-func (binary *binaryDecorator) install(ctx ModuleContext, file android.Path) {
- binary.baseInstaller.install(ctx, file)
+ // Need to determine symlinks early since some targets (ie APEX) need this
+ // information but will not call 'install'
for _, symlink := range binary.Properties.Symlinks {
binary.symlinks = append(binary.symlinks,
symlink+String(binary.Properties.Suffix)+ctx.toolchain().ExecutableSuffix())
@@ -368,6 +406,51 @@
}
}
+ return ret
+}
+
+func (binary *binaryDecorator) unstrippedOutputFilePath() android.Path {
+ return binary.unstrippedOutputFile
+}
+
+func (binary *binaryDecorator) symlinkList() []string {
+ return binary.symlinks
+}
+
+func (binary *binaryDecorator) nativeCoverage() bool {
+ return true
+}
+
+func (binary *binaryDecorator) coverageOutputFilePath() android.OptionalPath {
+ return binary.coverageOutputFile
+}
+
+// /system/bin/linker -> /apex/com.android.runtime/bin/linker
+func (binary *binaryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
+ dir := binary.baseInstaller.installDir(ctx)
+ dirOnDevice := android.InstallPathToOnDevicePath(ctx, dir)
+ target := "/" + filepath.Join("apex", "com.android.runtime", dir.Base(), file.Base())
+
+ ctx.InstallAbsoluteSymlink(dir, file.Base(), target)
+ binary.post_install_cmds = append(binary.post_install_cmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
+
+ for _, symlink := range binary.symlinks {
+ ctx.InstallAbsoluteSymlink(dir, symlink, target)
+ binary.post_install_cmds = append(binary.post_install_cmds, makeSymlinkCmd(dirOnDevice, symlink, target))
+ }
+}
+
+func (binary *binaryDecorator) install(ctx ModuleContext, file android.Path) {
+ // Bionic binaries (e.g. linker) is installed to the bootstrap subdirectory.
+ // The original path becomes a symlink to the corresponding file in the
+ // runtime APEX.
+ if isBionic(ctx.baseModuleName()) && ctx.Arch().Native && ctx.apexName() == "" && !ctx.inRecovery() {
+ if ctx.Device() {
+ binary.installSymlinkToRuntimeApex(ctx, file)
+ }
+ binary.baseInstaller.subDir = "bootstrap"
+ }
+ binary.baseInstaller.install(ctx, file)
for _, symlink := range binary.symlinks {
ctx.InstallSymlink(binary.baseInstaller.installDir(ctx), symlink, binary.baseInstaller.path)
}
@@ -380,3 +463,26 @@
func (binary *binaryDecorator) hostToolPath() android.OptionalPath {
return binary.toolPath
}
+
+func init() {
+ pctx.HostBinToolVariable("hostBionicSymbolsInjectCmd", "host_bionic_inject")
+}
+
+var injectHostBionicSymbols = pctx.AndroidStaticRule("injectHostBionicSymbols",
+ blueprint.RuleParams{
+ Command: "$hostBionicSymbolsInjectCmd -i $in -l $linker -o $out",
+ CommandDeps: []string{"$hostBionicSymbolsInjectCmd"},
+ }, "linker")
+
+func (binary *binaryDecorator) injectHostBionicLinkerSymbols(ctx ModuleContext, in, linker android.Path, out android.WritablePath) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: injectHostBionicSymbols,
+ Description: "inject host bionic symbols",
+ Input: in,
+ Implicit: linker,
+ Output: out,
+ Args: map[string]string{
+ "linker": linker.String(),
+ },
+ })
+}
diff --git a/cc/builder.go b/cc/builder.go
index eac2449..98c58dd 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -20,6 +20,7 @@
import (
"fmt"
+ "os"
"path/filepath"
"runtime"
"strconv"
@@ -29,6 +30,7 @@
"android/soong/android"
"android/soong/cc/config"
+ "android/soong/remoteexec"
)
const (
@@ -43,10 +45,18 @@
}
)
+func absSrcDir() string {
+ dir, err := os.Getwd()
+ if err != nil {
+ panic(fmt.Errorf("failed to get working directory: %s", err))
+ }
+ return dir
+}
+
var (
pctx = android.NewPackageContext("android/soong/cc")
- cc = pctx.AndroidGomaStaticRule("cc",
+ cc = pctx.AndroidRemoteStaticRule("cc", android.RemoteRuleSupports{Goma: true, RBE: true},
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
@@ -55,22 +65,47 @@
},
"ccCmd", "cFlags")
- ld = pctx.AndroidStaticRule("ld",
+ ccNoDeps = pctx.AndroidStaticRule("ccNoDeps",
blueprint.RuleParams{
- Command: "$ldCmd ${crtBegin} @${out}.rsp " +
+ Command: "$relPwd $ccCmd -c $cFlags -o $out $in",
+ CommandDeps: []string{"$ccCmd"},
+ },
+ "ccCmd", "cFlags")
+
+ ld, ldRE = remoteexec.StaticRules(pctx, "ld",
+ blueprint.RuleParams{
+ Command: "$reTemplate$ldCmd ${crtBegin} @${out}.rsp " +
"${libFlags} ${crtEnd} -o ${out} ${ldFlags}",
CommandDeps: []string{"$ldCmd"},
Rspfile: "${out}.rsp",
RspfileContent: "${in}",
+ // clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
+ Restat: true,
},
- "ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags")
+ &remoteexec.REParams{
+ Labels: map[string]string{"type": "link", "tool": "clang"},
+ ExecStrategy: "${config.RECXXLinksExecStrategy}",
+ Inputs: []string{"${out}.rsp"},
+ RSPFile: "${out}.rsp",
+ OutputFiles: []string{"${out}"},
+ ToolchainInputs: []string{"$ldCmd"},
+ Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
+ }, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags"}, nil)
- partialLd = pctx.AndroidStaticRule("partialLd",
+ partialLd, partialLdRE = remoteexec.StaticRules(pctx, "partialLd",
blueprint.RuleParams{
- Command: "$ldCmd -nostdlib -Wl,-r ${in} -o ${out} ${ldFlags}",
+ // Without -no-pie, clang 7.0 adds -pie to link Android files,
+ // but -r and -pie cannot be used together.
+ Command: "$reTemplate$ldCmd -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
CommandDeps: []string{"$ldCmd"},
- },
- "ldCmd", "ldFlags")
+ }, &remoteexec.REParams{
+ Labels: map[string]string{"type": "link", "tool": "clang"},
+ ExecStrategy: "${config.RECXXLinksExecStrategy}",
+ Inputs: []string{"$inCommaList"},
+ OutputFiles: []string{"${out}"},
+ ToolchainInputs: []string{"$ldCmd"},
+ Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
+ }, []string{"ldCmd", "ldFlags"}, []string{"inCommaList"})
ar = pctx.AndroidStaticRule("ar",
blueprint.RuleParams{
@@ -109,13 +144,27 @@
"objcopyCmd", "prefix")
_ = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
+ _ = pctx.SourcePathVariable("xzCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/xz")
+
+ // b/132822437: objcopy uses a file descriptor per .o file when called on .a files, which runs the system out of
+ // file descriptors on darwin. Limit concurrent calls to 5 on darwin.
+ darwinStripPool = func() blueprint.Pool {
+ if runtime.GOOS == "darwin" {
+ return pctx.StaticPool("darwinStripPool", blueprint.PoolParams{
+ Depth: 5,
+ })
+ } else {
+ return nil
+ }
+ }()
strip = pctx.AndroidStaticRule("strip",
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- Command: "CROSS_COMPILE=$crossCompile $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
- CommandDeps: []string{"$stripPath"},
+ Command: "CROSS_COMPILE=$crossCompile XZ=$xzCmd CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
+ CommandDeps: []string{"$stripPath", "$xzCmd"},
+ Pool: darwinStripPool,
},
"args", "crossCompile")
@@ -124,33 +173,22 @@
Command: "rm -f $out && touch $out",
})
- _ = pctx.SourcePathVariable("copyGccLibPath", "build/soong/scripts/copygcclib.sh")
-
- copyGccLib = pctx.AndroidStaticRule("copyGccLib",
- blueprint.RuleParams{
- Depfile: "${out}.d",
- Deps: blueprint.DepsGCC,
- Command: "$copyGccLibPath $out $ccCmd $cFlags -print-file-name=${libName}",
- CommandDeps: []string{"$copyGccLibPath", "$ccCmd"},
- },
- "ccCmd", "cFlags", "libName")
-
_ = pctx.SourcePathVariable("tocPath", "build/soong/scripts/toc.sh")
toc = pctx.AndroidStaticRule("toc",
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- Command: "CROSS_COMPILE=$crossCompile $tocPath -i ${in} -o ${out} -d ${out}.d",
+ Command: "CROSS_COMPILE=$crossCompile $tocPath $format -i ${in} -o ${out} -d ${out}.d",
CommandDeps: []string{"$tocPath"},
Restat: true,
},
- "crossCompile")
+ "crossCompile", "format")
clangTidy = pctx.AndroidStaticRule("clangTidy",
blueprint.RuleParams{
- Command: "rm -f $out && ${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
- CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
+ Command: "rm -f $out && CLANG_TIDY=${config.ClangBin}/clang-tidy ${config.ClangTidyShellPath} $tidyFlags $in -- $cFlags && touch $out",
+ CommandDeps: []string{"${config.ClangBin}/clang-tidy", "${config.ClangTidyShellPath}"},
},
"cFlags", "tidyFlags")
@@ -175,12 +213,18 @@
_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
- sAbiDump = pctx.AndroidStaticRule("sAbiDump",
+ sAbiDump, sAbiDumpRE = remoteexec.StaticRules(pctx, "sAbiDump",
blueprint.RuleParams{
- Command: "rm -f $out && $sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem ${config.RSIncludePath}",
+ Command: "rm -f $out && $reTemplate$sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
CommandDeps: []string{"$sAbiDumper"},
- },
- "cFlags", "exportDirs")
+ }, &remoteexec.REParams{
+ Labels: map[string]string{"type": "abi-dump", "tool": "header-abi-dumper"},
+ ExecStrategy: "${config.REAbiDumperExecStrategy}",
+ Platform: map[string]string{
+ remoteexec.PoolKey: "${config.RECXXPool}",
+ "InputRootAbsolutePath": absSrcDir(),
+ },
+ }, []string{"cFlags", "exportDirs"}, nil)
_ = pctx.SourcePathVariable("sAbiLinker", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-linker")
@@ -195,27 +239,32 @@
_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
- sAbiDiff = pctx.AndroidRuleFunc("sAbiDiff",
+ sAbiDiff = pctx.RuleFunc("sAbiDiff",
func(ctx android.PackageRuleContext) blueprint.RuleParams {
// TODO(b/78139997): Add -check-all-apis back
- commandStr := "($sAbiDiffer $allowFlags -lib $libName -arch $arch -o ${out} -new $in -old $referenceDump)"
- distAbiDiffDir := android.PathForDist(ctx, "abidiffs")
- commandStr += "|| (echo ' ---- Please update abi references by running platform/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l ${libName} ----'"
- if distAbiDiffDir.Valid() {
- commandStr += " && (mkdir -p " + distAbiDiffDir.String() + " && cp ${out} " + distAbiDiffDir.String() + ")"
- }
+ commandStr := "($sAbiDiffer ${allowFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
+ commandStr += "|| (echo 'error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py ${createReferenceDumpFlags} -l ${libName}'"
+ commandStr += " && (mkdir -p $$DIST_DIR/abidiffs && cp ${out} $$DIST_DIR/abidiffs/)"
commandStr += " && exit 1)"
return blueprint.RuleParams{
Command: commandStr,
CommandDeps: []string{"$sAbiDiffer"},
}
},
- "allowFlags", "referenceDump", "libName", "arch")
+ "allowFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
blueprint.RuleParams{
Command: "gunzip -c $in > $out",
})
+
+ zip = pctx.AndroidStaticRule("zip",
+ blueprint.RuleParams{
+ Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
+ CommandDeps: []string{"${SoongZipCmd}"},
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ })
)
func init() {
@@ -228,41 +277,46 @@
// Darwin doesn't have /proc
pctx.StaticVariable("relPwd", "")
}
+
+ pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
+ pctx.Import("android/soong/remoteexec")
}
type builderFlags struct {
- globalFlags string
- arFlags string
- asFlags string
- cFlags string
- toolingCFlags string // A separate set of Cflags for clang LibTooling tools
- conlyFlags string
- cppFlags string
- ldFlags string
- libFlags string
- yaccFlags string
- protoFlags string
- protoOutParams string
- tidyFlags string
- sAbiFlags string
- yasmFlags string
- aidlFlags string
- rsFlags string
- toolchain config.Toolchain
- clang bool
- tidy bool
- coverage bool
- sAbiDump bool
- protoRoot bool
+ globalFlags string
+ arFlags string
+ asFlags string
+ cFlags string
+ toolingCFlags string // A separate set of cFlags for clang LibTooling tools
+ toolingCppFlags string // A separate set of cppFlags for clang LibTooling tools
+ conlyFlags string
+ cppFlags string
+ ldFlags string
+ libFlags string
+ yaccFlags string
+ tidyFlags string
+ sAbiFlags string
+ yasmFlags string
+ aidlFlags string
+ rsFlags string
+ toolchain config.Toolchain
+ tidy bool
+ coverage bool
+ sAbiDump bool
systemIncludeFlags string
groupStaticLibs bool
- arGoldPlugin bool
stripKeepSymbols bool
+ stripKeepSymbolsList string
stripKeepMiniDebugInfo bool
stripAddGnuDebuglink bool
+ stripUseGnuStrip bool
+
+ proto android.ProtoFlags
+ protoC bool
+ protoOptionsFile bool
}
type Objects struct {
@@ -296,7 +350,7 @@
objFiles := make(android.Paths, len(srcFiles))
var tidyFiles android.Paths
- if flags.tidy && flags.clang {
+ if flags.tidy {
tidyFiles = make(android.Paths, 0, len(srcFiles))
}
var coverageFiles android.Paths
@@ -324,7 +378,7 @@
toolingCppflags := strings.Join([]string{
commonFlags,
flags.toolingCFlags,
- flags.cppFlags,
+ flags.toolingCppFlags,
}, " ")
cppflags := strings.Join([]string{
@@ -339,19 +393,14 @@
}, " ")
var sAbiDumpFiles android.Paths
- if flags.sAbiDump && flags.clang {
+ if flags.sAbiDump {
sAbiDumpFiles = make(android.Paths, 0, len(srcFiles))
}
- if flags.clang {
- cflags += " ${config.NoOverrideClangGlobalCflags}"
- toolingCflags += " ${config.NoOverrideClangGlobalCflags}"
- cppflags += " ${config.NoOverrideClangGlobalCflags}"
- toolingCppflags += " ${config.NoOverrideClangGlobalCflags}"
- } else {
- cflags += " ${config.NoOverrideGlobalCflags}"
- cppflags += " ${config.NoOverrideGlobalCflags}"
- }
+ cflags += " ${config.NoOverrideClangGlobalCflags}"
+ toolingCflags += " ${config.NoOverrideClangGlobalCflags}"
+ cppflags += " ${config.NoOverrideClangGlobalCflags}"
+ toolingCppflags += " ${config.NoOverrideClangGlobalCflags}"
for i, srcFile := range srcFiles {
objFile := android.ObjPathWithExt(ctx, subdir, srcFile, "o")
@@ -391,23 +440,27 @@
var moduleCflags string
var moduleToolingCflags string
var ccCmd string
- tidy := flags.tidy && flags.clang
+ tidy := flags.tidy
coverage := flags.coverage
- dump := flags.sAbiDump && flags.clang
+ dump := flags.sAbiDump
+ rule := cc
switch srcFile.Ext() {
- case ".S", ".s":
- ccCmd = "gcc"
+ case ".s":
+ rule = ccNoDeps
+ fallthrough
+ case ".S":
+ ccCmd = "clang"
moduleCflags = asflags
tidy = false
coverage = false
dump = false
case ".c":
- ccCmd = "gcc"
+ ccCmd = "clang"
moduleCflags = cflags
moduleToolingCflags = toolingCflags
case ".cpp", ".cc", ".mm":
- ccCmd = "g++"
+ ccCmd = "clang++"
moduleCflags = cppflags
moduleToolingCflags = toolingCppflags
default:
@@ -415,24 +468,9 @@
continue
}
- if flags.clang {
- switch ccCmd {
- case "gcc":
- ccCmd = "clang"
- case "g++":
- ccCmd = "clang++"
- default:
- panic("unrecoginzied ccCmd")
- }
- }
-
ccDesc := ccCmd
- if flags.clang {
- ccCmd = "${config.ClangBin}/" + ccCmd
- } else {
- ccCmd = gccCmd(flags.toolchain, ccCmd)
- }
+ ccCmd = "${config.ClangBin}/" + ccCmd
var implicitOutputs android.WritablePaths
if coverage {
@@ -442,7 +480,7 @@
}
ctx.Build(pctx, android.BuildParams{
- Rule: cc,
+ Rule: rule,
Description: ccDesc + " " + srcFile.Rel(),
Output: objFile,
ImplicitOutputs: implicitOutputs,
@@ -478,8 +516,12 @@
sAbiDumpFile := android.ObjPathWithExt(ctx, subdir, srcFile, "sdump")
sAbiDumpFiles = append(sAbiDumpFiles, sAbiDumpFile)
+ dumpRule := sAbiDump
+ if ctx.Config().IsEnvTrue("RBE_ABI_DUMPER") {
+ dumpRule = sAbiDumpRE
+ }
ctx.Build(pctx, android.BuildParams{
- Rule: sAbiDump,
+ Rule: dumpRule,
Description: "header-abi-dumper " + srcFile.Rel(),
Output: sAbiDumpFile,
Input: srcFile,
@@ -515,9 +557,6 @@
if !ctx.Darwin() {
arFlags += " -format=gnu"
}
- if flags.arGoldPlugin {
- arFlags += " --plugin ${config.LLVMGoldPlugin}"
- }
if flags.arFlags != "" {
arFlags += " " + flags.arFlags
}
@@ -617,12 +656,7 @@
objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath) {
- var ldCmd string
- if flags.clang {
- ldCmd = "${config.ClangBin}/clang++"
- } else {
- ldCmd = gccCmd(flags.toolchain, "g++")
- }
+ ldCmd := "${config.ClangBin}/clang++"
var libFlagsList []string
@@ -667,12 +701,17 @@
deps = append(deps, crtBegin.Path(), crtEnd.Path())
}
+ rule := ld
+ if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
+ rule = ldRE
+ }
+
ctx.Build(pctx, android.BuildParams{
- Rule: ld,
- Description: "link " + outputFile.Base(),
- Output: outputFile,
- Inputs: objFiles,
- Implicits: deps,
+ Rule: rule,
+ Description: "link " + outputFile.Base(),
+ Output: outputFile,
+ Inputs: objFiles,
+ Implicits: deps,
Args: map[string]string{
"ldCmd": ldCmd,
"crtBegin": crtBegin.String(),
@@ -686,18 +725,33 @@
// Generate a rule to combine .dump sAbi dump files from multiple source files
// into a single .ldump sAbi dump file
func TransformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path,
- baseName, exportedHeaderFlags string) android.OptionalPath {
+ baseName, exportedHeaderFlags string, symbolFile android.OptionalPath,
+ excludedSymbolVersions, excludedSymbolTags []string) android.OptionalPath {
+
outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
sabiLock.Lock()
lsdumpPaths = append(lsdumpPaths, outputFile.String())
sabiLock.Unlock()
+
+ implicits := android.Paths{soFile}
symbolFilterStr := "-so " + soFile.String()
+
+ if symbolFile.Valid() {
+ implicits = append(implicits, symbolFile.Path())
+ symbolFilterStr += " -v " + symbolFile.String()
+ }
+ for _, ver := range excludedSymbolVersions {
+ symbolFilterStr += " --exclude-symbol-version " + ver
+ }
+ for _, tag := range excludedSymbolTags {
+ symbolFilterStr += " --exclude-symbol-tag " + tag
+ }
ctx.Build(pctx, android.BuildParams{
Rule: sAbiLink,
Description: "header-abi-linker " + outputFile.Base(),
Output: outputFile,
Inputs: sAbiDumps,
- Implicit: soFile,
+ Implicits: implicits,
Args: map[string]string{
"symbolFilter": symbolFilterStr,
"arch": ctx.Arch().ArchType.Name,
@@ -719,14 +773,20 @@
}
func SourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
- baseName, exportedHeaderFlags string, isVndkExt bool) android.OptionalPath {
+ baseName, exportedHeaderFlags string, isLlndk, isVndkExt bool) android.OptionalPath {
outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
+ libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
+ createReferenceDumpFlags := ""
localAbiCheckAllowFlags := append([]string(nil), abiCheckAllowFlags...)
if exportedHeaderFlags == "" {
localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-advice-only")
}
+ if isLlndk {
+ localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-consider-opaque-types-different")
+ createReferenceDumpFlags = "--llndk"
+ }
if isVndkExt {
localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-allow-extensions")
}
@@ -738,10 +798,11 @@
Input: inputDump,
Implicit: referenceDump,
Args: map[string]string{
- "referenceDump": referenceDump.String(),
- "libName": baseName[0:(len(baseName) - len(filepath.Ext(baseName)))],
- "arch": ctx.Arch().ArchType.Name,
- "allowFlags": strings.Join(localAbiCheckAllowFlags, " "),
+ "referenceDump": referenceDump.String(),
+ "libName": libName,
+ "arch": ctx.Arch().ArchType.Name,
+ "allowFlags": strings.Join(localAbiCheckAllowFlags, " "),
+ "createReferenceDumpFlags": createReferenceDumpFlags,
},
})
return android.OptionalPathForPath(outputFile)
@@ -751,7 +812,18 @@
func TransformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path,
outputFile android.WritablePath, flags builderFlags) {
- crossCompile := gccCmd(flags.toolchain, "")
+ var format string
+ var crossCompile string
+ if ctx.Darwin() {
+ format = "--macho"
+ crossCompile = "${config.MacToolPath}"
+ } else if ctx.Windows() {
+ format = "--pe"
+ crossCompile = gccCmd(flags.toolchain, "")
+ } else {
+ format = "--elf"
+ crossCompile = gccCmd(flags.toolchain, "")
+ }
ctx.Build(pctx, android.BuildParams{
Rule: toc,
@@ -760,6 +832,7 @@
Input: inputFile,
Args: map[string]string{
"crossCompile": crossCompile,
+ "format": format,
},
})
}
@@ -768,22 +841,23 @@
func TransformObjsToObj(ctx android.ModuleContext, objFiles android.Paths,
flags builderFlags, outputFile android.WritablePath) {
- var ldCmd string
- if flags.clang {
- ldCmd = "${config.ClangBin}/clang++"
- } else {
- ldCmd = gccCmd(flags.toolchain, "g++")
- }
+ ldCmd := "${config.ClangBin}/clang++"
+ rule := partialLd
+ args := map[string]string{
+ "ldCmd": ldCmd,
+ "ldFlags": flags.ldFlags,
+ }
+ if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
+ rule = partialLdRE
+ args["inCommaList"] = strings.Join(objFiles.Strings(), ",")
+ }
ctx.Build(pctx, android.BuildParams{
- Rule: partialLd,
+ Rule: rule,
Description: "link " + outputFile.Base(),
Output: outputFile,
Inputs: objFiles,
- Args: map[string]string{
- "ldCmd": ldCmd,
- "ldFlags": flags.ldFlags,
- },
+ Args: args,
})
}
@@ -819,6 +893,12 @@
if flags.stripKeepSymbols {
args += " --keep-symbols"
}
+ if flags.stripKeepSymbolsList != "" {
+ args += " -k" + flags.stripKeepSymbolsList
+ }
+ if flags.stripUseGnuStrip {
+ args += " --use-gnu-strip"
+ }
ctx.Build(pctx, android.BuildParams{
Rule: strip,
@@ -843,13 +923,18 @@
})
}
-func TransformCoverageFilesToLib(ctx android.ModuleContext,
- inputs Objects, flags builderFlags, baseName string) android.OptionalPath {
+func TransformCoverageFilesToZip(ctx android.ModuleContext,
+ inputs Objects, baseName string) android.OptionalPath {
if len(inputs.coverageFiles) > 0 {
- outputFile := android.PathForModuleOut(ctx, baseName+".gcnodir")
+ outputFile := android.PathForModuleOut(ctx, baseName+".zip")
- TransformObjToStaticLib(ctx, inputs.coverageFiles, flags, outputFile, nil)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zip,
+ Description: "zip " + outputFile.Base(),
+ Inputs: inputs.coverageFiles,
+ Output: outputFile,
+ })
return android.OptionalPathForPath(outputFile)
}
@@ -857,21 +942,6 @@
return android.OptionalPath{}
}
-func CopyGccLib(ctx android.ModuleContext, libName string,
- flags builderFlags, outputFile android.WritablePath) {
-
- ctx.Build(pctx, android.BuildParams{
- Rule: copyGccLib,
- Description: "copy gcc library " + libName,
- Output: outputFile,
- Args: map[string]string{
- "ccCmd": gccCmd(flags.toolchain, "gcc"),
- "cFlags": flags.globalFlags,
- "libName": libName,
- },
- })
-}
-
func gccCmd(toolchain config.Toolchain, cmd string) string {
return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd)
}
diff --git a/cc/cc.go b/cc/cc.go
index 51ac184..0b39634 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,31 +34,42 @@
android.RegisterModuleType("cc_defaults", defaultsFactory)
android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("image", vendorMutator).Parallel()
- ctx.BottomUp("link", linkageMutator).Parallel()
- ctx.BottomUp("vndk", vndkMutator).Parallel()
+ ctx.BottomUp("image", ImageMutator).Parallel()
+ ctx.BottomUp("link", LinkageMutator).Parallel()
+ ctx.BottomUp("vndk", VndkMutator).Parallel()
ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
- ctx.BottomUp("begin", beginMutator).Parallel()
+ ctx.BottomUp("version", VersionMutator).Parallel()
+ ctx.BottomUp("begin", BeginMutator).Parallel()
+ ctx.BottomUp("sysprop", SyspropMutator).Parallel()
})
android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
+ ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
+ ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
+
ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi))
ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel()
+ ctx.TopDown("scs_deps", sanitizerDepsMutator(scs))
+ ctx.BottomUp("scs", sanitizerMutator(scs)).Parallel()
+
ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
- ctx.TopDown("minimal_runtime_deps", minimalRuntimeDepsMutator())
+ ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator)
+ ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
- ctx.BottomUp("coverage", coverageLinkingMutator).Parallel()
+ ctx.BottomUp("coverage", coverageMutator).Parallel()
ctx.TopDown("vndk_deps", sabiDepsMutator)
ctx.TopDown("lto_deps", ltoDepsMutator)
ctx.BottomUp("lto", ltoMutator).Parallel()
+
+ ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
})
pctx.Import("android/soong/cc/config")
@@ -68,6 +79,7 @@
SharedLibs, LateSharedLibs []string
StaticLibs, LateStaticLibs, WholeStaticLibs []string
HeaderLibs []string
+ RuntimeLibs []string
ReexportSharedLibHeaders, ReexportStaticLibHeaders, ReexportHeaderLibHeaders []string
@@ -79,14 +91,17 @@
ReexportGeneratedHeaders []string
CrtBegin, CrtEnd string
- LinkerScript string
+
+ // Used for host bionic
+ LinkerFlagsFile string
+ DynamicLinker string
}
type PathDeps struct {
// Paths to .so files
- SharedLibs, LateSharedLibs android.Paths
+ SharedLibs, EarlySharedLibs, LateSharedLibs android.Paths
// Paths to the dependencies to use for .so files (.so.toc files)
- SharedLibsDeps, LateSharedLibsDeps android.Paths
+ SharedLibsDeps, EarlySharedLibsDeps, LateSharedLibsDeps android.Paths
// Paths to .a files
StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
@@ -104,7 +119,12 @@
// Paths to crt*.o files
CrtBegin, CrtEnd android.OptionalPath
- LinkerScript android.OptionalPath
+
+ // Path to the file container flags to use with the linker
+ LinkerFlagsFile android.OptionalPath
+
+ // Path to the dynamic linker binary
+ DynamicLinker android.OptionalPath
}
type Flags struct {
@@ -117,8 +137,6 @@
CppFlags []string // Flags that apply to C++ source files
ToolingCppFlags []string // Flags that apply to C++ source files parsed by clang LibTooling tools
YaccFlags []string // Flags that apply to Yacc source files
- protoFlags []string // Flags that apply to proto source files
- protoOutParams []string // Flags that modify the output of proto generated files
aidlFlags []string // Flags that apply to aidl source files
rsFlags []string // Flags that apply to renderscript source files
LdFlags []string // Flags that apply to linker command lines
@@ -132,11 +150,9 @@
SystemIncludeFlags []string
Toolchain config.Toolchain
- Clang bool
Tidy bool
Coverage bool
SAbiDump bool
- ProtoRoot bool
RequiredInstructionSet string
DynamicLinker string
@@ -145,7 +161,10 @@
LdFlagsDeps android.Paths // Files depended on by linker flags
GroupStaticLibs bool
- ArGoldPlugin bool // Whether LLVM gold plugin option is passed to llvm-ar
+
+ proto android.ProtoFlags
+ protoC bool // Whether to use C instead of C++
+ protoOptionsFile bool // Whether to look for a .options file next to the .proto
}
type ObjectLinkerProperties struct {
@@ -158,21 +177,34 @@
// Properties used to compile all C or C++ modules
type BaseProperties struct {
- // compile module with clang instead of gcc
+ // Deprecated. true is the default, false is invalid.
Clang *bool `android:"arch_variant"`
// Minimum sdk version supported when compiling against the ndk
Sdk_version *string
- AndroidMkSharedLibs []string `blueprint:"mutated"`
- HideFromMake bool `blueprint:"mutated"`
- PreventInstall bool `blueprint:"mutated"`
+ AndroidMkSharedLibs []string `blueprint:"mutated"`
+ AndroidMkStaticLibs []string `blueprint:"mutated"`
+ AndroidMkRuntimeLibs []string `blueprint:"mutated"`
+ AndroidMkWholeStaticLibs []string `blueprint:"mutated"`
+ HideFromMake bool `blueprint:"mutated"`
+ PreventInstall bool `blueprint:"mutated"`
+ ApexesProvidingSharedLibs []string `blueprint:"mutated"`
UseVndk bool `blueprint:"mutated"`
// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
// file
Logtags []string
+
+ // Make this module available when building for recovery
+ Recovery_available *bool
+
+ InRecovery bool `blueprint:"mutated"`
+
+ // Allows this module to use non-APEX version of libraries. Useful
+ // for building binaries that are started before APEXes are activated.
+ Bootstrap *bool
}
type VendorProperties struct {
@@ -193,28 +225,45 @@
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
Vendor_available *bool
-}
-type UnusedProperties struct {
- Tags []string
+ // whether this module is capable of being loaded with other instance
+ // (possibly an older version) of the same module in the same process.
+ // Currently, a shared library that is a member of VNDK (vndk: {enabled: true})
+ // can be double loaded in a vendor process if the library is also a
+ // (direct and indirect) dependency of an LLNDK library. Such libraries must be
+ // explicitly marked as `double_loadable: true` by the owner, or the dependency
+ // from the LLNDK lib should be cut if the lib is not designed to be double loaded.
+ Double_loadable *bool
}
type ModuleContextIntf interface {
static() bool
staticBinary() bool
- clang() bool
toolchain() config.Toolchain
useSdk() bool
sdkVersion() string
useVndk() bool
+ isNdk() bool
+ isLlndk() bool
+ isLlndkPublic() bool
+ isVndkPrivate() bool
isVndk() bool
isVndkSp() bool
isVndkExt() bool
- createVndkSourceAbiDump() bool
+ inRecovery() bool
+ shouldCreateVndkSourceAbiDump() bool
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
isPgoCompile() bool
+ isNDKStubLibrary() bool
+ useClangLld(actx ModuleContext) bool
+ apexName() string
+ hasStubsVariants() bool
+ isStubs() bool
+ bootstrap() bool
+ mustUseVendorVariant() bool
+ nativeCoverage() bool
}
type ModuleContext interface {
@@ -255,9 +304,14 @@
linkerDeps(ctx DepsContext, deps Deps) Deps
linkerFlags(ctx ModuleContext, flags Flags) Flags
linkerProps() []interface{}
+ useClangLld(actx ModuleContext) bool
link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path
appendLdflags([]string)
+ unstrippedOutputFilePath() android.Path
+
+ nativeCoverage() bool
+ coverageOutputFilePath() android.OptionalPath
}
type installer interface {
@@ -266,6 +320,7 @@
inData() bool
inSanitizerDir() bool
hostToolPath() android.OptionalPath
+ relativeInstallPath() string
}
type dependencyTag struct {
@@ -274,11 +329,14 @@
library bool
reexportFlags bool
+
+ explicitlyVersioned bool
}
var (
sharedDepTag = dependencyTag{name: "shared", library: true}
sharedExportDepTag = dependencyTag{name: "shared", library: true, reexportFlags: true}
+ earlySharedDepTag = dependencyTag{name: "early_shared", library: true}
lateSharedDepTag = dependencyTag{name: "late shared", library: true}
staticDepTag = dependencyTag{name: "static", library: true}
staticExportDepTag = dependencyTag{name: "static", library: true, reexportFlags: true}
@@ -292,11 +350,15 @@
objDepTag = dependencyTag{name: "obj"}
crtBeginDepTag = dependencyTag{name: "crtbegin"}
crtEndDepTag = dependencyTag{name: "crtend"}
- linkerScriptDepTag = dependencyTag{name: "linker script"}
+ linkerFlagsDepTag = dependencyTag{name: "linker flags file"}
+ dynamicLinkerDepTag = dependencyTag{name: "dynamic linker"}
reuseObjTag = dependencyTag{name: "reuse objects"}
+ staticVariantTag = dependencyTag{name: "static variant"}
ndkStubDepTag = dependencyTag{name: "ndk stub", library: true}
ndkLateStubDepTag = dependencyTag{name: "ndk late stub", library: true}
vndkExtDepTag = dependencyTag{name: "vndk extends", library: true}
+ runtimeDepTag = dependencyTag{name: "runtime lib"}
+ coverageDepTag = dependencyTag{name: "coverage"}
)
// Module contains the properties and members used by all C/C++ module types, and implements
@@ -305,10 +367,10 @@
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.ApexModuleBase
Properties BaseProperties
VendorProperties VendorProperties
- unused UnusedProperties
// initialize before calling Init
hod android.HostOrDeviceSupported
@@ -326,6 +388,7 @@
vndkdep *vndkdep
lto *lto
pgo *pgo
+ xom *xom
androidMkSharedLibDeps []string
@@ -347,8 +410,33 @@
staticVariant *Module
}
+func (c *Module) OutputFile() android.OptionalPath {
+ return c.outputFile
+}
+
+func (c *Module) UnstrippedOutputFile() android.Path {
+ if c.linker != nil {
+ return c.linker.unstrippedOutputFilePath()
+ }
+ return nil
+}
+
+func (c *Module) CoverageOutputFile() android.OptionalPath {
+ if c.linker != nil {
+ return c.linker.coverageOutputFilePath()
+ }
+ return android.OptionalPath{}
+}
+
+func (c *Module) RelativeInstallPath() string {
+ if c.installer != nil {
+ return c.installer.relativeInstallPath()
+ }
+ return ""
+}
+
func (c *Module) Init() android.Module {
- c.AddProperties(&c.Properties, &c.VendorProperties, &c.unused)
+ c.AddProperties(&c.Properties, &c.VendorProperties)
if c.compiler != nil {
c.AddProperties(c.compiler.compilerProps()...)
}
@@ -379,14 +467,30 @@
if c.pgo != nil {
c.AddProperties(c.pgo.props()...)
}
+ if c.xom != nil {
+ c.AddProperties(c.xom.props()...)
+ }
for _, feature := range c.features {
c.AddProperties(feature.props()...)
}
+ c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
+ switch class {
+ case android.Device:
+ return ctx.Config().DevicePrefer32BitExecutables()
+ case android.HostCross:
+ // Windows builds always prefer 32-bit
+ return true
+ default:
+ return false
+ }
+ })
android.InitAndroidArchModule(c, c.hod, c.multilib)
android.InitDefaultableModule(c)
+ android.InitApexModule(c)
+
return c
}
@@ -405,6 +509,29 @@
return c.Properties.UseVndk
}
+func (c *Module) isCoverageVariant() bool {
+ return c.coverage.Properties.IsCoverageVariant
+}
+
+func (c *Module) isNdk() bool {
+ return inList(c.Name(), ndkMigratedLibs)
+}
+
+func (c *Module) isLlndk() bool {
+ // Returns true for both LLNDK (public) and LLNDK-private libs.
+ return inList(c.Name(), llndkLibraries)
+}
+
+func (c *Module) isLlndkPublic() bool {
+ // Returns true only for LLNDK (public) libs.
+ return c.isLlndk() && !c.isVndkPrivate()
+}
+
+func (c *Module) isVndkPrivate() bool {
+ // Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
+ return inList(c.Name(), vndkPrivateLibraries)
+}
+
func (c *Module) isVndk() bool {
if vndkdep := c.vndkdep; vndkdep != nil {
return vndkdep.isVndk()
@@ -419,6 +546,13 @@
return false
}
+func (c *Module) isNDKStubLibrary() bool {
+ if _, ok := c.compiler.(*stubDecorator); ok {
+ return true
+ }
+ return false
+}
+
func (c *Module) isVndkSp() bool {
if vndkdep := c.vndkdep; vndkdep != nil {
return vndkdep.isVndkSp()
@@ -433,6 +567,10 @@
return false
}
+func (c *Module) mustUseVendorVariant() bool {
+ return c.isVndkSp() || inList(c.Name(), config.VndkMustUseVendorVariantList)
+}
+
func (c *Module) getVndkExtendsModuleName() string {
if vndkdep := c.vndkdep; vndkdep != nil {
return vndkdep.getVndkExtendsModuleName()
@@ -446,6 +584,46 @@
return c.isVndk() || Bool(c.VendorProperties.Vendor_available)
}
+func (c *Module) inRecovery() bool {
+ return c.Properties.InRecovery || c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) onlyInRecovery() bool {
+ return c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) IsStubs() bool {
+ if library, ok := c.linker.(*libraryDecorator); ok {
+ return library.buildStubs()
+ } else if _, ok := c.linker.(*llndkStubDecorator); ok {
+ return true
+ }
+ return false
+}
+
+func (c *Module) HasStubsVariants() bool {
+ if library, ok := c.linker.(*libraryDecorator); ok {
+ return len(library.Properties.Stubs.Versions) > 0
+ }
+ return false
+}
+
+func (c *Module) bootstrap() bool {
+ return Bool(c.Properties.Bootstrap)
+}
+
+func (c *Module) nativeCoverage() bool {
+ return c.linker != nil && c.linker.nativeCoverage()
+}
+
+func isBionic(name string) bool {
+ switch name {
+ case "libc", "libm", "libdl", "linker":
+ return true
+ }
+ return false
+}
+
type baseModuleContext struct {
android.BaseContext
moduleContextImpl
@@ -471,10 +649,6 @@
ctx BaseModuleContext
}
-func (ctx *moduleContextImpl) clang() bool {
- return ctx.mod.clang(ctx.ctx)
-}
-
func (ctx *moduleContextImpl) toolchain() config.Toolchain {
return ctx.mod.toolchain(ctx.ctx)
}
@@ -484,16 +658,11 @@
}
func (ctx *moduleContextImpl) staticBinary() bool {
- if static, ok := ctx.mod.linker.(interface {
- staticBinary() bool
- }); ok {
- return static.staticBinary()
- }
- return false
+ return ctx.mod.staticBinary()
}
func (ctx *moduleContextImpl) useSdk() bool {
- if ctx.ctx.Device() && !ctx.useVndk() {
+ if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() {
return String(ctx.mod.Properties.Sdk_version) != ""
}
return false
@@ -521,6 +690,22 @@
return ctx.mod.useVndk()
}
+func (ctx *moduleContextImpl) isNdk() bool {
+ return ctx.mod.isNdk()
+}
+
+func (ctx *moduleContextImpl) isLlndk() bool {
+ return ctx.mod.isLlndk()
+}
+
+func (ctx *moduleContextImpl) isLlndkPublic() bool {
+ return ctx.mod.isLlndkPublic()
+}
+
+func (ctx *moduleContextImpl) isVndkPrivate() bool {
+ return ctx.mod.isVndkPrivate()
+}
+
func (ctx *moduleContextImpl) isVndk() bool {
return ctx.mod.isVndk()
}
@@ -529,6 +714,10 @@
return ctx.mod.isPgoCompile()
}
+func (ctx *moduleContextImpl) isNDKStubLibrary() bool {
+ return ctx.mod.isNDKStubLibrary()
+}
+
func (ctx *moduleContextImpl) isVndkSp() bool {
return ctx.mod.isVndkSp()
}
@@ -537,16 +726,49 @@
return ctx.mod.isVndkExt()
}
-// Create source abi dumps if the module belongs to the list of VndkLibraries.
-func (ctx *moduleContextImpl) createVndkSourceAbiDump() bool {
- skipAbiChecks := ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS")
- isVariantOnProductionDevice := true
- sanitize := ctx.mod.sanitize
- if sanitize != nil {
- isVariantOnProductionDevice = sanitize.isVariantOnProductionDevice()
+func (ctx *moduleContextImpl) mustUseVendorVariant() bool {
+ return ctx.mod.mustUseVendorVariant()
+}
+
+func (ctx *moduleContextImpl) inRecovery() bool {
+ return ctx.mod.inRecovery()
+}
+
+// Check whether ABI dumps should be created for this module.
+func (ctx *moduleContextImpl) shouldCreateVndkSourceAbiDump() bool {
+ if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+ return false
}
- vendorAvailable := Bool(ctx.mod.VendorProperties.Vendor_available)
- return !skipAbiChecks && isVariantOnProductionDevice && ctx.ctx.Device() && ((ctx.useVndk() && ctx.isVndk() && (vendorAvailable || ctx.isVndkExt())) || inList(ctx.baseModuleName(), llndkLibraries))
+
+ if ctx.ctx.Fuchsia() {
+ return false
+ }
+
+ if sanitize := ctx.mod.sanitize; sanitize != nil {
+ if !sanitize.isVariantOnProductionDevice() {
+ return false
+ }
+ }
+ if !ctx.ctx.Device() {
+ // Host modules do not need ABI dumps.
+ return false
+ }
+ if !ctx.mod.IsForPlatform() {
+ // APEX variants do not need ABI dumps.
+ return false
+ }
+ if ctx.isNdk() {
+ return true
+ }
+ if ctx.isLlndkPublic() {
+ return true
+ }
+ if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate() {
+ // Return true if this is VNDK-core, VNDK-SP, or VNDK-Ext and this is not
+ // VNDK-private.
+ return true
+ }
+ return false
}
func (ctx *moduleContextImpl) selectedStl() string {
@@ -556,6 +778,10 @@
return ""
}
+func (ctx *moduleContextImpl) useClangLld(actx ModuleContext) bool {
+ return ctx.mod.linker.useClangLld(actx)
+}
+
func (ctx *moduleContextImpl) baseModuleName() string {
return ctx.mod.ModuleBase.BaseModuleName()
}
@@ -564,6 +790,26 @@
return ctx.mod.getVndkExtendsModuleName()
}
+func (ctx *moduleContextImpl) apexName() string {
+ return ctx.mod.ApexName()
+}
+
+func (ctx *moduleContextImpl) hasStubsVariants() bool {
+ return ctx.mod.HasStubsVariants()
+}
+
+func (ctx *moduleContextImpl) isStubs() bool {
+ return ctx.mod.IsStubs()
+}
+
+func (ctx *moduleContextImpl) bootstrap() bool {
+ return ctx.mod.bootstrap()
+}
+
+func (ctx *moduleContextImpl) nativeCoverage() bool {
+ return ctx.mod.nativeCoverage()
+}
+
func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{
hod: hod,
@@ -583,6 +829,7 @@
module.vndkdep = &vndkdep{}
module.lto = <o{}
module.pgo = &pgo{}
+ module.xom = &xom{}
return module
}
@@ -603,6 +850,15 @@
return name
}
+func (c *Module) Symlinks() []string {
+ if p, ok := c.installer.(interface {
+ symlinkList() []string
+ }); ok {
+ return p.symlinkList()
+ }
+ return nil
+}
+
// orderDeps reorders dependencies into a list such that if module A depends on B, then
// A will precede B in the resultant list.
// This is convenient for passing into a linker.
@@ -658,7 +914,6 @@
}
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
-
ctx := &moduleContext{
ModuleContext: actx,
moduleContextImpl: moduleContextImpl{
@@ -672,9 +927,12 @@
return
}
+ if c.Properties.Clang != nil && *c.Properties.Clang == false {
+ ctx.PropertyErrorf("clang", "false (GCC) is no longer supported")
+ }
+
flags := Flags{
Toolchain: c.toolchain(ctx),
- Clang: c.clang(ctx),
}
if c.compiler != nil {
flags = c.compiler.compilerFlags(ctx, flags, deps)
@@ -689,7 +947,7 @@
flags = c.sanitize.flags(ctx, flags)
}
if c.coverage != nil {
- flags = c.coverage.flags(ctx, flags)
+ flags, deps = c.coverage.flags(ctx, flags, deps)
}
if c.lto != nil {
flags = c.lto.flags(ctx, flags)
@@ -697,6 +955,9 @@
if c.pgo != nil {
flags = c.pgo.flags(ctx, flags)
}
+ if c.xom != nil {
+ flags = c.xom.flags(ctx, flags)
+ }
for _, feature := range c.features {
flags = feature.flags(ctx, flags)
}
@@ -737,9 +998,23 @@
return
}
c.outputFile = android.OptionalPathForPath(outputFile)
+
+ // If a lib is directly included in any of the APEXes, unhide the stubs
+ // variant having the latest version gets visible to make. In addition,
+ // the non-stubs variant is renamed to <libname>.bootstrap. This is to
+ // force anything in the make world to link against the stubs library.
+ // (unless it is explicitly referenced via .bootstrap suffix or the
+ // module is marked with 'bootstrap: true').
+ if c.HasStubsVariants() &&
+ android.DirectlyInAnyApex(ctx, ctx.baseModuleName()) &&
+ !c.inRecovery() && !c.useVndk() && !c.static() && !c.isCoverageVariant() &&
+ c.IsStubs() {
+ c.Properties.HideFromMake = false // unhide
+ // Note: this is still non-installable
+ }
}
- if c.installer != nil && !c.Properties.PreventInstall && c.outputFile.Valid() {
+ if c.installer != nil && !c.Properties.PreventInstall && c.IsForPlatform() && c.outputFile.Valid() {
c.installer.install(ctx, c.outputFile.Path())
if ctx.Failed() {
return
@@ -747,7 +1022,7 @@
}
}
-func (c *Module) toolchain(ctx BaseModuleContext) config.Toolchain {
+func (c *Module) toolchain(ctx android.BaseContext) config.Toolchain {
if c.cachedToolchain == nil {
c.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
}
@@ -837,6 +1112,7 @@
deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs)
deps.HeaderLibs = android.LastUniqueStrings(deps.HeaderLibs)
+ deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
for _, lib := range deps.ReexportSharedLibHeaders {
if !inList(lib, deps.SharedLibs) {
@@ -877,11 +1153,17 @@
c.begin(ctx)
}
-func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
- if !c.Enabled() {
- return
+// Split name#version into name and version
+func stubsLibNameAndVersion(name string) (string, string) {
+ if sharp := strings.LastIndex(name, "#"); sharp != -1 && sharp != len(name)-1 {
+ version := name[sharp+1:]
+ libname := name[:sharp]
+ return libname, version
}
+ return name, ""
+}
+func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
ctx := &depsContext{
BottomUpMutatorContext: actx,
moduleContextImpl: moduleContextImpl{
@@ -915,25 +1197,28 @@
variantLibs = []string{}
nonvariantLibs = []string{}
for _, entry := range list {
- if ctx.useSdk() && inList(entry, ndkPrebuiltSharedLibraries) {
- if !inList(entry, ndkMigratedLibs) {
- nonvariantLibs = append(nonvariantLibs, entry+".ndk."+version)
+ // strip #version suffix out
+ name, _ := stubsLibNameAndVersion(entry)
+ if ctx.useSdk() && inList(name, ndkPrebuiltSharedLibraries) {
+ if !inList(name, ndkMigratedLibs) {
+ nonvariantLibs = append(nonvariantLibs, name+".ndk."+version)
} else {
- variantLibs = append(variantLibs, entry+ndkLibrarySuffix)
+ variantLibs = append(variantLibs, name+ndkLibrarySuffix)
}
- } else if ctx.useVndk() && inList(entry, llndkLibraries) {
- nonvariantLibs = append(nonvariantLibs, entry+llndkLibrarySuffix)
- } else if (ctx.Platform() || ctx.ProductSpecific()) && inList(entry, vendorPublicLibraries) {
- vendorPublicLib := entry + vendorPublicLibrarySuffix
+ } else if ctx.useVndk() && inList(name, llndkLibraries) {
+ nonvariantLibs = append(nonvariantLibs, name+llndkLibrarySuffix)
+ } else if (ctx.Platform() || ctx.ProductSpecific()) && inList(name, vendorPublicLibraries) {
+ vendorPublicLib := name + vendorPublicLibrarySuffix
if actx.OtherModuleExists(vendorPublicLib) {
nonvariantLibs = append(nonvariantLibs, vendorPublicLib)
} else {
// This can happen if vendor_public_library module is defined in a
// namespace that isn't visible to the current module. In that case,
// link to the original library.
- nonvariantLibs = append(nonvariantLibs, entry)
+ nonvariantLibs = append(nonvariantLibs, name)
}
} else {
+ // put name#version back
nonvariantLibs = append(nonvariantLibs, entry)
}
}
@@ -945,38 +1230,123 @@
deps.ReexportSharedLibHeaders, _ = rewriteNdkLibs(deps.ReexportSharedLibHeaders)
}
+ buildStubs := false
+ if c.linker != nil {
+ if library, ok := c.linker.(*libraryDecorator); ok {
+ if library.buildStubs() {
+ buildStubs = true
+ }
+ }
+ }
+
for _, lib := range deps.HeaderLibs {
depTag := headerDepTag
if inList(lib, deps.ReexportHeaderLibHeaders) {
depTag = headerExportDepTag
}
- actx.AddVariationDependencies(nil, depTag, lib)
+ if buildStubs {
+ actx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Target().String()},
+ {Mutator: "image", Variation: c.imageVariation()},
+ }, depTag, lib)
+ } else {
+ actx.AddVariationDependencies(nil, depTag, lib)
+ }
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, wholeStaticDepTag,
- deps.WholeStaticLibs...)
+ if buildStubs {
+ // Stubs lib does not have dependency to other static/shared libraries.
+ // Don't proceed.
+ return
+ }
+
+ syspropImplLibraries := syspropImplLibraries(actx.Config())
+
+ for _, lib := range deps.WholeStaticLibs {
+ depTag := wholeStaticDepTag
+ if impl, ok := syspropImplLibraries[lib]; ok {
+ lib = impl
+ }
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, depTag, lib)
+ }
for _, lib := range deps.StaticLibs {
depTag := staticDepTag
if inList(lib, deps.ReexportStaticLibHeaders) {
depTag = staticExportDepTag
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, depTag, lib)
+
+ if impl, ok := syspropImplLibraries[lib]; ok {
+ lib = impl
+ }
+
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, depTag, lib)
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, lateStaticDepTag,
- deps.LateStaticLibs...)
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, lateStaticDepTag, deps.LateStaticLibs...)
+
+ addSharedLibDependencies := func(depTag dependencyTag, name string, version string) {
+ var variations []blueprint.Variation
+ variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
+ versionVariantAvail := !ctx.useVndk() && !c.inRecovery()
+ if version != "" && versionVariantAvail {
+ // Version is explicitly specified. i.e. libFoo#30
+ variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
+ depTag.explicitlyVersioned = true
+ }
+ actx.AddVariationDependencies(variations, depTag, name)
+
+ // If the version is not specified, add dependency to the latest stubs library.
+ // The stubs library will be used when the depending module is built for APEX and
+ // the dependent module is not in the same APEX.
+ latestVersion := latestStubsVersionFor(actx.Config(), name)
+ if version == "" && latestVersion != "" && versionVariantAvail {
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ {Mutator: "version", Variation: latestVersion},
+ }, depTag, name)
+ // Note that depTag.explicitlyVersioned is false in this case.
+ }
+ }
+
+ // shared lib names without the #version suffix
+ var sharedLibNames []string
for _, lib := range deps.SharedLibs {
depTag := sharedDepTag
if inList(lib, deps.ReexportSharedLibHeaders) {
depTag = sharedExportDepTag
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, depTag, lib)
+
+ if impl, ok := syspropImplLibraries[lib]; ok {
+ lib = impl
+ }
+
+ name, version := stubsLibNameAndVersion(lib)
+ sharedLibNames = append(sharedLibNames, name)
+
+ addSharedLibDependencies(depTag, name, version)
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, lateSharedDepTag,
- deps.LateSharedLibs...)
+ for _, lib := range deps.LateSharedLibs {
+ if inList(lib, sharedLibNames) {
+ // This is to handle the case that some of the late shared libs (libc, libdl, libm, ...)
+ // are added also to SharedLibs with version (e.g., libc#10). If not skipped, we will be
+ // linking against both the stubs lib and the non-stubs lib at the same time.
+ continue
+ }
+ addSharedLibDependencies(lateSharedDepTag, lib, "")
+ }
+
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ }, runtimeDepTag, deps.RuntimeLibs...)
actx.AddDependency(c, genSourceDepTag, deps.GeneratedSources...)
@@ -988,23 +1358,30 @@
actx.AddDependency(c, depTag, gen)
}
- actx.AddDependency(c, objDepTag, deps.ObjFiles...)
+ actx.AddVariationDependencies(nil, objDepTag, deps.ObjFiles...)
if deps.CrtBegin != "" {
- actx.AddDependency(c, crtBeginDepTag, deps.CrtBegin)
+ actx.AddVariationDependencies(nil, crtBeginDepTag, deps.CrtBegin)
}
if deps.CrtEnd != "" {
- actx.AddDependency(c, crtEndDepTag, deps.CrtEnd)
+ actx.AddVariationDependencies(nil, crtEndDepTag, deps.CrtEnd)
}
- if deps.LinkerScript != "" {
- actx.AddDependency(c, linkerScriptDepTag, deps.LinkerScript)
+ if deps.LinkerFlagsFile != "" {
+ actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
+ }
+ if deps.DynamicLinker != "" {
+ actx.AddDependency(c, dynamicLinkerDepTag, deps.DynamicLinker)
}
version := ctx.sdkVersion()
actx.AddVariationDependencies([]blueprint.Variation{
- {"ndk_api", version}, {"link", "shared"}}, ndkStubDepTag, variantNdkLibs...)
+ {Mutator: "ndk_api", Variation: version},
+ {Mutator: "link", Variation: "shared"},
+ }, ndkStubDepTag, variantNdkLibs...)
actx.AddVariationDependencies([]blueprint.Variation{
- {"ndk_api", version}, {"link", "shared"}}, ndkLateStubDepTag, variantLateNdkLibs...)
+ {Mutator: "ndk_api", Variation: version},
+ {Mutator: "link", Variation: "shared"},
+ }, ndkLateStubDepTag, variantLateNdkLibs...)
if vndkdep := c.vndkdep; vndkdep != nil {
if vndkdep.isVndkExt() {
@@ -1013,32 +1390,19 @@
baseModuleMode = coreMode
}
actx.AddVariationDependencies([]blueprint.Variation{
- {"image", baseModuleMode}, {"link", "shared"}}, vndkExtDepTag,
- vndkdep.getVndkExtendsModuleName())
+ {Mutator: "image", Variation: baseModuleMode},
+ {Mutator: "link", Variation: "shared"},
+ }, vndkExtDepTag, vndkdep.getVndkExtendsModuleName())
}
}
}
-func beginMutator(ctx android.BottomUpMutatorContext) {
+func BeginMutator(ctx android.BottomUpMutatorContext) {
if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
c.beginMutator(ctx)
}
}
-func (c *Module) clang(ctx BaseModuleContext) bool {
- clang := Bool(c.Properties.Clang)
-
- if c.Properties.Clang == nil {
- clang = true
- }
-
- if !c.toolchain(ctx).ClangSupported() {
- clang = false
- }
-
- return clang
-}
-
// Whether a module can link to another module, taking into
// account NDK linking.
func checkLinkType(ctx android.ModuleContext, from *Module, to *Module, tag dependencyTag) {
@@ -1059,12 +1423,12 @@
// Platform code can link to anything
return
}
- if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
- // These are always allowed
+ if from.inRecovery() {
+ // Recovery code is not NDK
return
}
- if _, ok := to.linker.(*ndkPrebuiltLibraryLinker); ok {
- // These are allowed, but they don't set sdk_version
+ if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
+ // These are always allowed
return
}
if _, ok := to.linker.(*ndkPrebuiltStlLinker); ok {
@@ -1076,10 +1440,18 @@
// the NDK.
return
}
+
+ if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Name() == "libc++" {
+ // Bug: http://b/121358700 - Allow libclang_rt.* shared libraries (with sdk_version)
+ // to link to libc++ (non-NDK and without sdk_version).
+ return
+ }
+
if String(to.Properties.Sdk_version) == "" {
// NDK code linking to platform code is never okay.
ctx.ModuleErrorf("depends on non-NDK-built library %q",
ctx.OtherModuleName(to))
+ return
}
// At this point we know we have two NDK libraries, but we need to
@@ -1087,31 +1459,32 @@
// API level, as it is only valid to link against older or equivalent
// APIs.
- if String(from.Properties.Sdk_version) == "current" {
- // Current can link against anything.
- return
- } else if String(to.Properties.Sdk_version) == "current" {
- // Current can't be linked against by anything else.
- ctx.ModuleErrorf("links %q built against newer API version %q",
- ctx.OtherModuleName(to), "current")
- }
+ // Current can link against anything.
+ if String(from.Properties.Sdk_version) != "current" {
+ // Otherwise we need to check.
+ if String(to.Properties.Sdk_version) == "current" {
+ // Current can't be linked against by anything else.
+ ctx.ModuleErrorf("links %q built against newer API version %q",
+ ctx.OtherModuleName(to), "current")
+ } else {
+ fromApi, err := strconv.Atoi(String(from.Properties.Sdk_version))
+ if err != nil {
+ ctx.PropertyErrorf("sdk_version",
+ "Invalid sdk_version value (must be int or current): %q",
+ String(from.Properties.Sdk_version))
+ }
+ toApi, err := strconv.Atoi(String(to.Properties.Sdk_version))
+ if err != nil {
+ ctx.PropertyErrorf("sdk_version",
+ "Invalid sdk_version value (must be int or current): %q",
+ String(to.Properties.Sdk_version))
+ }
- fromApi, err := strconv.Atoi(String(from.Properties.Sdk_version))
- if err != nil {
- ctx.PropertyErrorf("sdk_version",
- "Invalid sdk_version value (must be int): %q",
- String(from.Properties.Sdk_version))
- }
- toApi, err := strconv.Atoi(String(to.Properties.Sdk_version))
- if err != nil {
- ctx.PropertyErrorf("sdk_version",
- "Invalid sdk_version value (must be int): %q",
- String(to.Properties.Sdk_version))
- }
-
- if toApi > fromApi {
- ctx.ModuleErrorf("links %q built against newer API version %q",
- ctx.OtherModuleName(to), String(to.Properties.Sdk_version))
+ if toApi > fromApi {
+ ctx.ModuleErrorf("links %q built against newer API version %q",
+ ctx.OtherModuleName(to), String(to.Properties.Sdk_version))
+ }
+ }
}
// Also check that the two STL choices are compatible.
@@ -1119,23 +1492,60 @@
toStl := to.stl.Properties.SelectedStl
if fromStl == "" || toStl == "" {
// Libraries that don't use the STL are unrestricted.
- return
- }
-
- if fromStl == "ndk_system" || toStl == "ndk_system" {
+ } else if fromStl == "ndk_system" || toStl == "ndk_system" {
// We can be permissive with the system "STL" since it is only the C++
// ABI layer, but in the future we should make sure that everyone is
// using either libc++ or nothing.
- return
- }
-
- if getNdkStlFamily(ctx, from) != getNdkStlFamily(ctx, to) {
+ } else if getNdkStlFamily(from) != getNdkStlFamily(to) {
ctx.ModuleErrorf("uses %q and depends on %q which uses incompatible %q",
from.stl.Properties.SelectedStl, ctx.OtherModuleName(to),
to.stl.Properties.SelectedStl)
}
}
+// Tests whether the dependent library is okay to be double loaded inside a single process.
+// If a library has a vendor variant and is a (transitive) dependency of an LLNDK library,
+// it is subject to be double loaded. Such lib should be explicitly marked as double_loadable: true
+// or as vndk-sp (vndk: { enabled: true, support_system_process: true}).
+func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) {
+ check := func(child, parent android.Module) bool {
+ to, ok := child.(*Module)
+ if !ok {
+ // follow thru cc.Defaults, etc.
+ return true
+ }
+
+ if lib, ok := to.linker.(*libraryDecorator); !ok || !lib.shared() {
+ return false
+ }
+
+ // if target lib has no vendor variant, keep checking dependency graph
+ if !to.hasVendorVariant() {
+ return true
+ }
+
+ if to.isVndkSp() || inList(child.Name(), llndkLibraries) || Bool(to.VendorProperties.Double_loadable) {
+ return false
+ }
+
+ var stringPath []string
+ for _, m := range ctx.GetWalkPath() {
+ stringPath = append(stringPath, m.Name())
+ }
+ ctx.ModuleErrorf("links a library %q which is not LL-NDK, "+
+ "VNDK-SP, or explicitly marked as 'double_loadable:true'. "+
+ "(dependency: %s)", ctx.OtherModuleName(to), strings.Join(stringPath, " -> "))
+ return false
+ }
+ if module, ok := ctx.Module().(*Module); ok {
+ if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
+ if inList(ctx.ModuleName(), llndkLibraries) || Bool(module.VendorProperties.Double_loadable) {
+ ctx.WalkDeps(check)
+ }
+ }
+ }
+}
+
// Convert dependencies to paths. Returns a PathDeps containing paths
func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps
@@ -1151,8 +1561,6 @@
if ccDep == nil {
// handling for a few module types that aren't cc Module but that are also supported
switch depTag {
- case android.DefaultsDepTag, android.SourceDepTag:
- // Nothing to do
case genSourceDepTag:
if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
depPaths.GeneratedSources = append(depPaths.GeneratedSources,
@@ -1179,23 +1587,25 @@
} else {
ctx.ModuleErrorf("module %q is not a genrule", depName)
}
- case linkerScriptDepTag:
+ case linkerFlagsDepTag:
if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
files := genRule.GeneratedSourceFiles()
if len(files) == 1 {
- depPaths.LinkerScript = android.OptionalPathForPath(files[0])
+ depPaths.LinkerFlagsFile = android.OptionalPathForPath(files[0])
} else if len(files) > 1 {
- ctx.ModuleErrorf("module %q can only generate a single file if used for a linker script", depName)
+ ctx.ModuleErrorf("module %q can only generate a single file if used for a linker flag file", depName)
}
} else {
ctx.ModuleErrorf("module %q is not a genrule", depName)
}
- default:
- ctx.ModuleErrorf("depends on non-cc module %q", depName)
}
return
}
+ if depTag == android.ProtoPluginDepTag {
+ return
+ }
+
if dep.Target().Os != ctx.Os() {
ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
return
@@ -1216,7 +1626,64 @@
return
}
}
+
+ if depTag == staticVariantTag {
+ if _, ok := ccDep.compiler.(libraryInterface); ok {
+ c.staticVariant = ccDep
+ return
+ }
+ }
+
+ // Extract explicitlyVersioned field from the depTag and reset it inside the struct.
+ // Otherwise, sharedDepTag and lateSharedDepTag with explicitlyVersioned set to true
+ // won't be matched to sharedDepTag and lateSharedDepTag.
+ explicitlyVersioned := false
+ if t, ok := depTag.(dependencyTag); ok {
+ explicitlyVersioned = t.explicitlyVersioned
+ t.explicitlyVersioned = false
+ depTag = t
+ }
+
if t, ok := depTag.(dependencyTag); ok && t.library {
+ depIsStatic := false
+ switch depTag {
+ case staticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag:
+ depIsStatic = true
+ }
+ if dependentLibrary, ok := ccDep.linker.(*libraryDecorator); ok && !depIsStatic {
+ depIsStubs := dependentLibrary.buildStubs()
+ depHasStubs := ccDep.HasStubsVariants()
+ depInSameApex := android.DirectlyInApex(c.ApexName(), depName)
+ depInPlatform := !android.DirectlyInAnyApex(ctx, depName)
+
+ var useThisDep bool
+ if depIsStubs && explicitlyVersioned {
+ // Always respect dependency to the versioned stubs (i.e. libX#10)
+ useThisDep = true
+ } else if !depHasStubs {
+ // Use non-stub variant if that is the only choice
+ // (i.e. depending on a lib without stubs.version property)
+ useThisDep = true
+ } else if c.IsForPlatform() {
+ // If not building for APEX, use stubs only when it is from
+ // an APEX (and not from platform)
+ useThisDep = (depInPlatform != depIsStubs)
+ if c.inRecovery() || c.bootstrap() {
+ // However, for recovery or bootstrap modules,
+ // always link to non-stub variant
+ useThisDep = !depIsStubs
+ }
+ } else {
+ // If building for APEX, use stubs only when it is not from
+ // the same APEX
+ useThisDep = (depInSameApex != depIsStubs)
+ }
+
+ if !useThisDep {
+ return // stop processing this dep
+ }
+ }
+
if i, ok := ccDep.linker.(exportedFlagsProducer); ok {
flags := i.exportedFlags()
deps := i.exportedFlagsDeps()
@@ -1248,6 +1715,11 @@
depPtr = &depPaths.SharedLibsDeps
depFile = ccDep.linker.(libraryInterface).toc()
directSharedDeps = append(directSharedDeps, ccDep)
+ case earlySharedDepTag:
+ ptr = &depPaths.EarlySharedLibs
+ depPtr = &depPaths.EarlySharedLibsDeps
+ depFile = ccDep.linker.(libraryInterface).toc()
+ directSharedDeps = append(directSharedDeps, ccDep)
case lateSharedDepTag, ndkLateStubDepTag:
ptr = &depPaths.LateSharedLibs
depPtr = &depPaths.LateSharedLibsDeps
@@ -1281,6 +1753,8 @@
depPaths.CrtBegin = linkFile
case crtEndDepTag:
depPaths.CrtEnd = linkFile
+ case dynamicLinkerDepTag:
+ depPaths.DynamicLinker = linkFile
}
switch depTag {
@@ -1317,28 +1791,63 @@
*depPtr = append(*depPtr, dep.Path())
}
- // Export the shared libs to Make.
- switch depTag {
- case sharedDepTag, sharedExportDepTag, lateSharedDepTag:
+ makeLibName := func(depName string) string {
libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
libName = strings.TrimPrefix(libName, "prebuilt_")
isLLndk := inList(libName, llndkLibraries)
isVendorPublicLib := inList(libName, vendorPublicLibraries)
- var makeLibName string
bothVendorAndCoreVariantsExist := ccDep.hasVendorVariant() || isLLndk
- if c.useVndk() && bothVendorAndCoreVariantsExist {
+
+ if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.isVndk() && !ccDep.mustUseVendorVariant() {
+ // The vendor module is a no-vendor-variant VNDK library. Depend on the
+ // core module instead.
+ return libName
+ } else if c.useVndk() && bothVendorAndCoreVariantsExist {
// The vendor module in Make will have been renamed to not conflict with the core
// module, so update the dependency name here accordingly.
- makeLibName = libName + vendorSuffix
+ return libName + vendorSuffix
} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
- makeLibName = libName + vendorPublicLibrarySuffix
+ return libName + vendorPublicLibrarySuffix
+ } else if ccDep.inRecovery() && !ccDep.onlyInRecovery() {
+ return libName + recoverySuffix
} else {
- makeLibName = libName
+ return libName
}
+ }
+
+ // Export the shared libs to Make.
+ switch depTag {
+ case sharedDepTag, sharedExportDepTag, lateSharedDepTag, earlySharedDepTag:
+ if dependentLibrary, ok := ccDep.linker.(*libraryDecorator); ok {
+ if dependentLibrary.buildStubs() && android.InAnyApex(depName) {
+ // Add the dependency to the APEX(es) providing the library so that
+ // m <module> can trigger building the APEXes as well.
+ for _, an := range android.GetApexesForModule(depName) {
+ c.Properties.ApexesProvidingSharedLibs = append(
+ c.Properties.ApexesProvidingSharedLibs, an)
+ }
+ }
+ }
+
// Note: the order of libs in this list is not important because
// they merely serve as Make dependencies and do not affect this lib itself.
- c.Properties.AndroidMkSharedLibs = append(c.Properties.AndroidMkSharedLibs, makeLibName)
+ c.Properties.AndroidMkSharedLibs = append(
+ c.Properties.AndroidMkSharedLibs, makeLibName(depName))
+ case ndkStubDepTag, ndkLateStubDepTag:
+ ndkStub := ccDep.linker.(*stubDecorator)
+ c.Properties.AndroidMkSharedLibs = append(
+ c.Properties.AndroidMkSharedLibs,
+ depName+"."+ndkStub.properties.ApiLevel)
+ case staticDepTag, staticExportDepTag, lateStaticDepTag:
+ c.Properties.AndroidMkStaticLibs = append(
+ c.Properties.AndroidMkStaticLibs, makeLibName(depName))
+ case runtimeDepTag:
+ c.Properties.AndroidMkRuntimeLibs = append(
+ c.Properties.AndroidMkRuntimeLibs, makeLibName(depName))
+ case wholeStaticDepTag:
+ c.Properties.AndroidMkWholeStaticLibs = append(
+ c.Properties.AndroidMkWholeStaticLibs, makeLibName(depName))
}
})
@@ -1375,6 +1884,10 @@
return c.installer.inSanitizerDir()
}
+func (c *Module) InstallInRecovery() bool {
+ return c.inRecovery()
+}
+
func (c *Module) HostToolPath() android.OptionalPath {
if c.installer == nil {
return android.OptionalPath{}
@@ -1402,20 +1915,82 @@
return false
}
+func (c *Module) staticBinary() bool {
+ if static, ok := c.linker.(interface {
+ staticBinary() bool
+ }); ok {
+ return static.staticBinary()
+ }
+ return false
+}
+
+func (c *Module) getMakeLinkType() string {
+ if c.useVndk() {
+ if inList(c.Name(), vndkCoreLibraries) || inList(c.Name(), vndkSpLibraries) || inList(c.Name(), llndkLibraries) {
+ if inList(c.Name(), vndkPrivateLibraries) {
+ return "native:vndk_private"
+ } else {
+ return "native:vndk"
+ }
+ } else {
+ return "native:vendor"
+ }
+ } else if c.inRecovery() {
+ return "native:recovery"
+ } else if c.Target().Os == android.Android && String(c.Properties.Sdk_version) != "" {
+ return "native:ndk:none:none"
+ // TODO(b/114741097): use the correct ndk stl once build errors have been fixed
+ //family, link := getNdkStlFamilyAndLinkType(c)
+ //return fmt.Sprintf("native:ndk:%s:%s", family, link)
+ } else if inList(c.Name(), vndkUsingCoreVariantLibraries) {
+ return "native:platform_vndk"
+ } else {
+ return "native:platform"
+ }
+}
+
+// Overrides ApexModule.IsInstallabeToApex()
+// Only shared libraries are installable to APEX.
+func (c *Module) IsInstallableToApex() bool {
+ if shared, ok := c.linker.(interface {
+ shared() bool
+ }); ok {
+ return shared.shared()
+ }
+ return false
+}
+
+func (c *Module) imageVariation() string {
+ variation := "core"
+ if c.useVndk() {
+ variation = "vendor"
+ } else if c.inRecovery() {
+ variation = "recovery"
+ }
+ return variation
+}
+
+func (c *Module) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Srcs = append(dpInfo.Srcs, c.Srcs().Strings()...)
+}
+
//
// Defaults
//
type Defaults struct {
android.ModuleBase
android.DefaultsModuleBase
+ android.ApexModuleBase
}
func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
-func (d *Defaults) DepsMutator(ctx android.BottomUpMutatorContext) {
-}
-
+// cc_defaults provides a set of properties that can be inherited by other cc
+// modules. A module can use the properties from a cc_defaults using
+// `defaults: ["<:default_module_name>"]`. Properties of both modules are
+// merged (when possible) by prepending the default module's values to the
+// depending module's values.
func defaultsFactory() android.Module {
return DefaultsFactory()
}
@@ -1434,7 +2009,6 @@
&BinaryLinkerProperties{},
&TestProperties{},
&TestBinaryProperties{},
- &UnusedProperties{},
&StlProperties{},
&SanitizeProperties{},
&StripProperties{},
@@ -1445,10 +2019,12 @@
&VndkProperties{},
<OProperties{},
&PgoProperties{},
+ &XomProperties{},
&android.ProtoProperties{},
)
android.InitDefaultsModule(module)
+ android.InitApexModule(module)
return module
}
@@ -1461,6 +2037,8 @@
// vendorMode is the variant used for /vendor code that compiles
// against the VNDK.
vendorMode = "vendor"
+
+ recoveryMode = "recovery"
)
func squashVendorSrcs(m *Module) {
@@ -1473,21 +2051,64 @@
}
}
-func vendorMutator(mctx android.BottomUpMutatorContext) {
+func squashRecoverySrcs(m *Module) {
+ if lib, ok := m.compiler.(*libraryDecorator); ok {
+ lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+ lib.baseCompiler.Properties.Target.Recovery.Srcs...)
+
+ lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+ lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+ }
+}
+
+func ImageMutator(mctx android.BottomUpMutatorContext) {
if mctx.Os() != android.Android {
return
}
- if genrule, ok := mctx.Module().(*genrule.Module); ok {
- if props, ok := genrule.Extra.(*VendorProperties); ok {
+ if g, ok := mctx.Module().(*genrule.Module); ok {
+ if props, ok := g.Extra.(*GenruleExtraProperties); ok {
+ var coreVariantNeeded bool = false
+ var vendorVariantNeeded bool = false
+ var recoveryVariantNeeded bool = false
if mctx.DeviceConfig().VndkVersion() == "" {
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
} else if Bool(props.Vendor_available) {
- mctx.CreateVariations(coreMode, vendorMode)
+ coreVariantNeeded = true
+ vendorVariantNeeded = true
} else if mctx.SocSpecific() || mctx.DeviceSpecific() {
- mctx.CreateVariations(vendorMode)
+ vendorVariantNeeded = true
} else {
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
+ }
+ if Bool(props.Recovery_available) {
+ recoveryVariantNeeded = true
+ }
+
+ if recoveryVariantNeeded {
+ primaryArch := mctx.Config().DevicePrimaryArchType()
+ moduleArch := g.Target().Arch.ArchType
+ if moduleArch != primaryArch {
+ recoveryVariantNeeded = false
+ }
+ }
+
+ var variants []string
+ if coreVariantNeeded {
+ variants = append(variants, coreMode)
+ }
+ if vendorVariantNeeded {
+ variants = append(variants, vendorMode)
+ }
+ if recoveryVariantNeeded {
+ variants = append(variants, recoveryMode)
+ }
+ mod := mctx.CreateVariations(variants...)
+ for i, v := range variants {
+ if v == recoveryMode {
+ m := mod[i].(*genrule.Module)
+ m.Extra.(*GenruleExtraProperties).InRecovery = true
+ }
}
}
}
@@ -1499,6 +2120,7 @@
// Sanity check
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+ productSpecific := mctx.ProductSpecific()
if m.VendorProperties.Vendor_available != nil && vendorSpecific {
mctx.PropertyErrorf("vendor_available",
@@ -1508,6 +2130,11 @@
if vndkdep := m.vndkdep; vndkdep != nil {
if vndkdep.isVndk() {
+ if productSpecific {
+ mctx.PropertyErrorf("product_specific",
+ "product_specific must not be true when `vndk: {enabled: true}`")
+ return
+ }
if vendorSpecific {
if !vndkdep.isVndkExt() {
mctx.PropertyErrorf("vndk",
@@ -1542,43 +2169,79 @@
}
}
+ var coreVariantNeeded bool = false
+ var vendorVariantNeeded bool = false
+ var recoveryVariantNeeded bool = false
+
if mctx.DeviceConfig().VndkVersion() == "" {
// If the device isn't compiling against the VNDK, we always
// use the core mode.
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
} else if _, ok := m.linker.(*llndkStubDecorator); ok {
// LL-NDK stubs only exist in the vendor variant, since the
// real libraries will be used in the core variant.
- mctx.CreateVariations(vendorMode)
+ vendorVariantNeeded = true
} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
// ... and LL-NDK headers as well
- mod := mctx.CreateVariations(vendorMode)
- vendor := mod[0].(*Module)
- vendor.Properties.UseVndk = true
+ vendorVariantNeeded = true
} else if _, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
// PRODUCT_EXTRA_VNDK_VERSIONS.
- mod := mctx.CreateVariations(vendorMode)
- vendor := mod[0].(*Module)
- vendor.Properties.UseVndk = true
+ vendorVariantNeeded = true
} else if m.hasVendorVariant() && !vendorSpecific {
// This will be available in both /system and /vendor
// or a /system directory that is available to vendor.
- mod := mctx.CreateVariations(coreMode, vendorMode)
- vendor := mod[1].(*Module)
- vendor.Properties.UseVndk = true
- squashVendorSrcs(vendor)
+ coreVariantNeeded = true
+ vendorVariantNeeded = true
} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
// This will be available in /vendor (or /odm) only
- mod := mctx.CreateVariations(vendorMode)
- vendor := mod[0].(*Module)
- vendor.Properties.UseVndk = true
- squashVendorSrcs(vendor)
+ vendorVariantNeeded = true
} else {
// This is either in /system (or similar: /data), or is a
// modules built with the NDK. Modules built with the NDK
// will be restricted using the existing link type checks.
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
+ }
+
+ if Bool(m.Properties.Recovery_available) {
+ recoveryVariantNeeded = true
+ }
+
+ if m.ModuleBase.InstallInRecovery() {
+ recoveryVariantNeeded = true
+ coreVariantNeeded = false
+ }
+
+ if recoveryVariantNeeded {
+ primaryArch := mctx.Config().DevicePrimaryArchType()
+ moduleArch := m.Target().Arch.ArchType
+ if moduleArch != primaryArch {
+ recoveryVariantNeeded = false
+ }
+ }
+
+ var variants []string
+ if coreVariantNeeded {
+ variants = append(variants, coreMode)
+ }
+ if vendorVariantNeeded {
+ variants = append(variants, vendorMode)
+ }
+ if recoveryVariantNeeded {
+ variants = append(variants, recoveryMode)
+ }
+ mod := mctx.CreateVariations(variants...)
+ for i, v := range variants {
+ if v == vendorMode {
+ m := mod[i].(*Module)
+ m.Properties.UseVndk = true
+ squashVendorSrcs(m)
+ } else if v == recoveryMode {
+ m := mod[i].(*Module)
+ m.Properties.InRecovery = true
+ m.MakeAsPlatform()
+ squashRecoverySrcs(m)
+ }
}
}
@@ -1590,6 +2253,7 @@
}
var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
var BoolPtr = proptools.BoolPtr
var String = proptools.String
var StringPtr = proptools.StringPtr
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 10c1aba..678f90d 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -16,7 +16,6 @@
import (
"android/soong/android"
- "android/soong/genrule"
"fmt"
"io/ioutil"
@@ -52,130 +51,64 @@
os.Exit(run())
}
-func createTestContext(t *testing.T, config android.Config, bp string) *android.TestContext {
+func createTestContext(t *testing.T, config android.Config, bp string, fs map[string][]byte,
+ os android.OsType) *android.TestContext {
+
ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(BinaryFactory))
+ ctx.RegisterModuleType("cc_binary_host", android.ModuleFactoryAdaptor(binaryHostFactory))
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(LibraryFactory))
ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(LibrarySharedFactory))
+ ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(LibraryStaticFactory))
ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(LibraryHeaderFactory))
- ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(toolchainLibraryFactory))
- ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(llndkLibraryFactory))
+ ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(ToolchainLibraryFactory))
+ ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(LlndkLibraryFactory))
ctx.RegisterModuleType("llndk_headers", android.ModuleFactoryAdaptor(llndkHeadersFactory))
ctx.RegisterModuleType("vendor_public_library", android.ModuleFactoryAdaptor(vendorPublicLibraryFactory))
- ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(objectFactory))
- ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(genrule.FileGroupFactory))
+ ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(ObjectFactory))
+ ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("image", vendorMutator).Parallel()
- ctx.BottomUp("link", linkageMutator).Parallel()
- ctx.BottomUp("vndk", vndkMutator).Parallel()
- ctx.BottomUp("begin", beginMutator).Parallel()
+ ctx.BottomUp("image", ImageMutator).Parallel()
+ ctx.BottomUp("link", LinkageMutator).Parallel()
+ ctx.BottomUp("vndk", VndkMutator).Parallel()
+ ctx.BottomUp("version", VersionMutator).Parallel()
+ ctx.BottomUp("begin", BeginMutator).Parallel()
})
- ctx.Register()
+ ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
+ })
// add some modules that are required by the compiler and/or linker
- bp = bp + `
- toolchain_library {
- name: "libatomic",
- vendor_available: true,
- }
+ bp = bp + GatherRequiredDepsForTest(os)
- toolchain_library {
- name: "libcompiler_rt-extras",
- vendor_available: true,
- }
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "foo.c": nil,
+ "bar.c": nil,
+ "a.proto": nil,
+ "b.aidl": nil,
+ "my_include": nil,
+ "foo.map.txt": nil,
+ "liba.so": nil,
+ }
- toolchain_library {
- name: "libgcc",
- vendor_available: true,
- }
+ for k, v := range fs {
+ mockFS[k] = v
+ }
- cc_library {
- name: "libc",
- no_libgcc: true,
- nocrt: true,
- system_shared_libs: [],
- }
- llndk_library {
- name: "libc",
- symbol_file: "",
- }
- cc_library {
- name: "libm",
- no_libgcc: true,
- nocrt: true,
- system_shared_libs: [],
- }
- llndk_library {
- name: "libm",
- symbol_file: "",
- }
- cc_library {
- name: "libdl",
- no_libgcc: true,
- nocrt: true,
- system_shared_libs: [],
- }
- llndk_library {
- name: "libdl",
- symbol_file: "",
- }
- cc_library {
- name: "libc++_static",
- no_libgcc: true,
- nocrt: true,
- system_shared_libs: [],
- stl: "none",
- vendor_available: true,
- }
- cc_library {
- name: "libc++",
- no_libgcc: true,
- nocrt: true,
- system_shared_libs: [],
- stl: "none",
- vendor_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
- }
- cc_library {
- name: "libunwind_llvm",
- no_libgcc: true,
- nocrt: true,
- system_shared_libs: [],
- stl: "none",
- vendor_available: true,
- }
-
- cc_object {
- name: "crtbegin_so",
- }
-
- cc_object {
- name: "crtend_so",
- }
-
- cc_library {
- name: "libprotobuf-cpp-lite",
- }
-
-`
-
- ctx.MockFileSystem(map[string][]byte{
- "Android.bp": []byte(bp),
- "foo.c": nil,
- "bar.c": nil,
- "a.proto": nil,
- "b.aidl": nil,
- "my_include": nil,
- })
+ ctx.MockFileSystem(mockFS)
return ctx
}
func testCcWithConfig(t *testing.T, bp string, config android.Config) *android.TestContext {
+ return testCcWithConfigForOs(t, bp, config, android.Android)
+}
+
+func testCcWithConfigForOs(t *testing.T, bp string, config android.Config, os android.OsType) *android.TestContext {
t.Helper()
- ctx := createTestContext(t, config, bp)
+ ctx := createTestContext(t, config, bp, nil, os)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
@@ -208,7 +141,8 @@
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := createTestContext(t, config, bp)
+ ctx := createTestContext(t, config, bp, nil, android.Android)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
if len(errs) > 0 {
@@ -226,10 +160,74 @@
}
const (
- coreVariant = "android_arm64_armv8-a_core_shared"
- vendorVariant = "android_arm64_armv8-a_vendor_shared"
+ coreVariant = "android_arm64_armv8-a_core_shared"
+ vendorVariant = "android_arm64_armv8-a_vendor_shared"
+ recoveryVariant = "android_arm64_armv8-a_recovery_shared"
)
+func TestFuchsiaDeps(t *testing.T) {
+ t.Helper()
+
+ bp := `
+ cc_library {
+ name: "libTest",
+ srcs: ["foo.c"],
+ target: {
+ fuchsia: {
+ srcs: ["bar.c"],
+ },
+ },
+ }`
+
+ config := android.TestArchConfigFuchsia(buildDir, nil)
+ ctx := testCcWithConfigForOs(t, bp, config, android.Fuchsia)
+
+ rt := false
+ fb := false
+
+ ld := ctx.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
+ implicits := ld.Implicits
+ for _, lib := range implicits {
+ if strings.Contains(lib.Rel(), "libcompiler_rt") {
+ rt = true
+ }
+
+ if strings.Contains(lib.Rel(), "libbioniccompat") {
+ fb = true
+ }
+ }
+
+ if !rt || !fb {
+ t.Errorf("fuchsia libs must link libcompiler_rt and libbioniccompat")
+ }
+}
+
+func TestFuchsiaTargetDecl(t *testing.T) {
+ t.Helper()
+
+ bp := `
+ cc_library {
+ name: "libTest",
+ srcs: ["foo.c"],
+ target: {
+ fuchsia: {
+ srcs: ["bar.c"],
+ },
+ },
+ }`
+
+ config := android.TestArchConfigFuchsia(buildDir, nil)
+ ctx := testCcWithConfigForOs(t, bp, config, android.Fuchsia)
+ ld := ctx.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
+ var objs []string
+ for _, o := range ld.Inputs {
+ objs = append(objs, o.Base())
+ }
+ if len(objs) != 2 || objs[0] != "foo.o" || objs[1] != "bar.o" {
+ t.Errorf("inputs of libTest must be []string{\"foo.o\", \"bar.o\"}, but was %#v.", objs)
+ }
+}
+
func TestVendorSrc(t *testing.T) {
ctx := testCc(t, `
cc_library {
@@ -444,6 +442,324 @@
nocrt: true,
}
`)
+
+ // Check whether an error is emitted when a VNDK lib depends on a non-VNDK lib.
+ testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ shared_libs: ["libnonvndk"],
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libnonvndk",
+ vendor_available: true,
+ nocrt: true,
+ }
+ `)
+
+ // Check whether an error is emitted when a VNDK-private lib depends on a non-VNDK lib.
+ testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
+ cc_library {
+ name: "libvndkprivate",
+ vendor_available: false,
+ vndk: {
+ enabled: true,
+ },
+ shared_libs: ["libnonvndk"],
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libnonvndk",
+ vendor_available: true,
+ nocrt: true,
+ }
+ `)
+
+ // Check whether an error is emitted when a VNDK-sp lib depends on a non-VNDK lib.
+ testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
+ cc_library {
+ name: "libvndksp",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ shared_libs: ["libnonvndk"],
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libnonvndk",
+ vendor_available: true,
+ nocrt: true,
+ }
+ `)
+
+ // Check whether an error is emitted when a VNDK-sp-private lib depends on a non-VNDK lib.
+ testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
+ cc_library {
+ name: "libvndkspprivate",
+ vendor_available: false,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ shared_libs: ["libnonvndk"],
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libnonvndk",
+ vendor_available: true,
+ nocrt: true,
+ }
+ `)
+}
+
+func TestDoubleLoadbleDep(t *testing.T) {
+ // okay to link : LLNDK -> double_loadable VNDK
+ testCc(t, `
+ cc_library {
+ name: "libllndk",
+ shared_libs: ["libdoubleloadable"],
+ }
+
+ llndk_library {
+ name: "libllndk",
+ symbol_file: "",
+ }
+
+ cc_library {
+ name: "libdoubleloadable",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ }
+ `)
+ // okay to link : LLNDK -> VNDK-SP
+ testCc(t, `
+ cc_library {
+ name: "libllndk",
+ shared_libs: ["libvndksp"],
+ }
+
+ llndk_library {
+ name: "libllndk",
+ symbol_file: "",
+ }
+
+ cc_library {
+ name: "libvndksp",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ }
+ `)
+ // okay to link : double_loadable -> double_loadable
+ testCc(t, `
+ cc_library {
+ name: "libdoubleloadable1",
+ shared_libs: ["libdoubleloadable2"],
+ vendor_available: true,
+ double_loadable: true,
+ }
+
+ cc_library {
+ name: "libdoubleloadable2",
+ vendor_available: true,
+ double_loadable: true,
+ }
+ `)
+ // okay to link : double_loadable VNDK -> double_loadable VNDK private
+ testCc(t, `
+ cc_library {
+ name: "libdoubleloadable",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ shared_libs: ["libnondoubleloadable"],
+ }
+
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: false,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ }
+ `)
+ // okay to link : LLNDK -> core-only -> vendor_available & double_loadable
+ testCc(t, `
+ cc_library {
+ name: "libllndk",
+ shared_libs: ["libcoreonly"],
+ }
+
+ llndk_library {
+ name: "libllndk",
+ symbol_file: "",
+ }
+
+ cc_library {
+ name: "libcoreonly",
+ shared_libs: ["libvendoravailable"],
+ }
+
+ // indirect dependency of LLNDK
+ cc_library {
+ name: "libvendoravailable",
+ vendor_available: true,
+ double_loadable: true,
+ }
+ `)
+}
+
+func TestDoubleLoadableDepError(t *testing.T) {
+ // Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
+ testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
+ cc_library {
+ name: "libllndk",
+ shared_libs: ["libnondoubleloadable"],
+ }
+
+ llndk_library {
+ name: "libllndk",
+ symbol_file: "",
+ }
+
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ }
+ `)
+
+ // Check whether an error is emitted when a LLNDK depends on a non-double_loadable vendor_available lib.
+ testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
+ cc_library {
+ name: "libllndk",
+ no_libgcc: true,
+ shared_libs: ["libnondoubleloadable"],
+ }
+
+ llndk_library {
+ name: "libllndk",
+ symbol_file: "",
+ }
+
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: true,
+ }
+ `)
+
+ // Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable vendor_available lib.
+ testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
+ cc_library {
+ name: "libdoubleloadable",
+ vendor_available: true,
+ double_loadable: true,
+ shared_libs: ["libnondoubleloadable"],
+ }
+
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: true,
+ }
+ `)
+
+ // Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable VNDK lib.
+ testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
+ cc_library {
+ name: "libdoubleloadable",
+ vendor_available: true,
+ double_loadable: true,
+ shared_libs: ["libnondoubleloadable"],
+ }
+
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ }
+ `)
+
+ // Check whether an error is emitted when a double_loadable VNDK depends on a non-double_loadable VNDK private lib.
+ testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
+ cc_library {
+ name: "libdoubleloadable",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ shared_libs: ["libnondoubleloadable"],
+ }
+
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: false,
+ vndk: {
+ enabled: true,
+ },
+ }
+ `)
+
+ // Check whether an error is emitted when a LLNDK depends on a non-double_loadable indirectly.
+ testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
+ cc_library {
+ name: "libllndk",
+ shared_libs: ["libcoreonly"],
+ }
+
+ llndk_library {
+ name: "libllndk",
+ symbol_file: "",
+ }
+
+ cc_library {
+ name: "libcoreonly",
+ shared_libs: ["libvendoravailable"],
+ }
+
+ // indirect dependency of LLNDK
+ cc_library {
+ name: "libvendoravailable",
+ vendor_available: true,
+ }
+ `)
+}
+
+func TestVndkMustNotBeProductSpecific(t *testing.T) {
+ // Check whether an error is emitted when a vndk lib has 'product_specific: true'.
+ testCcError(t, "product_specific must not be true when `vndk: {enabled: true}`", `
+ cc_library {
+ name: "libvndk",
+ product_specific: true, // Cause error
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+ `)
}
func TestVndkExt(t *testing.T) {
@@ -848,9 +1164,7 @@
}
`)
- // The pattern should be "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\""
- // but target.vendor.shared_libs has not been supported yet.
- testCcError(t, "unrecognized property \"target.vendor.shared_libs\"", `
+ testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
cc_library {
name: "libvndk",
vendor_available: true,
@@ -919,9 +1233,7 @@
}
`)
- // The pattern should be "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\""
- // but target.vendor.shared_libs has not been supported yet.
- testCcError(t, "unrecognized property \"target.vendor.shared_libs\"", `
+ testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
cc_library {
name: "libvndk_sp",
vendor_available: true,
@@ -1044,7 +1356,7 @@
func TestSplitListForSize(t *testing.T) {
for _, testCase := range splitListForSizeTestCases {
- out, _ := splitListForSize(android.PathsForTesting(testCase.in), testCase.size)
+ out, _ := splitListForSize(android.PathsForTesting(testCase.in...), testCase.size)
var outStrings [][]string
@@ -1389,6 +1701,157 @@
}
}
+func checkRuntimeLibs(t *testing.T, expected []string, module *Module) {
+ actual := module.Properties.AndroidMkRuntimeLibs
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("incorrect runtime_libs for shared libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ actual,
+ expected,
+ )
+ }
+}
+
+const runtimeLibAndroidBp = `
+ cc_library {
+ name: "libvendor_available1",
+ vendor_available: true,
+ no_libgcc : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libvendor_available2",
+ vendor_available: true,
+ runtime_libs: ["libvendor_available1"],
+ no_libgcc : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libvendor_available3",
+ vendor_available: true,
+ runtime_libs: ["libvendor_available1"],
+ target: {
+ vendor: {
+ exclude_runtime_libs: ["libvendor_available1"],
+ }
+ },
+ no_libgcc : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libcore",
+ runtime_libs: ["libvendor_available1"],
+ no_libgcc : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libvendor1",
+ vendor: true,
+ no_libgcc : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libvendor2",
+ vendor: true,
+ runtime_libs: ["libvendor_available1", "libvendor1"],
+ no_libgcc : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+`
+
+func TestRuntimeLibs(t *testing.T) {
+ ctx := testCc(t, runtimeLibAndroidBp)
+
+ // runtime_libs for core variants use the module names without suffixes.
+ variant := "android_arm64_armv8-a_core_shared"
+
+ module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+
+ module = ctx.ModuleForTests("libcore", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+
+ // runtime_libs for vendor variants have '.vendor' suffixes if the modules have both core
+ // and vendor variants.
+ variant = "android_arm64_armv8-a_vendor_shared"
+
+ module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"libvendor_available1.vendor"}, module)
+
+ module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"libvendor_available1.vendor", "libvendor1"}, module)
+}
+
+func TestExcludeRuntimeLibs(t *testing.T) {
+ ctx := testCc(t, runtimeLibAndroidBp)
+
+ variant := "android_arm64_armv8-a_core_shared"
+ module := ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+
+ variant = "android_arm64_armv8-a_vendor_shared"
+ module = ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
+ checkRuntimeLibs(t, nil, module)
+}
+
+func TestRuntimeLibsNoVndk(t *testing.T) {
+ ctx := testCcNoVndk(t, runtimeLibAndroidBp)
+
+ // If DeviceVndkVersion is not defined, then runtime_libs are copied as-is.
+
+ variant := "android_arm64_armv8-a_core_shared"
+
+ module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+
+ module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"libvendor_available1", "libvendor1"}, module)
+}
+
+func checkStaticLibs(t *testing.T, expected []string, module *Module) {
+ actual := module.Properties.AndroidMkStaticLibs
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("incorrect static_libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ actual,
+ expected,
+ )
+ }
+}
+
+const staticLibAndroidBp = `
+ cc_library {
+ name: "lib1",
+ }
+ cc_library {
+ name: "lib2",
+ static_libs: ["lib1"],
+ }
+`
+
+func TestStaticLibDepExport(t *testing.T) {
+ ctx := testCc(t, staticLibAndroidBp)
+
+ // Check the shared version of lib2.
+ variant := "android_arm64_armv8-a_core_shared"
+ module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
+ checkStaticLibs(t, []string{"lib1", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+
+ // Check the static version of lib2.
+ variant = "android_arm64_armv8-a_core_static"
+ module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
+ // libc++_static is linked additionally.
+ checkStaticLibs(t, []string{"lib1", "libc++_static", "libclang_rt.builtins-aarch64-android", "libatomic", "libgcc_stripped"}, module)
+}
+
var compilerFlagsTestCases = []struct {
in string
out bool
@@ -1538,3 +2001,167 @@
}
}
+
+func TestRecovery(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "librecovery",
+ recovery: true,
+ }
+ cc_library_shared {
+ name: "librecovery32",
+ recovery: true,
+ compile_multilib:"32",
+ }
+ cc_library_shared {
+ name: "libHalInRecovery",
+ recovery_available: true,
+ vendor: true,
+ }
+ `)
+
+ variants := ctx.ModuleVariantsForTests("librecovery")
+ const arm64 = "android_arm64_armv8-a_recovery_shared"
+ if len(variants) != 1 || !android.InList(arm64, variants) {
+ t.Errorf("variants of librecovery must be \"%s\" only, but was %#v", arm64, variants)
+ }
+
+ variants = ctx.ModuleVariantsForTests("librecovery32")
+ if android.InList(arm64, variants) {
+ t.Errorf("multilib was set to 32 for librecovery32, but its variants has %s.", arm64)
+ }
+
+ recoveryModule := ctx.ModuleForTests("libHalInRecovery", recoveryVariant).Module().(*Module)
+ if !recoveryModule.Platform() {
+ t.Errorf("recovery variant of libHalInRecovery must not specific to device, soc, or product")
+ }
+}
+
+func TestVersionedStubs(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libFoo",
+ srcs: ["foo.c"],
+ stubs: {
+ symbol_file: "foo.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ cc_library_shared {
+ name: "libBar",
+ srcs: ["bar.c"],
+ shared_libs: ["libFoo#1"],
+ }`)
+
+ variants := ctx.ModuleVariantsForTests("libFoo")
+ expectedVariants := []string{
+ "android_arm64_armv8-a_core_shared",
+ "android_arm64_armv8-a_core_shared_1",
+ "android_arm64_armv8-a_core_shared_2",
+ "android_arm64_armv8-a_core_shared_3",
+ "android_arm_armv7-a-neon_core_shared",
+ "android_arm_armv7-a-neon_core_shared_1",
+ "android_arm_armv7-a-neon_core_shared_2",
+ "android_arm_armv7-a-neon_core_shared_3",
+ }
+ variantsMismatch := false
+ if len(variants) != len(expectedVariants) {
+ variantsMismatch = true
+ } else {
+ for _, v := range expectedVariants {
+ if !inList(v, variants) {
+ variantsMismatch = false
+ }
+ }
+ }
+ if variantsMismatch {
+ t.Errorf("variants of libFoo expected:\n")
+ for _, v := range expectedVariants {
+ t.Errorf("%q\n", v)
+ }
+ t.Errorf(", but got:\n")
+ for _, v := range variants {
+ t.Errorf("%q\n", v)
+ }
+ }
+
+ libBarLinkRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_core_shared").Rule("ld")
+ libFlags := libBarLinkRule.Args["libFlags"]
+ libFoo1StubPath := "libFoo/android_arm64_armv8-a_core_shared_1/libFoo.so"
+ if !strings.Contains(libFlags, libFoo1StubPath) {
+ t.Errorf("%q is not found in %q", libFoo1StubPath, libFlags)
+ }
+
+ libBarCompileRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_core_shared").Rule("cc")
+ cFlags := libBarCompileRule.Args["cFlags"]
+ libFoo1VersioningMacro := "-D__LIBFOO_API__=1"
+ if !strings.Contains(cFlags, libFoo1VersioningMacro) {
+ t.Errorf("%q is not found in %q", libFoo1VersioningMacro, cFlags)
+ }
+}
+
+func TestStaticExecutable(t *testing.T) {
+ ctx := testCc(t, `
+ cc_binary {
+ name: "static_test",
+ srcs: ["foo.c"],
+ static_executable: true,
+ }`)
+
+ variant := "android_arm64_armv8-a_core"
+ binModuleRule := ctx.ModuleForTests("static_test", variant).Rule("ld")
+ libFlags := binModuleRule.Args["libFlags"]
+ systemStaticLibs := []string{"libc.a", "libm.a", "libdl.a"}
+ for _, lib := range systemStaticLibs {
+ if !strings.Contains(libFlags, lib) {
+ t.Errorf("Static lib %q was not found in %q", lib, libFlags)
+ }
+ }
+ systemSharedLibs := []string{"libc.so", "libm.so", "libdl.so"}
+ for _, lib := range systemSharedLibs {
+ if strings.Contains(libFlags, lib) {
+ t.Errorf("Shared lib %q was found in %q", lib, libFlags)
+ }
+ }
+}
+
+func TestStaticDepsOrderWithStubs(t *testing.T) {
+ ctx := testCc(t, `
+ cc_binary {
+ name: "mybin",
+ srcs: ["foo.c"],
+ static_libs: ["libB"],
+ static_executable: true,
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libB",
+ srcs: ["foo.c"],
+ shared_libs: ["libC"],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libC",
+ srcs: ["foo.c"],
+ stl: "none",
+ stubs: {
+ versions: ["1"],
+ },
+ }`)
+
+ mybin := ctx.ModuleForTests("mybin", "android_arm64_armv8-a_core").Module().(*Module)
+ actual := mybin.depsInLinkOrder
+ expected := getOutputPaths(ctx, "android_arm64_armv8-a_core_static", []string{"libB", "libC"})
+
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("staticDeps orderings were not propagated correctly"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ actual,
+ expected,
+ )
+ }
+}
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index c25578e..7b4f89b 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -18,7 +18,6 @@
"fmt"
"android/soong/android"
- "android/soong/cc/config"
"os"
"path"
"path/filepath"
@@ -62,10 +61,14 @@
outputDebugInfo = (getEnvVariable(envVariableGenerateDebugInfo, ctx) == envVariableTrue)
+ // Track which projects have already had CMakeLists.txt generated to keep the first
+ // variant for each project.
+ seenProjects := map[string]bool{}
+
ctx.VisitAllModules(func(module android.Module) {
if ccModule, ok := module.(*Module); ok {
if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
- generateCLionProject(compiledModule, ctx, ccModule)
+ generateCLionProject(compiledModule, ctx, ccModule, seenProjects)
}
}
})
@@ -114,14 +117,22 @@
return nil
}
-func generateCLionProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module) {
+func generateCLionProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module,
+ seenProjects map[string]bool) {
srcs := compiledModule.Srcs()
if len(srcs) == 0 {
return
}
- // Ensure the directory hosting the cmakelists.txt exists
+ // Only write CMakeLists.txt for the first variant of each architecture of each module
clionproject_location := getCMakeListsForModule(ccModule, ctx)
+ if seenProjects[clionproject_location] {
+ return
+ }
+
+ seenProjects[clionproject_location] = true
+
+ // Ensure the directory hosting the cmakelists.txt exists
projectDir := path.Dir(clionproject_location)
os.MkdirAll(projectDir, os.ModePerm)
@@ -138,18 +149,10 @@
f.WriteString(fmt.Sprintf("project(%s)\n", ccModule.ModuleBase.Name()))
f.WriteString(fmt.Sprintf("set(ANDROID_ROOT %s)\n\n", getAndroidSrcRootDirectory(ctx)))
- if ccModule.flags.Clang {
- pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
- f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang"))
- f.WriteString(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang++"))
- } else {
- toolchain := config.FindToolchain(ccModule.Os(), ccModule.Arch())
- root, _ := evalVariable(ctx, toolchain.GccRoot())
- triple, _ := evalVariable(ctx, toolchain.GccTriple())
- pathToCC := filepath.Join(root, "bin", triple+"-")
- f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "gcc"))
- f.WriteString(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "g++"))
- }
+ pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
+ f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang"))
+ f.WriteString(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang++"))
+
// Add all sources to the project.
f.WriteString("list(APPEND\n")
f.WriteString(" SOURCE_FILES\n")
@@ -192,10 +195,11 @@
writeAllIncludeDirectories(c.systemHeaderSearchPath, f, true)
writeAllIncludeDirectories(c.headerSearchPath, f, false)
if cflags {
+ writeAllRelativeFilePathFlags(c.relativeFilePathFlags, f, "CMAKE_C_FLAGS")
writeAllFlags(c.flags, f, "CMAKE_C_FLAGS")
}
-
if cppflags {
+ writeAllRelativeFilePathFlags(c.relativeFilePathFlags, f, "CMAKE_CXX_FLAGS")
writeAllFlags(c.flags, f, "CMAKE_CXX_FLAGS")
}
if c.sysroot != "" {
@@ -237,6 +241,17 @@
f.WriteString("list (APPEND SOURCE_FILES ${TMP_HEADERS})\n\n")
}
+type relativeFilePathFlagType struct {
+ flag string
+ relativeFilePath string
+}
+
+func writeAllRelativeFilePathFlags(relativeFilePathFlags []relativeFilePathFlagType, f *os.File, tag string) {
+ for _, flag := range relativeFilePathFlags {
+ f.WriteString(fmt.Sprintf("set(%s \"${%s} %s=%s\")\n", tag, tag, flag.flag, buildCMakePath(flag.relativeFilePath)))
+ }
+}
+
func writeAllFlags(flags []string, f *os.File, tag string) {
for _, flag := range flags {
f.WriteString(fmt.Sprintf("set(%s \"${%s} %s\")\n", tag, tag, flag))
@@ -251,6 +266,7 @@
systemHeaderSearchPath
flag
systemRoot
+ relativeFilePathFlag
)
type compilerParameters struct {
@@ -258,6 +274,8 @@
systemHeaderSearchPath []string
flags []string
sysroot string
+ // Must be in a=b/c/d format and can be split into "a" and "b/c/d"
+ relativeFilePathFlags []relativeFilePathFlagType
}
func makeCompilerParameters() compilerParameters {
@@ -282,6 +300,9 @@
if strings.HasPrefix(parameter, "--sysroot") {
return systemRoot
}
+ if strings.HasPrefix(parameter, "-fsanitize-blacklist") {
+ return relativeFilePathFlag
+ }
return flag
}
@@ -335,6 +356,16 @@
f.WriteString("# Found a system root path marker with no path")
}
i = i + 1
+ case relativeFilePathFlag:
+ flagComponents := strings.Split(param, "=")
+ if len(flagComponents) == 2 {
+ flagStruct := relativeFilePathFlagType{flag: flagComponents[0], relativeFilePath: flagComponents[1]}
+ compilerParameters.relativeFilePathFlags = append(compilerParameters.relativeFilePathFlags, flagStruct)
+ } else {
+ if outputDebugInfo {
+ f.WriteString(fmt.Sprintf("# Relative File Path Flag [%s] is not formatted as a=b/c/d \n", param))
+ }
+ }
}
}
return compilerParameters
diff --git a/cc/compdb.go b/cc/compdb.go
new file mode 100644
index 0000000..1102651
--- /dev/null
+++ b/cc/compdb.go
@@ -0,0 +1,203 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "android/soong/android"
+)
+
+// This singleton generates a compile_commands.json file. It does so for each
+// blueprint Android.bp resulting in a cc.Module when either make, mm, mma, mmm
+// or mmma is called. It will only create a single compile_commands.json file
+// at out/development/ide/compdb/compile_commands.json. It will also symlink it
+// to ${SOONG_LINK_COMPDB_TO} if set. In general this should be created by running
+// make SOONG_GEN_COMPDB=1 nothing to get all targets.
+
+func init() {
+ android.RegisterSingletonType("compdb_generator", compDBGeneratorSingleton)
+}
+
+func compDBGeneratorSingleton() android.Singleton {
+ return &compdbGeneratorSingleton{}
+}
+
+type compdbGeneratorSingleton struct{}
+
+const (
+ compdbFilename = "compile_commands.json"
+ compdbOutputProjectsDirectory = "out/development/ide/compdb"
+
+ // Environment variables used to modify behavior of this singleton.
+ envVariableGenerateCompdb = "SOONG_GEN_COMPDB"
+ envVariableGenerateCompdbDebugInfo = "SOONG_GEN_COMPDB_DEBUG"
+ envVariableCompdbLink = "SOONG_LINK_COMPDB_TO"
+)
+
+// A compdb entry. The compile_commands.json file is a list of these.
+type compDbEntry struct {
+ Directory string `json:"directory"`
+ Arguments []string `json:"arguments"`
+ File string `json:"file"`
+ Output string `json:"output,omitempty"`
+}
+
+func (c *compdbGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ if !ctx.Config().IsEnvTrue(envVariableGenerateCompdb) {
+ return
+ }
+
+ // Instruct the generator to indent the json file for easier debugging.
+ outputCompdbDebugInfo := ctx.Config().IsEnvTrue(envVariableGenerateCompdbDebugInfo)
+
+ // We only want one entry per file. We don't care what module/isa it's from
+ m := make(map[string]compDbEntry)
+ ctx.VisitAllModules(func(module android.Module) {
+ if ccModule, ok := module.(*Module); ok {
+ if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
+ generateCompdbProject(compiledModule, ctx, ccModule, m)
+ }
+ }
+ })
+
+ // Create the output file.
+ dir := filepath.Join(getCompdbAndroidSrcRootDirectory(ctx), compdbOutputProjectsDirectory)
+ os.MkdirAll(dir, 0777)
+ compDBFile := filepath.Join(dir, compdbFilename)
+ f, err := os.Create(compdbFilename)
+ if err != nil {
+ log.Fatalf("Could not create file %s: %s", filepath.Join(dir, compdbFilename), err)
+ }
+ defer f.Close()
+
+ v := make([]compDbEntry, 0, len(m))
+
+ for _, value := range m {
+ v = append(v, value)
+ }
+ var dat []byte
+ if outputCompdbDebugInfo {
+ dat, err = json.MarshalIndent(v, "", " ")
+ } else {
+ dat, err = json.Marshal(v)
+ }
+ if err != nil {
+ log.Fatalf("Failed to marshal: %s", err)
+ }
+ f.Write(dat)
+
+ finalLinkPath := filepath.Join(ctx.Config().Getenv(envVariableCompdbLink), compdbFilename)
+ if finalLinkPath != "" {
+ os.Remove(finalLinkPath)
+ if err := os.Symlink(compDBFile, finalLinkPath); err != nil {
+ log.Fatalf("Unable to symlink %s to %s: %s", compDBFile, finalLinkPath, err)
+ }
+ }
+}
+
+func expandAllVars(ctx android.SingletonContext, args []string) []string {
+ var out []string
+ for _, arg := range args {
+ if arg != "" {
+ if val, err := evalAndSplitVariable(ctx, arg); err == nil {
+ out = append(out, val...)
+ } else {
+ out = append(out, arg)
+ }
+ }
+ }
+ return out
+}
+
+func getArguments(src android.Path, ctx android.SingletonContext, ccModule *Module, ccPath string, cxxPath string) []string {
+ var args []string
+ isCpp := false
+ isAsm := false
+ // TODO It would be better to ask soong for the types here.
+ var clangPath string
+ switch src.Ext() {
+ case ".S", ".s", ".asm":
+ isAsm = true
+ isCpp = false
+ clangPath = ccPath
+ case ".c":
+ isAsm = false
+ isCpp = false
+ clangPath = ccPath
+ case ".cpp", ".cc", ".mm":
+ isAsm = false
+ isCpp = true
+ clangPath = cxxPath
+ default:
+ log.Print("Unknown file extension " + src.Ext() + " on file " + src.String())
+ isAsm = true
+ isCpp = false
+ clangPath = ccPath
+ }
+ args = append(args, clangPath)
+ args = append(args, expandAllVars(ctx, ccModule.flags.GlobalFlags)...)
+ args = append(args, expandAllVars(ctx, ccModule.flags.CFlags)...)
+ if isCpp {
+ args = append(args, expandAllVars(ctx, ccModule.flags.CppFlags)...)
+ } else if !isAsm {
+ args = append(args, expandAllVars(ctx, ccModule.flags.ConlyFlags)...)
+ }
+ args = append(args, expandAllVars(ctx, ccModule.flags.SystemIncludeFlags)...)
+ args = append(args, src.String())
+ return args
+}
+
+func generateCompdbProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module, builds map[string]compDbEntry) {
+ srcs := compiledModule.Srcs()
+ if len(srcs) == 0 {
+ return
+ }
+
+ rootDir := getCompdbAndroidSrcRootDirectory(ctx)
+ pathToCC, err := ctx.Eval(pctx, rootDir+"/${config.ClangBin}/")
+ ccPath := "/bin/false"
+ cxxPath := "/bin/false"
+ if err == nil {
+ ccPath = pathToCC + "clang"
+ cxxPath = pathToCC + "clang++"
+ }
+ for _, src := range srcs {
+ if _, ok := builds[src.String()]; !ok {
+ builds[src.String()] = compDbEntry{
+ Directory: rootDir,
+ Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
+ File: src.String(),
+ }
+ }
+ }
+}
+
+func evalAndSplitVariable(ctx android.SingletonContext, str string) ([]string, error) {
+ evaluated, err := ctx.Eval(pctx, str)
+ if err == nil {
+ return strings.Fields(evaluated), nil
+ }
+ return []string{""}, err
+}
+
+func getCompdbAndroidSrcRootDirectory(ctx android.SingletonContext) string {
+ srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
+ return srcPath
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index 1b0eb4e..f9af4d8 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -31,11 +31,11 @@
// list of source files used to compile the C/C++ module. May be .c, .cpp, or .S files.
// srcs may reference the outputs of other modules that produce source files like genrule
// or filegroup using the syntax ":module".
- Srcs []string `android:"arch_variant"`
+ Srcs []string `android:"path,arch_variant"`
// list of source files that should not be used to build the C/C++ module.
// This is most useful in the arch/multilib variants to remove non-common files
- Exclude_srcs []string `android:"arch_variant"`
+ Exclude_srcs []string `android:"path,arch_variant"`
// list of module-specific flags that will be used for C and C++ compiles.
Cflags []string `android:"arch_variant"`
@@ -73,7 +73,11 @@
// list of directories relative to the Blueprints file that will
// be added to the include path using -I
- Local_include_dirs []string `android:"arch_variant,variant_prepend",`
+ Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+
+ // Add the directory containing the Android.bp file to the list of include
+ // directories. Defaults to true.
+ Include_build_directory *bool
// list of generated sources to compile. These are the names of gensrcs or
// genrule modules.
@@ -132,16 +136,29 @@
Vendor struct {
// list of source files that should only be used in the
// vendor variant of the C/C++ module.
- Srcs []string
+ Srcs []string `android:"path"`
// list of source files that should not be used to
// build the vendor variant of the C/C++ module.
- Exclude_srcs []string
+ Exclude_srcs []string `android:"path"`
// List of additional cflags that should be used to build the vendor
// variant of the C/C++ module.
Cflags []string
}
+ Recovery struct {
+ // list of source files that should only be used in the
+ // recovery variant of the C/C++ module.
+ Srcs []string `android:"path"`
+
+ // list of source files that should not be used to
+ // build the recovery variant of the C/C++ module.
+ Exclude_srcs []string `android:"path"`
+
+ // List of additional cflags that should be used to build the recovery
+ // variant of the C/C++ module.
+ Cflags []string
+ }
}
Proto struct {
@@ -204,13 +221,16 @@
deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
- android.ExtractSourcesDeps(ctx, compiler.Properties.Srcs)
- android.ExtractSourcesDeps(ctx, compiler.Properties.Exclude_srcs)
-
+ android.ProtoDeps(ctx, &compiler.Proto)
if compiler.hasSrcExt(".proto") {
deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
}
+ if compiler.hasSrcExt(".sysprop") {
+ deps.HeaderLibs = append(deps.HeaderLibs, "libbase_headers")
+ deps.SharedLibs = append(deps.SharedLibs, "liblog")
+ }
+
if Bool(compiler.Properties.Openmp) {
deps.StaticLibs = append(deps.StaticLibs, "libomp")
}
@@ -229,8 +249,8 @@
return false
}
-func addToModuleList(ctx ModuleContext, list string, module string) {
- getNamedMapForConfig(ctx.Config(), list).Store(module, true)
+func addToModuleList(ctx ModuleContext, key android.OnceKey, module string) {
+ getNamedMapForConfig(ctx.Config(), key).Store(module, true)
}
// Create a Flags struct that collects the compile flags from global values,
@@ -238,15 +258,17 @@
func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
tc := ctx.toolchain()
- compiler.srcsBeforeGen = ctx.ExpandSources(compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
+ compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
CheckBadCompilerFlags(ctx, "cflags", compiler.Properties.Cflags)
CheckBadCompilerFlags(ctx, "cppflags", compiler.Properties.Cppflags)
CheckBadCompilerFlags(ctx, "conlyflags", compiler.Properties.Conlyflags)
CheckBadCompilerFlags(ctx, "asflags", compiler.Properties.Asflags)
+ CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags)
+ CheckBadCompilerFlags(ctx, "recovery.cflags", compiler.Properties.Target.Recovery.Cflags)
- esc := proptools.NinjaAndShellEscape
+ esc := proptools.NinjaAndShellEscapeList
flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Cflags)...)
flags.CppFlags = append(flags.CppFlags, esc(compiler.Properties.Cppflags)...)
@@ -269,8 +291,11 @@
flags.YasmFlags = append(flags.YasmFlags, f)
}
- flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
- flags.YasmFlags = append(flags.YasmFlags, "-I"+android.PathForModuleSrc(ctx).String())
+ if compiler.Properties.Include_build_directory == nil ||
+ *compiler.Properties.Include_build_directory {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
+ flags.YasmFlags = append(flags.YasmFlags, "-I"+android.PathForModuleSrc(ctx).String())
+ }
if !(ctx.useSdk() || ctx.useVndk()) || ctx.Host() {
flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
@@ -280,14 +305,16 @@
}
if ctx.useSdk() {
+ // TODO: Switch to --sysroot.
// The NDK headers are installed to a common sysroot. While a more
// typical Soong approach would be to only make the headers for the
// library you're using available, we're trying to emulate the NDK
// behavior here, and the NDK always has all the NDK headers available.
flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
"-isystem "+getCurrentIncludePath(ctx).String(),
- "-isystem "+getCurrentIncludePath(ctx).Join(ctx, tc.ClangTriple()).String())
+ "-isystem "+getCurrentIncludePath(ctx).Join(ctx, config.NDKTriple(tc)).String())
+ // TODO: Migrate to API suffixed triple?
// Traditionally this has come from android/api-level.h, but with the
// libc headers unified it must be set by the build system since we
// don't have per-API level copies of that header now.
@@ -297,14 +324,6 @@
}
flags.GlobalFlags = append(flags.GlobalFlags,
"-D__ANDROID_API__="+version)
-
- // Until the full NDK has been migrated to using ndk_headers, we still
- // need to add the legacy sysroot includes to get the full set of
- // headers.
- legacyIncludes := fmt.Sprintf(
- "prebuilts/ndk/current/platforms/android-%s/arch-%s/usr/include",
- ctx.sdkVersion(), ctx.Arch().ArchType.String())
- flags.SystemIncludeFlags = append(flags.SystemIncludeFlags, "-isystem "+legacyIncludes)
}
if ctx.useVndk() {
@@ -317,14 +336,19 @@
"-D__ANDROID_API__="+version, "-D__ANDROID_VNDK__")
}
+ if ctx.inRecovery() {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_RECOVERY__")
+ }
+
+ if ctx.apexName() != "" {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_APEX__="+ctx.apexName())
+ }
+
instructionSet := String(compiler.Properties.Instruction_set)
if flags.RequiredInstructionSet != "" {
instructionSet = flags.RequiredInstructionSet
}
- instructionSetFlags, err := tc.InstructionSetFlags(instructionSet)
- if flags.Clang {
- instructionSetFlags, err = tc.ClangInstructionSetFlags(instructionSet)
- }
+ instructionSetFlags, err := tc.ClangInstructionSetFlags(instructionSet)
if err != nil {
ctx.ModuleErrorf("%s", err)
}
@@ -334,24 +358,22 @@
// TODO: debug
flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Release.Cflags)...)
- if flags.Clang {
- CheckBadCompilerFlags(ctx, "clang_cflags", compiler.Properties.Clang_cflags)
- CheckBadCompilerFlags(ctx, "clang_asflags", compiler.Properties.Clang_asflags)
+ CheckBadCompilerFlags(ctx, "clang_cflags", compiler.Properties.Clang_cflags)
+ CheckBadCompilerFlags(ctx, "clang_asflags", compiler.Properties.Clang_asflags)
- flags.CFlags = config.ClangFilterUnknownCflags(flags.CFlags)
- flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Clang_cflags)...)
- flags.AsFlags = append(flags.AsFlags, esc(compiler.Properties.Clang_asflags)...)
- flags.CppFlags = config.ClangFilterUnknownCflags(flags.CppFlags)
- flags.ConlyFlags = config.ClangFilterUnknownCflags(flags.ConlyFlags)
- flags.LdFlags = config.ClangFilterUnknownCflags(flags.LdFlags)
+ flags.CFlags = config.ClangFilterUnknownCflags(flags.CFlags)
+ flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Clang_cflags)...)
+ flags.AsFlags = append(flags.AsFlags, esc(compiler.Properties.Clang_asflags)...)
+ flags.CppFlags = config.ClangFilterUnknownCflags(flags.CppFlags)
+ flags.ConlyFlags = config.ClangFilterUnknownCflags(flags.ConlyFlags)
+ flags.LdFlags = config.ClangFilterUnknownCflags(flags.LdFlags)
- target := "-target " + tc.ClangTriple()
- gccPrefix := "-B" + config.ToolPath(tc)
+ target := "-target " + tc.ClangTriple()
+ gccPrefix := "-B" + config.ToolPath(tc)
- flags.CFlags = append(flags.CFlags, target, gccPrefix)
- flags.AsFlags = append(flags.AsFlags, target, gccPrefix)
- flags.LdFlags = append(flags.LdFlags, target, gccPrefix)
- }
+ flags.CFlags = append(flags.CFlags, target, gccPrefix)
+ flags.AsFlags = append(flags.AsFlags, target, gccPrefix)
+ flags.LdFlags = append(flags.LdFlags, target, gccPrefix)
hod := "Host"
if ctx.Os().Class == android.Device {
@@ -362,22 +384,18 @@
flags.ConlyFlags = append([]string{"${config.CommonGlobalConlyflags}"}, flags.ConlyFlags...)
flags.CppFlags = append([]string{fmt.Sprintf("${config.%sGlobalCppflags}", hod)}, flags.CppFlags...)
- if flags.Clang {
- flags.AsFlags = append(flags.AsFlags, tc.ClangAsflags())
- flags.CppFlags = append([]string{"${config.CommonClangGlobalCppflags}"}, flags.CppFlags...)
- flags.GlobalFlags = append(flags.GlobalFlags,
- tc.ClangCflags(),
- "${config.CommonClangGlobalCflags}",
- fmt.Sprintf("${config.%sClangGlobalCflags}", hod))
- } else {
- flags.CppFlags = append([]string{"${config.CommonGlobalCppflags}"}, flags.CppFlags...)
- flags.GlobalFlags = append(flags.GlobalFlags,
- tc.Cflags(),
- "${config.CommonGlobalCflags}",
- fmt.Sprintf("${config.%sGlobalCflags}", hod))
+ flags.AsFlags = append(flags.AsFlags, tc.ClangAsflags())
+ flags.CppFlags = append([]string{"${config.CommonClangGlobalCppflags}"}, flags.CppFlags...)
+ flags.GlobalFlags = append(flags.GlobalFlags,
+ tc.ClangCflags(),
+ "${config.CommonClangGlobalCflags}",
+ fmt.Sprintf("${config.%sClangGlobalCflags}", hod))
+
+ if strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "external/") {
+ flags.GlobalFlags = append([]string{"${config.ClangExternalCflags}"}, flags.GlobalFlags...)
}
- if ctx.Device() {
+ if tc.Bionic() {
if Bool(compiler.Properties.Rtti) {
flags.CppFlags = append(flags.CppFlags, "-frtti")
} else {
@@ -387,19 +405,11 @@
flags.AsFlags = append(flags.AsFlags, "-D__ASSEMBLY__")
- if flags.Clang {
- flags.CppFlags = append(flags.CppFlags, tc.ClangCppflags())
- } else {
- flags.CppFlags = append(flags.CppFlags, tc.Cppflags())
- }
+ flags.CppFlags = append(flags.CppFlags, tc.ClangCppflags())
flags.YasmFlags = append(flags.YasmFlags, tc.YasmFlags())
- if flags.Clang {
- flags.GlobalFlags = append(flags.GlobalFlags, tc.ToolchainClangCflags())
- } else {
- flags.GlobalFlags = append(flags.GlobalFlags, tc.ToolchainCflags())
- }
+ flags.GlobalFlags = append(flags.GlobalFlags, tc.ToolchainClangCflags())
cStd := config.CStdVersion
if String(compiler.Properties.C_std) == "experimental" {
@@ -414,19 +424,6 @@
cppStd = config.CppStdVersion
case "experimental":
cppStd = config.ExperimentalCppStdVersion
- case "c++17", "gnu++17":
- // Map c++17 and gnu++17 to their 1z equivalents, until 17 is finalized.
- cppStd = strings.Replace(String(compiler.Properties.Cpp_std), "17", "1z", 1)
- }
-
- if !flags.Clang {
- // GCC uses an invalid C++14 ABI (emits calls to
- // __cxa_throw_bad_array_length, which is not a valid C++ RT ABI).
- // http://b/25022512
- // The host GCC doesn't support C++14 (and is deprecated, so likely
- // never will).
- // Build these modules with C++11.
- cppStd = config.GccCppStdVersion
}
if compiler.Properties.Gnu_extensions != nil && *compiler.Properties.Gnu_extensions == false {
@@ -441,6 +438,10 @@
flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
}
+ if ctx.inRecovery() {
+ flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Recovery.Cflags)...)
+ }
+
// We can enforce some rules more strictly in the code we own. strict
// indicates if this is code that we can be stricter with. If we have
// rules that we want to apply to *our* code (but maybe can't for
@@ -493,13 +494,18 @@
flags = rsFlags(ctx, flags, &compiler.Properties)
}
+ if compiler.hasSrcExt(".sysprop") {
+ flags.GlobalFlags = append(flags.GlobalFlags,
+ "-I"+android.PathForModuleGen(ctx, "sysprop", "include").String())
+ }
+
if len(compiler.Properties.Srcs) > 0 {
module := ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
if inList("-Wno-error", flags.CFlags) || inList("-Wno-error", flags.CppFlags) {
- addToModuleList(ctx, modulesUsingWnoError, module)
+ addToModuleList(ctx, modulesUsingWnoErrorKey, module)
} else if !inList("-Werror", flags.CFlags) && !inList("-Werror", flags.CppFlags) {
if warningsAreAllowed(ctx.ModuleDir()) {
- addToModuleList(ctx, modulesAddedWall, module)
+ addToModuleList(ctx, modulesAddedWallKey, module)
flags.CFlags = append([]string{"-Wall"}, flags.CFlags...)
} else {
flags.CFlags = append([]string{"-Wall", "-Werror"}, flags.CFlags...)
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 00c29f5..1ca1656 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -43,9 +43,12 @@
"-Wl,--icf=safe",
}
+ arm64Lldflags = append(ClangFilterUnknownLldflags(arm64Ldflags),
+ "-Wl,-z,max-page-size=4096")
+
arm64Cppflags = []string{}
- arm64CpuVariantCflags = map[string][]string{
+ arm64ClangCpuVariantCflags = map[string][]string{
"cortex-a53": []string{
"-mcpu=cortex-a53",
},
@@ -57,10 +60,17 @@
// core (cortex-a55) and is sensitive to ordering.
"-mcpu=cortex-a55",
},
+ "cortex-a76": []string{
+ // Use the cortex-a55 since it is similar to the little
+ // core (cortex-a55) and is sensitive to ordering.
+ "-mcpu=cortex-a55",
+ },
"kryo": []string{
- // Use the cortex-a57 cpu since some compilers
- // don't support a Kryo specific target yet.
- "-mcpu=cortex-a57",
+ "-mcpu=kryo",
+ },
+ "kryo385": []string{
+ // Use cortex-a53 because kryo385 is not supported in GCC/clang.
+ "-mcpu=cortex-a53",
},
"exynos-m1": []string{
"-mcpu=exynos-m1",
@@ -69,8 +79,6 @@
"-mcpu=exynos-m2",
},
}
-
- arm64ClangCpuVariantCflags = copyVariantFlags(arm64CpuVariantCflags)
)
const (
@@ -78,76 +86,40 @@
)
func init() {
- android.RegisterArchVariants(android.Arm64,
- "armv8_a",
- "armv8_2a",
- "cortex-a53",
- "cortex-a55",
- "cortex-a73",
- "cortex-a75",
- "kryo",
- "exynos-m1",
- "exynos-m2",
- "denver64")
-
- // Clang supports specific Kryo targeting
- replaceFirst(arm64ClangCpuVariantCflags["kryo"], "-mcpu=cortex-a57", "-mcpu=kryo")
-
pctx.StaticVariable("arm64GccVersion", arm64GccVersion)
pctx.SourcePathVariable("Arm64GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
- pctx.StaticVariable("Arm64Cflags", strings.Join(arm64Cflags, " "))
pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " "))
- pctx.StaticVariable("Arm64Cppflags", strings.Join(arm64Cppflags, " "))
+ pctx.StaticVariable("Arm64Lldflags", strings.Join(arm64Lldflags, " "))
pctx.StaticVariable("Arm64IncludeFlags", bionicHeaders("arm64"))
pctx.StaticVariable("Arm64ClangCflags", strings.Join(ClangFilterUnknownCflags(arm64Cflags), " "))
pctx.StaticVariable("Arm64ClangLdflags", strings.Join(ClangFilterUnknownCflags(arm64Ldflags), " "))
+ pctx.StaticVariable("Arm64ClangLldflags", strings.Join(ClangFilterUnknownCflags(arm64Lldflags), " "))
pctx.StaticVariable("Arm64ClangCppflags", strings.Join(ClangFilterUnknownCflags(arm64Cppflags), " "))
pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
- pctx.StaticVariable("Arm64CortexA53Cflags",
- strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
pctx.StaticVariable("Arm64ClangCortexA53Cflags",
strings.Join(arm64ClangCpuVariantCflags["cortex-a53"], " "))
- pctx.StaticVariable("Arm64CortexA55Cflags",
- strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
pctx.StaticVariable("Arm64ClangCortexA55Cflags",
strings.Join(arm64ClangCpuVariantCflags["cortex-a55"], " "))
- pctx.StaticVariable("Arm64KryoCflags",
- strings.Join(arm64CpuVariantCflags["kryo"], " "))
pctx.StaticVariable("Arm64ClangKryoCflags",
strings.Join(arm64ClangCpuVariantCflags["kryo"], " "))
- pctx.StaticVariable("Arm64ExynosM1Cflags",
- strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
pctx.StaticVariable("Arm64ClangExynosM1Cflags",
strings.Join(arm64ClangCpuVariantCflags["exynos-m1"], " "))
- pctx.StaticVariable("Arm64ExynosM2Cflags",
- strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
pctx.StaticVariable("Arm64ClangExynosM2Cflags",
strings.Join(arm64ClangCpuVariantCflags["exynos-m2"], " "))
}
var (
- arm64CpuVariantCflagsVar = map[string]string{
- "": "",
- "cortex-a53": "${config.Arm64CortexA53Cflags}",
- "cortex-a55": "${config.Arm64CortexA55Cflags}",
- "cortex-a73": "${config.Arm64CortexA53Cflags}",
- "cortex-a75": "${config.Arm64CortexA55Cflags}",
- "kryo": "${config.Arm64KryoCflags}",
- "exynos-m1": "${config.Arm64ExynosM1Cflags}",
- "exynos-m2": "${config.Arm64ExynosM2Cflags}",
- }
-
arm64ClangArchVariantCflagsVar = map[string]string{
"armv8-a": "${config.Arm64ClangArmv8ACflags}",
"armv8-2a": "${config.Arm64ClangArmv82ACflags}",
@@ -157,9 +129,12 @@
"": "",
"cortex-a53": "${config.Arm64ClangCortexA53Cflags}",
"cortex-a55": "${config.Arm64ClangCortexA55Cflags}",
+ "cortex-a72": "${config.Arm64ClangCortexA53Cflags}",
"cortex-a73": "${config.Arm64ClangCortexA53Cflags}",
"cortex-a75": "${config.Arm64ClangCortexA55Cflags}",
+ "cortex-a76": "${config.Arm64ClangCortexA55Cflags}",
"kryo": "${config.Arm64ClangKryoCflags}",
+ "kryo385": "${config.Arm64ClangCortexA53Cflags}",
"exynos-m1": "${config.Arm64ClangExynosM1Cflags}",
"exynos-m2": "${config.Arm64ClangExynosM2Cflags}",
}
@@ -169,7 +144,7 @@
toolchain64Bit
ldflags string
- toolchainCflags string
+ lldflags string
toolchainClangCflags string
}
@@ -189,22 +164,6 @@
return arm64GccVersion
}
-func (t *toolchainArm64) ToolchainCflags() string {
- return t.toolchainCflags
-}
-
-func (t *toolchainArm64) Cflags() string {
- return "${config.Arm64Cflags}"
-}
-
-func (t *toolchainArm64) Cppflags() string {
- return "${config.Arm64Cppflags}"
-}
-
-func (t *toolchainArm64) Ldflags() string {
- return t.ldflags
-}
-
func (t *toolchainArm64) IncludeFlags() string {
return "${config.Arm64IncludeFlags}"
}
@@ -225,11 +184,15 @@
return t.ldflags
}
+func (t *toolchainArm64) ClangLldflags() string {
+ return t.lldflags
+}
+
func (t *toolchainArm64) ToolchainClangCflags() string {
return t.toolchainClangCflags
}
-func (toolchainArm64) SanitizerRuntimeLibraryArch() string {
+func (toolchainArm64) LibclangRuntimeLibraryArch() string {
return "aarch64"
}
@@ -248,10 +211,7 @@
var extraLdflags string
switch arch.CpuVariant {
- case "cortex-a53", "cortex-a73", "kryo", "exynos-m1", "exynos-m2",
- // This variant might not need the workaround but leave it
- // in the list since it has had the workaround on before.
- "denver64":
+ case "cortex-a53", "cortex-a72", "cortex-a73", "kryo", "exynos-m1", "exynos-m2":
extraLdflags = "-Wl,--fix-cortex-a53-843419"
}
@@ -260,7 +220,10 @@
"${config.Arm64Ldflags}",
extraLdflags,
}, " "),
- toolchainCflags: variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant),
+ lldflags: strings.Join([]string{
+ "${config.Arm64Lldflags}",
+ extraLdflags,
+ }, " "),
toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
}
}
diff --git a/cc/config/arm64_fuchsia_device.go b/cc/config/arm64_fuchsia_device.go
new file mode 100644
index 0000000..02c0c14
--- /dev/null
+++ b/cc/config/arm64_fuchsia_device.go
@@ -0,0 +1,101 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "android/soong/android"
+)
+
+var fuchsiaArm64SysRoot string = "prebuilts/fuchsia_sdk/arch/arm64/sysroot"
+var fuchsiaArm64PrebuiltLibsRoot string = "fuchsia/prebuilt_libs/"
+
+type toolchainFuchsiaArm64 struct {
+ toolchain64Bit
+ toolchainFuchsia
+}
+
+func (t *toolchainFuchsiaArm64) Name() string {
+ return "arm64"
+}
+
+func (t *toolchainFuchsiaArm64) GccRoot() string {
+ return "${config.Arm64GccRoot}"
+}
+
+func (t *toolchainFuchsiaArm64) GccTriple() string {
+ return "aarch64-linux-android"
+}
+
+func (t *toolchainFuchsiaArm64) GccVersion() string {
+ return arm64GccVersion
+}
+
+func (t *toolchainFuchsiaArm64) Cflags() string {
+ return ""
+}
+
+func (t *toolchainFuchsiaArm64) Cppflags() string {
+ return ""
+}
+
+func (t *toolchainFuchsiaArm64) Ldflags() string {
+ return "-Wl,--fix-cortex-a53-843419"
+}
+
+func (t *toolchainFuchsiaArm64) IncludeFlags() string {
+ return ""
+}
+
+func (t *toolchainFuchsiaArm64) ToolchainCflags() string {
+ return "-mcpu=cortex-a53"
+}
+
+func (t *toolchainFuchsiaArm64) ClangTriple() string {
+ return "arm64-fuchsia-android"
+}
+
+func (t *toolchainFuchsiaArm64) ClangCppflags() string {
+ return "-Wno-error=deprecated-declarations"
+}
+
+func (t *toolchainFuchsiaArm64) ClangLdflags() string {
+ return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -L" + fuchsiaArm64PrebuiltLibsRoot + "/aarch64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/arm64/dist/"
+}
+
+func (t *toolchainFuchsiaArm64) ClangLldflags() string {
+ return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -L" + fuchsiaArm64PrebuiltLibsRoot + "/aarch64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/arm64/dist/"
+}
+
+func (t *toolchainFuchsiaArm64) ClangCflags() string {
+ return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -I" + fuchsiaArm64SysRoot + "/include"
+}
+
+func (t *toolchainFuchsiaArm64) Bionic() bool {
+ return false
+}
+
+func (t *toolchainFuchsiaArm64) ToolchainClangCflags() string {
+ return "-march=armv8-a"
+}
+
+var toolchainArm64FuchsiaSingleton Toolchain = &toolchainFuchsiaArm64{}
+
+func arm64FuchsiaToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainArm64FuchsiaSingleton
+}
+
+func init() {
+ registerToolchainFactory(android.Fuchsia, android.Arm64, arm64FuchsiaToolchainFactory)
+}
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index cad1b16..cd7c410 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -39,6 +39,8 @@
"-Wl,-m,armelf",
}
+ armLldflags = ClangFilterUnknownLldflags(armLdflags)
+
armArmCflags = []string{
"-fstrict-aliasing",
}
@@ -48,7 +50,7 @@
"-Os",
}
- armArchVariantCflags = map[string][]string{
+ armClangArchVariantCflags = map[string][]string{
"armv7-a": []string{
"-march=armv7-a",
"-mfloat-abi=softfp",
@@ -64,9 +66,14 @@
"-mfloat-abi=softfp",
"-mfpu=neon-fp-armv8",
},
+ "armv8-2a": []string{
+ "-march=armv8.2-a",
+ "-mfloat-abi=softfp",
+ "-mfpu=neon-fp-armv8",
+ },
}
- armCpuVariantCflags = map[string][]string{
+ armClangCpuVariantCflags = map[string][]string{
"cortex-a7": []string{
"-mcpu=cortex-a7",
"-mfpu=neon-vfpv4",
@@ -115,8 +122,17 @@
// better solution comes around. See Bug 27340895
"-D__ARM_FEATURE_LPAE=1",
},
+ "cortex-a76": []string{
+ "-mcpu=cortex-a55",
+ "-mfpu=neon-fp-armv8",
+ // Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+ // don't advertise.
+ // TODO This is a hack and we need to add it for each processor that supports LPAE until some
+ // better solution comes around. See Bug 27340895
+ "-D__ARM_FEATURE_LPAE=1",
+ },
"krait": []string{
- "-mcpu=cortex-a15",
+ "-mcpu=krait",
"-mfpu=neon-vfpv4",
// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
// don't advertise.
@@ -135,10 +151,16 @@
// better solution comes around. See Bug 27340895
"-D__ARM_FEATURE_LPAE=1",
},
+ "kryo385": []string{
+ // Use cortex-a53 because kryo385 is not supported in GCC/clang.
+ "-mcpu=cortex-a53",
+ // Fake an ARM compiler flag as these processors support LPAE which GCC/clang
+ // don't advertise.
+ // TODO This is a hack and we need to add it for each processor that supports LPAE until some
+ // better solution comes around. See Bug 27340895
+ "-D__ARM_FEATURE_LPAE=1",
+ },
}
-
- armClangCpuVariantCflags = copyVariantFlags(armCpuVariantCflags)
- armClangArchVariantCflags = copyVariantFlags(armArchVariantCflags)
)
const (
@@ -146,77 +168,20 @@
)
func init() {
- android.RegisterArchFeatures(android.Arm,
- "neon")
-
- android.RegisterArchVariants(android.Arm,
- "armv7-a",
- "armv7-a-neon",
- "armv8-a",
- "cortex-a7",
- "cortex-a8",
- "cortex-a9",
- "cortex-a15",
- "cortex-a53",
- "cortex-a53-a57",
- "cortex-a55",
- "cortex-a73",
- "cortex-a75",
- "krait",
- "kryo",
- "exynos-m1",
- "exynos-m2",
- "denver")
-
- android.RegisterArchVariantFeatures(android.Arm, "armv7-a-neon", "neon")
- android.RegisterArchVariantFeatures(android.Arm, "armv8-a", "neon")
-
- // Krait is not supported by GCC, but is supported by Clang, so
- // override the definitions when building modules with Clang.
- replaceFirst(armClangCpuVariantCflags["krait"], "-mcpu=cortex-a15", "-mcpu=krait")
-
- // The reason we use "-march=armv8-a+crc", instead of "-march=armv8-a", for
- // gcc is the latter would conflict with any specified/supported -mcpu!
- // All armv8-a cores supported by gcc 4.9 support crc, so it's safe
- // to add +crc. Besides, the use of gcc is only for legacy code.
- replaceFirst(armArchVariantCflags["armv8-a"], "-march=armv8-a", "-march=armv8-a+crc")
-
pctx.StaticVariable("armGccVersion", armGccVersion)
pctx.SourcePathVariable("ArmGccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
- pctx.StaticVariable("ArmToolchainCflags", strings.Join(armToolchainCflags, " "))
- pctx.StaticVariable("ArmCflags", strings.Join(armCflags, " "))
pctx.StaticVariable("ArmLdflags", strings.Join(armLdflags, " "))
- pctx.StaticVariable("ArmCppflags", strings.Join(armCppflags, " "))
+ pctx.StaticVariable("ArmLldflags", strings.Join(armLldflags, " "))
pctx.StaticVariable("ArmIncludeFlags", bionicHeaders("arm"))
- // Extended cflags
-
- // ARM vs. Thumb instruction set flags
- pctx.StaticVariable("ArmArmCflags", strings.Join(armArmCflags, " "))
- pctx.StaticVariable("ArmThumbCflags", strings.Join(armThumbCflags, " "))
-
- // Architecture variant cflags
- pctx.StaticVariable("ArmArmv7ACflags", strings.Join(armArchVariantCflags["armv7-a"], " "))
- pctx.StaticVariable("ArmArmv7ANeonCflags", strings.Join(armArchVariantCflags["armv7-a-neon"], " "))
- pctx.StaticVariable("ArmArmv8ACflags", strings.Join(armArchVariantCflags["armv8-a"], " "))
-
- // Cpu variant cflags
- pctx.StaticVariable("ArmGenericCflags", strings.Join(armCpuVariantCflags[""], " "))
- pctx.StaticVariable("ArmCortexA7Cflags", strings.Join(armCpuVariantCflags["cortex-a7"], " "))
- pctx.StaticVariable("ArmCortexA8Cflags", strings.Join(armCpuVariantCflags["cortex-a8"], " "))
- pctx.StaticVariable("ArmCortexA15Cflags", strings.Join(armCpuVariantCflags["cortex-a15"], " "))
- pctx.StaticVariable("ArmCortexA53Cflags", strings.Join(armCpuVariantCflags["cortex-a53"], " "))
- pctx.StaticVariable("ArmCortexA55Cflags", strings.Join(armCpuVariantCflags["cortex-a55"], " "))
- pctx.StaticVariable("ArmKraitCflags", strings.Join(armCpuVariantCflags["krait"], " "))
- pctx.StaticVariable("ArmKryoCflags", strings.Join(armCpuVariantCflags["kryo"], " "))
-
// Clang cflags
pctx.StaticVariable("ArmToolchainClangCflags", strings.Join(ClangFilterUnknownCflags(armToolchainCflags), " "))
pctx.StaticVariable("ArmClangCflags", strings.Join(ClangFilterUnknownCflags(armCflags), " "))
pctx.StaticVariable("ArmClangLdflags", strings.Join(ClangFilterUnknownCflags(armLdflags), " "))
+ pctx.StaticVariable("ArmClangLldflags", strings.Join(ClangFilterUnknownCflags(armLldflags), " "))
pctx.StaticVariable("ArmClangCppflags", strings.Join(ClangFilterUnknownCflags(armCppflags), " "))
// Clang ARM vs. Thumb instruction set cflags
@@ -230,6 +195,8 @@
strings.Join(armClangArchVariantCflags["armv7-a-neon"], " "))
pctx.StaticVariable("ArmClangArmv8ACflags",
strings.Join(armClangArchVariantCflags["armv8-a"], " "))
+ pctx.StaticVariable("ArmClangArmv82ACflags",
+ strings.Join(armClangArchVariantCflags["armv8-2a"], " "))
// Clang cpu variant cflags
pctx.StaticVariable("ArmClangGenericCflags",
@@ -251,33 +218,11 @@
}
var (
- armArchVariantCflagsVar = map[string]string{
- "armv7-a": "${config.ArmArmv7ACflags}",
- "armv7-a-neon": "${config.ArmArmv7ANeonCflags}",
- "armv8-a": "${config.ArmArmv8ACflags}",
- }
-
- armCpuVariantCflagsVar = map[string]string{
- "": "${config.ArmGenericCflags}",
- "cortex-a7": "${config.ArmCortexA7Cflags}",
- "cortex-a8": "${config.ArmCortexA8Cflags}",
- "cortex-a15": "${config.ArmCortexA15Cflags}",
- "cortex-a53": "${config.ArmCortexA53Cflags}",
- "cortex-a53.a57": "${config.ArmCortexA53Cflags}",
- "cortex-a55": "${config.ArmCortexA55Cflags}",
- "cortex-a73": "${config.ArmCortexA53Cflags}",
- "cortex-a75": "${config.ArmCortexA55Cflags}",
- "krait": "${config.ArmKraitCflags}",
- "kryo": "${config.ArmKryoCflags}",
- "exynos-m1": "${config.ArmCortexA53Cflags}",
- "exynos-m2": "${config.ArmCortexA53Cflags}",
- "denver": "${config.ArmCortexA15Cflags}",
- }
-
armClangArchVariantCflagsVar = map[string]string{
"armv7-a": "${config.ArmClangArmv7ACflags}",
"armv7-a-neon": "${config.ArmClangArmv7ANeonCflags}",
"armv8-a": "${config.ArmClangArmv8ACflags}",
+ "armv8-2a": "${config.ArmClangArmv82ACflags}",
}
armClangCpuVariantCflagsVar = map[string]string{
@@ -288,20 +233,22 @@
"cortex-a53": "${config.ArmClangCortexA53Cflags}",
"cortex-a53.a57": "${config.ArmClangCortexA53Cflags}",
"cortex-a55": "${config.ArmClangCortexA55Cflags}",
+ "cortex-a72": "${config.ArmClangCortexA53Cflags}",
"cortex-a73": "${config.ArmClangCortexA53Cflags}",
"cortex-a75": "${config.ArmClangCortexA55Cflags}",
"krait": "${config.ArmClangKraitCflags}",
"kryo": "${config.ArmClangKryoCflags}",
+ "kryo385": "${config.ArmClangCortexA53Cflags}",
"exynos-m1": "${config.ArmClangCortexA53Cflags}",
"exynos-m2": "${config.ArmClangCortexA53Cflags}",
- "denver": "${config.ArmClangCortexA15Cflags}",
}
)
type toolchainArm struct {
toolchain32Bit
- ldflags string
- toolchainCflags, toolchainClangCflags string
+ ldflags string
+ lldflags string
+ toolchainClangCflags string
}
func (t *toolchainArm) Name() string {
@@ -320,38 +267,17 @@
return armGccVersion
}
-func (t *toolchainArm) ToolchainCflags() string {
- return t.toolchainCflags
-}
-
-func (t *toolchainArm) Cflags() string {
- return "${config.ArmCflags}"
-}
-
-func (t *toolchainArm) Cppflags() string {
- return "${config.ArmCppflags}"
-}
-
-func (t *toolchainArm) Ldflags() string {
- return t.ldflags
-}
-
func (t *toolchainArm) IncludeFlags() string {
return "${config.ArmIncludeFlags}"
}
-func (t *toolchainArm) InstructionSetFlags(isa string) (string, error) {
- switch isa {
- case "arm":
- return "${config.ArmArmCflags}", nil
- case "thumb", "":
- return "${config.ArmThumbCflags}", nil
- default:
- return t.toolchainBase.InstructionSetFlags(isa)
- }
+func (t *toolchainArm) ClangTriple() string {
+ // http://b/72619014 work around llvm LTO bug.
+ return "armv7a-linux-androideabi"
}
-func (t *toolchainArm) ClangTriple() string {
+func (t *toolchainArm) ndkTriple() string {
+ // Use current NDK include path, while ClangTriple is changed.
return t.GccTriple()
}
@@ -371,6 +297,10 @@
return t.ldflags
}
+func (t *toolchainArm) ClangLldflags() string {
+ return t.lldflags // TODO: handle V8 cases
+}
+
func (t *toolchainArm) ClangInstructionSetFlags(isa string) (string, error) {
switch isa {
case "arm":
@@ -382,22 +312,17 @@
}
}
-func (toolchainArm) SanitizerRuntimeLibraryArch() string {
+func (toolchainArm) LibclangRuntimeLibraryArch() string {
return "arm"
}
func armToolchainFactory(arch android.Arch) Toolchain {
var fixCortexA8 string
- toolchainCflags := make([]string, 2, 3)
toolchainClangCflags := make([]string, 2, 3)
- toolchainCflags[0] = "${config.ArmToolchainCflags}"
- toolchainCflags[1] = armArchVariantCflagsVar[arch.ArchVariant]
toolchainClangCflags[0] = "${config.ArmToolchainClangCflags}"
toolchainClangCflags[1] = armClangArchVariantCflagsVar[arch.ArchVariant]
- toolchainCflags = append(toolchainCflags,
- variantOrDefault(armCpuVariantCflagsVar, arch.CpuVariant))
toolchainClangCflags = append(toolchainClangCflags,
variantOrDefault(armClangCpuVariantCflagsVar, arch.CpuVariant))
@@ -412,18 +337,18 @@
}
case "armv7-a":
fixCortexA8 = "-Wl,--fix-cortex-a8"
- case "armv8-a":
- // Nothing extra for armv8-a
+ case "armv8-a", "armv8-2a":
+ // Nothing extra for armv8-a/armv8-2a
default:
panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
}
return &toolchainArm{
- toolchainCflags: strings.Join(toolchainCflags, " "),
ldflags: strings.Join([]string{
"${config.ArmLdflags}",
fixCortexA8,
}, " "),
+ lldflags: "${config.ArmLldflags}",
toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
}
}
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 9201989..a87d569 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -36,6 +36,7 @@
"-Wno-error=maybe-uninitialized",
"-Wno-error=unused-but-set-parameter",
"-Wno-error=unused-but-set-variable",
+ "-Wno-extended-offsetof",
"-Wno-free-nonheap-object",
"-Wno-literal-suffix",
"-Wno-maybe-uninitialized",
@@ -82,15 +83,27 @@
"--enable-stdcall-fixup",
})
-var ClangLibToolingUnknownCflags = []string{
- "-flto*",
- "-fsanitize*",
-}
+// Ldflags that should be filtered out when linking with clang lld
+var ClangUnknownLldflags = sorted([]string{
+ "-fuse-ld=gold",
+ "-Wl,--fix-cortex-a8",
+ "-Wl,--no-fix-cortex-a8",
+ "-Wl,-m,aarch64_elf64_le_vec",
+})
+
+var ClangLibToolingUnknownCflags = sorted([]string{})
func init() {
pctx.StaticVariable("ClangExtraCflags", strings.Join([]string{
"-D__compiler_offsetof=__builtin_offsetof",
+ // Emit address-significance table which allows linker to perform safe ICF. Clang does
+ // not emit the table by default on Android since NDK still uses GNU binutils.
+ "-faddrsig",
+
+ // -Wimplicit-fallthrough is not enabled by -Wall.
+ "-Wimplicit-fallthrough",
+
// Help catch common 32/64-bit errors.
"-Werror=int-conversion",
@@ -111,26 +124,21 @@
// color codes if it is not running in a terminal.
"-fcolor-diagnostics",
- // http://b/29823425 Disable -Wexpansion-to-defined for Clang update to r271374
- "-Wno-expansion-to-defined",
-
// http://b/68236239 Allow 0/NULL instead of using nullptr everywhere.
"-Wno-zero-as-null-pointer-constant",
- // http://b/36463318 Clang executes with an absolute path, so clang-provided
- // headers are now absolute.
- "-fdebug-prefix-map=$$PWD/=",
- }, " "))
+ // Warnings from clang-7.0
+ "-Wno-sign-compare",
- pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
+ // Warnings from clang-8.0
+ "-Wno-defaulted-function-deleted",
+
// Disable -Winconsistent-missing-override until we can clean up the existing
// codebase for it.
"-Wno-inconsistent-missing-override",
+ }, " "))
- // Bug: http://b/29823425 Disable -Wnull-dereference until the
- // new instances detected by this warning are fixed.
- "-Wno-null-dereference",
-
+ pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
// Enable clang's thread-safety annotations in libcxx.
// Turn off -Wthread-safety-negative, to avoid breaking projects that use -Weverything.
"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
@@ -155,15 +163,31 @@
// http://b/72331526 Disable -Wtautological-* until the instances detected by these
// new warnings are fixed.
"-Wno-tautological-constant-compare",
+ "-Wno-tautological-type-limit-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-tautological-unsigned-zero-compare",
+
+ // Disable c++98-specific warning since Android is not concerned with C++98
+ // compatibility.
+ "-Wno-c++98-compat-extra-semi",
+
+ // Disable this warning because we don't care about behavior with older compilers.
+ "-Wno-return-std-move-in-c++11",
+ }, " "))
+
+ // Extra cflags for projects under external/ directory to disable warnings that are infeasible
+ // to fix in all the external projects and their upstream repos.
+ pctx.StaticVariable("ClangExtraExternalCflags", strings.Join([]string{
+ "-Wno-enum-compare",
+ "-Wno-enum-compare-switch",
// http://b/72331524 Allow null pointer arithmetic until the instances detected by
// this new warning are fixed.
"-Wno-null-pointer-arithmetic",
- // http://b/72330874 Disable -Wenum-compare until the instances detected by this new
- // warning are fixed.
- "-Wno-enum-compare",
- "-Wno-enum-compare-switch",
+ // Bug: http://b/29823425 Disable -Wnull-dereference until the
+ // new instances detected by this warning are fixed.
+ "-Wno-null-dereference",
}, " "))
}
@@ -178,6 +202,17 @@
return ret
}
+func ClangFilterUnknownLldflags(lldflags []string) []string {
+ ret := make([]string, 0, len(lldflags))
+ for _, f := range lldflags {
+ if !inListSorted(f, ClangUnknownLldflags) {
+ ret = append(ret, f)
+ }
+ }
+
+ return ret
+}
+
func inListSorted(s string, list []string) bool {
for _, l := range list {
if s == l {
diff --git a/cc/config/global.go b/cc/config/global.go
index 7a2aa80..fde5cab 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -15,11 +15,10 @@
package config
import (
- "fmt"
- "runtime"
"strings"
"android/soong/android"
+ "android/soong/remoteexec"
)
var (
@@ -85,14 +84,23 @@
"-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--no-undefined-version",
+ "-Wl,--exclude-libs,libgcc.a",
+ "-Wl,--exclude-libs,libgcc_stripped.a",
}
+ deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
+ []string{
+ "-fuse-ld=lld",
+ }...)
+
hostGlobalCflags = []string{}
hostGlobalCppflags = []string{}
hostGlobalLdflags = []string{}
+ hostGlobalLldflags = []string{"-fuse-ld=lld"}
+
commonGlobalCppflags = []string{
"-Wsign-promo",
}
@@ -107,17 +115,16 @@
}
CStdVersion = "gnu99"
- CppStdVersion = "gnu++14"
- GccCppStdVersion = "gnu++11"
+ CppStdVersion = "gnu++17"
ExperimentalCStdVersion = "gnu11"
- ExperimentalCppStdVersion = "gnu++1z"
+ ExperimentalCppStdVersion = "gnu++2a"
NdkMaxPrebuiltVersionInt = 27
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-4691093"
- ClangDefaultShortVersion = "6.0.2"
+ ClangDefaultVersion = "clang-r353983c"
+ ClangDefaultShortVersion = "9.0.3"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -136,22 +143,23 @@
commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=")
}
- pctx.StaticVariable("CommonGlobalCflags", strings.Join(commonGlobalCflags, " "))
pctx.StaticVariable("CommonGlobalConlyflags", strings.Join(commonGlobalConlyflags, " "))
- pctx.StaticVariable("DeviceGlobalCflags", strings.Join(deviceGlobalCflags, " "))
pctx.StaticVariable("DeviceGlobalCppflags", strings.Join(deviceGlobalCppflags, " "))
pctx.StaticVariable("DeviceGlobalLdflags", strings.Join(deviceGlobalLdflags, " "))
- pctx.StaticVariable("HostGlobalCflags", strings.Join(hostGlobalCflags, " "))
+ pctx.StaticVariable("DeviceGlobalLldflags", strings.Join(deviceGlobalLldflags, " "))
pctx.StaticVariable("HostGlobalCppflags", strings.Join(hostGlobalCppflags, " "))
pctx.StaticVariable("HostGlobalLdflags", strings.Join(hostGlobalLdflags, " "))
- pctx.StaticVariable("NoOverrideGlobalCflags", strings.Join(noOverrideGlobalCflags, " "))
-
- pctx.StaticVariable("CommonGlobalCppflags", strings.Join(commonGlobalCppflags, " "))
+ pctx.StaticVariable("HostGlobalLldflags", strings.Join(hostGlobalLldflags, " "))
pctx.StaticVariable("CommonClangGlobalCflags",
strings.Join(append(ClangFilterUnknownCflags(commonGlobalCflags), "${ClangExtraCflags}"), " "))
- pctx.StaticVariable("DeviceClangGlobalCflags",
- strings.Join(append(ClangFilterUnknownCflags(deviceGlobalCflags), "${ClangExtraTargetCflags}"), " "))
+ pctx.VariableFunc("DeviceClangGlobalCflags", func(ctx android.PackageVarContext) string {
+ if ctx.Config().Fuchsia() {
+ return strings.Join(ClangFilterUnknownCflags(deviceGlobalCflags), " ")
+ } else {
+ return strings.Join(append(ClangFilterUnknownCflags(deviceGlobalCflags), "${ClangExtraTargetCflags}"), " ")
+ }
+ })
pctx.StaticVariable("HostClangGlobalCflags",
strings.Join(ClangFilterUnknownCflags(hostGlobalCflags), " "))
pctx.StaticVariable("NoOverrideClangGlobalCflags",
@@ -160,6 +168,8 @@
pctx.StaticVariable("CommonClangGlobalCppflags",
strings.Join(append(ClangFilterUnknownCflags(commonGlobalCppflags), "${ClangExtraCppflags}"), " "))
+ pctx.StaticVariable("ClangExternalCflags", "${ClangExtraExternalCflags}")
+
// Everything in these lists is a crime against abstraction and dependency tracking.
// Do not add anything to this list.
pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I",
@@ -169,7 +179,6 @@
"hardware/libhardware/include",
"hardware/libhardware_legacy/include",
"hardware/ril/include",
- "libnativehelper/include",
"frameworks/native/include",
"frameworks/native/opengl/include",
"frameworks/av/include",
@@ -194,6 +203,7 @@
})
pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
+ pctx.StaticVariable("ClangTidyShellPath", "build/soong/scripts/clang-tidy.sh")
pctx.VariableFunc("ClangShortVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_RELEASE_VERSION"); override != "" {
@@ -202,11 +212,6 @@
return ClangDefaultShortVersion
})
pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib64/clang/${ClangShortVersion}/lib/linux")
- if runtime.GOOS == "darwin" {
- pctx.StaticVariable("LLVMGoldPlugin", "${ClangPath}/lib64/LLVMgold.dylib")
- } else {
- pctx.StaticVariable("LLVMGoldPlugin", "${ClangPath}/lib64/LLVMgold.so")
- }
// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
// being used for the rest of the build process.
@@ -228,6 +233,11 @@
}
return ""
})
+
+ pctx.VariableFunc("RECXXPool", remoteexec.EnvOverrideFunc("RBE_CXX_POOL", remoteexec.DefaultPool))
+ pctx.VariableFunc("RECXXLinksPool", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_POOL", remoteexec.DefaultPool))
+ pctx.VariableFunc("RECXXLinksExecStrategy", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+ pctx.VariableFunc("REAbiDumperExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_DUMPER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
}
var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
@@ -242,9 +252,11 @@
}, " ")
}
-func replaceFirst(slice []string, from, to string) {
- if slice[0] != from {
- panic(fmt.Errorf("Expected %q, found %q", from, to))
+func envOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
+ return func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv(envVar); override != "" {
+ return override
+ }
+ return defaultVal
}
- slice[0] = to
}
diff --git a/cc/config/mips64_device.go b/cc/config/mips64_device.go
index a6191b6..c2af951 100644
--- a/cc/config/mips64_device.go
+++ b/cc/config/mips64_device.go
@@ -55,23 +55,11 @@
)
func init() {
- android.RegisterArchVariants(android.Mips64,
- "mips64r2",
- "mips64r6")
- android.RegisterArchFeatures(android.Mips64,
- "rev6",
- "msa")
- android.RegisterArchVariantFeatures(android.Mips64, "mips64r6",
- "rev6")
-
pctx.StaticVariable("mips64GccVersion", mips64GccVersion)
pctx.SourcePathVariable("Mips64GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/mips/mips64el-linux-android-${mips64GccVersion}")
- pctx.StaticVariable("Mips64Cflags", strings.Join(mips64Cflags, " "))
- pctx.StaticVariable("Mips64Ldflags", strings.Join(mips64Ldflags, " "))
- pctx.StaticVariable("Mips64Cppflags", strings.Join(mips64Cppflags, " "))
pctx.StaticVariable("Mips64IncludeFlags", bionicHeaders("mips"))
// Clang cflags
@@ -83,7 +71,6 @@
// Architecture variant cflags
for variant, cflags := range mips64ArchVariantCflags {
- pctx.StaticVariable("Mips64"+variant+"VariantCflags", strings.Join(cflags, " "))
pctx.StaticVariable("Mips64"+variant+"VariantClangCflags",
strings.Join(ClangFilterUnknownCflags(cflags), " "))
}
@@ -91,8 +78,8 @@
type toolchainMips64 struct {
toolchain64Bit
- cflags, clangCflags string
- toolchainCflags, toolchainClangCflags string
+ clangCflags string
+ toolchainClangCflags string
}
func (t *toolchainMips64) Name() string {
@@ -111,22 +98,6 @@
return mips64GccVersion
}
-func (t *toolchainMips64) ToolchainCflags() string {
- return t.toolchainCflags
-}
-
-func (t *toolchainMips64) Cflags() string {
- return t.cflags
-}
-
-func (t *toolchainMips64) Cppflags() string {
- return "${config.Mips64Cppflags}"
-}
-
-func (t *toolchainMips64) Ldflags() string {
- return "${config.Mips64Ldflags}"
-}
-
func (t *toolchainMips64) IncludeFlags() string {
return "${config.Mips64IncludeFlags}"
}
@@ -155,15 +126,18 @@
return "${config.Mips64ClangLdflags}"
}
-func (toolchainMips64) SanitizerRuntimeLibraryArch() string {
+func (t *toolchainMips64) ClangLldflags() string {
+ // TODO: define and use Mips64ClangLldflags
+ return "${config.Mips64ClangLdflags}"
+}
+
+func (toolchainMips64) LibclangRuntimeLibraryArch() string {
return "mips64"
}
func mips64ToolchainFactory(arch android.Arch) Toolchain {
return &toolchainMips64{
- cflags: "${config.Mips64Cflags}",
clangCflags: "${config.Mips64ClangCflags}",
- toolchainCflags: "${config.Mips64" + arch.ArchVariant + "VariantCflags}",
toolchainClangCflags: "${config.Mips64" + arch.ArchVariant + "VariantClangCflags}",
}
}
diff --git a/cc/config/mips_device.go b/cc/config/mips_device.go
index 9709ada..ddbc41b 100644
--- a/cc/config/mips_device.go
+++ b/cc/config/mips_device.go
@@ -89,31 +89,12 @@
)
func init() {
- android.RegisterArchVariants(android.Mips,
- "mips32_fp",
- "mips32r2_fp",
- "mips32r2_fp_xburst",
- "mips32r2dsp_fp",
- "mips32r2dspr2_fp",
- "mips32r6")
- android.RegisterArchFeatures(android.Mips,
- "dspr2",
- "rev6",
- "msa")
- android.RegisterArchVariantFeatures(android.Mips, "mips32r2dspr2_fp",
- "dspr2")
- android.RegisterArchVariantFeatures(android.Mips, "mips32r6",
- "rev6")
-
pctx.StaticVariable("mipsGccVersion", mipsGccVersion)
pctx.SourcePathVariable("MipsGccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/mips/mips64el-linux-android-${mipsGccVersion}")
pctx.StaticVariable("MipsToolchainLdflags", strings.Join(mipsToolchainLdflags, " "))
- pctx.StaticVariable("MipsCflags", strings.Join(mipsCflags, " "))
- pctx.StaticVariable("MipsLdflags", strings.Join(mipsLdflags, " "))
- pctx.StaticVariable("MipsCppflags", strings.Join(mipsCppflags, " "))
pctx.StaticVariable("MipsIncludeFlags", bionicHeaders("mips"))
// Clang cflags
@@ -125,7 +106,6 @@
// Architecture variant cflags
for variant, cflags := range mipsArchVariantCflags {
- pctx.StaticVariable("Mips"+variant+"VariantCflags", strings.Join(cflags, " "))
pctx.StaticVariable("Mips"+variant+"VariantClangCflags",
strings.Join(ClangFilterUnknownCflags(cflags), " "))
}
@@ -133,8 +113,8 @@
type toolchainMips struct {
toolchain32Bit
- cflags, clangCflags string
- toolchainCflags, toolchainClangCflags string
+ clangCflags string
+ toolchainClangCflags string
}
func (t *toolchainMips) Name() string {
@@ -153,26 +133,6 @@
return mipsGccVersion
}
-func (t *toolchainMips) ToolchainLdflags() string {
- return "${config.MipsToolchainLdflags}"
-}
-
-func (t *toolchainMips) ToolchainCflags() string {
- return t.toolchainCflags
-}
-
-func (t *toolchainMips) Cflags() string {
- return t.cflags
-}
-
-func (t *toolchainMips) Cppflags() string {
- return "${config.MipsCppflags}"
-}
-
-func (t *toolchainMips) Ldflags() string {
- return "${config.MipsLdflags}"
-}
-
func (t *toolchainMips) IncludeFlags() string {
return "${config.MipsIncludeFlags}"
}
@@ -205,15 +165,18 @@
return "${config.MipsClangLdflags}"
}
-func (toolchainMips) SanitizerRuntimeLibraryArch() string {
+func (t *toolchainMips) ClangLldflags() string {
+ // TODO: define and use MipsClangLldflags
+ return "${config.MipsClangLdflags}"
+}
+
+func (toolchainMips) LibclangRuntimeLibraryArch() string {
return "mips"
}
func mipsToolchainFactory(arch android.Arch) Toolchain {
return &toolchainMips{
- cflags: "${config.MipsCflags}",
clangCflags: "${config.MipsClangCflags}",
- toolchainCflags: "${config.Mips" + arch.ArchVariant + "VariantCflags}",
toolchainClangCflags: "${config.Mips" + arch.ArchVariant + "VariantClangCflags}",
}
}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index a20d556..dd52a0e 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -30,6 +30,7 @@
}
return strings.Join([]string{
"-*",
+ "clang-diagnostic-unused-command-line-argument",
"google*",
"misc-macro-parentheses",
"performance*",
@@ -46,6 +47,7 @@
}
return strings.Join([]string{
"-*",
+ "clang-diagnostic-unused-command-line-argument",
"google*",
"-google-build-using-namespace",
"-google-default-arguments",
@@ -59,20 +61,30 @@
// Give warnings to header files only in selected directories.
// Do not give warnings to external or vendor header files, which contain too
// many warnings.
- pctx.StaticVariable("TidyDefaultHeaderDirs", strings.Join([]string{
- "art/",
- "bionic/",
- "bootable/",
- "build/",
- "cts/",
- "dalvik/",
- "developers/",
- "development/",
- "frameworks/",
- "libcore/",
- "libnativehelper/",
- "system/",
- }, "|"))
+ pctx.VariableFunc("TidyDefaultHeaderDirs", func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv("DEFAULT_TIDY_HEADER_DIRS"); override != "" {
+ return override
+ }
+ return strings.Join([]string{
+ "art/",
+ "bionic/",
+ "bootable/",
+ "build/",
+ "cts/",
+ "dalvik/",
+ "developers/",
+ "development/",
+ "frameworks/",
+ "libcore/",
+ "libnativehelper/",
+ "system/",
+ }, "|")
+ })
+
+ // Use WTIH_TIDY_FLAGS to pass extra global default clang-tidy flags.
+ pctx.VariableFunc("TidyWithTidyFlags", func(ctx android.PackageVarContext) string {
+ return ctx.Config().Getenv("WITH_TIDY_FLAGS")
+ })
}
type PathBasedTidyCheck struct {
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 279ceef..d5e9d01 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -36,7 +36,6 @@
factory := toolchainFactories[os][arch.ArchType]
if factory == nil {
panic(fmt.Errorf("Toolchain not found for %s arch %q", os.String(), arch.String()))
- return nil
}
return factory(arch)
}
@@ -50,15 +49,8 @@
GccVersion() string
ToolPath() string
- ToolchainCflags() string
- ToolchainLdflags() string
- Cflags() string
- Cppflags() string
- Ldflags() string
IncludeFlags() string
- InstructionSetFlags(string) (string, error)
- ClangSupported() bool
ClangTriple() string
ToolchainClangCflags() string
ToolchainClangLdflags() string
@@ -66,8 +58,11 @@
ClangCflags() string
ClangCppflags() string
ClangLdflags() string
+ ClangLldflags() string
ClangInstructionSetFlags(string) (string, error)
+ ndkTriple() string
+
YasmFlags() string
WindresFlags() string
@@ -77,7 +72,7 @@
ShlibSuffix() string
ExecutableSuffix() string
- SanitizerRuntimeLibraryArch() string
+ LibclangRuntimeLibraryArch() string
AvailableLibraries() []string
@@ -87,11 +82,17 @@
type toolchainBase struct {
}
-func (toolchainBase) InstructionSetFlags(s string) (string, error) {
- if s != "" {
- return "", fmt.Errorf("instruction_set: %s is not a supported instruction set", s)
+func (t *toolchainBase) ndkTriple() string {
+ return ""
+}
+
+func NDKTriple(toolchain Toolchain) string {
+ triple := toolchain.ndkTriple()
+ if triple == "" {
+ // Use the clang triple if there is no explicit NDK triple
+ triple = toolchain.ClangTriple()
}
- return "", nil
+ return triple
}
func (toolchainBase) ClangInstructionSetFlags(s string) (string, error) {
@@ -101,14 +102,6 @@
return "", nil
}
-func (toolchainBase) ToolchainCflags() string {
- return ""
-}
-
-func (toolchainBase) ToolchainLdflags() string {
- return ""
-}
-
func (toolchainBase) ToolchainClangCflags() string {
return ""
}
@@ -117,10 +110,6 @@
return ""
}
-func (toolchainBase) ClangSupported() bool {
- return true
-}
-
func (toolchainBase) ShlibSuffix() string {
return ".so"
}
@@ -141,7 +130,7 @@
return ""
}
-func (toolchainBase) SanitizerRuntimeLibraryArch() string {
+func (toolchainBase) LibclangRuntimeLibraryArch() string {
return ""
}
@@ -173,18 +162,6 @@
return false
}
-func copyVariantFlags(m map[string][]string) map[string][]string {
- ret := make(map[string][]string, len(m))
- for k, v := range m {
- l := make([]string, len(m[k]))
- for i := range m[k] {
- l[i] = v[i]
- }
- ret[k] = l
- }
- return ret
-}
-
func variantOrDefault(variants map[string]string, choice string) string {
if ret, ok := variants[choice]; ok {
return ret
@@ -199,32 +176,52 @@
return list
}
-func SanitizerRuntimeLibrary(t Toolchain, sanitizer string) string {
- arch := t.SanitizerRuntimeLibraryArch()
+func LibclangRuntimeLibrary(t Toolchain, library string) string {
+ arch := t.LibclangRuntimeLibraryArch()
if arch == "" {
return ""
}
- return "libclang_rt." + sanitizer + "-" + arch + "-android"
+ return "libclang_rt." + library + "-" + arch + "-android"
+}
+
+func BuiltinsRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "builtins")
}
func AddressSanitizerRuntimeLibrary(t Toolchain) string {
- return SanitizerRuntimeLibrary(t, "asan")
+ return LibclangRuntimeLibrary(t, "asan")
+}
+
+func HWAddressSanitizerRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "hwasan")
+}
+
+func HWAddressSanitizerStaticLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "hwasan_static")
}
func UndefinedBehaviorSanitizerRuntimeLibrary(t Toolchain) string {
- return SanitizerRuntimeLibrary(t, "ubsan_standalone")
+ return LibclangRuntimeLibrary(t, "ubsan_standalone")
}
func UndefinedBehaviorSanitizerMinimalRuntimeLibrary(t Toolchain) string {
- return SanitizerRuntimeLibrary(t, "ubsan_minimal")
+ return LibclangRuntimeLibrary(t, "ubsan_minimal")
}
func ThreadSanitizerRuntimeLibrary(t Toolchain) string {
- return SanitizerRuntimeLibrary(t, "tsan")
+ return LibclangRuntimeLibrary(t, "tsan")
}
func ProfileRuntimeLibrary(t Toolchain) string {
- return SanitizerRuntimeLibrary(t, "profile")
+ return LibclangRuntimeLibrary(t, "profile")
+}
+
+func ScudoRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "scudo")
+}
+
+func ScudoMinimalRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "scudo_minimal")
}
func ToolPath(t Toolchain) string {
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
new file mode 100644
index 0000000..542f737
--- /dev/null
+++ b/cc/config/vndk.go
@@ -0,0 +1,157 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+// List of VNDK libraries that have different core variant and vendor variant.
+// For these libraries, the vendor variants must be installed even if the device
+// has VndkUseCoreVariant set.
+var VndkMustUseVendorVariantList = []string{
+ "android.frameworks.sensorservice@1.0",
+ "android.hardware.atrace@1.0",
+ "android.hardware.audio.common@5.0",
+ "android.hardware.audio.effect@2.0",
+ "android.hardware.audio.effect@4.0",
+ "android.hardware.audio.effect@5.0",
+ "android.hardware.audio@2.0",
+ "android.hardware.audio@4.0",
+ "android.hardware.audio@5.0",
+ "android.hardware.automotive.evs@1.0",
+ "android.hardware.automotive.vehicle@2.0",
+ "android.hardware.bluetooth.audio@2.0",
+ "android.hardware.boot@1.0",
+ "android.hardware.broadcastradio@1.0",
+ "android.hardware.broadcastradio@1.1",
+ "android.hardware.broadcastradio@2.0",
+ "android.hardware.camera.device@1.0",
+ "android.hardware.camera.device@3.2",
+ "android.hardware.camera.device@3.3",
+ "android.hardware.camera.device@3.4",
+ "android.hardware.camera.provider@2.4",
+ "android.hardware.cas.native@1.0",
+ "android.hardware.cas@1.0",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.1",
+ "android.hardware.contexthub@1.0",
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.fastboot@1.0",
+ "android.hardware.gatekeeper@1.0",
+ "android.hardware.gnss@1.0",
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.composer@2.2",
+ "android.hardware.health@1.0",
+ "android.hardware.health@2.0",
+ "android.hardware.ir@1.0",
+ "android.hardware.keymaster@3.0",
+ "android.hardware.keymaster@4.0",
+ "android.hardware.light@2.0",
+ "android.hardware.media.bufferpool@1.0",
+ "android.hardware.media.omx@1.0",
+ "android.hardware.memtrack@1.0",
+ "android.hardware.neuralnetworks@1.0",
+ "android.hardware.neuralnetworks@1.1",
+ "android.hardware.neuralnetworks@1.2",
+ "android.hardware.nfc@1.1",
+ "android.hardware.nfc@1.2",
+ "android.hardware.oemlock@1.0",
+ "android.hardware.power.stats@1.0",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.radio@1.4",
+ "android.hardware.secure_element@1.0",
+ "android.hardware.sensors@1.0",
+ "android.hardware.soundtrigger@2.0",
+ "android.hardware.soundtrigger@2.0-core",
+ "android.hardware.soundtrigger@2.1",
+ "android.hardware.tetheroffload.config@1.0",
+ "android.hardware.tetheroffload.control@1.0",
+ "android.hardware.thermal@1.0",
+ "android.hardware.tv.cec@1.0",
+ "android.hardware.tv.input@1.0",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.weaver@1.0",
+ "android.hardware.wifi.hostapd@1.0",
+ "android.hardware.wifi.offload@1.0",
+ "android.hardware.wifi.supplicant@1.0",
+ "android.hardware.wifi.supplicant@1.1",
+ "android.hardware.wifi@1.0",
+ "android.hardware.wifi@1.1",
+ "android.hardware.wifi@1.2",
+ "android.hardwareundtrigger@2.0",
+ "android.hardwareundtrigger@2.0-core",
+ "android.hardwareundtrigger@2.1",
+ "android.hidl.allocator@1.0",
+ "android.hidl.token@1.0",
+ "android.hidl.token@1.0-utils",
+ "android.system.net.netd@1.0",
+ "android.system.wifi.keystore@1.0",
+ "libaudioroute",
+ "libaudioutils",
+ "libbinder",
+ "libcamera_metadata",
+ "libcrypto",
+ "libdiskconfig",
+ "libdumpstateutil",
+ "libexpat",
+ "libfmq",
+ "libgui",
+ "libhidlcache",
+ "libmedia_helper",
+ "libmedia_omx",
+ "libmemtrack",
+ "libnetutils",
+ "libpuresoftkeymasterdevice",
+ "libradio_metadata",
+ "libselinux",
+ "libsoftkeymasterdevice",
+ "libsqlite",
+ "libssl",
+ "libstagefright_bufferqueue_helper",
+ "libstagefright_flacdec",
+ "libstagefright_foundation",
+ "libstagefright_omx",
+ "libstagefright_omx_utils",
+ "libstagefright_soft_aacdec",
+ "libstagefright_soft_aacenc",
+ "libstagefright_soft_amrdec",
+ "libstagefright_soft_amrnbenc",
+ "libstagefright_soft_amrwbenc",
+ "libstagefright_soft_avcdec",
+ "libstagefright_soft_avcenc",
+ "libstagefright_soft_flacdec",
+ "libstagefright_soft_flacenc",
+ "libstagefright_soft_g711dec",
+ "libstagefright_soft_gsmdec",
+ "libstagefright_soft_hevcdec",
+ "libstagefright_soft_mp3dec",
+ "libstagefright_soft_mpeg2dec",
+ "libstagefright_soft_mpeg4dec",
+ "libstagefright_soft_mpeg4enc",
+ "libstagefright_soft_opusdec",
+ "libstagefright_soft_rawdec",
+ "libstagefright_soft_vorbisdec",
+ "libstagefright_soft_vpxdec",
+ "libstagefright_soft_vpxenc",
+ "libstagefright_xmlparser",
+ "libsysutils",
+ "libui",
+ "libvorbisidec",
+ "libxml2",
+ "libziparchive",
+}
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 12f3e6f..0f0420f 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -32,10 +32,16 @@
"-Wl,--hash-style=gnu",
}
+ x86_64Lldflags = ClangFilterUnknownLldflags(x86_64Ldflags)
+
x86_64ArchVariantCflags = map[string][]string{
"": []string{
"-march=x86-64",
},
+ "broadwell": []string{
+ "-march=broadwell",
+ },
+
"haswell": []string{
"-march=core-avx2",
},
@@ -48,6 +54,12 @@
"silvermont": []string{
"-march=slm",
},
+ "skylake": []string{
+ "-march=skylake",
+ },
+ "stoneyridge": []string{
+ "-march=bdver4",
+ },
}
x86_64ArchFeatureCflags = map[string][]string{
@@ -57,6 +69,8 @@
"sse4_2": []string{"-msse4.2"},
"popcnt": []string{"-mpopcnt"},
"avx": []string{"-mavx"},
+ "avx2": []string{"-mavx2"},
+ "avx512": []string{"-mavx512"},
"aes_ni": []string{"-maes"},
}
)
@@ -66,53 +80,11 @@
)
func init() {
- android.RegisterArchVariants(android.X86_64,
- "haswell",
- "ivybridge",
- "sandybridge",
- "silvermont")
- android.RegisterArchFeatures(android.X86_64,
+ android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64,
"ssse3",
"sse4",
"sse4_1",
"sse4_2",
- "aes_ni",
- "avx",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86_64, "",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86_64, "haswell",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86_64, "ivybridge",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86_64, "sandybridge",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86_64, "silvermont",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
"popcnt")
pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
@@ -123,14 +95,14 @@
pctx.StaticVariable("X86_64ToolchainCflags", "-m64")
pctx.StaticVariable("X86_64ToolchainLdflags", "-m64")
- pctx.StaticVariable("X86_64Cflags", strings.Join(x86_64Cflags, " "))
pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " "))
- pctx.StaticVariable("X86_64Cppflags", strings.Join(x86_64Cppflags, " "))
+ pctx.StaticVariable("X86_64Lldflags", strings.Join(x86_64Lldflags, " "))
pctx.StaticVariable("X86_64IncludeFlags", bionicHeaders("x86"))
// Clang cflags
pctx.StaticVariable("X86_64ClangCflags", strings.Join(ClangFilterUnknownCflags(x86_64Cflags), " "))
pctx.StaticVariable("X86_64ClangLdflags", strings.Join(ClangFilterUnknownCflags(x86_64Ldflags), " "))
+ pctx.StaticVariable("X86_64ClangLldflags", strings.Join(ClangFilterUnknownCflags(x86_64Lldflags), " "))
pctx.StaticVariable("X86_64ClangCppflags", strings.Join(ClangFilterUnknownCflags(x86_64Cppflags), " "))
// Yasm flags
@@ -140,7 +112,6 @@
// Architecture variant cflags
for variant, cflags := range x86_64ArchVariantCflags {
- pctx.StaticVariable("X86_64"+variant+"VariantCflags", strings.Join(cflags, " "))
pctx.StaticVariable("X86_64"+variant+"VariantClangCflags",
strings.Join(ClangFilterUnknownCflags(cflags), " "))
}
@@ -148,7 +119,7 @@
type toolchainX86_64 struct {
toolchain64Bit
- toolchainCflags, toolchainClangCflags string
+ toolchainClangCflags string
}
func (t *toolchainX86_64) Name() string {
@@ -167,26 +138,6 @@
return x86_64GccVersion
}
-func (t *toolchainX86_64) ToolchainLdflags() string {
- return "${config.X86_64ToolchainLdflags}"
-}
-
-func (t *toolchainX86_64) ToolchainCflags() string {
- return t.toolchainCflags
-}
-
-func (t *toolchainX86_64) Cflags() string {
- return "${config.X86_64Cflags}"
-}
-
-func (t *toolchainX86_64) Cppflags() string {
- return "${config.X86_64Cppflags}"
-}
-
-func (t *toolchainX86_64) Ldflags() string {
- return "${config.X86_64Ldflags}"
-}
-
func (t *toolchainX86_64) IncludeFlags() string {
return "${config.X86_64IncludeFlags}"
}
@@ -215,32 +166,29 @@
return "${config.X86_64Ldflags}"
}
+func (t *toolchainX86_64) ClangLldflags() string {
+ return "${config.X86_64Lldflags}"
+}
+
func (t *toolchainX86_64) YasmFlags() string {
return "${config.X86_64YasmFlags}"
}
-func (toolchainX86_64) SanitizerRuntimeLibraryArch() string {
+func (toolchainX86_64) LibclangRuntimeLibraryArch() string {
return "x86_64"
}
func x86_64ToolchainFactory(arch android.Arch) Toolchain {
- toolchainCflags := []string{
- "${config.X86_64ToolchainCflags}",
- "${config.X86_64" + arch.ArchVariant + "VariantCflags}",
- }
-
toolchainClangCflags := []string{
"${config.X86_64ToolchainCflags}",
"${config.X86_64" + arch.ArchVariant + "VariantClangCflags}",
}
for _, feature := range arch.ArchFeatures {
- toolchainCflags = append(toolchainCflags, x86_64ArchFeatureCflags[feature]...)
toolchainClangCflags = append(toolchainClangCflags, x86_64ArchFeatureCflags[feature]...)
}
return &toolchainX86_64{
- toolchainCflags: strings.Join(toolchainCflags, " "),
toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
}
}
diff --git a/cc/config/x86_64_fuchsia_device.go b/cc/config/x86_64_fuchsia_device.go
new file mode 100644
index 0000000..79af00c
--- /dev/null
+++ b/cc/config/x86_64_fuchsia_device.go
@@ -0,0 +1,106 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "android/soong/android"
+)
+
+var fuchsiaSysRoot string = "prebuilts/fuchsia_sdk/arch/x64/sysroot"
+var fuchsiaPrebuiltLibsRoot string = "fuchsia/prebuilt_libs"
+
+type toolchainFuchsia struct {
+ cFlags, ldFlags string
+}
+
+type toolchainFuchsiaX8664 struct {
+ toolchain64Bit
+ toolchainFuchsia
+}
+
+func (t *toolchainFuchsiaX8664) Name() string {
+ return "x86_64"
+}
+
+func (t *toolchainFuchsiaX8664) GccRoot() string {
+ return "${config.X86_64GccRoot}"
+}
+
+func (t *toolchainFuchsiaX8664) GccTriple() string {
+ return "x86_64-linux-android"
+}
+
+func (t *toolchainFuchsiaX8664) GccVersion() string {
+ return x86_64GccVersion
+}
+
+func (t *toolchainFuchsiaX8664) Cflags() string {
+ return ""
+}
+
+func (t *toolchainFuchsiaX8664) Cppflags() string {
+ return ""
+}
+
+func (t *toolchainFuchsiaX8664) Ldflags() string {
+ return ""
+}
+
+func (t *toolchainFuchsiaX8664) IncludeFlags() string {
+ return ""
+}
+
+func (t *toolchainFuchsiaX8664) ClangTriple() string {
+ return "x86_64-fuchsia-android"
+}
+
+func (t *toolchainFuchsiaX8664) ClangCppflags() string {
+ return "-Wno-error=deprecated-declarations"
+}
+
+func (t *toolchainFuchsiaX8664) ClangLdflags() string {
+ return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -L" + fuchsiaPrebuiltLibsRoot + "/x86_64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/x64/dist/"
+
+}
+
+func (t *toolchainFuchsiaX8664) ClangLldflags() string {
+ return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -L" + fuchsiaPrebuiltLibsRoot + "/x86_64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/x64/dist/"
+}
+
+func (t *toolchainFuchsiaX8664) ClangCflags() string {
+ return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -I" + fuchsiaSysRoot + "/include"
+}
+
+func (t *toolchainFuchsiaX8664) Bionic() bool {
+ return false
+}
+
+func (t *toolchainFuchsiaX8664) YasmFlags() string {
+ return "-f elf64 -m amd64"
+}
+
+func (t *toolchainFuchsiaX8664) ToolchainClangCflags() string {
+ return "-DUSE_SSSE3 -mssse3"
+}
+
+var toolchainFuchsiaSingleton Toolchain = &toolchainFuchsiaX8664{}
+
+func fuchsiaToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainFuchsiaSingleton
+}
+
+func init() {
+ registerToolchainFactory(android.Fuchsia, android.X86_64, fuchsiaToolchainFactory)
+}
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index cae9757..1026370 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -37,28 +37,14 @@
"-isysroot ${macSdkRoot}",
"-mmacosx-version-min=${macMinVersion}",
"-DMACOSX_DEPLOYMENT_TARGET=${macMinVersion}",
+
+ "-m64",
}
darwinLdflags = []string{
"-isysroot ${macSdkRoot}",
"-Wl,-syslibroot,${macSdkRoot}",
"-mmacosx-version-min=${macMinVersion}",
- }
-
- // Extended cflags
- darwinX86Cflags = []string{
- "-m32",
- }
-
- darwinX8664Cflags = []string{
- "-m64",
- }
-
- darwinX86Ldflags = []string{
- "-m32",
- }
-
- darwinX8664Ldflags = []string{
"-m64",
}
@@ -67,21 +53,16 @@
"-fstack-protector-strong",
}...)
- darwinX86ClangCflags = append(ClangFilterUnknownCflags(darwinX86Cflags), []string{
- "-msse3",
- }...)
-
darwinClangLdflags = ClangFilterUnknownCflags(darwinLdflags)
- darwinX86ClangLdflags = ClangFilterUnknownCflags(darwinX86Ldflags)
-
- darwinX8664ClangLdflags = ClangFilterUnknownCflags(darwinX8664Ldflags)
+ darwinClangLldflags = ClangFilterUnknownLldflags(darwinClangLdflags)
darwinSupportedSdkVersions = []string{
"10.10",
"10.11",
"10.12",
"10.13",
+ "10.14",
}
darwinAvailableLibraries = append(
@@ -98,6 +79,7 @@
"-framework Foundation",
"-framework IOKit",
"-framework Security",
+ "-framework SystemConfiguration",
)
)
@@ -136,25 +118,11 @@
pctx.StaticVariable("DarwinGccTriple", "i686-apple-darwin11")
- pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " "))
- pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " "))
-
pctx.StaticVariable("DarwinClangCflags", strings.Join(darwinClangCflags, " "))
pctx.StaticVariable("DarwinClangLdflags", strings.Join(darwinClangLdflags, " "))
+ pctx.StaticVariable("DarwinClangLldflags", strings.Join(darwinClangLldflags, " "))
- // Extended cflags
- pctx.StaticVariable("DarwinX86Cflags", strings.Join(darwinX86Cflags, " "))
- pctx.StaticVariable("DarwinX8664Cflags", strings.Join(darwinX8664Cflags, " "))
- pctx.StaticVariable("DarwinX86Ldflags", strings.Join(darwinX86Ldflags, " "))
- pctx.StaticVariable("DarwinX8664Ldflags", strings.Join(darwinX8664Ldflags, " "))
-
- pctx.StaticVariable("DarwinX86ClangCflags", strings.Join(darwinX86ClangCflags, " "))
- pctx.StaticVariable("DarwinX8664ClangCflags",
- strings.Join(ClangFilterUnknownCflags(darwinX8664Cflags), " "))
- pctx.StaticVariable("DarwinX86ClangLdflags", strings.Join(darwinX86ClangLdflags, " "))
- pctx.StaticVariable("DarwinX8664ClangLdflags", strings.Join(darwinX8664ClangLdflags, " "))
- pctx.StaticVariable("DarwinX86YasmFlags", "-f macho -m x86")
- pctx.StaticVariable("DarwinX8664YasmFlags", "-f macho -m amd64")
+ pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64")
}
func xcrun(ctx android.PackageVarContext, args ...string) string {
@@ -193,23 +161,10 @@
type toolchainDarwin struct {
cFlags, ldFlags string
-}
-
-type toolchainDarwinX86 struct {
- toolchain32Bit
- toolchainDarwin
-}
-
-type toolchainDarwinX8664 struct {
toolchain64Bit
- toolchainDarwin
}
-func (t *toolchainDarwinX86) Name() string {
- return "x86"
-}
-
-func (t *toolchainDarwinX8664) Name() string {
+func (t *toolchainDarwin) Name() string {
return "x86_64"
}
@@ -225,64 +180,32 @@
return darwinGccVersion
}
-func (t *toolchainDarwin) Cflags() string {
- return "${config.DarwinCflags} ${config.DarwinX86Cflags}"
-}
-
-func (t *toolchainDarwinX8664) Cflags() string {
- return "${config.DarwinCflags} ${config.DarwinX8664Cflags}"
-}
-
-func (t *toolchainDarwin) Cppflags() string {
- return ""
-}
-
-func (t *toolchainDarwinX86) Ldflags() string {
- return "${config.DarwinLdflags} ${config.DarwinX86Ldflags}"
-}
-
-func (t *toolchainDarwinX8664) Ldflags() string {
- return "${config.DarwinLdflags} ${config.DarwinX8664Ldflags}"
-}
-
func (t *toolchainDarwin) IncludeFlags() string {
return ""
}
-func (t *toolchainDarwinX86) ClangTriple() string {
- return "i686-apple-darwin"
-}
-
-func (t *toolchainDarwinX86) ClangCflags() string {
- return "${config.DarwinClangCflags} ${config.DarwinX86ClangCflags}"
-}
-
-func (t *toolchainDarwinX8664) ClangTriple() string {
+func (t *toolchainDarwin) ClangTriple() string {
return "x86_64-apple-darwin"
}
-func (t *toolchainDarwinX8664) ClangCflags() string {
- return "${config.DarwinClangCflags} ${config.DarwinX8664ClangCflags}"
+func (t *toolchainDarwin) ClangCflags() string {
+ return "${config.DarwinClangCflags}"
}
func (t *toolchainDarwin) ClangCppflags() string {
return ""
}
-func (t *toolchainDarwinX86) ClangLdflags() string {
- return "${config.DarwinClangLdflags} ${config.DarwinX86ClangLdflags}"
+func (t *toolchainDarwin) ClangLdflags() string {
+ return "${config.DarwinClangLdflags}"
}
-func (t *toolchainDarwinX8664) ClangLdflags() string {
- return "${config.DarwinClangLdflags} ${config.DarwinX8664ClangLdflags}"
+func (t *toolchainDarwin) ClangLldflags() string {
+ return "${config.DarwinClangLldflags}"
}
-func (t *toolchainDarwinX86) YasmFlags() string {
- return "${config.DarwinX86YasmFlags}"
-}
-
-func (t *toolchainDarwinX8664) YasmFlags() string {
- return "${config.DarwinX8664YasmFlags}"
+func (t *toolchainDarwin) YasmFlags() string {
+ return "${config.DarwinYasmFlags}"
}
func (t *toolchainDarwin) ShlibSuffix() string {
@@ -301,18 +224,12 @@
return "${config.MacToolPath}"
}
-var toolchainDarwinX86Singleton Toolchain = &toolchainDarwinX86{}
-var toolchainDarwinX8664Singleton Toolchain = &toolchainDarwinX8664{}
+var toolchainDarwinSingleton Toolchain = &toolchainDarwin{}
-func darwinX86ToolchainFactory(arch android.Arch) Toolchain {
- return toolchainDarwinX86Singleton
-}
-
-func darwinX8664ToolchainFactory(arch android.Arch) Toolchain {
- return toolchainDarwinX8664Singleton
+func darwinToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainDarwinSingleton
}
func init() {
- registerToolchainFactory(android.Darwin, android.X86, darwinX86ToolchainFactory)
- registerToolchainFactory(android.Darwin, android.X86_64, darwinX8664ToolchainFactory)
+ registerToolchainFactory(android.Darwin, android.X86_64, darwinToolchainFactory)
}
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index b9ce4af..500014e 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -38,6 +38,8 @@
"-Wl,--hash-style=gnu",
}
+ x86Lldflags = ClangFilterUnknownLldflags(x86Ldflags)
+
x86ArchVariantCflags = map[string][]string{
"": []string{
"-march=prescott",
@@ -49,6 +51,10 @@
"-march=atom",
"-mfpmath=sse",
},
+ "broadwell": []string{
+ "-march=broadwell",
+ "-mfpmath=sse",
+ },
"haswell": []string{
"-march=core-avx2",
"-mfpmath=sse",
@@ -65,6 +71,14 @@
"-march=slm",
"-mfpmath=sse",
},
+ "skylake": []string{
+ "-march=skylake",
+ "-mfpmath=sse",
+ },
+ "stoneyridge": []string{
+ "-march=bdver4",
+ "-mfpmath=sse",
+ },
}
x86ArchFeatureCflags = map[string][]string{
@@ -73,6 +87,7 @@
"sse4_1": []string{"-msse4.1"},
"sse4_2": []string{"-msse4.2"},
"avx": []string{"-mavx"},
+ "avx2": []string{"-mavx2"},
"aes_ni": []string{"-maes"},
}
)
@@ -82,63 +97,6 @@
)
func init() {
- android.RegisterArchVariants(android.X86,
- "atom",
- "haswell",
- "ivybridge",
- "sandybridge",
- "silvermont",
- "x86_64")
- android.RegisterArchFeatures(android.X86,
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- "movbe")
- android.RegisterArchVariantFeatures(android.X86, "x86_64",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86, "atom",
- "ssse3",
- "movbe")
- android.RegisterArchVariantFeatures(android.X86, "haswell",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- "movbe")
- android.RegisterArchVariantFeatures(android.X86, "ivybridge",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86, "sandybridge",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt")
- android.RegisterArchVariantFeatures(android.X86, "silvermont",
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "popcnt",
- "movbe")
-
pctx.StaticVariable("x86GccVersion", x86GccVersion)
pctx.SourcePathVariable("X86GccRoot",
@@ -147,14 +105,14 @@
pctx.StaticVariable("X86ToolchainCflags", "-m32")
pctx.StaticVariable("X86ToolchainLdflags", "-m32")
- pctx.StaticVariable("X86Cflags", strings.Join(x86Cflags, " "))
pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " "))
- pctx.StaticVariable("X86Cppflags", strings.Join(x86Cppflags, " "))
+ pctx.StaticVariable("X86Lldflags", strings.Join(x86Lldflags, " "))
pctx.StaticVariable("X86IncludeFlags", bionicHeaders("x86"))
// Clang cflags
pctx.StaticVariable("X86ClangCflags", strings.Join(ClangFilterUnknownCflags(x86ClangCflags), " "))
pctx.StaticVariable("X86ClangLdflags", strings.Join(ClangFilterUnknownCflags(x86Ldflags), " "))
+ pctx.StaticVariable("X86ClangLldflags", strings.Join(ClangFilterUnknownCflags(x86Lldflags), " "))
pctx.StaticVariable("X86ClangCppflags", strings.Join(ClangFilterUnknownCflags(x86Cppflags), " "))
// Yasm flags
@@ -164,7 +122,6 @@
// Architecture variant cflags
for variant, cflags := range x86ArchVariantCflags {
- pctx.StaticVariable("X86"+variant+"VariantCflags", strings.Join(cflags, " "))
pctx.StaticVariable("X86"+variant+"VariantClangCflags",
strings.Join(ClangFilterUnknownCflags(cflags), " "))
}
@@ -172,7 +129,7 @@
type toolchainX86 struct {
toolchain32Bit
- toolchainCflags, toolchainClangCflags string
+ toolchainClangCflags string
}
func (t *toolchainX86) Name() string {
@@ -191,26 +148,6 @@
return x86GccVersion
}
-func (t *toolchainX86) ToolchainLdflags() string {
- return "${config.X86ToolchainLdflags}"
-}
-
-func (t *toolchainX86) ToolchainCflags() string {
- return t.toolchainCflags
-}
-
-func (t *toolchainX86) Cflags() string {
- return "${config.X86Cflags}"
-}
-
-func (t *toolchainX86) Cppflags() string {
- return "${config.X86Cppflags}"
-}
-
-func (t *toolchainX86) Ldflags() string {
- return "${config.X86Ldflags}"
-}
-
func (t *toolchainX86) IncludeFlags() string {
return "${config.X86IncludeFlags}"
}
@@ -239,32 +176,29 @@
return "${config.X86Ldflags}"
}
+func (t *toolchainX86) ClangLldflags() string {
+ return "${config.X86Lldflags}"
+}
+
func (t *toolchainX86) YasmFlags() string {
return "${config.X86YasmFlags}"
}
-func (toolchainX86) SanitizerRuntimeLibraryArch() string {
+func (toolchainX86) LibclangRuntimeLibraryArch() string {
return "i686"
}
func x86ToolchainFactory(arch android.Arch) Toolchain {
- toolchainCflags := []string{
- "${config.X86ToolchainCflags}",
- "${config.X86" + arch.ArchVariant + "VariantCflags}",
- }
-
toolchainClangCflags := []string{
"${config.X86ToolchainCflags}",
"${config.X86" + arch.ArchVariant + "VariantClangCflags}",
}
for _, feature := range arch.ArchFeatures {
- toolchainCflags = append(toolchainCflags, x86ArchFeatureCflags[feature]...)
toolchainClangCflags = append(toolchainClangCflags, x86ArchFeatureCflags[feature]...)
}
return &toolchainX86{
- toolchainCflags: strings.Join(toolchainCflags, " "),
toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
}
}
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 9f0bbbd..fb1cdeb 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -61,11 +61,14 @@
// Use the device gcc toolchain
"--gcc-toolchain=${LinuxBionicGccRoot}",
})
+
+ linuxBionicLldflags = ClangFilterUnknownLldflags(linuxBionicLdflags)
)
func init() {
pctx.StaticVariable("LinuxBionicCflags", strings.Join(linuxBionicCflags, " "))
pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
+ pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLldflags, " "))
pctx.StaticVariable("LinuxBionicIncludeFlags", bionicHeaders("x86"))
@@ -93,18 +96,6 @@
return "4.9"
}
-func (t *toolchainLinuxBionic) Cflags() string {
- return ""
-}
-
-func (t *toolchainLinuxBionic) Cppflags() string {
- return ""
-}
-
-func (t *toolchainLinuxBionic) Ldflags() string {
- return ""
-}
-
func (t *toolchainLinuxBionic) IncludeFlags() string {
return "${config.LinuxBionicIncludeFlags}"
}
@@ -126,10 +117,14 @@
return "${config.LinuxBionicLdflags}"
}
+func (t *toolchainLinuxBionic) ClangLldflags() string {
+ return "${config.LinuxBionicLldflags}"
+}
+
func (t *toolchainLinuxBionic) ToolchainClangCflags() string {
return "-m64 -march=x86-64" +
// TODO: We're not really android, but we don't have a triple yet b/31393676
- " -U__ANDROID__ -fno-emulated-tls"
+ " -U__ANDROID__"
}
func (t *toolchainLinuxBionic) ToolchainClangLdflags() string {
@@ -144,6 +139,10 @@
return true
}
+func (toolchainLinuxBionic) LibclangRuntimeLibraryArch() string {
+ return "x86_64"
+}
+
var toolchainLinuxBionicSingleton Toolchain = &toolchainLinuxBionic{}
func linuxBionicToolchainFactory(arch android.Arch) Toolchain {
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 4f05068..f072f34 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -78,30 +78,23 @@
"--sysroot ${LinuxGccRoot}/sysroot",
}...)
+ linuxClangLldflags = ClangFilterUnknownLldflags(linuxClangLdflags)
+
linuxX86ClangLdflags = append(ClangFilterUnknownCflags(linuxX86Ldflags), []string{
"-B${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}/32",
"-L${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}/32",
"-L${LinuxGccRoot}/${LinuxGccTriple}/lib32",
}...)
+ linuxX86ClangLldflags = ClangFilterUnknownLldflags(linuxX86ClangLdflags)
+
linuxX8664ClangLdflags = append(ClangFilterUnknownCflags(linuxX8664Ldflags), []string{
"-B${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}",
"-L${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}",
"-L${LinuxGccRoot}/${LinuxGccTriple}/lib64",
}...)
- linuxClangCppflags = []string{
- "-isystem ${LinuxGccRoot}/${LinuxGccTriple}/include/c++/${LinuxGccVersion}",
- "-isystem ${LinuxGccRoot}/${LinuxGccTriple}/include/c++/${LinuxGccVersion}/backward",
- }
-
- linuxX86ClangCppflags = []string{
- "-isystem ${LinuxGccRoot}/${LinuxGccTriple}/include/c++/${LinuxGccVersion}/${LinuxGccTriple}/32",
- }
-
- linuxX8664ClangCppflags = []string{
- "-isystem ${LinuxGccRoot}/${LinuxGccTriple}/include/c++/${LinuxGccVersion}/${LinuxGccTriple}",
- }
+ linuxX8664ClangLldflags = ClangFilterUnknownLldflags(linuxX8664ClangLdflags)
linuxAvailableLibraries = addPrefix([]string{
"c",
@@ -118,38 +111,37 @@
)
const (
- linuxGccVersion = "4.8"
+ linuxGccVersion = "4.8.3"
+ linuxGlibcVersion = "2.17"
)
func init() {
pctx.StaticVariable("LinuxGccVersion", linuxGccVersion)
+ pctx.StaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+ // Most places use the full GCC version. A few only use up to the first two numbers.
+ if p := strings.Split(linuxGccVersion, "."); len(p) > 2 {
+ pctx.StaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
+ } else {
+ pctx.StaticVariable("ShortLinuxGccVersion", linuxGccVersion)
+ }
pctx.SourcePathVariable("LinuxGccRoot",
- "prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-linux-glibc2.15-${LinuxGccVersion}")
+ "prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
pctx.StaticVariable("LinuxGccTriple", "x86_64-linux")
- pctx.StaticVariable("LinuxCflags", strings.Join(linuxCflags, " "))
- pctx.StaticVariable("LinuxLdflags", strings.Join(linuxLdflags, " "))
-
pctx.StaticVariable("LinuxClangCflags", strings.Join(linuxClangCflags, " "))
pctx.StaticVariable("LinuxClangLdflags", strings.Join(linuxClangLdflags, " "))
- pctx.StaticVariable("LinuxClangCppflags", strings.Join(linuxClangCppflags, " "))
-
- // Extended cflags
- pctx.StaticVariable("LinuxX86Cflags", strings.Join(linuxX86Cflags, " "))
- pctx.StaticVariable("LinuxX8664Cflags", strings.Join(linuxX8664Cflags, " "))
- pctx.StaticVariable("LinuxX86Ldflags", strings.Join(linuxX86Ldflags, " "))
- pctx.StaticVariable("LinuxX8664Ldflags", strings.Join(linuxX8664Ldflags, " "))
+ pctx.StaticVariable("LinuxClangLldflags", strings.Join(linuxClangLldflags, " "))
pctx.StaticVariable("LinuxX86ClangCflags",
strings.Join(ClangFilterUnknownCflags(linuxX86Cflags), " "))
pctx.StaticVariable("LinuxX8664ClangCflags",
strings.Join(ClangFilterUnknownCflags(linuxX8664Cflags), " "))
pctx.StaticVariable("LinuxX86ClangLdflags", strings.Join(linuxX86ClangLdflags, " "))
+ pctx.StaticVariable("LinuxX86ClangLldflags", strings.Join(linuxX86ClangLldflags, " "))
pctx.StaticVariable("LinuxX8664ClangLdflags", strings.Join(linuxX8664ClangLdflags, " "))
- pctx.StaticVariable("LinuxX86ClangCppflags", strings.Join(linuxX86ClangCppflags, " "))
- pctx.StaticVariable("LinuxX8664ClangCppflags", strings.Join(linuxX8664ClangCppflags, " "))
+ pctx.StaticVariable("LinuxX8664ClangLldflags", strings.Join(linuxX8664ClangLldflags, " "))
// Yasm flags
pctx.StaticVariable("LinuxX86YasmFlags", "-f elf32 -m x86")
pctx.StaticVariable("LinuxX8664YasmFlags", "-f elf64 -m amd64")
@@ -189,26 +181,6 @@
return linuxGccVersion
}
-func (t *toolchainLinuxX86) Cflags() string {
- return "${config.LinuxCflags} ${config.LinuxX86Cflags}"
-}
-
-func (t *toolchainLinuxX8664) Cflags() string {
- return "${config.LinuxCflags} ${config.LinuxX8664Cflags}"
-}
-
-func (t *toolchainLinux) Cppflags() string {
- return ""
-}
-
-func (t *toolchainLinuxX86) Ldflags() string {
- return "${config.LinuxLdflags} ${config.LinuxX86Ldflags}"
-}
-
-func (t *toolchainLinuxX8664) Ldflags() string {
- return "${config.LinuxLdflags} ${config.LinuxX8664Ldflags}"
-}
-
func (t *toolchainLinux) IncludeFlags() string {
return ""
}
@@ -222,7 +194,7 @@
}
func (t *toolchainLinuxX86) ClangCppflags() string {
- return "${config.LinuxClangCppflags} ${config.LinuxX86ClangCppflags}"
+ return ""
}
func (t *toolchainLinuxX8664) ClangTriple() string {
@@ -234,17 +206,25 @@
}
func (t *toolchainLinuxX8664) ClangCppflags() string {
- return "${config.LinuxClangCppflags} ${config.LinuxX8664ClangCppflags}"
+ return ""
}
func (t *toolchainLinuxX86) ClangLdflags() string {
return "${config.LinuxClangLdflags} ${config.LinuxX86ClangLdflags}"
}
+func (t *toolchainLinuxX86) ClangLldflags() string {
+ return "${config.LinuxClangLldflags} ${config.LinuxX86ClangLldflags}"
+}
+
func (t *toolchainLinuxX8664) ClangLdflags() string {
return "${config.LinuxClangLdflags} ${config.LinuxX8664ClangLdflags}"
}
+func (t *toolchainLinuxX8664) ClangLldflags() string {
+ return "${config.LinuxClangLldflags} ${config.LinuxX8664ClangLldflags}"
+}
+
func (t *toolchainLinuxX86) YasmFlags() string {
return "${config.LinuxX86YasmFlags}"
}
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 016e711..0f500b6 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -33,9 +33,9 @@
// Use C99-compliant printf functions (%zd).
"-D__USE_MINGW_ANSI_STDIO=1",
- // Admit to using >= Vista. Both are needed because of <_mingw.h>.
- "-D_WIN32_WINNT=0x0600",
- "-DWINVER=0x0600",
+ // Admit to using >= Windows 7. Both are needed because of <_mingw.h>.
+ "-D_WIN32_WINNT=0x0601",
+ "-DWINVER=0x0601",
// Get 64-bit off_t and related functions.
"-D_FILE_OFFSET_BITS=64",
@@ -45,26 +45,21 @@
windowsIncludeFlags = []string{
"-isystem ${WindowsGccRoot}/${WindowsGccTriple}/include",
- "-isystem ${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/include",
}
- windowsClangCppflags = []string{
- "-isystem ${WindowsGccRoot}/${WindowsGccTriple}/include/c++/4.8.3",
- "-isystem ${WindowsGccRoot}/${WindowsGccTriple}/include/c++/4.8.3/backward",
- }
+ windowsClangCppflags = []string{}
- windowsX86ClangCppflags = []string{
- "-isystem ${WindowsGccRoot}/${WindowsGccTriple}/include/c++/4.8.3/${WindowsGccTriple}/32",
- }
+ windowsX86ClangCppflags = []string{}
- windowsX8664ClangCppflags = []string{
- "-isystem ${WindowsGccRoot}/${WindowsGccTriple}/include/c++/4.8.3/${WindowsGccTriple}",
- }
+ windowsX8664ClangCppflags = []string{}
windowsLdflags = []string{
"--enable-stdcall-fixup",
+ "-Wl,--dynamicbase",
+ "-Wl,--nxcompat",
}
- windowsClangLdflags = append(ClangFilterUnknownCflags(windowsLdflags), []string{}...)
+ windowsClangLdflags = append(ClangFilterUnknownCflags(windowsLdflags), []string{}...)
+ windowsClangLldflags = ClangFilterUnknownLldflags(windowsClangLdflags)
windowsX86Cflags = []string{
"-m32",
@@ -80,27 +75,34 @@
"-L${WindowsGccRoot}/${WindowsGccTriple}/lib32",
}
windowsX86ClangLdflags = append(ClangFilterUnknownCflags(windowsX86Ldflags), []string{
+ "-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
"-B${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/32",
"-L${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/32",
"-B${WindowsGccRoot}/${WindowsGccTriple}/lib32",
}...)
+ windowsX86ClangLldflags = ClangFilterUnknownLldflags(windowsX86ClangLdflags)
windowsX8664Ldflags = []string{
"-m64",
"-L${WindowsGccRoot}/${WindowsGccTriple}/lib64",
+ "-Wl,--high-entropy-va",
}
windowsX8664ClangLdflags = append(ClangFilterUnknownCflags(windowsX8664Ldflags), []string{
+ "-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
"-B${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3",
"-L${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3",
"-B${WindowsGccRoot}/${WindowsGccTriple}/lib64",
}...)
+ windowsX8664ClangLldflags = ClangFilterUnknownLldflags(windowsX8664ClangLdflags)
windowsAvailableLibraries = addPrefix([]string{
"gdi32",
"imagehlp",
"iphlpapi",
"netapi32",
+ "oleaut32",
"ole32",
+ "opengl32",
"powrprof",
"psapi",
"pthread",
@@ -108,6 +110,7 @@
"uuid",
"version",
"ws2_32",
+ "windowscodecs",
}, "-l")
)
@@ -123,28 +126,26 @@
pctx.StaticVariable("WindowsGccTriple", "x86_64-w64-mingw32")
- pctx.StaticVariable("WindowsCflags", strings.Join(windowsCflags, " "))
- pctx.StaticVariable("WindowsLdflags", strings.Join(windowsLdflags, " "))
-
pctx.StaticVariable("WindowsClangCflags", strings.Join(windowsClangCflags, " "))
pctx.StaticVariable("WindowsClangLdflags", strings.Join(windowsClangLdflags, " "))
+ pctx.StaticVariable("WindowsClangLldflags", strings.Join(windowsClangLldflags, " "))
pctx.StaticVariable("WindowsClangCppflags", strings.Join(windowsClangCppflags, " "))
- pctx.StaticVariable("WindowsX86Cflags", strings.Join(windowsX86Cflags, " "))
- pctx.StaticVariable("WindowsX8664Cflags", strings.Join(windowsX8664Cflags, " "))
- pctx.StaticVariable("WindowsX86Ldflags", strings.Join(windowsX86Ldflags, " "))
- pctx.StaticVariable("WindowsX8664Ldflags", strings.Join(windowsX8664Ldflags, " "))
-
pctx.StaticVariable("WindowsX86ClangCflags",
strings.Join(ClangFilterUnknownCflags(windowsX86Cflags), " "))
pctx.StaticVariable("WindowsX8664ClangCflags",
strings.Join(ClangFilterUnknownCflags(windowsX8664Cflags), " "))
pctx.StaticVariable("WindowsX86ClangLdflags", strings.Join(windowsX86ClangLdflags, " "))
+ pctx.StaticVariable("WindowsX86ClangLldflags", strings.Join(windowsX86ClangLldflags, " "))
pctx.StaticVariable("WindowsX8664ClangLdflags", strings.Join(windowsX8664ClangLdflags, " "))
+ pctx.StaticVariable("WindowsX8664ClangLldflags", strings.Join(windowsX8664ClangLldflags, " "))
pctx.StaticVariable("WindowsX86ClangCppflags", strings.Join(windowsX86ClangCppflags, " "))
pctx.StaticVariable("WindowsX8664ClangCppflags", strings.Join(windowsX8664ClangCppflags, " "))
pctx.StaticVariable("WindowsIncludeFlags", strings.Join(windowsIncludeFlags, " "))
+ // Yasm flags
+ pctx.StaticVariable("WindowsX86YasmFlags", "-f win32 -m x86")
+ pctx.StaticVariable("WindowsX8664YasmFlags", "-f win64 -m amd64")
}
type toolchainWindows struct {
@@ -181,26 +182,6 @@
return windowsGccVersion
}
-func (t *toolchainWindowsX86) Cflags() string {
- return "${config.WindowsCflags} ${config.WindowsX86Cflags}"
-}
-
-func (t *toolchainWindowsX8664) Cflags() string {
- return "${config.WindowsCflags} ${config.WindowsX8664Cflags}"
-}
-
-func (t *toolchainWindows) Cppflags() string {
- return ""
-}
-
-func (t *toolchainWindowsX86) Ldflags() string {
- return "${config.WindowsLdflags} ${config.WindowsX86Ldflags}"
-}
-
-func (t *toolchainWindowsX8664) Ldflags() string {
- return "${config.WindowsLdflags} ${config.WindowsX8664Ldflags}"
-}
-
func (t *toolchainWindows) IncludeFlags() string {
return "${config.WindowsIncludeFlags}"
}
@@ -213,10 +194,6 @@
return "-F pe-x86-64"
}
-func (t *toolchainWindows) ClangSupported() bool {
- return false
-}
-
func (t *toolchainWindowsX86) ClangTriple() string {
return "i686-windows-gnu"
}
@@ -245,10 +222,26 @@
return "${config.WindowsClangLdflags} ${config.WindowsX86ClangLdflags}"
}
+func (t *toolchainWindowsX86) ClangLldflags() string {
+ return "${config.WindowsClangLldflags} ${config.WindowsX86ClangLldflags}"
+}
+
func (t *toolchainWindowsX8664) ClangLdflags() string {
return "${config.WindowsClangLdflags} ${config.WindowsX8664ClangLdflags}"
}
+func (t *toolchainWindowsX8664) ClangLldflags() string {
+ return "${config.WindowsClangLldflags} ${config.WindowsX8664ClangLldflags}"
+}
+
+func (t *toolchainWindowsX86) YasmFlags() string {
+ return "${config.WindowsX86YasmFlags}"
+}
+
+func (t *toolchainWindowsX8664) YasmFlags() string {
+ return "${config.WindowsX8664YasmFlags}"
+}
+
func (t *toolchainWindows) ShlibSuffix() string {
return ".dll"
}
diff --git a/cc/coverage.go b/cc/coverage.go
index 391b118..2e81a9e 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -15,13 +15,21 @@
package cc
import (
+ "strconv"
+
+ "github.com/google/blueprint"
+
"android/soong/android"
)
type CoverageProperties struct {
Native_coverage *bool
- CoverageEnabled bool `blueprint:"mutated"`
+ NeedCoverageVariant bool `blueprint:"mutated"`
+ NeedCoverageBuild bool `blueprint:"mutated"`
+
+ CoverageEnabled bool `blueprint:"mutated"`
+ IsCoverageVariant bool `blueprint:"mutated"`
}
type coverage struct {
@@ -35,21 +43,38 @@
return []interface{}{&cov.Properties}
}
-func (cov *coverage) begin(ctx BaseModuleContext) {}
+func getProfileLibraryName(ctx ModuleContextIntf) string {
+ // This function should only ever be called for a cc.Module, so the
+ // following statement should always succeed.
+ if ctx.useSdk() {
+ return "libprofile-extras_ndk"
+ } else {
+ return "libprofile-extras"
+ }
+}
-func (cov *coverage) deps(ctx BaseModuleContext, deps Deps) Deps {
+func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
+ if cov.Properties.NeedCoverageVariant {
+ ctx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, coverageDepTag, getProfileLibraryName(ctx))
+ }
return deps
}
-func (cov *coverage) flags(ctx ModuleContext, flags Flags) Flags {
+func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
if !ctx.DeviceConfig().NativeCoverageEnabled() {
- return flags
+ return flags, deps
}
if cov.Properties.CoverageEnabled {
flags.Coverage = true
flags.GlobalFlags = append(flags.GlobalFlags, "--coverage", "-O0")
cov.linkCoverage = true
+
+ // Override -Wframe-larger-than and non-default optimization
+ // flags that the module may use.
+ flags.CFlags = append(flags.CFlags, "-Wno-frame-larger-than=", "-O0")
}
// Even if we don't have coverage enabled, if any of our object files were compiled
@@ -88,32 +113,85 @@
if cov.linkCoverage {
flags.LdFlags = append(flags.LdFlags, "--coverage")
+
+ coverage := ctx.GetDirectDepWithTag(getProfileLibraryName(ctx), coverageDepTag).(*Module)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--wrap,getenv")
}
- return flags
+ return flags, deps
}
-func coverageLinkingMutator(mctx android.BottomUpMutatorContext) {
+func (cov *coverage) begin(ctx BaseModuleContext) {
+ // Coverage is disabled globally
+ if !ctx.DeviceConfig().NativeCoverageEnabled() {
+ return
+ }
+
+ var needCoverageVariant bool
+ var needCoverageBuild bool
+
+ if ctx.Host() {
+ // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
+ // Just turn off for now.
+ } else if !ctx.nativeCoverage() {
+ // Native coverage is not supported for this module type.
+ } else {
+ // Check if Native_coverage is set to false. This property defaults to true.
+ needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true)
+ if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" {
+ // Native coverage is not supported for SDK versions < 23
+ if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
+ needCoverageVariant = false
+ }
+ }
+
+ if needCoverageVariant {
+ // Coverage variant is actually built with coverage if enabled for its module path
+ needCoverageBuild = ctx.DeviceConfig().CoverageEnabledForPath(ctx.ModuleDir())
+ }
+ }
+
+ cov.Properties.NeedCoverageBuild = needCoverageBuild
+ cov.Properties.NeedCoverageVariant = needCoverageVariant
+}
+
+// Coverage is an interface for non-CC modules to implement to be mutated for coverage
+type Coverage interface {
+ android.Module
+ IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
+ PreventInstall()
+ HideFromMake()
+}
+
+func coverageMutator(mctx android.BottomUpMutatorContext) {
if c, ok := mctx.Module().(*Module); ok && c.coverage != nil {
- var enabled bool
+ needCoverageVariant := c.coverage.Properties.NeedCoverageVariant
+ needCoverageBuild := c.coverage.Properties.NeedCoverageBuild
+ if needCoverageVariant {
+ m := mctx.CreateVariations("", "cov")
- if !mctx.DeviceConfig().NativeCoverageEnabled() {
- // Coverage is disabled globally
- } else if mctx.Host() {
- // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
- // Just turn off for now.
- } else if c.coverage.Properties.Native_coverage != nil {
- enabled = *c.coverage.Properties.Native_coverage
- } else {
- enabled = mctx.DeviceConfig().CoverageEnabledForPath(mctx.ModuleDir())
- }
+ // Setup the non-coverage version and set HideFromMake and
+ // PreventInstall to true.
+ m[0].(*Module).coverage.Properties.CoverageEnabled = false
+ m[0].(*Module).coverage.Properties.IsCoverageVariant = false
+ m[0].(*Module).Properties.HideFromMake = true
+ m[0].(*Module).Properties.PreventInstall = true
- if enabled {
- // Create a variation so that we don't need to recompile objects
- // when turning on or off coverage. We'll still relink the necessary
- // binaries, since we don't know which ones those are until later.
- m := mctx.CreateLocalVariations("cov")
- m[0].(*Module).coverage.Properties.CoverageEnabled = true
+ // The coverage-enabled version inherits HideFromMake,
+ // PreventInstall from the original module.
+ m[1].(*Module).coverage.Properties.CoverageEnabled = needCoverageBuild
+ m[1].(*Module).coverage.Properties.IsCoverageVariant = true
}
+ } else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) {
+ // APEX modules fall here
+
+ // Note: variant "" is also created because an APEX can be depended on by another
+ // module which are split into "" and "cov" variants. e.g. when cc_test refers
+ // to an APEX via 'data' property.
+ m := mctx.CreateVariations("", "cov")
+ m[0].(Coverage).PreventInstall()
+ m[0].(Coverage).HideFromMake()
}
}
diff --git a/cc/gen.go b/cc/gen.go
index f22a783..0c3d089 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -14,22 +14,21 @@
package cc
-// This file generates the final rules for compiling all C/C++. All properties related to
-// compiling should have been translated into builderFlags or another argument to the Transform*
-// functions.
-
import (
+ "path/filepath"
+
"github.com/google/blueprint"
"android/soong/android"
)
func init() {
- pctx.SourcePathVariable("lexCmd", "prebuilts/misc/${config.HostPrebuiltTag}/flex/flex-2.5.39")
+ pctx.SourcePathVariable("lexCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/flex")
pctx.SourcePathVariable("yaccCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/bison")
pctx.SourcePathVariable("yaccDataDir", "prebuilts/build-tools/common/bison")
pctx.HostBinToolVariable("aidlCmd", "aidl-cpp")
+ pctx.HostBinToolVariable("syspropCmd", "sysprop_cpp")
}
var (
@@ -48,13 +47,21 @@
aidl = pctx.AndroidStaticRule("aidl",
blueprint.RuleParams{
- Command: "$aidlCmd -d${out}.d -ninja $aidlFlags $in $outDir $out",
+ Command: "$aidlCmd -d${out}.d --ninja $aidlFlags $in $outDir $out",
CommandDeps: []string{"$aidlCmd"},
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
},
"aidlFlags", "outDir")
+ sysprop = pctx.AndroidStaticRule("sysprop",
+ blueprint.RuleParams{
+ Command: "$syspropCmd --header-dir=$headerOutDir --system-header-dir=$systemOutDir " +
+ "--source-dir=$srcOutDir --include-name=$includeName $in",
+ CommandDeps: []string{"$syspropCmd"},
+ },
+ "headerOutDir", "systemOutDir", "srcOutDir", "includeName")
+
windmc = pctx.AndroidStaticRule("windmc",
blueprint.RuleParams{
Command: "$windmcCmd -r$$(dirname $out) -h$$(dirname $out) $in",
@@ -82,7 +89,6 @@
}
func genAidl(ctx android.ModuleContext, aidlFile android.Path, outFile android.ModuleGenPath, aidlFlags string) android.Paths {
-
ctx.Build(pctx, android.BuildParams{
Rule: aidl,
Description: "aidl " + aidlFile.Rel(),
@@ -107,6 +113,28 @@
})
}
+func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Path) {
+ headerFile := android.PathForModuleGen(ctx, "sysprop", "include", syspropFile.Rel()+".h")
+ systemHeaderFile := android.PathForModuleGen(ctx, "sysprop/system", "include", syspropFile.Rel()+".h")
+ cppFile := android.PathForModuleGen(ctx, "sysprop", syspropFile.Rel()+".cpp")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: sysprop,
+ Description: "sysprop " + syspropFile.Rel(),
+ Output: cppFile,
+ ImplicitOutput: headerFile,
+ Input: syspropFile,
+ Args: map[string]string{
+ "headerOutDir": filepath.Dir(headerFile.String()),
+ "systemOutDir": filepath.Dir(systemHeaderFile.String()),
+ "srcOutDir": filepath.Dir(cppFile.String()),
+ "includeName": syspropFile.Rel() + ".h",
+ },
+ })
+
+ return cppFile, headerFile
+}
+
func genWinMsg(ctx android.ModuleContext, srcFile android.Path, flags builderFlags) (android.Path, android.Path) {
headerFile := android.GenPathWithExt(ctx, "windmc", srcFile, "h")
rcFile := android.GenPathWithExt(ctx, "windmc", srcFile, "rc")
@@ -153,8 +181,7 @@
srcFiles[i] = cppFile
genLex(ctx, srcFile, cppFile)
case ".proto":
- ccFile, headerFile := genProto(ctx, srcFile, buildFlags.protoFlags,
- buildFlags.protoOutParams, buildFlags.protoRoot)
+ ccFile, headerFile := genProto(ctx, srcFile, buildFlags)
srcFiles[i] = ccFile
deps = append(deps, headerFile)
case ".aidl":
@@ -169,6 +196,10 @@
rcFile, headerFile := genWinMsg(ctx, srcFile, buildFlags)
srcFiles[i] = rcFile
deps = append(deps, headerFile)
+ case ".sysprop":
+ cppFile, headerFile := genSysprop(ctx, srcFile)
+ srcFiles[i] = cppFile
+ deps = append(deps, headerFile)
}
}
diff --git a/cc/gen_stub_libs.py b/cc/gen_stub_libs.py
index 8c99bbf..81bc398 100755
--- a/cc/gen_stub_libs.py
+++ b/cc/gen_stub_libs.py
@@ -20,6 +20,7 @@
import logging
import os
import re
+import sys
ALL_ARCHITECTURES = (
@@ -107,22 +108,42 @@
return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
-def should_omit_version(name, tags, arch, api, vndk):
+def should_omit_version(version, arch, api, vndk, apex):
"""Returns True if the version section should be ommitted.
We want to omit any sections that do not have any symbols we'll have in the
stub library. Sections that contain entirely future symbols or only symbols
for certain architectures.
"""
- if version_is_private(name):
+ if version_is_private(version.name):
return True
- if 'platform-only' in tags:
+ if 'platform-only' in version.tags:
return True
- if 'vndk' in tags and not vndk:
+
+ no_vndk_no_apex = 'vndk' not in version.tags and 'apex' not in version.tags
+ keep = no_vndk_no_apex or \
+ ('vndk' in version.tags and vndk) or \
+ ('apex' in version.tags and apex)
+ if not keep:
return True
- if not symbol_in_arch(tags, arch):
+ if not symbol_in_arch(version.tags, arch):
return True
- if not symbol_in_api(tags, arch, api):
+ if not symbol_in_api(version.tags, arch, api):
+ return True
+ return False
+
+
+def should_omit_symbol(symbol, arch, api, vndk, apex):
+ """Returns True if the symbol should be omitted."""
+ no_vndk_no_apex = 'vndk' not in symbol.tags and 'apex' not in symbol.tags
+ keep = no_vndk_no_apex or \
+ ('vndk' in symbol.tags and vndk) or \
+ ('apex' in symbol.tags and apex)
+ if not keep:
+ return True
+ if not symbol_in_arch(symbol.tags, arch):
+ return True
+ if not symbol_in_api(symbol.tags, arch, api):
return True
return False
@@ -189,6 +210,15 @@
pass
+class MultiplyDefinedSymbolError(RuntimeError):
+ """A symbol name was multiply defined."""
+ def __init__(self, multiply_defined_symbols):
+ super(MultiplyDefinedSymbolError, self).__init__(
+ 'Version script contains multiple definitions for: {}'.format(
+ ', '.join(multiply_defined_symbols)))
+ self.multiply_defined_symbols = multiply_defined_symbols
+
+
class Version(object):
"""A version block of a symbol file."""
def __init__(self, name, base, tags, symbols):
@@ -218,12 +248,15 @@
def __eq__(self, other):
return self.name == other.name and set(self.tags) == set(other.tags)
-
class SymbolFileParser(object):
"""Parses NDK symbol files."""
- def __init__(self, input_file, api_map):
+ def __init__(self, input_file, api_map, arch, api, vndk, apex):
self.input_file = input_file
self.api_map = api_map
+ self.arch = arch
+ self.api = api
+ self.vndk = vndk
+ self.apex = apex
self.current_line = None
def parse(self):
@@ -235,8 +268,36 @@
else:
raise ParseError(
'Unexpected contents at top level: ' + self.current_line)
+
+ self.check_no_duplicate_symbols(versions)
return versions
+ def check_no_duplicate_symbols(self, versions):
+ """Raises errors for multiply defined symbols.
+
+ This situation is the normal case when symbol versioning is actually
+ used, but this script doesn't currently handle that. The error message
+ will be a not necessarily obvious "error: redefition of 'foo'" from
+ stub.c, so it's better for us to catch this situation and raise a
+ better error.
+ """
+ symbol_names = set()
+ multiply_defined_symbols = set()
+ for version in versions:
+ if should_omit_version(version, self.arch, self.api, self.vndk, self.apex):
+ continue
+
+ for symbol in version.symbols:
+ if should_omit_symbol(symbol, self.arch, self.api, self.vndk, self.apex):
+ continue
+
+ if symbol.name in symbol_names:
+ multiply_defined_symbols.add(symbol.name)
+ symbol_names.add(symbol.name)
+ if multiply_defined_symbols:
+ raise MultiplyDefinedSymbolError(
+ sorted(list(multiply_defined_symbols)))
+
def parse_version(self):
"""Parses a single version section and returns a Version object."""
name = self.current_line.split('{')[0].strip()
@@ -274,7 +335,8 @@
elif global_scope and not cpp_symbols:
symbols.append(self.parse_symbol())
else:
- # We're in a hidden scope or in 'extern "C++"' block. Ignore everything.
+ # We're in a hidden scope or in 'extern "C++"' block. Ignore
+ # everything.
pass
raise ParseError('Unexpected EOF in version block.')
@@ -310,12 +372,13 @@
class Generator(object):
"""Output generator that writes stub source files and version scripts."""
- def __init__(self, src_file, version_script, arch, api, vndk):
+ def __init__(self, src_file, version_script, arch, api, vndk, apex):
self.src_file = src_file
self.version_script = version_script
self.arch = arch
self.api = api
self.vndk = vndk
+ self.apex = apex
def write(self, versions):
"""Writes all symbol data to the output files."""
@@ -324,20 +387,14 @@
def write_version(self, version):
"""Writes a single version block's data to the output files."""
- name = version.name
- tags = version.tags
- if should_omit_version(name, tags, self.arch, self.api, self.vndk):
+ if should_omit_version(version, self.arch, self.api, self.vndk, self.apex):
return
- section_versioned = symbol_versioned_in_api(tags, self.api)
+ section_versioned = symbol_versioned_in_api(version.tags, self.api)
version_empty = True
pruned_symbols = []
for symbol in version.symbols:
- if not self.vndk and 'vndk' in symbol.tags:
- continue
- if not symbol_in_arch(symbol.tags, self.arch):
- continue
- if not symbol_in_api(symbol.tags, self.arch, self.api):
+ if should_omit_symbol(symbol, self.arch, self.api, self.vndk, self.apex):
continue
if symbol_versioned_in_api(symbol.tags, self.api):
@@ -400,6 +457,8 @@
help='Architecture being targeted.')
parser.add_argument(
'--vndk', action='store_true', help='Use the VNDK variant.')
+ parser.add_argument(
+ '--apex', action='store_true', help='Use the APEX variant.')
parser.add_argument(
'--api-map', type=os.path.realpath, required=True,
@@ -432,12 +491,16 @@
logging.basicConfig(level=verbose_map[verbosity])
with open(args.symbol_file) as symbol_file:
- versions = SymbolFileParser(symbol_file, api_map).parse()
+ try:
+ versions = SymbolFileParser(symbol_file, api_map, args.arch, api,
+ args.vndk, args.apex).parse()
+ except MultiplyDefinedSymbolError as ex:
+ sys.exit('{}: error: {}'.format(args.symbol_file, ex))
with open(args.stub_src, 'w') as src_file:
with open(args.version_script, 'w') as version_file:
generator = Generator(src_file, version_file, args.arch, api,
- args.vndk)
+ args.vndk, args.apex)
generator.write(versions)
diff --git a/cc/genrule.go b/cc/genrule.go
index 51c0d16..decf6ea 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -23,13 +23,21 @@
android.RegisterModuleType("cc_genrule", genRuleFactory)
}
+type GenruleExtraProperties struct {
+ Vendor_available *bool
+ Recovery_available *bool
+
+ // This genrule is for recovery variant
+ InRecovery bool `blueprint:"mutated"`
+}
+
// cc_genrule is a genrule that can depend on other cc_* objects.
// The cmd may be run multiple times, once for each of the different arch/etc
// variations.
func genRuleFactory() android.Module {
module := genrule.NewGenRule()
- module.Extra = &VendorProperties{}
+ module.Extra = &GenruleExtraProperties{}
module.AddProperties(module.Extra)
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibBoth)
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
new file mode 100644
index 0000000..92024ac
--- /dev/null
+++ b/cc/genrule_test.go
@@ -0,0 +1,88 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "reflect"
+ "testing"
+
+ "android/soong/android"
+)
+
+func testGenruleContext(config android.Config, bp string,
+ fs map[string][]byte) *android.TestContext {
+
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("cc_genrule", android.ModuleFactoryAdaptor(genRuleFactory))
+ ctx.Register()
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "tool": nil,
+ "foo": nil,
+ "bar": nil,
+ }
+
+ for k, v := range fs {
+ mockFS[k] = v
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ return ctx
+}
+
+func TestArchGenruleCmd(t *testing.T) {
+ config := android.TestArchConfig(buildDir, nil)
+ bp := `
+ cc_genrule {
+ name: "gen",
+ tool_files: ["tool"],
+ cmd: "$(location tool) $(in) $(out)",
+ arch: {
+ arm: {
+ srcs: ["foo"],
+ out: ["out_arm"],
+ },
+ arm64: {
+ srcs: ["bar"],
+ out: ["out_arm64"],
+ },
+ },
+ }
+ `
+
+ ctx := testGenruleContext(config, bp, nil)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if errs == nil {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+ if errs != nil {
+ t.Fatal(errs)
+ }
+
+ gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out_arm")
+ expected := []string{"foo"}
+ if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
+ t.Errorf(`want arm inputs %v, got %v`, expected, gen.Inputs.Strings())
+ }
+
+ gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm64")
+ expected = []string{"bar"}
+ if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
+ t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Inputs.Strings())
+ }
+}
diff --git a/cc/installer.go b/cc/installer.go
index 33f29f2..bd8f9e7 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -73,7 +73,7 @@
dir = filepath.Join(dir, "vendor")
}
return android.PathForModuleInstall(ctx, dir, installer.subDir,
- String(installer.Properties.Relative_install_path), installer.relative)
+ installer.relativeInstallPath(), installer.relative)
}
func (installer *baseInstaller) install(ctx ModuleContext, file android.Path) {
@@ -91,3 +91,7 @@
func (installer *baseInstaller) hostToolPath() android.OptionalPath {
return android.OptionalPath{}
}
+
+func (installer *baseInstaller) relativeInstallPath() string {
+ return String(installer.Properties.Relative_install_path)
+}
diff --git a/cc/kernel_headers.go b/cc/kernel_headers.go
index 42dc770..82a779c 100644
--- a/cc/kernel_headers.go
+++ b/cc/kernel_headers.go
@@ -26,7 +26,7 @@
if ctx.Device() {
f := &stub.libraryDecorator.flagExporter
for _, dir := range ctx.DeviceConfig().DeviceKernelHeaderDirs() {
- f.flags = append(f.flags, "-isystem"+dir)
+ f.flags = append(f.flags, "-isystem "+dir)
}
}
return stub.libraryDecorator.linkStatic(ctx, flags, deps, objs)
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index fd563e6..825b920 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libbuildversion",
host_supported: true,
+ recovery_available: true,
srcs: ["libbuildversion.cpp"],
export_include_dirs: ["include"],
cflags: ["-fvisibility=hidden"],
diff --git a/cc/libbuildversion/libbuildversion.cpp b/cc/libbuildversion/libbuildversion.cpp
index d80d587..5242025 100644
--- a/cc/libbuildversion/libbuildversion.cpp
+++ b/cc/libbuildversion/libbuildversion.cpp
@@ -23,9 +23,19 @@
namespace android {
namespace build {
+#define PLACEHOLDER "SOONG BUILD NUMBER PLACEHOLDER"
+
+extern "C" {
+ char soong_build_number[128] = PLACEHOLDER;
+}
+
#ifdef __ANDROID__
std::string GetBuildNumber() {
+ if (strcmp(PLACEHOLDER, soong_build_number) != 0) {
+ return soong_build_number;
+ }
+
const prop_info* pi = __system_property_find("ro.build.version.incremental");
if (pi == nullptr) return "";
@@ -42,10 +52,6 @@
#else
-extern "C" {
- char soong_build_number[128] = "SOONG BUILD NUMBER PLACEHOLDER";
-}
-
std::string GetBuildNumber() {
return soong_build_number;
}
diff --git a/cc/libbuildversion/tests/Android.bp b/cc/libbuildversion/tests/Android.bp
index a18bc6c..b3b2061 100644
--- a/cc/libbuildversion/tests/Android.bp
+++ b/cc/libbuildversion/tests/Android.bp
@@ -2,10 +2,53 @@
name: "build_version_test_defaults",
use_version_lib: true,
host_supported: true,
+ dist: {
+ targets: ["test_build_version_test"],
+ },
target: {
+ android_arm: {
+ dist: {
+ dir: "android/arm",
+ },
+ },
+ android_arm64: {
+ dist: {
+ dir: "android/arm64",
+ },
+ },
+ android_x86: {
+ dist: {
+ dir: "android/x86",
+ },
+ },
+ android_x86_64: {
+ dist: {
+ dir: "android/x86_64",
+ },
+ },
+ darwin: {
+ dist: {
+ dir: "host/",
+ },
+ },
+ linux_glibc_x86: {
+ dist: {
+ dir: "host32/",
+ },
+ },
+ linux_glibc_x86_64: {
+ dist: {
+ dir: "host/",
+ },
+ },
windows: {
enabled: true,
},
+ windows_x86_64: {
+ dist: {
+ dest: "win64/build_ver_test.exe",
+ },
+ },
},
}
@@ -20,6 +63,11 @@
not_windows: {
shared_libs: ["libbuild_version_test"],
},
+ host: {
+ dist: {
+ suffix: "_host",
+ },
+ },
},
}
diff --git a/cc/library.go b/cc/library.go
index 76f8a8c..f93f5af 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -15,42 +15,44 @@
package cc
import (
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strconv"
"strings"
+ "sync"
- "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
"android/soong/android"
+ "android/soong/cc/config"
+ "android/soong/genrule"
)
+type StaticSharedLibraryProperties struct {
+ Srcs []string `android:"path,arch_variant"`
+ Cflags []string `android:"path,arch_variant"`
+
+ Enabled *bool `android:"arch_variant"`
+ Whole_static_libs []string `android:"arch_variant"`
+ Static_libs []string `android:"arch_variant"`
+ Shared_libs []string `android:"arch_variant"`
+ System_shared_libs []string `android:"arch_variant"`
+
+ Export_shared_lib_headers []string `android:"arch_variant"`
+ Export_static_lib_headers []string `android:"arch_variant"`
+}
+
type LibraryProperties struct {
- Static struct {
- Srcs []string `android:"arch_variant"`
- Cflags []string `android:"arch_variant"`
+ Static StaticSharedLibraryProperties `android:"arch_variant"`
+ Shared StaticSharedLibraryProperties `android:"arch_variant"`
- Enabled *bool `android:"arch_variant"`
- Whole_static_libs []string `android:"arch_variant"`
- Static_libs []string `android:"arch_variant"`
- Shared_libs []string `android:"arch_variant"`
- } `android:"arch_variant"`
- Shared struct {
- Srcs []string `android:"arch_variant"`
- Cflags []string `android:"arch_variant"`
-
- Enabled *bool `android:"arch_variant"`
- Whole_static_libs []string `android:"arch_variant"`
- Static_libs []string `android:"arch_variant"`
- Shared_libs []string `android:"arch_variant"`
- } `android:"arch_variant"`
-
- // local file name to pass to the linker as --version_script
- Version_script *string `android:"arch_variant"`
// local file name to pass to the linker as -unexported_symbols_list
- Unexported_symbols_list *string `android:"arch_variant"`
+ Unexported_symbols_list *string `android:"path,arch_variant"`
// local file name to pass to the linker as -force_symbols_not_weak_list
- Force_symbols_not_weak_list *string `android:"arch_variant"`
+ Force_symbols_not_weak_list *string `android:"path,arch_variant"`
// local file name to pass to the linker as -force_symbols_weak_list
- Force_symbols_weak_list *string `android:"arch_variant"`
+ Force_symbols_weak_list *string `android:"path,arch_variant"`
// rename host libraries to prevent overlap with system installed libraries
Unique_host_soname *bool
@@ -64,14 +66,45 @@
// export headers generated from .proto sources
Export_proto_headers *bool
}
- Target struct {
- Vendor struct {
- // version script for this vendor variant
- Version_script *string `android:"arch_variant"`
- }
- }
+
+ Sysprop struct {
+ // Whether platform owns this sysprop library.
+ Platform *bool
+ } `blueprint:"mutated"`
Static_ndk_lib *bool
+
+ Stubs struct {
+ // Relative path to the symbol map. The symbol map provides the list of
+ // symbols that are exported for stubs variant of this library.
+ Symbol_file *string `android:"path"`
+
+ // List versions to generate stubs libs for.
+ Versions []string
+ }
+
+ // set the name of the output
+ Stem *string `android:"arch_variant"`
+
+ // Names of modules to be overridden. Listed modules can only be other shared libraries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden libraries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other library will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // Properties for ABI compatibility checker
+ Header_abi_checker struct {
+ // Path to a symbol file that specifies the symbols to be included in the generated
+ // ABI dump file
+ Symbol_file *string `android:"path"`
+
+ // Symbol versions that should be ignored from the symbol file
+ Exclude_symbol_versions []string
+
+ // Symbol tags that should be ignored from the symbol file
+ Exclude_symbol_tags []string
+ }
}
type LibraryMutatedProperties struct {
@@ -85,6 +118,11 @@
VariantIsShared bool `blueprint:"mutated"`
// This variant is static
VariantIsStatic bool `blueprint:"mutated"`
+
+ // This variant is a stubs lib
+ BuildStubs bool `blueprint:"mutated"`
+ // Version of the stubs lib
+ StubsVersion string `blueprint:"mutated"`
}
type FlagExporterProperties struct {
@@ -114,42 +152,48 @@
android.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
}
-// Module factory for combined static + shared libraries, device by default but with possible host
-// support
+// cc_library creates both static and/or shared libraries for a device and/or
+// host. By default, a cc_library has a single variant that targets the device.
+// Specifying `host_supported: true` also creates a library that targets the
+// host.
func LibraryFactory() android.Module {
module, _ := NewLibrary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for static libraries
+// cc_library_static creates a static library for a device and/or host binary.
func LibraryStaticFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// Module factory for shared libraries
+// cc_library_shared creates a shared library for a device and/or host.
func LibrarySharedFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyShared()
return module.Init()
}
-// Module factory for host static libraries
+// cc_library_host_static creates a static library that is linkable to a host
+// binary.
func LibraryHostStaticFactory() android.Module {
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// Module factory for host shared libraries
+// cc_library_host_shared creates a shared library that is usable on a host.
func LibraryHostSharedFactory() android.Module {
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyShared()
return module.Init()
}
-// Module factory for header-only libraries
+// cc_library_headers contains a set of c/c++ headers which are imported by
+// other soong cc modules using the header_libs property. For best practices,
+// use export_include_dirs property or LOCAL_EXPORT_C_INCLUDE_DIRS for
+// Make.
func LibraryHeaderFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.HeaderOnly()
@@ -217,7 +261,6 @@
flagExporter
stripper
- relocationPacker
// If we're used as a whole_static_lib, our missing dependencies need
// to be given
@@ -230,8 +273,6 @@
// shlib suffix.
libName string
- sanitize *sanitize
-
sabi *sabi
// Output archive of gcno coverage information files
@@ -247,6 +288,20 @@
// not included in the NDK.
ndkSysrootPath android.Path
+ // Location of the linked, unstripped library for shared libraries
+ unstrippedOutputFile android.Path
+
+ // Location of the file that should be copied to dist dir when requested
+ distFile android.OptionalPath
+
+ versionScriptPath android.ModuleGenPath
+
+ post_install_cmds []string
+
+ // If useCoreVariant is true, the vendor variant of a VNDK library is
+ // not installed.
+ useCoreVariant bool
+
// Decorated interafaces
*baseCompiler
*baseLinker
@@ -260,8 +315,7 @@
&library.Properties,
&library.MutatedProperties,
&library.flagExporter.Properties,
- &library.stripper.StripProperties,
- &library.relocationPacker.Properties)
+ &library.stripper.StripProperties)
}
func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -282,11 +336,6 @@
if library.shared() {
libName := library.getLibName(ctx)
- // GCC for Android assumes that -shared means -Bsymbolic, use -Wl,-shared instead
- sharedFlag := "-Wl,-shared"
- if flags.Clang || ctx.Host() {
- sharedFlag = "-shared"
- }
var f []string
if ctx.toolchain().Bionic() {
f = append(f,
@@ -308,7 +357,7 @@
}
} else {
f = append(f,
- sharedFlag,
+ "-shared",
"-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
}
@@ -326,7 +375,29 @@
flags.YasmFlags = append(flags.YasmFlags, f)
}
- return library.baseCompiler.compilerFlags(ctx, flags, deps)
+ flags = library.baseCompiler.compilerFlags(ctx, flags, deps)
+ if library.buildStubs() {
+ // Remove -include <file> when compiling stubs. Otherwise, the force included
+ // headers might cause conflicting types error with the symbols in the
+ // generated stubs source code. e.g.
+ // double acos(double); // in header
+ // void acos() {} // in the generated source code
+ removeInclude := func(flags []string) []string {
+ ret := flags[:0]
+ for _, f := range flags {
+ if strings.HasPrefix(f, "-include ") {
+ continue
+ }
+ ret = append(ret, f)
+ }
+ return ret
+ }
+ flags.GlobalFlags = removeInclude(flags.GlobalFlags)
+ flags.CFlags = removeInclude(flags.CFlags)
+
+ flags = addStubLibraryCompilerFlags(flags)
+ }
+ return flags
}
func extractExportIncludesFromFlags(flags []string) []string {
@@ -349,6 +420,12 @@
}
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+ if library.buildStubs() {
+ objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
+ library.versionScriptPath = versionScript
+ return objs
+ }
+
if !library.buildShared() && !library.buildStatic() {
if len(library.baseCompiler.Properties.Srcs) > 0 {
ctx.PropertyErrorf("srcs", "cc_library_headers must not have any srcs")
@@ -361,7 +438,7 @@
}
return Objects{}
}
- if ctx.createVndkSourceAbiDump() || library.sabi.Properties.CreateSAbiDumps {
+ if ctx.shouldCreateVndkSourceAbiDump() || library.sabi.Properties.CreateSAbiDumps {
exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
var SourceAbiFlags []string
for _, dir := range exportIncludeDirs.Strings() {
@@ -413,7 +490,10 @@
func (library *libraryDecorator) getLibName(ctx ModuleContext) string {
name := library.libName
if name == "" {
- name = ctx.baseModuleName()
+ name = String(library.Properties.Stem)
+ if name == "" {
+ name = ctx.baseModuleName()
+ }
}
if ctx.isVndkExt() {
@@ -429,19 +509,49 @@
return name + library.MutatedProperties.VariantName
}
+var versioningMacroNamesListMutex sync.Mutex
+
func (library *libraryDecorator) linkerInit(ctx BaseModuleContext) {
location := InstallInSystem
- if library.sanitize.inSanitizerDir() {
+ if library.baseLinker.sanitize.inSanitizerDir() {
location = InstallInSanitizerDir
}
library.baseInstaller.location = location
-
library.baseLinker.linkerInit(ctx)
+ // Let baseLinker know whether this variant is for stubs or not, so that
+ // it can omit things that are not required for linking stubs.
+ library.baseLinker.dynamicProperties.BuildStubs = library.buildStubs()
- library.relocationPacker.packingInit(ctx)
+ if library.buildStubs() {
+ macroNames := versioningMacroNamesList(ctx.Config())
+ myName := versioningMacroName(ctx.ModuleName())
+ versioningMacroNamesListMutex.Lock()
+ defer versioningMacroNamesListMutex.Unlock()
+ if (*macroNames)[myName] == "" {
+ (*macroNames)[myName] = ctx.ModuleName()
+ } else if (*macroNames)[myName] != ctx.ModuleName() {
+ ctx.ModuleErrorf("Macro name %q for versioning conflicts with macro name from module %q ", myName, (*macroNames)[myName])
+ }
+ }
+}
+
+func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ deps = library.baseCompiler.compilerDeps(ctx, deps)
+
+ return deps
}
func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ if library.static() {
+ if library.Properties.Static.System_shared_libs != nil {
+ library.baseLinker.Properties.System_shared_libs = library.Properties.Static.System_shared_libs
+ }
+ } else if library.shared() {
+ if library.Properties.Shared.System_shared_libs != nil {
+ library.baseLinker.Properties.System_shared_libs = library.Properties.Shared.System_shared_libs
+ }
+ }
+
deps = library.baseLinker.linkerDeps(ctx, deps)
if library.static() {
@@ -449,6 +559,9 @@
library.Properties.Static.Whole_static_libs...)
deps.StaticLibs = append(deps.StaticLibs, library.Properties.Static.Static_libs...)
deps.SharedLibs = append(deps.SharedLibs, library.Properties.Static.Shared_libs...)
+
+ deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.Properties.Static.Export_shared_lib_headers...)
+ deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.Properties.Static.Export_static_lib_headers...)
} else if library.shared() {
if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
if !ctx.useSdk() {
@@ -470,18 +583,24 @@
deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.Properties.Shared.Whole_static_libs...)
deps.StaticLibs = append(deps.StaticLibs, library.Properties.Shared.Static_libs...)
deps.SharedLibs = append(deps.SharedLibs, library.Properties.Shared.Shared_libs...)
+
+ deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.Properties.Shared.Export_shared_lib_headers...)
+ deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.Properties.Shared.Export_static_lib_headers...)
}
if ctx.useVndk() {
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
}
-
- android.ExtractSourceDeps(ctx, library.Properties.Version_script)
- android.ExtractSourceDeps(ctx, library.Properties.Unexported_symbols_list)
- android.ExtractSourceDeps(ctx, library.Properties.Force_symbols_not_weak_list)
- android.ExtractSourceDeps(ctx, library.Properties.Force_symbols_weak_list)
- android.ExtractSourceDeps(ctx, library.Properties.Target.Vendor.Version_script)
+ if ctx.inRecovery() {
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+ }
return deps
}
@@ -496,15 +615,21 @@
outputFile := android.PathForModuleOut(ctx, fileName)
builderFlags := flagsToBuilderFlags(flags)
- if Bool(library.baseLinker.Properties.Use_version_lib) && ctx.Host() {
- versionedOutputFile := outputFile
- outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
- library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ if Bool(library.baseLinker.Properties.Use_version_lib) {
+ if ctx.Host() {
+ versionedOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
+ library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ } else {
+ versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
+ library.distFile = android.OptionalPathForPath(versionedOutputFile)
+ library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ }
}
TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
- library.coverageOutputFile = TransformCoverageFilesToLib(ctx, library.objects, builderFlags,
+ library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects,
ctx.ModuleName()+library.MutatedProperties.VariantName)
library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
@@ -520,23 +645,10 @@
var linkerDeps android.Paths
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
- versionScript := ctx.ExpandOptionalSource(library.Properties.Version_script, "version_script")
unexportedSymbols := ctx.ExpandOptionalSource(library.Properties.Unexported_symbols_list, "unexported_symbols_list")
forceNotWeakSymbols := ctx.ExpandOptionalSource(library.Properties.Force_symbols_not_weak_list, "force_symbols_not_weak_list")
forceWeakSymbols := ctx.ExpandOptionalSource(library.Properties.Force_symbols_weak_list, "force_symbols_weak_list")
- if ctx.useVndk() && library.Properties.Target.Vendor.Version_script != nil {
- versionScript = ctx.ExpandOptionalSource(library.Properties.Target.Vendor.Version_script, "target.vendor.version_script")
- }
if !ctx.Darwin() {
- if versionScript.Valid() {
- flags.LdFlags = append(flags.LdFlags, "-Wl,--version-script,"+versionScript.String())
- linkerDeps = append(linkerDeps, versionScript.Path())
- if library.sanitize.isSanitizerEnabled(cfi) {
- cfiExportsMap := android.PathForSource(ctx, cfiExportsMapPath)
- flags.LdFlags = append(flags.LdFlags, "-Wl,--version-script,"+cfiExportsMap.String())
- linkerDeps = append(linkerDeps, cfiExportsMap)
- }
- }
if unexportedSymbols.Valid() {
ctx.PropertyErrorf("unexported_symbols_list", "Only supported on Darwin")
}
@@ -547,9 +659,6 @@
ctx.PropertyErrorf("force_symbols_weak_list", "Only supported on Darwin")
}
} else {
- if versionScript.Valid() {
- ctx.PropertyErrorf("version_script", "Not supported on Darwin")
- }
if unexportedSymbols.Valid() {
flags.LdFlags = append(flags.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
linkerDeps = append(linkerDeps, unexportedSymbols.Path())
@@ -563,6 +672,11 @@
linkerDeps = append(linkerDeps, forceWeakSymbols.Path())
}
}
+ if library.buildStubs() {
+ linkerScriptFlags := "-Wl,--version-script," + library.versionScriptPath.String()
+ flags.LdFlags = append(flags.LdFlags, linkerScriptFlags)
+ linkerDeps = append(linkerDeps, library.versionScriptPath)
+ }
fileName := library.getLibName(ctx) + flags.Toolchain.ShlibSuffix()
outputFile := android.PathForModuleOut(ctx, fileName)
@@ -570,56 +684,49 @@
builderFlags := flagsToBuilderFlags(flags)
- if !ctx.Darwin() && !ctx.Windows() {
- // Optimize out relinking against shared libraries whose interface hasn't changed by
- // depending on a table of contents file instead of the library itself.
- tocPath := outputFile.RelPathString()
- tocPath = pathtools.ReplaceExtension(tocPath, flags.Toolchain.ShlibSuffix()[1:]+".toc")
- tocFile := android.PathForOutput(ctx, tocPath)
- library.tocFile = android.OptionalPathForPath(tocFile)
- TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
- }
-
- if library.relocationPacker.needsPacking(ctx) {
- packedOutputFile := outputFile
- outputFile = android.PathForModuleOut(ctx, "unpacked", fileName)
- library.relocationPacker.pack(ctx, outputFile, packedOutputFile, builderFlags)
- }
+ // Optimize out relinking against shared libraries whose interface hasn't changed by
+ // depending on a table of contents file instead of the library itself.
+ tocPath := outputFile.RelPathString()
+ tocPath = pathtools.ReplaceExtension(tocPath, flags.Toolchain.ShlibSuffix()[1:]+".toc")
+ tocFile := android.PathForOutput(ctx, tocPath)
+ library.tocFile = android.OptionalPathForPath(tocFile)
+ TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
if library.stripper.needsStrip(ctx) {
+ if ctx.Darwin() {
+ builderFlags.stripUseGnuStrip = true
+ }
strippedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
library.stripper.strip(ctx, outputFile, strippedOutputFile, builderFlags)
}
- if Bool(library.baseLinker.Properties.Use_version_lib) && ctx.Host() {
- versionedOutputFile := outputFile
- outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
- library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ library.unstrippedOutputFile = outputFile
+
+ if Bool(library.baseLinker.Properties.Use_version_lib) {
+ if ctx.Host() {
+ versionedOutputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
+ library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ } else {
+ versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
+ library.distFile = android.OptionalPathForPath(versionedOutputFile)
+
+ if library.stripper.needsStrip(ctx) {
+ out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
+ library.distFile = android.OptionalPathForPath(out)
+ library.stripper.strip(ctx, versionedOutputFile, out, builderFlags)
+ }
+
+ library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
+ }
}
- sharedLibs := deps.SharedLibs
+ sharedLibs := deps.EarlySharedLibs
+ sharedLibs = append(sharedLibs, deps.SharedLibs...)
sharedLibs = append(sharedLibs, deps.LateSharedLibs...)
- // TODO(danalbert): Clean this up when soong supports prebuilts.
- if strings.HasPrefix(ctx.selectedStl(), "ndk_libc++") {
- libDir := getNdkStlLibDir(ctx, "libc++")
-
- if strings.HasSuffix(ctx.selectedStl(), "_shared") {
- deps.StaticLibs = append(deps.StaticLibs,
- libDir.Join(ctx, "libandroid_support.a"))
- } else {
- deps.StaticLibs = append(deps.StaticLibs,
- libDir.Join(ctx, "libc++abi.a"),
- libDir.Join(ctx, "libandroid_support.a"))
- }
-
- if ctx.Arch().ArchType == android.Arm {
- deps.StaticLibs = append(deps.StaticLibs,
- libDir.Join(ctx, "libunwind.a"))
- }
- }
-
+ linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
linkerDeps = append(linkerDeps, objs.tidyFiles...)
@@ -634,21 +741,55 @@
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.StaticLibObjs.sAbiDumpFiles...)
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.WholeStaticLibObjs.sAbiDumpFiles...)
- library.coverageOutputFile = TransformCoverageFilesToLib(ctx, objs, builderFlags, library.getLibName(ctx))
+ library.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
library.linkSAbiDumpFiles(ctx, objs, fileName, ret)
return ret
}
+func (library *libraryDecorator) unstrippedOutputFilePath() android.Path {
+ return library.unstrippedOutputFile
+}
+
+func (library *libraryDecorator) nativeCoverage() bool {
+ if library.header() || library.buildStubs() {
+ return false
+ }
+ return true
+}
+
+func (library *libraryDecorator) coverageOutputFilePath() android.OptionalPath {
+ return library.coverageOutputFile
+}
+
+func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
+ isLlndk := inList(ctx.baseModuleName(), llndkLibraries) || inList(ctx.baseModuleName(), ndkMigratedLibs)
+
+ refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isLlndk, false)
+ refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isLlndk, true)
+
+ if refAbiDumpTextFile.Valid() {
+ if refAbiDumpGzipFile.Valid() {
+ ctx.ModuleErrorf(
+ "Two reference ABI dump files are found: %q and %q. Please delete the stale one.",
+ refAbiDumpTextFile, refAbiDumpGzipFile)
+ return nil
+ }
+ return refAbiDumpTextFile.Path()
+ }
+ if refAbiDumpGzipFile.Valid() {
+ return UnzipRefDump(ctx, refAbiDumpGzipFile.Path(), fileName)
+ }
+ return nil
+}
+
func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
- //Also take into account object re-use.
- if len(objs.sAbiDumpFiles) > 0 && ctx.createVndkSourceAbiDump() {
+ if len(objs.sAbiDumpFiles) > 0 && ctx.shouldCreateVndkSourceAbiDump() {
vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
vndkVersion = ver
}
- refSourceDumpFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, vndkVsNdk(ctx), true)
exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
var SourceAbiFlags []string
for _, dir := range exportIncludeDirs.Strings() {
@@ -658,22 +799,19 @@
SourceAbiFlags = append(SourceAbiFlags, reexportedInclude)
}
exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
- library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags)
- if refSourceDumpFile.Valid() {
- unzippedRefDump := UnzipRefDump(ctx, refSourceDumpFile.Path(), fileName)
+ library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
+ android.OptionalPathForModuleSrc(ctx, library.Properties.Header_abi_checker.Symbol_file),
+ library.Properties.Header_abi_checker.Exclude_symbol_versions,
+ library.Properties.Header_abi_checker.Exclude_symbol_tags)
+
+ refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
+ if refAbiDumpFile != nil {
library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
- unzippedRefDump, fileName, exportedHeaderFlags, ctx.isVndkExt())
+ refAbiDumpFile, fileName, exportedHeaderFlags, ctx.isLlndk(), ctx.isVndkExt())
}
}
}
-func vndkVsNdk(ctx ModuleContext) bool {
- if inList(ctx.baseModuleName(), llndkLibraries) {
- return false
- }
- return true
-}
-
func (library *libraryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
@@ -704,10 +842,10 @@
if Bool(library.Properties.Proto.Export_proto_headers) {
if library.baseCompiler.hasSrcExt(".proto") {
includes := []string{}
- if flags.ProtoRoot {
- includes = append(includes, "-I"+android.ProtoSubDir(ctx).String())
+ if flags.proto.CanonicalPathFromRoot {
+ includes = append(includes, "-I"+flags.proto.SubDir.String())
}
- includes = append(includes, "-I"+android.ProtoDir(ctx).String())
+ includes = append(includes, "-I"+flags.proto.Dir.String())
library.reexportFlags(includes)
library.reuseExportedFlags = append(library.reuseExportedFlags, includes...)
library.reexportDeps(library.baseCompiler.pathDeps) // TODO: restrict to proto deps
@@ -715,21 +853,50 @@
}
}
+ if library.baseCompiler.hasSrcExt(".sysprop") {
+ internalFlags := []string{
+ "-I" + android.PathForModuleGen(ctx, "sysprop", "include").String(),
+ }
+ systemFlags := []string{
+ "-I" + android.PathForModuleGen(ctx, "sysprop/system", "include").String(),
+ }
+
+ flags := internalFlags
+
+ if library.Properties.Sysprop.Platform != nil {
+ isProduct := ctx.ProductSpecific() && !ctx.useVndk()
+ isVendor := ctx.useVndk()
+ isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
+
+ useSystem := isProduct || (isOwnerPlatform == isVendor)
+
+ if useSystem {
+ flags = systemFlags
+ }
+ }
+
+ library.reexportFlags(flags)
+ library.reexportDeps(library.baseCompiler.pathDeps)
+ library.reuseExportedFlags = append(library.reuseExportedFlags, flags...)
+ }
+
+ if library.buildStubs() {
+ library.reexportFlags([]string{"-D" + versioningMacroName(ctx.ModuleName()) + "=" + library.stubsVersion()})
+ }
+
return out
}
func (library *libraryDecorator) buildStatic() bool {
- return library.MutatedProperties.BuildStatic &&
- (library.Properties.Static.Enabled == nil || *library.Properties.Static.Enabled)
+ return library.MutatedProperties.BuildStatic && BoolDefault(library.Properties.Static.Enabled, true)
}
func (library *libraryDecorator) buildShared() bool {
- return library.MutatedProperties.BuildShared &&
- (library.Properties.Shared.Enabled == nil || *library.Properties.Shared.Enabled)
+ return library.MutatedProperties.BuildShared && BoolDefault(library.Properties.Shared.Enabled, true)
}
func (library *libraryDecorator) getWholeStaticMissingDeps() []string {
- return library.wholeStaticMissingDeps
+ return append([]string(nil), library.wholeStaticMissingDeps...)
}
func (library *libraryDecorator) objs() Objects {
@@ -744,12 +911,23 @@
return library.tocFile
}
+func (library *libraryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
+ dir := library.baseInstaller.installDir(ctx)
+ dirOnDevice := android.InstallPathToOnDevicePath(ctx, dir)
+ target := "/" + filepath.Join("apex", "com.android.runtime", dir.Base(), "bionic", file.Base())
+ ctx.InstallAbsoluteSymlink(dir, file.Base(), target)
+ library.post_install_cmds = append(library.post_install_cmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
+}
+
func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
if library.shared() {
if ctx.Device() && ctx.useVndk() {
if ctx.isVndkSp() {
library.baseInstaller.subDir = "vndk-sp"
} else if ctx.isVndk() {
+ if ctx.DeviceConfig().VndkUseCoreVariant() && !ctx.mustUseVendorVariant() {
+ library.useCoreVariant = true
+ }
library.baseInstaller.subDir = "vndk"
}
@@ -760,15 +938,26 @@
library.baseInstaller.subDir += "-" + vndkVersion
}
}
+ } else if len(library.Properties.Stubs.Versions) > 0 && android.DirectlyInAnyApex(ctx, ctx.ModuleName()) {
+ // Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
+ // The original path becomes a symlink to the corresponding file in the
+ // runtime APEX.
+ if isBionic(ctx.baseModuleName()) && !library.buildStubs() && ctx.Arch().Native && !ctx.inRecovery() {
+ if ctx.Device() {
+ library.installSymlinkToRuntimeApex(ctx, file)
+ }
+ library.baseInstaller.subDir = "bootstrap"
+ }
}
library.baseInstaller.install(ctx, file)
}
if Bool(library.Properties.Static_ndk_lib) && library.static() &&
- !ctx.useVndk() && ctx.Device() &&
- library.sanitize.isUnsanitizedVariant() {
+ !ctx.useVndk() && !ctx.inRecovery() && ctx.Device() &&
+ library.baseLinker.sanitize.isUnsanitizedVariant() &&
+ !library.buildStubs() {
installPath := getNdkSysrootBase(ctx).Join(
- ctx, "usr/lib", ctx.toolchain().ClangTriple(), file.Base())
+ ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: android.Cp,
@@ -816,6 +1005,33 @@
library.MutatedProperties.BuildStatic = false
}
+func (library *libraryDecorator) buildStubs() bool {
+ return library.MutatedProperties.BuildStubs
+}
+
+func (library *libraryDecorator) stubsVersion() string {
+ return library.MutatedProperties.StubsVersion
+}
+
+var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
+
+func versioningMacroNamesList(config android.Config) *map[string]string {
+ return config.Once(versioningMacroNamesListKey, func() interface{} {
+ m := make(map[string]string)
+ return &m
+ }).(*map[string]string)
+}
+
+// alphanumeric and _ characters are preserved.
+// other characters are all converted to _
+var charsNotForMacro = regexp.MustCompile("[^a-zA-Z0-9_]+")
+
+func versioningMacroName(moduleName string) string {
+ macroName := charsNotForMacro.ReplaceAllString(moduleName, "_")
+ macroName = strings.ToUpper(moduleName)
+ return "__" + macroName + "_API__"
+}
+
func NewLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module := newModule(hod, android.MultilibBoth)
@@ -825,9 +1041,8 @@
BuildStatic: true,
},
baseCompiler: NewBaseCompiler(),
- baseLinker: NewBaseLinker(),
+ baseLinker: NewBaseLinker(module.sanitize),
baseInstaller: NewBaseInstaller("lib", "lib64", InstallInSystem),
- sanitize: module.sanitize,
sabi: module.sabi,
}
@@ -843,24 +1058,56 @@
func reuseStaticLibrary(mctx android.BottomUpMutatorContext, static, shared *Module) {
if staticCompiler, ok := static.compiler.(*libraryDecorator); ok {
sharedCompiler := shared.compiler.(*libraryDecorator)
+
+ // Check libraries in addition to cflags, since libraries may be exporting different
+ // include directories.
if len(staticCompiler.Properties.Static.Cflags) == 0 &&
- len(sharedCompiler.Properties.Shared.Cflags) == 0 {
+ len(sharedCompiler.Properties.Shared.Cflags) == 0 &&
+ len(staticCompiler.Properties.Static.Whole_static_libs) == 0 &&
+ len(sharedCompiler.Properties.Shared.Whole_static_libs) == 0 &&
+ len(staticCompiler.Properties.Static.Static_libs) == 0 &&
+ len(sharedCompiler.Properties.Shared.Static_libs) == 0 &&
+ len(staticCompiler.Properties.Static.Shared_libs) == 0 &&
+ len(sharedCompiler.Properties.Shared.Shared_libs) == 0 &&
+ staticCompiler.Properties.Static.System_shared_libs == nil &&
+ sharedCompiler.Properties.Shared.System_shared_libs == nil {
mctx.AddInterVariantDependency(reuseObjTag, shared, static)
sharedCompiler.baseCompiler.Properties.OriginalSrcs =
sharedCompiler.baseCompiler.Properties.Srcs
sharedCompiler.baseCompiler.Properties.Srcs = nil
sharedCompiler.baseCompiler.Properties.Generated_sources = nil
+ } else {
+ // This dep is just to reference static variant from shared variant
+ mctx.AddInterVariantDependency(staticVariantTag, shared, static)
}
}
}
-func linkageMutator(mctx android.BottomUpMutatorContext) {
+func LinkageMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*Module); ok && m.linker != nil {
- if library, ok := m.linker.(libraryInterface); ok {
- var modules []blueprint.Module
+ switch library := m.linker.(type) {
+ case prebuiltLibraryInterface:
+ // Always create both the static and shared variants for prebuilt libraries, and then disable the one
+ // that is not being used. This allows them to share the name of a cc_library module, which requires that
+ // all the variants of the cc_library also exist on the prebuilt.
+ modules := mctx.CreateLocalVariations("static", "shared")
+ static := modules[0].(*Module)
+ shared := modules[1].(*Module)
+
+ static.linker.(prebuiltLibraryInterface).setStatic()
+ shared.linker.(prebuiltLibraryInterface).setShared()
+
+ if !library.buildStatic() {
+ static.linker.(prebuiltLibraryInterface).disablePrebuilt()
+ }
+ if !library.buildShared() {
+ shared.linker.(prebuiltLibraryInterface).disablePrebuilt()
+ }
+
+ case libraryInterface:
if library.buildStatic() && library.buildShared() {
- modules = mctx.CreateLocalVariations("static", "shared")
+ modules := mctx.CreateLocalVariations("static", "shared")
static := modules[0].(*Module)
shared := modules[1].(*Module)
@@ -870,12 +1117,86 @@
reuseStaticLibrary(mctx, static, shared)
} else if library.buildStatic() {
- modules = mctx.CreateLocalVariations("static")
+ modules := mctx.CreateLocalVariations("static")
modules[0].(*Module).linker.(libraryInterface).setStatic()
} else if library.buildShared() {
- modules = mctx.CreateLocalVariations("shared")
+ modules := mctx.CreateLocalVariations("shared")
modules[0].(*Module).linker.(libraryInterface).setShared()
}
}
}
}
+
+var stubVersionsKey = android.NewOnceKey("stubVersions")
+
+// maps a module name to the list of stubs versions available for the module
+func stubsVersionsFor(config android.Config) map[string][]string {
+ return config.Once(stubVersionsKey, func() interface{} {
+ return make(map[string][]string)
+ }).(map[string][]string)
+}
+
+var stubsVersionsLock sync.Mutex
+
+func latestStubsVersionFor(config android.Config, name string) string {
+ versions, ok := stubsVersionsFor(config)[name]
+ if ok && len(versions) > 0 {
+ // the versions are alreay sorted in ascending order
+ return versions[len(versions)-1]
+ }
+ return ""
+}
+
+// Version mutator splits a module into the mandatory non-stubs variant
+// (which is unnamed) and zero or more stubs variants.
+func VersionMutator(mctx android.BottomUpMutatorContext) {
+ if m, ok := mctx.Module().(*Module); ok && !m.inRecovery() && m.linker != nil {
+ if library, ok := m.linker.(*libraryDecorator); ok && library.buildShared() &&
+ len(library.Properties.Stubs.Versions) > 0 {
+ versions := []string{}
+ for _, v := range library.Properties.Stubs.Versions {
+ if _, err := strconv.Atoi(v); err != nil {
+ mctx.PropertyErrorf("versions", "%q is not a number", v)
+ }
+ versions = append(versions, v)
+ }
+ sort.Slice(versions, func(i, j int) bool {
+ left, _ := strconv.Atoi(versions[i])
+ right, _ := strconv.Atoi(versions[j])
+ return left < right
+ })
+
+ // save the list of versions for later use
+ copiedVersions := make([]string, len(versions))
+ copy(copiedVersions, versions)
+ stubsVersionsLock.Lock()
+ defer stubsVersionsLock.Unlock()
+ stubsVersionsFor(mctx.Config())[mctx.ModuleName()] = copiedVersions
+
+ // "" is for the non-stubs variant
+ versions = append([]string{""}, versions...)
+
+ modules := mctx.CreateVariations(versions...)
+ for i, m := range modules {
+ l := m.(*Module).linker.(*libraryDecorator)
+ if versions[i] != "" {
+ l.MutatedProperties.BuildStubs = true
+ l.MutatedProperties.StubsVersion = versions[i]
+ m.(*Module).Properties.HideFromMake = true
+ m.(*Module).sanitize = nil
+ m.(*Module).stl = nil
+ m.(*Module).Properties.PreventInstall = true
+ }
+ }
+ } else {
+ mctx.CreateVariations("")
+ }
+ return
+ }
+ if genrule, ok := mctx.Module().(*genrule.Module); ok {
+ if props, ok := genrule.Extra.(*GenruleExtraProperties); ok && !props.InRecovery {
+ mctx.CreateVariations("")
+ return
+ }
+ }
+}
diff --git a/cc/linker.go b/cc/linker.go
index bcedc8d..e063e44 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -16,7 +16,9 @@
import (
"android/soong/android"
+ "android/soong/cc/config"
"fmt"
+ "strconv"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -47,7 +49,7 @@
// list of system libraries that will be dynamically linked to
// shared library and executable modules. If unset, generally defaults to libc,
// libm, and libdl. Set to [] to prevent linking against the defaults.
- System_shared_libs []string
+ System_shared_libs []string `android:"arch_variant"`
// allow the module to contain undefined symbols. By default,
// modules cannot contain undefined symbols that are not satisified by their immediate
@@ -58,6 +60,12 @@
// don't link in libgcc.a
No_libgcc *bool
+ // don't link in libclang_rt.builtins-*.a
+ No_libcrt *bool `android:"arch_variant"`
+
+ // Use clang lld instead of gnu ld.
+ Use_clang_lld *bool `android:"arch_variant"`
+
// -l arguments to pass to linker for host-provided shared libraries
Host_ldlibs []string `android:"arch_variant"`
@@ -85,32 +93,81 @@
// between static libraries, but it is generally better to order them correctly instead.
Group_static_libs *bool `android:"arch_variant"`
+ // list of modules that should be installed with this module. This is similar to 'required'
+ // but '.vendor' suffix will be appended to the module names if the shared libraries have
+ // vendor variants and this module uses VNDK.
+ Runtime_libs []string `android:"arch_variant"`
+
Target struct {
Vendor struct {
+ // list of shared libs that only should be used to build the vendor
+ // variant of the C/C++ module.
+ Shared_libs []string
+
+ // list of shared libs that should not be used to build the vendor variant
+ // of the C/C++ module.
+ Exclude_shared_libs []string
+
+ // list of static libs that should not be used to build the vendor variant
+ // of the C/C++ module.
+ Exclude_static_libs []string
+
+ // list of header libs that should not be used to build the vendor variant
+ // of the C/C++ module.
+ Exclude_header_libs []string
+
+ // list of runtime libs that should not be installed along with the vendor
+ // variant of the C/C++ module.
+ Exclude_runtime_libs []string
+
+ // version script for this vendor variant
+ Version_script *string `android:"arch_variant"`
+ }
+ Recovery struct {
+ // list of shared libs that only should be used to build the recovery
+ // variant of the C/C++ module.
+ Shared_libs []string
+
// list of shared libs that should not be used to build
- // the vendor variant of the C/C++ module.
+ // the recovery variant of the C/C++ module.
Exclude_shared_libs []string
// list of static libs that should not be used to build
- // the vendor variant of the C/C++ module.
+ // the recovery variant of the C/C++ module.
Exclude_static_libs []string
+
+ // list of header libs that should not be used to build the recovery variant
+ // of the C/C++ module.
+ Exclude_header_libs []string
}
}
// make android::build:GetBuildNumber() available containing the build ID.
Use_version_lib *bool `android:"arch_variant"`
+
+ // Generate compact dynamic relocation table, default true.
+ Pack_relocations *bool `android:"arch_variant"`
+
+ // local file name to pass to the linker as --version_script
+ Version_script *string `android:"path,arch_variant"`
+
+ // Local file name to pass to the linker as --symbol-ordering-file
+ Symbol_ordering_file *string `android:"arch_variant"`
}
-func NewBaseLinker() *baseLinker {
- return &baseLinker{}
+func NewBaseLinker(sanitize *sanitize) *baseLinker {
+ return &baseLinker{sanitize: sanitize}
}
// baseLinker provides support for shared_libs, static_libs, and whole_static_libs properties
type baseLinker struct {
Properties BaseLinkerProperties
dynamicProperties struct {
- RunPaths []string `blueprint:"mutated"`
+ RunPaths []string `blueprint:"mutated"`
+ BuildStubs bool `blueprint:"mutated"`
}
+
+ sanitize *sanitize
}
func (linker *baseLinker) appendLdflags(flags []string) {
@@ -129,11 +186,12 @@
return []interface{}{&linker.Properties, &linker.dynamicProperties}
}
-func (linker *baseLinker) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
+func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...)
deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...)
deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...)
+ deps.RuntimeLibs = append(deps.RuntimeLibs, linker.Properties.Runtime_libs...)
deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, linker.Properties.Export_header_lib_headers...)
deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, linker.Properties.Export_static_lib_headers...)
@@ -145,53 +203,89 @@
}
if ctx.useVndk() {
+ deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Vendor.Shared_libs...)
deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Vendor.Exclude_shared_libs)
deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Vendor.Exclude_shared_libs)
deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
+ deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Vendor.Exclude_header_libs)
deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs)
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
+ deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
}
- if ctx.ModuleName() != "libcompiler_rt-extras" {
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libcompiler_rt-extras")
+ if ctx.inRecovery() {
+ deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Recovery.Shared_libs...)
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Recovery.Exclude_header_libs)
+ deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Recovery.Exclude_header_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
}
if ctx.toolchain().Bionic() {
- // libgcc and libatomic have to be last on the command line
- deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
- if !Bool(linker.Properties.No_libgcc) {
+ // libclang_rt.builtins, libgcc and libatomic have to be last on the command line
+ if !Bool(linker.Properties.No_libcrt) {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc_stripped")
+ } else if !Bool(linker.Properties.No_libgcc) {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc")
}
- if !ctx.static() {
- systemSharedLibs := linker.Properties.System_shared_libs
- if systemSharedLibs == nil {
- systemSharedLibs = []string{"libc", "libm", "libdl"}
- }
-
- if inList("libdl", deps.SharedLibs) {
- // If system_shared_libs has libc but not libdl, make sure shared_libs does not
- // have libdl to avoid loading libdl before libc.
- if inList("libc", systemSharedLibs) {
- if !inList("libdl", systemSharedLibs) {
- ctx.PropertyErrorf("shared_libs",
- "libdl must be in system_shared_libs, not shared_libs")
- }
- _, deps.SharedLibs = removeFromList("libdl", deps.SharedLibs)
- }
- }
-
- // If libc and libdl are both in system_shared_libs make sure libd comes after libc
- // to avoid loading libdl before libc.
- if inList("libdl", systemSharedLibs) && inList("libc", systemSharedLibs) &&
- indexList("libdl", systemSharedLibs) < indexList("libc", systemSharedLibs) {
- ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
- }
-
- deps.LateSharedLibs = append(deps.LateSharedLibs, systemSharedLibs...)
- } else if ctx.useSdk() || ctx.useVndk() {
- deps.LateSharedLibs = append(deps.LateSharedLibs, "libc", "libm", "libdl")
+ systemSharedLibs := linker.Properties.System_shared_libs
+ if systemSharedLibs == nil {
+ // Provide a default system_shared_libs if it is unspecified. Note: If an
+ // empty list [] is specified, it implies that the module declines the
+ // default system_shared_libs.
+ systemSharedLibs = []string{"libc", "libm", "libdl"}
}
+
+ if inList("libdl", deps.SharedLibs) {
+ // If system_shared_libs has libc but not libdl, make sure shared_libs does not
+ // have libdl to avoid loading libdl before libc.
+ if inList("libc", systemSharedLibs) {
+ if !inList("libdl", systemSharedLibs) {
+ ctx.PropertyErrorf("shared_libs",
+ "libdl must be in system_shared_libs, not shared_libs")
+ }
+ _, deps.SharedLibs = removeFromList("libdl", deps.SharedLibs)
+ }
+ }
+
+ if inList("libc_scudo", deps.SharedLibs) {
+ // libc_scudo is an alternate implementation of all
+ // allocation functions (malloc, free), that uses
+ // the scudo allocator instead of the default native
+ // allocator. If this library is in the list, make
+ // sure it's first so it properly overrides the
+ // allocation functions of all other shared libraries.
+ _, deps.SharedLibs = removeFromList("libc_scudo", deps.SharedLibs)
+ deps.SharedLibs = append([]string{"libc_scudo"}, deps.SharedLibs...)
+ }
+
+ // If libc and libdl are both in system_shared_libs make sure libdl comes after libc
+ // to avoid loading libdl before libc.
+ if inList("libdl", systemSharedLibs) && inList("libc", systemSharedLibs) &&
+ indexList("libdl", systemSharedLibs) < indexList("libc", systemSharedLibs) {
+ ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
+ }
+
+ deps.LateSharedLibs = append(deps.LateSharedLibs, systemSharedLibs...)
+ }
+
+ if ctx.Fuchsia() {
+ if ctx.ModuleName() != "libbioniccompat" &&
+ ctx.ModuleName() != "libcompiler_rt-extras" &&
+ ctx.ModuleName() != "libcompiler_rt" {
+ deps.StaticLibs = append(deps.StaticLibs, "libbioniccompat")
+ }
+ if ctx.ModuleName() != "libcompiler_rt" && ctx.ModuleName() != "libcompiler_rt-extras" {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libcompiler_rt")
+ }
+
}
if ctx.Windows() {
@@ -201,6 +295,41 @@
return deps
}
+func (linker *baseLinker) useClangLld(ctx ModuleContext) bool {
+ // Clang lld is not ready for for Darwin host executables yet.
+ // See https://lld.llvm.org/AtomLLD.html for status of lld for Mach-O.
+ if ctx.Darwin() {
+ return false
+ }
+ // http://b/110800681 - lld cannot link Android's Windows modules yet.
+ if ctx.Windows() {
+ return false
+ }
+ if linker.Properties.Use_clang_lld != nil {
+ return Bool(linker.Properties.Use_clang_lld)
+ }
+ return true
+}
+
+// Check whether the SDK version is not older than the specific one
+func CheckSdkVersionAtLeast(ctx ModuleContext, SdkVersion int) bool {
+ if ctx.sdkVersion() == "current" {
+ return true
+ }
+ parsedSdkVersion, err := strconv.Atoi(ctx.sdkVersion())
+ if err != nil {
+ ctx.PropertyErrorf("sdk_version",
+ "Invalid sdk_version value (must be int or current): %q",
+ ctx.sdkVersion())
+ }
+ if parsedSdkVersion < SdkVersion {
+ return false
+ }
+ return true
+}
+
+// ModuleContext extends BaseModuleContext
+// BaseModuleContext should know if LLD is used?
func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
toolchain := ctx.toolchain()
@@ -209,7 +338,21 @@
hod = "Device"
}
- flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
+ if linker.useClangLld(ctx) {
+ flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
+ if !BoolDefault(linker.Properties.Pack_relocations, true) {
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=none")
+ } else if ctx.Device() {
+ // The SHT_RELR relocations is only supported by API level >= 28.
+ // Do not turn this on if older version NDK is used.
+ if !ctx.useSdk() || CheckSdkVersionAtLeast(ctx, 28) {
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=android+relr")
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--use-android-relr-tags")
+ }
+ }
+ } else {
+ flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
+ }
if Bool(linker.Properties.Allow_undefined_symbols) {
if ctx.Darwin() {
// darwin defaults to treating undefined symbols as errors
@@ -219,13 +362,13 @@
flags.LdFlags = append(flags.LdFlags, "-Wl,--no-undefined")
}
- if flags.Clang {
- flags.LdFlags = append(flags.LdFlags, toolchain.ClangLdflags())
+ if linker.useClangLld(ctx) {
+ flags.LdFlags = append(flags.LdFlags, toolchain.ClangLldflags())
} else {
- flags.LdFlags = append(flags.LdFlags, toolchain.Ldflags())
+ flags.LdFlags = append(flags.LdFlags, toolchain.ClangLdflags())
}
- if !ctx.toolchain().Bionic() {
+ if !ctx.toolchain().Bionic() && !ctx.Fuchsia() {
CheckBadHostLdlibs(ctx, "host_ldlibs", linker.Properties.Host_ldlibs)
flags.LdFlags = append(flags.LdFlags, linker.Properties.Host_ldlibs...)
@@ -244,9 +387,13 @@
}
}
+ if ctx.Fuchsia() {
+ flags.LdFlags = append(flags.LdFlags, "-lfdio", "-lzircon")
+ }
+
CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
- flags.LdFlags = append(flags.LdFlags, proptools.NinjaAndShellEscape(linker.Properties.Ldflags)...)
+ flags.LdFlags = append(flags.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
if ctx.Host() {
rpath_prefix := `\$$ORIGIN/`
@@ -269,16 +416,52 @@
flags.LdFlags = append(flags.LdFlags, "-Wl,--hash-style=both")
}
- if flags.Clang {
- flags.LdFlags = append(flags.LdFlags, toolchain.ToolchainClangLdflags())
- } else {
- flags.LdFlags = append(flags.LdFlags, toolchain.ToolchainLdflags())
- }
+ flags.LdFlags = append(flags.LdFlags, toolchain.ToolchainClangLdflags())
if Bool(linker.Properties.Group_static_libs) {
flags.GroupStaticLibs = true
}
+ // Version_script is not needed when linking stubs lib where the version
+ // script is created from the symbol map file.
+ if !linker.dynamicProperties.BuildStubs {
+ versionScript := ctx.ExpandOptionalSource(
+ linker.Properties.Version_script, "version_script")
+
+ if ctx.useVndk() && linker.Properties.Target.Vendor.Version_script != nil {
+ versionScript = ctx.ExpandOptionalSource(
+ linker.Properties.Target.Vendor.Version_script,
+ "target.vendor.version_script")
+ }
+
+ if versionScript.Valid() {
+ if ctx.Darwin() {
+ ctx.PropertyErrorf("version_script", "Not supported on Darwin")
+ } else {
+ flags.LdFlags = append(flags.LdFlags,
+ "-Wl,--version-script,"+versionScript.String())
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, versionScript.Path())
+
+ if linker.sanitize.isSanitizerEnabled(cfi) {
+ cfiExportsMap := android.PathForSource(ctx, cfiExportsMapPath)
+ flags.LdFlags = append(flags.LdFlags,
+ "-Wl,--version-script,"+cfiExportsMap.String())
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, cfiExportsMap)
+ }
+ }
+ }
+ }
+
+ if !linker.dynamicProperties.BuildStubs {
+ symbolOrderingFile := ctx.ExpandOptionalSource(
+ linker.Properties.Symbol_ordering_file, "Symbol_ordering_file")
+ if symbolOrderingFile.Valid() {
+ flags.LdFlags = append(flags.LdFlags,
+ "-Wl,--symbol-ordering-file,"+symbolOrderingFile.String())
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, symbolOrderingFile.Path())
+ }
+ }
+
return flags
}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 6e64acf..56ef2b6 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -144,23 +144,27 @@
timestampFiles = append(timestampFiles, stub.processHeaders(ctx, dir, genHeaderOutDir))
}
- includePrefix := "-I "
+ includePrefix := "-I"
if Bool(stub.Properties.Export_headers_as_system) {
includePrefix = "-isystem "
}
- stub.reexportFlags([]string{includePrefix + " " + genHeaderOutDir.String()})
+ stub.reexportFlags([]string{includePrefix + genHeaderOutDir.String()})
stub.reexportDeps(timestampFiles)
}
if Bool(stub.Properties.Export_headers_as_system) {
- stub.exportIncludes(ctx, "-isystem")
+ stub.exportIncludes(ctx, "-isystem ")
stub.libraryDecorator.flagExporter.Properties.Export_include_dirs = []string{}
}
return stub.libraryDecorator.link(ctx, flags, deps, objs)
}
+func (stub *llndkStubDecorator) nativeCoverage() bool {
+ return false
+}
+
func NewLLndkStubLibrary() *Module {
module, library := NewLibrary(android.DeviceSupported)
library.BuildOnlyShared()
@@ -172,11 +176,13 @@
libraryDecorator: library,
}
stub.Properties.Vendor_available = BoolPtr(true)
+ module.Properties.UseVndk = true
module.compiler = stub
module.linker = stub
module.installer = nil
module.AddProperties(
+ &module.Properties,
&stub.Properties,
&library.MutatedProperties,
&library.flagExporter.Properties)
@@ -184,7 +190,7 @@
return module
}
-func llndkLibraryFactory() android.Module {
+func LlndkLibraryFactory() android.Module {
module := NewLLndkStubLibrary()
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
return module
@@ -210,7 +216,10 @@
module.linker = decorator
module.installer = nil
- module.AddProperties(&library.MutatedProperties, &library.flagExporter.Properties)
+ module.AddProperties(
+ &module.Properties,
+ &library.MutatedProperties,
+ &library.flagExporter.Properties)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
@@ -218,6 +227,6 @@
}
func init() {
- android.RegisterModuleType("llndk_library", llndkLibraryFactory)
+ android.RegisterModuleType("llndk_library", LlndkLibraryFactory)
android.RegisterModuleType("llndk_headers", llndkHeadersFactory)
}
diff --git a/cc/lto.go b/cc/lto.go
index 2ced124..1084869 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -49,6 +49,9 @@
// since it is an object dependency of an LTO module.
FullDep bool `blueprint:"mutated"`
ThinDep bool `blueprint:"mutated"`
+
+ // Use clang lld instead of gnu ld.
+ Use_clang_lld *bool
}
type lto struct {
@@ -69,24 +72,37 @@
return deps
}
+func (lto *lto) useClangLld(ctx BaseModuleContext) bool {
+ if lto.Properties.Use_clang_lld != nil {
+ return Bool(lto.Properties.Use_clang_lld)
+ }
+ return true
+}
+
func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
if lto.LTO() {
var ltoFlag string
if Bool(lto.Properties.Lto.Thin) {
- ltoFlag = "-flto=thin"
+ ltoFlag = "-flto=thin -fsplit-lto-unit"
} else {
ltoFlag = "-flto"
}
flags.CFlags = append(flags.CFlags, ltoFlag)
flags.LdFlags = append(flags.LdFlags, ltoFlag)
- if ctx.Device() {
- // Work around bug in Clang that doesn't pass correct emulated
- // TLS option to target. See b/72706604 or
- // https://github.com/android-ndk/ndk/issues/498.
- flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-emulated-tls")
+
+ if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && lto.useClangLld(ctx) {
+ // Set appropriate ThinLTO cache policy
+ cacheDirFormat := "-Wl,--thinlto-cache-dir="
+ cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
+ flags.LdFlags = append(flags.LdFlags, cacheDirFormat+cacheDir)
+
+ // Limit the size of the ThinLTO cache to the lesser of 10% of available
+ // disk space and 10GB.
+ cachePolicyFormat := "-Wl,--thinlto-cache-policy="
+ policy := "cache_size=10%:cache_size_bytes=10g"
+ flags.LdFlags = append(flags.LdFlags, cachePolicyFormat+policy)
}
- flags.ArGoldPlugin = true
// If the module does not have a profile, be conservative and do not inline
// or unroll loops during LTO, in order to prevent significant size bloat.
diff --git a/cc/makevars.go b/cc/makevars.go
index 3bb00a1..aa6fdea 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -24,24 +24,24 @@
"android/soong/cc/config"
)
-const (
- modulesAddedWall = "ModulesAddedWall"
- modulesUsingWnoError = "ModulesUsingWnoError"
- modulesMissingProfileFile = "ModulesMissingProfileFile"
+var (
+ modulesAddedWallKey = android.NewOnceKey("ModulesAddedWall")
+ modulesUsingWnoErrorKey = android.NewOnceKey("ModulesUsingWnoError")
+ modulesMissingProfileFileKey = android.NewOnceKey("ModulesMissingProfileFile")
)
func init() {
android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
}
-func getNamedMapForConfig(config android.Config, name string) *sync.Map {
- return config.Once(name, func() interface{} {
+func getNamedMapForConfig(config android.Config, key android.OnceKey) *sync.Map {
+ return config.Once(key, func() interface{} {
return &sync.Map{}
}).(*sync.Map)
}
-func makeStringOfKeys(ctx android.MakeVarsContext, setName string) string {
- set := getNamedMapForConfig(ctx.Config(), setName)
+func makeStringOfKeys(ctx android.MakeVarsContext, key android.OnceKey) string {
+ set := getNamedMapForConfig(ctx.Config(), key)
keys := []string{}
set.Range(func(key interface{}, value interface{}) bool {
keys = append(keys, key.(string))
@@ -72,7 +72,10 @@
ctx.Strict("CLANG_CXX", "${config.ClangBin}/clang++")
ctx.Strict("LLVM_AS", "${config.ClangBin}/llvm-as")
ctx.Strict("LLVM_LINK", "${config.ClangBin}/llvm-link")
+ ctx.Strict("LLVM_OBJCOPY", "${config.ClangBin}/llvm-objcopy")
+ ctx.Strict("LLVM_STRIP", "${config.ClangBin}/llvm-strip")
ctx.Strict("PATH_TO_CLANG_TIDY", "${config.ClangBin}/clang-tidy")
+ ctx.Strict("PATH_TO_CLANG_TIDY_SHELL", "${config.ClangTidyShellPath}")
ctx.StrictSorted("CLANG_CONFIG_UNKNOWN_CFLAGS", strings.Join(config.ClangUnknownCflags, " "))
ctx.Strict("RS_LLVM_PREBUILTS_VERSION", "${config.RSClangVersion}")
@@ -83,9 +86,8 @@
ctx.Strict("RS_LLVM_AS", "${config.RSLLVMPrebuiltsPath}/llvm-as")
ctx.Strict("RS_LLVM_LINK", "${config.RSLLVMPrebuiltsPath}/llvm-link")
- ctx.Strict("GLOBAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
- ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.ClangExtraNoOverrideCflags}")
- ctx.Strict("GLOBAL_CPPFLAGS_NO_OVERRIDE", "")
+ ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ClangExternalCflags}")
+ ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideClangGlobalCflags}")
ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
ctx.Strict("NDK_PREBUILT_SHARED_LIBRARIES", strings.Join(ndkPrebuiltSharedLibs, " "))
@@ -95,10 +97,11 @@
ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(vndkSpLibraries, " "))
ctx.Strict("LLNDK_LIBRARIES", strings.Join(llndkLibraries, " "))
ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(vndkPrivateLibraries, " "))
+ ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(vndkUsingCoreVariantLibraries, " "))
// Filter vendor_public_library that are exported to make
exportedVendorPublicLibraries := []string{}
- ctx.SingletonContext().VisitAllModules(func(module android.Module) {
+ ctx.VisitAllModules(func(module android.Module) {
if ccModule, ok := module.(*Module); ok {
baseName := ccModule.BaseModuleName()
if inList(baseName, vendorPublicLibraries) && module.ExportedToMake() {
@@ -115,33 +118,40 @@
ctx.Strict("LSDUMP_PATHS", strings.Join(lsdumpPaths, " "))
ctx.Strict("ANDROID_WARNING_ALLOWED_PROJECTS", makeStringOfWarningAllowedProjects())
- ctx.Strict("SOONG_MODULES_ADDED_WALL", makeStringOfKeys(ctx, modulesAddedWall))
- ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeStringOfKeys(ctx, modulesUsingWnoError))
- ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeStringOfKeys(ctx, modulesMissingProfileFile))
+ ctx.Strict("SOONG_MODULES_ADDED_WALL", makeStringOfKeys(ctx, modulesAddedWallKey))
+ ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeStringOfKeys(ctx, modulesUsingWnoErrorKey))
+ ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeStringOfKeys(ctx, modulesMissingProfileFileKey))
ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " "))
ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS", strings.Join(asanLdflags, " "))
ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_STATIC_LIBRARIES", strings.Join(asanLibs, " "))
+ ctx.Strict("HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(hwasanCflags, " "))
+ ctx.Strict("HWADDRESS_SANITIZER_GLOBAL_OPTIONS", strings.Join(hwasanGlobalOptions, ","))
+
ctx.Strict("CFI_EXTRA_CFLAGS", strings.Join(cfiCflags, " "))
+ ctx.Strict("CFI_EXTRA_ASFLAGS", strings.Join(cfiAsflags, " "))
ctx.Strict("CFI_EXTRA_LDFLAGS", strings.Join(cfiLdflags, " "))
ctx.Strict("INTEGER_OVERFLOW_EXTRA_CFLAGS", strings.Join(intOverflowCflags, " "))
ctx.Strict("DEFAULT_C_STD_VERSION", config.CStdVersion)
ctx.Strict("DEFAULT_CPP_STD_VERSION", config.CppStdVersion)
- ctx.Strict("DEFAULT_GCC_CPP_STD_VERSION", config.GccCppStdVersion)
ctx.Strict("EXPERIMENTAL_C_STD_VERSION", config.ExperimentalCStdVersion)
ctx.Strict("EXPERIMENTAL_CPP_STD_VERSION", config.ExperimentalCppStdVersion)
ctx.Strict("DEFAULT_GLOBAL_TIDY_CHECKS", "${config.TidyDefaultGlobalChecks}")
ctx.Strict("DEFAULT_LOCAL_TIDY_CHECKS", joinLocalTidyChecks(config.DefaultLocalTidyChecks))
ctx.Strict("DEFAULT_TIDY_HEADER_DIRS", "${config.TidyDefaultHeaderDirs}")
+ ctx.Strict("WITH_TIDY_FLAGS", "${config.TidyWithTidyFlags}")
ctx.Strict("AIDL_CPP", "${aidlCmd}")
ctx.Strict("RS_GLOBAL_INCLUDES", "${config.RsGlobalIncludes}")
+ ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
+ ctx.Strict("XZ", "${xzCmd}")
+
nativeHelperIncludeFlags, err := ctx.Eval("${config.CommonNativehelperInclude}")
if err != nil {
panic(err)
@@ -163,21 +173,13 @@
sort.Strings(ndkMigratedLibs)
ctx.Strict("NDK_MIGRATED_LIBS", strings.Join(ndkMigratedLibs, " "))
- hostTargets := ctx.Config().Targets[android.Host]
+ hostTargets := ctx.Config().Targets[android.BuildOs]
makeVarsToolchain(ctx, "", hostTargets[0])
if len(hostTargets) > 1 {
makeVarsToolchain(ctx, "2ND_", hostTargets[1])
}
- crossTargets := ctx.Config().Targets[android.HostCross]
- if len(crossTargets) > 0 {
- makeVarsToolchain(ctx, "", crossTargets[0])
- if len(crossTargets) > 1 {
- makeVarsToolchain(ctx, "2ND_", crossTargets[1])
- }
- }
-
- deviceTargets := ctx.Config().Targets[android.Device]
+ deviceTargets := ctx.Config().Targets[android.Android]
makeVarsToolchain(ctx, "", deviceTargets[0])
if len(deviceTargets) > 1 {
makeVarsToolchain(ctx, "2ND_", deviceTargets[1])
@@ -190,8 +192,6 @@
switch target.Os.Class {
case android.Host:
typePrefix = "HOST_"
- case android.HostCross:
- typePrefix = "HOST_CROSS_"
case android.Device:
typePrefix = "TARGET_"
}
@@ -211,28 +211,6 @@
productExtraLdflags += "-static"
}
- ctx.Strict(makePrefix+"GLOBAL_CFLAGS", strings.Join([]string{
- toolchain.Cflags(),
- "${config.CommonGlobalCflags}",
- fmt.Sprintf("${config.%sGlobalCflags}", hod),
- toolchain.ToolchainCflags(),
- productExtraCflags,
- }, " "))
- ctx.Strict(makePrefix+"GLOBAL_CONLYFLAGS", strings.Join([]string{
- "${config.CommonGlobalConlyflags}",
- }, " "))
- ctx.Strict(makePrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
- "${config.CommonGlobalCppflags}",
- fmt.Sprintf("${config.%sGlobalCppflags}", hod),
- toolchain.Cppflags(),
- }, " "))
- ctx.Strict(makePrefix+"GLOBAL_LDFLAGS", strings.Join([]string{
- fmt.Sprintf("${config.%sGlobalLdflags}", hod),
- toolchain.Ldflags(),
- toolchain.ToolchainLdflags(),
- productExtraLdflags,
- }, " "))
-
includeFlags, err := ctx.Eval(toolchain.IncludeFlags())
if err != nil {
panic(err)
@@ -242,91 +220,90 @@
ctx.StrictRaw(makePrefix+"C_SYSTEM_INCLUDES", strings.Join(systemIncludes, " "))
if target.Arch.ArchType == android.Arm {
- flags, err := toolchain.InstructionSetFlags("arm")
+ flags, err := toolchain.ClangInstructionSetFlags("arm")
if err != nil {
panic(err)
}
ctx.Strict(makePrefix+"arm_CFLAGS", flags)
- flags, err = toolchain.InstructionSetFlags("thumb")
+ flags, err = toolchain.ClangInstructionSetFlags("thumb")
if err != nil {
panic(err)
}
ctx.Strict(makePrefix+"thumb_CFLAGS", flags)
}
- if toolchain.ClangSupported() {
- clangPrefix := secondPrefix + "CLANG_" + typePrefix
- clangExtras := "-target " + toolchain.ClangTriple()
- clangExtras += " -B" + config.ToolPath(toolchain)
+ clangPrefix := secondPrefix + "CLANG_" + typePrefix
+ clangExtras := "-target " + toolchain.ClangTriple()
+ clangExtras += " -B" + config.ToolPath(toolchain)
- ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
- toolchain.ClangCflags(),
- "${config.CommonClangGlobalCflags}",
- fmt.Sprintf("${config.%sClangGlobalCflags}", hod),
- toolchain.ToolchainClangCflags(),
- clangExtras,
- productExtraCflags,
- }, " "))
- ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
- "${config.CommonClangGlobalCppflags}",
- fmt.Sprintf("${config.%sGlobalCppflags}", hod),
- toolchain.ClangCppflags(),
- }, " "))
- ctx.Strict(clangPrefix+"GLOBAL_LDFLAGS", strings.Join([]string{
- fmt.Sprintf("${config.%sGlobalLdflags}", hod),
- toolchain.ClangLdflags(),
- toolchain.ToolchainClangLdflags(),
- productExtraLdflags,
- clangExtras,
- }, " "))
+ ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
+ toolchain.ClangCflags(),
+ "${config.CommonClangGlobalCflags}",
+ fmt.Sprintf("${config.%sClangGlobalCflags}", hod),
+ toolchain.ToolchainClangCflags(),
+ clangExtras,
+ productExtraCflags,
+ }, " "))
+ ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
+ "${config.CommonClangGlobalCppflags}",
+ fmt.Sprintf("${config.%sGlobalCppflags}", hod),
+ toolchain.ClangCppflags(),
+ }, " "))
+ ctx.Strict(clangPrefix+"GLOBAL_LDFLAGS", strings.Join([]string{
+ fmt.Sprintf("${config.%sGlobalLdflags}", hod),
+ toolchain.ClangLdflags(),
+ toolchain.ToolchainClangLdflags(),
+ productExtraLdflags,
+ clangExtras,
+ }, " "))
+ ctx.Strict(clangPrefix+"GLOBAL_LLDFLAGS", strings.Join([]string{
+ fmt.Sprintf("${config.%sGlobalLldflags}", hod),
+ toolchain.ClangLldflags(),
+ toolchain.ToolchainClangLdflags(),
+ productExtraLdflags,
+ clangExtras,
+ }, " "))
- if target.Os.Class == android.Device {
- ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
- ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
- }
-
- // This is used by external/gentoo/...
- ctx.Strict("CLANG_CONFIG_"+target.Arch.ArchType.Name+"_"+typePrefix+"TRIPLE",
- toolchain.ClangTriple())
-
- ctx.Strict(makePrefix+"CLANG_SUPPORTED", "true")
- } else {
- ctx.Strict(makePrefix+"CLANG_SUPPORTED", "")
+ if target.Os.Class == android.Device {
+ ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
+ ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
+ ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
+ ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
+ ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
+ ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
+ ctx.Strict(secondPrefix+"SCUDO_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoRuntimeLibrary(toolchain), ".so"))
+ ctx.Strict(secondPrefix+"SCUDO_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoMinimalRuntimeLibrary(toolchain), ".so"))
}
- ctx.Strict(makePrefix+"CC", gccCmd(toolchain, "gcc"))
- ctx.Strict(makePrefix+"CXX", gccCmd(toolchain, "g++"))
+ // This is used by external/gentoo/...
+ ctx.Strict("CLANG_CONFIG_"+target.Arch.ArchType.Name+"_"+typePrefix+"TRIPLE",
+ toolchain.ClangTriple())
if target.Os == android.Darwin {
ctx.Strict(makePrefix+"AR", "${config.MacArPath}")
+ ctx.Strict(makePrefix+"NM", "${config.MacToolPath}/nm")
+ ctx.Strict(makePrefix+"OTOOL", "${config.MacToolPath}/otool")
+ ctx.Strict(makePrefix+"STRIP", "${config.MacStripPath}")
} else {
ctx.Strict(makePrefix+"AR", "${config.ClangBin}/llvm-ar")
ctx.Strict(makePrefix+"READELF", gccCmd(toolchain, "readelf"))
ctx.Strict(makePrefix+"NM", gccCmd(toolchain, "nm"))
- }
-
- if target.Os == android.Windows {
- ctx.Strict(makePrefix+"OBJDUMP", gccCmd(toolchain, "objdump"))
+ ctx.Strict(makePrefix+"STRIP", gccCmd(toolchain, "strip"))
}
if target.Os.Class == android.Device {
ctx.Strict(makePrefix+"OBJCOPY", gccCmd(toolchain, "objcopy"))
ctx.Strict(makePrefix+"LD", gccCmd(toolchain, "ld"))
- ctx.Strict(makePrefix+"STRIP", gccCmd(toolchain, "strip"))
ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
- ctx.Strict(makePrefix+"NDK_GCC_VERSION", toolchain.GccVersion())
- ctx.Strict(makePrefix+"NDK_TRIPLE", toolchain.ClangTriple())
+ ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain))
+ ctx.Strict(makePrefix+"TOOLS_PREFIX", gccCmd(toolchain, ""))
}
- if target.Os.Class == android.Host || target.Os.Class == android.HostCross {
+ if target.Os.Class == android.Host {
ctx.Strict(makePrefix+"AVAILABLE_LIBRARIES", strings.Join(toolchain.AvailableLibraries(), " "))
}
- ctx.Strict(makePrefix+"TOOLCHAIN_ROOT", toolchain.GccRoot())
- ctx.Strict(makePrefix+"TOOLS_PREFIX", gccCmd(toolchain, ""))
ctx.Strict(makePrefix+"SHLIB_SUFFIX", toolchain.ShlibSuffix())
ctx.Strict(makePrefix+"EXECUTABLE_SUFFIX", toolchain.ExecutableSuffix())
}
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 9fabc97..5e45c1a 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -26,7 +26,7 @@
)
var (
- preprocessBionicHeaders = pctx.AndroidStaticRule("preprocessBionicHeaders",
+ versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders",
blueprint.RuleParams{
// The `&& touch $out` isn't really necessary, but Blueprint won't
// let us have only implicit outputs.
@@ -34,10 +34,17 @@
CommandDeps: []string{"$versionerCmd"},
},
"depsPath", "srcDir", "outDir")
+
+ preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader",
+ blueprint.RuleParams{
+ Command: "$preprocessor -o $out $in",
+ CommandDeps: []string{"$preprocessor"},
+ },
+ "preprocessor")
)
func init() {
- pctx.HostBinToolVariable("versionerCmd", "versioner")
+ pctx.SourcePathVariable("versionerCmd", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/versioner")
}
// Returns the NDK base include path for use with sdk_version current. Usable with -I.
@@ -45,7 +52,7 @@
return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
}
-type headerProperies struct {
+type headerProperties struct {
// Base directory of the headers being installed. As an example:
//
// ndk_headers {
@@ -63,22 +70,27 @@
To *string
// List of headers to install. Glob compatible. Common case is "include/**/*.h".
- Srcs []string
+ Srcs []string `android:"path"`
+
+ // Source paths that should be excluded from the srcs glob.
+ Exclude_srcs []string `android:"path"`
// Path to the NOTICE file associated with the headers.
- License *string
+ License *string `android:"path"`
+
+ // True if this API is not yet ready to be shipped in the NDK. It will be
+ // available in the platform for testing, but will be excluded from the
+ // sysroot provided to the NDK proper.
+ Draft bool
}
type headerModule struct {
android.ModuleBase
- properties headerProperies
+ properties headerProperties
installPaths android.Paths
- licensePath android.ModuleSrcPath
-}
-
-func (m *headerModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ licensePath android.Path
}
func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
@@ -124,11 +136,11 @@
// but keep them when doing regular platform build.
// Ndk_abis property is only set to true with build/soong/scripts/build-ndk-prebuilts.sh
// TODO: Revert this once MIPS is supported in NDK again.
- if Bool(ctx.AConfig().Ndk_abis) && strings.Contains(ctx.ModuleName(), "mips") {
+ if ctx.Config().NdkAbis() && strings.Contains(ctx.ModuleName(), "mips") {
return
}
- srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
+ srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
for _, header := range srcFiles {
installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
String(m.properties.To))
@@ -154,10 +166,10 @@
return module
}
-type preprocessedHeaderProperies struct {
+type versionedHeaderProperties struct {
// Base directory of the headers being installed. As an example:
//
- // preprocessed_ndk_headers {
+ // versioned_ndk_headers {
// name: "foo",
// from: "include",
// to: "",
@@ -172,6 +184,11 @@
// Path to the NOTICE file associated with the headers.
License *string
+
+ // True if this API is not yet ready to be shipped in the NDK. It will be
+ // available in the platform for testing, but will be excluded from the
+ // sysroot provided to the NDK proper.
+ Draft bool
}
// Like ndk_headers, but preprocesses the headers with the bionic versioner:
@@ -181,19 +198,16 @@
// module does not have the srcs property, and operates on a full directory (the `from` property).
//
// Note that this is really only built to handle bionic/libc/include.
-type preprocessedHeaderModule struct {
+type versionedHeaderModule struct {
android.ModuleBase
- properties preprocessedHeaderProperies
+ properties versionedHeaderProperties
installPaths android.Paths
- licensePath android.ModuleSrcPath
+ licensePath android.Path
}
-func (m *preprocessedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
-}
-
-func (m *preprocessedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if String(m.properties.License) == "" {
ctx.PropertyErrorf("license", "field is required")
}
@@ -218,7 +232,8 @@
processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
}
-func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path, srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
+func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
+ srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
// The versioner depends on a dependencies directory to simplify determining include paths
// when parsing headers. This directory contains architecture specific directories as well
// as a common directory, each of which contains symlinks to the actually directories to
@@ -248,7 +263,7 @@
timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
ctx.Build(pctx, android.BuildParams{
- Rule: preprocessBionicHeaders,
+ Rule: versionBionicHeaders,
Description: "versioner preprocess " + srcDir.Rel(),
Output: timestampFile,
Implicits: append(srcFiles, depsGlob...),
@@ -263,16 +278,94 @@
return timestampFile
}
-func preprocessedNdkHeadersFactory() android.Module {
- module := &preprocessedHeaderModule{}
+func versionedNdkHeadersFactory() android.Module {
+ module := &versionedHeaderModule{}
module.AddProperties(&module.properties)
- // Host module rather than device module because device module install steps
- // do not get run when embedded in make. We're not any of the existing
- // module types that can be exposed via the Android.mk exporter, so just use
- // a host module.
- android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
+ android.InitAndroidModule(module)
+
+ return module
+}
+
+// preprocessed_ndk_header {
+// name: "foo",
+// preprocessor: "foo.sh",
+// srcs: [...],
+// to: "android",
+// }
+//
+// Will invoke the preprocessor as:
+// $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src
+// For each src in srcs.
+type preprocessedHeadersProperties struct {
+ // The preprocessor to run. Must be a program inside the source directory
+ // with no dependencies.
+ Preprocessor *string
+
+ // Source path to the files to be preprocessed.
+ Srcs []string
+
+ // Source paths that should be excluded from the srcs glob.
+ Exclude_srcs []string
+
+ // Install path within the sysroot. This is relative to usr/include.
+ To *string
+
+ // Path to the NOTICE file associated with the headers.
+ License *string
+
+ // True if this API is not yet ready to be shipped in the NDK. It will be
+ // available in the platform for testing, but will be excluded from the
+ // sysroot provided to the NDK proper.
+ Draft bool
+}
+
+type preprocessedHeadersModule struct {
+ android.ModuleBase
+
+ properties preprocessedHeadersProperties
+
+ installPaths android.Paths
+ licensePath android.Path
+}
+
+func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if String(m.properties.License) == "" {
+ ctx.PropertyErrorf("license", "field is required")
+ }
+
+ preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor))
+ m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
+
+ srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+ installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
+ for _, src := range srcFiles {
+ installPath := installDir.Join(ctx, src.Base())
+ m.installPaths = append(m.installPaths, installPath)
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: preprocessNdkHeader,
+ Description: "preprocess " + src.Rel(),
+ Input: src,
+ Output: installPath,
+ Args: map[string]string{
+ "preprocessor": preprocessor.String(),
+ },
+ })
+ }
+
+ if len(m.installPaths) == 0 {
+ ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
+ }
+}
+
+func preprocessedNdkHeadersFactory() android.Module {
+ module := &preprocessedHeadersModule{}
+
+ module.AddProperties(&module.properties)
+
+ android.InitAndroidModule(module)
return module
}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 5a76666..7199467 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -31,14 +31,15 @@
genStubSrc = pctx.AndroidStaticRule("genStubSrc",
blueprint.RuleParams{
Command: "$toolPath --arch $arch --api $apiLevel --api-map " +
- "$apiMap $vndk $in $out",
+ "$apiMap $flags $in $out",
CommandDeps: []string{"$toolPath"},
- }, "arch", "apiLevel", "apiMap", "vndk")
+ }, "arch", "apiLevel", "apiMap", "flags")
ndkLibrarySuffix = ".ndk"
ndkPrebuiltSharedLibs = []string{
"android",
+ "binder_ndk",
"c",
"dl",
"EGL",
@@ -52,6 +53,7 @@
"OpenMAXAL",
"OpenSLES",
"stdc++",
+ "sync",
"vulkan",
"z",
}
@@ -60,7 +62,7 @@
// These libraries have migrated over to the new ndk_library, which is added
// as a variation dependency via depsMutator.
ndkMigratedLibs = []string{}
- ndkMigratedLibsLock sync.Mutex // protects ndkMigratedLibs writes during parallel beginMutator
+ ndkMigratedLibsLock sync.Mutex // protects ndkMigratedLibs writes during parallel BeginMutator
)
// Creates a stub shared library based on the provided version file.
@@ -90,6 +92,11 @@
// Private property for use by the mutator that splits per-API level.
ApiLevel string `blueprint:"mutated"`
+
+ // True if this API is not yet ready to be shipped in the NDK. It will be
+ // available in the platform for testing, but will be excluded from the
+ // sysroot provided to the NDK proper.
+ Draft bool
}
type stubDecorator struct {
@@ -156,7 +163,7 @@
return strconv.Atoi(firstSupportedVersion)
}
-func shouldUseVersionScript(stub *stubDecorator) (bool, error) {
+func shouldUseVersionScript(ctx android.BaseContext, stub *stubDecorator) (bool, error) {
// unversioned_until is normally empty, in which case we should use the version script.
if String(stub.properties.Unversioned_until) == "" {
return true, nil
@@ -174,12 +181,12 @@
return true, nil
}
- unversionedUntil, err := strconv.Atoi(String(stub.properties.Unversioned_until))
+ unversionedUntil, err := android.ApiStrToNum(ctx, String(stub.properties.Unversioned_until))
if err != nil {
return true, err
}
- version, err := strconv.Atoi(stub.properties.ApiLevel)
+ version, err := android.ApiStrToNum(ctx, stub.properties.ApiLevel)
if err != nil {
return true, err
}
@@ -265,7 +272,7 @@
return addStubLibraryCompilerFlags(flags)
}
-func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, vndk string) (Objects, android.ModuleGenPath) {
+func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, genstubFlags string) (Objects, android.ModuleGenPath) {
arch := ctx.Arch().ArchType.String()
stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
@@ -282,7 +289,7 @@
"arch": arch,
"apiLevel": apiLevel,
"apiMap": apiLevelsJson.String(),
- "vndk": vndk,
+ "flags": genstubFlags,
},
})
@@ -318,7 +325,7 @@
func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
objs Objects) android.Path {
- useVersionScript, err := shouldUseVersionScript(stub)
+ useVersionScript, err := shouldUseVersionScript(ctx, stub)
if err != nil {
ctx.ModuleErrorf(err.Error())
}
@@ -331,6 +338,10 @@
return stub.libraryDecorator.link(ctx, flags, deps, objs)
}
+func (stub *stubDecorator) nativeCoverage() bool {
+ return false
+}
+
func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
arch := ctx.Target().Arch.ArchType.Name
apiLevel := stub.properties.ApiLevel
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 7e8d989..2a7e657 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -23,7 +23,6 @@
)
func init() {
- android.RegisterModuleType("ndk_prebuilt_library", ndkPrebuiltLibraryFactory)
android.RegisterModuleType("ndk_prebuilt_object", ndkPrebuiltObjectFactory)
android.RegisterModuleType("ndk_prebuilt_static_stl", ndkPrebuiltStaticStlFactory)
android.RegisterModuleType("ndk_prebuilt_shared_stl", ndkPrebuiltSharedStlFactory)
@@ -69,7 +68,7 @@
module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
module.linker = &ndkPrebuiltObjectLinker{
objectLinker: objectLinker{
- baseLinker: NewBaseLinker(),
+ baseLinker: NewBaseLinker(nil),
},
}
module.Properties.HideFromMake = true
@@ -80,66 +79,32 @@
deps PathDeps, objs Objects) android.Path {
// A null build step, but it sets up the output path.
if !strings.HasPrefix(ctx.ModuleName(), "ndk_crt") {
- ctx.ModuleErrorf("NDK prebuilts must have an ndk_crt prefixed name")
+ ctx.ModuleErrorf("NDK prebuilt objects must have an ndk_crt prefixed name")
}
return ndkPrebuiltModuleToPath(ctx, flags.Toolchain, objectExtension, ctx.sdkVersion())
}
-type ndkPrebuiltLibraryLinker struct {
+type ndkPrebuiltStlLinker struct {
*libraryDecorator
}
-func (ndk *ndkPrebuiltLibraryLinker) linkerProps() []interface{} {
+func (ndk *ndkPrebuiltStlLinker) linkerProps() []interface{} {
return append(ndk.libraryDecorator.linkerProps(), &ndk.Properties, &ndk.flagExporter.Properties)
}
-func (*ndkPrebuiltLibraryLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
+func (*ndkPrebuiltStlLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
// NDK libraries can't have any dependencies
return deps
}
-func ndkPrebuiltLibraryFactory() android.Module {
- module, library := NewLibrary(android.DeviceSupported)
- library.BuildOnlyShared()
- linker := &ndkPrebuiltLibraryLinker{
- libraryDecorator: library,
- }
- module.compiler = nil
- module.linker = linker
- module.installer = nil
- module.stl = nil
- module.Properties.HideFromMake = true
- return module.Init()
-}
-
-func (ndk *ndkPrebuiltLibraryLinker) link(ctx ModuleContext, flags Flags,
- deps PathDeps, objs Objects) android.Path {
- // A null build step, but it sets up the output path.
- ndk.exportIncludes(ctx, "-isystem")
-
- return ndkPrebuiltModuleToPath(ctx, flags.Toolchain, flags.Toolchain.ShlibSuffix(),
- ctx.sdkVersion())
-}
-
-// The NDK STLs are slightly different from the prebuilt system libraries:
-// * Are not specific to each platform version.
-// * The libraries are not in a predictable location for each STL.
-
-type ndkPrebuiltStlLinker struct {
- ndkPrebuiltLibraryLinker
-}
-
func ndkPrebuiltSharedStlFactory() android.Module {
module, library := NewLibrary(android.DeviceSupported)
library.BuildOnlyShared()
- linker := &ndkPrebuiltStlLinker{
- ndkPrebuiltLibraryLinker: ndkPrebuiltLibraryLinker{
- libraryDecorator: library,
- },
- }
module.compiler = nil
- module.linker = linker
+ module.linker = &ndkPrebuiltStlLinker{
+ libraryDecorator: library,
+ }
module.installer = nil
minVersionString := "minimum"
noStlString := "none"
@@ -151,32 +116,28 @@
func ndkPrebuiltStaticStlFactory() android.Module {
module, library := NewLibrary(android.DeviceSupported)
library.BuildOnlyStatic()
- linker := &ndkPrebuiltStlLinker{
- ndkPrebuiltLibraryLinker: ndkPrebuiltLibraryLinker{
- libraryDecorator: library,
- },
- }
module.compiler = nil
- module.linker = linker
+ module.linker = &ndkPrebuiltStlLinker{
+ libraryDecorator: library,
+ }
module.installer = nil
module.Properties.HideFromMake = true
return module.Init()
}
-func getNdkStlLibDir(ctx android.ModuleContext, stl string) android.SourcePath {
- libDir := "cxx-stl/llvm-libc++/libs"
- ndkSrcRoot := "prebuilts/ndk/current/sources"
- return android.PathForSource(ctx, ndkSrcRoot).Join(ctx, libDir, ctx.Arch().Abi[0])
+func getNdkStlLibDir(ctx android.ModuleContext) android.SourcePath {
+ libDir := "prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs"
+ return android.PathForSource(ctx, libDir).Join(ctx, ctx.Arch().Abi[0])
}
func (ndk *ndkPrebuiltStlLinker) link(ctx ModuleContext, flags Flags,
deps PathDeps, objs Objects) android.Path {
// A null build step, but it sets up the output path.
if !strings.HasPrefix(ctx.ModuleName(), "ndk_lib") {
- ctx.ModuleErrorf("NDK prebuilts must have an ndk_lib prefixed name")
+ ctx.ModuleErrorf("NDK prebuilt libraries must have an ndk_lib prefixed name")
}
- ndk.exportIncludes(ctx, "-isystem")
+ ndk.exportIncludes(ctx, "-isystem ")
libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
libExt := flags.Toolchain.ShlibSuffix()
@@ -184,8 +145,6 @@
libExt = staticLibraryExtension
}
- stlName := strings.TrimSuffix(libName, "_shared")
- stlName = strings.TrimSuffix(stlName, "_static")
- libDir := getNdkStlLibDir(ctx, stlName)
+ libDir := getNdkStlLibDir(ctx)
return libDir.Join(ctx, libName+libExt)
}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index c7ba588..e39bae5 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -59,10 +59,11 @@
func init() {
android.RegisterModuleType("ndk_headers", ndkHeadersFactory)
android.RegisterModuleType("ndk_library", ndkLibraryFactory)
+ android.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
android.RegisterSingletonType("ndk", NdkSingleton)
- pctx.Import("android/soong/common")
+ pctx.Import("android/soong/android")
}
func getNdkInstallBase(ctx android.PathContext) android.OutputPath {
@@ -103,17 +104,38 @@
}
if m, ok := module.(*headerModule); ok {
+ if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
+ return
+ }
+
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
- if m, ok := module.(*preprocessedHeaderModule); ok {
+ if m, ok := module.(*versionedHeaderModule); ok {
+ if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
+ return
+ }
+
+ installPaths = append(installPaths, m.installPaths...)
+ licensePaths = append(licensePaths, m.licensePath)
+ }
+
+ if m, ok := module.(*preprocessedHeadersModule); ok {
+ if ctx.Config().ExcludeDraftNdkApis() && m.properties.Draft {
+ return
+ }
+
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
if m, ok := module.(*Module); ok {
if installer, ok := m.installer.(*stubDecorator); ok {
+ if ctx.Config().ExcludeDraftNdkApis() &&
+ installer.properties.Draft {
+ return
+ }
installPaths = append(installPaths, installer.installPath)
}
@@ -126,6 +148,10 @@
}
})
+ // Include only a single copy of each license file. The Bionic NOTICE is
+ // long and is referenced by multiple Bionic modules.
+ licensePaths = android.FirstUniquePaths(licensePaths)
+
combinedLicense := getNdkInstallBase(ctx).Join(ctx, "NOTICE")
ctx.Build(pctx, android.BuildParams{
Rule: android.Cat,
diff --git a/cc/object.go b/cc/object.go
index d0f4f20..2fefd30 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -25,7 +25,7 @@
//
func init() {
- android.RegisterModuleType("cc_object", objectFactory)
+ android.RegisterModuleType("cc_object", ObjectFactory)
}
type objectLinker struct {
@@ -33,12 +33,20 @@
Properties ObjectLinkerProperties
}
-func objectFactory() android.Module {
+// cc_object runs the compiler without running the linker. It is rarely
+// necessary, but sometimes used to generate .s files from .c files to use as
+// input to a cc_genrule module.
+func ObjectFactory() android.Module {
module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
module.linker = &objectLinker{
- baseLinker: NewBaseLinker(),
+ baseLinker: NewBaseLinker(nil),
}
module.compiler = NewBaseCompiler()
+
+ // Clang's address-significance tables are incompatible with ld -r.
+ module.compiler.appendCflags([]string{"-fno-addrsig"})
+
+ module.stl = &stl{}
return module.Init()
}
@@ -63,11 +71,7 @@
}
func (*objectLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
- if flags.Clang {
- flags.LdFlags = append(flags.LdFlags, ctx.toolchain().ToolchainClangLdflags())
- } else {
- flags.LdFlags = append(flags.LdFlags, ctx.toolchain().ToolchainLdflags())
- }
+ flags.LdFlags = append(flags.LdFlags, ctx.toolchain().ToolchainClangLdflags())
return flags
}
@@ -106,3 +110,15 @@
ctx.CheckbuildFile(outputFile)
return outputFile
}
+
+func (object *objectLinker) unstrippedOutputFilePath() android.Path {
+ return nil
+}
+
+func (object *objectLinker) nativeCoverage() bool {
+ return true
+}
+
+func (object *objectLinker) coverageOutputFilePath() android.OptionalPath {
+ return android.OptionalPath{}
+}
diff --git a/cc/pgo.go b/cc/pgo.go
index d39e429..7334ea2 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -19,14 +19,19 @@
"path/filepath"
"strings"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/cc/config"
)
var (
// Add flags to ignore warnings that profiles are old or missing for
- // some functions
- profileUseOtherFlags = []string{"-Wno-backend-plugin"}
+ // some functions, and turn on the experimental new pass manager.
+ profileUseOtherFlags = []string{
+ "-Wno-backend-plugin",
+ "-fexperimental-new-pass-manager",
+ }
globalPgoProfileProjects = []string{
"toolchain/pgo-profiles",
@@ -34,7 +39,8 @@
}
)
-const pgoProfileProjectsConfigKey = "PgoProfileProjects"
+var pgoProfileProjectsConfigKey = android.NewOnceKey("PgoProfileProjects")
+
const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
const profileSamplingFlag = "-gline-tables-only"
const profileUseInstrumentFormat = "-fprofile-use=%s"
@@ -47,7 +53,7 @@
}
func recordMissingProfileFile(ctx BaseModuleContext, missing string) {
- getNamedMapForConfig(ctx.Config(), modulesMissingProfileFile).Store(missing, true)
+ getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
}
type PgoProperties struct {
@@ -160,13 +166,8 @@
return flags
}
- // Skip -fprofile-use if 'enable_profile_use' property is set
- if props.Pgo.Enable_profile_use != nil && *props.Pgo.Enable_profile_use == false {
- return flags
- }
-
- // If the profile file is found, add flags to use the profile
- if profileFile := props.getPgoProfileFile(ctx); profileFile.Valid() {
+ if props.PgoCompile {
+ profileFile := props.getPgoProfileFile(ctx)
profileFilePath := profileFile.Path()
profileUseFlags := props.profileUseFlags(ctx, profileFilePath.String())
@@ -257,7 +258,8 @@
}
}
- if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
+ if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") &&
+ proptools.BoolDefault(pgo.Properties.Pgo.Enable_profile_use, true) {
if profileFile := pgo.Properties.getPgoProfileFile(ctx); profileFile.Valid() {
pgo.Properties.PgoCompile = true
}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 3f277aa..48e4667 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -29,11 +29,20 @@
prebuilt() *android.Prebuilt
}
+type prebuiltLinkerProperties struct {
+
+ // a prebuilt library or binary. Can reference a genrule module that generates an executable file.
+ Srcs []string `android:"path,arch_variant"`
+
+ // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined
+ // symbols, etc), default true.
+ Check_elf_files *bool
+}
+
type prebuiltLinker struct {
android.Prebuilt
- properties struct {
- Srcs []string `android:"arch_variant"`
- }
+
+ properties prebuiltLinkerProperties
}
func (p *prebuiltLinker) prebuilt() *android.Prebuilt {
@@ -44,12 +53,33 @@
return p.properties.Srcs
}
+type prebuiltLibraryInterface interface {
+ libraryInterface
+ prebuiltLinkerInterface
+ disablePrebuilt()
+}
+
type prebuiltLibraryLinker struct {
*libraryDecorator
prebuiltLinker
}
var _ prebuiltLinkerInterface = (*prebuiltLibraryLinker)(nil)
+var _ prebuiltLibraryInterface = (*prebuiltLibraryLinker)(nil)
+
+func (p *prebuiltLibraryLinker) linkerInit(ctx BaseModuleContext) {}
+
+func (p *prebuiltLibraryLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ return p.libraryDecorator.linkerDeps(ctx, deps)
+}
+
+func (p *prebuiltLibraryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+ return flags
+}
+
+func (p *prebuiltLibraryLinker) linkerProps() []interface{} {
+ return p.libraryDecorator.linkerProps()
+}
func (p *prebuiltLibraryLinker) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
@@ -58,13 +88,47 @@
p.libraryDecorator.exportIncludes(ctx, "-I")
p.libraryDecorator.reexportFlags(deps.ReexportedFlags)
p.libraryDecorator.reexportDeps(deps.ReexportedFlagsDeps)
- // TODO(ccross): .toc optimization, stripping, packing
- return p.Prebuilt.SingleSourcePath(ctx)
+
+ builderFlags := flagsToBuilderFlags(flags)
+
+ in := p.Prebuilt.SingleSourcePath(ctx)
+
+ if p.shared() {
+ p.unstrippedOutputFile = in
+ libName := ctx.baseModuleName() + flags.Toolchain.ShlibSuffix()
+ if p.needsStrip(ctx) {
+ stripped := android.PathForModuleOut(ctx, "stripped", libName)
+ p.strip(ctx, in, stripped, builderFlags)
+ in = stripped
+ }
+
+ // Optimize out relinking against shared libraries whose interface hasn't changed by
+ // depending on a table of contents file instead of the library itself.
+ tocFile := android.PathForModuleOut(ctx, libName+".toc")
+ p.tocFile = android.OptionalPathForPath(tocFile)
+ TransformSharedObjectToToc(ctx, in, tocFile, builderFlags)
+ }
+
+ return in
}
return nil
}
+func (p *prebuiltLibraryLinker) shared() bool {
+ return p.libraryDecorator.shared()
+}
+
+func (p *prebuiltLibraryLinker) nativeCoverage() bool {
+ return false
+}
+
+func (p *prebuiltLibraryLinker) disablePrebuilt() {
+ p.properties.Srcs = nil
+}
+
+// cc_prebuilt_library_shared installs a precompiled shared library that are
+// listed in the srcs property in the device's directory.
func prebuiltSharedLibraryFactory() android.Module {
module, _ := NewPrebuiltSharedLibrary(android.HostAndDeviceSupported)
return module.Init()
@@ -83,9 +147,15 @@
module.AddProperties(&prebuilt.properties)
android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
+
+ // Prebuilt libraries can be included in APEXes
+ android.InitApexModule(module)
+
return module, library
}
+// cc_prebuilt_library_static installs a precompiled static library that are
+// listed in the srcs property in the device's directory.
func prebuiltStaticLibraryFactory() android.Module {
module, _ := NewPrebuiltStaticLibrary(android.HostAndDeviceSupported)
return module.Init()
@@ -118,17 +188,26 @@
flags Flags, deps PathDeps, objs Objects) android.Path {
// TODO(ccross): verify shared library dependencies
if len(p.properties.Srcs) > 0 {
- // TODO(ccross): .toc optimization, stripping, packing
+ builderFlags := flagsToBuilderFlags(flags)
+
+ fileName := p.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
+ in := p.Prebuilt.SingleSourcePath(ctx)
+
+ p.unstrippedOutputFile = in
+
+ if p.needsStrip(ctx) {
+ stripped := android.PathForModuleOut(ctx, "stripped", fileName)
+ p.strip(ctx, in, stripped, builderFlags)
+ in = stripped
+ }
// Copy binaries to a name matching the final installed name
- fileName := p.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
outputFile := android.PathForModuleOut(ctx, fileName)
-
ctx.Build(pctx, android.BuildParams{
Rule: android.CpExecutable,
Description: "prebuilt",
Output: outputFile,
- Input: p.Prebuilt.SingleSourcePath(ctx),
+ Input: in,
})
return outputFile
@@ -137,6 +216,8 @@
return nil
}
+// cc_prebuilt_binary installs a precompiled executable in srcs property in the
+// device's directory.
func prebuiltBinaryFactory() android.Module {
module, _ := NewPrebuiltBinary(android.HostAndDeviceSupported)
return module.Init()
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
new file mode 100644
index 0000000..7cc2651
--- /dev/null
+++ b/cc/prebuilt_test.go
@@ -0,0 +1,126 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "testing"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+func TestPrebuilt(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "liba",
+ }
+
+ cc_prebuilt_library_shared {
+ name: "liba",
+ srcs: ["liba.so"],
+ }
+
+ cc_library {
+ name: "libb",
+ }
+
+ cc_prebuilt_library_static {
+ name: "libb",
+ srcs: ["libb.a"],
+ }
+
+ cc_library_shared {
+ name: "libd",
+ }
+
+ cc_prebuilt_library_shared {
+ name: "libd",
+ srcs: ["libd.so"],
+ }
+
+ cc_library_static {
+ name: "libe",
+ }
+
+ cc_prebuilt_library_static {
+ name: "libe",
+ srcs: ["libe.a"],
+ }
+ `
+
+ fs := map[string][]byte{
+ "liba.so": nil,
+ "libb.a": nil,
+ "libd.so": nil,
+ "libe.a": nil,
+ }
+
+ config := android.TestArchConfig(buildDir, nil)
+
+ ctx := createTestContext(t, config, bp, fs, android.Android)
+
+ ctx.RegisterModuleType("cc_prebuilt_library_shared", android.ModuleFactoryAdaptor(prebuiltSharedLibraryFactory))
+ ctx.RegisterModuleType("cc_prebuilt_library_static", android.ModuleFactoryAdaptor(prebuiltStaticLibraryFactory))
+ ctx.RegisterModuleType("cc_prebuilt_binary", android.ModuleFactoryAdaptor(prebuiltBinaryFactory))
+
+ ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
+ ctx.PostDepsMutators(android.RegisterPrebuiltsPostDepsMutators)
+
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ // Verify that all the modules exist and that their dependencies were connected correctly
+ liba := ctx.ModuleForTests("liba", "android_arm64_armv8-a_core_shared").Module()
+ libb := ctx.ModuleForTests("libb", "android_arm64_armv8-a_core_static").Module()
+ libd := ctx.ModuleForTests("libd", "android_arm64_armv8-a_core_shared").Module()
+ libe := ctx.ModuleForTests("libe", "android_arm64_armv8-a_core_static").Module()
+
+ prebuiltLiba := ctx.ModuleForTests("prebuilt_liba", "android_arm64_armv8-a_core_shared").Module()
+ prebuiltLibb := ctx.ModuleForTests("prebuilt_libb", "android_arm64_armv8-a_core_static").Module()
+ prebuiltLibd := ctx.ModuleForTests("prebuilt_libd", "android_arm64_armv8-a_core_shared").Module()
+ prebuiltLibe := ctx.ModuleForTests("prebuilt_libe", "android_arm64_armv8-a_core_static").Module()
+
+ hasDep := func(m android.Module, wantDep android.Module) bool {
+ t.Helper()
+ var found bool
+ ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+ if dep == wantDep {
+ found = true
+ }
+ })
+ return found
+ }
+
+ if !hasDep(liba, prebuiltLiba) {
+ t.Errorf("liba missing dependency on prebuilt_liba")
+ }
+
+ if !hasDep(libb, prebuiltLibb) {
+ t.Errorf("libb missing dependency on prebuilt_libb")
+ }
+
+ if !hasDep(libd, prebuiltLibd) {
+ t.Errorf("libd missing dependency on prebuilt_libd")
+ }
+
+ if !hasDep(libe, prebuiltLibe) {
+ t.Errorf("libe missing dependency on prebuilt_libe")
+ }
+}
diff --git a/cc/proto.go b/cc/proto.go
index 42bb536..f818edc 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -15,89 +15,99 @@
package cc
import (
- "strings"
-
- "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
- "github.com/google/blueprint/proptools"
"android/soong/android"
)
-func init() {
- pctx.HostBinToolVariable("protocCmd", "aprotoc")
-}
-
-var (
- proto = pctx.AndroidStaticRule("protoc",
- blueprint.RuleParams{
- Command: "$protocCmd --cpp_out=$protoOutParams:$outDir -I $protoBase $protoFlags $in",
- CommandDeps: []string{"$protocCmd"},
- }, "protoFlags", "protoOutParams", "protoBase", "outDir")
-)
-
// genProto creates a rule to convert a .proto file to generated .pb.cc and .pb.h files and returns
// the paths to the generated files.
-func genProto(ctx android.ModuleContext, protoFile android.Path,
- protoFlags, protoOutParams string, root bool) (ccFile, headerFile android.WritablePath) {
+func genProto(ctx android.ModuleContext, protoFile android.Path, flags builderFlags) (cc, header android.WritablePath) {
+ var ccFile, headerFile android.ModuleGenPath
- var protoBase string
- if root {
- protoBase = "."
- ccFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb.cc")
+ srcSuffix := ".cc"
+ if flags.protoC {
+ srcSuffix = ".c"
+ }
+
+ if flags.proto.CanonicalPathFromRoot {
+ ccFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb"+srcSuffix)
headerFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb.h")
} else {
rel := protoFile.Rel()
- protoBase = strings.TrimSuffix(protoFile.String(), rel)
- ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb.cc"))
+ ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb"+srcSuffix))
headerFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb.h"))
}
- ctx.Build(pctx, android.BuildParams{
- Rule: proto,
- Description: "protoc " + protoFile.Rel(),
- Outputs: android.WritablePaths{ccFile, headerFile},
- Input: protoFile,
- Args: map[string]string{
- "outDir": android.ProtoDir(ctx).String(),
- "protoFlags": protoFlags,
- "protoOutParams": protoOutParams,
- "protoBase": protoBase,
- },
- })
+ protoDeps := flags.proto.Deps
+ if flags.protoOptionsFile {
+ optionsFile := pathtools.ReplaceExtension(protoFile.String(), "options")
+ optionsPath := android.PathForSource(ctx, optionsFile)
+ protoDeps = append(android.Paths{optionsPath}, protoDeps...)
+ }
+
+ outDir := flags.proto.Dir
+ depFile := ccFile.ReplaceExtension(ctx, "d")
+ outputs := android.WritablePaths{ccFile, headerFile}
+
+ rule := android.NewRuleBuilder()
+
+ android.ProtoRule(ctx, rule, protoFile, flags.proto, protoDeps, outDir, depFile, outputs)
+
+ rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
return ccFile, headerFile
}
-func protoDeps(ctx BaseModuleContext, deps Deps, p *android.ProtoProperties, static bool) Deps {
+func protoDeps(ctx DepsContext, deps Deps, p *android.ProtoProperties, static bool) Deps {
var lib string
- switch String(p.Proto.Type) {
- case "full":
- if ctx.useSdk() {
- lib = "libprotobuf-cpp-full-ndk"
+ if String(p.Proto.Plugin) == "" {
+ switch String(p.Proto.Type) {
+ case "full":
+ if ctx.useSdk() {
+ lib = "libprotobuf-cpp-full-ndk"
+ static = true
+ } else {
+ lib = "libprotobuf-cpp-full"
+ }
+ case "lite", "":
+ if ctx.useSdk() {
+ lib = "libprotobuf-cpp-lite-ndk"
+ static = true
+ } else {
+ lib = "libprotobuf-cpp-lite"
+ }
+ case "nanopb-c":
+ lib = "libprotobuf-c-nano"
static = true
- } else {
- lib = "libprotobuf-cpp-full"
- }
- case "lite", "":
- if ctx.useSdk() {
- lib = "libprotobuf-cpp-lite-ndk"
+ case "nanopb-c-enable_malloc":
+ lib = "libprotobuf-c-nano-enable_malloc"
static = true
- } else {
- lib = "libprotobuf-cpp-lite"
+ case "nanopb-c-16bit":
+ lib = "libprotobuf-c-nano-16bit"
+ static = true
+ case "nanopb-c-enable_malloc-16bit":
+ lib = "libprotobuf-c-nano-enable_malloc-16bit"
+ static = true
+ case "nanopb-c-32bit":
+ lib = "libprotobuf-c-nano-32bit"
+ static = true
+ case "nanopb-c-enable_malloc-32bit":
+ lib = "libprotobuf-c-nano-enable_malloc-32bit"
+ static = true
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
}
- default:
- ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- String(p.Proto.Type))
- }
- if static {
- deps.StaticLibs = append(deps.StaticLibs, lib)
- deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, lib)
- } else {
- deps.SharedLibs = append(deps.SharedLibs, lib)
- deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, lib)
+ if static {
+ deps.StaticLibs = append(deps.StaticLibs, lib)
+ deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, lib)
+ } else {
+ deps.SharedLibs = append(deps.SharedLibs, lib)
+ deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, lib)
+ }
}
return deps
@@ -106,16 +116,41 @@
func protoFlags(ctx ModuleContext, flags Flags, p *android.ProtoProperties) Flags {
flags.CFlags = append(flags.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
- flags.ProtoRoot = android.ProtoCanonicalPathFromRoot(ctx, p)
- if flags.ProtoRoot {
- flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.ProtoSubDir(ctx).String())
+ flags.proto = android.GetProtoFlags(ctx, p)
+ if flags.proto.CanonicalPathFromRoot {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.SubDir.String())
}
- flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.ProtoDir(ctx).String())
+ flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.Dir.String())
- flags.protoFlags = android.ProtoFlags(ctx, p)
+ if String(p.Proto.Plugin) == "" {
+ var plugin string
- if proptools.String(p.Proto.Type) == "lite" {
- flags.protoOutParams = append(flags.protoOutParams, "lite")
+ switch String(p.Proto.Type) {
+ case "nanopb-c", "nanopb-c-enable_malloc", "nanopb-c-16bit", "nanopb-c-enable_malloc-16bit", "nanopb-c-32bit", "nanopb-c-enable_malloc-32bit":
+ flags.protoC = true
+ flags.protoOptionsFile = true
+ flags.proto.OutTypeFlag = "--nanopb_out"
+ plugin = "protoc-gen-nanopb"
+ case "full":
+ flags.proto.OutTypeFlag = "--cpp_out"
+ case "lite":
+ flags.proto.OutTypeFlag = "--cpp_out"
+ flags.proto.OutParams = append(flags.proto.OutParams, "lite")
+ case "":
+ // TODO(b/119714316): this should be equivalent to "lite" in
+ // order to match protoDeps, but some modules are depending on
+ // this behavior
+ flags.proto.OutTypeFlag = "--cpp_out"
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
+ }
+
+ if plugin != "" {
+ path := ctx.Config().HostToolPath(ctx, plugin)
+ flags.proto.Deps = append(flags.proto.Deps, path)
+ flags.proto.Flags = append(flags.proto.Flags, "--plugin="+path.String())
+ }
}
return flags
diff --git a/cc/proto_test.go b/cc/proto_test.go
new file mode 100644
index 0000000..a7fcef9
--- /dev/null
+++ b/cc/proto_test.go
@@ -0,0 +1,75 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "runtime"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestProto(t *testing.T) {
+ t.Run("simple", func(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["a.proto"],
+ }`)
+
+ proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Output("proto/a.pb.cc")
+
+ if cmd := proto.RuleParams.Command; !strings.Contains(cmd, "--cpp_out=") {
+ t.Errorf("expected '--cpp_out' in %q", cmd)
+ }
+ })
+
+ t.Run("plugin", func(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("TODO(b/129763458): cc_binary_host tests fail on mac when trying to exec xcrun")
+ }
+ ctx := testCc(t, `
+ cc_binary_host {
+ name: "protoc-gen-foobar",
+ stl: "none",
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["a.proto"],
+ proto: {
+ plugin: "foobar",
+ },
+ }`)
+
+ buildOS := android.BuildOs.String()
+
+ proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Output("proto/a.pb.cc")
+ foobar := ctx.ModuleForTests("protoc-gen-foobar", buildOS+"_x86_64")
+
+ cmd := proto.RuleParams.Command
+ if w := "--foobar_out="; !strings.Contains(cmd, w) {
+ t.Errorf("expected %q in %q", w, cmd)
+ }
+
+ foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().String()
+
+ if w := "--plugin=protoc-gen-foobar=" + foobarPath; !strings.Contains(cmd, w) {
+ t.Errorf("expected %q in %q", w, cmd)
+ }
+ })
+
+}
diff --git a/cc/relocation_packer.go b/cc/relocation_packer.go
deleted file mode 100644
index 5006623..0000000
--- a/cc/relocation_packer.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cc
-
-import (
- "runtime"
-
- "github.com/google/blueprint"
-
- "android/soong/android"
-)
-
-func init() {
- pctx.SourcePathVariable("relocationPackerCmd", "prebuilts/misc/${config.HostPrebuiltTag}/relocation_packer/relocation_packer")
-}
-
-var relocationPackerRule = pctx.AndroidStaticRule("packRelocations",
- blueprint.RuleParams{
- Command: "rm -f $out && cp $in $out && $relocationPackerCmd $out",
- CommandDeps: []string{"$relocationPackerCmd"},
- })
-
-type RelocationPackerProperties struct {
- Pack_relocations *bool `android:"arch_variant"`
-
- // This will be true even if we're embedded in Make, in which case
- // we'll defer to make to actually do the packing.
- PackingRelocations bool `blueprint:"mutated"`
-}
-
-type relocationPacker struct {
- Properties RelocationPackerProperties
-}
-
-func (p *relocationPacker) packingInit(ctx BaseModuleContext) {
- enabled := true
- // Relocation packer isn't available on Darwin yet
- if runtime.GOOS == "darwin" {
- enabled = false
- }
- if ctx.Target().Os != android.Android {
- enabled = false
- }
- if ctx.Config().Getenv("DISABLE_RELOCATION_PACKER") == "true" {
- enabled = false
- }
- if ctx.useSdk() {
- enabled = false
- }
- if p.Properties.Pack_relocations != nil &&
- *p.Properties.Pack_relocations == false {
- enabled = false
- }
-
- p.Properties.PackingRelocations = enabled
-}
-
-func (p *relocationPacker) needsPacking(ctx ModuleContext) bool {
- if ctx.Config().EmbeddedInMake() {
- return false
- }
- return p.Properties.PackingRelocations
-}
-
-func (p *relocationPacker) pack(ctx ModuleContext, in, out android.ModuleOutPath, flags builderFlags) {
- ctx.Build(pctx, android.BuildParams{
- Rule: relocationPackerRule,
- Description: "pack relocations",
- Output: out,
- Input: in,
- })
-}
diff --git a/cc/rs.go b/cc/rs.go
index 68ba54b..5421b92 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -16,13 +16,22 @@
import (
"android/soong/android"
+ "path/filepath"
+ "runtime"
"strings"
"github.com/google/blueprint"
)
func init() {
- pctx.HostBinToolVariable("rsCmd", "llvm-rs-cc")
+ pctx.VariableFunc("rsCmd", func(ctx android.PackageVarContext) string {
+ if ctx.Config().UnbundledBuild() {
+ // Use RenderScript prebuilts for unbundled builds but not PDK builds
+ return filepath.Join("prebuilts/sdk/tools", runtime.GOOS, "bin/llvm-rs-cc")
+ } else {
+ return pctx.HostBinToolPath(ctx, "llvm-rs-cc").String()
+ }
+ })
}
var rsCppCmdLine = strings.Replace(`
@@ -49,6 +58,11 @@
return android.PathForModuleGen(ctx, "rs", "ScriptC_"+fileName+".cpp")
}
+func rsGeneratedHFile(ctx android.ModuleContext, rsFile android.Path) android.WritablePath {
+ fileName := strings.TrimSuffix(rsFile.Base(), rsFile.Ext())
+ return android.PathForModuleGen(ctx, "rs", "ScriptC_"+fileName+".h")
+}
+
func rsGeneratedDepFile(ctx android.ModuleContext, rsFile android.Path) android.WritablePath {
fileName := strings.TrimSuffix(rsFile.Base(), rsFile.Ext())
return android.PathForModuleGen(ctx, "rs", fileName+".d")
@@ -56,18 +70,20 @@
func rsGenerateCpp(ctx android.ModuleContext, rsFiles android.Paths, rsFlags string) android.Paths {
stampFile := android.PathForModuleGen(ctx, "rs", "rs.stamp")
- depFiles := make(android.WritablePaths, len(rsFiles))
- cppFiles := make(android.WritablePaths, len(rsFiles))
- for i, rsFile := range rsFiles {
- depFiles[i] = rsGeneratedDepFile(ctx, rsFile)
- cppFiles[i] = rsGeneratedCppFile(ctx, rsFile)
+ depFiles := make(android.WritablePaths, 0, len(rsFiles))
+ genFiles := make(android.WritablePaths, 0, 2*len(rsFiles))
+ for _, rsFile := range rsFiles {
+ depFiles = append(depFiles, rsGeneratedDepFile(ctx, rsFile))
+ genFiles = append(genFiles,
+ rsGeneratedCppFile(ctx, rsFile),
+ rsGeneratedHFile(ctx, rsFile))
}
ctx.Build(pctx, android.BuildParams{
Rule: rsCpp,
Description: "llvm-rs-cc",
Output: stampFile,
- ImplicitOutputs: cppFiles,
+ ImplicitOutputs: genFiles,
Inputs: rsFiles,
Args: map[string]string{
"rsFlags": rsFlags,
diff --git a/cc/sabi.go b/cc/sabi.go
index 42b2f35..4a86499 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -71,17 +71,7 @@
// Assuming that the cflags which clang LibTooling tools cannot
// understand have not been converted to ninja variables yet.
flags.ToolingCFlags = filterOutWithPrefix(flags.CFlags, config.ClangLibToolingUnknownCflags)
-
- // RSClang does not support recent mcpu option likes exynos-m2.
- // So we need overriding mcpu option when we want to use it.
- mappedArch := map[string]string{
- "exynos-m2": "cortex-a53",
- "cortex-a55": "cortex-a53",
- "cortex-a75": "cortex-a57",
- }
- if arch, ok := mappedArch[ctx.Arch().CpuVariant]; ok {
- flags.ToolingCFlags = append(flags.ToolingCFlags, "-mcpu="+arch)
- }
+ flags.ToolingCppFlags = filterOutWithPrefix(flags.CppFlags, config.ClangLibToolingUnknownCflags)
return flags
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index de97035..b7a36a6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -16,11 +16,12 @@
import (
"fmt"
- "io"
"sort"
"strings"
"sync"
+ "github.com/google/blueprint"
+
"android/soong/android"
"android/soong/cc/config"
)
@@ -34,15 +35,30 @@
asanLdflags = []string{"-Wl,-u,__asan_preinit"}
asanLibs = []string{"libasan"}
+ // TODO(pcc): Stop passing -hwasan-allow-ifunc here once it has been made
+ // the default.
+ hwasanCflags = []string{"-fno-omit-frame-pointer", "-Wno-frame-larger-than=",
+ "-mllvm", "-hwasan-create-frame-descriptions=0",
+ "-mllvm", "-hwasan-allow-ifunc",
+ "-fsanitize-hwaddress-abi=platform"}
+
cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
+ // -flto and -fvisibility are required by clang when -fsanitize=cfi is
+ // used, but have no effect on assembly files
+ cfiAsflags = []string{"-flto", "-fvisibility=default"}
cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
"-Wl,-plugin-opt,O1"}
- cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
- cfiStaticLibsMutex sync.Mutex
+ cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
+ cfiStaticLibsMutex sync.Mutex
+ hwasanStaticLibsMutex sync.Mutex
- intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
- minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer", "-fno-sanitize-recover=integer"}
+ intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
+
+ minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
+ "-fno-sanitize-recover=integer,undefined"}
+ hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
+ "export_memory_stats=0", "max_malloc_fill_size=0"}
)
type sanitizerType int
@@ -57,21 +73,48 @@
const (
asan sanitizerType = iota + 1
+ hwasan
tsan
intOverflow
cfi
+ scs
)
-func (t sanitizerType) String() string {
+// Name of the sanitizer variation for this sanitizer type
+func (t sanitizerType) variationName() string {
switch t {
case asan:
return "asan"
+ case hwasan:
+ return "hwasan"
case tsan:
return "tsan"
case intOverflow:
return "intOverflow"
case cfi:
return "cfi"
+ case scs:
+ return "scs"
+ default:
+ panic(fmt.Errorf("unknown sanitizerType %d", t))
+ }
+}
+
+// This is the sanitizer names in SANITIZE_[TARGET|HOST]
+func (t sanitizerType) name() string {
+ switch t {
+ case asan:
+ return "address"
+ case hwasan:
+ return "hwaddress"
+ case tsan:
+ return "thread"
+ case intOverflow:
+ return "integer_overflow"
+ case cfi:
+ return "cfi"
+ case scs:
+ return "shadow-call-stack"
default:
panic(fmt.Errorf("unknown sanitizerType %d", t))
}
@@ -83,8 +126,9 @@
Never *bool `android:"arch_variant"`
// main sanitizers
- Address *bool `android:"arch_variant"`
- Thread *bool `android:"arch_variant"`
+ Address *bool `android:"arch_variant"`
+ Thread *bool `android:"arch_variant"`
+ Hwaddress *bool `android:"arch_variant"`
// local sanitizers
Undefined *bool `android:"arch_variant"`
@@ -94,6 +138,8 @@
Safestack *bool `android:"arch_variant"`
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
+ Scudo *bool `android:"arch_variant"`
+ Scs *bool `android:"arch_variant"`
// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
// Replaces abort() on error with a human-readable error message.
@@ -103,6 +149,7 @@
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
Misc_undefined []string `android:"arch_variant"`
+ No_recover []string
}
// value to pass to -fsanitize-recover=
@@ -112,21 +159,22 @@
Blacklist *string
} `android:"arch_variant"`
- SanitizerEnabled bool `blueprint:"mutated"`
- SanitizeDep bool `blueprint:"mutated"`
- MinimalRuntimeDep bool `blueprint:"mutated"`
- InSanitizerDir bool `blueprint:"mutated"`
+ SanitizerEnabled bool `blueprint:"mutated"`
+ SanitizeDep bool `blueprint:"mutated"`
+ MinimalRuntimeDep bool `blueprint:"mutated"`
+ UbsanRuntimeDep bool `blueprint:"mutated"`
+ InSanitizerDir bool `blueprint:"mutated"`
+ Sanitizers []string `blueprint:"mutated"`
+ DiagSanitizers []string `blueprint:"mutated"`
}
type sanitize struct {
Properties SanitizeProperties
-
- runtimeLibrary string
- androidMkRuntimeLibrary string
}
func init() {
android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
+ android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
}
func (sanitize *sanitize) props() []interface{} {
@@ -141,6 +189,11 @@
s.Never = BoolPtr(true)
}
+ // Sanitizers do not work on Fuchsia yet.
+ if ctx.Fuchsia() {
+ s.Never = BoolPtr(true)
+ }
+
// Never always wins.
if Bool(s.Never) {
return
@@ -149,15 +202,15 @@
var globalSanitizers []string
var globalSanitizersDiag []string
- if ctx.clang() {
- if ctx.Host() {
+ if ctx.Host() {
+ if !ctx.Windows() {
globalSanitizers = ctx.Config().SanitizeHost()
- } else {
- arches := ctx.Config().SanitizeDeviceArch()
- if len(arches) == 0 || inList(ctx.Arch().ArchType.Name, arches) {
- globalSanitizers = ctx.Config().SanitizeDevice()
- globalSanitizersDiag = ctx.Config().SanitizeDeviceDiag()
- }
+ }
+ } else {
+ arches := ctx.Config().SanitizeDeviceArch()
+ if len(arches) == 0 || inList(ctx.Arch().ArchType.Name, arches) {
+ globalSanitizers = ctx.Config().SanitizeDevice()
+ globalSanitizersDiag = ctx.Config().SanitizeDeviceDiag()
}
}
@@ -199,18 +252,28 @@
}
}
+ // Global integer_overflow builds do not support static libraries.
if found, globalSanitizers = removeFromList("integer_overflow", globalSanitizers); found && s.Integer_overflow == nil {
- if !ctx.Config().IntegerOverflowDisabledForPath(ctx.ModuleDir()) {
+ if !ctx.Config().IntegerOverflowDisabledForPath(ctx.ModuleDir()) && !ctx.static() {
s.Integer_overflow = boolPtr(true)
}
}
+ if found, globalSanitizers = removeFromList("scudo", globalSanitizers); found && s.Scudo == nil {
+ s.Scudo = boolPtr(true)
+ }
+
+ if found, globalSanitizers = removeFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
+ s.Hwaddress = boolPtr(true)
+ }
+
if len(globalSanitizers) > 0 {
ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
}
+ // Global integer_overflow builds do not support static library diagnostics.
if found, globalSanitizersDiag = removeFromList("integer_overflow", globalSanitizersDiag); found &&
- s.Diag.Integer_overflow == nil && Bool(s.Integer_overflow) {
+ s.Diag.Integer_overflow == nil && Bool(s.Integer_overflow) && !ctx.static() {
s.Diag.Integer_overflow = boolPtr(true)
}
@@ -244,16 +307,30 @@
s.Diag.Cfi = nil
}
+ // HWASan requires AArch64 hardware feature (top-byte-ignore).
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Hwaddress = nil
+ }
+
+ // SCS is only implemented on AArch64.
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Scs = nil
+ }
+
// Also disable CFI if ASAN is enabled.
- if Bool(s.Address) {
+ if Bool(s.Address) || Bool(s.Hwaddress) {
s.Cfi = nil
s.Diag.Cfi = nil
}
- // Also disable CFI for host builds.
+ // Disable sanitizers that depend on the UBSan runtime for host builds.
if ctx.Host() {
s.Cfi = nil
s.Diag.Cfi = nil
+ s.Misc_undefined = nil
+ s.Undefined = nil
+ s.All_undefined = nil
+ s.Integer_overflow = nil
}
// Also disable CFI for VNDK variants of components
@@ -262,6 +339,12 @@
s.Diag.Cfi = nil
}
+ // HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
+ // Keep libc instrumented so that recovery can run hwasan-instrumented code if necessary.
+ if ctx.inRecovery() && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
+ s.Hwaddress = nil
+ }
+
if ctx.staticBinary() {
s.Address = nil
s.Coverage = nil
@@ -280,10 +363,21 @@
}
if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
- Bool(s.Coverage) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0) {
+ Bool(s.Coverage) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
+ Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs)) {
sanitize.Properties.SanitizerEnabled = true
}
+ // Disable Scudo if ASan or TSan is enabled, or if it's disabled globally.
+ if Bool(s.Address) || Bool(s.Thread) || Bool(s.Hwaddress) || ctx.Config().DisableScudo() {
+ s.Scudo = nil
+ }
+
+ if Bool(s.Hwaddress) {
+ s.Address = nil
+ s.Thread = nil
+ }
+
if Bool(s.Coverage) {
if !Bool(s.Address) {
ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
@@ -299,12 +393,33 @@
if ctx.Device() {
if Bool(sanitize.Properties.Sanitize.Address) {
deps.StaticLibs = append(deps.StaticLibs, asanLibs...)
+ // Compiling asan and having libc_scudo in the same
+ // executable will cause the executable to crash.
+ // Remove libc_scudo since it is only used to override
+ // allocation functions which asan already overrides.
+ _, deps.SharedLibs = removeFromList("libc_scudo", deps.SharedLibs)
}
}
return deps
}
+func toDisableImplicitIntegerChange(flags []string) bool {
+ // Returns true if any flag is fsanitize*integer, and there is
+ // no explicit flag about sanitize=implicit-integer-sign-change.
+ for _, f := range flags {
+ if strings.Contains(f, "sanitize=implicit-integer-sign-change") {
+ return false
+ }
+ }
+ for _, f := range flags {
+ if strings.HasPrefix(f, "-fsanitize") && strings.Contains(f, "integer") {
+ return true
+ }
+ }
+ return false
+}
+
func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
@@ -313,54 +428,10 @@
flags.LdFlags = append(flags.LdFlags, minimalRuntimePath)
flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
}
- if !sanitize.Properties.SanitizerEnabled {
+ if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
return flags
}
- if !ctx.clang() {
- ctx.ModuleErrorf("Use of sanitizers requires clang")
- }
-
- var sanitizers []string
- var diagSanitizers []string
-
- if Bool(sanitize.Properties.Sanitize.All_undefined) {
- sanitizers = append(sanitizers, "undefined")
- } else {
- if Bool(sanitize.Properties.Sanitize.Undefined) {
- sanitizers = append(sanitizers,
- "bool",
- "integer-divide-by-zero",
- "return",
- "returns-nonnull-attribute",
- "shift-exponent",
- "unreachable",
- "vla-bound",
- // TODO(danalbert): The following checks currently have compiler performance issues.
- //"alignment",
- //"bounds",
- //"enum",
- //"float-cast-overflow",
- //"float-divide-by-zero",
- //"nonnull-attribute",
- //"null",
- //"shift-base",
- //"signed-integer-overflow",
- // TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.
- // https://llvm.org/PR19302
- // http://reviews.llvm.org/D6974
- // "object-size",
- )
- }
- sanitizers = append(sanitizers, sanitize.Properties.Sanitize.Misc_undefined...)
- }
-
- if Bool(sanitize.Properties.Sanitize.Diag.Undefined) {
- diagSanitizers = append(diagSanitizers, "undefined")
- }
-
- diagSanitizers = append(diagSanitizers, sanitize.Properties.Sanitize.Diag.Misc_undefined...)
-
if Bool(sanitize.Properties.Sanitize.Address) {
if ctx.Arch().ArchType == android.Arm {
// Frame pointer based unwinder in ASan requires ARM frame setup.
@@ -376,52 +447,40 @@
flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
} else {
flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
- flags.DynamicLinker = "/system/bin/linker_asan"
+ if ctx.bootstrap() {
+ flags.DynamicLinker = "/system/bin/bootstrap/linker_asan"
+ } else {
+ flags.DynamicLinker = "/system/bin/linker_asan"
+ }
if flags.Toolchain.Is64Bit() {
flags.DynamicLinker += "64"
}
}
- sanitizers = append(sanitizers, "address")
- diagSanitizers = append(diagSanitizers, "address")
}
- if Bool(sanitize.Properties.Sanitize.Thread) {
- sanitizers = append(sanitizers, "thread")
+ if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ flags.CFlags = append(flags.CFlags, hwasanCflags...)
}
if Bool(sanitize.Properties.Sanitize.Coverage) {
flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp")
}
- if Bool(sanitize.Properties.Sanitize.Safestack) {
- sanitizers = append(sanitizers, "safe-stack")
- }
-
if Bool(sanitize.Properties.Sanitize.Cfi) {
if ctx.Arch().ArchType == android.Arm {
// __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up
// to do this on a function basis, so force Thumb on the entire module.
flags.RequiredInstructionSet = "thumb"
}
- sanitizers = append(sanitizers, "cfi")
flags.CFlags = append(flags.CFlags, cfiCflags...)
+ flags.AsFlags = append(flags.AsFlags, cfiAsflags...)
// Only append the default visibility flag if -fvisibility has not already been set
// to hidden.
if !inList("-fvisibility=hidden", flags.CFlags) {
flags.CFlags = append(flags.CFlags, "-fvisibility=default")
}
flags.LdFlags = append(flags.LdFlags, cfiLdflags...)
- if ctx.Device() {
- // Work around a bug in Clang. The CFI sanitizer requires LTO, and when
- // LTO is enabled, the Clang driver fails to enable emutls for Android.
- // See b/72706604 or https://github.com/android-ndk/ndk/issues/498.
- flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-emulated-tls")
- }
- flags.ArGoldPlugin = true
- if Bool(sanitize.Properties.Sanitize.Diag.Cfi) {
- diagSanitizers = append(diagSanitizers, "cfi")
- }
if ctx.staticBinary() {
_, flags.CFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.CFlags)
@@ -430,21 +489,14 @@
}
if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
- if !ctx.static() {
- sanitizers = append(sanitizers, "unsigned-integer-overflow")
- sanitizers = append(sanitizers, "signed-integer-overflow")
- flags.CFlags = append(flags.CFlags, intOverflowCflags...)
- if Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) {
- diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow")
- diagSanitizers = append(diagSanitizers, "signed-integer-overflow")
- }
- }
+ flags.CFlags = append(flags.CFlags, intOverflowCflags...)
}
- if len(sanitizers) > 0 {
- sanitizeArg := "-fsanitize=" + strings.Join(sanitizers, ",")
+ if len(sanitize.Properties.Sanitizers) > 0 {
+ sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
flags.CFlags = append(flags.CFlags, sanitizeArg)
+ flags.AsFlags = append(flags.AsFlags, sanitizeArg)
if ctx.Host() {
flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover=all")
flags.LdFlags = append(flags.LdFlags, sanitizeArg)
@@ -460,10 +512,14 @@
flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
}
}
+ // http://b/119329758, Android core does not boot up with this sanitizer yet.
+ if toDisableImplicitIntegerChange(flags.CFlags) {
+ flags.CFlags = append(flags.CFlags, "-fno-sanitize=implicit-integer-sign-change")
+ }
}
- if len(diagSanitizers) > 0 {
- flags.CFlags = append(flags.CFlags, "-fno-sanitize-trap="+strings.Join(diagSanitizers, ","))
+ if len(sanitize.Properties.DiagSanitizers) > 0 {
+ flags.CFlags = append(flags.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
}
// FIXME: enable RTTI if diag + (cfi or vptr)
@@ -472,28 +528,9 @@
strings.Join(sanitize.Properties.Sanitize.Recover, ","))
}
- // Link a runtime library if needed.
- runtimeLibrary := ""
- if Bool(sanitize.Properties.Sanitize.Address) {
- runtimeLibrary = config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
- } else if Bool(sanitize.Properties.Sanitize.Thread) {
- runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(ctx.toolchain())
- } else if len(diagSanitizers) > 0 {
- runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(ctx.toolchain())
- }
-
- if runtimeLibrary != "" {
- // ASan runtime library must be the first in the link order.
- flags.libFlags = append([]string{
- "${config.ClangAsanLibDir}/" + runtimeLibrary + ctx.toolchain().ShlibSuffix(),
- }, flags.libFlags...)
- sanitize.runtimeLibrary = runtimeLibrary
-
- // When linking against VNDK, use the vendor variant of the runtime lib
- sanitize.androidMkRuntimeLibrary = sanitize.runtimeLibrary
- if ctx.useVndk() {
- sanitize.androidMkRuntimeLibrary = sanitize.runtimeLibrary + vendorSuffix
- }
+ if sanitize.Properties.Sanitize.Diag.No_recover != nil {
+ flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover="+
+ strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ","))
}
blacklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blacklist)
@@ -506,17 +543,17 @@
}
func (sanitize *sanitize) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if sanitize.androidMkRuntimeLibrary != "" {
- fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES += "+sanitize.androidMkRuntimeLibrary)
- }
- })
-
// Add a suffix for CFI-enabled static libraries to allow surfacing both to make without a
// name conflict.
if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Cfi) {
ret.SubName += ".cfi"
}
+ if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ ret.SubName += ".hwasan"
+ }
+ if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Scs) {
+ ret.SubName += ".scs"
+ }
}
func (sanitize *sanitize) inSanitizerDir() bool {
@@ -527,12 +564,16 @@
switch t {
case asan:
return sanitize.Properties.Sanitize.Address
+ case hwasan:
+ return sanitize.Properties.Sanitize.Hwaddress
case tsan:
return sanitize.Properties.Sanitize.Thread
case intOverflow:
return sanitize.Properties.Sanitize.Integer_overflow
case cfi:
return sanitize.Properties.Sanitize.Cfi
+ case scs:
+ return sanitize.Properties.Sanitize.Scs
default:
panic(fmt.Errorf("unknown sanitizerType %d", t))
}
@@ -540,12 +581,15 @@
func (sanitize *sanitize) isUnsanitizedVariant() bool {
return !sanitize.isSanitizerEnabled(asan) &&
+ !sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
- !sanitize.isSanitizerEnabled(cfi)
+ !sanitize.isSanitizerEnabled(cfi) &&
+ !sanitize.isSanitizerEnabled(scs)
}
func (sanitize *sanitize) isVariantOnProductionDevice() bool {
return !sanitize.isSanitizerEnabled(asan) &&
+ !sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan)
}
@@ -556,13 +600,16 @@
if !b {
sanitize.Properties.Sanitize.Coverage = nil
}
+ case hwasan:
+ sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
case tsan:
sanitize.Properties.Sanitize.Thread = boolPtr(b)
case intOverflow:
sanitize.Properties.Sanitize.Integer_overflow = boolPtr(b)
case cfi:
sanitize.Properties.Sanitize.Cfi = boolPtr(b)
- sanitize.Properties.Sanitize.Diag.Cfi = boolPtr(b)
+ case scs:
+ sanitize.Properties.Sanitize.Scs = boolPtr(b)
default:
panic(fmt.Errorf("unknown sanitizerType %d", t))
}
@@ -596,54 +643,238 @@
return sanitizerVal != nil && *sanitizerVal == true
}
-// Propagate asan requirements down from binaries
+func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+ t, ok := tag.(dependencyTag)
+ return ok && t.library || t == reuseObjTag
+}
+
+// Propagate sanitizer requirements down from binaries
func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
return func(mctx android.TopDownMutatorContext) {
if c, ok := mctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
- mctx.VisitDepsDepthFirst(func(module android.Module) {
- if d, ok := module.(*Module); ok && d.sanitize != nil &&
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
+ return false
+ }
+ if d, ok := child.(*Module); ok && d.sanitize != nil &&
!Bool(d.sanitize.Properties.Sanitize.Never) &&
!d.sanitize.isSanitizerExplicitlyDisabled(t) {
- if (t == cfi && d.static()) || t != cfi {
+ if t == cfi || t == hwasan || t == scs {
+ if d.static() {
+ d.sanitize.Properties.SanitizeDep = true
+ }
+ } else {
d.sanitize.Properties.SanitizeDep = true
}
}
+ return true
+ })
+ } else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
+ // If an APEX module includes a lib which is enabled for a sanitizer T, then
+ // the APEX module is also enabled for the same sanitizer type.
+ mctx.VisitDirectDeps(func(child android.Module) {
+ if c, ok := child.(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
+ sanitizeable.EnableSanitizer(t.name())
+ }
})
}
}
}
// Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
-func minimalRuntimeDepsMutator() func(android.TopDownMutatorContext) {
- return func(mctx android.TopDownMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
- mctx.VisitDepsDepthFirst(func(module android.Module) {
- if d, ok := module.(*Module); ok && d.static() && d.sanitize != nil {
+func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+ if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
+ return false
+ }
+ if d, ok := child.(*Module); ok && d.static() && d.sanitize != nil {
- // If a static dependency will be built with the minimal runtime,
+ if enableMinimalRuntime(d.sanitize) {
+ // If a static dependency is built with the minimal runtime,
// make sure we include the ubsan minimal runtime.
- if enableMinimalRuntime(d.sanitize) {
- c.sanitize.Properties.MinimalRuntimeDep = true
- }
+ c.sanitize.Properties.MinimalRuntimeDep = true
+ } else if Bool(d.sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
+ len(d.sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0 {
+ // If a static dependency runs with full ubsan diagnostics,
+ // make sure we include the ubsan runtime.
+ c.sanitize.Properties.UbsanRuntimeDep = true
}
- })
+ }
+ return true
+ })
+ }
+}
+
+// Add the dependency to the runtime library for each of the sanitizer variants
+func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
+ if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+ if !c.Enabled() {
+ return
+ }
+ var sanitizers []string
+ var diagSanitizers []string
+
+ if Bool(c.sanitize.Properties.Sanitize.All_undefined) {
+ sanitizers = append(sanitizers, "undefined")
+ } else {
+ if Bool(c.sanitize.Properties.Sanitize.Undefined) {
+ sanitizers = append(sanitizers,
+ "bool",
+ "integer-divide-by-zero",
+ "return",
+ "returns-nonnull-attribute",
+ "shift-exponent",
+ "unreachable",
+ "vla-bound",
+ // TODO(danalbert): The following checks currently have compiler performance issues.
+ //"alignment",
+ //"bounds",
+ //"enum",
+ //"float-cast-overflow",
+ //"float-divide-by-zero",
+ //"nonnull-attribute",
+ //"null",
+ //"shift-base",
+ //"signed-integer-overflow",
+ // TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.
+ // https://llvm.org/PR19302
+ // http://reviews.llvm.org/D6974
+ // "object-size",
+ )
+ }
+ sanitizers = append(sanitizers, c.sanitize.Properties.Sanitize.Misc_undefined...)
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Diag.Undefined) {
+ diagSanitizers = append(diagSanitizers, "undefined")
+ }
+
+ diagSanitizers = append(diagSanitizers, c.sanitize.Properties.Sanitize.Diag.Misc_undefined...)
+
+ if Bool(c.sanitize.Properties.Sanitize.Address) {
+ sanitizers = append(sanitizers, "address")
+ diagSanitizers = append(diagSanitizers, "address")
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
+ sanitizers = append(sanitizers, "hwaddress")
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Thread) {
+ sanitizers = append(sanitizers, "thread")
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Safestack) {
+ sanitizers = append(sanitizers, "safe-stack")
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Cfi) {
+ sanitizers = append(sanitizers, "cfi")
+
+ if Bool(c.sanitize.Properties.Sanitize.Diag.Cfi) {
+ diagSanitizers = append(diagSanitizers, "cfi")
+ }
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Integer_overflow) {
+ sanitizers = append(sanitizers, "unsigned-integer-overflow")
+ sanitizers = append(sanitizers, "signed-integer-overflow")
+ if Bool(c.sanitize.Properties.Sanitize.Diag.Integer_overflow) {
+ diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow")
+ diagSanitizers = append(diagSanitizers, "signed-integer-overflow")
+ }
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Scudo) {
+ sanitizers = append(sanitizers, "scudo")
+ }
+
+ if Bool(c.sanitize.Properties.Sanitize.Scs) {
+ sanitizers = append(sanitizers, "shadow-call-stack")
+ }
+
+ // Save the list of sanitizers. These will be used again when generating
+ // the build rules (for Cflags, etc.)
+ c.sanitize.Properties.Sanitizers = sanitizers
+ c.sanitize.Properties.DiagSanitizers = diagSanitizers
+
+ // Determine the runtime library required
+ runtimeLibrary := ""
+ toolchain := c.toolchain(mctx)
+ if Bool(c.sanitize.Properties.Sanitize.Address) {
+ runtimeLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
+ } else if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
+ if c.staticBinary() {
+ runtimeLibrary = config.HWAddressSanitizerStaticLibrary(toolchain)
+ } else {
+ runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
+ }
+ } else if Bool(c.sanitize.Properties.Sanitize.Thread) {
+ runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain)
+ } else if Bool(c.sanitize.Properties.Sanitize.Scudo) {
+ if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
+ runtimeLibrary = config.ScudoMinimalRuntimeLibrary(toolchain)
+ } else {
+ runtimeLibrary = config.ScudoRuntimeLibrary(toolchain)
+ }
+ } else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep {
+ runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
+ }
+
+ if mctx.Device() && runtimeLibrary != "" {
+ if inList(runtimeLibrary, llndkLibraries) && !c.static() && c.useVndk() {
+ runtimeLibrary = runtimeLibrary + llndkLibrarySuffix
+ }
+
+ // Adding dependency to the runtime library. We are using *FarVariation*
+ // because the runtime libraries themselves are not mutated by sanitizer
+ // mutators and thus don't have sanitizer variants whereas this module
+ // has been already mutated.
+ //
+ // Note that by adding dependency with {static|shared}DepTag, the lib is
+ // added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
+ if c.staticBinary() {
+ // static executable gets static runtime libs
+ mctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ {Mutator: "image", Variation: c.imageVariation()},
+ {Mutator: "arch", Variation: mctx.Target().String()},
+ }, staticDepTag, runtimeLibrary)
+ } else if !c.static() {
+ // dynamic executable and shared libs get shared runtime libs
+ mctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ {Mutator: "image", Variation: c.imageVariation()},
+ {Mutator: "arch", Variation: mctx.Target().String()},
+ }, earlySharedDepTag, runtimeLibrary)
+ }
+ // static lib does not have dependency to the runtime library. The
+ // dependency will be added to the executables or shared libs using
+ // the static lib.
}
}
}
+type Sanitizeable interface {
+ android.Module
+ IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool
+ EnableSanitizer(sanitizerName string)
+}
+
// Create sanitized variants for modules that need them
func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
if c.isDependencyRoot() && c.sanitize.isSanitizerEnabled(t) {
- modules := mctx.CreateVariations(t.String())
+ modules := mctx.CreateVariations(t.variationName())
modules[0].(*Module).sanitize.SetSanitizer(t, true)
} else if c.sanitize.isSanitizerEnabled(t) || c.sanitize.Properties.SanitizeDep {
// Save original sanitizer status before we assign values to variant
// 0 as that overwrites the original.
isSanitizerEnabled := c.sanitize.isSanitizerEnabled(t)
- modules := mctx.CreateVariations("", t.String())
+ modules := mctx.CreateVariations("", t.variationName())
modules[0].(*Module).sanitize.SetSanitizer(t, false)
modules[1].(*Module).sanitize.SetSanitizer(t, true)
@@ -688,21 +919,84 @@
modules[1].(*Module).Properties.PreventInstall = true
modules[1].(*Module).Properties.HideFromMake = true
}
+ } else if t == scs {
+ // We don't currently link any static libraries built with make into
+ // libraries built with SCS, so we don't need logic for propagating
+ // SCSness of dependencies into make.
+ if !c.static() {
+ if isSanitizerEnabled {
+ modules[0].(*Module).Properties.PreventInstall = true
+ modules[0].(*Module).Properties.HideFromMake = true
+ } else {
+ modules[1].(*Module).Properties.PreventInstall = true
+ modules[1].(*Module).Properties.HideFromMake = true
+ }
+ }
+ } else if t == hwasan {
+ if mctx.Device() {
+ // CFI and HWASAN are currently mutually exclusive so disable
+ // CFI if this is an HWASAN variant.
+ modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
+ }
+
+ if c.static() {
+ if c.useVndk() {
+ hwasanVendorStaticLibs := hwasanVendorStaticLibs(mctx.Config())
+ hwasanStaticLibsMutex.Lock()
+ *hwasanVendorStaticLibs = append(*hwasanVendorStaticLibs, c.Name())
+ hwasanStaticLibsMutex.Unlock()
+ } else {
+ hwasanStaticLibs := hwasanStaticLibs(mctx.Config())
+ hwasanStaticLibsMutex.Lock()
+ *hwasanStaticLibs = append(*hwasanStaticLibs, c.Name())
+ hwasanStaticLibsMutex.Unlock()
+ }
+ } else {
+ if isSanitizerEnabled {
+ modules[0].(*Module).Properties.PreventInstall = true
+ modules[0].(*Module).Properties.HideFromMake = true
+ } else {
+ modules[1].(*Module).Properties.PreventInstall = true
+ modules[1].(*Module).Properties.HideFromMake = true
+ }
+ }
}
}
c.sanitize.Properties.SanitizeDep = false
+ } else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
+ // APEX modules fall here
+ mctx.CreateVariations(t.variationName())
}
}
}
+var cfiStaticLibsKey = android.NewOnceKey("cfiStaticLibs")
+
func cfiStaticLibs(config android.Config) *[]string {
- return config.Once("cfiStaticLibs", func() interface{} {
+ return config.Once(cfiStaticLibsKey, func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
+var hwasanStaticLibsKey = android.NewOnceKey("hwasanStaticLibs")
+
+func hwasanStaticLibs(config android.Config) *[]string {
+ return config.Once(hwasanStaticLibsKey, func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
+var hwasanVendorStaticLibsKey = android.NewOnceKey("hwasanVendorStaticLibs")
+
+func hwasanVendorStaticLibs(config android.Config) *[]string {
+ return config.Once(hwasanVendorStaticLibsKey, func() interface{} {
return &[]string{}
}).(*[]string)
}
func enableMinimalRuntime(sanitize *sanitize) bool {
if !Bool(sanitize.Properties.Sanitize.Address) &&
+ !Bool(sanitize.Properties.Sanitize.Hwaddress) &&
(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
len(sanitize.Properties.Sanitize.Misc_undefined) > 0) &&
!(Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
@@ -718,3 +1012,13 @@
sort.Strings(*cfiStaticLibs)
ctx.Strict("SOONG_CFI_STATIC_LIBRARIES", strings.Join(*cfiStaticLibs, " "))
}
+
+func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
+ hwasanStaticLibs := hwasanStaticLibs(ctx.Config())
+ sort.Strings(*hwasanStaticLibs)
+ ctx.Strict("SOONG_HWASAN_STATIC_LIBRARIES", strings.Join(*hwasanStaticLibs, " "))
+
+ hwasanVendorStaticLibs := hwasanVendorStaticLibs(ctx.Config())
+ sort.Strings(*hwasanVendorStaticLibs)
+ ctx.Strict("SOONG_HWASAN_VENDOR_STATIC_LIBRARIES", strings.Join(*hwasanVendorStaticLibs, " "))
+}
diff --git a/cc/stl.go b/cc/stl.go
index 2da6471..1a5dd79 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -17,20 +17,27 @@
import (
"android/soong/android"
"fmt"
+ "strconv"
)
-func getNdkStlFamily(ctx android.ModuleContext, m *Module) string {
+func getNdkStlFamily(m *Module) string {
+ family, _ := getNdkStlFamilyAndLinkType(m)
+ return family
+}
+
+func getNdkStlFamilyAndLinkType(m *Module) (string, string) {
stl := m.stl.Properties.SelectedStl
switch stl {
- case "ndk_libc++_shared", "ndk_libc++_static":
- return "libc++"
+ case "ndk_libc++_shared":
+ return "libc++", "shared"
+ case "ndk_libc++_static":
+ return "libc++", "static"
case "ndk_system":
- return "system"
+ return "system", "shared"
case "":
- return "none"
+ return "none", "none"
default:
- ctx.ModuleErrorf("stl: %q is not a valid STL", stl)
- return ""
+ panic(fmt.Errorf("stl: %q is not a valid STL", stl))
}
}
@@ -59,7 +66,7 @@
}
if ctx.useSdk() && ctx.Device() {
switch s {
- case "":
+ case "", "system":
return "ndk_system"
case "c++_shared", "c++_static":
return "ndk_lib" + s
@@ -75,15 +82,35 @@
}
} else if ctx.Windows() {
switch s {
- case "libc++", "libc++_static", "libstdc++", "":
- // libc++ is not supported on mingw
- return "libstdc++"
+ case "libc++", "libc++_static", "":
+ // Only use static libc++ for Windows.
+ return "libc++_static"
case "none":
return ""
default:
ctx.ModuleErrorf("stl: %q is not a supported STL for windows", s)
return ""
}
+ } else if ctx.Fuchsia() {
+ switch s {
+ case "c++_static":
+ return "libc++_static"
+ case "c++_shared":
+ return "libc++"
+ case "libc++", "libc++_static":
+ return s
+ case "none":
+ return ""
+ case "":
+ if ctx.static() {
+ return "libc++_static"
+ } else {
+ return "libc++"
+ }
+ default:
+ ctx.ModuleErrorf("stl: %q is not a supported STL on Fuchsia", s)
+ return ""
+ }
} else {
switch s {
case "libc++", "libc++_static":
@@ -104,6 +131,26 @@
}()
}
+func needsLibAndroidSupport(ctx BaseModuleContext) bool {
+ versionStr, err := normalizeNdkApiLevel(ctx, ctx.sdkVersion(), ctx.Arch())
+ if err != nil {
+ ctx.PropertyErrorf("sdk_version", err.Error())
+ }
+
+ if versionStr == "current" {
+ return false
+ }
+
+ version, err := strconv.Atoi(versionStr)
+ if err != nil {
+ panic(fmt.Sprintf(
+ "invalid API level returned from normalizeNdkApiLevel: %q",
+ versionStr))
+ }
+
+ return version < 21
+}
+
func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps {
switch stl.Properties.SelectedStl {
case "libstdc++":
@@ -129,10 +176,18 @@
// The system STL doesn't have a prebuilt (it uses the system's libstdc++), but it does have
// its own includes. The includes are handled in CCBase.Flags().
deps.SharedLibs = append([]string{"libstdc++"}, deps.SharedLibs...)
- case "ndk_libc++_shared":
- deps.SharedLibs = append(deps.SharedLibs, stl.Properties.SelectedStl)
- case "ndk_libc++_static":
- deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl)
+ case "ndk_libc++_shared", "ndk_libc++_static":
+ if stl.Properties.SelectedStl == "ndk_libc++_shared" {
+ deps.SharedLibs = append(deps.SharedLibs, stl.Properties.SelectedStl)
+ } else {
+ deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi")
+ }
+ if needsLibAndroidSupport(ctx) {
+ deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
+ }
+ if ctx.Arch().ArchType == android.Arm {
+ deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
+ }
default:
panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
}
@@ -165,6 +220,20 @@
} else {
flags.LdFlags = append(flags.LdFlags, hostDynamicGccLibs[ctx.Os()]...)
}
+ if ctx.Windows() {
+ // Use SjLj exceptions for 32-bit. libgcc_eh implements SjLj
+ // exception model for 32-bit.
+ if ctx.Arch().ArchType == android.X86 {
+ flags.CppFlags = append(flags.CppFlags, "-fsjlj-exceptions")
+ }
+ flags.CppFlags = append(flags.CppFlags,
+ // Disable visiblity annotations since we're using static
+ // libc++.
+ "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+ "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+ // Use Win32 threads in libc++.
+ "-D_LIBCPP_HAS_THREAD_API_WIN32")
+ }
} else {
if ctx.Arch().ArchType == android.Arm {
flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
@@ -176,7 +245,10 @@
ndkSrcRoot := android.PathForSource(ctx, "prebuilts/ndk/current/sources/cxx-stl/system/include")
flags.CFlags = append(flags.CFlags, "-isystem "+ndkSrcRoot.String())
case "ndk_libc++_shared", "ndk_libc++_static":
- // Nothing.
+ if ctx.Arch().ArchType == android.Arm {
+ // Make sure the _Unwind_XXX symbols are not re-exported.
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind.a")
+ }
case "":
// None or error.
if !ctx.toolchain().Bionic() {
@@ -199,12 +271,13 @@
func init() {
hostDynamicGccLibs = map[android.OsType][]string{
- android.Linux: []string{"-lgcc_s", "-lgcc", "-lc", "-lgcc_s", "-lgcc"},
- android.Darwin: []string{"-lc", "-lSystem"},
- android.Windows: []string{"-lmsvcr110", "-lmingw32", "-lgcc", "-lmoldname",
- "-lmingwex", "-lmsvcrt", "-ladvapi32", "-lshell32", "-luser32",
- "-lkernel32", "-lmingw32", "-lgcc", "-lmoldname", "-lmingwex",
- "-lmsvcrt"},
+ android.Fuchsia: []string{"-lc", "-lunwind"},
+ android.Linux: []string{"-lgcc_s", "-lgcc", "-lc", "-lgcc_s", "-lgcc"},
+ android.Darwin: []string{"-lc", "-lSystem"},
+ android.Windows: []string{"-Wl,--start-group", "-lmingw32", "-lgcc", "-lgcc_eh",
+ "-lmoldname", "-lmingwex", "-lmsvcrt", "-lucrt", "-lpthread",
+ "-ladvapi32", "-lshell32", "-luser32", "-lkernel32", "-lpsapi",
+ "-Wl,--end-group"},
}
hostStaticGccLibs = map[android.OsType][]string{
android.Linux: []string{"-Wl,--start-group", "-lgcc", "-lgcc_eh", "-lc", "-Wl,--end-group"},
diff --git a/cc/strip.go b/cc/strip.go
index a7c2d4e..7122585 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -15,14 +15,19 @@
package cc
import (
+ "strings"
+
"android/soong/android"
)
type StripProperties struct {
Strip struct {
- None *bool
- Keep_symbols *bool
- }
+ None *bool `android:"arch_variant"`
+ All *bool `android:"arch_variant"`
+ Keep_symbols *bool `android:"arch_variant"`
+ Keep_symbols_list []string `android:"arch_variant"`
+ Use_gnu_strip *bool `android:"arch_variant"`
+ } `android:"arch_variant"`
}
type stripper struct {
@@ -30,17 +35,28 @@
}
func (stripper *stripper) needsStrip(ctx ModuleContext) bool {
- return !ctx.Config().EmbeddedInMake() && !Bool(stripper.StripProperties.Strip.None)
+ // TODO(ccross): enable host stripping when embedded in make? Make never had support for stripping host binaries.
+ return (!ctx.Config().EmbeddedInMake() || ctx.Device()) && !Bool(stripper.StripProperties.Strip.None)
}
-func (stripper *stripper) strip(ctx ModuleContext, in, out android.ModuleOutPath,
+func (stripper *stripper) strip(ctx ModuleContext, in android.Path, out android.ModuleOutPath,
flags builderFlags) {
if ctx.Darwin() {
TransformDarwinStrip(ctx, in, out)
} else {
- flags.stripKeepSymbols = Bool(stripper.StripProperties.Strip.Keep_symbols)
- // TODO(ccross): don't add gnu debuglink for user builds
- flags.stripAddGnuDebuglink = true
+ if Bool(stripper.StripProperties.Strip.Keep_symbols) {
+ flags.stripKeepSymbols = true
+ } else if len(stripper.StripProperties.Strip.Keep_symbols_list) > 0 {
+ flags.stripKeepSymbolsList = strings.Join(stripper.StripProperties.Strip.Keep_symbols_list, ",")
+ } else if !Bool(stripper.StripProperties.Strip.All) {
+ flags.stripKeepMiniDebugInfo = true
+ }
+ if Bool(stripper.StripProperties.Strip.Use_gnu_strip) {
+ flags.stripUseGnuStrip = true
+ }
+ if ctx.Config().Debuggable() && !flags.stripKeepMiniDebugInfo {
+ flags.stripAddGnuDebuglink = true
+ }
TransformStrip(ctx, in, out, flags)
}
}
diff --git a/cc/sysprop.go b/cc/sysprop.go
new file mode 100644
index 0000000..656f79f
--- /dev/null
+++ b/cc/sysprop.go
@@ -0,0 +1,47 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "sync"
+
+ "android/soong/android"
+)
+
+type syspropLibraryInterface interface {
+ CcModuleName() string
+}
+
+var (
+ syspropImplLibrariesKey = android.NewOnceKey("syspropImplLibirares")
+ syspropImplLibrariesLock sync.Mutex
+)
+
+func syspropImplLibraries(config android.Config) map[string]string {
+ return config.Once(syspropImplLibrariesKey, func() interface{} {
+ return make(map[string]string)
+ }).(map[string]string)
+}
+
+// gather list of sysprop libraries
+func SyspropMutator(mctx android.BottomUpMutatorContext) {
+ if m, ok := mctx.Module().(syspropLibraryInterface); ok {
+ syspropImplLibraries := syspropImplLibraries(mctx.Config())
+ syspropImplLibrariesLock.Lock()
+ defer syspropImplLibrariesLock.Unlock()
+
+ syspropImplLibraries[mctx.ModuleName()] = m.CcModuleName()
+ }
+}
diff --git a/cc/test.go b/cc/test.go
index b8e9cdb..c735fd9 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -16,15 +16,24 @@
import (
"path/filepath"
- "runtime"
"strings"
"android/soong/android"
+ "android/soong/tradefed"
)
type TestProperties struct {
// if set, build against the gtest library. Defaults to true.
Gtest *bool
+
+ // if set, use the isolated gtest runner. Defaults to false.
+ Isolated *bool
+}
+
+// Test option struct.
+type TestOptions struct {
+ // The UID that you want to run the test as on a device.
+ Run_test_as *string
}
type TestBinaryProperties struct {
@@ -39,11 +48,26 @@
// list of files or filegroup modules that provide data that should be installed alongside
// the test
- Data []string
+ Data []string `android:"path"`
// list of compatibility suites (for example "cts", "vts") that the module should be
// installed into.
Test_suites []string `android:"arch_variant"`
+
+ // the name of the test configuration (for example "AndroidTest.xml") that should be
+ // installed with the module.
+ Test_config *string `android:"path,arch_variant"`
+
+ // the name of the test configuration template (for example "AndroidTestTemplate.xml") that
+ // should be installed with the module.
+ Test_config_template *string `android:"path,arch_variant"`
+
+ // Test options.
+ Test_options TestOptions
+
+ // Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+ // with root permission.
+ Require_root *bool
}
func init() {
@@ -54,31 +78,41 @@
android.RegisterModuleType("cc_benchmark_host", BenchmarkHostFactory)
}
-// Module factory for tests
+// cc_test generates a test config file and an executable binary file to test
+// specific functionality on a device. The executable binary gets an implicit
+// static_libs dependency on libgtests unless the gtest flag is set to false.
func TestFactory() android.Module {
module := NewTest(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for test libraries
+// cc_test_library creates an archive of files (i.e. .o files) which is later
+// referenced by another module (such as cc_test, cc_defaults or cc_test_library)
+// for archiving or linking.
func TestLibraryFactory() android.Module {
module := NewTestLibrary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for benchmarks
+// cc_benchmark compiles an executable binary that performs benchmark testing
+// of a specific component in a device. Additional files such as test suites
+// and test configuration are installed on the side of the compiled executed
+// binary.
func BenchmarkFactory() android.Module {
module := NewBenchmark(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for host tests
+// cc_test_host compiles a test host binary.
func TestHostFactory() android.Module {
module := NewTest(android.HostSupported)
return module.Init()
}
-// Module factory for host benchmarks
+// cc_benchmark_host compiles an executable binary that performs benchmark
+// testing of a specific component in the host. Additional files such as
+// test suites and test configuration are installed on the side of the
+// compiled executed binary.
func BenchmarkHostFactory() android.Module {
module := NewBenchmark(android.HostSupported)
return module.Init()
@@ -109,6 +143,10 @@
if m, ok := mctx.Module().(*Module); ok {
if test, ok := m.linker.(testPerSrc); ok {
if test.testPerSrc() && len(test.srcs()) > 0 {
+ if duplicate, found := checkDuplicate(test.srcs()); found {
+ mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
+ return
+ }
testNames := make([]string, len(test.srcs()))
for i, src := range test.srcs() {
testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
@@ -122,13 +160,24 @@
}
}
+func checkDuplicate(values []string) (duplicate string, found bool) {
+ seen := make(map[string]string)
+ for _, v := range values {
+ if duplicate, found = seen[v]; found {
+ return
+ }
+ seen[v] = v
+ }
+ return
+}
+
type testDecorator struct {
Properties TestProperties
linker *baseLinker
}
func (test *testDecorator) gtest() bool {
- return test.Properties.Gtest == nil || *test.Properties.Gtest == true
+ return BoolDefault(test.Properties.Gtest, true)
}
func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -159,6 +208,8 @@
if test.gtest() {
if ctx.useSdk() && ctx.Device() {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_c++", "libgtest_ndk_c++")
+ } else if BoolDefault(test.Properties.Isolated, false) {
+ deps.StaticLibs = append(deps.StaticLibs, "libgtest_isolated_main")
} else {
deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest")
}
@@ -168,13 +219,17 @@
}
func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
- // add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
+ // 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
// find out/host/linux-x86/lib[64]/library.so
- runpath := "../../lib"
- if ctx.toolchain().Is64Bit() {
- runpath += "64"
+ // 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
+ // also find out/host/linux-x86/lib[64]/library.so
+ runpaths := []string{"../../lib", "../../../lib"}
+ for _, runpath := range runpaths {
+ if ctx.toolchain().Is64Bit() {
+ runpath += "64"
+ }
+ linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
}
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
// add "" to rpath so that test binaries can find libraries in their own test directory
linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
@@ -194,6 +249,7 @@
*baseCompiler
Properties TestBinaryProperties
data android.Paths
+ testConfig android.Path
}
func (test *testBinary) linkerProps() []interface{} {
@@ -208,8 +264,6 @@
}
func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
- android.ExtractSourcesDeps(ctx, test.Properties.Data)
-
deps = test.testDecorator.linkerDeps(ctx, deps)
deps = test.binaryDecorator.linkerDeps(ctx, deps)
return deps
@@ -222,7 +276,20 @@
}
func (test *testBinary) install(ctx ModuleContext, file android.Path) {
- test.data = ctx.ExpandSources(test.Properties.Data, nil)
+ test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
+ var configs []tradefed.Config
+ if Bool(test.Properties.Require_root) {
+ configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+ }
+ if Bool(test.testDecorator.Properties.Isolated) {
+ configs = append(configs, tradefed.Option{"not-shardable", "true"})
+ }
+ if test.Properties.Test_options.Run_test_as != nil {
+ configs = append(configs, tradefed.Option{"run-test-as", String(test.Properties.Test_options.Run_test_as)})
+ }
+
+ test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
+ test.Properties.Test_config_template, test.Properties.Test_suites, configs)
test.binaryDecorator.baseInstaller.dir = "nativetest"
test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
@@ -296,17 +363,30 @@
type BenchmarkProperties struct {
// list of files or filegroup modules that provide data that should be installed alongside
// the test
- Data []string
+ Data []string `android:"path"`
// list of compatibility suites (for example "cts", "vts") that the module should be
// installed into.
- Test_suites []string
+ Test_suites []string `android:"arch_variant"`
+
+ // the name of the test configuration (for example "AndroidTest.xml") that should be
+ // installed with the module.
+ Test_config *string `android:"path,arch_variant"`
+
+ // the name of the test configuration template (for example "AndroidTestTemplate.xml") that
+ // should be installed with the module.
+ Test_config_template *string `android:"path,arch_variant"`
+
+ // Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+ // with root permission.
+ Require_root *bool
}
type benchmarkDecorator struct {
*binaryDecorator
Properties BenchmarkProperties
data android.Paths
+ testConfig android.Path
}
func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
@@ -325,30 +405,26 @@
}
func (benchmark *benchmarkDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
- android.ExtractSourcesDeps(ctx, benchmark.Properties.Data)
deps = benchmark.binaryDecorator.linkerDeps(ctx, deps)
deps.StaticLibs = append(deps.StaticLibs, "libgoogle-benchmark")
return deps
}
func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
- benchmark.data = ctx.ExpandSources(benchmark.Properties.Data, nil)
+ benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
+ var configs []tradefed.Config
+ if Bool(benchmark.Properties.Require_root) {
+ configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+ }
+ benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
+ benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs)
+
benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
benchmark.binaryDecorator.baseInstaller.install(ctx, file)
}
func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
- // Benchmarks aren't supported on Darwin
- if runtime.GOOS == "darwin" {
- switch hod {
- case android.HostAndDeviceSupported:
- hod = android.DeviceSupported
- case android.HostSupported:
- hod = android.NeitherHostNorDeviceSupported
- }
- }
-
module, binary := NewBinary(hod)
module.multilib = android.MultilibBoth
binary.baseInstaller = NewBaseInstaller("benchmarktest", "benchmarktest64", InstallInData)
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 4a7b0f7..21ea765 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -15,14 +15,12 @@
package cc
import (
- "fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"android/soong/android"
- "android/soong/genrule"
)
type dataFile struct {
@@ -129,7 +127,7 @@
"dir/bar/baz": nil,
})
ctx.RegisterModuleType("filegroup",
- android.ModuleFactoryAdaptor(genrule.FileGroupFactory))
+ android.ModuleFactoryAdaptor(android.FileGroupFactory))
ctx.RegisterModuleType("test",
android.ModuleFactoryAdaptor(newTest))
ctx.Register()
@@ -155,7 +153,7 @@
path := filepath.Join(test.data[i].path, test.data[i].file)
if test.data[i].file != got[i].Rel() ||
path != got[i].String() {
- fmt.Errorf("expected %s:%s got %s:%s",
+ t.Errorf("expected %s:%s got %s:%s",
path, test.data[i].file,
got[i].String(), got[i].Rel())
}
@@ -168,7 +166,7 @@
android.ModuleBase
data android.Paths
Properties struct {
- Data []string
+ Data []string `android:"path"`
}
}
@@ -179,10 +177,6 @@
return m
}
-func (test *testDataTest) DepsMutator(ctx android.BottomUpMutatorContext) {
- android.ExtractSourcesDeps(ctx, test.Properties.Data)
-}
-
func (test *testDataTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- test.data = ctx.ExpandSources(test.Properties.Data, nil)
+ test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
}
diff --git a/cc/test_gen_stub_libs.py b/cc/test_gen_stub_libs.py
index b20a5c7..2ee9886 100755
--- a/cc/test_gen_stub_libs.py
+++ b/cc/test_gen_stub_libs.py
@@ -15,7 +15,7 @@
# limitations under the License.
#
"""Tests for gen_stub_libs.py."""
-import cStringIO
+import io
import textwrap
import unittest
@@ -163,51 +163,129 @@
class OmitVersionTest(unittest.TestCase):
def test_omit_private(self):
- self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, False))
-
- self.assertTrue(gsl.should_omit_version(
- 'foo_PRIVATE', [], 'arm', 9, False))
- self.assertTrue(gsl.should_omit_version(
- 'foo_PLATFORM', [], 'arm', 9, False))
-
- self.assertTrue(gsl.should_omit_version(
- 'foo', ['platform-only'], 'arm', 9, False))
-
- def test_omit_vndk(self):
- self.assertTrue(gsl.should_omit_version(
- 'foo', ['vndk'], 'arm', 9, False))
-
- self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, True))
- self.assertFalse(gsl.should_omit_version(
- 'foo', ['vndk'], 'arm', 9, True))
-
- def test_omit_arch(self):
- self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, False))
- self.assertFalse(gsl.should_omit_version(
- 'foo', ['arm'], 'arm', 9, False))
-
- self.assertTrue(gsl.should_omit_version(
- 'foo', ['x86'], 'arm', 9, False))
-
- def test_omit_api(self):
- self.assertFalse(gsl.should_omit_version('foo', [], 'arm', 9, False))
self.assertFalse(
- gsl.should_omit_version('foo', ['introduced=9'], 'arm', 9, False))
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, False, False))
self.assertTrue(
- gsl.should_omit_version('foo', ['introduced=14'], 'arm', 9, False))
+ gsl.should_omit_version(
+ gsl.Version('foo_PRIVATE', None, [], []), 'arm', 9, False, False))
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo_PLATFORM', None, [], []), 'arm', 9, False, False))
+
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['platform-only'], []), 'arm', 9,
+ False, False))
+
+ def test_omit_vndk(self):
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['vndk'], []), 'arm', 9, False, False))
+
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, True, False))
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['vndk'], []), 'arm', 9, True, False))
+
+ def test_omit_apex(self):
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['apex'], []), 'arm', 9, False, False))
+
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, False, True))
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['apex'], []), 'arm', 9, False, True))
+
+ def test_omit_arch(self):
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, False, False))
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['arm'], []), 'arm', 9, False, False))
+
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['x86'], []), 'arm', 9, False, False))
+
+ def test_omit_api(self):
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, False, False))
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['introduced=9'], []), 'arm', 9,
+ False, False))
+
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['introduced=14'], []), 'arm', 9,
+ False, False))
+
+
+class OmitSymbolTest(unittest.TestCase):
+ def test_omit_vndk(self):
+ self.assertTrue(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['vndk']), 'arm', 9, False, False))
+
+ self.assertFalse(
+ gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, True, False))
+ self.assertFalse(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['vndk']), 'arm', 9, True, False))
+
+ def test_omit_apex(self):
+ self.assertTrue(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['apex']), 'arm', 9, False, False))
+
+ self.assertFalse(
+ gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False, True))
+ self.assertFalse(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['apex']), 'arm', 9, False, True))
+
+ def test_omit_arch(self):
+ self.assertFalse(
+ gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False, False))
+ self.assertFalse(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['arm']), 'arm', 9, False, False))
+
+ self.assertTrue(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['x86']), 'arm', 9, False, False))
+
+ def test_omit_api(self):
+ self.assertFalse(
+ gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False, False))
+ self.assertFalse(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['introduced=9']), 'arm', 9, False, False))
+
+ self.assertTrue(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['introduced=14']), 'arm', 9, False, False))
class SymbolFileParseTest(unittest.TestCase):
def test_next_line(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
foo
bar
# baz
qux
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
self.assertIsNone(parser.current_line)
self.assertEqual('foo', parser.next_line().strip())
@@ -223,7 +301,7 @@
self.assertEqual('', parser.current_line)
def test_parse_version(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 { # foo bar
baz;
qux; # woodly doodly
@@ -232,7 +310,7 @@
VERSION_2 {
} VERSION_1; # asdf
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.next_line()
version = parser.parse_version()
@@ -253,31 +331,31 @@
self.assertEqual([], version.tags)
def test_parse_version_eof(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.next_line()
with self.assertRaises(gsl.ParseError):
parser.parse_version()
def test_unknown_scope_label(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
foo:
}
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.next_line()
with self.assertRaises(gsl.ParseError):
parser.parse_version()
def test_parse_symbol(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
foo;
bar; # baz qux
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.next_line()
symbol = parser.parse_symbol()
@@ -290,47 +368,47 @@
self.assertEqual(['baz', 'qux'], symbol.tags)
def test_wildcard_symbol_global(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
*;
};
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.next_line()
with self.assertRaises(gsl.ParseError):
parser.parse_version()
def test_wildcard_symbol_local(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
local:
*;
};
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.next_line()
version = parser.parse_version()
self.assertEqual([], version.symbols)
def test_missing_semicolon(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
foo
};
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.next_line()
with self.assertRaises(gsl.ParseError):
parser.parse_version()
def test_parse_fails_invalid_input(self):
with self.assertRaises(gsl.ParseError):
- input_file = cStringIO.StringIO('foo')
- parser = gsl.SymbolFileParser(input_file, {})
+ input_file = io.StringIO('foo')
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
parser.parse()
def test_parse(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
local:
hidden1;
@@ -347,7 +425,7 @@
qwerty;
} VERSION_1;
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
versions = parser.parse()
expected = [
@@ -363,14 +441,38 @@
self.assertEqual(expected, versions)
+ def test_parse_vndk_apex_symbol(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ foo;
+ bar; # vndk
+ baz; # vndk apex
+ qux; # apex
+ };
+ """))
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, True)
+
+ parser.next_line()
+ version = parser.parse_version()
+ self.assertEqual('VERSION_1', version.name)
+ self.assertIsNone(version.base)
+
+ expected_symbols = [
+ gsl.Symbol('foo', []),
+ gsl.Symbol('bar', ['vndk']),
+ gsl.Symbol('baz', ['vndk', 'apex']),
+ gsl.Symbol('qux', ['apex']),
+ ]
+ self.assertEqual(expected_symbols, version.symbols)
+
class GeneratorTest(unittest.TestCase):
def test_omit_version(self):
# Thorough testing of the cases involved here is handled by
# OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
version = gsl.Version('VERSION_PRIVATE', None, [], [
gsl.Symbol('foo', []),
@@ -396,9 +498,9 @@
def test_omit_symbol(self):
# Thorough testing of the cases involved here is handled by
# SymbolPresenceTest.
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
version = gsl.Version('VERSION_1', None, [], [
gsl.Symbol('foo', ['x86']),
@@ -421,10 +523,17 @@
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
+ version = gsl.Version('VERSION_1', None, [], [
+ gsl.Symbol('foo', ['apex']),
+ ])
+ generator.write_version(version)
+ self.assertEqual('', src_file.getvalue())
+ self.assertEqual('', version_file.getvalue())
+
def test_write(self):
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
versions = [
gsl.Version('VERSION_1', None, [], [
@@ -475,7 +584,7 @@
'P': 9001,
}
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
global:
foo; # var
@@ -499,18 +608,19 @@
VERSION_4 { # versioned=9
wibble;
wizzes; # vndk
+ waggle; # apex
} VERSION_2;
VERSION_5 { # versioned=14
wobble;
} VERSION_4;
"""))
- parser = gsl.SymbolFileParser(input_file, api_map)
+ parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9, False, False)
versions = parser.parse()
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = gsl.Generator(src_file, version_file, 'arm', 9, False, False)
generator.write(versions)
expected_src = textwrap.dedent("""\
@@ -545,7 +655,7 @@
'Q': 9002,
}
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
global:
foo; # introduced=O
@@ -555,12 +665,12 @@
*;
};
"""))
- parser = gsl.SymbolFileParser(input_file, api_map)
+ parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9001, False, False)
versions = parser.parse()
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
- generator = gsl.Generator(src_file, version_file, 'arm', 9001, False)
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = gsl.Generator(src_file, version_file, 'arm', 9001, False, False)
generator.write(versions)
expected_src = textwrap.dedent("""\
@@ -578,6 +688,115 @@
""")
self.assertEqual(expected_version, version_file.getvalue())
+ def test_multiple_definition(self):
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ foo;
+ bar;
+ baz;
+ qux; # arm
+ local:
+ *;
+ };
+
+ VERSION_2 {
+ global:
+ bar;
+ qux; # arm64
+ } VERSION_1;
+
+ VERSION_PRIVATE {
+ global:
+ baz;
+ } VERSION_2;
+
+ """))
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+
+ with self.assertRaises(gsl.MultiplyDefinedSymbolError) as cm:
+ parser.parse()
+ self.assertEquals(['bar', 'foo'],
+ cm.exception.multiply_defined_symbols)
+
+ def test_integration_with_apex(self):
+ api_map = {
+ 'O': 9000,
+ 'P': 9001,
+ }
+
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo; # var
+ bar; # x86
+ fizz; # introduced=O
+ buzz; # introduced=P
+ local:
+ *;
+ };
+
+ VERSION_2 { # arm
+ baz; # introduced=9
+ qux; # versioned=14
+ } VERSION_1;
+
+ VERSION_3 { # introduced=14
+ woodly;
+ doodly; # var
+ } VERSION_2;
+
+ VERSION_4 { # versioned=9
+ wibble;
+ wizzes; # vndk
+ waggle; # apex
+ bubble; # apex vndk
+ duddle; # vndk apex
+ } VERSION_2;
+
+ VERSION_5 { # versioned=14
+ wobble;
+ } VERSION_4;
+ """))
+ parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9, False, True)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ generator = gsl.Generator(src_file, version_file, 'arm', 9, False, True)
+ generator.write(versions)
+
+ expected_src = textwrap.dedent("""\
+ int foo = 0;
+ void baz() {}
+ void qux() {}
+ void wibble() {}
+ void waggle() {}
+ void bubble() {}
+ void duddle() {}
+ void wobble() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ VERSION_1 {
+ global:
+ foo;
+ };
+ VERSION_2 {
+ global:
+ baz;
+ } VERSION_1;
+ VERSION_4 {
+ global:
+ wibble;
+ waggle;
+ bubble;
+ duddle;
+ } VERSION_2;
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
def main():
suite = unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/cc/testing.go b/cc/testing.go
new file mode 100644
index 0000000..8d76c2f
--- /dev/null
+++ b/cc/testing.go
@@ -0,0 +1,185 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "android/soong/android"
+)
+
+func GatherRequiredDepsForTest(os android.OsType) string {
+ ret := `
+ toolchain_library {
+ name: "libatomic",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libcompiler_rt-extras",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-arm-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-aarch64-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-i686-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-x86_64-android",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libgcc",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libgcc_stripped",
+ vendor_available: true,
+ recovery_available: true,
+ src: "",
+ }
+
+ cc_library {
+ name: "libc",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+ llndk_library {
+ name: "libc",
+ symbol_file: "",
+ }
+ cc_library {
+ name: "libm",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+ llndk_library {
+ name: "libm",
+ symbol_file: "",
+ }
+ cc_library {
+ name: "libdl",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+ llndk_library {
+ name: "libdl",
+ symbol_file: "",
+ }
+ cc_library {
+ name: "libc++_static",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ vendor_available: true,
+ recovery_available: true,
+ }
+ cc_library {
+ name: "libc++",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ vendor_available: true,
+ recovery_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ }
+ cc_library {
+ name: "libunwind_llvm",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ stl: "none",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_object {
+ name: "crtbegin_so",
+ recovery_available: true,
+ vendor_available: true,
+ }
+
+ cc_object {
+ name: "crtbegin_static",
+ recovery_available: true,
+ vendor_available: true,
+ }
+
+ cc_object {
+ name: "crtend_so",
+ recovery_available: true,
+ vendor_available: true,
+ }
+
+ cc_object {
+ name: "crtend_android",
+ recovery_available: true,
+ vendor_available: true,
+ }
+
+ cc_library {
+ name: "libprotobuf-cpp-lite",
+ }
+ `
+ if os == android.Fuchsia {
+ ret += `
+ cc_library {
+ name: "libbioniccompat",
+ stl: "none",
+ }
+ cc_library {
+ name: "libcompiler_rt",
+ stl: "none",
+ }
+ `
+ }
+ return ret
+}
diff --git a/cc/tidy.go b/cc/tidy.go
index 8ca94ef..5455392 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -31,6 +31,9 @@
// Extra checks to enable or disable in clang-tidy
Tidy_checks []string
+
+ // Checks that should be treated as errors.
+ Tidy_checks_as_errors []string
}
type tidyFeature struct {
@@ -62,16 +65,16 @@
return flags
}
- // Clang-tidy requires clang
- if !flags.Clang {
- return flags
- }
-
flags.Tidy = true
- esc := proptools.NinjaAndShellEscape
-
+ // Add global WITH_TIDY_FLAGS and local tidy_flags.
+ withTidyFlags := ctx.Config().Getenv("WITH_TIDY_FLAGS")
+ if len(withTidyFlags) > 0 {
+ flags.TidyFlags = append(flags.TidyFlags, withTidyFlags)
+ }
+ esc := proptools.NinjaAndShellEscapeList
flags.TidyFlags = append(flags.TidyFlags, esc(tidy.Properties.Tidy_flags)...)
+ // If TidyFlags is empty, add default header filter.
if len(flags.TidyFlags) == 0 {
headerFilter := "-header-filter=\"(" + ctx.ModuleDir() + "|${config.TidyDefaultHeaderDirs})\""
flags.TidyFlags = append(flags.TidyFlags, headerFilter)
@@ -83,9 +86,21 @@
flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before=-fno-caret-diagnostics")
}
- // We might be using the static analyzer through clang tidy.
- // https://bugs.llvm.org/show_bug.cgi?id=32914
- flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before=-D__clang_analyzer__")
+ extraArgFlags := []string{
+ // We might be using the static analyzer through clang tidy.
+ // https://bugs.llvm.org/show_bug.cgi?id=32914
+ "-D__clang_analyzer__",
+
+ // A recent change in clang-tidy (r328258) enabled destructor inlining, which
+ // appears to cause a number of false positives. Until that's resolved, this turns
+ // off the effects of r328258.
+ // https://bugs.llvm.org/show_bug.cgi?id=37459
+ "-Xclang", "-analyzer-config", "-Xclang", "c++-temp-dtor-inlining=false",
+ }
+
+ for _, f := range extraArgFlags {
+ flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before="+f)
+ }
tidyChecks := "-checks="
if checks := ctx.Config().TidyChecks(); len(checks) > 0 {
@@ -96,7 +111,17 @@
if len(tidy.Properties.Tidy_checks) > 0 {
tidyChecks = tidyChecks + "," + strings.Join(esc(tidy.Properties.Tidy_checks), ",")
}
+ if ctx.Windows() {
+ // https://b.corp.google.com/issues/120614316
+ // mingw32 has cert-dcl16-c warning in NO_ERROR,
+ // which is used in many Android files.
+ tidyChecks = tidyChecks + ",-cert-dcl16-c"
+ }
flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
+ if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+ tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
+ flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
+ }
return flags
}
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index 2bb4018..8ab8bc9 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -23,11 +23,20 @@
//
func init() {
- android.RegisterModuleType("toolchain_library", toolchainLibraryFactory)
+ android.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
+}
+
+type toolchainLibraryProperties struct {
+ // the prebuilt toolchain library, as a path from the top of the source tree
+ Src *string `android:"arch_variant"`
}
type toolchainLibraryDecorator struct {
*libraryDecorator
+
+ stripper
+
+ Properties toolchainLibraryProperties
}
func (*toolchainLibraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
@@ -35,7 +44,13 @@
return deps
}
-func toolchainLibraryFactory() android.Module {
+func (library *toolchainLibraryDecorator) linkerProps() []interface{} {
+ var props []interface{}
+ props = append(props, library.libraryDecorator.linkerProps()...)
+ return append(props, &library.Properties, &library.stripper.StripProperties)
+}
+
+func ToolchainLibraryFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
toolchainLibrary := &toolchainLibraryDecorator{
@@ -43,7 +58,6 @@
}
module.compiler = toolchainLibrary
module.linker = toolchainLibrary
- module.Properties.Clang = BoolPtr(false)
module.stl = nil
module.sanitize = nil
module.installer = nil
@@ -58,16 +72,24 @@
func (library *toolchainLibraryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
- libName := ctx.ModuleName() + staticLibraryExtension
- outputFile := android.PathForModuleOut(ctx, libName)
-
- if flags.Clang {
- ctx.ModuleErrorf("toolchain_library must use GCC, not Clang")
+ if library.Properties.Src == nil {
+ ctx.PropertyErrorf("src", "No library source specified")
+ return android.PathForSource(ctx, "")
}
- CopyGccLib(ctx, libName, flagsToBuilderFlags(flags), outputFile)
+ srcPath := android.PathForSource(ctx, *library.Properties.Src)
- ctx.CheckbuildFile(outputFile)
+ if library.stripper.StripProperties.Strip.Keep_symbols_list != nil {
+ fileName := ctx.ModuleName() + staticLibraryExtension
+ outputFile := android.PathForModuleOut(ctx, fileName)
+ buildFlags := flagsToBuilderFlags(flags)
+ library.stripper.strip(ctx, srcPath, outputFile, buildFlags)
+ return outputFile
+ }
- return outputFile
+ return srcPath
+}
+
+func (library *toolchainLibraryDecorator) nativeCoverage() bool {
+ return false
}
diff --git a/cc/util.go b/cc/util.go
index 1e4a0c0..5dcbaef 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path/filepath"
"regexp"
"strings"
@@ -58,34 +59,34 @@
func flagsToBuilderFlags(in Flags) builderFlags {
return builderFlags{
- globalFlags: strings.Join(in.GlobalFlags, " "),
- arFlags: strings.Join(in.ArFlags, " "),
- asFlags: strings.Join(in.AsFlags, " "),
- cFlags: strings.Join(in.CFlags, " "),
- toolingCFlags: strings.Join(in.ToolingCFlags, " "),
- conlyFlags: strings.Join(in.ConlyFlags, " "),
- cppFlags: strings.Join(in.CppFlags, " "),
- yaccFlags: strings.Join(in.YaccFlags, " "),
- protoFlags: strings.Join(in.protoFlags, " "),
- protoOutParams: strings.Join(in.protoOutParams, ","),
- aidlFlags: strings.Join(in.aidlFlags, " "),
- rsFlags: strings.Join(in.rsFlags, " "),
- ldFlags: strings.Join(in.LdFlags, " "),
- libFlags: strings.Join(in.libFlags, " "),
- tidyFlags: strings.Join(in.TidyFlags, " "),
- sAbiFlags: strings.Join(in.SAbiFlags, " "),
- yasmFlags: strings.Join(in.YasmFlags, " "),
- toolchain: in.Toolchain,
- clang: in.Clang,
- coverage: in.Coverage,
- tidy: in.Tidy,
- sAbiDump: in.SAbiDump,
- protoRoot: in.ProtoRoot,
+ globalFlags: strings.Join(in.GlobalFlags, " "),
+ arFlags: strings.Join(in.ArFlags, " "),
+ asFlags: strings.Join(in.AsFlags, " "),
+ cFlags: strings.Join(in.CFlags, " "),
+ toolingCFlags: strings.Join(in.ToolingCFlags, " "),
+ toolingCppFlags: strings.Join(in.ToolingCppFlags, " "),
+ conlyFlags: strings.Join(in.ConlyFlags, " "),
+ cppFlags: strings.Join(in.CppFlags, " "),
+ yaccFlags: strings.Join(in.YaccFlags, " "),
+ aidlFlags: strings.Join(in.aidlFlags, " "),
+ rsFlags: strings.Join(in.rsFlags, " "),
+ ldFlags: strings.Join(in.LdFlags, " "),
+ libFlags: strings.Join(in.libFlags, " "),
+ tidyFlags: strings.Join(in.TidyFlags, " "),
+ sAbiFlags: strings.Join(in.SAbiFlags, " "),
+ yasmFlags: strings.Join(in.YasmFlags, " "),
+ toolchain: in.Toolchain,
+ coverage: in.Coverage,
+ tidy: in.Tidy,
+ sAbiDump: in.SAbiDump,
systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
groupStaticLibs: in.GroupStaticLibs,
- arGoldPlugin: in.ArGoldPlugin,
+
+ proto: in.proto,
+ protoC: in.protoC,
+ protoOptionsFile: in.protoOptionsFile,
}
}
@@ -102,3 +103,36 @@
}
return list
}
+
+var shlibVersionPattern = regexp.MustCompile("(?:\\.\\d+(?:svn)?)+")
+
+// splitFileExt splits a file name into root, suffix and ext. root stands for the file name without
+// the file extension and the version number (e.g. "libexample"). suffix stands for the
+// concatenation of the file extension and the version number (e.g. ".so.1.0"). ext stands for the
+// file extension after the version numbers are trimmed (e.g. ".so").
+func splitFileExt(name string) (string, string, string) {
+ // Extract and trim the shared lib version number if the file name ends with dot digits.
+ suffix := ""
+ matches := shlibVersionPattern.FindAllStringIndex(name, -1)
+ if len(matches) > 0 {
+ lastMatch := matches[len(matches)-1]
+ if lastMatch[1] == len(name) {
+ suffix = name[lastMatch[0]:lastMatch[1]]
+ name = name[0:lastMatch[0]]
+ }
+ }
+
+ // Extract the file name root and the file extension.
+ ext := filepath.Ext(name)
+ root := strings.TrimSuffix(name, ext)
+ suffix = ext + suffix
+
+ return root, suffix, ext
+}
+
+// linkDirOnDevice/linkName -> target
+func makeSymlinkCmd(linkDirOnDevice string, linkName string, target string) string {
+ dir := filepath.Join("$(PRODUCT_OUT)", linkDirOnDevice)
+ return "mkdir -p " + dir + " && " +
+ "ln -sf " + target + " " + filepath.Join(dir, linkName)
+}
diff --git a/cc/util_test.go b/cc/util_test.go
new file mode 100644
index 0000000..7c718ea
--- /dev/null
+++ b/cc/util_test.go
@@ -0,0 +1,84 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "testing"
+)
+
+func TestSplitFileExt(t *testing.T) {
+ t.Run("soname with version", func(t *testing.T) {
+ root, suffix, ext := splitFileExt("libtest.so.1.0.30")
+ expected := "libtest"
+ if root != expected {
+ t.Errorf("root should be %q but got %q", expected, root)
+ }
+ expected = ".so.1.0.30"
+ if suffix != expected {
+ t.Errorf("suffix should be %q but got %q", expected, suffix)
+ }
+ expected = ".so"
+ if ext != expected {
+ t.Errorf("ext should be %q but got %q", expected, ext)
+ }
+ })
+
+ t.Run("soname with svn version", func(t *testing.T) {
+ root, suffix, ext := splitFileExt("libtest.so.1svn")
+ expected := "libtest"
+ if root != expected {
+ t.Errorf("root should be %q but got %q", expected, root)
+ }
+ expected = ".so.1svn"
+ if suffix != expected {
+ t.Errorf("suffix should be %q but got %q", expected, suffix)
+ }
+ expected = ".so"
+ if ext != expected {
+ t.Errorf("ext should be %q but got %q", expected, ext)
+ }
+ })
+
+ t.Run("version numbers in the middle should be ignored", func(t *testing.T) {
+ root, suffix, ext := splitFileExt("libtest.1.0.30.so")
+ expected := "libtest.1.0.30"
+ if root != expected {
+ t.Errorf("root should be %q but got %q", expected, root)
+ }
+ expected = ".so"
+ if suffix != expected {
+ t.Errorf("suffix should be %q but got %q", expected, suffix)
+ }
+ expected = ".so"
+ if ext != expected {
+ t.Errorf("ext should be %q but got %q", expected, ext)
+ }
+ })
+
+ t.Run("no known file extension", func(t *testing.T) {
+ root, suffix, ext := splitFileExt("test.exe")
+ expected := "test"
+ if root != expected {
+ t.Errorf("root should be %q but got %q", expected, root)
+ }
+ expected = ".exe"
+ if suffix != expected {
+ t.Errorf("suffix should be %q but got %q", expected, suffix)
+ }
+ if ext != expected {
+ t.Errorf("ext should be %q but got %q", expected, ext)
+ }
+ })
+}
diff --git a/cc/vndk.go b/cc/vndk.go
index 5a24a98..44a83e7 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -15,11 +15,13 @@
package cc
import (
+ "errors"
"sort"
"strings"
"sync"
"android/soong/android"
+ "android/soong/cc/config"
)
type VndkProperties struct {
@@ -151,50 +153,55 @@
}
// Check the dependencies of VNDK shared libraries.
- if !vndkIsVndkDepAllowed(vndk, to.vndkdep) {
- ctx.ModuleErrorf("(%s) should not link to %q (%s)",
- vndk.typeName(), to.Name(), to.vndkdep.typeName())
+ if err := vndkIsVndkDepAllowed(vndk, to.vndkdep); err != nil {
+ ctx.ModuleErrorf("(%s) should not link to %q (%s): %v",
+ vndk.typeName(), to.Name(), to.vndkdep.typeName(), err)
return
}
}
-func vndkIsVndkDepAllowed(from *vndkdep, to *vndkdep) bool {
+func vndkIsVndkDepAllowed(from *vndkdep, to *vndkdep) error {
// Check the dependencies of VNDK, VNDK-Ext, VNDK-SP, VNDK-SP-Ext and vendor modules.
if from.isVndkExt() {
if from.isVndkSp() {
- // VNDK-SP-Ext may depend on VNDK-SP, VNDK-SP-Ext, or vendor libs (excluding
- // VNDK and VNDK-Ext).
- return to.isVndkSp() || !to.isVndk()
+ if to.isVndk() && !to.isVndkSp() {
+ return errors.New("VNDK-SP extensions must not depend on VNDK or VNDK extensions")
+ }
+ return nil
}
// VNDK-Ext may depend on VNDK, VNDK-Ext, VNDK-SP, VNDK-SP-Ext, or vendor libs.
- return true
+ return nil
}
if from.isVndk() {
if to.isVndkExt() {
- // VNDK-core and VNDK-SP must not depend on VNDK extensions.
- return false
+ return errors.New("VNDK-core and VNDK-SP must not depend on VNDK extensions")
}
if from.isVndkSp() {
- // VNDK-SP must only depend on VNDK-SP.
- return to.isVndkSp()
+ if !to.isVndkSp() {
+ return errors.New("VNDK-SP must only depend on VNDK-SP")
+ }
+ return nil
}
- // VNDK-core may depend on VNDK-core or VNDK-SP.
- return to.isVndk()
+ if !to.isVndk() {
+ return errors.New("VNDK-core must only depend on VNDK-core or VNDK-SP")
+ }
+ return nil
}
// Vendor modules may depend on VNDK, VNDK-Ext, VNDK-SP, VNDK-SP-Ext, or vendor libs.
- return true
+ return nil
}
var (
- vndkCoreLibraries []string
- vndkSpLibraries []string
- llndkLibraries []string
- vndkPrivateLibraries []string
- vndkLibrariesLock sync.Mutex
+ vndkCoreLibraries []string
+ vndkSpLibraries []string
+ llndkLibraries []string
+ vndkPrivateLibraries []string
+ vndkUsingCoreVariantLibraries []string
+ vndkLibrariesLock sync.Mutex
)
// gather list of vndk-core, vndk-sp, and ll-ndk libs
-func vndkMutator(mctx android.BottomUpMutatorContext) {
+func VndkMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*Module); ok && m.Enabled() {
if lib, ok := m.linker.(*llndkStubDecorator); ok {
vndkLibrariesLock.Lock()
@@ -218,6 +225,12 @@
if m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
vndkLibrariesLock.Lock()
defer vndkLibrariesLock.Unlock()
+ if mctx.DeviceConfig().VndkUseCoreVariant() && !inList(name, config.VndkMustUseVendorVariantList) {
+ if !inList(name, vndkUsingCoreVariantLibraries) {
+ vndkUsingCoreVariantLibraries = append(vndkUsingCoreVariantLibraries, name)
+ sort.Strings(vndkUsingCoreVariantLibraries)
+ }
+ }
if m.vndkdep.isVndkSp() {
if !inList(name, vndkSpLibraries) {
vndkSpLibraries = append(vndkSpLibraries, name)
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 9c9545d..74f7f27 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -21,7 +21,8 @@
)
var (
- vndkSuffix = ".vndk."
+ vndkSuffix = ".vndk."
+ binder32Suffix = ".binder32"
)
// Creates vndk prebuilts that include the VNDK version.
@@ -53,8 +54,16 @@
// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
Target_arch *string
+ // If the prebuilt snapshot lib is built with 32 bit binder, this must be set to true.
+ // The lib with 64 bit binder does not need to set this property.
+ Binder32bit *bool
+
// Prebuilt files for each arch.
Srcs []string `android:"arch_variant"`
+
+ // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols,
+ // etc).
+ Check_elf_files *bool
}
type vndkPrebuiltLibraryDecorator struct {
@@ -67,10 +76,14 @@
}
func (p *vndkPrebuiltLibraryDecorator) NameSuffix() string {
+ suffix := p.version()
if p.arch() != "" {
- return vndkSuffix + p.version() + "." + p.arch()
+ suffix += "." + p.arch()
}
- return vndkSuffix + p.version()
+ if Bool(p.properties.Binder32bit) {
+ suffix += binder32Suffix
+ }
+ return vndkSuffix + suffix
}
func (p *vndkPrebuiltLibraryDecorator) version() string {
@@ -81,6 +94,13 @@
return String(p.properties.Target_arch)
}
+func (p *vndkPrebuiltLibraryDecorator) binderBit() string {
+ if Bool(p.properties.Binder32bit) {
+ return "32"
+ }
+ return "64"
+}
+
func (p *vndkPrebuiltLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
return p.libraryDecorator.linkerFlags(ctx, flags)
@@ -114,6 +134,9 @@
if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
return
}
+ if ctx.DeviceConfig().BinderBitness() != p.binderBit() {
+ return
+ }
if p.shared() {
if ctx.isVndkSp() {
p.baseInstaller.subDir = "vndk-sp-" + p.version()
@@ -130,11 +153,14 @@
module.stl = nil
module.sanitize = nil
library.StripProperties.Strip.None = BoolPtr(true)
+ module.Properties.UseVndk = true
prebuilt := &vndkPrebuiltLibraryDecorator{
libraryDecorator: library,
}
+ prebuilt.properties.Check_elf_files = BoolPtr(false)
+
module.compiler = nil
module.linker = prebuilt
module.installer = prebuilt
diff --git a/cc/xom.go b/cc/xom.go
new file mode 100644
index 0000000..9337990
--- /dev/null
+++ b/cc/xom.go
@@ -0,0 +1,76 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "android/soong/android"
+)
+
+type XomProperties struct {
+ Xom *bool
+}
+
+type xom struct {
+ Properties XomProperties
+}
+
+func (xom *xom) props() []interface{} {
+ return []interface{}{&xom.Properties}
+}
+
+func (xom *xom) begin(ctx BaseModuleContext) {}
+
+func (xom *xom) deps(ctx BaseModuleContext, deps Deps) Deps {
+ return deps
+}
+
+func (xom *xom) flags(ctx ModuleContext, flags Flags) Flags {
+ disableXom := false
+
+ if !ctx.Config().EnableXOM() || ctx.Config().XOMDisabledForPath(ctx.ModuleDir()) {
+ disableXom = true
+ }
+
+ if xom.Properties.Xom != nil && !*xom.Properties.Xom {
+ return flags
+ }
+
+ // If any static dependencies have XOM disabled, we should disable XOM in this module,
+ // the assumption being if it's been explicitly disabled then there's probably incompatible
+ // code in the library which may get pulled in.
+ if !disableXom {
+ ctx.VisitDirectDeps(func(m android.Module) {
+ cc, ok := m.(*Module)
+ if !ok || cc.xom == nil || !cc.static() {
+ return
+ }
+ if cc.xom.Properties.Xom != nil && !*cc.xom.Properties.Xom {
+ disableXom = true
+ return
+ }
+ })
+ }
+
+ // Enable execute-only if none of the dependencies disable it,
+ // also if it's explicitly set true (allows overriding dependencies disabling it).
+ if !disableXom || (xom.Properties.Xom != nil && *xom.Properties.Xom) {
+ // XOM is only supported on AArch64 when using lld.
+ if ctx.Arch().ArchType == android.Arm64 && ctx.useClangLld(ctx) {
+ flags.LdFlags = append(flags.LdFlags, "-Wl,-execute-only")
+ }
+ }
+
+ return flags
+}
diff --git a/ui/build/util_linux.go b/cmd/dep_fixer/Android.bp
similarity index 71%
copy from ui/build/util_linux.go
copy to cmd/dep_fixer/Android.bp
index 0a4e1d2..d2d1113 100644
--- a/ui/build/util_linux.go
+++ b/cmd/dep_fixer/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All rights reserved.
+// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package build
-
-import (
- "syscall"
-)
-
-const ioctlGetTermios = syscall.TCGETS
+blueprint_go_binary {
+ name: "dep_fixer",
+ deps: ["androidmk-parser"],
+ srcs: [
+ "main.go",
+ "deps.go",
+ ],
+ testSrcs: ["deps_test.go"],
+}
diff --git a/cmd/dep_fixer/deps.go b/cmd/dep_fixer/deps.go
new file mode 100644
index 0000000..64c97f5
--- /dev/null
+++ b/cmd/dep_fixer/deps.go
@@ -0,0 +1,95 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+
+ "android/soong/androidmk/parser"
+)
+
+type Deps struct {
+ Output string
+ Inputs []string
+}
+
+func Parse(filename string, r io.Reader) (*Deps, error) {
+ p := parser.NewParser(filename, r)
+ nodes, errs := p.Parse()
+
+ if len(errs) == 1 {
+ return nil, errs[0]
+ } else if len(errs) > 1 {
+ return nil, fmt.Errorf("many errors: %v", errs)
+ }
+
+ pos := func(node parser.Node) string {
+ return p.Unpack(node.Pos()).String() + ": "
+ }
+
+ ret := &Deps{}
+
+ for _, node := range nodes {
+ switch x := node.(type) {
+ case *parser.Comment:
+ // Do nothing
+ case *parser.Rule:
+ if x.Recipe != "" {
+ return nil, fmt.Errorf("%sunexpected recipe in rule: %v", pos(node), x)
+ }
+
+ if !x.Target.Const() {
+ return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Target.Dump())
+ }
+ outputs := x.Target.Words()
+ if len(outputs) == 0 {
+ return nil, fmt.Errorf("%smissing output: %v", pos(node), x)
+ }
+ ret.Output = outputs[0].Value(nil)
+
+ if !x.Prerequisites.Const() {
+ return nil, fmt.Errorf("%sunsupported variable expansion: %v", pos(node), x.Prerequisites.Dump())
+ }
+ for _, input := range x.Prerequisites.Words() {
+ ret.Inputs = append(ret.Inputs, input.Value(nil))
+ }
+ default:
+ return nil, fmt.Errorf("%sunexpected line: %#v", pos(node), node)
+ }
+ }
+
+ return ret, nil
+}
+
+func (d *Deps) Print() []byte {
+ // We don't really have to escape every \, but it's simpler,
+ // and ninja will handle it.
+ replacer := strings.NewReplacer(" ", "\\ ",
+ ":", "\\:",
+ "#", "\\#",
+ "$", "$$",
+ "\\", "\\\\")
+
+ b := &bytes.Buffer{}
+ fmt.Fprintf(b, "%s:", replacer.Replace(d.Output))
+ for _, input := range d.Inputs {
+ fmt.Fprintf(b, " %s", replacer.Replace(input))
+ }
+ fmt.Fprintln(b)
+ return b.Bytes()
+}
diff --git a/cmd/dep_fixer/deps_test.go b/cmd/dep_fixer/deps_test.go
new file mode 100644
index 0000000..0a779b7
--- /dev/null
+++ b/cmd/dep_fixer/deps_test.go
@@ -0,0 +1,389 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func TestParse(t *testing.T) {
+ testCases := []struct {
+ name string
+ input string
+ output Deps
+ err error
+ }{
+ // These come from the ninja test suite
+ {
+ name: "Basic",
+ input: "build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h",
+ output: Deps{
+ Output: "build/ninja.o",
+ Inputs: []string{
+ "ninja.cc",
+ "ninja.h",
+ "eval_env.h",
+ "manifest_parser.h",
+ },
+ },
+ },
+ {
+ name: "EarlyNewlineAndWhitespace",
+ input: ` \
+ out: in`,
+ output: Deps{
+ Output: "out",
+ Inputs: []string{"in"},
+ },
+ },
+ {
+ name: "Continuation",
+ input: `foo.o: \
+ bar.h baz.h
+`,
+ output: Deps{
+ Output: "foo.o",
+ Inputs: []string{"bar.h", "baz.h"},
+ },
+ },
+ {
+ name: "CarriageReturnContinuation",
+ input: "foo.o: \\\r\n bar.h baz.h\r\n",
+ output: Deps{
+ Output: "foo.o",
+ Inputs: []string{"bar.h", "baz.h"},
+ },
+ },
+ {
+ name: "BackSlashes",
+ input: `Project\Dir\Build\Release8\Foo\Foo.res : \
+ Dir\Library\Foo.rc \
+ Dir\Library\Version\Bar.h \
+ Dir\Library\Foo.ico \
+ Project\Thing\Bar.tlb \
+`,
+ output: Deps{
+ Output: `Project\Dir\Build\Release8\Foo\Foo.res`,
+ Inputs: []string{
+ `Dir\Library\Foo.rc`,
+ `Dir\Library\Version\Bar.h`,
+ `Dir\Library\Foo.ico`,
+ `Project\Thing\Bar.tlb`,
+ },
+ },
+ },
+ {
+ name: "Spaces",
+ input: `a\ bc\ def: a\ b c d`,
+ output: Deps{
+ Output: `a bc def`,
+ Inputs: []string{"a b", "c", "d"},
+ },
+ },
+ {
+ name: "Escapes",
+ input: `\!\@\#$$\%\^\&\\:`,
+ output: Deps{
+ Output: `\!\@#$\%\^\&\`,
+ },
+ },
+ {
+ name: "SpecialChars",
+ // Ninja includes a number of '=', but our parser can't handle that,
+ // since it sees the equals and switches over to assuming it's an
+ // assignment.
+ //
+ // We don't have any files in our tree that contain an '=' character,
+ // and Kati can't handle parsing this either, so for now I'm just
+ // going to remove all the '=' characters below.
+ //
+ // It looks like make will only do this for the first
+ // dependency, but not later dependencies.
+ input: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: \
+ en@quot.header~ t+t-x!1 \
+ openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif \
+ Fu` + "\303\244ball",
+ output: Deps{
+ Output: "C:/Program Files (x86)/Microsoft crtdefs.h",
+ Inputs: []string{
+ "en@quot.header~",
+ "t+t-x!1",
+ "openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif",
+ "Fu\303\244ball",
+ },
+ },
+ },
+ // Ninja's UnifyMultipleOutputs and RejectMultipleDifferentOutputs tests have been omitted,
+ // since we don't want the same behavior.
+
+ // Our own tests
+ {
+ name: "Multiple outputs",
+ input: `a b: c
+a: d
+b: e`,
+ output: Deps{
+ Output: "b",
+ Inputs: []string{
+ "c",
+ "d",
+ "e",
+ },
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ out, err := Parse("test.d", bytes.NewBufferString(tc.input))
+ if err != tc.err {
+ t.Fatalf("Unexpected error: %v (expected %v)", err, tc.err)
+ }
+
+ if out.Output != tc.output.Output {
+ t.Errorf("output file doesn't match:\n"+
+ " str: %#v\n"+
+ "want: %#v\n"+
+ " got: %#v", tc.input, tc.output.Output, out.Output)
+ }
+
+ matches := true
+ if len(out.Inputs) != len(tc.output.Inputs) {
+ matches = false
+ } else {
+ for i := range out.Inputs {
+ if out.Inputs[i] != tc.output.Inputs[i] {
+ matches = false
+ }
+ }
+ }
+ if !matches {
+ t.Errorf("input files don't match:\n"+
+ " str: %#v\n"+
+ "want: %#v\n"+
+ " got: %#v", tc.input, tc.output.Inputs, out.Inputs)
+ }
+ })
+ }
+}
+
+func BenchmarkParsing(b *testing.B) {
+ // Write it out to a file to most closely match ninja's perftest
+ tmpfile, err := ioutil.TempFile("", "depfile")
+ if err != nil {
+ b.Fatal("Failed to create temp file:", err)
+ }
+ defer os.Remove(tmpfile.Name())
+ _, err = io.WriteString(tmpfile, `out/soong/.intermediates/external/ninja/ninja/linux_glibc_x86_64/obj/external/ninja/src/ninja.o: \
+ external/ninja/src/ninja.cc external/libcxx/include/errno.h \
+ external/libcxx/include/__config \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/features.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/predefs.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/cdefs.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wordsize.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/errno.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/errno.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/errno.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/asm/errno.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno-base.h \
+ external/libcxx/include/limits.h \
+ prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/limits.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/limits.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix1_lim.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/local_lim.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/limits.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix2_lim.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/xopen_lim.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
+ external/libcxx/include/stdio.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdio.h \
+ external/libcxx/include/stddef.h \
+ prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stddef.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/types.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/typesizes.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/libio.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/_G_config.h \
+ external/libcxx/include/wchar.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wchar.h \
+ prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdarg.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sys_errlist.h \
+ external/libcxx/include/stdlib.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdlib.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitflags.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitstatus.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/endian.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/endian.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/byteswap.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/xlocale.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/types.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/time.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/select.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sigset.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/time.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select2.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/sysmacros.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/alloca.h \
+ external/libcxx/include/string.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/string.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/getopt.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/unistd.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix_opt.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/environments.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/confname.h \
+ external/ninja/src/browse.h external/ninja/src/build.h \
+ external/libcxx/include/cstdio external/libcxx/include/map \
+ external/libcxx/include/__tree external/libcxx/include/iterator \
+ external/libcxx/include/iosfwd \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wchar.h \
+ external/libcxx/include/__functional_base \
+ external/libcxx/include/type_traits external/libcxx/include/cstddef \
+ prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/__stddef_max_align_t.h \
+ external/libcxx/include/__nullptr external/libcxx/include/typeinfo \
+ external/libcxx/include/exception external/libcxx/include/cstdlib \
+ external/libcxx/include/cstdint external/libcxx/include/stdint.h \
+ prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdint.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdint.h \
+ external/libcxx/include/new external/libcxx/include/utility \
+ external/libcxx/include/__tuple \
+ external/libcxx/include/initializer_list \
+ external/libcxx/include/cstring external/libcxx/include/__debug \
+ external/libcxx/include/memory external/libcxx/include/limits \
+ external/libcxx/include/__undef_macros external/libcxx/include/tuple \
+ external/libcxx/include/stdexcept external/libcxx/include/cassert \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/assert.h \
+ external/libcxx/include/atomic external/libcxx/include/algorithm \
+ external/libcxx/include/functional external/libcxx/include/queue \
+ external/libcxx/include/deque external/libcxx/include/__split_buffer \
+ external/libcxx/include/vector external/libcxx/include/__bit_reference \
+ external/libcxx/include/climits external/libcxx/include/set \
+ external/libcxx/include/string external/libcxx/include/string_view \
+ external/libcxx/include/__string external/libcxx/include/cwchar \
+ external/libcxx/include/cwctype external/libcxx/include/cctype \
+ external/libcxx/include/ctype.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/ctype.h \
+ external/libcxx/include/wctype.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wctype.h \
+ external/ninja/src/graph.h external/ninja/src/eval_env.h \
+ external/ninja/src/string_piece.h external/ninja/src/timestamp.h \
+ external/ninja/src/util.h external/ninja/src/exit_status.h \
+ external/ninja/src/line_printer.h external/ninja/src/metrics.h \
+ external/ninja/src/build_log.h external/ninja/src/hash_map.h \
+ external/libcxx/include/unordered_map \
+ external/libcxx/include/__hash_table external/libcxx/include/cmath \
+ external/libcxx/include/math.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/math.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_val.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_valf.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_vall.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/inf.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/nan.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathdef.h \
+ prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathcalls.h \
+ external/ninja/src/deps_log.h external/ninja/src/clean.h \
+ external/ninja/src/debug_flags.h external/ninja/src/disk_interface.h \
+ external/ninja/src/graphviz.h external/ninja/src/manifest_parser.h \
+ external/ninja/src/lexer.h external/ninja/src/state.h \
+ external/ninja/src/version.h`)
+ tmpfile.Close()
+ if err != nil {
+ b.Fatal("Failed to write dep file:", err)
+ }
+ b.ResetTimer()
+
+ for n := 0; n < b.N; n++ {
+ depfile, err := ioutil.ReadFile(tmpfile.Name())
+ if err != nil {
+ b.Fatal("Failed to read dep file:", err)
+ }
+
+ _, err = Parse(tmpfile.Name(), bytes.NewBuffer(depfile))
+ if err != nil {
+ b.Fatal("Failed to parse:", err)
+ }
+ }
+}
+
+func TestDepPrint(t *testing.T) {
+ testCases := []struct {
+ name string
+ input Deps
+ output string
+ }{
+ {
+ name: "Empty",
+ input: Deps{
+ Output: "a",
+ },
+ output: "a:",
+ },
+ {
+ name: "Basic",
+ input: Deps{
+ Output: "a",
+ Inputs: []string{"b", "c"},
+ },
+ output: "a: b c",
+ },
+ {
+ name: "Escapes",
+ input: Deps{
+ Output: `\!\@#$\%\^\&\`,
+ },
+ output: `\\!\\@\#$$\\%\\^\\&\\:`,
+ },
+ {
+ name: "Spaces",
+ input: Deps{
+ Output: "a b",
+ Inputs: []string{"c d", "e f "},
+ },
+ output: `a\ b: c\ d e\ f\ `,
+ },
+ {
+ name: "SpecialChars",
+ input: Deps{
+ Output: "C:/Program Files (x86)/Microsoft crtdefs.h",
+ Inputs: []string{
+ "en@quot.header~",
+ "t+t-x!1",
+ "openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif",
+ "Fu\303\244ball",
+ },
+ },
+ output: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: en@quot.header~ t+t-x!1 openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif Fu` + "\303\244ball",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ out := tc.input.Print()
+ outStr := string(out)
+ want := tc.output + "\n"
+
+ if outStr != want {
+ t.Errorf("output doesn't match:\nwant:%q\n got:%q", want, outStr)
+ }
+ })
+ }
+}
diff --git a/cmd/dep_fixer/main.go b/cmd/dep_fixer/main.go
new file mode 100644
index 0000000..f94cf2f
--- /dev/null
+++ b/cmd/dep_fixer/main.go
@@ -0,0 +1,79 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This tool reads "make"-like dependency files, and outputs a canonical version
+// that can be used by ninja. Ninja doesn't support multiple output files (even
+// though it doesn't care what the output file is, or whether it matches what is
+// expected).
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+)
+
+func main() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "Usage: %s [-o <output>] <depfile.d> [<depfile.d>...]", os.Args[0])
+ flag.PrintDefaults()
+ }
+ output := flag.String("o", "", "Optional output file (defaults to rewriting source if necessary)")
+ flag.Parse()
+
+ if flag.NArg() < 1 {
+ log.Fatal("Expected at least one input file as an argument")
+ }
+
+ var mergedDeps *Deps
+ var firstInput []byte
+
+ for i, arg := range flag.Args() {
+ input, err := ioutil.ReadFile(arg)
+ if err != nil {
+ log.Fatalf("Error opening %q: %v", arg, err)
+ }
+
+ deps, err := Parse(arg, bytes.NewBuffer(append([]byte(nil), input...)))
+ if err != nil {
+ log.Fatalf("Failed to parse: %v", err)
+ }
+
+ if i == 0 {
+ mergedDeps = deps
+ firstInput = input
+ } else {
+ mergedDeps.Inputs = append(mergedDeps.Inputs, deps.Inputs...)
+ }
+ }
+
+ new := mergedDeps.Print()
+
+ if *output == "" || *output == flag.Arg(0) {
+ if !bytes.Equal(firstInput, new) {
+ err := ioutil.WriteFile(flag.Arg(0), new, 0666)
+ if err != nil {
+ log.Fatalf("Failed to write: %v", err)
+ }
+ }
+ } else {
+ err := ioutil.WriteFile(*output, new, 0666)
+ if err != nil {
+ log.Fatalf("Failed to write to %q: %v", *output, err)
+ }
+ }
+}
diff --git a/cmd/diff_target_files/Android.bp b/cmd/diff_target_files/Android.bp
new file mode 100644
index 0000000..5397f4b
--- /dev/null
+++ b/cmd/diff_target_files/Android.bp
@@ -0,0 +1,16 @@
+blueprint_go_binary {
+ name: "diff_target_files",
+ srcs: [
+ "compare.go",
+ "diff_target_files.go",
+ "glob.go",
+ "target_files.go",
+ "whitelist.go",
+ "zip_artifact.go",
+ ],
+ testSrcs: [
+ "compare_test.go",
+ "glob_test.go",
+ "whitelist_test.go",
+ ],
+}
diff --git a/cmd/diff_target_files/compare.go b/cmd/diff_target_files/compare.go
new file mode 100644
index 0000000..00cd9ca
--- /dev/null
+++ b/cmd/diff_target_files/compare.go
@@ -0,0 +1,133 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// compareTargetFiles takes two ZipArtifacts and compares the files they contain by examining
+// the path, size, and CRC of each file.
+func compareTargetFiles(priZip, refZip ZipArtifact, artifact string, whitelists []whitelist, filters []string) (zipDiff, error) {
+ priZipFiles, err := priZip.Files()
+ if err != nil {
+ return zipDiff{}, fmt.Errorf("error fetching target file lists from primary zip %v", err)
+ }
+
+ refZipFiles, err := refZip.Files()
+ if err != nil {
+ return zipDiff{}, fmt.Errorf("error fetching target file lists from reference zip %v", err)
+ }
+
+ priZipFiles, err = filterTargetZipFiles(priZipFiles, artifact, filters)
+ if err != nil {
+ return zipDiff{}, err
+ }
+
+ refZipFiles, err = filterTargetZipFiles(refZipFiles, artifact, filters)
+ if err != nil {
+ return zipDiff{}, err
+ }
+
+ // Compare the file lists from both builds
+ diff := diffTargetFilesLists(refZipFiles, priZipFiles)
+
+ return applyWhitelists(diff, whitelists)
+}
+
+// zipDiff contains the list of files that differ between two zip files.
+type zipDiff struct {
+ modified [][2]*ZipArtifactFile
+ onlyInA, onlyInB []*ZipArtifactFile
+}
+
+// String pretty-prints the list of files that differ between two zip files.
+func (d *zipDiff) String() string {
+ buf := &bytes.Buffer{}
+
+ must := func(n int, err error) {
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ var sizeChange int64
+
+ if len(d.modified) > 0 {
+ must(fmt.Fprintln(buf, "files modified:"))
+ for _, f := range d.modified {
+ must(fmt.Fprintf(buf, " %v (%v bytes -> %v bytes)\n", f[0].Name, f[0].UncompressedSize64, f[1].UncompressedSize64))
+ sizeChange += int64(f[1].UncompressedSize64) - int64(f[0].UncompressedSize64)
+ }
+ }
+
+ if len(d.onlyInA) > 0 {
+ must(fmt.Fprintln(buf, "files removed:"))
+ for _, f := range d.onlyInA {
+ must(fmt.Fprintf(buf, " - %v (%v bytes)\n", f.Name, f.UncompressedSize64))
+ sizeChange -= int64(f.UncompressedSize64)
+ }
+ }
+
+ if len(d.onlyInB) > 0 {
+ must(fmt.Fprintln(buf, "files added:"))
+ for _, f := range d.onlyInB {
+ must(fmt.Fprintf(buf, " + %v (%v bytes)\n", f.Name, f.UncompressedSize64))
+ sizeChange += int64(f.UncompressedSize64)
+ }
+ }
+
+ if len(d.modified) > 0 || len(d.onlyInA) > 0 || len(d.onlyInB) > 0 {
+ must(fmt.Fprintf(buf, "total size change: %v bytes\n", sizeChange))
+ }
+
+ return buf.String()
+}
+
+func diffTargetFilesLists(a, b []*ZipArtifactFile) zipDiff {
+ i := 0
+ j := 0
+
+ diff := zipDiff{}
+
+ for i < len(a) && j < len(b) {
+ if a[i].Name == b[j].Name {
+ if a[i].UncompressedSize64 != b[j].UncompressedSize64 || a[i].CRC32 != b[j].CRC32 {
+ diff.modified = append(diff.modified, [2]*ZipArtifactFile{a[i], b[j]})
+ }
+ i++
+ j++
+ } else if a[i].Name < b[j].Name {
+ // a[i] is not present in b
+ diff.onlyInA = append(diff.onlyInA, a[i])
+ i++
+ } else {
+ // b[j] is not present in a
+ diff.onlyInB = append(diff.onlyInB, b[j])
+ j++
+ }
+ }
+ for i < len(a) {
+ diff.onlyInA = append(diff.onlyInA, a[i])
+ i++
+ }
+ for j < len(b) {
+ diff.onlyInB = append(diff.onlyInB, b[j])
+ j++
+ }
+
+ return diff
+}
diff --git a/cmd/diff_target_files/compare_test.go b/cmd/diff_target_files/compare_test.go
new file mode 100644
index 0000000..9d3f8a5
--- /dev/null
+++ b/cmd/diff_target_files/compare_test.go
@@ -0,0 +1,131 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "archive/zip"
+ "reflect"
+ "testing"
+)
+
+func TestDiffTargetFilesLists(t *testing.T) {
+ zipArtifactFile := func(name string, crc32 uint32, size uint64) *ZipArtifactFile {
+ return &ZipArtifactFile{
+ File: &zip.File{
+ FileHeader: zip.FileHeader{
+ Name: name,
+ CRC32: crc32,
+ UncompressedSize64: size,
+ },
+ },
+ }
+ }
+ x0 := zipArtifactFile("x", 0, 0)
+ x1 := zipArtifactFile("x", 1, 0)
+ x2 := zipArtifactFile("x", 0, 2)
+ y0 := zipArtifactFile("y", 0, 0)
+ //y1 := zipArtifactFile("y", 1, 0)
+ //y2 := zipArtifactFile("y", 1, 2)
+ z0 := zipArtifactFile("z", 0, 0)
+ z1 := zipArtifactFile("z", 1, 0)
+ //z2 := zipArtifactFile("z", 1, 2)
+
+ testCases := []struct {
+ name string
+ a, b []*ZipArtifactFile
+ diff zipDiff
+ }{
+ {
+ name: "same",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, nil},
+ },
+ {
+ name: "first only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{y0, z0},
+ diff: zipDiff{nil, []*ZipArtifactFile{x0}, nil},
+ },
+ {
+ name: "middle only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, z0},
+ diff: zipDiff{nil, []*ZipArtifactFile{y0}, nil},
+ },
+ {
+ name: "last only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, y0},
+ diff: zipDiff{nil, []*ZipArtifactFile{z0}, nil},
+ },
+
+ {
+ name: "first only in b",
+ a: []*ZipArtifactFile{y0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{x0}},
+ },
+ {
+ name: "middle only in b",
+ a: []*ZipArtifactFile{x0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{y0}},
+ },
+ {
+ name: "last only in b",
+ a: []*ZipArtifactFile{x0, y0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{z0}},
+ },
+
+ {
+ name: "diff",
+ a: []*ZipArtifactFile{x0},
+ b: []*ZipArtifactFile{x1},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x1}}, nil, nil},
+ },
+ {
+ name: "diff plus unique last",
+ a: []*ZipArtifactFile{x0, y0},
+ b: []*ZipArtifactFile{x1, z0},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x1}}, []*ZipArtifactFile{y0}, []*ZipArtifactFile{z0}},
+ },
+ {
+ name: "diff plus unique first",
+ a: []*ZipArtifactFile{x0, z0},
+ b: []*ZipArtifactFile{y0, z1},
+ diff: zipDiff{[][2]*ZipArtifactFile{{z0, z1}}, []*ZipArtifactFile{x0}, []*ZipArtifactFile{y0}},
+ },
+ {
+ name: "diff size",
+ a: []*ZipArtifactFile{x0},
+ b: []*ZipArtifactFile{x2},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x2}}, nil, nil},
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ diff := diffTargetFilesLists(test.a, test.b)
+
+ if !reflect.DeepEqual(diff, test.diff) {
+
+ t.Errorf("diffTargetFilesLists = %v, %v, %v", diff.modified, diff.onlyInA, diff.onlyInB)
+ t.Errorf(" want %v, %v, %v", test.diff.modified, test.diff.onlyInA, test.diff.onlyInB)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/diff_target_files.go b/cmd/diff_target_files/diff_target_files.go
new file mode 100644
index 0000000..75bc8ee
--- /dev/null
+++ b/cmd/diff_target_files/diff_target_files.go
@@ -0,0 +1,82 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+)
+
+var (
+ whitelists = newMultiString("whitelist", "whitelist patterns in the form <pattern>[:<regex of line to ignore>]")
+ whitelistFiles = newMultiString("whitelist_file", "files containing whitelist definitions")
+
+ filters = newMultiString("filter", "filter patterns to apply to files in target-files.zip before comparing")
+)
+
+func newMultiString(name, usage string) *multiString {
+ var f multiString
+ flag.Var(&f, name, usage)
+ return &f
+}
+
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ flag.Parse()
+
+ if flag.NArg() != 2 {
+ fmt.Fprintf(os.Stderr, "Error, exactly two arguments are required\n")
+ os.Exit(1)
+ }
+
+ whitelists, err := parseWhitelists(*whitelists, *whitelistFiles)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error parsing whitelists: %v\n", err)
+ os.Exit(1)
+ }
+
+ priZip, err := NewLocalZipArtifact(flag.Arg(0))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening zip file %v: %v\n", flag.Arg(0), err)
+ os.Exit(1)
+ }
+ defer priZip.Close()
+
+ refZip, err := NewLocalZipArtifact(flag.Arg(1))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening zip file %v: %v\n", flag.Arg(1), err)
+ os.Exit(1)
+ }
+ defer refZip.Close()
+
+ diff, err := compareTargetFiles(priZip, refZip, targetFilesPattern, whitelists, *filters)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error comparing zip files: %v\n", err)
+ os.Exit(1)
+ }
+
+ fmt.Print(diff.String())
+
+ if len(diff.modified) > 0 || len(diff.onlyInA) > 0 || len(diff.onlyInB) > 0 {
+ fmt.Fprintln(os.Stderr, "differences found")
+ os.Exit(1)
+ }
+}
diff --git a/cmd/diff_target_files/glob.go b/cmd/diff_target_files/glob.go
new file mode 100644
index 0000000..ed91af7
--- /dev/null
+++ b/cmd/diff_target_files/glob.go
@@ -0,0 +1,81 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "errors"
+ "path/filepath"
+ "strings"
+)
+
+// Match returns true if name matches pattern using the same rules as filepath.Match, but supporting
+// recursive globs (**).
+func Match(pattern, name string) (bool, error) {
+ if filepath.Base(pattern) == "**" {
+ return false, errors.New("pattern has '**' as last path element")
+ }
+
+ patternDir := pattern[len(pattern)-1] == '/'
+ nameDir := name[len(name)-1] == '/'
+
+ if patternDir != nameDir {
+ return false, nil
+ }
+
+ if nameDir {
+ name = name[:len(name)-1]
+ pattern = pattern[:len(pattern)-1]
+ }
+
+ for {
+ var patternFile, nameFile string
+ pattern, patternFile = filepath.Dir(pattern), filepath.Base(pattern)
+
+ if patternFile == "**" {
+ if strings.Contains(pattern, "**") {
+ return false, errors.New("pattern contains multiple '**'")
+ }
+ // Test if the any prefix of name matches the part of the pattern before **
+ for {
+ if name == "." || name == "/" {
+ return name == pattern, nil
+ }
+ if match, err := filepath.Match(pattern, name); err != nil {
+ return false, err
+ } else if match {
+ return true, nil
+ }
+ name = filepath.Dir(name)
+ }
+ } else if strings.Contains(patternFile, "**") {
+ return false, errors.New("pattern contains other characters between '**' and path separator")
+ }
+
+ name, nameFile = filepath.Dir(name), filepath.Base(name)
+
+ if nameFile == "." && patternFile == "." {
+ return true, nil
+ } else if nameFile == "/" && patternFile == "/" {
+ return true, nil
+ } else if nameFile == "." || patternFile == "." || nameFile == "/" || patternFile == "/" {
+ return false, nil
+ }
+
+ match, err := filepath.Match(patternFile, nameFile)
+ if err != nil || !match {
+ return match, err
+ }
+ }
+}
diff --git a/cmd/diff_target_files/glob_test.go b/cmd/diff_target_files/glob_test.go
new file mode 100644
index 0000000..63df68d
--- /dev/null
+++ b/cmd/diff_target_files/glob_test.go
@@ -0,0 +1,158 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "testing"
+)
+
+func TestMatch(t *testing.T) {
+ testCases := []struct {
+ pattern, name string
+ match bool
+ }{
+ {"a/*", "b/", false},
+ {"a/*", "b/a", false},
+ {"a/*", "b/b/", false},
+ {"a/*", "b/b/c", false},
+ {"a/**/*", "b/", false},
+ {"a/**/*", "b/a", false},
+ {"a/**/*", "b/b/", false},
+ {"a/**/*", "b/b/c", false},
+
+ {"a/*", "a/", false},
+ {"a/*", "a/a", true},
+ {"a/*", "a/b/", false},
+ {"a/*", "a/b/c", false},
+
+ {"a/*/", "a/", false},
+ {"a/*/", "a/a", false},
+ {"a/*/", "a/b/", true},
+ {"a/*/", "a/b/c", false},
+
+ {"a/**/*", "a/", false},
+ {"a/**/*", "a/a", true},
+ {"a/**/*", "a/b/", false},
+ {"a/**/*", "a/b/c", true},
+
+ {"a/**/*/", "a/", false},
+ {"a/**/*/", "a/a", false},
+ {"a/**/*/", "a/b/", true},
+ {"a/**/*/", "a/b/c", false},
+
+ {"**/*", "a/", false},
+ {"**/*", "a/a", true},
+ {"**/*", "a/b/", false},
+ {"**/*", "a/b/c", true},
+
+ {"**/*/", "a/", true},
+ {"**/*/", "a/a", false},
+ {"**/*/", "a/b/", true},
+ {"**/*/", "a/b/c", false},
+
+ {`a/\*\*/\*`, `a/**/*`, true},
+ {`a/\*\*/\*`, `a/a/*`, false},
+ {`a/\*\*/\*`, `a/**/a`, false},
+ {`a/\*\*/\*`, `a/a/a`, false},
+
+ {`a/**/\*`, `a/**/*`, true},
+ {`a/**/\*`, `a/a/*`, true},
+ {`a/**/\*`, `a/**/a`, false},
+ {`a/**/\*`, `a/a/a`, false},
+
+ {`a/\*\*/*`, `a/**/*`, true},
+ {`a/\*\*/*`, `a/a/*`, false},
+ {`a/\*\*/*`, `a/**/a`, true},
+ {`a/\*\*/*`, `a/a/a`, false},
+
+ {`*/**/a`, `a/a/a`, true},
+ {`*/**/a`, `*/a/a`, true},
+ {`*/**/a`, `a/**/a`, true},
+ {`*/**/a`, `*/**/a`, true},
+
+ {`\*/\*\*/a`, `a/a/a`, false},
+ {`\*/\*\*/a`, `*/a/a`, false},
+ {`\*/\*\*/a`, `a/**/a`, false},
+ {`\*/\*\*/a`, `*/**/a`, true},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {`a/[a-c]`, `a/b`, true},
+ {`a/[abc]`, `a/b`, true},
+
+ {`a/\[abc]`, `a/b`, false},
+ {`a/\[abc]`, `a/[abc]`, true},
+
+ {`a/\[abc\]`, `a/b`, false},
+ {`a/\[abc\]`, `a/[abc]`, true},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {"/a/*", "/a/", false},
+ {"/a/*", "/a/a", true},
+ {"/a/*", "/a/b/", false},
+ {"/a/*", "/a/b/c", false},
+
+ {"/a/*/", "/a/", false},
+ {"/a/*/", "/a/a", false},
+ {"/a/*/", "/a/b/", true},
+ {"/a/*/", "/a/b/c", false},
+
+ {"/a/**/*", "/a/", false},
+ {"/a/**/*", "/a/a", true},
+ {"/a/**/*", "/a/b/", false},
+ {"/a/**/*", "/a/b/c", true},
+
+ {"/**/*", "/a/", false},
+ {"/**/*", "/a/a", true},
+ {"/**/*", "/a/b/", false},
+ {"/**/*", "/a/b/c", true},
+
+ {"/**/*/", "/a/", true},
+ {"/**/*/", "/a/a", false},
+ {"/**/*/", "/a/b/", true},
+ {"/**/*/", "/a/b/c", false},
+
+ {`a`, `/a`, false},
+ {`/a`, `a`, false},
+ {`*`, `/a`, false},
+ {`/*`, `a`, false},
+ {`**/*`, `/a`, false},
+ {`/**/*`, `a`, false},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.pattern+","+test.name, func(t *testing.T) {
+ match, err := Match(test.pattern, test.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if match != test.match {
+ t.Errorf("want: %v, got %v", test.match, match)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/known_nondeterminism.whitelist b/cmd/diff_target_files/known_nondeterminism.whitelist
new file mode 100644
index 0000000..6d71403
--- /dev/null
+++ b/cmd/diff_target_files/known_nondeterminism.whitelist
@@ -0,0 +1,10 @@
+// List of files that are known to be non-deterministic, along with the
+// bug number to tracking fixing the non-determinism.
+[
+ {
+ "Paths": [
+ // b/120039850
+ "system/framework/oat/*/services.art"
+ ]
+ }
+]
diff --git a/cmd/diff_target_files/props.whitelist b/cmd/diff_target_files/props.whitelist
new file mode 100644
index 0000000..9245b8b
--- /dev/null
+++ b/cmd/diff_target_files/props.whitelist
@@ -0,0 +1,18 @@
+[
+ // Ignore date, version and hostname properties in build.prop and prop.default files.
+ {
+ "Paths": [
+ "**/build.prop",
+ "**/prop.default"
+ ],
+ "IgnoreMatchingLines": [
+ "ro\\..*build\\.date=.*",
+ "ro\\..*build\\.date\\.utc=.*",
+ "ro\\..*build\\.version\\.incremental=.*",
+ "ro\\..*build\\.fingerprint=.*",
+ "ro\\.build\\.display\\.id=.*",
+ "ro\\.build\\.description=.*",
+ "ro\\.build\\.host=.*"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/cmd/diff_target_files/target_files.go b/cmd/diff_target_files/target_files.go
new file mode 100644
index 0000000..8705ca7
--- /dev/null
+++ b/cmd/diff_target_files/target_files.go
@@ -0,0 +1,86 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+const targetFilesPattern = "*-target_files-*.zip"
+
+var targetZipPartitions = []string{
+ "BOOT/RAMDISK/",
+ "BOOT/",
+ "DATA/",
+ "ODM/",
+ "OEM/",
+ "PRODUCT/",
+ "PRODUCT_SERVICES/",
+ "ROOT/",
+ "SYSTEM/",
+ "SYSTEM_OTHER/",
+ "VENDOR/",
+}
+
+var targetZipFilter = []string{
+ "IMAGES/",
+ "OTA/",
+ "META/",
+ "PREBUILT_IMAGES/",
+ "RADIO/",
+}
+
+func filterTargetZipFiles(files []*ZipArtifactFile, artifact string, patterns []string) ([]*ZipArtifactFile, error) {
+ var ret []*ZipArtifactFile
+outer:
+ for _, f := range files {
+ if f.FileInfo().IsDir() {
+ continue
+ }
+
+ if artifact == targetFilesPattern {
+ found := false
+ for _, p := range targetZipPartitions {
+ if strings.HasPrefix(f.Name, p) {
+ f.Name = strings.ToLower(p) + strings.TrimPrefix(f.Name, p)
+ found = true
+ }
+ }
+ for _, filter := range targetZipFilter {
+ if strings.HasPrefix(f.Name, filter) {
+ continue outer
+ }
+ }
+
+ if !found {
+ return nil, fmt.Errorf("unmatched prefix for %s", f.Name)
+ }
+ }
+
+ if patterns != nil {
+ for _, pattern := range patterns {
+ match, _ := Match(pattern, f.Name)
+ if match {
+ ret = append(ret, f)
+ }
+ }
+ } else {
+ ret = append(ret, f)
+ }
+ }
+
+ return ret, nil
+}
diff --git a/cmd/diff_target_files/whitelist.go b/cmd/diff_target_files/whitelist.go
new file mode 100644
index 0000000..f00fc1e
--- /dev/null
+++ b/cmd/diff_target_files/whitelist.go
@@ -0,0 +1,251 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "io"
+ "os"
+ "regexp"
+ "strings"
+ "unicode"
+)
+
+type jsonWhitelist struct {
+ Paths []string
+ IgnoreMatchingLines []string
+}
+
+type whitelist struct {
+ path string
+ ignoreMatchingLines []string
+}
+
+func parseWhitelists(whitelists []string, whitelistFiles []string) ([]whitelist, error) {
+ var ret []whitelist
+
+ add := func(path string, ignoreMatchingLines []string) {
+ for _, x := range ret {
+ if x.path == path {
+ x.ignoreMatchingLines = append(x.ignoreMatchingLines, ignoreMatchingLines...)
+ return
+ }
+ }
+
+ ret = append(ret, whitelist{
+ path: path,
+ ignoreMatchingLines: ignoreMatchingLines,
+ })
+ }
+
+ for _, file := range whitelistFiles {
+ newWhitelists, err := parseWhitelistFile(file)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, w := range newWhitelists {
+ add(w.path, w.ignoreMatchingLines)
+ }
+ }
+
+ for _, s := range whitelists {
+ colon := strings.IndexRune(s, ':')
+ var ignoreMatchingLines []string
+ if colon >= 0 {
+ ignoreMatchingLines = []string{s[colon+1:]}
+ }
+ add(s, ignoreMatchingLines)
+ }
+
+ return ret, nil
+}
+
+func parseWhitelistFile(file string) ([]whitelist, error) {
+ r, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+
+ d := json.NewDecoder(newJSONCommentStripper(r))
+
+ var jsonWhitelists []jsonWhitelist
+
+ err = d.Decode(&jsonWhitelists)
+
+ var whitelists []whitelist
+ for _, w := range jsonWhitelists {
+ for _, p := range w.Paths {
+ whitelists = append(whitelists, whitelist{
+ path: p,
+ ignoreMatchingLines: w.IgnoreMatchingLines,
+ })
+ }
+ }
+
+ return whitelists, err
+}
+
+func filterModifiedPaths(l [][2]*ZipArtifactFile, whitelists []whitelist) ([][2]*ZipArtifactFile, error) {
+outer:
+ for i := 0; i < len(l); i++ {
+ for _, w := range whitelists {
+ if match, err := Match(w.path, l[i][0].Name); err != nil {
+ return l, err
+ } else if match {
+ if match, err := diffIgnoringMatchingLines(l[i][0], l[i][1], w.ignoreMatchingLines); err != nil {
+ return l, err
+ } else if match || len(w.ignoreMatchingLines) == 0 {
+ l = append(l[:i], l[i+1:]...)
+ i--
+ }
+ continue outer
+ }
+ }
+ }
+
+ if len(l) == 0 {
+ l = nil
+ }
+
+ return l, nil
+}
+
+func filterNewPaths(l []*ZipArtifactFile, whitelists []whitelist) ([]*ZipArtifactFile, error) {
+outer:
+ for i := 0; i < len(l); i++ {
+ for _, w := range whitelists {
+ if match, err := Match(w.path, l[i].Name); err != nil {
+ return l, err
+ } else if match && len(w.ignoreMatchingLines) == 0 {
+ l = append(l[:i], l[i+1:]...)
+ i--
+ }
+ continue outer
+ }
+ }
+
+ if len(l) == 0 {
+ l = nil
+ }
+
+ return l, nil
+}
+
+func diffIgnoringMatchingLines(a *ZipArtifactFile, b *ZipArtifactFile, ignoreMatchingLines []string) (match bool, err error) {
+ lineMatchesIgnores := func(b []byte) (bool, error) {
+ for _, m := range ignoreMatchingLines {
+ if match, err := regexp.Match(m, b); err != nil {
+ return false, err
+ } else if match {
+ return match, nil
+ }
+ }
+ return false, nil
+ }
+
+ filter := func(z *ZipArtifactFile) ([]byte, error) {
+ var ret []byte
+
+ r, err := z.Open()
+ if err != nil {
+ return nil, err
+ }
+ s := bufio.NewScanner(r)
+
+ for s.Scan() {
+ if match, err := lineMatchesIgnores(s.Bytes()); err != nil {
+ return nil, err
+ } else if !match {
+ ret = append(ret, "\n"...)
+ ret = append(ret, s.Bytes()...)
+ }
+ }
+
+ return ret, nil
+ }
+
+ bufA, err := filter(a)
+ if err != nil {
+ return false, err
+ }
+ bufB, err := filter(b)
+ if err != nil {
+ return false, err
+ }
+
+ return bytes.Compare(bufA, bufB) == 0, nil
+}
+
+func applyWhitelists(diff zipDiff, whitelists []whitelist) (zipDiff, error) {
+ var err error
+
+ diff.modified, err = filterModifiedPaths(diff.modified, whitelists)
+ if err != nil {
+ return diff, err
+ }
+ diff.onlyInA, err = filterNewPaths(diff.onlyInA, whitelists)
+ if err != nil {
+ return diff, err
+ }
+ diff.onlyInB, err = filterNewPaths(diff.onlyInB, whitelists)
+ if err != nil {
+ return diff, err
+ }
+
+ return diff, nil
+}
+
+func newJSONCommentStripper(r io.Reader) *jsonCommentStripper {
+ return &jsonCommentStripper{
+ r: bufio.NewReader(r),
+ }
+}
+
+type jsonCommentStripper struct {
+ r *bufio.Reader
+ b []byte
+ err error
+}
+
+func (j *jsonCommentStripper) Read(buf []byte) (int, error) {
+ for len(j.b) == 0 {
+ if j.err != nil {
+ return 0, j.err
+ }
+
+ j.b, j.err = j.r.ReadBytes('\n')
+
+ if isComment(j.b) {
+ j.b = nil
+ }
+ }
+
+ n := copy(buf, j.b)
+ j.b = j.b[n:]
+ return n, nil
+}
+
+var commentPrefix = []byte("//")
+
+func isComment(b []byte) bool {
+ for len(b) > 0 && unicode.IsSpace(rune(b[0])) {
+ b = b[1:]
+ }
+ return bytes.HasPrefix(b, commentPrefix)
+}
diff --git a/cmd/diff_target_files/whitelist_test.go b/cmd/diff_target_files/whitelist_test.go
new file mode 100644
index 0000000..4b19fdd
--- /dev/null
+++ b/cmd/diff_target_files/whitelist_test.go
@@ -0,0 +1,126 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "archive/zip"
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+func bytesToZipArtifactFile(name string, data []byte) *ZipArtifactFile {
+ buf := &bytes.Buffer{}
+ w := zip.NewWriter(buf)
+ f, err := w.Create(name)
+ if err != nil {
+ panic(err)
+ }
+ _, err = f.Write(data)
+ if err != nil {
+ panic(err)
+ }
+
+ w.Close()
+
+ r, err := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ if err != nil {
+ panic(err)
+ }
+
+ return &ZipArtifactFile{r.File[0]}
+}
+
+var f1a = bytesToZipArtifactFile("dir/f1", []byte(`
+a
+foo: bar
+c
+`))
+
+var f1b = bytesToZipArtifactFile("dir/f1", []byte(`
+a
+foo: baz
+c
+`))
+
+var f2 = bytesToZipArtifactFile("dir/f2", nil)
+
+func Test_applyWhitelists(t *testing.T) {
+ type args struct {
+ diff zipDiff
+ whitelists []whitelist
+ }
+ tests := []struct {
+ name string
+ args args
+ want zipDiff
+ wantErr bool
+ }{
+ {
+ name: "simple",
+ args: args{
+ diff: zipDiff{
+ onlyInA: []*ZipArtifactFile{f1a, f2},
+ },
+ whitelists: []whitelist{{path: "dir/f1"}},
+ },
+ want: zipDiff{
+ onlyInA: []*ZipArtifactFile{f2},
+ },
+ },
+ {
+ name: "glob",
+ args: args{
+ diff: zipDiff{
+ onlyInA: []*ZipArtifactFile{f1a, f2},
+ },
+ whitelists: []whitelist{{path: "dir/*"}},
+ },
+ want: zipDiff{},
+ },
+ {
+ name: "modified",
+ args: args{
+ diff: zipDiff{
+ modified: [][2]*ZipArtifactFile{{f1a, f1b}},
+ },
+ whitelists: []whitelist{{path: "dir/*"}},
+ },
+ want: zipDiff{},
+ },
+ {
+ name: "matching lines",
+ args: args{
+ diff: zipDiff{
+ modified: [][2]*ZipArtifactFile{{f1a, f1b}},
+ },
+ whitelists: []whitelist{{path: "dir/*", ignoreMatchingLines: []string{"foo: .*"}}},
+ },
+ want: zipDiff{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := applyWhitelists(tt.args.diff, tt.args.whitelists)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("applyWhitelists() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("applyWhitelists() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/zip_artifact.go b/cmd/diff_target_files/zip_artifact.go
new file mode 100644
index 0000000..08ce889
--- /dev/null
+++ b/cmd/diff_target_files/zip_artifact.go
@@ -0,0 +1,174 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "archive/zip"
+ "context"
+ "fmt"
+ "hash/crc32"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+)
+
+// ZipArtifact represents a zip file that may be local or remote.
+type ZipArtifact interface {
+ // Files returns the list of files contained in the zip file.
+ Files() ([]*ZipArtifactFile, error)
+
+ // Close closes the zip file artifact.
+ Close()
+}
+
+// localZipArtifact is a handle to a local zip file artifact.
+type localZipArtifact struct {
+ zr *zip.ReadCloser
+ files []*ZipArtifactFile
+}
+
+// NewLocalZipArtifact returns a ZipArtifact for a local zip file..
+func NewLocalZipArtifact(name string) (ZipArtifact, error) {
+ zr, err := zip.OpenReader(name)
+ if err != nil {
+ return nil, err
+ }
+
+ var files []*ZipArtifactFile
+ for _, zf := range zr.File {
+ files = append(files, &ZipArtifactFile{zf})
+ }
+
+ return &localZipArtifact{
+ zr: zr,
+ files: files,
+ }, nil
+}
+
+// Files returns the list of files contained in the local zip file artifact.
+func (z *localZipArtifact) Files() ([]*ZipArtifactFile, error) {
+ return z.files, nil
+}
+
+// Close closes the buffered reader of the local zip file artifact.
+func (z *localZipArtifact) Close() {
+ z.zr.Close()
+}
+
+// ZipArtifactFile contains a zip.File handle to the data inside the remote *-target_files-*.zip
+// build artifact.
+type ZipArtifactFile struct {
+ *zip.File
+}
+
+// Extract begins extract a file from inside a ZipArtifact. It returns an
+// ExtractedZipArtifactFile handle.
+func (zf *ZipArtifactFile) Extract(ctx context.Context, dir string,
+ limiter chan bool) *ExtractedZipArtifactFile {
+
+ d := &ExtractedZipArtifactFile{
+ initCh: make(chan struct{}),
+ }
+
+ go func() {
+ defer close(d.initCh)
+ limiter <- true
+ defer func() { <-limiter }()
+
+ zr, err := zf.Open()
+ if err != nil {
+ d.err = err
+ return
+ }
+ defer zr.Close()
+
+ crc := crc32.NewIEEE()
+ r := io.TeeReader(zr, crc)
+
+ if filepath.Clean(zf.Name) != zf.Name {
+ d.err = fmt.Errorf("invalid filename %q", zf.Name)
+ return
+ }
+ path := filepath.Join(dir, zf.Name)
+
+ err = os.MkdirAll(filepath.Dir(path), 0777)
+ if err != nil {
+ d.err = err
+ return
+ }
+
+ err = os.Remove(path)
+ if err != nil && !os.IsNotExist(err) {
+ d.err = err
+ return
+ }
+
+ if zf.Mode().IsRegular() {
+ w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, zf.Mode())
+ if err != nil {
+ d.err = err
+ return
+ }
+ defer w.Close()
+
+ _, err = io.Copy(w, r)
+ if err != nil {
+ d.err = err
+ return
+ }
+ } else if zf.Mode()&os.ModeSymlink != 0 {
+ target, err := ioutil.ReadAll(r)
+ if err != nil {
+ d.err = err
+ return
+ }
+
+ err = os.Symlink(string(target), path)
+ if err != nil {
+ d.err = err
+ return
+ }
+ } else {
+ d.err = fmt.Errorf("unknown mode %q", zf.Mode())
+ return
+ }
+
+ if crc.Sum32() != zf.CRC32 {
+ d.err = fmt.Errorf("crc mismatch for %v", zf.Name)
+ return
+ }
+
+ d.path = path
+ }()
+
+ return d
+}
+
+// ExtractedZipArtifactFile is a handle to a downloaded file from a remoteZipArtifact. The download
+// may still be in progress, and will be complete with Path() returns.
+type ExtractedZipArtifactFile struct {
+ initCh chan struct{}
+ err error
+
+ path string
+}
+
+// Path returns the path to the downloaded file and any errors that occurred during the download.
+// It will block until the download is complete.
+func (d *ExtractedZipArtifactFile) Path() (string, error) {
+ <-d.initCh
+ return d.path, d.err
+}
diff --git a/cmd/extract_apks/Android.bp b/cmd/extract_apks/Android.bp
new file mode 100644
index 0000000..90548cd
--- /dev/null
+++ b/cmd/extract_apks/Android.bp
@@ -0,0 +1,21 @@
+blueprint_go_binary {
+ name: "extract_apks",
+ srcs: ["main.go"],
+ deps: [
+ "android-archive-zip",
+ "golang-protobuf-proto",
+ "soong-cmd-extract_apks-proto",
+ ],
+ testSrcs: ["main_test.go"]
+}
+
+bootstrap_go_package {
+ name: "soong-cmd-extract_apks-proto",
+ pkgPath: "android/soong/cmd/extract_apks/bundle_proto",
+ deps: ["golang-protobuf-proto"],
+ srcs: [
+ "bundle_proto/commands.pb.go",
+ "bundle_proto/config.pb.go",
+ "bundle_proto/targeting.pb.go",
+ ],
+}
diff --git a/cmd/extract_apks/bundle_proto/commands.pb.go b/cmd/extract_apks/bundle_proto/commands.pb.go
new file mode 100644
index 0000000..bbf3314
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/commands.pb.go
@@ -0,0 +1,1033 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: commands.proto
+
+package android_bundle_proto
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type DeliveryType int32
+
+const (
+ DeliveryType_UNKNOWN_DELIVERY_TYPE DeliveryType = 0
+ DeliveryType_INSTALL_TIME DeliveryType = 1
+ DeliveryType_ON_DEMAND DeliveryType = 2
+ DeliveryType_FAST_FOLLOW DeliveryType = 3
+)
+
+var DeliveryType_name = map[int32]string{
+ 0: "UNKNOWN_DELIVERY_TYPE",
+ 1: "INSTALL_TIME",
+ 2: "ON_DEMAND",
+ 3: "FAST_FOLLOW",
+}
+
+var DeliveryType_value = map[string]int32{
+ "UNKNOWN_DELIVERY_TYPE": 0,
+ "INSTALL_TIME": 1,
+ "ON_DEMAND": 2,
+ "FAST_FOLLOW": 3,
+}
+
+func (x DeliveryType) String() string {
+ return proto.EnumName(DeliveryType_name, int32(x))
+}
+
+func (DeliveryType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{0}
+}
+
+type SystemApkMetadata_SystemApkType int32
+
+const (
+ SystemApkMetadata_UNSPECIFIED_VALUE SystemApkMetadata_SystemApkType = 0
+ // Uncompressed APK for system image.
+ SystemApkMetadata_SYSTEM SystemApkMetadata_SystemApkType = 1
+ // Stub APK for compressed APK in the system image
+ // (contains only android manifest).
+ SystemApkMetadata_SYSTEM_STUB SystemApkMetadata_SystemApkType = 2
+ // Compressed APK for system image.
+ SystemApkMetadata_SYSTEM_COMPRESSED SystemApkMetadata_SystemApkType = 3
+)
+
+var SystemApkMetadata_SystemApkType_name = map[int32]string{
+ 0: "UNSPECIFIED_VALUE",
+ 1: "SYSTEM",
+ 2: "SYSTEM_STUB",
+ 3: "SYSTEM_COMPRESSED",
+}
+
+var SystemApkMetadata_SystemApkType_value = map[string]int32{
+ "UNSPECIFIED_VALUE": 0,
+ "SYSTEM": 1,
+ "SYSTEM_STUB": 2,
+ "SYSTEM_COMPRESSED": 3,
+}
+
+func (x SystemApkMetadata_SystemApkType) String() string {
+ return proto.EnumName(SystemApkMetadata_SystemApkType_name, int32(x))
+}
+
+func (SystemApkMetadata_SystemApkType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{10, 0}
+}
+
+// Describes the output of the "build-apks" command.
+type BuildApksResult struct {
+ // The package name of this app.
+ PackageName string `protobuf:"bytes,4,opt,name=package_name,json=packageName,proto3" json:"package_name,omitempty"`
+ // List of the created variants.
+ Variant []*Variant `protobuf:"bytes,1,rep,name=variant,proto3" json:"variant,omitempty"`
+ // Metadata about BundleTool used to build the APKs.
+ Bundletool *Bundletool `protobuf:"bytes,2,opt,name=bundletool,proto3" json:"bundletool,omitempty"`
+ // List of the created asset slices.
+ AssetSliceSet []*AssetSliceSet `protobuf:"bytes,3,rep,name=asset_slice_set,json=assetSliceSet,proto3" json:"asset_slice_set,omitempty"`
+ // Information about local testing mode.
+ LocalTestingInfo *LocalTestingInfo `protobuf:"bytes,5,opt,name=local_testing_info,json=localTestingInfo,proto3" json:"local_testing_info,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BuildApksResult) Reset() { *m = BuildApksResult{} }
+func (m *BuildApksResult) String() string { return proto.CompactTextString(m) }
+func (*BuildApksResult) ProtoMessage() {}
+func (*BuildApksResult) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{0}
+}
+
+func (m *BuildApksResult) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BuildApksResult.Unmarshal(m, b)
+}
+func (m *BuildApksResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BuildApksResult.Marshal(b, m, deterministic)
+}
+func (m *BuildApksResult) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BuildApksResult.Merge(m, src)
+}
+func (m *BuildApksResult) XXX_Size() int {
+ return xxx_messageInfo_BuildApksResult.Size(m)
+}
+func (m *BuildApksResult) XXX_DiscardUnknown() {
+ xxx_messageInfo_BuildApksResult.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildApksResult proto.InternalMessageInfo
+
+func (m *BuildApksResult) GetPackageName() string {
+ if m != nil {
+ return m.PackageName
+ }
+ return ""
+}
+
+func (m *BuildApksResult) GetVariant() []*Variant {
+ if m != nil {
+ return m.Variant
+ }
+ return nil
+}
+
+func (m *BuildApksResult) GetBundletool() *Bundletool {
+ if m != nil {
+ return m.Bundletool
+ }
+ return nil
+}
+
+func (m *BuildApksResult) GetAssetSliceSet() []*AssetSliceSet {
+ if m != nil {
+ return m.AssetSliceSet
+ }
+ return nil
+}
+
+func (m *BuildApksResult) GetLocalTestingInfo() *LocalTestingInfo {
+ if m != nil {
+ return m.LocalTestingInfo
+ }
+ return nil
+}
+
+// Variant is a group of APKs that covers a part of the device configuration
+// space. APKs from multiple variants are never combined on one device.
+type Variant struct {
+ // Variant-level targeting.
+ // This targeting is fairly high-level and each APK has its own targeting as
+ // well.
+ Targeting *VariantTargeting `protobuf:"bytes,1,opt,name=targeting,proto3" json:"targeting,omitempty"`
+ // Set of APKs, one set per module.
+ ApkSet []*ApkSet `protobuf:"bytes,2,rep,name=apk_set,json=apkSet,proto3" json:"apk_set,omitempty"`
+ // Number of the variant, starting at 0 (unless overridden).
+ // A device will receive APKs from the first variant that matches the device
+ // configuration, with higher variant numbers having priority over lower
+ // variant numbers.
+ VariantNumber uint32 `protobuf:"varint,3,opt,name=variant_number,json=variantNumber,proto3" json:"variant_number,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Variant) Reset() { *m = Variant{} }
+func (m *Variant) String() string { return proto.CompactTextString(m) }
+func (*Variant) ProtoMessage() {}
+func (*Variant) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{1}
+}
+
+func (m *Variant) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Variant.Unmarshal(m, b)
+}
+func (m *Variant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Variant.Marshal(b, m, deterministic)
+}
+func (m *Variant) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Variant.Merge(m, src)
+}
+func (m *Variant) XXX_Size() int {
+ return xxx_messageInfo_Variant.Size(m)
+}
+func (m *Variant) XXX_DiscardUnknown() {
+ xxx_messageInfo_Variant.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Variant proto.InternalMessageInfo
+
+func (m *Variant) GetTargeting() *VariantTargeting {
+ if m != nil {
+ return m.Targeting
+ }
+ return nil
+}
+
+func (m *Variant) GetApkSet() []*ApkSet {
+ if m != nil {
+ return m.ApkSet
+ }
+ return nil
+}
+
+func (m *Variant) GetVariantNumber() uint32 {
+ if m != nil {
+ return m.VariantNumber
+ }
+ return 0
+}
+
+// Represents a module.
+// For pre-L devices multiple modules (possibly all) may be merged into one.
+type ApkSet struct {
+ ModuleMetadata *ModuleMetadata `protobuf:"bytes,1,opt,name=module_metadata,json=moduleMetadata,proto3" json:"module_metadata,omitempty"`
+ // APKs.
+ ApkDescription []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApkSet) Reset() { *m = ApkSet{} }
+func (m *ApkSet) String() string { return proto.CompactTextString(m) }
+func (*ApkSet) ProtoMessage() {}
+func (*ApkSet) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{2}
+}
+
+func (m *ApkSet) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApkSet.Unmarshal(m, b)
+}
+func (m *ApkSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApkSet.Marshal(b, m, deterministic)
+}
+func (m *ApkSet) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApkSet.Merge(m, src)
+}
+func (m *ApkSet) XXX_Size() int {
+ return xxx_messageInfo_ApkSet.Size(m)
+}
+func (m *ApkSet) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApkSet.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApkSet proto.InternalMessageInfo
+
+func (m *ApkSet) GetModuleMetadata() *ModuleMetadata {
+ if m != nil {
+ return m.ModuleMetadata
+ }
+ return nil
+}
+
+func (m *ApkSet) GetApkDescription() []*ApkDescription {
+ if m != nil {
+ return m.ApkDescription
+ }
+ return nil
+}
+
+type ModuleMetadata struct {
+ // Module name.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // Indicates the delivery type (e.g. on-demand) of the module.
+ DeliveryType DeliveryType `protobuf:"varint,6,opt,name=delivery_type,json=deliveryType,proto3,enum=android.bundle.DeliveryType" json:"delivery_type,omitempty"`
+ // Indicates whether this module is marked "instant".
+ IsInstant bool `protobuf:"varint,3,opt,name=is_instant,json=isInstant,proto3" json:"is_instant,omitempty"`
+ // Names of the modules that this module directly depends on.
+ // Each module implicitly depends on the base module.
+ Dependencies []string `protobuf:"bytes,4,rep,name=dependencies,proto3" json:"dependencies,omitempty"`
+ // The targeting that makes a conditional module installed.
+ // Relevant only for Split APKs.
+ Targeting *ModuleTargeting `protobuf:"bytes,5,opt,name=targeting,proto3" json:"targeting,omitempty"`
+ // Deprecated. Please use delivery_type.
+ OnDemandDeprecated bool `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ModuleMetadata) Reset() { *m = ModuleMetadata{} }
+func (m *ModuleMetadata) String() string { return proto.CompactTextString(m) }
+func (*ModuleMetadata) ProtoMessage() {}
+func (*ModuleMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{3}
+}
+
+func (m *ModuleMetadata) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ModuleMetadata.Unmarshal(m, b)
+}
+func (m *ModuleMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ModuleMetadata.Marshal(b, m, deterministic)
+}
+func (m *ModuleMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ModuleMetadata.Merge(m, src)
+}
+func (m *ModuleMetadata) XXX_Size() int {
+ return xxx_messageInfo_ModuleMetadata.Size(m)
+}
+func (m *ModuleMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_ModuleMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ModuleMetadata proto.InternalMessageInfo
+
+func (m *ModuleMetadata) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *ModuleMetadata) GetDeliveryType() DeliveryType {
+ if m != nil {
+ return m.DeliveryType
+ }
+ return DeliveryType_UNKNOWN_DELIVERY_TYPE
+}
+
+func (m *ModuleMetadata) GetIsInstant() bool {
+ if m != nil {
+ return m.IsInstant
+ }
+ return false
+}
+
+func (m *ModuleMetadata) GetDependencies() []string {
+ if m != nil {
+ return m.Dependencies
+ }
+ return nil
+}
+
+func (m *ModuleMetadata) GetTargeting() *ModuleTargeting {
+ if m != nil {
+ return m.Targeting
+ }
+ return nil
+}
+
+// Deprecated: Do not use.
+func (m *ModuleMetadata) GetOnDemandDeprecated() bool {
+ if m != nil {
+ return m.OnDemandDeprecated
+ }
+ return false
+}
+
+// Set of asset slices belonging to a single asset module.
+type AssetSliceSet struct {
+ // Module level metadata.
+ AssetModuleMetadata *AssetModuleMetadata `protobuf:"bytes,1,opt,name=asset_module_metadata,json=assetModuleMetadata,proto3" json:"asset_module_metadata,omitempty"`
+ // Asset slices.
+ ApkDescription []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *AssetSliceSet) Reset() { *m = AssetSliceSet{} }
+func (m *AssetSliceSet) String() string { return proto.CompactTextString(m) }
+func (*AssetSliceSet) ProtoMessage() {}
+func (*AssetSliceSet) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{4}
+}
+
+func (m *AssetSliceSet) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_AssetSliceSet.Unmarshal(m, b)
+}
+func (m *AssetSliceSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_AssetSliceSet.Marshal(b, m, deterministic)
+}
+func (m *AssetSliceSet) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_AssetSliceSet.Merge(m, src)
+}
+func (m *AssetSliceSet) XXX_Size() int {
+ return xxx_messageInfo_AssetSliceSet.Size(m)
+}
+func (m *AssetSliceSet) XXX_DiscardUnknown() {
+ xxx_messageInfo_AssetSliceSet.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetSliceSet proto.InternalMessageInfo
+
+func (m *AssetSliceSet) GetAssetModuleMetadata() *AssetModuleMetadata {
+ if m != nil {
+ return m.AssetModuleMetadata
+ }
+ return nil
+}
+
+func (m *AssetSliceSet) GetApkDescription() []*ApkDescription {
+ if m != nil {
+ return m.ApkDescription
+ }
+ return nil
+}
+
+type AssetModuleMetadata struct {
+ // Module name.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // Indicates the delivery type for persistent install.
+ DeliveryType DeliveryType `protobuf:"varint,4,opt,name=delivery_type,json=deliveryType,proto3,enum=android.bundle.DeliveryType" json:"delivery_type,omitempty"`
+ // Metadata for instant installs.
+ InstantMetadata *InstantMetadata `protobuf:"bytes,3,opt,name=instant_metadata,json=instantMetadata,proto3" json:"instant_metadata,omitempty"`
+ // Deprecated. Use delivery_type.
+ OnDemandDeprecated bool `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *AssetModuleMetadata) Reset() { *m = AssetModuleMetadata{} }
+func (m *AssetModuleMetadata) String() string { return proto.CompactTextString(m) }
+func (*AssetModuleMetadata) ProtoMessage() {}
+func (*AssetModuleMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{5}
+}
+
+func (m *AssetModuleMetadata) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_AssetModuleMetadata.Unmarshal(m, b)
+}
+func (m *AssetModuleMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_AssetModuleMetadata.Marshal(b, m, deterministic)
+}
+func (m *AssetModuleMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_AssetModuleMetadata.Merge(m, src)
+}
+func (m *AssetModuleMetadata) XXX_Size() int {
+ return xxx_messageInfo_AssetModuleMetadata.Size(m)
+}
+func (m *AssetModuleMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_AssetModuleMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetModuleMetadata proto.InternalMessageInfo
+
+func (m *AssetModuleMetadata) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *AssetModuleMetadata) GetDeliveryType() DeliveryType {
+ if m != nil {
+ return m.DeliveryType
+ }
+ return DeliveryType_UNKNOWN_DELIVERY_TYPE
+}
+
+func (m *AssetModuleMetadata) GetInstantMetadata() *InstantMetadata {
+ if m != nil {
+ return m.InstantMetadata
+ }
+ return nil
+}
+
+// Deprecated: Do not use.
+func (m *AssetModuleMetadata) GetOnDemandDeprecated() bool {
+ if m != nil {
+ return m.OnDemandDeprecated
+ }
+ return false
+}
+
+type InstantMetadata struct {
+ // Indicates whether this module is marked "instant".
+ IsInstant bool `protobuf:"varint,1,opt,name=is_instant,json=isInstant,proto3" json:"is_instant,omitempty"`
+ // Indicates the delivery type for instant install.
+ DeliveryType DeliveryType `protobuf:"varint,3,opt,name=delivery_type,json=deliveryType,proto3,enum=android.bundle.DeliveryType" json:"delivery_type,omitempty"`
+ // Deprecated. Use delivery_type.
+ OnDemandDeprecated bool `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *InstantMetadata) Reset() { *m = InstantMetadata{} }
+func (m *InstantMetadata) String() string { return proto.CompactTextString(m) }
+func (*InstantMetadata) ProtoMessage() {}
+func (*InstantMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{6}
+}
+
+func (m *InstantMetadata) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_InstantMetadata.Unmarshal(m, b)
+}
+func (m *InstantMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_InstantMetadata.Marshal(b, m, deterministic)
+}
+func (m *InstantMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_InstantMetadata.Merge(m, src)
+}
+func (m *InstantMetadata) XXX_Size() int {
+ return xxx_messageInfo_InstantMetadata.Size(m)
+}
+func (m *InstantMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_InstantMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_InstantMetadata proto.InternalMessageInfo
+
+func (m *InstantMetadata) GetIsInstant() bool {
+ if m != nil {
+ return m.IsInstant
+ }
+ return false
+}
+
+func (m *InstantMetadata) GetDeliveryType() DeliveryType {
+ if m != nil {
+ return m.DeliveryType
+ }
+ return DeliveryType_UNKNOWN_DELIVERY_TYPE
+}
+
+// Deprecated: Do not use.
+func (m *InstantMetadata) GetOnDemandDeprecated() bool {
+ if m != nil {
+ return m.OnDemandDeprecated
+ }
+ return false
+}
+
+type ApkDescription struct {
+ Targeting *ApkTargeting `protobuf:"bytes,1,opt,name=targeting,proto3" json:"targeting,omitempty"`
+ // Path to the APK file.
+ // BEGIN-INTERNAL
+ // The path may be a blobkey if the proto is not constructed by bundletool.
+ // END-INTERNAL
+ Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
+ // Types that are valid to be assigned to ApkMetadataOneofValue:
+ // *ApkDescription_SplitApkMetadata
+ // *ApkDescription_StandaloneApkMetadata
+ // *ApkDescription_InstantApkMetadata
+ // *ApkDescription_SystemApkMetadata
+ // *ApkDescription_AssetSliceMetadata
+ // *ApkDescription_ApexApkMetadata
+ ApkMetadataOneofValue isApkDescription_ApkMetadataOneofValue `protobuf_oneof:"apk_metadata_oneof_value"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApkDescription) Reset() { *m = ApkDescription{} }
+func (m *ApkDescription) String() string { return proto.CompactTextString(m) }
+func (*ApkDescription) ProtoMessage() {}
+func (*ApkDescription) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{7}
+}
+
+func (m *ApkDescription) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApkDescription.Unmarshal(m, b)
+}
+func (m *ApkDescription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApkDescription.Marshal(b, m, deterministic)
+}
+func (m *ApkDescription) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApkDescription.Merge(m, src)
+}
+func (m *ApkDescription) XXX_Size() int {
+ return xxx_messageInfo_ApkDescription.Size(m)
+}
+func (m *ApkDescription) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApkDescription.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApkDescription proto.InternalMessageInfo
+
+func (m *ApkDescription) GetTargeting() *ApkTargeting {
+ if m != nil {
+ return m.Targeting
+ }
+ return nil
+}
+
+func (m *ApkDescription) GetPath() string {
+ if m != nil {
+ return m.Path
+ }
+ return ""
+}
+
+type isApkDescription_ApkMetadataOneofValue interface {
+ isApkDescription_ApkMetadataOneofValue()
+}
+
+type ApkDescription_SplitApkMetadata struct {
+ SplitApkMetadata *SplitApkMetadata `protobuf:"bytes,3,opt,name=split_apk_metadata,json=splitApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_StandaloneApkMetadata struct {
+ StandaloneApkMetadata *StandaloneApkMetadata `protobuf:"bytes,4,opt,name=standalone_apk_metadata,json=standaloneApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_InstantApkMetadata struct {
+ InstantApkMetadata *SplitApkMetadata `protobuf:"bytes,5,opt,name=instant_apk_metadata,json=instantApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_SystemApkMetadata struct {
+ SystemApkMetadata *SystemApkMetadata `protobuf:"bytes,6,opt,name=system_apk_metadata,json=systemApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_AssetSliceMetadata struct {
+ AssetSliceMetadata *SplitApkMetadata `protobuf:"bytes,7,opt,name=asset_slice_metadata,json=assetSliceMetadata,proto3,oneof"`
+}
+
+type ApkDescription_ApexApkMetadata struct {
+ ApexApkMetadata *ApexApkMetadata `protobuf:"bytes,8,opt,name=apex_apk_metadata,json=apexApkMetadata,proto3,oneof"`
+}
+
+func (*ApkDescription_SplitApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_StandaloneApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_InstantApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_SystemApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_AssetSliceMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_ApexApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (m *ApkDescription) GetApkMetadataOneofValue() isApkDescription_ApkMetadataOneofValue {
+ if m != nil {
+ return m.ApkMetadataOneofValue
+ }
+ return nil
+}
+
+func (m *ApkDescription) GetSplitApkMetadata() *SplitApkMetadata {
+ if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_SplitApkMetadata); ok {
+ return x.SplitApkMetadata
+ }
+ return nil
+}
+
+func (m *ApkDescription) GetStandaloneApkMetadata() *StandaloneApkMetadata {
+ if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_StandaloneApkMetadata); ok {
+ return x.StandaloneApkMetadata
+ }
+ return nil
+}
+
+func (m *ApkDescription) GetInstantApkMetadata() *SplitApkMetadata {
+ if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_InstantApkMetadata); ok {
+ return x.InstantApkMetadata
+ }
+ return nil
+}
+
+func (m *ApkDescription) GetSystemApkMetadata() *SystemApkMetadata {
+ if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_SystemApkMetadata); ok {
+ return x.SystemApkMetadata
+ }
+ return nil
+}
+
+func (m *ApkDescription) GetAssetSliceMetadata() *SplitApkMetadata {
+ if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_AssetSliceMetadata); ok {
+ return x.AssetSliceMetadata
+ }
+ return nil
+}
+
+func (m *ApkDescription) GetApexApkMetadata() *ApexApkMetadata {
+ if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_ApexApkMetadata); ok {
+ return x.ApexApkMetadata
+ }
+ return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*ApkDescription) XXX_OneofWrappers() []interface{} {
+ return []interface{}{
+ (*ApkDescription_SplitApkMetadata)(nil),
+ (*ApkDescription_StandaloneApkMetadata)(nil),
+ (*ApkDescription_InstantApkMetadata)(nil),
+ (*ApkDescription_SystemApkMetadata)(nil),
+ (*ApkDescription_AssetSliceMetadata)(nil),
+ (*ApkDescription_ApexApkMetadata)(nil),
+ }
+}
+
+// Holds data specific to Split APKs.
+type SplitApkMetadata struct {
+ SplitId string `protobuf:"bytes,1,opt,name=split_id,json=splitId,proto3" json:"split_id,omitempty"`
+ // Indicates whether this APK is the master split of the module.
+ IsMasterSplit bool `protobuf:"varint,2,opt,name=is_master_split,json=isMasterSplit,proto3" json:"is_master_split,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SplitApkMetadata) Reset() { *m = SplitApkMetadata{} }
+func (m *SplitApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*SplitApkMetadata) ProtoMessage() {}
+func (*SplitApkMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{8}
+}
+
+func (m *SplitApkMetadata) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SplitApkMetadata.Unmarshal(m, b)
+}
+func (m *SplitApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SplitApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *SplitApkMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SplitApkMetadata.Merge(m, src)
+}
+func (m *SplitApkMetadata) XXX_Size() int {
+ return xxx_messageInfo_SplitApkMetadata.Size(m)
+}
+func (m *SplitApkMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_SplitApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SplitApkMetadata proto.InternalMessageInfo
+
+func (m *SplitApkMetadata) GetSplitId() string {
+ if m != nil {
+ return m.SplitId
+ }
+ return ""
+}
+
+func (m *SplitApkMetadata) GetIsMasterSplit() bool {
+ if m != nil {
+ return m.IsMasterSplit
+ }
+ return false
+}
+
+// Holds data specific to Standalone APKs.
+type StandaloneApkMetadata struct {
+ // Names of the modules fused in this standalone APK.
+ FusedModuleName []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *StandaloneApkMetadata) Reset() { *m = StandaloneApkMetadata{} }
+func (m *StandaloneApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*StandaloneApkMetadata) ProtoMessage() {}
+func (*StandaloneApkMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{9}
+}
+
+func (m *StandaloneApkMetadata) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_StandaloneApkMetadata.Unmarshal(m, b)
+}
+func (m *StandaloneApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_StandaloneApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *StandaloneApkMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_StandaloneApkMetadata.Merge(m, src)
+}
+func (m *StandaloneApkMetadata) XXX_Size() int {
+ return xxx_messageInfo_StandaloneApkMetadata.Size(m)
+}
+func (m *StandaloneApkMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_StandaloneApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StandaloneApkMetadata proto.InternalMessageInfo
+
+func (m *StandaloneApkMetadata) GetFusedModuleName() []string {
+ if m != nil {
+ return m.FusedModuleName
+ }
+ return nil
+}
+
+// Holds data specific to system APKs.
+type SystemApkMetadata struct {
+ // Names of the modules fused in this system APK.
+ FusedModuleName []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
+ // Indicates whether the APK is uncompressed system APK, stub APK or
+ // compressed system APK.
+ SystemApkType SystemApkMetadata_SystemApkType `protobuf:"varint,2,opt,name=system_apk_type,json=systemApkType,proto3,enum=android.bundle.SystemApkMetadata_SystemApkType" json:"system_apk_type,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SystemApkMetadata) Reset() { *m = SystemApkMetadata{} }
+func (m *SystemApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*SystemApkMetadata) ProtoMessage() {}
+func (*SystemApkMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{10}
+}
+
+func (m *SystemApkMetadata) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SystemApkMetadata.Unmarshal(m, b)
+}
+func (m *SystemApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SystemApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *SystemApkMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SystemApkMetadata.Merge(m, src)
+}
+func (m *SystemApkMetadata) XXX_Size() int {
+ return xxx_messageInfo_SystemApkMetadata.Size(m)
+}
+func (m *SystemApkMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_SystemApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SystemApkMetadata proto.InternalMessageInfo
+
+func (m *SystemApkMetadata) GetFusedModuleName() []string {
+ if m != nil {
+ return m.FusedModuleName
+ }
+ return nil
+}
+
+func (m *SystemApkMetadata) GetSystemApkType() SystemApkMetadata_SystemApkType {
+ if m != nil {
+ return m.SystemApkType
+ }
+ return SystemApkMetadata_UNSPECIFIED_VALUE
+}
+
+// Holds data specific to APEX APKs.
+type ApexApkMetadata struct {
+ // Configuration for processing of APKs embedded in an APEX image.
+ ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApexApkMetadata) Reset() { *m = ApexApkMetadata{} }
+func (m *ApexApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*ApexApkMetadata) ProtoMessage() {}
+func (*ApexApkMetadata) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{11}
+}
+
+func (m *ApexApkMetadata) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApexApkMetadata.Unmarshal(m, b)
+}
+func (m *ApexApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApexApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *ApexApkMetadata) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApexApkMetadata.Merge(m, src)
+}
+func (m *ApexApkMetadata) XXX_Size() int {
+ return xxx_messageInfo_ApexApkMetadata.Size(m)
+}
+func (m *ApexApkMetadata) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApexApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexApkMetadata proto.InternalMessageInfo
+
+func (m *ApexApkMetadata) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
+ if m != nil {
+ return m.ApexEmbeddedApkConfig
+ }
+ return nil
+}
+
+type LocalTestingInfo struct {
+ // Indicates if the bundle is built in local testing mode.
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ // The local testing path, as specified in the base manifest.
+ // This refers to the relative path on the external directory of the app where
+ // APKs will be pushed for local testing.
+ // Set only if local testing is enabled.
+ LocalTestingPath string `protobuf:"bytes,2,opt,name=local_testing_path,json=localTestingPath,proto3" json:"local_testing_path,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *LocalTestingInfo) Reset() { *m = LocalTestingInfo{} }
+func (m *LocalTestingInfo) String() string { return proto.CompactTextString(m) }
+func (*LocalTestingInfo) ProtoMessage() {}
+func (*LocalTestingInfo) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0dff099eb2e3dfdb, []int{12}
+}
+
+func (m *LocalTestingInfo) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_LocalTestingInfo.Unmarshal(m, b)
+}
+func (m *LocalTestingInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_LocalTestingInfo.Marshal(b, m, deterministic)
+}
+func (m *LocalTestingInfo) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_LocalTestingInfo.Merge(m, src)
+}
+func (m *LocalTestingInfo) XXX_Size() int {
+ return xxx_messageInfo_LocalTestingInfo.Size(m)
+}
+func (m *LocalTestingInfo) XXX_DiscardUnknown() {
+ xxx_messageInfo_LocalTestingInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LocalTestingInfo proto.InternalMessageInfo
+
+func (m *LocalTestingInfo) GetEnabled() bool {
+ if m != nil {
+ return m.Enabled
+ }
+ return false
+}
+
+func (m *LocalTestingInfo) GetLocalTestingPath() string {
+ if m != nil {
+ return m.LocalTestingPath
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterEnum("android.bundle.DeliveryType", DeliveryType_name, DeliveryType_value)
+ proto.RegisterEnum("android.bundle.SystemApkMetadata_SystemApkType", SystemApkMetadata_SystemApkType_name, SystemApkMetadata_SystemApkType_value)
+ proto.RegisterType((*BuildApksResult)(nil), "android.bundle.BuildApksResult")
+ proto.RegisterType((*Variant)(nil), "android.bundle.Variant")
+ proto.RegisterType((*ApkSet)(nil), "android.bundle.ApkSet")
+ proto.RegisterType((*ModuleMetadata)(nil), "android.bundle.ModuleMetadata")
+ proto.RegisterType((*AssetSliceSet)(nil), "android.bundle.AssetSliceSet")
+ proto.RegisterType((*AssetModuleMetadata)(nil), "android.bundle.AssetModuleMetadata")
+ proto.RegisterType((*InstantMetadata)(nil), "android.bundle.InstantMetadata")
+ proto.RegisterType((*ApkDescription)(nil), "android.bundle.ApkDescription")
+ proto.RegisterType((*SplitApkMetadata)(nil), "android.bundle.SplitApkMetadata")
+ proto.RegisterType((*StandaloneApkMetadata)(nil), "android.bundle.StandaloneApkMetadata")
+ proto.RegisterType((*SystemApkMetadata)(nil), "android.bundle.SystemApkMetadata")
+ proto.RegisterType((*ApexApkMetadata)(nil), "android.bundle.ApexApkMetadata")
+ proto.RegisterType((*LocalTestingInfo)(nil), "android.bundle.LocalTestingInfo")
+}
+
+func init() {
+ proto.RegisterFile("commands.proto", fileDescriptor_0dff099eb2e3dfdb)
+}
+
+var fileDescriptor_0dff099eb2e3dfdb = []byte{
+ // 1104 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcf, 0x6f, 0xe2, 0x46,
+ 0x14, 0x5e, 0x03, 0x0b, 0xe1, 0x05, 0xb0, 0x33, 0x1b, 0xba, 0xde, 0x68, 0x77, 0xcb, 0xba, 0x4a,
+ 0x85, 0xa2, 0x2a, 0xab, 0xa6, 0x3d, 0xad, 0xd4, 0x4a, 0x10, 0x9c, 0x96, 0x2d, 0x90, 0xc8, 0x26,
+ 0x89, 0x92, 0x4a, 0x1d, 0x4d, 0x98, 0x49, 0xd6, 0xc2, 0xbf, 0xca, 0x98, 0x28, 0xf9, 0x57, 0x7a,
+ 0xa9, 0x7a, 0xec, 0xb1, 0xd7, 0xfe, 0x51, 0x3d, 0xf5, 0xde, 0xca, 0x63, 0x03, 0xb6, 0xb1, 0xd4,
+ 0x64, 0xd5, 0x13, 0x7e, 0x6f, 0xbe, 0xf9, 0xe6, 0xbd, 0xf7, 0xbd, 0x79, 0x0c, 0x34, 0x26, 0x9e,
+ 0xe3, 0x10, 0x97, 0xf2, 0x7d, 0x7f, 0xe6, 0x05, 0x1e, 0x6a, 0x10, 0x97, 0xce, 0x3c, 0x8b, 0xee,
+ 0x5f, 0xcd, 0x5d, 0x6a, 0xb3, 0x9d, 0xda, 0xc4, 0x73, 0xaf, 0xad, 0x9b, 0x68, 0x75, 0x47, 0x0e,
+ 0xc8, 0xec, 0x86, 0x05, 0x96, 0x1b, 0x3b, 0xb4, 0x3f, 0x0b, 0x20, 0x77, 0xe7, 0x96, 0x4d, 0x3b,
+ 0xfe, 0x94, 0x1b, 0x8c, 0xcf, 0xed, 0x00, 0xbd, 0x81, 0x9a, 0x4f, 0x26, 0x53, 0x72, 0xc3, 0xb0,
+ 0x4b, 0x1c, 0xa6, 0x96, 0x5a, 0x52, 0xbb, 0x6a, 0x6c, 0xc6, 0xbe, 0x11, 0x71, 0x18, 0xfa, 0x12,
+ 0x2a, 0xb7, 0x64, 0x66, 0x11, 0x37, 0x50, 0xa5, 0x56, 0xb1, 0xbd, 0x79, 0xf0, 0x7c, 0x3f, 0x7d,
+ 0xee, 0xfe, 0x59, 0xb4, 0x6c, 0x2c, 0x70, 0xe8, 0x1d, 0x40, 0xb4, 0x14, 0x78, 0x9e, 0xad, 0x16,
+ 0x5a, 0x52, 0x7b, 0xf3, 0x60, 0x27, 0xbb, 0xab, 0xbb, 0x44, 0x18, 0x09, 0x34, 0xd2, 0x41, 0x26,
+ 0x9c, 0xb3, 0x00, 0x73, 0xdb, 0x9a, 0x30, 0xcc, 0x59, 0xa0, 0x16, 0xc5, 0xb1, 0xaf, 0xb2, 0x04,
+ 0x9d, 0x10, 0x66, 0x86, 0x28, 0x93, 0x05, 0x46, 0x9d, 0x24, 0x4d, 0x34, 0x02, 0x64, 0x7b, 0x13,
+ 0x62, 0xe3, 0x80, 0xf1, 0xb0, 0x06, 0xd8, 0x72, 0xaf, 0x3d, 0xf5, 0xa9, 0x08, 0xa5, 0x95, 0x65,
+ 0x1a, 0x84, 0xc8, 0x71, 0x04, 0xec, 0xbb, 0xd7, 0x9e, 0xa1, 0xd8, 0x19, 0x8f, 0xf6, 0x9b, 0x04,
+ 0x95, 0x38, 0x4f, 0xf4, 0x2d, 0x54, 0x97, 0xb5, 0x55, 0xa5, 0x7c, 0xca, 0x18, 0x3b, 0x5e, 0xe0,
+ 0x8c, 0xd5, 0x16, 0xf4, 0x16, 0x2a, 0xc4, 0x9f, 0x8a, 0xd4, 0x0a, 0x22, 0xb5, 0x4f, 0xd6, 0x52,
+ 0xf3, 0xa7, 0x61, 0x4e, 0x65, 0x22, 0x7e, 0xd1, 0x2e, 0x34, 0xe2, 0xd2, 0x62, 0x77, 0xee, 0x5c,
+ 0xb1, 0x99, 0x5a, 0x6c, 0x49, 0xed, 0xba, 0x51, 0x8f, 0xbd, 0x23, 0xe1, 0xd4, 0x7e, 0x91, 0xa0,
+ 0x1c, 0xed, 0x44, 0xdf, 0x81, 0xec, 0x78, 0x74, 0x6e, 0x33, 0xec, 0xb0, 0x80, 0x50, 0x12, 0x90,
+ 0x38, 0xd0, 0xd7, 0xd9, 0xa3, 0x86, 0x02, 0x36, 0x8c, 0x51, 0x46, 0xc3, 0x49, 0xd9, 0x21, 0x51,
+ 0x18, 0x2b, 0x65, 0x7c, 0x32, 0xb3, 0xfc, 0xc0, 0xf2, 0xdc, 0x38, 0xe6, 0xd7, 0x39, 0x31, 0xf7,
+ 0x56, 0x28, 0xa3, 0x41, 0x52, 0xb6, 0xf6, 0x6b, 0x01, 0x1a, 0xe9, 0xb3, 0x10, 0x82, 0x92, 0x68,
+ 0x3a, 0x49, 0x34, 0x9d, 0xf8, 0x46, 0x1d, 0xa8, 0x53, 0x66, 0x5b, 0xb7, 0x6c, 0x76, 0x8f, 0x83,
+ 0x7b, 0x9f, 0xa9, 0xe5, 0x96, 0xd4, 0x6e, 0x1c, 0xbc, 0xcc, 0x9e, 0xd6, 0x8b, 0x41, 0xe3, 0x7b,
+ 0x9f, 0x19, 0x35, 0x9a, 0xb0, 0xd0, 0x2b, 0x00, 0x8b, 0x63, 0xcb, 0xe5, 0x41, 0xd8, 0xb3, 0x61,
+ 0xa5, 0x36, 0x8c, 0xaa, 0xc5, 0xfb, 0x91, 0x03, 0x69, 0x50, 0xa3, 0xcc, 0x67, 0x2e, 0x65, 0xee,
+ 0xc4, 0x62, 0x5c, 0x2d, 0xb5, 0x8a, 0xed, 0xaa, 0x91, 0xf2, 0xa1, 0x6f, 0x92, 0x0a, 0x47, 0x4d,
+ 0xf3, 0x69, 0x7e, 0xe1, 0x72, 0x05, 0xfe, 0x1a, 0xb6, 0x3d, 0x17, 0x53, 0x16, 0x5e, 0x56, 0x4c,
+ 0x99, 0x3f, 0x63, 0x13, 0x12, 0x30, 0x2a, 0x6e, 0xc2, 0x46, 0xb7, 0xa0, 0x4a, 0x06, 0xf2, 0xdc,
+ 0x9e, 0x58, 0xee, 0x2d, 0x57, 0xb5, 0x3f, 0x24, 0xa8, 0xa7, 0x7a, 0x1a, 0x9d, 0x43, 0x33, 0xba,
+ 0x0b, 0xf9, 0x5a, 0x7e, 0x96, 0x7b, 0x23, 0x32, 0x82, 0x3e, 0x23, 0xeb, 0xce, 0xff, 0x4f, 0xd5,
+ 0xbf, 0x24, 0x78, 0x96, 0x73, 0xea, 0xc3, 0xa4, 0x2d, 0x3d, 0x5a, 0xda, 0xf7, 0xa0, 0xc4, 0xba,
+ 0xae, 0x6a, 0x51, 0xcc, 0x97, 0x27, 0x96, 0x7b, 0x59, 0x07, 0xd9, 0x4a, 0x3b, 0x3e, 0x52, 0xa4,
+ 0xdf, 0x25, 0x90, 0x33, 0xd4, 0x99, 0x86, 0x93, 0xb2, 0x0d, 0xb7, 0x96, 0x77, 0xf1, 0xd1, 0x79,
+ 0x7f, 0x5c, 0xac, 0xff, 0x94, 0xa0, 0x91, 0xd6, 0x0f, 0xbd, 0x5b, 0x1f, 0x5d, 0x2f, 0x73, 0x24,
+ 0xcf, 0xed, 0x6a, 0x04, 0x25, 0x9f, 0x04, 0x1f, 0xc4, 0xa1, 0x55, 0x43, 0x7c, 0xa3, 0x13, 0x40,
+ 0xdc, 0xb7, 0xad, 0x00, 0x87, 0xed, 0x94, 0x91, 0x64, 0x6d, 0x26, 0x9a, 0x21, 0xb2, 0xe3, 0x4f,
+ 0x17, 0x85, 0xfb, 0xfe, 0x89, 0xa1, 0xf0, 0x8c, 0x0f, 0x61, 0x78, 0x1e, 0x96, 0x8d, 0x12, 0xdb,
+ 0x73, 0x59, 0x9a, 0xb6, 0x24, 0x68, 0x77, 0xd7, 0x68, 0x97, 0xf0, 0x34, 0x77, 0x93, 0xe7, 0x2d,
+ 0xa0, 0x31, 0x6c, 0x2f, 0x7a, 0x28, 0xc5, 0xfe, 0xf4, 0xc1, 0x41, 0xa3, 0x78, 0x7f, 0x92, 0xd5,
+ 0x84, 0x67, 0xfc, 0x9e, 0x07, 0xcc, 0x49, 0x93, 0x96, 0x05, 0xe9, 0x9b, 0x35, 0x52, 0x01, 0x4d,
+ 0xb3, 0x6e, 0xf1, 0xac, 0x33, 0x0c, 0x35, 0xf9, 0x5f, 0xb8, 0x64, 0xad, 0x3c, 0x3c, 0xd4, 0xd5,
+ 0xbf, 0xe2, 0x92, 0x75, 0x08, 0x5b, 0xc4, 0x67, 0x77, 0xe9, 0x40, 0x37, 0xf2, 0x6f, 0x51, 0xc7,
+ 0x67, 0x77, 0x69, 0x46, 0x99, 0xa4, 0x5d, 0xdd, 0x1d, 0x50, 0x93, 0x4c, 0xd8, 0x73, 0x99, 0x77,
+ 0x8d, 0x6f, 0x89, 0x3d, 0x67, 0xda, 0x29, 0x28, 0xd9, 0xa0, 0xd0, 0x0b, 0xd8, 0x88, 0x5a, 0xc6,
+ 0xa2, 0xf1, 0x78, 0xa8, 0x08, 0xbb, 0x4f, 0xd1, 0xe7, 0x20, 0x5b, 0x1c, 0x3b, 0x84, 0x07, 0x6c,
+ 0x86, 0x85, 0x33, 0xea, 0x70, 0xa3, 0x6e, 0xf1, 0xa1, 0xf0, 0x0a, 0x36, 0xad, 0x0f, 0xcd, 0x5c,
+ 0xd1, 0xd1, 0x1e, 0x6c, 0x5d, 0xcf, 0x39, 0xa3, 0x8b, 0x81, 0x19, 0xcf, 0xa0, 0x70, 0xc0, 0xcb,
+ 0x62, 0x21, 0x1a, 0x53, 0xe1, 0xbb, 0xe6, 0x7d, 0x69, 0xa3, 0xa0, 0x14, 0xb5, 0xbf, 0x25, 0xd8,
+ 0x5a, 0x53, 0xe3, 0x31, 0x3c, 0xe8, 0x1c, 0xe4, 0x84, 0xf2, 0xe2, 0x82, 0x17, 0xc4, 0x05, 0x7f,
+ 0xfb, 0x9f, 0xaa, 0xaf, 0x3c, 0xe2, 0xce, 0xd7, 0x79, 0xd2, 0xd4, 0x2e, 0xa1, 0x9e, 0x5a, 0x47,
+ 0x4d, 0xd8, 0x3a, 0x1d, 0x99, 0x27, 0xfa, 0x61, 0xff, 0xa8, 0xaf, 0xf7, 0xf0, 0x59, 0x67, 0x70,
+ 0xaa, 0x2b, 0x4f, 0x10, 0x40, 0xd9, 0xbc, 0x30, 0xc7, 0xfa, 0x50, 0x91, 0x90, 0x0c, 0x9b, 0xd1,
+ 0x37, 0x36, 0xc7, 0xa7, 0x5d, 0xa5, 0x10, 0xee, 0x89, 0x1d, 0x87, 0xc7, 0xc3, 0x13, 0x43, 0x37,
+ 0x4d, 0xbd, 0xa7, 0x14, 0xb5, 0x9f, 0x41, 0xce, 0x48, 0x8b, 0x7e, 0x0a, 0x75, 0x64, 0x77, 0x98,
+ 0x39, 0x57, 0x8c, 0x52, 0x46, 0x45, 0x3a, 0xd1, 0x8b, 0x32, 0x7e, 0xf8, 0xed, 0xe6, 0x75, 0x87,
+ 0x1e, 0xc3, 0x3b, 0xfe, 0xf4, 0x50, 0x80, 0x8d, 0x26, 0xc9, 0x73, 0x6b, 0x97, 0xa0, 0x64, 0xdf,
+ 0x59, 0x48, 0x85, 0x0a, 0x73, 0xc9, 0x95, 0xcd, 0x68, 0x3c, 0x36, 0x17, 0x26, 0xfa, 0x22, 0xfb,
+ 0x7e, 0x4b, 0x8c, 0x9e, 0xd4, 0xeb, 0xec, 0x84, 0x04, 0x1f, 0xf6, 0x7e, 0x84, 0x5a, 0x72, 0x7a,
+ 0xa2, 0x17, 0xd0, 0x3c, 0x1d, 0xfd, 0x30, 0x3a, 0x3e, 0x1f, 0xe1, 0x9e, 0x3e, 0xe8, 0x9f, 0xe9,
+ 0xc6, 0x05, 0x1e, 0x5f, 0x9c, 0x84, 0xd5, 0x52, 0xa0, 0xd6, 0x1f, 0x99, 0xe3, 0xce, 0x60, 0x80,
+ 0xc7, 0xfd, 0xa1, 0xae, 0x48, 0xa8, 0x0e, 0xd5, 0xe3, 0x10, 0x37, 0xec, 0x8c, 0x7a, 0x4a, 0x21,
+ 0x2c, 0xe1, 0x51, 0xc7, 0x1c, 0xe3, 0xa3, 0xe3, 0xc1, 0xe0, 0xf8, 0x5c, 0x29, 0x76, 0xf7, 0x00,
+ 0x4d, 0x3c, 0x27, 0x93, 0xfb, 0xe5, 0x76, 0x6c, 0xe3, 0xc8, 0xc6, 0xe2, 0x8d, 0x7d, 0x55, 0x16,
+ 0x3f, 0x5f, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb1, 0xe5, 0xcb, 0x87, 0xab, 0x0b, 0x00, 0x00,
+}
diff --git a/cmd/extract_apks/bundle_proto/commands.proto b/cmd/extract_apks/bundle_proto/commands.proto
new file mode 100644
index 0000000..b36340b
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/commands.proto
@@ -0,0 +1,197 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+syntax = "proto3";
+
+package android.bundle;
+
+import "config.proto";
+import "targeting.proto";
+
+option go_package = "android_bundle_proto";
+option java_package = "com.android.bundle";
+
+// Describes the output of the "build-apks" command.
+message BuildApksResult {
+ // The package name of this app.
+ string package_name = 4;
+
+ // List of the created variants.
+ repeated Variant variant = 1;
+
+ // Metadata about BundleTool used to build the APKs.
+ Bundletool bundletool = 2;
+
+ // List of the created asset slices.
+ repeated AssetSliceSet asset_slice_set = 3;
+
+ // Information about local testing mode.
+ LocalTestingInfo local_testing_info = 5;
+}
+
+// Variant is a group of APKs that covers a part of the device configuration
+// space. APKs from multiple variants are never combined on one device.
+message Variant {
+ // Variant-level targeting.
+ // This targeting is fairly high-level and each APK has its own targeting as
+ // well.
+ VariantTargeting targeting = 1;
+
+ // Set of APKs, one set per module.
+ repeated ApkSet apk_set = 2;
+
+ // Number of the variant, starting at 0 (unless overridden).
+ // A device will receive APKs from the first variant that matches the device
+ // configuration, with higher variant numbers having priority over lower
+ // variant numbers.
+ uint32 variant_number = 3;
+}
+
+// Represents a module.
+// For pre-L devices multiple modules (possibly all) may be merged into one.
+message ApkSet {
+ ModuleMetadata module_metadata = 1;
+
+ // APKs.
+ repeated ApkDescription apk_description = 2;
+}
+
+message ModuleMetadata {
+ // Module name.
+ string name = 1;
+
+ // Indicates the delivery type (e.g. on-demand) of the module.
+ DeliveryType delivery_type = 6;
+
+ // Indicates whether this module is marked "instant".
+ bool is_instant = 3;
+
+ // Names of the modules that this module directly depends on.
+ // Each module implicitly depends on the base module.
+ repeated string dependencies = 4;
+
+ // The targeting that makes a conditional module installed.
+ // Relevant only for Split APKs.
+ ModuleTargeting targeting = 5;
+
+ // Deprecated. Please use delivery_type.
+ bool on_demand_deprecated = 2 [deprecated = true];
+}
+
+// Set of asset slices belonging to a single asset module.
+message AssetSliceSet {
+ // Module level metadata.
+ AssetModuleMetadata asset_module_metadata = 1;
+
+ // Asset slices.
+ repeated ApkDescription apk_description = 2;
+}
+
+message AssetModuleMetadata {
+ // Module name.
+ string name = 1;
+
+ // Indicates the delivery type for persistent install.
+ DeliveryType delivery_type = 4;
+
+ // Metadata for instant installs.
+ InstantMetadata instant_metadata = 3;
+
+ // Deprecated. Use delivery_type.
+ bool on_demand_deprecated = 2 [deprecated = true];
+}
+
+message InstantMetadata {
+ // Indicates whether this module is marked "instant".
+ bool is_instant = 1;
+
+ // Indicates the delivery type for instant install.
+ DeliveryType delivery_type = 3;
+
+ // Deprecated. Use delivery_type.
+ bool on_demand_deprecated = 2 [deprecated = true];
+}
+
+enum DeliveryType {
+ UNKNOWN_DELIVERY_TYPE = 0;
+ INSTALL_TIME = 1;
+ ON_DEMAND = 2;
+ FAST_FOLLOW = 3;
+}
+
+message ApkDescription {
+ ApkTargeting targeting = 1;
+
+ // Path to the APK file.
+ // BEGIN-INTERNAL
+ // The path may be a blobkey if the proto is not constructed by bundletool.
+ // END-INTERNAL
+ string path = 2;
+
+ oneof apk_metadata_oneof_value {
+ // Set only for Split APKs.
+ SplitApkMetadata split_apk_metadata = 3;
+ // Set only for standalone APKs.
+ StandaloneApkMetadata standalone_apk_metadata = 4;
+ // Set only for Instant split APKs.
+ SplitApkMetadata instant_apk_metadata = 5;
+ // Set only for system APKs.
+ SystemApkMetadata system_apk_metadata = 6;
+ // Set only for asset slices.
+ SplitApkMetadata asset_slice_metadata = 7;
+ // Set only for APEX APKs.
+ ApexApkMetadata apex_apk_metadata = 8;
+ }
+}
+
+// Holds data specific to Split APKs.
+message SplitApkMetadata {
+ string split_id = 1;
+
+ // Indicates whether this APK is the master split of the module.
+ bool is_master_split = 2;
+}
+
+// Holds data specific to Standalone APKs.
+message StandaloneApkMetadata {
+ // Names of the modules fused in this standalone APK.
+ repeated string fused_module_name = 1;
+
+ reserved 2;
+}
+
+// Holds data specific to system APKs.
+message SystemApkMetadata {
+ // Names of the modules fused in this system APK.
+ repeated string fused_module_name = 1;
+ enum SystemApkType {
+ UNSPECIFIED_VALUE = 0;
+ // Uncompressed APK for system image.
+ SYSTEM = 1;
+ // Stub APK for compressed APK in the system image
+ // (contains only android manifest).
+ SYSTEM_STUB = 2;
+ // Compressed APK for system image.
+ SYSTEM_COMPRESSED = 3;
+ }
+ // Indicates whether the APK is uncompressed system APK, stub APK or
+ // compressed system APK.
+ SystemApkType system_apk_type = 2;
+}
+
+// Holds data specific to APEX APKs.
+message ApexApkMetadata {
+ // Configuration for processing of APKs embedded in an APEX image.
+ repeated ApexEmbeddedApkConfig apex_embedded_apk_config = 1;
+}
+
+message LocalTestingInfo {
+ // Indicates if the bundle is built in local testing mode.
+ bool enabled = 1;
+ // The local testing path, as specified in the base manifest.
+ // This refers to the relative path on the external directory of the app where
+ // APKs will be pushed for local testing.
+ // Set only if local testing is enabled.
+ string local_testing_path = 2;
+}
diff --git a/cmd/extract_apks/bundle_proto/config.pb.go b/cmd/extract_apks/bundle_proto/config.pb.go
new file mode 100644
index 0000000..a28147a
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/config.pb.go
@@ -0,0 +1,952 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: config.proto
+
+package android_bundle_proto
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type BundleConfig_BundleType int32
+
+const (
+ BundleConfig_REGULAR BundleConfig_BundleType = 0
+ BundleConfig_APEX BundleConfig_BundleType = 1
+ BundleConfig_ASSET_ONLY BundleConfig_BundleType = 2
+)
+
+var BundleConfig_BundleType_name = map[int32]string{
+ 0: "REGULAR",
+ 1: "APEX",
+ 2: "ASSET_ONLY",
+}
+
+var BundleConfig_BundleType_value = map[string]int32{
+ "REGULAR": 0,
+ "APEX": 1,
+ "ASSET_ONLY": 2,
+}
+
+func (x BundleConfig_BundleType) String() string {
+ return proto.EnumName(BundleConfig_BundleType_name, int32(x))
+}
+
+func (BundleConfig_BundleType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{0, 0}
+}
+
+type SplitDimension_Value int32
+
+const (
+ SplitDimension_UNSPECIFIED_VALUE SplitDimension_Value = 0
+ SplitDimension_ABI SplitDimension_Value = 1
+ SplitDimension_SCREEN_DENSITY SplitDimension_Value = 2
+ SplitDimension_LANGUAGE SplitDimension_Value = 3
+ SplitDimension_TEXTURE_COMPRESSION_FORMAT SplitDimension_Value = 4
+ // BEGIN-INTERNAL
+ SplitDimension_GRAPHICS_API SplitDimension_Value = 5
+)
+
+var SplitDimension_Value_name = map[int32]string{
+ 0: "UNSPECIFIED_VALUE",
+ 1: "ABI",
+ 2: "SCREEN_DENSITY",
+ 3: "LANGUAGE",
+ 4: "TEXTURE_COMPRESSION_FORMAT",
+ 5: "GRAPHICS_API",
+}
+
+var SplitDimension_Value_value = map[string]int32{
+ "UNSPECIFIED_VALUE": 0,
+ "ABI": 1,
+ "SCREEN_DENSITY": 2,
+ "LANGUAGE": 3,
+ "TEXTURE_COMPRESSION_FORMAT": 4,
+ "GRAPHICS_API": 5,
+}
+
+func (x SplitDimension_Value) String() string {
+ return proto.EnumName(SplitDimension_Value_name, int32(x))
+}
+
+func (SplitDimension_Value) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{9, 0}
+}
+
+type BundleConfig struct {
+ Bundletool *Bundletool `protobuf:"bytes,1,opt,name=bundletool,proto3" json:"bundletool,omitempty"`
+ Optimizations *Optimizations `protobuf:"bytes,2,opt,name=optimizations,proto3" json:"optimizations,omitempty"`
+ Compression *Compression `protobuf:"bytes,3,opt,name=compression,proto3" json:"compression,omitempty"`
+ // Resources to be always kept in the master split.
+ MasterResources *MasterResources `protobuf:"bytes,4,opt,name=master_resources,json=masterResources,proto3" json:"master_resources,omitempty"`
+ ApexConfig *ApexConfig `protobuf:"bytes,5,opt,name=apex_config,json=apexConfig,proto3" json:"apex_config,omitempty"`
+ // APKs to be signed with the same key as generated APKs.
+ UnsignedEmbeddedApkConfig []*UnsignedEmbeddedApkConfig `protobuf:"bytes,6,rep,name=unsigned_embedded_apk_config,json=unsignedEmbeddedApkConfig,proto3" json:"unsigned_embedded_apk_config,omitempty"`
+ AssetModulesConfig *AssetModulesConfig `protobuf:"bytes,7,opt,name=asset_modules_config,json=assetModulesConfig,proto3" json:"asset_modules_config,omitempty"`
+ Type BundleConfig_BundleType `protobuf:"varint,8,opt,name=type,proto3,enum=android.bundle.BundleConfig_BundleType" json:"type,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BundleConfig) Reset() { *m = BundleConfig{} }
+func (m *BundleConfig) String() string { return proto.CompactTextString(m) }
+func (*BundleConfig) ProtoMessage() {}
+func (*BundleConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{0}
+}
+
+func (m *BundleConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BundleConfig.Unmarshal(m, b)
+}
+func (m *BundleConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BundleConfig.Marshal(b, m, deterministic)
+}
+func (m *BundleConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BundleConfig.Merge(m, src)
+}
+func (m *BundleConfig) XXX_Size() int {
+ return xxx_messageInfo_BundleConfig.Size(m)
+}
+func (m *BundleConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_BundleConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BundleConfig proto.InternalMessageInfo
+
+func (m *BundleConfig) GetBundletool() *Bundletool {
+ if m != nil {
+ return m.Bundletool
+ }
+ return nil
+}
+
+func (m *BundleConfig) GetOptimizations() *Optimizations {
+ if m != nil {
+ return m.Optimizations
+ }
+ return nil
+}
+
+func (m *BundleConfig) GetCompression() *Compression {
+ if m != nil {
+ return m.Compression
+ }
+ return nil
+}
+
+func (m *BundleConfig) GetMasterResources() *MasterResources {
+ if m != nil {
+ return m.MasterResources
+ }
+ return nil
+}
+
+func (m *BundleConfig) GetApexConfig() *ApexConfig {
+ if m != nil {
+ return m.ApexConfig
+ }
+ return nil
+}
+
+func (m *BundleConfig) GetUnsignedEmbeddedApkConfig() []*UnsignedEmbeddedApkConfig {
+ if m != nil {
+ return m.UnsignedEmbeddedApkConfig
+ }
+ return nil
+}
+
+func (m *BundleConfig) GetAssetModulesConfig() *AssetModulesConfig {
+ if m != nil {
+ return m.AssetModulesConfig
+ }
+ return nil
+}
+
+func (m *BundleConfig) GetType() BundleConfig_BundleType {
+ if m != nil {
+ return m.Type
+ }
+ return BundleConfig_REGULAR
+}
+
+type Bundletool struct {
+ // Version of BundleTool used to build the Bundle.
+ Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Bundletool) Reset() { *m = Bundletool{} }
+func (m *Bundletool) String() string { return proto.CompactTextString(m) }
+func (*Bundletool) ProtoMessage() {}
+func (*Bundletool) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{1}
+}
+
+func (m *Bundletool) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Bundletool.Unmarshal(m, b)
+}
+func (m *Bundletool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Bundletool.Marshal(b, m, deterministic)
+}
+func (m *Bundletool) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Bundletool.Merge(m, src)
+}
+func (m *Bundletool) XXX_Size() int {
+ return xxx_messageInfo_Bundletool.Size(m)
+}
+func (m *Bundletool) XXX_DiscardUnknown() {
+ xxx_messageInfo_Bundletool.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Bundletool proto.InternalMessageInfo
+
+func (m *Bundletool) GetVersion() string {
+ if m != nil {
+ return m.Version
+ }
+ return ""
+}
+
+type Compression struct {
+ // Glob matching the list of files to leave uncompressed in the APKs.
+ // The matching is done against the path of files in the APK, thus excluding
+ // the name of the modules, and using forward slash ("/") as a name separator.
+ // Examples: "res/raw/**", "assets/**/*.uncompressed", etc.
+ UncompressedGlob []string `protobuf:"bytes,1,rep,name=uncompressed_glob,json=uncompressedGlob,proto3" json:"uncompressed_glob,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Compression) Reset() { *m = Compression{} }
+func (m *Compression) String() string { return proto.CompactTextString(m) }
+func (*Compression) ProtoMessage() {}
+func (*Compression) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{2}
+}
+
+func (m *Compression) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Compression.Unmarshal(m, b)
+}
+func (m *Compression) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Compression.Marshal(b, m, deterministic)
+}
+func (m *Compression) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Compression.Merge(m, src)
+}
+func (m *Compression) XXX_Size() int {
+ return xxx_messageInfo_Compression.Size(m)
+}
+func (m *Compression) XXX_DiscardUnknown() {
+ xxx_messageInfo_Compression.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Compression proto.InternalMessageInfo
+
+func (m *Compression) GetUncompressedGlob() []string {
+ if m != nil {
+ return m.UncompressedGlob
+ }
+ return nil
+}
+
+// Resources to keep in the master split.
+type MasterResources struct {
+ // Resource IDs to be kept in master split.
+ ResourceIds []int32 `protobuf:"varint,1,rep,packed,name=resource_ids,json=resourceIds,proto3" json:"resource_ids,omitempty"`
+ // Resource names to be kept in master split.
+ ResourceNames []string `protobuf:"bytes,2,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *MasterResources) Reset() { *m = MasterResources{} }
+func (m *MasterResources) String() string { return proto.CompactTextString(m) }
+func (*MasterResources) ProtoMessage() {}
+func (*MasterResources) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{3}
+}
+
+func (m *MasterResources) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_MasterResources.Unmarshal(m, b)
+}
+func (m *MasterResources) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_MasterResources.Marshal(b, m, deterministic)
+}
+func (m *MasterResources) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MasterResources.Merge(m, src)
+}
+func (m *MasterResources) XXX_Size() int {
+ return xxx_messageInfo_MasterResources.Size(m)
+}
+func (m *MasterResources) XXX_DiscardUnknown() {
+ xxx_messageInfo_MasterResources.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MasterResources proto.InternalMessageInfo
+
+func (m *MasterResources) GetResourceIds() []int32 {
+ if m != nil {
+ return m.ResourceIds
+ }
+ return nil
+}
+
+func (m *MasterResources) GetResourceNames() []string {
+ if m != nil {
+ return m.ResourceNames
+ }
+ return nil
+}
+
+type Optimizations struct {
+ SplitsConfig *SplitsConfig `protobuf:"bytes,1,opt,name=splits_config,json=splitsConfig,proto3" json:"splits_config,omitempty"`
+ // This is for uncompressing native libraries on M+ devices (L+ devices on
+ // instant apps).
+ UncompressNativeLibraries *UncompressNativeLibraries `protobuf:"bytes,2,opt,name=uncompress_native_libraries,json=uncompressNativeLibraries,proto3" json:"uncompress_native_libraries,omitempty"`
+ // This is for uncompressing dex files on P+ devices.
+ UncompressDexFiles *UncompressDexFiles `protobuf:"bytes,3,opt,name=uncompress_dex_files,json=uncompressDexFiles,proto3" json:"uncompress_dex_files,omitempty"`
+ // Configuration for the generation of standalone APKs.
+ // If no StandaloneConfig is set, the configuration is inherited from
+ // splits_config.
+ StandaloneConfig *StandaloneConfig `protobuf:"bytes,4,opt,name=standalone_config,json=standaloneConfig,proto3" json:"standalone_config,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Optimizations) Reset() { *m = Optimizations{} }
+func (m *Optimizations) String() string { return proto.CompactTextString(m) }
+func (*Optimizations) ProtoMessage() {}
+func (*Optimizations) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{4}
+}
+
+func (m *Optimizations) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Optimizations.Unmarshal(m, b)
+}
+func (m *Optimizations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Optimizations.Marshal(b, m, deterministic)
+}
+func (m *Optimizations) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Optimizations.Merge(m, src)
+}
+func (m *Optimizations) XXX_Size() int {
+ return xxx_messageInfo_Optimizations.Size(m)
+}
+func (m *Optimizations) XXX_DiscardUnknown() {
+ xxx_messageInfo_Optimizations.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Optimizations proto.InternalMessageInfo
+
+func (m *Optimizations) GetSplitsConfig() *SplitsConfig {
+ if m != nil {
+ return m.SplitsConfig
+ }
+ return nil
+}
+
+func (m *Optimizations) GetUncompressNativeLibraries() *UncompressNativeLibraries {
+ if m != nil {
+ return m.UncompressNativeLibraries
+ }
+ return nil
+}
+
+func (m *Optimizations) GetUncompressDexFiles() *UncompressDexFiles {
+ if m != nil {
+ return m.UncompressDexFiles
+ }
+ return nil
+}
+
+func (m *Optimizations) GetStandaloneConfig() *StandaloneConfig {
+ if m != nil {
+ return m.StandaloneConfig
+ }
+ return nil
+}
+
+type UncompressNativeLibraries struct {
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *UncompressNativeLibraries) Reset() { *m = UncompressNativeLibraries{} }
+func (m *UncompressNativeLibraries) String() string { return proto.CompactTextString(m) }
+func (*UncompressNativeLibraries) ProtoMessage() {}
+func (*UncompressNativeLibraries) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{5}
+}
+
+func (m *UncompressNativeLibraries) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_UncompressNativeLibraries.Unmarshal(m, b)
+}
+func (m *UncompressNativeLibraries) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_UncompressNativeLibraries.Marshal(b, m, deterministic)
+}
+func (m *UncompressNativeLibraries) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_UncompressNativeLibraries.Merge(m, src)
+}
+func (m *UncompressNativeLibraries) XXX_Size() int {
+ return xxx_messageInfo_UncompressNativeLibraries.Size(m)
+}
+func (m *UncompressNativeLibraries) XXX_DiscardUnknown() {
+ xxx_messageInfo_UncompressNativeLibraries.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UncompressNativeLibraries proto.InternalMessageInfo
+
+func (m *UncompressNativeLibraries) GetEnabled() bool {
+ if m != nil {
+ return m.Enabled
+ }
+ return false
+}
+
+type UncompressDexFiles struct {
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *UncompressDexFiles) Reset() { *m = UncompressDexFiles{} }
+func (m *UncompressDexFiles) String() string { return proto.CompactTextString(m) }
+func (*UncompressDexFiles) ProtoMessage() {}
+func (*UncompressDexFiles) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{6}
+}
+
+func (m *UncompressDexFiles) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_UncompressDexFiles.Unmarshal(m, b)
+}
+func (m *UncompressDexFiles) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_UncompressDexFiles.Marshal(b, m, deterministic)
+}
+func (m *UncompressDexFiles) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_UncompressDexFiles.Merge(m, src)
+}
+func (m *UncompressDexFiles) XXX_Size() int {
+ return xxx_messageInfo_UncompressDexFiles.Size(m)
+}
+func (m *UncompressDexFiles) XXX_DiscardUnknown() {
+ xxx_messageInfo_UncompressDexFiles.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UncompressDexFiles proto.InternalMessageInfo
+
+func (m *UncompressDexFiles) GetEnabled() bool {
+ if m != nil {
+ return m.Enabled
+ }
+ return false
+}
+
+// Optimization configuration used to generate Split APKs.
+type SplitsConfig struct {
+ SplitDimension []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SplitsConfig) Reset() { *m = SplitsConfig{} }
+func (m *SplitsConfig) String() string { return proto.CompactTextString(m) }
+func (*SplitsConfig) ProtoMessage() {}
+func (*SplitsConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{7}
+}
+
+func (m *SplitsConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SplitsConfig.Unmarshal(m, b)
+}
+func (m *SplitsConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SplitsConfig.Marshal(b, m, deterministic)
+}
+func (m *SplitsConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SplitsConfig.Merge(m, src)
+}
+func (m *SplitsConfig) XXX_Size() int {
+ return xxx_messageInfo_SplitsConfig.Size(m)
+}
+func (m *SplitsConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_SplitsConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SplitsConfig proto.InternalMessageInfo
+
+func (m *SplitsConfig) GetSplitDimension() []*SplitDimension {
+ if m != nil {
+ return m.SplitDimension
+ }
+ return nil
+}
+
+// Optimization configuration used to generate Standalone APKs.
+type StandaloneConfig struct {
+ // Device targeting dimensions to shard.
+ SplitDimension []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
+ // Whether 64 bit libraries should be stripped from Standalone APKs.
+ Strip_64BitLibraries bool `protobuf:"varint,2,opt,name=strip_64_bit_libraries,json=strip64BitLibraries,proto3" json:"strip_64_bit_libraries,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *StandaloneConfig) Reset() { *m = StandaloneConfig{} }
+func (m *StandaloneConfig) String() string { return proto.CompactTextString(m) }
+func (*StandaloneConfig) ProtoMessage() {}
+func (*StandaloneConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{8}
+}
+
+func (m *StandaloneConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_StandaloneConfig.Unmarshal(m, b)
+}
+func (m *StandaloneConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_StandaloneConfig.Marshal(b, m, deterministic)
+}
+func (m *StandaloneConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_StandaloneConfig.Merge(m, src)
+}
+func (m *StandaloneConfig) XXX_Size() int {
+ return xxx_messageInfo_StandaloneConfig.Size(m)
+}
+func (m *StandaloneConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_StandaloneConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StandaloneConfig proto.InternalMessageInfo
+
+func (m *StandaloneConfig) GetSplitDimension() []*SplitDimension {
+ if m != nil {
+ return m.SplitDimension
+ }
+ return nil
+}
+
+func (m *StandaloneConfig) GetStrip_64BitLibraries() bool {
+ if m != nil {
+ return m.Strip_64BitLibraries
+ }
+ return false
+}
+
+type SplitDimension struct {
+ Value SplitDimension_Value `protobuf:"varint,1,opt,name=value,proto3,enum=android.bundle.SplitDimension_Value" json:"value,omitempty"`
+ // If set to 'true', indicates that APKs should *not* be split by this
+ // dimension.
+ Negate bool `protobuf:"varint,2,opt,name=negate,proto3" json:"negate,omitempty"`
+ // Optional transformation to be applied to asset directories where
+ // the targeting is encoded in the directory name (e.g: assets/foo#tcf_etc1)
+ SuffixStripping *SuffixStripping `protobuf:"bytes,3,opt,name=suffix_stripping,json=suffixStripping,proto3" json:"suffix_stripping,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SplitDimension) Reset() { *m = SplitDimension{} }
+func (m *SplitDimension) String() string { return proto.CompactTextString(m) }
+func (*SplitDimension) ProtoMessage() {}
+func (*SplitDimension) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{9}
+}
+
+func (m *SplitDimension) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SplitDimension.Unmarshal(m, b)
+}
+func (m *SplitDimension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SplitDimension.Marshal(b, m, deterministic)
+}
+func (m *SplitDimension) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SplitDimension.Merge(m, src)
+}
+func (m *SplitDimension) XXX_Size() int {
+ return xxx_messageInfo_SplitDimension.Size(m)
+}
+func (m *SplitDimension) XXX_DiscardUnknown() {
+ xxx_messageInfo_SplitDimension.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SplitDimension proto.InternalMessageInfo
+
+func (m *SplitDimension) GetValue() SplitDimension_Value {
+ if m != nil {
+ return m.Value
+ }
+ return SplitDimension_UNSPECIFIED_VALUE
+}
+
+func (m *SplitDimension) GetNegate() bool {
+ if m != nil {
+ return m.Negate
+ }
+ return false
+}
+
+func (m *SplitDimension) GetSuffixStripping() *SuffixStripping {
+ if m != nil {
+ return m.SuffixStripping
+ }
+ return nil
+}
+
+type SuffixStripping struct {
+ // If set to 'true', indicates that the targeting suffix should be removed
+ // from assets paths for this dimension when splits (or asset slices) are
+ // generated.
+ // This only applies to assets.
+ // For example a folder with path "assets/level1_textures#tcf_etc1"
+ // would be outputted to "assets/level1_textures". File contents are
+ // unchanged.
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ // The default suffix to be used for the cases where separate slices can't
+ // be generated for this dimension. In the case of standalone/universal APKs
+ // generation, stripping the suffix can lead to file name collisions. This
+ // default suffix defines the directories to retain. The others are
+ // discarded: standalone/universal APKs will contain only directories
+ // targeted at this value for the dimension.
+ //
+ // If not set or empty, the fallback directory in each directory group will be
+ // used (for example, if both "assets/level1_textures#tcf_etc1" and
+ // "assets/level1_textures" are present and the default suffix is empty,
+ // then only "assets/level1_textures" will be used).
+ DefaultSuffix string `protobuf:"bytes,2,opt,name=default_suffix,json=defaultSuffix,proto3" json:"default_suffix,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SuffixStripping) Reset() { *m = SuffixStripping{} }
+func (m *SuffixStripping) String() string { return proto.CompactTextString(m) }
+func (*SuffixStripping) ProtoMessage() {}
+func (*SuffixStripping) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{10}
+}
+
+func (m *SuffixStripping) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SuffixStripping.Unmarshal(m, b)
+}
+func (m *SuffixStripping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SuffixStripping.Marshal(b, m, deterministic)
+}
+func (m *SuffixStripping) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SuffixStripping.Merge(m, src)
+}
+func (m *SuffixStripping) XXX_Size() int {
+ return xxx_messageInfo_SuffixStripping.Size(m)
+}
+func (m *SuffixStripping) XXX_DiscardUnknown() {
+ xxx_messageInfo_SuffixStripping.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SuffixStripping proto.InternalMessageInfo
+
+func (m *SuffixStripping) GetEnabled() bool {
+ if m != nil {
+ return m.Enabled
+ }
+ return false
+}
+
+func (m *SuffixStripping) GetDefaultSuffix() string {
+ if m != nil {
+ return m.DefaultSuffix
+ }
+ return ""
+}
+
+// Configuration for processing APEX bundles.
+// https://source.android.com/devices/tech/ota/apex
+type ApexConfig struct {
+ // Configuration for processing of APKs embedded in an APEX image.
+ ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApexConfig) Reset() { *m = ApexConfig{} }
+func (m *ApexConfig) String() string { return proto.CompactTextString(m) }
+func (*ApexConfig) ProtoMessage() {}
+func (*ApexConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{11}
+}
+
+func (m *ApexConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApexConfig.Unmarshal(m, b)
+}
+func (m *ApexConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApexConfig.Marshal(b, m, deterministic)
+}
+func (m *ApexConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApexConfig.Merge(m, src)
+}
+func (m *ApexConfig) XXX_Size() int {
+ return xxx_messageInfo_ApexConfig.Size(m)
+}
+func (m *ApexConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApexConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexConfig proto.InternalMessageInfo
+
+func (m *ApexConfig) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
+ if m != nil {
+ return m.ApexEmbeddedApkConfig
+ }
+ return nil
+}
+
+type ApexEmbeddedApkConfig struct {
+ // Android package name of the APK.
+ PackageName string `protobuf:"bytes,1,opt,name=package_name,json=packageName,proto3" json:"package_name,omitempty"`
+ // Path to the APK within the APEX system image.
+ Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApexEmbeddedApkConfig) Reset() { *m = ApexEmbeddedApkConfig{} }
+func (m *ApexEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
+func (*ApexEmbeddedApkConfig) ProtoMessage() {}
+func (*ApexEmbeddedApkConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{12}
+}
+
+func (m *ApexEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApexEmbeddedApkConfig.Unmarshal(m, b)
+}
+func (m *ApexEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApexEmbeddedApkConfig.Marshal(b, m, deterministic)
+}
+func (m *ApexEmbeddedApkConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApexEmbeddedApkConfig.Merge(m, src)
+}
+func (m *ApexEmbeddedApkConfig) XXX_Size() int {
+ return xxx_messageInfo_ApexEmbeddedApkConfig.Size(m)
+}
+func (m *ApexEmbeddedApkConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApexEmbeddedApkConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexEmbeddedApkConfig proto.InternalMessageInfo
+
+func (m *ApexEmbeddedApkConfig) GetPackageName() string {
+ if m != nil {
+ return m.PackageName
+ }
+ return ""
+}
+
+func (m *ApexEmbeddedApkConfig) GetPath() string {
+ if m != nil {
+ return m.Path
+ }
+ return ""
+}
+
+type UnsignedEmbeddedApkConfig struct {
+ // Path to the APK inside the module (e.g. if the path inside the bundle
+ // is split/assets/example.apk, this will be assets/example.apk).
+ Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *UnsignedEmbeddedApkConfig) Reset() { *m = UnsignedEmbeddedApkConfig{} }
+func (m *UnsignedEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
+func (*UnsignedEmbeddedApkConfig) ProtoMessage() {}
+func (*UnsignedEmbeddedApkConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{13}
+}
+
+func (m *UnsignedEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_UnsignedEmbeddedApkConfig.Unmarshal(m, b)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_UnsignedEmbeddedApkConfig.Marshal(b, m, deterministic)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_UnsignedEmbeddedApkConfig.Merge(m, src)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_Size() int {
+ return xxx_messageInfo_UnsignedEmbeddedApkConfig.Size(m)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_UnsignedEmbeddedApkConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UnsignedEmbeddedApkConfig proto.InternalMessageInfo
+
+func (m *UnsignedEmbeddedApkConfig) GetPath() string {
+ if m != nil {
+ return m.Path
+ }
+ return ""
+}
+
+type AssetModulesConfig struct {
+ // App versionCodes that will be updated with these asset modules.
+ // Only relevant for asset-only bundles.
+ AppVersion []int64 `protobuf:"varint,1,rep,packed,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
+ // Version tag for the asset upload.
+ // Only relevant for asset-only bundles.
+ AssetVersionTag string `protobuf:"bytes,2,opt,name=asset_version_tag,json=assetVersionTag,proto3" json:"asset_version_tag,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *AssetModulesConfig) Reset() { *m = AssetModulesConfig{} }
+func (m *AssetModulesConfig) String() string { return proto.CompactTextString(m) }
+func (*AssetModulesConfig) ProtoMessage() {}
+func (*AssetModulesConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_3eaf2c85e69e9ea4, []int{14}
+}
+
+func (m *AssetModulesConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_AssetModulesConfig.Unmarshal(m, b)
+}
+func (m *AssetModulesConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_AssetModulesConfig.Marshal(b, m, deterministic)
+}
+func (m *AssetModulesConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_AssetModulesConfig.Merge(m, src)
+}
+func (m *AssetModulesConfig) XXX_Size() int {
+ return xxx_messageInfo_AssetModulesConfig.Size(m)
+}
+func (m *AssetModulesConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_AssetModulesConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetModulesConfig proto.InternalMessageInfo
+
+func (m *AssetModulesConfig) GetAppVersion() []int64 {
+ if m != nil {
+ return m.AppVersion
+ }
+ return nil
+}
+
+func (m *AssetModulesConfig) GetAssetVersionTag() string {
+ if m != nil {
+ return m.AssetVersionTag
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterEnum("android.bundle.BundleConfig_BundleType", BundleConfig_BundleType_name, BundleConfig_BundleType_value)
+ proto.RegisterEnum("android.bundle.SplitDimension_Value", SplitDimension_Value_name, SplitDimension_Value_value)
+ proto.RegisterType((*BundleConfig)(nil), "android.bundle.BundleConfig")
+ proto.RegisterType((*Bundletool)(nil), "android.bundle.Bundletool")
+ proto.RegisterType((*Compression)(nil), "android.bundle.Compression")
+ proto.RegisterType((*MasterResources)(nil), "android.bundle.MasterResources")
+ proto.RegisterType((*Optimizations)(nil), "android.bundle.Optimizations")
+ proto.RegisterType((*UncompressNativeLibraries)(nil), "android.bundle.UncompressNativeLibraries")
+ proto.RegisterType((*UncompressDexFiles)(nil), "android.bundle.UncompressDexFiles")
+ proto.RegisterType((*SplitsConfig)(nil), "android.bundle.SplitsConfig")
+ proto.RegisterType((*StandaloneConfig)(nil), "android.bundle.StandaloneConfig")
+ proto.RegisterType((*SplitDimension)(nil), "android.bundle.SplitDimension")
+ proto.RegisterType((*SuffixStripping)(nil), "android.bundle.SuffixStripping")
+ proto.RegisterType((*ApexConfig)(nil), "android.bundle.ApexConfig")
+ proto.RegisterType((*ApexEmbeddedApkConfig)(nil), "android.bundle.ApexEmbeddedApkConfig")
+ proto.RegisterType((*UnsignedEmbeddedApkConfig)(nil), "android.bundle.UnsignedEmbeddedApkConfig")
+ proto.RegisterType((*AssetModulesConfig)(nil), "android.bundle.AssetModulesConfig")
+}
+
+func init() {
+ proto.RegisterFile("config.proto", fileDescriptor_3eaf2c85e69e9ea4)
+}
+
+var fileDescriptor_3eaf2c85e69e9ea4 = []byte{
+ // 1001 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdb, 0x6e, 0xdb, 0x46,
+ 0x10, 0x0d, 0x75, 0xb1, 0xe5, 0x91, 0x2c, 0xd1, 0xdb, 0x38, 0x50, 0x2e, 0x4d, 0x5c, 0xa2, 0x41,
+ 0xdd, 0xb4, 0x50, 0x01, 0x3b, 0xcd, 0x83, 0x83, 0x3e, 0xd0, 0x32, 0xad, 0x2a, 0xd0, 0x0d, 0x4b,
+ 0xc9, 0x4d, 0x5a, 0xa0, 0x8b, 0x95, 0xb8, 0x52, 0xb7, 0xa6, 0x48, 0x82, 0x4b, 0x1a, 0x4a, 0xfb,
+ 0x09, 0x7d, 0xe9, 0x8f, 0xf4, 0xa7, 0xfa, 0x25, 0x05, 0x97, 0xa4, 0x2c, 0x51, 0x52, 0x9e, 0xfa,
+ 0x24, 0xce, 0xec, 0x39, 0xb3, 0x3b, 0xb3, 0x67, 0x67, 0x04, 0x95, 0x89, 0xeb, 0x4c, 0xf9, 0xac,
+ 0xe1, 0xf9, 0x6e, 0xe0, 0xa2, 0x2a, 0x75, 0x2c, 0xdf, 0xe5, 0x56, 0x63, 0x1c, 0x3a, 0x96, 0xcd,
+ 0xb4, 0xbf, 0x8a, 0x50, 0xb9, 0x94, 0x9f, 0x4d, 0x09, 0x43, 0x17, 0x00, 0xf1, 0x52, 0xe0, 0xba,
+ 0x76, 0x5d, 0x39, 0x51, 0x4e, 0xcb, 0x67, 0x4f, 0x1a, 0xeb, 0xac, 0xc6, 0xe5, 0x12, 0x81, 0x57,
+ 0xd0, 0xa8, 0x09, 0x87, 0xae, 0x17, 0xf0, 0x39, 0xff, 0x83, 0x06, 0xdc, 0x75, 0x44, 0x3d, 0x27,
+ 0xe9, 0x9f, 0x67, 0xe9, 0xfd, 0x55, 0x10, 0x5e, 0xe7, 0xa0, 0x1f, 0xa0, 0x3c, 0x71, 0xe7, 0x9e,
+ 0xcf, 0x84, 0xe0, 0xae, 0x53, 0xcf, 0xcb, 0x10, 0x4f, 0xb3, 0x21, 0x9a, 0xf7, 0x10, 0xbc, 0x8a,
+ 0x47, 0xef, 0x40, 0x9d, 0x53, 0x11, 0x30, 0x9f, 0xf8, 0x4c, 0xb8, 0xa1, 0x3f, 0x61, 0xa2, 0x5e,
+ 0x90, 0x31, 0x5e, 0x64, 0x63, 0x74, 0x25, 0x0e, 0xa7, 0x30, 0x5c, 0x9b, 0xaf, 0x3b, 0xd0, 0x5b,
+ 0x28, 0x53, 0x8f, 0x2d, 0x48, 0x5c, 0xc1, 0x7a, 0x71, 0x7b, 0x31, 0x74, 0x8f, 0x2d, 0xe2, 0xe2,
+ 0x61, 0xa0, 0xcb, 0x6f, 0xf4, 0x3b, 0x3c, 0x0b, 0x1d, 0xc1, 0x67, 0x0e, 0xb3, 0x08, 0x9b, 0x8f,
+ 0x99, 0x65, 0x31, 0x8b, 0x50, 0xef, 0x36, 0x8d, 0xb6, 0x77, 0x92, 0x3f, 0x2d, 0x9f, 0x7d, 0x9d,
+ 0x8d, 0x36, 0x4a, 0x38, 0x46, 0x42, 0xd1, 0xbd, 0xdb, 0x24, 0xf8, 0xe3, 0x70, 0xd7, 0x12, 0x1a,
+ 0xc2, 0x43, 0x2a, 0x04, 0x0b, 0xc8, 0xdc, 0xb5, 0x42, 0x9b, 0x89, 0x74, 0x8f, 0x7d, 0x79, 0x62,
+ 0x6d, 0xe3, 0xc4, 0x11, 0xb6, 0x1b, 0x43, 0x93, 0xe0, 0x88, 0x6e, 0xf8, 0xd0, 0x5b, 0x28, 0x04,
+ 0x1f, 0x3d, 0x56, 0x2f, 0x9d, 0x28, 0xa7, 0xd5, 0xb3, 0xaf, 0xb6, 0x8b, 0x20, 0xc6, 0x26, 0xc6,
+ 0xf0, 0xa3, 0xc7, 0xb0, 0x24, 0x69, 0xe7, 0x00, 0xf7, 0x3e, 0x54, 0x86, 0x7d, 0x6c, 0xb4, 0x46,
+ 0x1d, 0x1d, 0xab, 0x0f, 0x50, 0x09, 0x0a, 0xfa, 0xc0, 0x78, 0xaf, 0x2a, 0xa8, 0x0a, 0xa0, 0x9b,
+ 0xa6, 0x31, 0x24, 0xfd, 0x5e, 0xe7, 0x83, 0x9a, 0xd3, 0xbe, 0x4d, 0x49, 0x52, 0x4e, 0x75, 0xd8,
+ 0xbf, 0x63, 0xbe, 0x54, 0x41, 0x24, 0xa4, 0x03, 0x9c, 0x9a, 0xef, 0x0a, 0x25, 0x45, 0xcd, 0x69,
+ 0x17, 0x50, 0x5e, 0x91, 0x01, 0xfa, 0x06, 0x8e, 0x42, 0x27, 0x95, 0x02, 0xb3, 0xc8, 0xcc, 0x76,
+ 0xc7, 0x75, 0xe5, 0x24, 0x7f, 0x7a, 0x80, 0xd5, 0xd5, 0x85, 0x96, 0xed, 0x8e, 0xb5, 0x5f, 0xa0,
+ 0x96, 0xb9, 0x7e, 0xf4, 0x05, 0x54, 0x52, 0xc9, 0x10, 0x6e, 0x09, 0x49, 0x2d, 0xe2, 0x72, 0xea,
+ 0x6b, 0x5b, 0x02, 0xbd, 0x84, 0xea, 0x12, 0xe2, 0xd0, 0x39, 0x8b, 0x14, 0x1e, 0xc5, 0x3f, 0x4c,
+ 0xbd, 0xbd, 0xc8, 0xa9, 0xfd, 0x9b, 0x83, 0xc3, 0x35, 0x8d, 0x23, 0x1d, 0x0e, 0x85, 0x67, 0xf3,
+ 0x60, 0x79, 0x33, 0xf1, 0xc3, 0x7a, 0x96, 0xad, 0xa9, 0x29, 0x41, 0xc9, 0x9d, 0x54, 0xc4, 0x8a,
+ 0x85, 0x38, 0x3c, 0xbd, 0xcf, 0x82, 0x38, 0x34, 0xe0, 0x77, 0x8c, 0xd8, 0x7c, 0xec, 0x53, 0x9f,
+ 0xb3, 0xf4, 0xa9, 0x6d, 0x91, 0x53, 0x4a, 0xe9, 0x49, 0x46, 0x27, 0x25, 0x44, 0x72, 0xda, 0xb1,
+ 0x14, 0xc9, 0x69, 0x65, 0x2b, 0x8b, 0x2d, 0xc8, 0x94, 0xdb, 0x4c, 0x24, 0x6f, 0x51, 0xdb, 0xbd,
+ 0xc7, 0x15, 0x5b, 0x5c, 0x47, 0x48, 0x8c, 0xc2, 0x0d, 0x1f, 0xea, 0xc2, 0x91, 0x08, 0xa8, 0x63,
+ 0x51, 0xdb, 0x75, 0x58, 0x5a, 0x87, 0xf8, 0x69, 0x9e, 0x6c, 0xd4, 0x61, 0x09, 0x4c, 0x6a, 0xa1,
+ 0x8a, 0x8c, 0x47, 0xfb, 0x1e, 0x1e, 0xef, 0x4c, 0x2e, 0x92, 0x0e, 0x73, 0xe8, 0xd8, 0x66, 0x96,
+ 0xac, 0x74, 0x09, 0xa7, 0xa6, 0xd6, 0x00, 0xb4, 0x79, 0xde, 0x4f, 0xe0, 0x7f, 0x82, 0xca, 0xea,
+ 0xa5, 0xa0, 0x16, 0xd4, 0xe4, 0xb5, 0x10, 0x8b, 0xcf, 0x99, 0x23, 0xc5, 0xa9, 0xc8, 0x97, 0xfc,
+ 0x7c, 0xeb, 0x5d, 0x5e, 0xa5, 0x28, 0x5c, 0x15, 0x6b, 0xb6, 0xf6, 0xb7, 0x02, 0x6a, 0x36, 0xcd,
+ 0xff, 0x2d, 0x3a, 0x3a, 0x87, 0x47, 0x22, 0xf0, 0xb9, 0x47, 0xde, 0xbc, 0x26, 0x63, 0x1e, 0x64,
+ 0x84, 0x52, 0xc2, 0x9f, 0xc9, 0xd5, 0x37, 0xaf, 0x2f, 0x79, 0xb0, 0xac, 0x9a, 0xf6, 0x4f, 0x0e,
+ 0xaa, 0xeb, 0x71, 0xd1, 0x05, 0x14, 0xef, 0xa8, 0x1d, 0x32, 0x59, 0x96, 0xea, 0xd9, 0x97, 0x9f,
+ 0x3e, 0x46, 0xe3, 0x26, 0xc2, 0xe2, 0x98, 0x82, 0x1e, 0xc1, 0x9e, 0xc3, 0x66, 0x34, 0x60, 0xc9,
+ 0x9e, 0x89, 0x15, 0xb5, 0x68, 0x11, 0x4e, 0xa7, 0x7c, 0x41, 0xe4, 0x21, 0x3c, 0xee, 0xcc, 0x12,
+ 0x69, 0x6d, 0xb4, 0x68, 0x53, 0xe2, 0xcc, 0x14, 0x86, 0x6b, 0x62, 0xdd, 0xa1, 0xfd, 0x09, 0x45,
+ 0xb9, 0x27, 0x3a, 0x86, 0xa3, 0x51, 0xcf, 0x1c, 0x18, 0xcd, 0xf6, 0x75, 0xdb, 0xb8, 0x22, 0x37,
+ 0x7a, 0x67, 0x64, 0xa8, 0x0f, 0xd0, 0x3e, 0xe4, 0xf5, 0xcb, 0xb6, 0xaa, 0x20, 0x04, 0x55, 0xb3,
+ 0x89, 0x0d, 0xa3, 0x47, 0xae, 0x8c, 0x9e, 0xd9, 0x1e, 0x7e, 0x50, 0x73, 0xa8, 0x02, 0xa5, 0x8e,
+ 0xde, 0x6b, 0x8d, 0xf4, 0x96, 0xa1, 0xe6, 0xd1, 0x73, 0x78, 0x32, 0x34, 0xde, 0x0f, 0x47, 0xd8,
+ 0x20, 0xcd, 0x7e, 0x77, 0x80, 0x0d, 0xd3, 0x6c, 0xf7, 0x7b, 0xe4, 0xba, 0x8f, 0xbb, 0xfa, 0x50,
+ 0x2d, 0x20, 0x15, 0x2a, 0x2d, 0xac, 0x0f, 0x7e, 0x6c, 0x37, 0x4d, 0xa2, 0x0f, 0xda, 0x6a, 0x51,
+ 0xc3, 0x50, 0xcb, 0x1c, 0x70, 0xb7, 0x90, 0xa2, 0xde, 0x61, 0xb1, 0x29, 0x0d, 0xed, 0x80, 0xc4,
+ 0x49, 0x24, 0x4d, 0xed, 0x30, 0xf1, 0xc6, 0x91, 0x34, 0x1b, 0xe0, 0x7e, 0xa0, 0xa0, 0x5f, 0xa1,
+ 0x2e, 0x27, 0xd0, 0xb6, 0x01, 0x12, 0x0b, 0xe3, 0xe5, 0xb6, 0x71, 0xb4, 0x39, 0x3c, 0x8e, 0xe9,
+ 0x36, 0xb7, 0xd6, 0x83, 0xe3, 0xad, 0xf8, 0xa8, 0x19, 0x7a, 0x74, 0x72, 0x4b, 0x67, 0x71, 0xa3,
+ 0x93, 0xc9, 0x1c, 0xe0, 0x72, 0xe2, 0x8b, 0xda, 0x1c, 0x42, 0x50, 0xf0, 0x68, 0xf0, 0x5b, 0x92,
+ 0x86, 0xfc, 0xd6, 0xbe, 0x8b, 0x1e, 0xe5, 0xae, 0x29, 0x95, 0x12, 0x94, 0x15, 0x02, 0x05, 0xb4,
+ 0x39, 0x8d, 0xd0, 0x8b, 0x68, 0xf0, 0x7a, 0x24, 0xed, 0xfe, 0x51, 0xa6, 0xf9, 0x68, 0xb8, 0x7a,
+ 0x37, 0xb1, 0x07, 0xbd, 0x82, 0xa3, 0x78, 0xe0, 0x25, 0x10, 0x12, 0xd0, 0x59, 0x72, 0x90, 0x9a,
+ 0x5c, 0x48, 0x80, 0x43, 0x3a, 0xbb, 0x7c, 0x05, 0x68, 0xe2, 0xce, 0x33, 0x65, 0xfa, 0xf9, 0x61,
+ 0x62, 0x93, 0xd8, 0x26, 0xf2, 0xef, 0xd1, 0x78, 0x4f, 0xfe, 0x9c, 0xff, 0x17, 0x00, 0x00, 0xff,
+ 0xff, 0x6b, 0x05, 0xbf, 0x99, 0x35, 0x09, 0x00, 0x00,
+}
diff --git a/cmd/extract_apks/bundle_proto/config.proto b/cmd/extract_apks/bundle_proto/config.proto
new file mode 100644
index 0000000..1c161aa
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/config.proto
@@ -0,0 +1,162 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+syntax = "proto3";
+
+package android.bundle;
+
+option go_package = "android_bundle_proto";
+option java_package = "com.android.bundle";
+
+message BundleConfig {
+ Bundletool bundletool = 1;
+ Optimizations optimizations = 2;
+ Compression compression = 3;
+ // Resources to be always kept in the master split.
+ MasterResources master_resources = 4;
+ ApexConfig apex_config = 5;
+ // APKs to be signed with the same key as generated APKs.
+ repeated UnsignedEmbeddedApkConfig unsigned_embedded_apk_config = 6;
+ AssetModulesConfig asset_modules_config = 7;
+
+ enum BundleType {
+ REGULAR = 0;
+ APEX = 1;
+ ASSET_ONLY = 2;
+ }
+ BundleType type = 8;
+}
+
+message Bundletool {
+ reserved 1;
+ // Version of BundleTool used to build the Bundle.
+ string version = 2;
+}
+
+message Compression {
+ // Glob matching the list of files to leave uncompressed in the APKs.
+ // The matching is done against the path of files in the APK, thus excluding
+ // the name of the modules, and using forward slash ("/") as a name separator.
+ // Examples: "res/raw/**", "assets/**/*.uncompressed", etc.
+ repeated string uncompressed_glob = 1;
+}
+
+// Resources to keep in the master split.
+message MasterResources {
+ // Resource IDs to be kept in master split.
+ repeated int32 resource_ids = 1;
+ // Resource names to be kept in master split.
+ repeated string resource_names = 2;
+}
+
+message Optimizations {
+ SplitsConfig splits_config = 1;
+ // This is for uncompressing native libraries on M+ devices (L+ devices on
+ // instant apps).
+ UncompressNativeLibraries uncompress_native_libraries = 2;
+ // This is for uncompressing dex files on P+ devices.
+ UncompressDexFiles uncompress_dex_files = 3;
+ // Configuration for the generation of standalone APKs.
+ // If no StandaloneConfig is set, the configuration is inherited from
+ // splits_config.
+ StandaloneConfig standalone_config = 4;
+}
+
+message UncompressNativeLibraries {
+ bool enabled = 1;
+}
+
+message UncompressDexFiles {
+ bool enabled = 1;
+}
+
+// Optimization configuration used to generate Split APKs.
+message SplitsConfig {
+ repeated SplitDimension split_dimension = 1;
+}
+
+// Optimization configuration used to generate Standalone APKs.
+message StandaloneConfig {
+ // Device targeting dimensions to shard.
+ repeated SplitDimension split_dimension = 1;
+ // Whether 64 bit libraries should be stripped from Standalone APKs.
+ bool strip_64_bit_libraries = 2;
+}
+
+message SplitDimension {
+ enum Value {
+ UNSPECIFIED_VALUE = 0;
+ ABI = 1;
+ SCREEN_DENSITY = 2;
+ LANGUAGE = 3;
+ TEXTURE_COMPRESSION_FORMAT = 4;
+ // BEGIN-INTERNAL
+ GRAPHICS_API = 5;
+ // END-INTERNAL
+ }
+ Value value = 1;
+
+ // If set to 'true', indicates that APKs should *not* be split by this
+ // dimension.
+ bool negate = 2;
+
+ // Optional transformation to be applied to asset directories where
+ // the targeting is encoded in the directory name (e.g: assets/foo#tcf_etc1)
+ SuffixStripping suffix_stripping = 3;
+}
+
+message SuffixStripping {
+ // If set to 'true', indicates that the targeting suffix should be removed
+ // from assets paths for this dimension when splits (or asset slices) are
+ // generated.
+ // This only applies to assets.
+ // For example a folder with path "assets/level1_textures#tcf_etc1"
+ // would be outputted to "assets/level1_textures". File contents are
+ // unchanged.
+ bool enabled = 1;
+
+ // The default suffix to be used for the cases where separate slices can't
+ // be generated for this dimension. In the case of standalone/universal APKs
+ // generation, stripping the suffix can lead to file name collisions. This
+ // default suffix defines the directories to retain. The others are
+ // discarded: standalone/universal APKs will contain only directories
+ // targeted at this value for the dimension.
+ //
+ // If not set or empty, the fallback directory in each directory group will be
+ // used (for example, if both "assets/level1_textures#tcf_etc1" and
+ // "assets/level1_textures" are present and the default suffix is empty,
+ // then only "assets/level1_textures" will be used).
+ string default_suffix = 2;
+}
+
+// Configuration for processing APEX bundles.
+// https://source.android.com/devices/tech/ota/apex
+message ApexConfig {
+ // Configuration for processing of APKs embedded in an APEX image.
+ repeated ApexEmbeddedApkConfig apex_embedded_apk_config = 1;
+}
+
+message ApexEmbeddedApkConfig {
+ // Android package name of the APK.
+ string package_name = 1;
+
+ // Path to the APK within the APEX system image.
+ string path = 2;
+}
+
+message UnsignedEmbeddedApkConfig {
+ // Path to the APK inside the module (e.g. if the path inside the bundle
+ // is split/assets/example.apk, this will be assets/example.apk).
+ string path = 1;
+}
+
+message AssetModulesConfig {
+ // App versionCodes that will be updated with these asset modules.
+ // Only relevant for asset-only bundles.
+ repeated int64 app_version = 1;
+
+ // Version tag for the asset upload.
+ // Only relevant for asset-only bundles.
+ string asset_version_tag = 2;
+}
diff --git a/cmd/extract_apks/bundle_proto/regen.sh b/cmd/extract_apks/bundle_proto/regen.sh
new file mode 100755
index 0000000..89fb655
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/regen.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+# Generates the golang source file of protos file describing APK set table of
+# contents (toc.pb file).
+
+set -e
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+hash aprotoc &>/dev/null || die "could not find aprotoc. ${error_msg}"
+# TODO(asmundak): maybe have the paths relative to repo top?
+(cd "${0%/*}" && aprotoc --go_out=paths=source_relative:. commands.proto config.proto targeting.proto ) || die "build failed. ${error_msg}"
diff --git a/cmd/extract_apks/bundle_proto/targeting.pb.go b/cmd/extract_apks/bundle_proto/targeting.pb.go
new file mode 100644
index 0000000..187bc44
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/targeting.pb.go
@@ -0,0 +1,1734 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: targeting.proto
+
+package android_bundle_proto
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type ScreenDensity_DensityAlias int32
+
+const (
+ ScreenDensity_DENSITY_UNSPECIFIED ScreenDensity_DensityAlias = 0
+ ScreenDensity_NODPI ScreenDensity_DensityAlias = 1
+ ScreenDensity_LDPI ScreenDensity_DensityAlias = 2
+ ScreenDensity_MDPI ScreenDensity_DensityAlias = 3
+ ScreenDensity_TVDPI ScreenDensity_DensityAlias = 4
+ ScreenDensity_HDPI ScreenDensity_DensityAlias = 5
+ ScreenDensity_XHDPI ScreenDensity_DensityAlias = 6
+ ScreenDensity_XXHDPI ScreenDensity_DensityAlias = 7
+ ScreenDensity_XXXHDPI ScreenDensity_DensityAlias = 8
+)
+
+var ScreenDensity_DensityAlias_name = map[int32]string{
+ 0: "DENSITY_UNSPECIFIED",
+ 1: "NODPI",
+ 2: "LDPI",
+ 3: "MDPI",
+ 4: "TVDPI",
+ 5: "HDPI",
+ 6: "XHDPI",
+ 7: "XXHDPI",
+ 8: "XXXHDPI",
+}
+
+var ScreenDensity_DensityAlias_value = map[string]int32{
+ "DENSITY_UNSPECIFIED": 0,
+ "NODPI": 1,
+ "LDPI": 2,
+ "MDPI": 3,
+ "TVDPI": 4,
+ "HDPI": 5,
+ "XHDPI": 6,
+ "XXHDPI": 7,
+ "XXXHDPI": 8,
+}
+
+func (x ScreenDensity_DensityAlias) String() string {
+ return proto.EnumName(ScreenDensity_DensityAlias_name, int32(x))
+}
+
+func (ScreenDensity_DensityAlias) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{4, 0}
+}
+
+type TextureCompressionFormat_TextureCompressionFormatAlias int32
+
+const (
+ TextureCompressionFormat_UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT TextureCompressionFormat_TextureCompressionFormatAlias = 0
+ TextureCompressionFormat_ETC1_RGB8 TextureCompressionFormat_TextureCompressionFormatAlias = 1
+ TextureCompressionFormat_PALETTED TextureCompressionFormat_TextureCompressionFormatAlias = 2
+ TextureCompressionFormat_THREE_DC TextureCompressionFormat_TextureCompressionFormatAlias = 3
+ TextureCompressionFormat_ATC TextureCompressionFormat_TextureCompressionFormatAlias = 4
+ TextureCompressionFormat_LATC TextureCompressionFormat_TextureCompressionFormatAlias = 5
+ TextureCompressionFormat_DXT1 TextureCompressionFormat_TextureCompressionFormatAlias = 6
+ TextureCompressionFormat_S3TC TextureCompressionFormat_TextureCompressionFormatAlias = 7
+ TextureCompressionFormat_PVRTC TextureCompressionFormat_TextureCompressionFormatAlias = 8
+ TextureCompressionFormat_ASTC TextureCompressionFormat_TextureCompressionFormatAlias = 9
+ TextureCompressionFormat_ETC2 TextureCompressionFormat_TextureCompressionFormatAlias = 10
+)
+
+var TextureCompressionFormat_TextureCompressionFormatAlias_name = map[int32]string{
+ 0: "UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT",
+ 1: "ETC1_RGB8",
+ 2: "PALETTED",
+ 3: "THREE_DC",
+ 4: "ATC",
+ 5: "LATC",
+ 6: "DXT1",
+ 7: "S3TC",
+ 8: "PVRTC",
+ 9: "ASTC",
+ 10: "ETC2",
+}
+
+var TextureCompressionFormat_TextureCompressionFormatAlias_value = map[string]int32{
+ "UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT": 0,
+ "ETC1_RGB8": 1,
+ "PALETTED": 2,
+ "THREE_DC": 3,
+ "ATC": 4,
+ "LATC": 5,
+ "DXT1": 6,
+ "S3TC": 7,
+ "PVRTC": 8,
+ "ASTC": 9,
+ "ETC2": 10,
+}
+
+func (x TextureCompressionFormat_TextureCompressionFormatAlias) String() string {
+ return proto.EnumName(TextureCompressionFormat_TextureCompressionFormatAlias_name, int32(x))
+}
+
+func (TextureCompressionFormat_TextureCompressionFormatAlias) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{10, 0}
+}
+
+type Abi_AbiAlias int32
+
+const (
+ Abi_UNSPECIFIED_CPU_ARCHITECTURE Abi_AbiAlias = 0
+ Abi_ARMEABI Abi_AbiAlias = 1
+ Abi_ARMEABI_V7A Abi_AbiAlias = 2
+ Abi_ARM64_V8A Abi_AbiAlias = 3
+ Abi_X86 Abi_AbiAlias = 4
+ Abi_X86_64 Abi_AbiAlias = 5
+ Abi_MIPS Abi_AbiAlias = 6
+ Abi_MIPS64 Abi_AbiAlias = 7
+)
+
+var Abi_AbiAlias_name = map[int32]string{
+ 0: "UNSPECIFIED_CPU_ARCHITECTURE",
+ 1: "ARMEABI",
+ 2: "ARMEABI_V7A",
+ 3: "ARM64_V8A",
+ 4: "X86",
+ 5: "X86_64",
+ 6: "MIPS",
+ 7: "MIPS64",
+}
+
+var Abi_AbiAlias_value = map[string]int32{
+ "UNSPECIFIED_CPU_ARCHITECTURE": 0,
+ "ARMEABI": 1,
+ "ARMEABI_V7A": 2,
+ "ARM64_V8A": 3,
+ "X86": 4,
+ "X86_64": 5,
+ "MIPS": 6,
+ "MIPS64": 7,
+}
+
+func (x Abi_AbiAlias) String() string {
+ return proto.EnumName(Abi_AbiAlias_name, int32(x))
+}
+
+func (Abi_AbiAlias) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{11, 0}
+}
+
+type Sanitizer_SanitizerAlias int32
+
+const (
+ Sanitizer_NONE Sanitizer_SanitizerAlias = 0
+ Sanitizer_HWADDRESS Sanitizer_SanitizerAlias = 1
+)
+
+var Sanitizer_SanitizerAlias_name = map[int32]string{
+ 0: "NONE",
+ 1: "HWADDRESS",
+}
+
+var Sanitizer_SanitizerAlias_value = map[string]int32{
+ "NONE": 0,
+ "HWADDRESS": 1,
+}
+
+func (x Sanitizer_SanitizerAlias) String() string {
+ return proto.EnumName(Sanitizer_SanitizerAlias_name, int32(x))
+}
+
+func (Sanitizer_SanitizerAlias) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{13, 0}
+}
+
+// Targeting on the level of variants.
+type VariantTargeting struct {
+ SdkVersionTargeting *SdkVersionTargeting `protobuf:"bytes,1,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
+ AbiTargeting *AbiTargeting `protobuf:"bytes,2,opt,name=abi_targeting,json=abiTargeting,proto3" json:"abi_targeting,omitempty"`
+ ScreenDensityTargeting *ScreenDensityTargeting `protobuf:"bytes,3,opt,name=screen_density_targeting,json=screenDensityTargeting,proto3" json:"screen_density_targeting,omitempty"`
+ MultiAbiTargeting *MultiAbiTargeting `protobuf:"bytes,4,opt,name=multi_abi_targeting,json=multiAbiTargeting,proto3" json:"multi_abi_targeting,omitempty"`
+ TextureCompressionFormatTargeting *TextureCompressionFormatTargeting `protobuf:"bytes,5,opt,name=texture_compression_format_targeting,json=textureCompressionFormatTargeting,proto3" json:"texture_compression_format_targeting,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *VariantTargeting) Reset() { *m = VariantTargeting{} }
+func (m *VariantTargeting) String() string { return proto.CompactTextString(m) }
+func (*VariantTargeting) ProtoMessage() {}
+func (*VariantTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{0}
+}
+
+func (m *VariantTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_VariantTargeting.Unmarshal(m, b)
+}
+func (m *VariantTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_VariantTargeting.Marshal(b, m, deterministic)
+}
+func (m *VariantTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_VariantTargeting.Merge(m, src)
+}
+func (m *VariantTargeting) XXX_Size() int {
+ return xxx_messageInfo_VariantTargeting.Size(m)
+}
+func (m *VariantTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_VariantTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_VariantTargeting proto.InternalMessageInfo
+
+func (m *VariantTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+ if m != nil {
+ return m.SdkVersionTargeting
+ }
+ return nil
+}
+
+func (m *VariantTargeting) GetAbiTargeting() *AbiTargeting {
+ if m != nil {
+ return m.AbiTargeting
+ }
+ return nil
+}
+
+func (m *VariantTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
+ if m != nil {
+ return m.ScreenDensityTargeting
+ }
+ return nil
+}
+
+func (m *VariantTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
+ if m != nil {
+ return m.MultiAbiTargeting
+ }
+ return nil
+}
+
+func (m *VariantTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
+ if m != nil {
+ return m.TextureCompressionFormatTargeting
+ }
+ return nil
+}
+
+// Targeting on the level of individual APKs.
+type ApkTargeting struct {
+ AbiTargeting *AbiTargeting `protobuf:"bytes,1,opt,name=abi_targeting,json=abiTargeting,proto3" json:"abi_targeting,omitempty"`
+ GraphicsApiTargeting *GraphicsApiTargeting `protobuf:"bytes,2,opt,name=graphics_api_targeting,json=graphicsApiTargeting,proto3" json:"graphics_api_targeting,omitempty"`
+ LanguageTargeting *LanguageTargeting `protobuf:"bytes,3,opt,name=language_targeting,json=languageTargeting,proto3" json:"language_targeting,omitempty"`
+ ScreenDensityTargeting *ScreenDensityTargeting `protobuf:"bytes,4,opt,name=screen_density_targeting,json=screenDensityTargeting,proto3" json:"screen_density_targeting,omitempty"`
+ SdkVersionTargeting *SdkVersionTargeting `protobuf:"bytes,5,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
+ TextureCompressionFormatTargeting *TextureCompressionFormatTargeting `protobuf:"bytes,6,opt,name=texture_compression_format_targeting,json=textureCompressionFormatTargeting,proto3" json:"texture_compression_format_targeting,omitempty"`
+ MultiAbiTargeting *MultiAbiTargeting `protobuf:"bytes,7,opt,name=multi_abi_targeting,json=multiAbiTargeting,proto3" json:"multi_abi_targeting,omitempty"`
+ SanitizerTargeting *SanitizerTargeting `protobuf:"bytes,8,opt,name=sanitizer_targeting,json=sanitizerTargeting,proto3" json:"sanitizer_targeting,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApkTargeting) Reset() { *m = ApkTargeting{} }
+func (m *ApkTargeting) String() string { return proto.CompactTextString(m) }
+func (*ApkTargeting) ProtoMessage() {}
+func (*ApkTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{1}
+}
+
+func (m *ApkTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApkTargeting.Unmarshal(m, b)
+}
+func (m *ApkTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApkTargeting.Marshal(b, m, deterministic)
+}
+func (m *ApkTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApkTargeting.Merge(m, src)
+}
+func (m *ApkTargeting) XXX_Size() int {
+ return xxx_messageInfo_ApkTargeting.Size(m)
+}
+func (m *ApkTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApkTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApkTargeting proto.InternalMessageInfo
+
+func (m *ApkTargeting) GetAbiTargeting() *AbiTargeting {
+ if m != nil {
+ return m.AbiTargeting
+ }
+ return nil
+}
+
+func (m *ApkTargeting) GetGraphicsApiTargeting() *GraphicsApiTargeting {
+ if m != nil {
+ return m.GraphicsApiTargeting
+ }
+ return nil
+}
+
+func (m *ApkTargeting) GetLanguageTargeting() *LanguageTargeting {
+ if m != nil {
+ return m.LanguageTargeting
+ }
+ return nil
+}
+
+func (m *ApkTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
+ if m != nil {
+ return m.ScreenDensityTargeting
+ }
+ return nil
+}
+
+func (m *ApkTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+ if m != nil {
+ return m.SdkVersionTargeting
+ }
+ return nil
+}
+
+func (m *ApkTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
+ if m != nil {
+ return m.TextureCompressionFormatTargeting
+ }
+ return nil
+}
+
+func (m *ApkTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
+ if m != nil {
+ return m.MultiAbiTargeting
+ }
+ return nil
+}
+
+func (m *ApkTargeting) GetSanitizerTargeting() *SanitizerTargeting {
+ if m != nil {
+ return m.SanitizerTargeting
+ }
+ return nil
+}
+
+// Targeting on the module level.
+// The semantic of the targeting is the "AND" rule on all immediate values.
+type ModuleTargeting struct {
+ SdkVersionTargeting *SdkVersionTargeting `protobuf:"bytes,1,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
+ DeviceFeatureTargeting []*DeviceFeatureTargeting `protobuf:"bytes,2,rep,name=device_feature_targeting,json=deviceFeatureTargeting,proto3" json:"device_feature_targeting,omitempty"`
+ UserCountriesTargeting *UserCountriesTargeting `protobuf:"bytes,3,opt,name=user_countries_targeting,json=userCountriesTargeting,proto3" json:"user_countries_targeting,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ModuleTargeting) Reset() { *m = ModuleTargeting{} }
+func (m *ModuleTargeting) String() string { return proto.CompactTextString(m) }
+func (*ModuleTargeting) ProtoMessage() {}
+func (*ModuleTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{2}
+}
+
+func (m *ModuleTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ModuleTargeting.Unmarshal(m, b)
+}
+func (m *ModuleTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ModuleTargeting.Marshal(b, m, deterministic)
+}
+func (m *ModuleTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ModuleTargeting.Merge(m, src)
+}
+func (m *ModuleTargeting) XXX_Size() int {
+ return xxx_messageInfo_ModuleTargeting.Size(m)
+}
+func (m *ModuleTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_ModuleTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ModuleTargeting proto.InternalMessageInfo
+
+func (m *ModuleTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+ if m != nil {
+ return m.SdkVersionTargeting
+ }
+ return nil
+}
+
+func (m *ModuleTargeting) GetDeviceFeatureTargeting() []*DeviceFeatureTargeting {
+ if m != nil {
+ return m.DeviceFeatureTargeting
+ }
+ return nil
+}
+
+func (m *ModuleTargeting) GetUserCountriesTargeting() *UserCountriesTargeting {
+ if m != nil {
+ return m.UserCountriesTargeting
+ }
+ return nil
+}
+
+// User Countries targeting describing an inclusive/exclusive list of country
+// codes that module targets.
+type UserCountriesTargeting struct {
+ // List of country codes in the two-letter CLDR territory format.
+ CountryCodes []string `protobuf:"bytes,1,rep,name=country_codes,json=countryCodes,proto3" json:"country_codes,omitempty"`
+ // Indicates if the list above is exclusive.
+ Exclude bool `protobuf:"varint,2,opt,name=exclude,proto3" json:"exclude,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *UserCountriesTargeting) Reset() { *m = UserCountriesTargeting{} }
+func (m *UserCountriesTargeting) String() string { return proto.CompactTextString(m) }
+func (*UserCountriesTargeting) ProtoMessage() {}
+func (*UserCountriesTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{3}
+}
+
+func (m *UserCountriesTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_UserCountriesTargeting.Unmarshal(m, b)
+}
+func (m *UserCountriesTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_UserCountriesTargeting.Marshal(b, m, deterministic)
+}
+func (m *UserCountriesTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_UserCountriesTargeting.Merge(m, src)
+}
+func (m *UserCountriesTargeting) XXX_Size() int {
+ return xxx_messageInfo_UserCountriesTargeting.Size(m)
+}
+func (m *UserCountriesTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_UserCountriesTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UserCountriesTargeting proto.InternalMessageInfo
+
+func (m *UserCountriesTargeting) GetCountryCodes() []string {
+ if m != nil {
+ return m.CountryCodes
+ }
+ return nil
+}
+
+func (m *UserCountriesTargeting) GetExclude() bool {
+ if m != nil {
+ return m.Exclude
+ }
+ return false
+}
+
+type ScreenDensity struct {
+ // Types that are valid to be assigned to DensityOneof:
+ // *ScreenDensity_DensityAlias_
+ // *ScreenDensity_DensityDpi
+ DensityOneof isScreenDensity_DensityOneof `protobuf_oneof:"density_oneof"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ScreenDensity) Reset() { *m = ScreenDensity{} }
+func (m *ScreenDensity) String() string { return proto.CompactTextString(m) }
+func (*ScreenDensity) ProtoMessage() {}
+func (*ScreenDensity) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{4}
+}
+
+func (m *ScreenDensity) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ScreenDensity.Unmarshal(m, b)
+}
+func (m *ScreenDensity) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ScreenDensity.Marshal(b, m, deterministic)
+}
+func (m *ScreenDensity) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ScreenDensity.Merge(m, src)
+}
+func (m *ScreenDensity) XXX_Size() int {
+ return xxx_messageInfo_ScreenDensity.Size(m)
+}
+func (m *ScreenDensity) XXX_DiscardUnknown() {
+ xxx_messageInfo_ScreenDensity.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ScreenDensity proto.InternalMessageInfo
+
+type isScreenDensity_DensityOneof interface {
+ isScreenDensity_DensityOneof()
+}
+
+type ScreenDensity_DensityAlias_ struct {
+ DensityAlias ScreenDensity_DensityAlias `protobuf:"varint,1,opt,name=density_alias,json=densityAlias,proto3,enum=android.bundle.ScreenDensity_DensityAlias,oneof"`
+}
+
+type ScreenDensity_DensityDpi struct {
+ DensityDpi int32 `protobuf:"varint,2,opt,name=density_dpi,json=densityDpi,proto3,oneof"`
+}
+
+func (*ScreenDensity_DensityAlias_) isScreenDensity_DensityOneof() {}
+
+func (*ScreenDensity_DensityDpi) isScreenDensity_DensityOneof() {}
+
+func (m *ScreenDensity) GetDensityOneof() isScreenDensity_DensityOneof {
+ if m != nil {
+ return m.DensityOneof
+ }
+ return nil
+}
+
+func (m *ScreenDensity) GetDensityAlias() ScreenDensity_DensityAlias {
+ if x, ok := m.GetDensityOneof().(*ScreenDensity_DensityAlias_); ok {
+ return x.DensityAlias
+ }
+ return ScreenDensity_DENSITY_UNSPECIFIED
+}
+
+func (m *ScreenDensity) GetDensityDpi() int32 {
+ if x, ok := m.GetDensityOneof().(*ScreenDensity_DensityDpi); ok {
+ return x.DensityDpi
+ }
+ return 0
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*ScreenDensity) XXX_OneofWrappers() []interface{} {
+ return []interface{}{
+ (*ScreenDensity_DensityAlias_)(nil),
+ (*ScreenDensity_DensityDpi)(nil),
+ }
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+type Int32Value struct {
+ // The int32 value.
+ Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Int32Value) Reset() { *m = Int32Value{} }
+func (m *Int32Value) String() string { return proto.CompactTextString(m) }
+func (*Int32Value) ProtoMessage() {}
+func (*Int32Value) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{5}
+}
+
+func (m *Int32Value) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Int32Value.Unmarshal(m, b)
+}
+func (m *Int32Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Int32Value.Marshal(b, m, deterministic)
+}
+func (m *Int32Value) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Int32Value.Merge(m, src)
+}
+func (m *Int32Value) XXX_Size() int {
+ return xxx_messageInfo_Int32Value.Size(m)
+}
+func (m *Int32Value) XXX_DiscardUnknown() {
+ xxx_messageInfo_Int32Value.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Int32Value proto.InternalMessageInfo
+
+func (m *Int32Value) GetValue() int32 {
+ if m != nil {
+ return m.Value
+ }
+ return 0
+}
+
+type SdkVersion struct {
+ // Inclusive.
+ Min *Int32Value `protobuf:"bytes,1,opt,name=min,proto3" json:"min,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SdkVersion) Reset() { *m = SdkVersion{} }
+func (m *SdkVersion) String() string { return proto.CompactTextString(m) }
+func (*SdkVersion) ProtoMessage() {}
+func (*SdkVersion) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{6}
+}
+
+func (m *SdkVersion) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SdkVersion.Unmarshal(m, b)
+}
+func (m *SdkVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SdkVersion.Marshal(b, m, deterministic)
+}
+func (m *SdkVersion) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SdkVersion.Merge(m, src)
+}
+func (m *SdkVersion) XXX_Size() int {
+ return xxx_messageInfo_SdkVersion.Size(m)
+}
+func (m *SdkVersion) XXX_DiscardUnknown() {
+ xxx_messageInfo_SdkVersion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SdkVersion proto.InternalMessageInfo
+
+func (m *SdkVersion) GetMin() *Int32Value {
+ if m != nil {
+ return m.Min
+ }
+ return nil
+}
+
+type GraphicsApi struct {
+ // Types that are valid to be assigned to ApiOneof:
+ // *GraphicsApi_MinOpenGlVersion
+ // *GraphicsApi_MinVulkanVersion
+ ApiOneof isGraphicsApi_ApiOneof `protobuf_oneof:"api_oneof"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *GraphicsApi) Reset() { *m = GraphicsApi{} }
+func (m *GraphicsApi) String() string { return proto.CompactTextString(m) }
+func (*GraphicsApi) ProtoMessage() {}
+func (*GraphicsApi) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{7}
+}
+
+func (m *GraphicsApi) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_GraphicsApi.Unmarshal(m, b)
+}
+func (m *GraphicsApi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_GraphicsApi.Marshal(b, m, deterministic)
+}
+func (m *GraphicsApi) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_GraphicsApi.Merge(m, src)
+}
+func (m *GraphicsApi) XXX_Size() int {
+ return xxx_messageInfo_GraphicsApi.Size(m)
+}
+func (m *GraphicsApi) XXX_DiscardUnknown() {
+ xxx_messageInfo_GraphicsApi.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GraphicsApi proto.InternalMessageInfo
+
+type isGraphicsApi_ApiOneof interface {
+ isGraphicsApi_ApiOneof()
+}
+
+type GraphicsApi_MinOpenGlVersion struct {
+ MinOpenGlVersion *OpenGlVersion `protobuf:"bytes,1,opt,name=min_open_gl_version,json=minOpenGlVersion,proto3,oneof"`
+}
+
+type GraphicsApi_MinVulkanVersion struct {
+ MinVulkanVersion *VulkanVersion `protobuf:"bytes,2,opt,name=min_vulkan_version,json=minVulkanVersion,proto3,oneof"`
+}
+
+func (*GraphicsApi_MinOpenGlVersion) isGraphicsApi_ApiOneof() {}
+
+func (*GraphicsApi_MinVulkanVersion) isGraphicsApi_ApiOneof() {}
+
+func (m *GraphicsApi) GetApiOneof() isGraphicsApi_ApiOneof {
+ if m != nil {
+ return m.ApiOneof
+ }
+ return nil
+}
+
+func (m *GraphicsApi) GetMinOpenGlVersion() *OpenGlVersion {
+ if x, ok := m.GetApiOneof().(*GraphicsApi_MinOpenGlVersion); ok {
+ return x.MinOpenGlVersion
+ }
+ return nil
+}
+
+func (m *GraphicsApi) GetMinVulkanVersion() *VulkanVersion {
+ if x, ok := m.GetApiOneof().(*GraphicsApi_MinVulkanVersion); ok {
+ return x.MinVulkanVersion
+ }
+ return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*GraphicsApi) XXX_OneofWrappers() []interface{} {
+ return []interface{}{
+ (*GraphicsApi_MinOpenGlVersion)(nil),
+ (*GraphicsApi_MinVulkanVersion)(nil),
+ }
+}
+
+type VulkanVersion struct {
+ Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"`
+ Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *VulkanVersion) Reset() { *m = VulkanVersion{} }
+func (m *VulkanVersion) String() string { return proto.CompactTextString(m) }
+func (*VulkanVersion) ProtoMessage() {}
+func (*VulkanVersion) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{8}
+}
+
+func (m *VulkanVersion) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_VulkanVersion.Unmarshal(m, b)
+}
+func (m *VulkanVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_VulkanVersion.Marshal(b, m, deterministic)
+}
+func (m *VulkanVersion) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_VulkanVersion.Merge(m, src)
+}
+func (m *VulkanVersion) XXX_Size() int {
+ return xxx_messageInfo_VulkanVersion.Size(m)
+}
+func (m *VulkanVersion) XXX_DiscardUnknown() {
+ xxx_messageInfo_VulkanVersion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_VulkanVersion proto.InternalMessageInfo
+
+func (m *VulkanVersion) GetMajor() int32 {
+ if m != nil {
+ return m.Major
+ }
+ return 0
+}
+
+func (m *VulkanVersion) GetMinor() int32 {
+ if m != nil {
+ return m.Minor
+ }
+ return 0
+}
+
+type OpenGlVersion struct {
+ // e.g. OpenGL ES 3.2 is represented as { major: 3, minor: 2 }
+ Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"`
+ Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *OpenGlVersion) Reset() { *m = OpenGlVersion{} }
+func (m *OpenGlVersion) String() string { return proto.CompactTextString(m) }
+func (*OpenGlVersion) ProtoMessage() {}
+func (*OpenGlVersion) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{9}
+}
+
+func (m *OpenGlVersion) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_OpenGlVersion.Unmarshal(m, b)
+}
+func (m *OpenGlVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_OpenGlVersion.Marshal(b, m, deterministic)
+}
+func (m *OpenGlVersion) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_OpenGlVersion.Merge(m, src)
+}
+func (m *OpenGlVersion) XXX_Size() int {
+ return xxx_messageInfo_OpenGlVersion.Size(m)
+}
+func (m *OpenGlVersion) XXX_DiscardUnknown() {
+ xxx_messageInfo_OpenGlVersion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_OpenGlVersion proto.InternalMessageInfo
+
+func (m *OpenGlVersion) GetMajor() int32 {
+ if m != nil {
+ return m.Major
+ }
+ return 0
+}
+
+func (m *OpenGlVersion) GetMinor() int32 {
+ if m != nil {
+ return m.Minor
+ }
+ return 0
+}
+
+type TextureCompressionFormat struct {
+ Alias TextureCompressionFormat_TextureCompressionFormatAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.TextureCompressionFormat_TextureCompressionFormatAlias" json:"alias,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *TextureCompressionFormat) Reset() { *m = TextureCompressionFormat{} }
+func (m *TextureCompressionFormat) String() string { return proto.CompactTextString(m) }
+func (*TextureCompressionFormat) ProtoMessage() {}
+func (*TextureCompressionFormat) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{10}
+}
+
+func (m *TextureCompressionFormat) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_TextureCompressionFormat.Unmarshal(m, b)
+}
+func (m *TextureCompressionFormat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_TextureCompressionFormat.Marshal(b, m, deterministic)
+}
+func (m *TextureCompressionFormat) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_TextureCompressionFormat.Merge(m, src)
+}
+func (m *TextureCompressionFormat) XXX_Size() int {
+ return xxx_messageInfo_TextureCompressionFormat.Size(m)
+}
+func (m *TextureCompressionFormat) XXX_DiscardUnknown() {
+ xxx_messageInfo_TextureCompressionFormat.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TextureCompressionFormat proto.InternalMessageInfo
+
+func (m *TextureCompressionFormat) GetAlias() TextureCompressionFormat_TextureCompressionFormatAlias {
+ if m != nil {
+ return m.Alias
+ }
+ return TextureCompressionFormat_UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT
+}
+
+type Abi struct {
+ Alias Abi_AbiAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Abi_AbiAlias" json:"alias,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Abi) Reset() { *m = Abi{} }
+func (m *Abi) String() string { return proto.CompactTextString(m) }
+func (*Abi) ProtoMessage() {}
+func (*Abi) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{11}
+}
+
+func (m *Abi) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Abi.Unmarshal(m, b)
+}
+func (m *Abi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Abi.Marshal(b, m, deterministic)
+}
+func (m *Abi) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Abi.Merge(m, src)
+}
+func (m *Abi) XXX_Size() int {
+ return xxx_messageInfo_Abi.Size(m)
+}
+func (m *Abi) XXX_DiscardUnknown() {
+ xxx_messageInfo_Abi.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Abi proto.InternalMessageInfo
+
+func (m *Abi) GetAlias() Abi_AbiAlias {
+ if m != nil {
+ return m.Alias
+ }
+ return Abi_UNSPECIFIED_CPU_ARCHITECTURE
+}
+
+type MultiAbi struct {
+ Abi []*Abi `protobuf:"bytes,1,rep,name=abi,proto3" json:"abi,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *MultiAbi) Reset() { *m = MultiAbi{} }
+func (m *MultiAbi) String() string { return proto.CompactTextString(m) }
+func (*MultiAbi) ProtoMessage() {}
+func (*MultiAbi) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{12}
+}
+
+func (m *MultiAbi) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_MultiAbi.Unmarshal(m, b)
+}
+func (m *MultiAbi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_MultiAbi.Marshal(b, m, deterministic)
+}
+func (m *MultiAbi) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MultiAbi.Merge(m, src)
+}
+func (m *MultiAbi) XXX_Size() int {
+ return xxx_messageInfo_MultiAbi.Size(m)
+}
+func (m *MultiAbi) XXX_DiscardUnknown() {
+ xxx_messageInfo_MultiAbi.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MultiAbi proto.InternalMessageInfo
+
+func (m *MultiAbi) GetAbi() []*Abi {
+ if m != nil {
+ return m.Abi
+ }
+ return nil
+}
+
+type Sanitizer struct {
+ Alias Sanitizer_SanitizerAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Sanitizer_SanitizerAlias" json:"alias,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Sanitizer) Reset() { *m = Sanitizer{} }
+func (m *Sanitizer) String() string { return proto.CompactTextString(m) }
+func (*Sanitizer) ProtoMessage() {}
+func (*Sanitizer) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{13}
+}
+
+func (m *Sanitizer) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Sanitizer.Unmarshal(m, b)
+}
+func (m *Sanitizer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Sanitizer.Marshal(b, m, deterministic)
+}
+func (m *Sanitizer) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Sanitizer.Merge(m, src)
+}
+func (m *Sanitizer) XXX_Size() int {
+ return xxx_messageInfo_Sanitizer.Size(m)
+}
+func (m *Sanitizer) XXX_DiscardUnknown() {
+ xxx_messageInfo_Sanitizer.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Sanitizer proto.InternalMessageInfo
+
+func (m *Sanitizer) GetAlias() Sanitizer_SanitizerAlias {
+ if m != nil {
+ return m.Alias
+ }
+ return Sanitizer_NONE
+}
+
+type DeviceFeature struct {
+ FeatureName string `protobuf:"bytes,1,opt,name=feature_name,json=featureName,proto3" json:"feature_name,omitempty"`
+ // Equivalent of android:glEsVersion or android:version in <uses-feature>.
+ FeatureVersion int32 `protobuf:"varint,2,opt,name=feature_version,json=featureVersion,proto3" json:"feature_version,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *DeviceFeature) Reset() { *m = DeviceFeature{} }
+func (m *DeviceFeature) String() string { return proto.CompactTextString(m) }
+func (*DeviceFeature) ProtoMessage() {}
+func (*DeviceFeature) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{14}
+}
+
+func (m *DeviceFeature) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_DeviceFeature.Unmarshal(m, b)
+}
+func (m *DeviceFeature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_DeviceFeature.Marshal(b, m, deterministic)
+}
+func (m *DeviceFeature) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_DeviceFeature.Merge(m, src)
+}
+func (m *DeviceFeature) XXX_Size() int {
+ return xxx_messageInfo_DeviceFeature.Size(m)
+}
+func (m *DeviceFeature) XXX_DiscardUnknown() {
+ xxx_messageInfo_DeviceFeature.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DeviceFeature proto.InternalMessageInfo
+
+func (m *DeviceFeature) GetFeatureName() string {
+ if m != nil {
+ return m.FeatureName
+ }
+ return ""
+}
+
+func (m *DeviceFeature) GetFeatureVersion() int32 {
+ if m != nil {
+ return m.FeatureVersion
+ }
+ return 0
+}
+
+// Targeting specific for directories under assets/.
+type AssetsDirectoryTargeting struct {
+ Abi *AbiTargeting `protobuf:"bytes,1,opt,name=abi,proto3" json:"abi,omitempty"`
+ GraphicsApi *GraphicsApiTargeting `protobuf:"bytes,2,opt,name=graphics_api,json=graphicsApi,proto3" json:"graphics_api,omitempty"`
+ TextureCompressionFormat *TextureCompressionFormatTargeting `protobuf:"bytes,3,opt,name=texture_compression_format,json=textureCompressionFormat,proto3" json:"texture_compression_format,omitempty"`
+ Language *LanguageTargeting `protobuf:"bytes,4,opt,name=language,proto3" json:"language,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *AssetsDirectoryTargeting) Reset() { *m = AssetsDirectoryTargeting{} }
+func (m *AssetsDirectoryTargeting) String() string { return proto.CompactTextString(m) }
+func (*AssetsDirectoryTargeting) ProtoMessage() {}
+func (*AssetsDirectoryTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{15}
+}
+
+func (m *AssetsDirectoryTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_AssetsDirectoryTargeting.Unmarshal(m, b)
+}
+func (m *AssetsDirectoryTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_AssetsDirectoryTargeting.Marshal(b, m, deterministic)
+}
+func (m *AssetsDirectoryTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_AssetsDirectoryTargeting.Merge(m, src)
+}
+func (m *AssetsDirectoryTargeting) XXX_Size() int {
+ return xxx_messageInfo_AssetsDirectoryTargeting.Size(m)
+}
+func (m *AssetsDirectoryTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_AssetsDirectoryTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetsDirectoryTargeting proto.InternalMessageInfo
+
+func (m *AssetsDirectoryTargeting) GetAbi() *AbiTargeting {
+ if m != nil {
+ return m.Abi
+ }
+ return nil
+}
+
+func (m *AssetsDirectoryTargeting) GetGraphicsApi() *GraphicsApiTargeting {
+ if m != nil {
+ return m.GraphicsApi
+ }
+ return nil
+}
+
+func (m *AssetsDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormatTargeting {
+ if m != nil {
+ return m.TextureCompressionFormat
+ }
+ return nil
+}
+
+func (m *AssetsDirectoryTargeting) GetLanguage() *LanguageTargeting {
+ if m != nil {
+ return m.Language
+ }
+ return nil
+}
+
+// Targeting specific for directories under lib/.
+type NativeDirectoryTargeting struct {
+ Abi *Abi `protobuf:"bytes,1,opt,name=abi,proto3" json:"abi,omitempty"`
+ GraphicsApi *GraphicsApi `protobuf:"bytes,2,opt,name=graphics_api,json=graphicsApi,proto3" json:"graphics_api,omitempty"`
+ TextureCompressionFormat *TextureCompressionFormat `protobuf:"bytes,3,opt,name=texture_compression_format,json=textureCompressionFormat,proto3" json:"texture_compression_format,omitempty"`
+ Sanitizer *Sanitizer `protobuf:"bytes,4,opt,name=sanitizer,proto3" json:"sanitizer,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *NativeDirectoryTargeting) Reset() { *m = NativeDirectoryTargeting{} }
+func (m *NativeDirectoryTargeting) String() string { return proto.CompactTextString(m) }
+func (*NativeDirectoryTargeting) ProtoMessage() {}
+func (*NativeDirectoryTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{16}
+}
+
+func (m *NativeDirectoryTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_NativeDirectoryTargeting.Unmarshal(m, b)
+}
+func (m *NativeDirectoryTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_NativeDirectoryTargeting.Marshal(b, m, deterministic)
+}
+func (m *NativeDirectoryTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_NativeDirectoryTargeting.Merge(m, src)
+}
+func (m *NativeDirectoryTargeting) XXX_Size() int {
+ return xxx_messageInfo_NativeDirectoryTargeting.Size(m)
+}
+func (m *NativeDirectoryTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_NativeDirectoryTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_NativeDirectoryTargeting proto.InternalMessageInfo
+
+func (m *NativeDirectoryTargeting) GetAbi() *Abi {
+ if m != nil {
+ return m.Abi
+ }
+ return nil
+}
+
+func (m *NativeDirectoryTargeting) GetGraphicsApi() *GraphicsApi {
+ if m != nil {
+ return m.GraphicsApi
+ }
+ return nil
+}
+
+func (m *NativeDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormat {
+ if m != nil {
+ return m.TextureCompressionFormat
+ }
+ return nil
+}
+
+func (m *NativeDirectoryTargeting) GetSanitizer() *Sanitizer {
+ if m != nil {
+ return m.Sanitizer
+ }
+ return nil
+}
+
+// Targeting specific for image files under apex/.
+type ApexImageTargeting struct {
+ MultiAbi *MultiAbiTargeting `protobuf:"bytes,1,opt,name=multi_abi,json=multiAbi,proto3" json:"multi_abi,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApexImageTargeting) Reset() { *m = ApexImageTargeting{} }
+func (m *ApexImageTargeting) String() string { return proto.CompactTextString(m) }
+func (*ApexImageTargeting) ProtoMessage() {}
+func (*ApexImageTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{17}
+}
+
+func (m *ApexImageTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApexImageTargeting.Unmarshal(m, b)
+}
+func (m *ApexImageTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApexImageTargeting.Marshal(b, m, deterministic)
+}
+func (m *ApexImageTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApexImageTargeting.Merge(m, src)
+}
+func (m *ApexImageTargeting) XXX_Size() int {
+ return xxx_messageInfo_ApexImageTargeting.Size(m)
+}
+func (m *ApexImageTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApexImageTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexImageTargeting proto.InternalMessageInfo
+
+func (m *ApexImageTargeting) GetMultiAbi() *MultiAbiTargeting {
+ if m != nil {
+ return m.MultiAbi
+ }
+ return nil
+}
+
+type AbiTargeting struct {
+ Value []*Abi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ Alternatives []*Abi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *AbiTargeting) Reset() { *m = AbiTargeting{} }
+func (m *AbiTargeting) String() string { return proto.CompactTextString(m) }
+func (*AbiTargeting) ProtoMessage() {}
+func (*AbiTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{18}
+}
+
+func (m *AbiTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_AbiTargeting.Unmarshal(m, b)
+}
+func (m *AbiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_AbiTargeting.Marshal(b, m, deterministic)
+}
+func (m *AbiTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_AbiTargeting.Merge(m, src)
+}
+func (m *AbiTargeting) XXX_Size() int {
+ return xxx_messageInfo_AbiTargeting.Size(m)
+}
+func (m *AbiTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_AbiTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AbiTargeting proto.InternalMessageInfo
+
+func (m *AbiTargeting) GetValue() []*Abi {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *AbiTargeting) GetAlternatives() []*Abi {
+ if m != nil {
+ return m.Alternatives
+ }
+ return nil
+}
+
+type MultiAbiTargeting struct {
+ Value []*MultiAbi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ Alternatives []*MultiAbi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *MultiAbiTargeting) Reset() { *m = MultiAbiTargeting{} }
+func (m *MultiAbiTargeting) String() string { return proto.CompactTextString(m) }
+func (*MultiAbiTargeting) ProtoMessage() {}
+func (*MultiAbiTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{19}
+}
+
+func (m *MultiAbiTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_MultiAbiTargeting.Unmarshal(m, b)
+}
+func (m *MultiAbiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_MultiAbiTargeting.Marshal(b, m, deterministic)
+}
+func (m *MultiAbiTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MultiAbiTargeting.Merge(m, src)
+}
+func (m *MultiAbiTargeting) XXX_Size() int {
+ return xxx_messageInfo_MultiAbiTargeting.Size(m)
+}
+func (m *MultiAbiTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_MultiAbiTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MultiAbiTargeting proto.InternalMessageInfo
+
+func (m *MultiAbiTargeting) GetValue() []*MultiAbi {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *MultiAbiTargeting) GetAlternatives() []*MultiAbi {
+ if m != nil {
+ return m.Alternatives
+ }
+ return nil
+}
+
+type ScreenDensityTargeting struct {
+ Value []*ScreenDensity `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ Alternatives []*ScreenDensity `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ScreenDensityTargeting) Reset() { *m = ScreenDensityTargeting{} }
+func (m *ScreenDensityTargeting) String() string { return proto.CompactTextString(m) }
+func (*ScreenDensityTargeting) ProtoMessage() {}
+func (*ScreenDensityTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{20}
+}
+
+func (m *ScreenDensityTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ScreenDensityTargeting.Unmarshal(m, b)
+}
+func (m *ScreenDensityTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ScreenDensityTargeting.Marshal(b, m, deterministic)
+}
+func (m *ScreenDensityTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ScreenDensityTargeting.Merge(m, src)
+}
+func (m *ScreenDensityTargeting) XXX_Size() int {
+ return xxx_messageInfo_ScreenDensityTargeting.Size(m)
+}
+func (m *ScreenDensityTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_ScreenDensityTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ScreenDensityTargeting proto.InternalMessageInfo
+
+func (m *ScreenDensityTargeting) GetValue() []*ScreenDensity {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *ScreenDensityTargeting) GetAlternatives() []*ScreenDensity {
+ if m != nil {
+ return m.Alternatives
+ }
+ return nil
+}
+
+type LanguageTargeting struct {
+ // ISO-639: 2 or 3 letter language code.
+ Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ Alternatives []string `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *LanguageTargeting) Reset() { *m = LanguageTargeting{} }
+func (m *LanguageTargeting) String() string { return proto.CompactTextString(m) }
+func (*LanguageTargeting) ProtoMessage() {}
+func (*LanguageTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{21}
+}
+
+func (m *LanguageTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_LanguageTargeting.Unmarshal(m, b)
+}
+func (m *LanguageTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_LanguageTargeting.Marshal(b, m, deterministic)
+}
+func (m *LanguageTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_LanguageTargeting.Merge(m, src)
+}
+func (m *LanguageTargeting) XXX_Size() int {
+ return xxx_messageInfo_LanguageTargeting.Size(m)
+}
+func (m *LanguageTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_LanguageTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LanguageTargeting proto.InternalMessageInfo
+
+func (m *LanguageTargeting) GetValue() []string {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *LanguageTargeting) GetAlternatives() []string {
+ if m != nil {
+ return m.Alternatives
+ }
+ return nil
+}
+
+type GraphicsApiTargeting struct {
+ Value []*GraphicsApi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ Alternatives []*GraphicsApi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *GraphicsApiTargeting) Reset() { *m = GraphicsApiTargeting{} }
+func (m *GraphicsApiTargeting) String() string { return proto.CompactTextString(m) }
+func (*GraphicsApiTargeting) ProtoMessage() {}
+func (*GraphicsApiTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{22}
+}
+
+func (m *GraphicsApiTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_GraphicsApiTargeting.Unmarshal(m, b)
+}
+func (m *GraphicsApiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_GraphicsApiTargeting.Marshal(b, m, deterministic)
+}
+func (m *GraphicsApiTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_GraphicsApiTargeting.Merge(m, src)
+}
+func (m *GraphicsApiTargeting) XXX_Size() int {
+ return xxx_messageInfo_GraphicsApiTargeting.Size(m)
+}
+func (m *GraphicsApiTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_GraphicsApiTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GraphicsApiTargeting proto.InternalMessageInfo
+
+func (m *GraphicsApiTargeting) GetValue() []*GraphicsApi {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *GraphicsApiTargeting) GetAlternatives() []*GraphicsApi {
+ if m != nil {
+ return m.Alternatives
+ }
+ return nil
+}
+
+type SdkVersionTargeting struct {
+ Value []*SdkVersion `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ Alternatives []*SdkVersion `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SdkVersionTargeting) Reset() { *m = SdkVersionTargeting{} }
+func (m *SdkVersionTargeting) String() string { return proto.CompactTextString(m) }
+func (*SdkVersionTargeting) ProtoMessage() {}
+func (*SdkVersionTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{23}
+}
+
+func (m *SdkVersionTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SdkVersionTargeting.Unmarshal(m, b)
+}
+func (m *SdkVersionTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SdkVersionTargeting.Marshal(b, m, deterministic)
+}
+func (m *SdkVersionTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SdkVersionTargeting.Merge(m, src)
+}
+func (m *SdkVersionTargeting) XXX_Size() int {
+ return xxx_messageInfo_SdkVersionTargeting.Size(m)
+}
+func (m *SdkVersionTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_SdkVersionTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SdkVersionTargeting proto.InternalMessageInfo
+
+func (m *SdkVersionTargeting) GetValue() []*SdkVersion {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *SdkVersionTargeting) GetAlternatives() []*SdkVersion {
+ if m != nil {
+ return m.Alternatives
+ }
+ return nil
+}
+
+type TextureCompressionFormatTargeting struct {
+ Value []*TextureCompressionFormat `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ Alternatives []*TextureCompressionFormat `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *TextureCompressionFormatTargeting) Reset() { *m = TextureCompressionFormatTargeting{} }
+func (m *TextureCompressionFormatTargeting) String() string { return proto.CompactTextString(m) }
+func (*TextureCompressionFormatTargeting) ProtoMessage() {}
+func (*TextureCompressionFormatTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{24}
+}
+
+func (m *TextureCompressionFormatTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_TextureCompressionFormatTargeting.Unmarshal(m, b)
+}
+func (m *TextureCompressionFormatTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_TextureCompressionFormatTargeting.Marshal(b, m, deterministic)
+}
+func (m *TextureCompressionFormatTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_TextureCompressionFormatTargeting.Merge(m, src)
+}
+func (m *TextureCompressionFormatTargeting) XXX_Size() int {
+ return xxx_messageInfo_TextureCompressionFormatTargeting.Size(m)
+}
+func (m *TextureCompressionFormatTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_TextureCompressionFormatTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TextureCompressionFormatTargeting proto.InternalMessageInfo
+
+func (m *TextureCompressionFormatTargeting) GetValue() []*TextureCompressionFormat {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *TextureCompressionFormatTargeting) GetAlternatives() []*TextureCompressionFormat {
+ if m != nil {
+ return m.Alternatives
+ }
+ return nil
+}
+
+type SanitizerTargeting struct {
+ Value []*Sanitizer `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SanitizerTargeting) Reset() { *m = SanitizerTargeting{} }
+func (m *SanitizerTargeting) String() string { return proto.CompactTextString(m) }
+func (*SanitizerTargeting) ProtoMessage() {}
+func (*SanitizerTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{25}
+}
+
+func (m *SanitizerTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SanitizerTargeting.Unmarshal(m, b)
+}
+func (m *SanitizerTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SanitizerTargeting.Marshal(b, m, deterministic)
+}
+func (m *SanitizerTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SanitizerTargeting.Merge(m, src)
+}
+func (m *SanitizerTargeting) XXX_Size() int {
+ return xxx_messageInfo_SanitizerTargeting.Size(m)
+}
+func (m *SanitizerTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_SanitizerTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SanitizerTargeting proto.InternalMessageInfo
+
+func (m *SanitizerTargeting) GetValue() []*Sanitizer {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+// Since other atom targeting messages have the "OR" semantic on values
+// the DeviceFeatureTargeting represents only one device feature to retain
+// that convention.
+type DeviceFeatureTargeting struct {
+ RequiredFeature *DeviceFeature `protobuf:"bytes,1,opt,name=required_feature,json=requiredFeature,proto3" json:"required_feature,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *DeviceFeatureTargeting) Reset() { *m = DeviceFeatureTargeting{} }
+func (m *DeviceFeatureTargeting) String() string { return proto.CompactTextString(m) }
+func (*DeviceFeatureTargeting) ProtoMessage() {}
+func (*DeviceFeatureTargeting) Descriptor() ([]byte, []int) {
+ return fileDescriptor_df45b505afdf471e, []int{26}
+}
+
+func (m *DeviceFeatureTargeting) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_DeviceFeatureTargeting.Unmarshal(m, b)
+}
+func (m *DeviceFeatureTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_DeviceFeatureTargeting.Marshal(b, m, deterministic)
+}
+func (m *DeviceFeatureTargeting) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_DeviceFeatureTargeting.Merge(m, src)
+}
+func (m *DeviceFeatureTargeting) XXX_Size() int {
+ return xxx_messageInfo_DeviceFeatureTargeting.Size(m)
+}
+func (m *DeviceFeatureTargeting) XXX_DiscardUnknown() {
+ xxx_messageInfo_DeviceFeatureTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DeviceFeatureTargeting proto.InternalMessageInfo
+
+func (m *DeviceFeatureTargeting) GetRequiredFeature() *DeviceFeature {
+ if m != nil {
+ return m.RequiredFeature
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("android.bundle.ScreenDensity_DensityAlias", ScreenDensity_DensityAlias_name, ScreenDensity_DensityAlias_value)
+ proto.RegisterEnum("android.bundle.TextureCompressionFormat_TextureCompressionFormatAlias", TextureCompressionFormat_TextureCompressionFormatAlias_name, TextureCompressionFormat_TextureCompressionFormatAlias_value)
+ proto.RegisterEnum("android.bundle.Abi_AbiAlias", Abi_AbiAlias_name, Abi_AbiAlias_value)
+ proto.RegisterEnum("android.bundle.Sanitizer_SanitizerAlias", Sanitizer_SanitizerAlias_name, Sanitizer_SanitizerAlias_value)
+ proto.RegisterType((*VariantTargeting)(nil), "android.bundle.VariantTargeting")
+ proto.RegisterType((*ApkTargeting)(nil), "android.bundle.ApkTargeting")
+ proto.RegisterType((*ModuleTargeting)(nil), "android.bundle.ModuleTargeting")
+ proto.RegisterType((*UserCountriesTargeting)(nil), "android.bundle.UserCountriesTargeting")
+ proto.RegisterType((*ScreenDensity)(nil), "android.bundle.ScreenDensity")
+ proto.RegisterType((*Int32Value)(nil), "android.bundle.Int32Value")
+ proto.RegisterType((*SdkVersion)(nil), "android.bundle.SdkVersion")
+ proto.RegisterType((*GraphicsApi)(nil), "android.bundle.GraphicsApi")
+ proto.RegisterType((*VulkanVersion)(nil), "android.bundle.VulkanVersion")
+ proto.RegisterType((*OpenGlVersion)(nil), "android.bundle.OpenGlVersion")
+ proto.RegisterType((*TextureCompressionFormat)(nil), "android.bundle.TextureCompressionFormat")
+ proto.RegisterType((*Abi)(nil), "android.bundle.Abi")
+ proto.RegisterType((*MultiAbi)(nil), "android.bundle.MultiAbi")
+ proto.RegisterType((*Sanitizer)(nil), "android.bundle.Sanitizer")
+ proto.RegisterType((*DeviceFeature)(nil), "android.bundle.DeviceFeature")
+ proto.RegisterType((*AssetsDirectoryTargeting)(nil), "android.bundle.AssetsDirectoryTargeting")
+ proto.RegisterType((*NativeDirectoryTargeting)(nil), "android.bundle.NativeDirectoryTargeting")
+ proto.RegisterType((*ApexImageTargeting)(nil), "android.bundle.ApexImageTargeting")
+ proto.RegisterType((*AbiTargeting)(nil), "android.bundle.AbiTargeting")
+ proto.RegisterType((*MultiAbiTargeting)(nil), "android.bundle.MultiAbiTargeting")
+ proto.RegisterType((*ScreenDensityTargeting)(nil), "android.bundle.ScreenDensityTargeting")
+ proto.RegisterType((*LanguageTargeting)(nil), "android.bundle.LanguageTargeting")
+ proto.RegisterType((*GraphicsApiTargeting)(nil), "android.bundle.GraphicsApiTargeting")
+ proto.RegisterType((*SdkVersionTargeting)(nil), "android.bundle.SdkVersionTargeting")
+ proto.RegisterType((*TextureCompressionFormatTargeting)(nil), "android.bundle.TextureCompressionFormatTargeting")
+ proto.RegisterType((*SanitizerTargeting)(nil), "android.bundle.SanitizerTargeting")
+ proto.RegisterType((*DeviceFeatureTargeting)(nil), "android.bundle.DeviceFeatureTargeting")
+}
+
+func init() {
+ proto.RegisterFile("targeting.proto", fileDescriptor_df45b505afdf471e)
+}
+
+var fileDescriptor_df45b505afdf471e = []byte{
+ // 1504 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x6f, 0xe3, 0xc4,
+ 0x17, 0xaf, 0x93, 0xa6, 0x4d, 0x4e, 0x92, 0xd6, 0x3b, 0xe9, 0xbf, 0xff, 0xb0, 0xec, 0x4a, 0x5b,
+ 0xef, 0x85, 0xee, 0x0a, 0x05, 0xda, 0xae, 0xba, 0x15, 0x97, 0x22, 0xd7, 0x71, 0x9b, 0x48, 0x4d,
+ 0x9a, 0x75, 0xdc, 0x6c, 0x59, 0x90, 0xbc, 0x4e, 0x3c, 0x0d, 0xa6, 0x89, 0x1d, 0x6c, 0xa7, 0xda,
+ 0xe5, 0x05, 0x81, 0x90, 0x90, 0x78, 0xe2, 0x8d, 0x77, 0x3e, 0x00, 0x12, 0x4f, 0x08, 0x89, 0x07,
+ 0x24, 0x3e, 0x0c, 0x7c, 0x0c, 0x34, 0xbe, 0x24, 0x9e, 0xc4, 0x4e, 0xb3, 0x2c, 0xf0, 0x50, 0xe5,
+ 0xcc, 0xf1, 0x39, 0xbf, 0x73, 0x99, 0x73, 0x66, 0xce, 0x14, 0x56, 0x1d, 0xd5, 0xea, 0x62, 0x47,
+ 0x37, 0xba, 0xa5, 0x81, 0x65, 0x3a, 0x26, 0x5a, 0x51, 0x0d, 0xcd, 0x32, 0x75, 0xad, 0xd4, 0x1e,
+ 0x1a, 0x5a, 0x0f, 0x73, 0x7f, 0x26, 0x81, 0x6d, 0xa9, 0x96, 0xae, 0x1a, 0x8e, 0x1c, 0x88, 0xa2,
+ 0x27, 0xf0, 0x3f, 0x5b, 0xbb, 0x50, 0x2e, 0xb1, 0x65, 0xeb, 0xa6, 0xa1, 0x8c, 0x30, 0x8a, 0xcc,
+ 0x2d, 0x66, 0x33, 0xbb, 0x7d, 0xbb, 0x44, 0x83, 0x94, 0x9a, 0xda, 0x45, 0xcb, 0x93, 0x1d, 0x61,
+ 0x48, 0x05, 0x7b, 0x9a, 0x89, 0x78, 0xc8, 0xab, 0x6d, 0x3d, 0x04, 0x98, 0x70, 0x01, 0x6f, 0x4c,
+ 0x02, 0xf2, 0x6d, 0x7d, 0x8c, 0x94, 0x53, 0x43, 0x2b, 0xf4, 0x0c, 0x8a, 0x76, 0xc7, 0xc2, 0xd8,
+ 0x50, 0x34, 0x6c, 0xd8, 0xba, 0xf3, 0x22, 0x84, 0x96, 0x74, 0xd1, 0xee, 0x4d, 0xb9, 0xe7, 0xca,
+ 0x97, 0x3d, 0xf1, 0x31, 0xee, 0xba, 0x1d, 0xc9, 0x47, 0x8f, 0xa1, 0xd0, 0x1f, 0xf6, 0x1c, 0x5d,
+ 0xa1, 0x5d, 0x5d, 0x74, 0xc1, 0x37, 0x26, 0xc1, 0x6b, 0x44, 0x94, 0xf2, 0xf7, 0x5a, 0x7f, 0x92,
+ 0x85, 0xbe, 0x62, 0xe0, 0x8e, 0x83, 0x9f, 0x3b, 0x43, 0x0b, 0x2b, 0x1d, 0xb3, 0x3f, 0xb0, 0xb0,
+ 0xed, 0x66, 0xf6, 0xdc, 0xb4, 0xfa, 0xaa, 0x13, 0x32, 0x92, 0x72, 0x8d, 0x6c, 0x4d, 0x1a, 0x91,
+ 0x3d, 0x5d, 0x61, 0xac, 0x7a, 0xe8, 0x6a, 0x8e, 0x8d, 0x6e, 0x38, 0x57, 0x89, 0x70, 0x7f, 0xa4,
+ 0x20, 0xc7, 0x0f, 0x2e, 0x66, 0xec, 0x06, 0xf3, 0xd2, 0xbb, 0xf1, 0x14, 0xd6, 0xbb, 0x96, 0x3a,
+ 0xf8, 0x44, 0xef, 0xd8, 0x8a, 0x3a, 0x98, 0xde, 0xd9, 0x3b, 0x93, 0x58, 0x47, 0xbe, 0x34, 0x3f,
+ 0x08, 0x61, 0xae, 0x75, 0x23, 0xb8, 0xa8, 0x01, 0xa8, 0xa7, 0x1a, 0xdd, 0xa1, 0xda, 0xc5, 0x53,
+ 0x7b, 0x3c, 0xb5, 0x0d, 0xc7, 0xbe, 0x64, 0x68, 0x1b, 0x7a, 0x93, 0xac, 0x99, 0xb5, 0xb3, 0xf8,
+ 0x8f, 0xd4, 0x4e, 0x6c, 0xe7, 0xa4, 0x5e, 0xb1, 0x73, 0xe6, 0xae, 0xa0, 0xa5, 0x7f, 0xaf, 0x82,
+ 0xe2, 0x3a, 0x63, 0xf9, 0x15, 0x3a, 0xa3, 0x09, 0x05, 0x5b, 0x35, 0x74, 0x47, 0xff, 0x1c, 0x5b,
+ 0x21, 0xc8, 0xb4, 0x0b, 0xc9, 0x4d, 0xa5, 0x2b, 0x10, 0x1d, 0x63, 0x22, 0x7b, 0x8a, 0xc7, 0xfd,
+ 0x98, 0x80, 0xd5, 0x9a, 0xa9, 0x0d, 0x7b, 0xf8, 0x3f, 0x38, 0xd3, 0x9e, 0x41, 0x51, 0xc3, 0x97,
+ 0x7a, 0x07, 0x2b, 0xe7, 0x58, 0x75, 0xf7, 0x27, 0xdc, 0x04, 0xc9, 0xa8, 0xa2, 0x2a, 0xbb, 0xf2,
+ 0x87, 0x9e, 0x78, 0xa8, 0xa8, 0xb4, 0x48, 0x3e, 0xb1, 0x30, 0xb4, 0xb1, 0xa5, 0x74, 0xcc, 0xa1,
+ 0xe1, 0x58, 0x3a, 0xb6, 0xaf, 0x3e, 0xf2, 0x4e, 0x6d, 0x6c, 0x09, 0x81, 0x78, 0xc8, 0xc2, 0x30,
+ 0x92, 0xcf, 0x3d, 0x81, 0xf5, 0x68, 0x0d, 0x74, 0x1b, 0xf2, 0x9e, 0xd9, 0x17, 0x4a, 0xc7, 0xd4,
+ 0xb0, 0x5d, 0x64, 0x6e, 0x25, 0x37, 0x33, 0x52, 0xce, 0x67, 0x0a, 0x84, 0x87, 0x8a, 0xb0, 0x8c,
+ 0x9f, 0x77, 0x7a, 0x43, 0x0d, 0xbb, 0x6d, 0x9f, 0x96, 0x82, 0x25, 0xf7, 0x7d, 0x02, 0xf2, 0x54,
+ 0x0b, 0xa1, 0xc7, 0x90, 0x0f, 0x9a, 0x4f, 0xed, 0xe9, 0xaa, 0xed, 0xe6, 0x7f, 0x65, 0xfb, 0xc1,
+ 0xcc, 0xc6, 0x2b, 0xf9, 0xbf, 0x3c, 0xd1, 0xa8, 0x2c, 0x48, 0x39, 0x2d, 0xb4, 0x46, 0x1b, 0x90,
+ 0x0d, 0x20, 0xb5, 0x81, 0xee, 0xba, 0x90, 0xaa, 0x2c, 0x48, 0xe0, 0x33, 0xcb, 0x03, 0x9d, 0xfb,
+ 0x02, 0x72, 0x61, 0x08, 0xf4, 0x7f, 0x28, 0x94, 0xc5, 0x7a, 0xb3, 0x2a, 0x7f, 0xa8, 0x9c, 0xd6,
+ 0x9b, 0x0d, 0x51, 0xa8, 0x1e, 0x56, 0xc5, 0x32, 0xbb, 0x80, 0x32, 0x90, 0xaa, 0x9f, 0x94, 0x1b,
+ 0x55, 0x96, 0x41, 0x69, 0x58, 0x3c, 0x26, 0x54, 0x82, 0x50, 0x35, 0x42, 0x25, 0xc9, 0x67, 0xb9,
+ 0x45, 0xc8, 0x45, 0xc2, 0xac, 0x10, 0x2a, 0x45, 0x98, 0x67, 0x2e, 0xb9, 0x84, 0x00, 0x96, 0xce,
+ 0x3c, 0x7a, 0x19, 0x65, 0x61, 0xf9, 0xcc, 0x5f, 0xa4, 0x0f, 0x56, 0xc7, 0x61, 0x9b, 0x06, 0x36,
+ 0xcf, 0x39, 0x0e, 0xa0, 0x6a, 0x38, 0x3b, 0xdb, 0x2d, 0xb5, 0x37, 0xc4, 0x68, 0x0d, 0x52, 0x97,
+ 0x84, 0x70, 0xb3, 0x91, 0x92, 0xbc, 0x05, 0xf7, 0x0e, 0xc0, 0xb8, 0x0c, 0xd1, 0x9b, 0x90, 0xec,
+ 0xeb, 0x86, 0x5f, 0xaf, 0xd7, 0x27, 0xf3, 0x35, 0x06, 0x93, 0x88, 0x18, 0xf7, 0x0b, 0x03, 0xd9,
+ 0xd0, 0x61, 0x8b, 0xea, 0x50, 0xe8, 0xeb, 0x86, 0x62, 0x0e, 0xb0, 0xa1, 0x74, 0x7b, 0x41, 0x1f,
+ 0xf8, 0x68, 0x37, 0x27, 0xd1, 0x4e, 0x06, 0xd8, 0x38, 0xea, 0xf9, 0x96, 0x2b, 0x0b, 0x12, 0xdb,
+ 0xd7, 0x0d, 0x8a, 0x87, 0x6a, 0x80, 0x08, 0xde, 0xe5, 0xb0, 0x77, 0xa1, 0x1a, 0x23, 0xb8, 0x44,
+ 0x34, 0x5c, 0xcb, 0x95, 0xa2, 0xe1, 0x28, 0xde, 0x41, 0x16, 0x32, 0xe4, 0xfe, 0xf0, 0x72, 0xf3,
+ 0x2e, 0xe4, 0xa9, 0xaf, 0x24, 0x3d, 0x7d, 0xf5, 0x53, 0xd3, 0x0a, 0xd2, 0xe3, 0x2e, 0x5c, 0xae,
+ 0x6e, 0x98, 0x96, 0xb7, 0xe3, 0x92, 0xb7, 0x20, 0xca, 0xb4, 0xa7, 0x2f, 0xa3, 0xfc, 0x73, 0x02,
+ 0x8a, 0x71, 0x47, 0x25, 0xfa, 0x18, 0x52, 0xe1, 0x92, 0x3d, 0x9c, 0xf7, 0x8c, 0x8d, 0xfd, 0xe0,
+ 0xd6, 0xa2, 0xe4, 0x81, 0x72, 0xbf, 0x32, 0x70, 0x73, 0xa6, 0x20, 0x7a, 0x00, 0xf7, 0x42, 0xc5,
+ 0xaa, 0xc8, 0xe2, 0x99, 0x7c, 0x2a, 0x89, 0x8a, 0x70, 0x52, 0x6b, 0x48, 0x62, 0xb3, 0x59, 0x3d,
+ 0xa9, 0x2b, 0x87, 0x27, 0x52, 0x8d, 0x97, 0xd9, 0x05, 0x94, 0x87, 0x8c, 0x28, 0x0b, 0x5b, 0x8a,
+ 0x74, 0x74, 0xb0, 0xc7, 0x32, 0x28, 0x07, 0xe9, 0x06, 0x7f, 0x2c, 0xca, 0xb2, 0x58, 0x66, 0x13,
+ 0x64, 0x25, 0x57, 0x24, 0x51, 0x54, 0xca, 0x02, 0x9b, 0x44, 0xcb, 0x90, 0xe4, 0x65, 0xc1, 0xab,
+ 0xe8, 0x63, 0x42, 0xa5, 0x08, 0x55, 0x3e, 0x93, 0xb7, 0xd8, 0x25, 0x42, 0x35, 0x77, 0x64, 0x81,
+ 0x5d, 0x26, 0x55, 0xde, 0x68, 0x49, 0xb2, 0xc0, 0xa6, 0x09, 0x93, 0x6f, 0xca, 0x02, 0x9b, 0x21,
+ 0x94, 0x28, 0x0b, 0xdb, 0x2c, 0x70, 0xbf, 0x31, 0x90, 0xe4, 0xdb, 0x3a, 0xda, 0xa6, 0x93, 0x14,
+ 0x35, 0x4c, 0x90, 0x3f, 0x2a, 0xf4, 0xaf, 0x19, 0x48, 0x07, 0x3c, 0x74, 0x0b, 0x6e, 0x84, 0xa3,
+ 0x14, 0x1a, 0xa7, 0x0a, 0x2f, 0x09, 0x95, 0xaa, 0x2c, 0x0a, 0x24, 0x5c, 0x76, 0x81, 0x34, 0x16,
+ 0x2f, 0xd5, 0x44, 0xfe, 0x80, 0x74, 0xe9, 0x2a, 0x64, 0xfd, 0x85, 0xd2, 0x7a, 0xc4, 0xb3, 0x09,
+ 0x12, 0x39, 0x2f, 0xd5, 0x76, 0x1f, 0x2a, 0xad, 0x3d, 0xde, 0x8b, 0xee, 0x6c, 0x6f, 0x97, 0x5d,
+ 0x74, 0x5b, 0x73, 0x6f, 0x57, 0xd9, 0x7d, 0xe8, 0xc5, 0x57, 0xab, 0x36, 0x9a, 0x5e, 0xc3, 0x12,
+ 0x6a, 0xf7, 0x21, 0xbb, 0xcc, 0x6d, 0x41, 0x3a, 0xb8, 0xb3, 0xd0, 0x5d, 0x48, 0xaa, 0x6d, 0xdd,
+ 0x3d, 0xed, 0xb2, 0xdb, 0x85, 0x88, 0x20, 0x24, 0xf2, 0x9d, 0xbb, 0x84, 0xcc, 0xe8, 0x4e, 0x42,
+ 0xfb, 0x74, 0xe8, 0x9b, 0xb1, 0xb7, 0xd7, 0x98, 0xa2, 0xd2, 0x70, 0x1f, 0x56, 0xe8, 0x0f, 0xc4,
+ 0xcf, 0xfa, 0x49, 0x5d, 0xf4, 0xf6, 0xb3, 0xf2, 0x84, 0x2f, 0x97, 0xc9, 0x46, 0xb3, 0x0c, 0xf7,
+ 0x11, 0xe4, 0xa9, 0x4b, 0x04, 0x6d, 0x40, 0x2e, 0xb8, 0x7e, 0x0c, 0xb5, 0xef, 0x9d, 0x23, 0x19,
+ 0x29, 0xeb, 0xf3, 0xea, 0x6a, 0x1f, 0xa3, 0x37, 0x60, 0x35, 0x10, 0x09, 0xb7, 0x6b, 0x4a, 0x5a,
+ 0xf1, 0xd9, 0x7e, 0xc3, 0x70, 0xbf, 0x27, 0xa0, 0xc8, 0xdb, 0x36, 0x76, 0xec, 0xb2, 0x6e, 0xe1,
+ 0x8e, 0x63, 0x5a, 0xa1, 0x09, 0xa7, 0x14, 0x24, 0xe6, 0xea, 0x51, 0x91, 0x08, 0xa2, 0x23, 0xc8,
+ 0x85, 0x27, 0xc4, 0x97, 0x9a, 0x0b, 0xb3, 0xa1, 0xb9, 0x10, 0x99, 0x70, 0x3d, 0x7e, 0x00, 0xf2,
+ 0xef, 0xc1, 0xbf, 0x31, 0xf6, 0x14, 0xe3, 0xc6, 0x1e, 0xf4, 0x3e, 0xa4, 0x83, 0x11, 0x32, 0x6e,
+ 0xf8, 0x9f, 0x9e, 0x3a, 0x47, 0x2a, 0xdc, 0x0f, 0x09, 0x28, 0xd6, 0x55, 0x47, 0xbf, 0xc4, 0x11,
+ 0x59, 0xbc, 0x1b, 0xce, 0x62, 0x6c, 0x79, 0xa1, 0xfd, 0xc8, 0xe4, 0xbd, 0x3e, 0x23, 0x79, 0x74,
+ 0xce, 0xce, 0xe7, 0xc8, 0xd9, 0xe6, 0xbc, 0x39, 0x9b, 0x91, 0xaa, 0x47, 0x90, 0x19, 0x8d, 0x61,
+ 0x7e, 0xae, 0x5e, 0x8b, 0xad, 0x7e, 0x69, 0x2c, 0xcb, 0xc9, 0x80, 0xf8, 0x01, 0x7e, 0x5e, 0xed,
+ 0x53, 0x73, 0xfa, 0x3e, 0x64, 0x46, 0x73, 0xa6, 0x9f, 0xa3, 0x39, 0xa6, 0xcb, 0x74, 0x30, 0x5d,
+ 0x72, 0x16, 0xe4, 0xa8, 0x21, 0xf3, 0xfe, 0xf8, 0x76, 0x8d, 0x6d, 0x67, 0x4f, 0x02, 0x3d, 0x82,
+ 0x9c, 0xda, 0x73, 0xb0, 0x65, 0xb8, 0x3b, 0x67, 0xfb, 0x13, 0x5c, 0xa4, 0x06, 0x25, 0xc8, 0x7d,
+ 0xc9, 0xc0, 0xb5, 0x29, 0x9f, 0x50, 0x89, 0xb6, 0x5c, 0x8c, 0x8b, 0x22, 0x30, 0xff, 0x5e, 0xa4,
+ 0xf9, 0x78, 0x35, 0xda, 0x87, 0xef, 0x18, 0x58, 0x8f, 0x7e, 0xb0, 0xa0, 0x1d, 0xda, 0x91, 0x9b,
+ 0x33, 0xc7, 0xad, 0xc0, 0x1b, 0x3e, 0xd2, 0x9b, 0x2b, 0x74, 0x69, 0x97, 0x6a, 0x70, 0x6d, 0xaa,
+ 0x49, 0xc2, 0xd3, 0x0e, 0x19, 0x26, 0x7d, 0x6b, 0x5c, 0x84, 0xb5, 0xcc, 0x04, 0xdc, 0xb7, 0x0c,
+ 0xac, 0x45, 0x1d, 0x15, 0x68, 0x8b, 0x8e, 0x6f, 0x66, 0x8b, 0xf8, 0xf6, 0x3e, 0x88, 0x8c, 0x6e,
+ 0xa6, 0x26, 0xed, 0xcc, 0x37, 0x0c, 0x14, 0x22, 0x9e, 0x09, 0xe8, 0x6d, 0xda, 0x97, 0xeb, 0xf1,
+ 0x4f, 0x8b, 0xc0, 0x95, 0xfd, 0x48, 0x57, 0x66, 0x29, 0xd2, 0x9e, 0xfc, 0xc4, 0xc0, 0xc6, 0x95,
+ 0x47, 0x1d, 0xb9, 0x9f, 0xc2, 0x7e, 0xcd, 0xdf, 0xf8, 0xbe, 0x97, 0xc7, 0x91, 0x5e, 0xce, 0x0f,
+ 0x43, 0xfb, 0x2c, 0x02, 0x9a, 0x7e, 0xce, 0xa1, 0xb7, 0x68, 0x1f, 0x67, 0x9c, 0x22, 0xfe, 0x8c,
+ 0xdc, 0x86, 0xf5, 0xe8, 0xe7, 0x14, 0xaa, 0x00, 0x6b, 0xe1, 0xcf, 0x86, 0xba, 0x85, 0xb5, 0xe0,
+ 0x69, 0x16, 0x37, 0xee, 0x52, 0x08, 0xd2, 0x6a, 0xa0, 0xe6, 0x33, 0x0e, 0x1e, 0x00, 0xea, 0x98,
+ 0xfd, 0x09, 0xa5, 0xa7, 0x6b, 0xfe, 0x5a, 0xf1, 0xd6, 0x8a, 0xfb, 0x0f, 0xb6, 0xf6, 0x92, 0xfb,
+ 0xb3, 0xf3, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x47, 0xe0, 0x85, 0xa8, 0x7a, 0x13, 0x00, 0x00,
+}
diff --git a/cmd/extract_apks/bundle_proto/targeting.proto b/cmd/extract_apks/bundle_proto/targeting.proto
new file mode 100644
index 0000000..cdc910b
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/targeting.proto
@@ -0,0 +1,232 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+syntax = "proto3";
+
+package android.bundle;
+
+option go_package = "android_bundle_proto";
+option java_package = "com.android.bundle";
+
+// Targeting on the level of variants.
+message VariantTargeting {
+ SdkVersionTargeting sdk_version_targeting = 1;
+ AbiTargeting abi_targeting = 2;
+ ScreenDensityTargeting screen_density_targeting = 3;
+ MultiAbiTargeting multi_abi_targeting = 4;
+ TextureCompressionFormatTargeting texture_compression_format_targeting = 5;
+}
+
+// Targeting on the level of individual APKs.
+message ApkTargeting {
+ AbiTargeting abi_targeting = 1;
+ GraphicsApiTargeting graphics_api_targeting = 2;
+ LanguageTargeting language_targeting = 3;
+ ScreenDensityTargeting screen_density_targeting = 4;
+ SdkVersionTargeting sdk_version_targeting = 5;
+ TextureCompressionFormatTargeting texture_compression_format_targeting = 6;
+ MultiAbiTargeting multi_abi_targeting = 7;
+ SanitizerTargeting sanitizer_targeting = 8;
+}
+
+// Targeting on the module level.
+// The semantic of the targeting is the "AND" rule on all immediate values.
+message ModuleTargeting {
+ SdkVersionTargeting sdk_version_targeting = 1;
+ repeated DeviceFeatureTargeting device_feature_targeting = 2;
+ UserCountriesTargeting user_countries_targeting = 3;
+}
+
+// User Countries targeting describing an inclusive/exclusive list of country
+// codes that module targets.
+message UserCountriesTargeting {
+ // List of country codes in the two-letter CLDR territory format.
+ repeated string country_codes = 1;
+
+ // Indicates if the list above is exclusive.
+ bool exclude = 2;
+}
+
+message ScreenDensity {
+ enum DensityAlias {
+ DENSITY_UNSPECIFIED = 0;
+ NODPI = 1;
+ LDPI = 2;
+ MDPI = 3;
+ TVDPI = 4;
+ HDPI = 5;
+ XHDPI = 6;
+ XXHDPI = 7;
+ XXXHDPI = 8;
+ }
+
+ oneof density_oneof {
+ DensityAlias density_alias = 1;
+ int32 density_dpi = 2;
+ }
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+message Int32Value {
+ // The int32 value.
+ int32 value = 1;
+}
+
+message SdkVersion {
+ // Inclusive.
+ Int32Value min = 1;
+}
+
+message GraphicsApi {
+ oneof api_oneof {
+ // Inclusive.
+ OpenGlVersion min_open_gl_version = 1;
+ // Inclusive.
+ VulkanVersion min_vulkan_version = 2;
+ }
+}
+
+message VulkanVersion {
+ int32 major = 1; // VK_VERSION_MAJOR
+ int32 minor = 2; // VK_VERSION_MINOR
+}
+
+message OpenGlVersion {
+ // e.g. OpenGL ES 3.2 is represented as { major: 3, minor: 2 }
+ int32 major = 1; // GL_MAJOR_VERSION
+ int32 minor = 2; // GL_MINOR_VERSION
+}
+
+message TextureCompressionFormat {
+ enum TextureCompressionFormatAlias {
+ UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT = 0;
+ ETC1_RGB8 = 1;
+ PALETTED = 2;
+ THREE_DC = 3;
+ ATC = 4;
+ LATC = 5;
+ DXT1 = 6;
+ S3TC = 7;
+ PVRTC = 8;
+ ASTC = 9;
+ ETC2 = 10;
+ }
+ TextureCompressionFormatAlias alias = 1;
+}
+
+message Abi {
+ enum AbiAlias {
+ UNSPECIFIED_CPU_ARCHITECTURE = 0;
+ ARMEABI = 1;
+ ARMEABI_V7A = 2;
+ ARM64_V8A = 3;
+ X86 = 4;
+ X86_64 = 5;
+ MIPS = 6;
+ MIPS64 = 7;
+ }
+ AbiAlias alias = 1;
+}
+
+message MultiAbi {
+ repeated Abi abi = 1;
+}
+
+message Sanitizer {
+ enum SanitizerAlias {
+ NONE = 0;
+ HWADDRESS = 1;
+ }
+ SanitizerAlias alias = 1;
+}
+
+message DeviceFeature {
+ string feature_name = 1;
+ // Equivalent of android:glEsVersion or android:version in <uses-feature>.
+ int32 feature_version = 2;
+}
+
+// Targeting specific for directories under assets/.
+message AssetsDirectoryTargeting {
+ AbiTargeting abi = 1;
+ GraphicsApiTargeting graphics_api = 2;
+ TextureCompressionFormatTargeting texture_compression_format = 3;
+ LanguageTargeting language = 4;
+}
+
+// Targeting specific for directories under lib/.
+message NativeDirectoryTargeting {
+ Abi abi = 1;
+ GraphicsApi graphics_api = 2;
+ TextureCompressionFormat texture_compression_format = 3;
+ Sanitizer sanitizer = 4;
+}
+
+// Targeting specific for image files under apex/.
+message ApexImageTargeting {
+ MultiAbiTargeting multi_abi = 1;
+}
+
+message AbiTargeting {
+ repeated Abi value = 1;
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ repeated Abi alternatives = 2;
+}
+
+message MultiAbiTargeting {
+ repeated MultiAbi value = 1;
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ repeated MultiAbi alternatives = 2;
+}
+
+message ScreenDensityTargeting {
+ repeated ScreenDensity value = 1;
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ repeated ScreenDensity alternatives = 2;
+}
+
+message LanguageTargeting {
+ // ISO-639: 2 or 3 letter language code.
+ repeated string value = 1;
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ repeated string alternatives = 2;
+}
+
+message GraphicsApiTargeting {
+ repeated GraphicsApi value = 1;
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ repeated GraphicsApi alternatives = 2;
+}
+
+message SdkVersionTargeting {
+ repeated SdkVersion value = 1;
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ repeated SdkVersion alternatives = 2;
+}
+
+message TextureCompressionFormatTargeting {
+ repeated TextureCompressionFormat value = 1;
+ // Targeting of other sibling directories that were in the Bundle.
+ // For master splits this is targeting of other master splits.
+ repeated TextureCompressionFormat alternatives = 2;
+}
+
+message SanitizerTargeting {
+ repeated Sanitizer value = 1;
+}
+
+// Since other atom targeting messages have the "OR" semantic on values
+// the DeviceFeatureTargeting represents only one device feature to retain
+// that convention.
+message DeviceFeatureTargeting {
+ DeviceFeature required_feature = 1;
+}
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
new file mode 100644
index 0000000..85dd6d1
--- /dev/null
+++ b/cmd/extract_apks/main.go
@@ -0,0 +1,569 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copies all the entries (APKs/APEXes) matching the target configuration from the given
+// APK set into a zip file. Run it without arguments to see usage details.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "math"
+ "os"
+ "regexp"
+ "sort"
+ "strings"
+
+ "github.com/golang/protobuf/proto"
+
+ "android/soong/cmd/extract_apks/bundle_proto"
+ "android/soong/third_party/zip"
+)
+
+type TargetConfig struct {
+ sdkVersion int32
+ screenDpi map[android_bundle_proto.ScreenDensity_DensityAlias]bool
+ // Map holding <ABI alias>:<its sequence number in the flag> info.
+ abis map[android_bundle_proto.Abi_AbiAlias]int
+ allowPrereleased bool
+ stem string
+}
+
+// An APK set is a zip archive. An entry 'toc.pb' describes its contents.
+// It is a protobuf message BuildApkResult.
+type Toc *android_bundle_proto.BuildApksResult
+
+type ApkSet struct {
+ path string
+ reader *zip.ReadCloser
+ entries map[string]*zip.File
+}
+
+func newApkSet(path string) (*ApkSet, error) {
+ apkSet := &ApkSet{path: path, entries: make(map[string]*zip.File)}
+ var err error
+ if apkSet.reader, err = zip.OpenReader(apkSet.path); err != nil {
+ return nil, err
+ }
+ for _, f := range apkSet.reader.File {
+ apkSet.entries[f.Name] = f
+ }
+ return apkSet, nil
+}
+
+func (apkSet *ApkSet) getToc() (Toc, error) {
+ var err error
+ tocFile, ok := apkSet.entries["toc.pb"]
+ if !ok {
+ return nil, fmt.Errorf("%s: APK set should have toc.pb entry", apkSet.path)
+ }
+ rc, err := tocFile.Open()
+ if err != nil {
+ return nil, err
+ }
+ bytes := make([]byte, tocFile.FileHeader.UncompressedSize64)
+ if _, err := rc.Read(bytes); err != io.EOF {
+ return nil, err
+ }
+ rc.Close()
+ buildApksResult := new(android_bundle_proto.BuildApksResult)
+ if err = proto.Unmarshal(bytes, buildApksResult); err != nil {
+ return nil, err
+ }
+ return buildApksResult, nil
+}
+
+func (apkSet *ApkSet) close() {
+ apkSet.reader.Close()
+}
+
+// Matchers for selection criteria
+
+type abiTargetingMatcher struct {
+ *android_bundle_proto.AbiTargeting
+}
+
+func (m abiTargetingMatcher) matches(config TargetConfig) bool {
+ if m.AbiTargeting == nil {
+ return true
+ }
+ if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
+ return true
+ }
+ // Find the one that appears first in the abis flags.
+ abiIdx := math.MaxInt32
+ for _, v := range m.GetValue() {
+ if i, ok := config.abis[v.Alias]; ok {
+ if i < abiIdx {
+ abiIdx = i
+ }
+ }
+ }
+ if abiIdx == math.MaxInt32 {
+ return false
+ }
+ // See if any alternatives appear before the above one.
+ for _, a := range m.GetAlternatives() {
+ if i, ok := config.abis[a.Alias]; ok {
+ if i < abiIdx {
+ // There is a better alternative. Skip this one.
+ return false
+ }
+ }
+ }
+ return true
+}
+
+type apkDescriptionMatcher struct {
+ *android_bundle_proto.ApkDescription
+}
+
+func (m apkDescriptionMatcher) matches(config TargetConfig) bool {
+ return m.ApkDescription == nil || (apkTargetingMatcher{m.Targeting}).matches(config)
+}
+
+type apkTargetingMatcher struct {
+ *android_bundle_proto.ApkTargeting
+}
+
+func (m apkTargetingMatcher) matches(config TargetConfig) bool {
+ return m.ApkTargeting == nil ||
+ (abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
+ languageTargetingMatcher{m.LanguageTargeting}.matches(config) &&
+ screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
+ sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
+ multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config))
+}
+
+type languageTargetingMatcher struct {
+ *android_bundle_proto.LanguageTargeting
+}
+
+func (m languageTargetingMatcher) matches(_ TargetConfig) bool {
+ if m.LanguageTargeting == nil {
+ return true
+ }
+ log.Fatal("language based entry selection is not implemented")
+ return false
+}
+
+type moduleMetadataMatcher struct {
+ *android_bundle_proto.ModuleMetadata
+}
+
+func (m moduleMetadataMatcher) matches(config TargetConfig) bool {
+ return m.ModuleMetadata == nil ||
+ (m.GetDeliveryType() == android_bundle_proto.DeliveryType_INSTALL_TIME &&
+ moduleTargetingMatcher{m.Targeting}.matches(config) &&
+ !m.IsInstant)
+}
+
+type moduleTargetingMatcher struct {
+ *android_bundle_proto.ModuleTargeting
+}
+
+func (m moduleTargetingMatcher) matches(config TargetConfig) bool {
+ return m.ModuleTargeting == nil ||
+ (sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
+ userCountriesTargetingMatcher{m.UserCountriesTargeting}.matches(config))
+}
+
+// A higher number means a higher priority.
+// This order must be kept identical to bundletool's.
+var multiAbiPriorities = map[android_bundle_proto.Abi_AbiAlias]int{
+ android_bundle_proto.Abi_ARMEABI: 1,
+ android_bundle_proto.Abi_ARMEABI_V7A: 2,
+ android_bundle_proto.Abi_ARM64_V8A: 3,
+ android_bundle_proto.Abi_X86: 4,
+ android_bundle_proto.Abi_X86_64: 5,
+ android_bundle_proto.Abi_MIPS: 6,
+ android_bundle_proto.Abi_MIPS64: 7,
+}
+
+type multiAbiTargetingMatcher struct {
+ *android_bundle_proto.MultiAbiTargeting
+}
+
+func (t multiAbiTargetingMatcher) matches(config TargetConfig) bool {
+ if t.MultiAbiTargeting == nil {
+ return true
+ }
+ if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
+ return true
+ }
+ // Find the one with the highest priority.
+ highestPriority := 0
+ for _, v := range t.GetValue() {
+ for _, a := range v.GetAbi() {
+ if _, ok := config.abis[a.Alias]; ok {
+ if highestPriority < multiAbiPriorities[a.Alias] {
+ highestPriority = multiAbiPriorities[a.Alias]
+ }
+ }
+ }
+ }
+ if highestPriority == 0 {
+ return false
+ }
+ // See if there are any matching alternatives with a higher priority.
+ for _, v := range t.GetAlternatives() {
+ for _, a := range v.GetAbi() {
+ if _, ok := config.abis[a.Alias]; ok {
+ if highestPriority < multiAbiPriorities[a.Alias] {
+ // There's a better one. Skip this one.
+ return false
+ }
+ }
+ }
+ }
+ return true
+}
+
+type screenDensityTargetingMatcher struct {
+ *android_bundle_proto.ScreenDensityTargeting
+}
+
+func (m screenDensityTargetingMatcher) matches(config TargetConfig) bool {
+ if m.ScreenDensityTargeting == nil {
+ return true
+ }
+ if _, ok := config.screenDpi[android_bundle_proto.ScreenDensity_DENSITY_UNSPECIFIED]; ok {
+ return true
+ }
+ for _, v := range m.GetValue() {
+ switch x := v.GetDensityOneof().(type) {
+ case *android_bundle_proto.ScreenDensity_DensityAlias_:
+ if _, ok := config.screenDpi[x.DensityAlias]; ok {
+ return true
+ }
+ default:
+ log.Fatal("For screen density, only DPI name based entry selection (e.g. HDPI, XHDPI) is implemented")
+ }
+ }
+ return false
+}
+
+type sdkVersionTargetingMatcher struct {
+ *android_bundle_proto.SdkVersionTargeting
+}
+
+func (m sdkVersionTargetingMatcher) matches(config TargetConfig) bool {
+ const preReleaseVersion = 10000
+ if m.SdkVersionTargeting == nil {
+ return true
+ }
+ if len(m.Value) > 1 {
+ log.Fatal(fmt.Sprintf("sdk_version_targeting should not have multiple values:%#v", m.Value))
+ }
+ // Inspect only sdkVersionTargeting.Value.
+ // Even though one of the SdkVersionTargeting.Alternatives values may be
+ // better matching, we will select all of them
+ return m.Value[0].Min == nil ||
+ m.Value[0].Min.Value <= config.sdkVersion ||
+ (config.allowPrereleased && m.Value[0].Min.Value == preReleaseVersion)
+}
+
+type textureCompressionFormatTargetingMatcher struct {
+ *android_bundle_proto.TextureCompressionFormatTargeting
+}
+
+func (m textureCompressionFormatTargetingMatcher) matches(_ TargetConfig) bool {
+ if m.TextureCompressionFormatTargeting == nil {
+ return true
+ }
+ log.Fatal("texture based entry selection is not implemented")
+ return false
+}
+
+type userCountriesTargetingMatcher struct {
+ *android_bundle_proto.UserCountriesTargeting
+}
+
+func (m userCountriesTargetingMatcher) matches(_ TargetConfig) bool {
+ if m.UserCountriesTargeting == nil {
+ return true
+ }
+ log.Fatal("country based entry selection is not implemented")
+ return false
+}
+
+type variantTargetingMatcher struct {
+ *android_bundle_proto.VariantTargeting
+}
+
+func (m variantTargetingMatcher) matches(config TargetConfig) bool {
+ if m.VariantTargeting == nil {
+ return true
+ }
+ return sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
+ abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
+ multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config) &&
+ screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
+ textureCompressionFormatTargetingMatcher{m.TextureCompressionFormatTargeting}.matches(config)
+}
+
+type SelectionResult struct {
+ moduleName string
+ entries []string
+}
+
+// Return all entries matching target configuration
+func selectApks(toc Toc, targetConfig TargetConfig) SelectionResult {
+ var result SelectionResult
+ for _, variant := range (*toc).GetVariant() {
+ if !(variantTargetingMatcher{variant.GetTargeting()}.matches(targetConfig)) {
+ continue
+ }
+ for _, as := range variant.GetApkSet() {
+ if !(moduleMetadataMatcher{as.ModuleMetadata}.matches(targetConfig)) {
+ continue
+ }
+ for _, apkdesc := range as.GetApkDescription() {
+ if (apkDescriptionMatcher{apkdesc}).matches(targetConfig) {
+ result.entries = append(result.entries, apkdesc.GetPath())
+ // TODO(asmundak): As it turns out, moduleName which we get from
+ // the ModuleMetadata matches the module names of the generated
+ // entry paths just by coincidence, only for the split APKs. We
+ // need to discuss this with bundletool folks.
+ result.moduleName = as.GetModuleMetadata().GetName()
+ }
+ }
+ // we allow only a single module, so bail out here if we found one
+ if result.moduleName != "" {
+ return result
+ }
+ }
+ }
+ return result
+}
+
+type Zip2ZipWriter interface {
+ CopyFrom(file *zip.File, name string) error
+}
+
+// Writes out selected entries, renaming them as needed
+func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
+ writer Zip2ZipWriter, partition string) ([]string, error) {
+ // Renaming rules:
+ // splits/MODULE-master.apk to STEM.apk
+ // else
+ // splits/MODULE-*.apk to STEM>-$1.apk
+ // TODO(asmundak):
+ // add more rules, for .apex files
+ renameRules := []struct {
+ rex *regexp.Regexp
+ repl string
+ }{
+ {
+ regexp.MustCompile(`^.*/` + selected.moduleName + `-master\.apk$`),
+ config.stem + `.apk`,
+ },
+ {
+ regexp.MustCompile(`^.*/` + selected.moduleName + `(-.*\.apk)$`),
+ config.stem + `$1`,
+ },
+ {
+ regexp.MustCompile(`^universal\.apk$`),
+ config.stem + ".apk",
+ },
+ }
+ renamer := func(path string) (string, bool) {
+ for _, rr := range renameRules {
+ if rr.rex.MatchString(path) {
+ return rr.rex.ReplaceAllString(path, rr.repl), true
+ }
+ }
+ return "", false
+ }
+
+ entryOrigin := make(map[string]string) // output entry to input entry
+ var apkcerts []string
+ for _, apk := range selected.entries {
+ apkFile, ok := apkSet.entries[apk]
+ if !ok {
+ return nil, fmt.Errorf("TOC refers to an entry %s which does not exist", apk)
+ }
+ inName := apkFile.Name
+ outName, ok := renamer(inName)
+ if !ok {
+ log.Fatalf("selected an entry with unexpected name %s", inName)
+ }
+ if origin, ok := entryOrigin[inName]; ok {
+ log.Fatalf("selected entries %s and %s will have the same output name %s",
+ origin, inName, outName)
+ }
+ entryOrigin[outName] = inName
+ if err := writer.CopyFrom(apkFile, outName); err != nil {
+ return nil, err
+ }
+ if partition != "" {
+ apkcerts = append(apkcerts, fmt.Sprintf(
+ `name="%s" certificate="PRESIGNED" private_key=""`, outName))
+ }
+ }
+ sort.Strings(apkcerts)
+ return apkcerts, nil
+}
+
+func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error {
+ if len(selected.entries) != 1 {
+ return fmt.Errorf("Too many matching entries for extract-single:\n%v", selected.entries)
+ }
+ apk, ok := apkSet.entries[selected.entries[0]]
+ if !ok {
+ return fmt.Errorf("Couldn't find apk path %s", selected.entries[0])
+ }
+ inputReader, _ := apk.Open()
+ _, err := io.Copy(outFile, inputReader)
+ return err
+}
+
+// Arguments parsing
+var (
+ outputFile = flag.String("o", "", "output file containing extracted entries")
+ targetConfig = TargetConfig{
+ screenDpi: map[android_bundle_proto.ScreenDensity_DensityAlias]bool{},
+ abis: map[android_bundle_proto.Abi_AbiAlias]int{},
+ }
+ extractSingle = flag.Bool("extract-single", false,
+ "extract a single target and output it uncompressed. only available for standalone apks and apexes.")
+ apkcertsOutput = flag.String("apkcerts", "",
+ "optional apkcerts.txt output file containing signing info of all outputted apks")
+ partition = flag.String("partition", "", "partition string. required when -apkcerts is used.")
+)
+
+// Parse abi values
+type abiFlagValue struct {
+ targetConfig *TargetConfig
+}
+
+func (a abiFlagValue) String() string {
+ return "all"
+}
+
+func (a abiFlagValue) Set(abiList string) error {
+ for i, abi := range strings.Split(abiList, ",") {
+ v, ok := android_bundle_proto.Abi_AbiAlias_value[abi]
+ if !ok {
+ return fmt.Errorf("bad ABI value: %q", abi)
+ }
+ targetConfig.abis[android_bundle_proto.Abi_AbiAlias(v)] = i
+ }
+ return nil
+}
+
+// Parse screen density values
+type screenDensityFlagValue struct {
+ targetConfig *TargetConfig
+}
+
+func (s screenDensityFlagValue) String() string {
+ return "none"
+}
+
+func (s screenDensityFlagValue) Set(densityList string) error {
+ if densityList == "none" {
+ return nil
+ }
+ if densityList == "all" {
+ targetConfig.screenDpi[android_bundle_proto.ScreenDensity_DENSITY_UNSPECIFIED] = true
+ return nil
+ }
+ for _, density := range strings.Split(densityList, ",") {
+ v, found := android_bundle_proto.ScreenDensity_DensityAlias_value[density]
+ if !found {
+ return fmt.Errorf("bad screen density value: %q", density)
+ }
+ targetConfig.screenDpi[android_bundle_proto.ScreenDensity_DensityAlias(v)] = true
+ }
+ return nil
+}
+
+func processArgs() {
+ flag.Usage = func() {
+ fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+
+ `-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `+
+ `[-apkcerts <apkcerts output file> -partition <partition>] <APK set>`)
+ flag.PrintDefaults()
+ os.Exit(2)
+ }
+ version := flag.Uint("sdk-version", 0, "SDK version")
+ flag.Var(abiFlagValue{&targetConfig}, "abis",
+ "comma-separated ABIs list of ARMEABI ARMEABI_V7A ARM64_V8A X86 X86_64 MIPS MIPS64")
+ flag.Var(screenDensityFlagValue{&targetConfig}, "screen-densities",
+ "'all' or comma-separated list of screen density names (NODPI LDPI MDPI TVDPI HDPI XHDPI XXHDPI XXXHDPI)")
+ flag.BoolVar(&targetConfig.allowPrereleased, "allow-prereleased", false,
+ "allow prereleased")
+ flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
+ flag.Parse()
+ if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 ||
+ (targetConfig.stem == "" && !*extractSingle) || (*apkcertsOutput != "" && *partition == "") {
+ flag.Usage()
+ }
+ targetConfig.sdkVersion = int32(*version)
+
+}
+
+func main() {
+ processArgs()
+ var toc Toc
+ apkSet, err := newApkSet(flag.Arg(0))
+ if err == nil {
+ defer apkSet.close()
+ toc, err = apkSet.getToc()
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
+ sel := selectApks(toc, targetConfig)
+ if len(sel.entries) == 0 {
+ log.Fatalf("there are no entries for the target configuration: %#v", targetConfig)
+ }
+
+ outFile, err := os.Create(*outputFile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer outFile.Close()
+
+ if *extractSingle {
+ err = apkSet.extractAndCopySingle(sel, outFile)
+ } else {
+ writer := zip.NewWriter(outFile)
+ defer func() {
+ if err := writer.Close(); err != nil {
+ log.Fatal(err)
+ }
+ }()
+ apkcerts, err := apkSet.writeApks(sel, targetConfig, writer, *partition)
+ if err == nil && *apkcertsOutput != "" {
+ apkcertsFile, err := os.Create(*apkcertsOutput)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer apkcertsFile.Close()
+ for _, a := range apkcerts {
+ _, err = apkcertsFile.WriteString(a + "\n")
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ }
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
new file mode 100644
index 0000000..356faab
--- /dev/null
+++ b/cmd/extract_apks/main_test.go
@@ -0,0 +1,493 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+
+ bp "android/soong/cmd/extract_apks/bundle_proto"
+ "android/soong/third_party/zip"
+)
+
+type testConfigDesc struct {
+ name string
+ targetConfig TargetConfig
+ expected SelectionResult
+}
+
+type testDesc struct {
+ protoText string
+ configs []testConfigDesc
+}
+
+func TestSelectApks_ApkSet(t *testing.T) {
+ testCases := []testDesc{
+ {
+ protoText: `
+variant {
+ targeting {
+ sdk_version_targeting {
+ value { min { value: 29 } } } }
+ apk_set {
+ module_metadata {
+ name: "base" targeting {} delivery_type: INSTALL_TIME }
+ apk_description {
+ targeting {
+ screen_density_targeting {
+ value { density_alias: LDPI } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-ldpi.apk"
+ split_apk_metadata { split_id: "config.ldpi" } }
+ apk_description {
+ targeting {
+ screen_density_targeting {
+ value { density_alias: MDPI } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-mdpi.apk"
+ split_apk_metadata { split_id: "config.mdpi" } }
+ apk_description {
+ targeting {
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-master.apk"
+ split_apk_metadata { is_master_split: true } }
+ apk_description {
+ targeting {
+ abi_targeting {
+ value { alias: ARMEABI_V7A }
+ alternatives { alias: ARM64_V8A }
+ alternatives { alias: X86 }
+ alternatives { alias: X86_64 } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-armeabi_v7a.apk"
+ split_apk_metadata { split_id: "config.armeabi_v7a" } }
+ apk_description {
+ targeting {
+ abi_targeting {
+ value { alias: ARM64_V8A }
+ alternatives { alias: ARMEABI_V7A }
+ alternatives { alias: X86 }
+ alternatives { alias: X86_64 } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-arm64_v8a.apk"
+ split_apk_metadata { split_id: "config.arm64_v8a" } }
+ apk_description {
+ targeting {
+ abi_targeting {
+ value { alias: X86 }
+ alternatives { alias: ARMEABI_V7A }
+ alternatives { alias: ARM64_V8A }
+ alternatives { alias: X86_64 } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-x86.apk"
+ split_apk_metadata { split_id: "config.x86" } }
+ apk_description {
+ targeting {
+ abi_targeting {
+ value { alias: X86_64 }
+ alternatives { alias: ARMEABI_V7A }
+ alternatives { alias: ARM64_V8A }
+ alternatives { alias: X86 } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-x86_64.apk"
+ split_apk_metadata { split_id: "config.x86_64" } } }
+}
+bundletool {
+ version: "0.10.3" }
+
+`,
+ configs: []testConfigDesc{
+ {
+ name: "one",
+ targetConfig: TargetConfig{
+ sdkVersion: 29,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARMEABI_V7A: 0,
+ bp.Abi_ARM64_V8A: 1,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "splits/base-ldpi.apk",
+ "splits/base-mdpi.apk",
+ "splits/base-master.apk",
+ "splits/base-armeabi_v7a.apk",
+ },
+ },
+ },
+ {
+ name: "two",
+ targetConfig: TargetConfig{
+ sdkVersion: 29,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_LDPI: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{},
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "splits/base-ldpi.apk",
+ "splits/base-master.apk",
+ },
+ },
+ },
+ {
+ name: "three",
+ targetConfig: TargetConfig{
+ sdkVersion: 20,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_LDPI: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{},
+ },
+ expected: SelectionResult{
+ "",
+ nil,
+ },
+ },
+ {
+ name: "four",
+ targetConfig: TargetConfig{
+ sdkVersion: 29,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_MDPI: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARM64_V8A: 0,
+ bp.Abi_ARMEABI_V7A: 1,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "splits/base-mdpi.apk",
+ "splits/base-master.apk",
+ "splits/base-arm64_v8a.apk",
+ },
+ },
+ },
+ },
+ },
+ {
+ protoText: `
+variant {
+ targeting {
+ sdk_version_targeting {
+ value { min { value: 10000 } } } }
+ apk_set {
+ module_metadata {
+ name: "base" targeting {} delivery_type: INSTALL_TIME }
+ apk_description {
+ targeting {
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "splits/base-master.apk"
+ split_apk_metadata { is_master_split: true } } } }`,
+ configs: []testConfigDesc{
+ {
+ name: "Prerelease",
+ targetConfig: TargetConfig{
+ sdkVersion: 30,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{},
+ abis: map[bp.Abi_AbiAlias]int{},
+ allowPrereleased: true,
+ },
+ expected: SelectionResult{
+ "base",
+ []string{"splits/base-master.apk"},
+ },
+ },
+ },
+ },
+ {
+ protoText: `
+variant {
+ targeting {
+ sdk_version_targeting {
+ value { min { value: 29 } } } }
+ apk_set {
+ module_metadata {
+ name: "base" targeting {} delivery_type: INSTALL_TIME }
+ apk_description {
+ targeting {}
+ path: "universal.apk"
+ standalone_apk_metadata { fused_module_name: "base" } } } }`,
+ configs: []testConfigDesc{
+ {
+ name: "Universal",
+ targetConfig: TargetConfig{sdkVersion: 30},
+ expected: SelectionResult{
+ "base",
+ []string{"universal.apk"},
+ },
+ },
+ },
+ },
+ }
+ for _, testCase := range testCases {
+ var toc bp.BuildApksResult
+ if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
+ t.Fatal(err)
+ }
+ for _, config := range testCase.configs {
+ actual := selectApks(&toc, config.targetConfig)
+ if !reflect.DeepEqual(config.expected, actual) {
+ t.Errorf("%s: expected %v, got %v", config.name, config.expected, actual)
+ }
+ }
+ }
+}
+
+func TestSelectApks_ApexSet(t *testing.T) {
+ testCases := []testDesc{
+ {
+ protoText: `
+variant {
+ targeting {
+ sdk_version_targeting {
+ value { min { value: 29 } } } }
+ apk_set {
+ module_metadata {
+ name: "base" targeting {} delivery_type: INSTALL_TIME }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value { abi { alias: ARMEABI_V7A } }
+ alternatives { abi { alias: ARM64_V8A } }
+ alternatives { abi { alias: X86 } }
+ alternatives { abi { alias: X86_64 } } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "standalones/standalone-armeabi_v7a.apex"
+ apex_apk_metadata { } }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value { abi { alias: ARM64_V8A } }
+ alternatives { abi { alias: ARMEABI_V7A } }
+ alternatives { abi { alias: X86 } }
+ alternatives { abi { alias: X86_64 } } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "standalones/standalone-arm64_v8a.apex"
+ apex_apk_metadata { } }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value { abi { alias: X86 } }
+ alternatives { abi { alias: ARMEABI_V7A } }
+ alternatives { abi { alias: ARM64_V8A } }
+ alternatives { abi { alias: X86_64 } } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "standalones/standalone-x86.apex"
+ apex_apk_metadata { } }
+ apk_description {
+ targeting {
+ multi_abi_targeting {
+ value { abi { alias: X86_64 } }
+ alternatives { abi { alias: ARMEABI_V7A } }
+ alternatives { abi { alias: ARM64_V8A } }
+ alternatives { abi { alias: X86 } } }
+ sdk_version_targeting {
+ value { min { value: 21 } } } }
+ path: "standalones/standalone-x86_64.apex"
+ apex_apk_metadata { } } }
+}
+bundletool {
+ version: "0.10.3" }
+
+`,
+ configs: []testConfigDesc{
+ {
+ name: "order matches priorities",
+ targetConfig: TargetConfig{
+ sdkVersion: 29,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARM64_V8A: 0,
+ bp.Abi_ARMEABI_V7A: 1,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-arm64_v8a.apex",
+ },
+ },
+ },
+ {
+ name: "order doesn't match priorities",
+ targetConfig: TargetConfig{
+ sdkVersion: 29,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARMEABI_V7A: 0,
+ bp.Abi_ARM64_V8A: 1,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-arm64_v8a.apex",
+ },
+ },
+ },
+ {
+ name: "single choice",
+ targetConfig: TargetConfig{
+ sdkVersion: 29,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARMEABI_V7A: 0,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-armeabi_v7a.apex",
+ },
+ },
+ },
+ {
+ name: "cross platform",
+ targetConfig: TargetConfig{
+ sdkVersion: 29,
+ screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+ bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+ },
+ abis: map[bp.Abi_AbiAlias]int{
+ bp.Abi_ARM64_V8A: 0,
+ bp.Abi_MIPS64: 1,
+ bp.Abi_X86: 2,
+ },
+ },
+ expected: SelectionResult{
+ "base",
+ []string{
+ "standalones/standalone-x86.apex",
+ },
+ },
+ },
+ },
+ },
+ }
+ for _, testCase := range testCases {
+ var toc bp.BuildApksResult
+ if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
+ t.Fatal(err)
+ }
+ for _, config := range testCase.configs {
+ actual := selectApks(&toc, config.targetConfig)
+ if !reflect.DeepEqual(config.expected, actual) {
+ t.Errorf("%s: expected %v, got %v", config.name, config.expected, actual)
+ }
+ }
+ }
+}
+
+type testZip2ZipWriter struct {
+ entries map[string]string
+}
+
+func (w testZip2ZipWriter) CopyFrom(file *zip.File, out string) error {
+ if x, ok := w.entries[out]; ok {
+ return fmt.Errorf("%s and %s both write to %s", x, file.Name, out)
+ }
+ w.entries[out] = file.Name
+ return nil
+}
+
+type testCaseWriteApks struct {
+ name string
+ moduleName string
+ stem string
+ partition string
+ // what we write from what
+ expectedZipEntries map[string]string
+ expectedApkcerts []string
+}
+
+func TestWriteApks(t *testing.T) {
+ testCases := []testCaseWriteApks{
+ {
+ name: "splits",
+ moduleName: "mybase",
+ stem: "Foo",
+ partition: "system",
+ expectedZipEntries: map[string]string{
+ "Foo.apk": "splits/mybase-master.apk",
+ "Foo-xhdpi.apk": "splits/mybase-xhdpi.apk",
+ },
+ expectedApkcerts: []string{
+ `name="Foo-xhdpi.apk" certificate="PRESIGNED" private_key=""`,
+ `name="Foo.apk" certificate="PRESIGNED" private_key=""`,
+ },
+ },
+ {
+ name: "universal",
+ moduleName: "base",
+ stem: "Bar",
+ partition: "product",
+ expectedZipEntries: map[string]string{
+ "Bar.apk": "universal.apk",
+ },
+ expectedApkcerts: []string{
+ `name="Bar.apk" certificate="PRESIGNED" private_key=""`,
+ },
+ },
+ }
+ for _, testCase := range testCases {
+ apkSet := ApkSet{entries: make(map[string]*zip.File)}
+ sel := SelectionResult{moduleName: testCase.moduleName}
+ for _, in := range testCase.expectedZipEntries {
+ apkSet.entries[in] = &zip.File{FileHeader: zip.FileHeader{Name: in}}
+ sel.entries = append(sel.entries, in)
+ }
+ writer := testZip2ZipWriter{make(map[string]string)}
+ config := TargetConfig{stem: testCase.stem}
+ apkcerts, err := apkSet.writeApks(sel, config, writer, testCase.partition)
+ if err != nil {
+ t.Error(err)
+ }
+ if !reflect.DeepEqual(testCase.expectedZipEntries, writer.entries) {
+ t.Errorf("expected zip entries %v, got %v", testCase.expectedZipEntries, writer.entries)
+ }
+ if !reflect.DeepEqual(testCase.expectedApkcerts, apkcerts) {
+ t.Errorf("expected apkcerts %v, got %v", testCase.expectedApkcerts, apkcerts)
+ }
+ }
+}
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index 3f24ab2..ea0bf4e 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -13,7 +13,7 @@
// limitations under the License.
// This tool extracts ELF LOAD segments from our linker binary, and produces an
-// assembly file and linker script which will embed those segments as sections
+// assembly file and linker flags which will embed those segments as sections
// in another binary.
package main
@@ -26,38 +26,15 @@
"io/ioutil"
"log"
"os"
- "text/template"
+ "strings"
)
-var linkerScriptTemplate = template.Must(template.New("linker_script").Parse(`
-ENTRY(__dlwrap__start)
-SECTIONS {
- __dlwrap_original_start = _start;
- /DISCARD/ : { *(.interp) }
-
-{{range .}}
- . = {{ printf "0x%x" .Vaddr }};
- {{.Name}} : { KEEP(*({{.Name}})) }
-{{end}}
-
- .text : { *(.text .text.*) }
- .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
- .data : { *(.data .data.* .gnu.linkonce.d.*) }
- .bss : { *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) }
-}
-`))
-
-type LinkerSection struct {
- Name string
- Vaddr uint64
-}
-
func main() {
var asmPath string
- var scriptPath string
+ var flagsPath string
flag.StringVar(&asmPath, "s", "", "Path to save the assembly file")
- flag.StringVar(&scriptPath, "T", "", "Path to save the linker script")
+ flag.StringVar(&flagsPath, "f", "", "Path to save the linker flags")
flag.Parse()
f, err := os.Open(flag.Arg(0))
@@ -72,19 +49,21 @@
}
asm := &bytes.Buffer{}
-
- fmt.Fprintln(asm, ".globl __dlwrap_linker_entry")
- fmt.Fprintf(asm, ".set __dlwrap_linker_entry, 0x%x\n\n", ef.Entry)
-
baseLoadAddr := uint64(0x1000)
- sections := []LinkerSection{}
load := 0
+ linkFlags := []string{}
+
+ fmt.Fprintln(asm, ".globl __dlwrap_linker_offset")
+ fmt.Fprintf(asm, ".set __dlwrap_linker_offset, 0x%x\n", baseLoadAddr)
+
for _, prog := range ef.Progs {
if prog.Type != elf.PT_LOAD {
continue
}
sectionName := fmt.Sprintf(".linker.sect%d", load)
+ symName := fmt.Sprintf("__dlwrap_linker_sect%d", load)
+
flags := ""
if prog.Flags&elf.PF_W != 0 {
flags += "w"
@@ -94,10 +73,12 @@
}
fmt.Fprintf(asm, ".section %s, \"a%s\"\n", sectionName, flags)
- if load == 0 {
- fmt.Fprintln(asm, ".globl __dlwrap_linker_code_start")
- fmt.Fprintln(asm, "__dlwrap_linker_code_start:")
- }
+ fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName)
+
+ linkFlags = append(linkFlags,
+ fmt.Sprintf("-Wl,--undefined=%s", symName),
+ fmt.Sprintf("-Wl,--section-start=%s=0x%x",
+ sectionName, baseLoadAddr+prog.Vaddr))
buffer, _ := ioutil.ReadAll(prog.Open())
bytesToAsm(asm, buffer)
@@ -113,11 +94,6 @@
}
fmt.Fprintln(asm)
- sections = append(sections, LinkerSection{
- Name: sectionName,
- Vaddr: baseLoadAddr + prog.Vaddr,
- })
-
load += 1
}
@@ -127,13 +103,10 @@
}
}
- if scriptPath != "" {
- buf := &bytes.Buffer{}
- if err := linkerScriptTemplate.Execute(buf, sections); err != nil {
- log.Fatalf("Failed to create linker script: %v", err)
- }
- if err := ioutil.WriteFile(scriptPath, buf.Bytes(), 0777); err != nil {
- log.Fatalf("Unable to write %q: %v", scriptPath, err)
+ if flagsPath != "" {
+ flags := strings.Join(linkFlags, " ")
+ if err := ioutil.WriteFile(flagsPath, []byte(flags), 0777); err != nil {
+ log.Fatalf("Unable to write %q: %v", flagsPath, err)
}
}
}
diff --git a/ui/build/util_linux.go b/cmd/host_bionic_inject/Android.bp
similarity index 70%
copy from ui/build/util_linux.go
copy to cmd/host_bionic_inject/Android.bp
index 0a4e1d2..5994103 100644
--- a/ui/build/util_linux.go
+++ b/cmd/host_bionic_inject/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All rights reserved.
+// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,10 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package build
-
-import (
- "syscall"
-)
-
-const ioctlGetTermios = syscall.TCGETS
+blueprint_go_binary {
+ name: "host_bionic_inject",
+ deps: ["soong-symbol_inject"],
+ srcs: ["host_bionic_inject.go"],
+ testSrcs: ["host_bionic_inject_test.go"],
+}
diff --git a/cmd/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_inject/host_bionic_inject.go
new file mode 100644
index 0000000..f7163d7
--- /dev/null
+++ b/cmd/host_bionic_inject/host_bionic_inject.go
@@ -0,0 +1,169 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Verifies a host bionic executable with an embedded linker, then injects
+// the address of the _start function for the linker_wrapper to use.
+package main
+
+import (
+ "debug/elf"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+
+ "android/soong/symbol_inject"
+)
+
+func main() {
+ var inputFile, linkerFile, outputFile string
+
+ flag.StringVar(&inputFile, "i", "", "Input file")
+ flag.StringVar(&linkerFile, "l", "", "Linker file")
+ flag.StringVar(&outputFile, "o", "", "Output file")
+ flag.Parse()
+
+ if inputFile == "" || linkerFile == "" || outputFile == "" || flag.NArg() != 0 {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ r, err := os.Open(inputFile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(2)
+ }
+ defer r.Close()
+
+ file, err := symbol_inject.OpenFile(r)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(3)
+ }
+
+ linker, err := elf.Open(linkerFile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(4)
+ }
+
+ start_addr, err := parseElf(r, linker)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(5)
+ }
+
+ w, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(6)
+ }
+ defer w.Close()
+
+ err = symbol_inject.InjectUint64Symbol(file, w, "__dlwrap_original_start", start_addr)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(7)
+ }
+}
+
+// Check the ELF file, and return the address to the _start function
+func parseElf(r io.ReaderAt, linker *elf.File) (uint64, error) {
+ file, err := elf.NewFile(r)
+ if err != nil {
+ return 0, err
+ }
+
+ symbols, err := file.Symbols()
+ if err != nil {
+ return 0, err
+ }
+
+ for _, prog := range file.Progs {
+ if prog.Type == elf.PT_INTERP {
+ return 0, fmt.Errorf("File should not have a PT_INTERP header")
+ }
+ }
+
+ if dlwrap_start, err := findSymbol(symbols, "__dlwrap__start"); err != nil {
+ return 0, err
+ } else if dlwrap_start.Value != file.Entry {
+ return 0, fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)",
+ file.Entry, dlwrap_start.Value)
+ }
+
+ err = checkLinker(file, linker, symbols)
+ if err != nil {
+ return 0, err
+ }
+
+ start, err := findSymbol(symbols, "_start")
+ if err != nil {
+ return 0, fmt.Errorf("Failed to find _start symbol")
+ }
+ return start.Value, nil
+}
+
+func findSymbol(symbols []elf.Symbol, name string) (elf.Symbol, error) {
+ for _, sym := range symbols {
+ if sym.Name == name {
+ return sym, nil
+ }
+ }
+ return elf.Symbol{}, fmt.Errorf("Failed to find symbol %q", name)
+}
+
+// Check that all of the PT_LOAD segments have been embedded properly
+func checkLinker(file, linker *elf.File, fileSyms []elf.Symbol) error {
+ dlwrap_linker_offset, err := findSymbol(fileSyms, "__dlwrap_linker_offset")
+ if err != nil {
+ return err
+ }
+
+ for i, lprog := range linker.Progs {
+ if lprog.Type != elf.PT_LOAD {
+ continue
+ }
+
+ laddr := lprog.Vaddr + dlwrap_linker_offset.Value
+
+ found := false
+ for _, prog := range file.Progs {
+ if prog.Type != elf.PT_LOAD {
+ continue
+ }
+
+ if laddr < prog.Vaddr || laddr > prog.Vaddr+prog.Memsz {
+ continue
+ }
+ found = true
+
+ if lprog.Flags != prog.Flags {
+ return fmt.Errorf("Linker prog %d (0x%x) flags (%s) do not match (%s)",
+ i, lprog.Vaddr, lprog.Flags, prog.Flags)
+ }
+
+ if laddr+lprog.Memsz > prog.Vaddr+prog.Filesz {
+ return fmt.Errorf("Linker prog %d (0x%x) not fully present (0x%x > 0x%x)",
+ i, lprog.Vaddr, laddr+lprog.Memsz, prog.Vaddr+prog.Filesz)
+ }
+ }
+ if !found {
+ return fmt.Errorf("Linker prog %d (0x%x) not found at offset 0x%x",
+ i, lprog.Vaddr, dlwrap_linker_offset.Value)
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/host_bionic_inject/host_bionic_inject_test.go b/cmd/host_bionic_inject/host_bionic_inject_test.go
new file mode 100644
index 0000000..b415b34
--- /dev/null
+++ b/cmd/host_bionic_inject/host_bionic_inject_test.go
@@ -0,0 +1,146 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "debug/elf"
+ "fmt"
+ "testing"
+)
+
+// prog is a shortcut to fill out a elf.Prog structure
+func prog(flags elf.ProgFlag, offset, addr, filesz, memsz uint64) *elf.Prog {
+ return &elf.Prog{
+ ProgHeader: elf.ProgHeader{
+ Type: elf.PT_LOAD,
+ Flags: flags,
+ Off: offset,
+ Vaddr: addr,
+ Paddr: addr,
+ Filesz: filesz,
+ Memsz: memsz,
+ },
+ }
+}
+
+// linkerGold returns an example elf.File from a linker binary that was linked
+// with gold.
+func linkerGold() *elf.File {
+ return &elf.File{
+ Progs: []*elf.Prog{
+ prog(elf.PF_R|elf.PF_X, 0, 0, 0xd0fac, 0xd0fac),
+ prog(elf.PF_R|elf.PF_W, 0xd1050, 0xd2050, 0x6890, 0xd88c),
+ },
+ }
+}
+
+// fileGold returns an example elf binary with a properly embedded linker. The
+// embedded linker was the one returned by linkerGold.
+func fileGold() *elf.File {
+ return &elf.File{
+ Progs: []*elf.Prog{
+ prog(elf.PF_R, 0, 0, 0x2e0, 0x2e0),
+ prog(elf.PF_R|elf.PF_X, 0x1000, 0x1000, 0xd0fac, 0xd0fac),
+ prog(elf.PF_R|elf.PF_W, 0xd2050, 0xd3050, 0xd88c, 0xd88c),
+ prog(elf.PF_R, 0xe0000, 0xe1000, 0x10e4, 0x10e4),
+ prog(elf.PF_R|elf.PF_X, 0xe2000, 0xe3000, 0x1360, 0x1360),
+ prog(elf.PF_R|elf.PF_W, 0xe4000, 0xe5000, 0x1358, 0x1358),
+ },
+ }
+}
+
+// linkerLld returns an example elf.File from a linker binary that was linked
+// with lld.
+func linkerLld() *elf.File {
+ return &elf.File{
+ Progs: []*elf.Prog{
+ prog(elf.PF_R, 0, 0, 0x3c944, 0x3c944),
+ prog(elf.PF_R|elf.PF_X, 0x3d000, 0x3d000, 0x946fa, 0x946fa),
+ prog(elf.PF_R|elf.PF_W, 0xd2000, 0xd2000, 0x7450, 0xf778),
+ },
+ }
+}
+
+// fileGold returns an example elf binary with a properly embedded linker. The
+// embedded linker was the one returned by linkerLld.
+func fileLld() *elf.File {
+ return &elf.File{
+ Progs: []*elf.Prog{
+ prog(elf.PF_R, 0, 0, 0x3d944, 0x3d944),
+ prog(elf.PF_R|elf.PF_X, 0x3e000, 0x3e000, 0x946fa, 0x946fa),
+ prog(elf.PF_R|elf.PF_W, 0xd3000, 0xd3000, 0xf778, 0xf778),
+ prog(elf.PF_R, 0xe3000, 0xe3000, 0x10e4, 0x10e4),
+ prog(elf.PF_R|elf.PF_X, 0xe5000, 0xe5000, 0x1360, 0x1360),
+ prog(elf.PF_R|elf.PF_W, 0xe7000, 0xe7000, 0x1358, 0x1358),
+ },
+ }
+}
+
+// linkerOffset returns the symbol representing the linker offset used by both
+// fileGold and fileLld
+func linkerOffset() []elf.Symbol {
+ return []elf.Symbol{
+ elf.Symbol{
+ Name: "__dlwrap_linker_offset",
+ Value: 0x1000,
+ },
+ }
+}
+
+func TestCheckLinker(t *testing.T) {
+ cases := []struct {
+ name string
+ err error
+ file func() *elf.File
+ linker func() *elf.File
+ }{
+ {
+ name: "good gold-linked linker",
+ file: fileGold,
+ linker: linkerGold,
+ },
+ {
+ name: "good lld-linked linker",
+ file: fileLld,
+ linker: linkerLld,
+ },
+ {
+ name: "truncated RO section",
+ err: fmt.Errorf("Linker prog 0 (0x0) not fully present (0x3d944 > 0x3d943)"),
+ file: func() *elf.File {
+ f := fileLld()
+ f.Progs[0].Filesz -= 1
+ f.Progs[0].Memsz -= 1
+ return f
+ },
+ linker: linkerLld,
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ err := checkLinker(tc.file(), tc.linker(), linkerOffset())
+ if tc.err == nil {
+ if err != nil {
+ t.Fatalf("No error expected, but got: %v", err)
+ }
+ } else if err == nil {
+ t.Fatalf("Returned no error, but wanted: %v", tc.err)
+ } else if err.Error() != tc.err.Error() {
+ t.Fatalf("Different error found:\nwant: %v\n got: %v", tc.err, err)
+ }
+ })
+ }
+}
diff --git a/cmd/javac_wrapper/javac_wrapper.go b/cmd/javac_wrapper/javac_wrapper.go
index 4df4938..7a448ba 100644
--- a/cmd/javac_wrapper/javac_wrapper.go
+++ b/cmd/javac_wrapper/javac_wrapper.go
@@ -176,4 +176,12 @@
regexp.MustCompile(`Note: (Some input files|.*\.java) uses? unchecked or unsafe operations.`),
regexp.MustCompile(`Note: Recompile with -Xlint:unchecked for details.`),
regexp.MustCompile(`bootstrap class path not set in conjunction with -source`),
+
+ regexp.MustCompile(`javadoc: warning - The old Doclet and Taglet APIs in the packages`),
+ regexp.MustCompile(`com.sun.javadoc, com.sun.tools.doclets and their implementations`),
+ regexp.MustCompile(`are planned to be removed in a future JDK release. These`),
+ regexp.MustCompile(`components have been superseded by the new APIs in jdk.javadoc.doclet.`),
+ regexp.MustCompile(`Users are strongly recommended to migrate to the new APIs.`),
+
+ regexp.MustCompile(`javadoc: option --boot-class-path not allowed with target 1.9`),
}
diff --git a/cmd/javac_wrapper/javac_wrapper_test.go b/cmd/javac_wrapper/javac_wrapper_test.go
index d76793f..ad657e7 100644
--- a/cmd/javac_wrapper/javac_wrapper_test.go
+++ b/cmd/javac_wrapper/javac_wrapper_test.go
@@ -64,6 +64,17 @@
in: "\n",
out: "\n",
},
+ {
+ in: `
+javadoc: warning - The old Doclet and Taglet APIs in the packages
+com.sun.javadoc, com.sun.tools.doclets and their implementations
+are planned to be removed in a future JDK release. These
+components have been superseded by the new APIs in jdk.javadoc.doclet.
+Users are strongly recommended to migrate to the new APIs.
+javadoc: option --boot-class-path not allowed with target 1.9
+`,
+ out: "\n",
+ },
}
func TestJavacColorize(t *testing.T) {
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index ace079d..ab658fd 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -16,10 +16,14 @@
name: "merge_zips",
deps: [
"android-archive-zip",
+ "blueprint-pathtools",
"soong-jar",
],
srcs: [
"merge_zips.go",
],
+ testSrcs: [
+ "merge_zips_test.go",
+ ],
}
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index df04358..68fe259 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -19,12 +19,14 @@
"flag"
"fmt"
"hash/crc32"
+ "io"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
- "strings"
+
+ "github.com/google/blueprint/pathtools"
"android/soong/jar"
"android/soong/third_party/zip"
@@ -63,19 +65,20 @@
zipsToNotStrip = make(zipsToNotStripSet)
stripDirEntries = flag.Bool("D", false, "strip directory entries from the output zip file")
manifest = flag.String("m", "", "manifest file to insert in jar")
- entrypoint = flag.String("e", "", "par entrypoint file to insert in par")
+ pyMain = flag.String("pm", "", "__main__.py file to insert in par")
+ prefix = flag.String("prefix", "", "A file to prefix to the zip file")
ignoreDuplicates = flag.Bool("ignore-duplicates", false, "take each entry from the first zip it exists in and don't warn")
)
func init() {
- flag.Var(&stripDirs, "stripDir", "the prefix of file path to be excluded from the output zip")
- flag.Var(&stripFiles, "stripFile", "filenames to be excluded from the output zip, accepts wildcards")
+ flag.Var(&stripDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
+ flag.Var(&stripFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
flag.Var(&zipsToNotStrip, "zipToNotStrip", "the input zip file which is not applicable for stripping")
}
func main() {
flag.Usage = func() {
- fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [-e entrypoint] output [inputs...]")
+ fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] output [inputs...]")
flag.PrintDefaults()
}
@@ -97,6 +100,19 @@
log.Fatal(err)
}
defer output.Close()
+
+ var offset int64
+ if *prefix != "" {
+ prefixFile, err := os.Open(*prefix)
+ if err != nil {
+ log.Fatal(err)
+ }
+ offset, err = io.Copy(output, prefixFile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+
writer := zip.NewWriter(output)
defer func() {
err := writer.Close()
@@ -104,6 +120,7 @@
log.Fatal(err)
}
}()
+ writer.SetOffset(offset)
// make readers
readers := []namedZipReader{}
@@ -113,7 +130,7 @@
log.Fatal(err)
}
defer reader.Close()
- namedReader := namedZipReader{path: input, reader: reader}
+ namedReader := namedZipReader{path: input, reader: &reader.Reader}
readers = append(readers, namedReader)
}
@@ -121,13 +138,13 @@
log.Fatal(errors.New("must specify -j when specifying a manifest via -m"))
}
- if *entrypoint != "" && !*emulatePar {
- log.Fatal(errors.New("must specify -p when specifying a entrypoint via -e"))
+ if *pyMain != "" && !*emulatePar {
+ log.Fatal(errors.New("must specify -p when specifying a Python __main__.py via -pm"))
}
// do merge
- err = mergeZips(readers, writer, *manifest, *entrypoint, *sortEntries, *emulateJar, *emulatePar,
- *stripDirEntries, *ignoreDuplicates)
+ err = mergeZips(readers, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar,
+ *stripDirEntries, *ignoreDuplicates, []string(stripFiles), []string(stripDirs), map[string]bool(zipsToNotStrip))
if err != nil {
log.Fatal(err)
}
@@ -136,7 +153,7 @@
// a namedZipReader reads a .zip file and can say which file it's reading
type namedZipReader struct {
path string
- reader *zip.ReadCloser
+ reader *zip.Reader
}
// a zipEntryPath refers to a file contained in a zip
@@ -167,6 +184,10 @@
return ze.content.FileHeader.CRC32
}
+func (ze zipEntry) Size() uint64 {
+ return ze.content.FileHeader.UncompressedSize64
+}
+
func (ze zipEntry) WriteToZip(dest string, zw *zip.Writer) error {
return zw.CopyFrom(ze.content, dest)
}
@@ -189,6 +210,10 @@
return crc32.ChecksumIEEE(be.content)
}
+func (be bufferEntry) Size() uint64 {
+ return uint64(len(be.content))
+}
+
func (be bufferEntry) WriteToZip(dest string, zw *zip.Writer) error {
w, err := zw.CreateHeader(be.fh)
if err != nil {
@@ -209,6 +234,7 @@
String() string
IsDir() bool
CRC32() uint32
+ Size() uint64
WriteToZip(dest string, zw *zip.Writer) error
}
@@ -218,8 +244,9 @@
source zipSource
}
-func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, entrypoint string,
- sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool) error {
+func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, pyMain string,
+ sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
+ stripFiles, stripDirs []string, zipsToNotStrip map[string]bool) error {
sourceByDest := make(map[string]zipSource, 0)
orderedMappings := []fileMapping{}
@@ -243,7 +270,12 @@
addMapping(jar.MetaDir, dirSource)
}
- fh, buf, err := jar.ManifestFileContents(manifest)
+ contents, err := ioutil.ReadFile(manifest)
+ if err != nil {
+ return err
+ }
+
+ fh, buf, err := jar.ManifestFileContents(contents)
if err != nil {
return err
}
@@ -252,20 +284,20 @@
addMapping(jar.ManifestFile, fileSource)
}
- if entrypoint != "" {
- buf, err := ioutil.ReadFile(entrypoint)
+ if pyMain != "" {
+ buf, err := ioutil.ReadFile(pyMain)
if err != nil {
return err
}
fh := &zip.FileHeader{
- Name: "entry_point.txt",
+ Name: "__main__.py",
Method: zip.Store,
UncompressedSize64: uint64(len(buf)),
}
fh.SetMode(0700)
fh.SetModTime(jar.DefaultTime)
fileSource := bufferEntry{fh, buf}
- addMapping("entry_point.txt", fileSource)
+ addMapping("__main__.py", fileSource)
}
if emulatePar {
@@ -317,8 +349,12 @@
for _, namedReader := range readers {
_, skipStripThisZip := zipsToNotStrip[namedReader.path]
for _, file := range namedReader.reader.File {
- if !skipStripThisZip && shouldStripFile(emulateJar, file.Name) {
- continue
+ if !skipStripThisZip {
+ if skip, err := shouldStripEntry(emulateJar, stripFiles, stripDirs, file.Name); err != nil {
+ return err
+ } else if skip {
+ continue
+ }
}
if stripDirEntries && file.FileInfo().IsDir() {
@@ -337,25 +373,27 @@
return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
dest, existingSource, source)
}
+
if ignoreDuplicates {
continue
}
+
if emulateJar &&
file.Name == jar.ManifestFile || file.Name == jar.ModuleInfoClass {
// Skip manifest and module info files that are not from the first input file
continue
}
- if !source.IsDir() {
- if emulateJar {
- if existingSource.CRC32() != source.CRC32() {
- fmt.Fprintf(os.Stdout, "WARNING: Duplicate path %v found in %v and %v\n",
- dest, existingSource, source)
- }
- } else {
- return fmt.Errorf("Duplicate path %v found in %v and %v\n",
- dest, existingSource, source)
- }
+
+ if source.IsDir() {
+ continue
}
+
+ if existingSource.CRC32() == source.CRC32() && existingSource.Size() == source.Size() {
+ continue
+ }
+
+ return fmt.Errorf("Duplicate path %v found in %v and %v\n",
+ dest, existingSource, source)
}
}
}
@@ -398,26 +436,41 @@
return ret
}
-func shouldStripFile(emulateJar bool, name string) bool {
+func shouldStripEntry(emulateJar bool, stripFiles, stripDirs []string, name string) (bool, error) {
for _, dir := range stripDirs {
- if strings.HasPrefix(name, dir+"/") {
- if emulateJar {
- if name != jar.MetaDir && name != jar.ManifestFile {
- return true
+ dir = filepath.Clean(dir)
+ patterns := []string{
+ dir + "/", // the directory itself
+ dir + "/**/*", // files recursively in the directory
+ dir + "/**/*/", // directories recursively in the directory
+ }
+
+ for _, pattern := range patterns {
+ match, err := pathtools.Match(pattern, name)
+ if err != nil {
+ return false, fmt.Errorf("%s: %s", err.Error(), pattern)
+ } else if match {
+ if emulateJar {
+ // When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
+ // requested.
+ // TODO(ccross): which files does this affect?
+ if name != jar.MetaDir && name != jar.ManifestFile {
+ return true, nil
+ }
}
- } else {
- return true
+ return true, nil
}
}
}
+
for _, pattern := range stripFiles {
- if match, err := filepath.Match(pattern, filepath.Base(name)); err != nil {
- panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+ if match, err := pathtools.Match(pattern, name); err != nil {
+ return false, fmt.Errorf("%s: %s", err.Error(), pattern)
} else if match {
- return true
+ return true, nil
}
}
- return false
+ return false, nil
}
func jarSort(files []fileMapping) {
diff --git a/cmd/merge_zips/merge_zips_test.go b/cmd/merge_zips/merge_zips_test.go
new file mode 100644
index 0000000..dbde270
--- /dev/null
+++ b/cmd/merge_zips/merge_zips_test.go
@@ -0,0 +1,306 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+
+ "android/soong/jar"
+ "android/soong/third_party/zip"
+)
+
+type testZipEntry struct {
+ name string
+ mode os.FileMode
+ data []byte
+}
+
+var (
+ A = testZipEntry{"A", 0755, []byte("foo")}
+ a = testZipEntry{"a", 0755, []byte("foo")}
+ a2 = testZipEntry{"a", 0755, []byte("FOO2")}
+ a3 = testZipEntry{"a", 0755, []byte("Foo3")}
+ bDir = testZipEntry{"b/", os.ModeDir | 0755, nil}
+ bbDir = testZipEntry{"b/b/", os.ModeDir | 0755, nil}
+ bbb = testZipEntry{"b/b/b", 0755, nil}
+ ba = testZipEntry{"b/a", 0755, []byte("foob")}
+ bc = testZipEntry{"b/c", 0755, []byte("bar")}
+ bd = testZipEntry{"b/d", 0700, []byte("baz")}
+ be = testZipEntry{"b/e", 0700, []byte("")}
+
+ metainfDir = testZipEntry{jar.MetaDir, os.ModeDir | 0755, nil}
+ manifestFile = testZipEntry{jar.ManifestFile, 0755, []byte("manifest")}
+ manifestFile2 = testZipEntry{jar.ManifestFile, 0755, []byte("manifest2")}
+ moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info")}
+)
+
+func TestMergeZips(t *testing.T) {
+ testCases := []struct {
+ name string
+ in [][]testZipEntry
+ stripFiles []string
+ stripDirs []string
+ jar bool
+ sort bool
+ ignoreDuplicates bool
+ stripDirEntries bool
+ zipsToNotStrip map[string]bool
+
+ out []testZipEntry
+ err string
+ }{
+ {
+ name: "duplicates error",
+ in: [][]testZipEntry{
+ {a},
+ {a2},
+ {a3},
+ },
+ out: []testZipEntry{a},
+ err: "duplicate",
+ },
+ {
+ name: "duplicates take first",
+ in: [][]testZipEntry{
+ {a},
+ {a2},
+ {a3},
+ },
+ out: []testZipEntry{a},
+
+ ignoreDuplicates: true,
+ },
+ {
+ name: "duplicates identical",
+ in: [][]testZipEntry{
+ {a},
+ {a},
+ },
+ out: []testZipEntry{a},
+ },
+ {
+ name: "sort",
+ in: [][]testZipEntry{
+ {be, bc, bDir, bbDir, bbb, A, metainfDir, manifestFile},
+ },
+ out: []testZipEntry{A, metainfDir, manifestFile, bDir, bbDir, bbb, bc, be},
+
+ sort: true,
+ },
+ {
+ name: "jar sort",
+ in: [][]testZipEntry{
+ {be, bc, bDir, A, metainfDir, manifestFile},
+ },
+ out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
+
+ jar: true,
+ },
+ {
+ name: "jar merge",
+ in: [][]testZipEntry{
+ {metainfDir, manifestFile, bDir, be},
+ {metainfDir, manifestFile2, bDir, bc},
+ {metainfDir, manifestFile2, A},
+ },
+ out: []testZipEntry{metainfDir, manifestFile, A, bDir, bc, be},
+
+ jar: true,
+ },
+ {
+ name: "merge",
+ in: [][]testZipEntry{
+ {bDir, be},
+ {bDir, bc},
+ {A},
+ },
+ out: []testZipEntry{bDir, be, bc, A},
+ },
+ {
+ name: "strip dir entries",
+ in: [][]testZipEntry{
+ {a, bDir, bbDir, bbb, bc, bd, be},
+ },
+ out: []testZipEntry{a, bbb, bc, bd, be},
+
+ stripDirEntries: true,
+ },
+ {
+ name: "strip files",
+ in: [][]testZipEntry{
+ {a, bDir, bbDir, bbb, bc, bd, be},
+ },
+ out: []testZipEntry{a, bDir, bbDir, bbb, bc},
+
+ stripFiles: []string{"b/d", "b/e"},
+ },
+ {
+ // merge_zips used to treat -stripFile a as stripping any file named a, it now only strips a in the
+ // root of the zip.
+ name: "strip file name",
+ in: [][]testZipEntry{
+ {a, bDir, ba},
+ },
+ out: []testZipEntry{bDir, ba},
+
+ stripFiles: []string{"a"},
+ },
+ {
+ name: "strip files glob",
+ in: [][]testZipEntry{
+ {a, bDir, ba},
+ },
+ out: []testZipEntry{bDir},
+
+ stripFiles: []string{"**/a"},
+ },
+ {
+ name: "strip dirs",
+ in: [][]testZipEntry{
+ {a, bDir, bbDir, bbb, bc, bd, be},
+ },
+ out: []testZipEntry{a},
+
+ stripDirs: []string{"b"},
+ },
+ {
+ name: "strip dirs glob",
+ in: [][]testZipEntry{
+ {a, bDir, bbDir, bbb, bc, bd, be},
+ },
+ out: []testZipEntry{a, bDir, bc, bd, be},
+
+ stripDirs: []string{"b/*"},
+ },
+ {
+ name: "zips to not strip",
+ in: [][]testZipEntry{
+ {a, bDir, bc},
+ {bDir, bd},
+ {bDir, be},
+ },
+ out: []testZipEntry{a, bDir, bd},
+
+ stripDirs: []string{"b"},
+ zipsToNotStrip: map[string]bool{
+ "in1": true,
+ },
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ var readers []namedZipReader
+ for i, in := range test.in {
+ r := testZipEntriesToZipReader(in)
+ readers = append(readers, namedZipReader{
+ path: "in" + strconv.Itoa(i),
+ reader: r,
+ })
+ }
+
+ want := testZipEntriesToBuf(test.out)
+
+ out := &bytes.Buffer{}
+ writer := zip.NewWriter(out)
+
+ err := mergeZips(readers, writer, "", "",
+ test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
+ test.stripFiles, test.stripDirs, test.zipsToNotStrip)
+
+ closeErr := writer.Close()
+ if closeErr != nil {
+ t.Fatal(err)
+ }
+
+ if test.err != "" {
+ if err == nil {
+ t.Fatal("missing err, expected: ", test.err)
+ } else if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(test.err)) {
+ t.Fatal("incorrect err, want:", test.err, "got:", err)
+ }
+ return
+ }
+
+ if !bytes.Equal(want, out.Bytes()) {
+ t.Error("incorrect zip output")
+ t.Errorf("want:\n%s", dumpZip(want))
+ t.Errorf("got:\n%s", dumpZip(out.Bytes()))
+ }
+ })
+ }
+}
+
+func testZipEntriesToBuf(entries []testZipEntry) []byte {
+ b := &bytes.Buffer{}
+ zw := zip.NewWriter(b)
+
+ for _, e := range entries {
+ fh := zip.FileHeader{
+ Name: e.name,
+ }
+ fh.SetMode(e.mode)
+
+ w, err := zw.CreateHeader(&fh)
+ if err != nil {
+ panic(err)
+ }
+
+ _, err = w.Write(e.data)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ err := zw.Close()
+ if err != nil {
+ panic(err)
+ }
+
+ return b.Bytes()
+}
+
+func testZipEntriesToZipReader(entries []testZipEntry) *zip.Reader {
+ b := testZipEntriesToBuf(entries)
+ r := bytes.NewReader(b)
+
+ zr, err := zip.NewReader(r, int64(len(b)))
+ if err != nil {
+ panic(err)
+ }
+
+ return zr
+}
+
+func dumpZip(buf []byte) string {
+ r := bytes.NewReader(buf)
+ zr, err := zip.NewReader(r, int64(len(buf)))
+ if err != nil {
+ panic(err)
+ }
+
+ var ret string
+
+ for _, f := range zr.File {
+ ret += fmt.Sprintf("%v: %v %v %08x\n", f.Name, f.Mode(), f.UncompressedSize64, f.CRC32)
+ }
+
+ return ret
+}
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index 04a5802..13b3679 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -17,6 +17,7 @@
deps: [
"soong-ui-build",
"soong-ui-logger",
+ "soong-ui-terminal",
"soong-ui-tracer",
"soong-zip",
],
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 06c5626..330c5dd 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -18,6 +18,7 @@
"context"
"flag"
"fmt"
+ "io"
"io/ioutil"
"os"
"path/filepath"
@@ -27,8 +28,11 @@
"syscall"
"time"
+ "android/soong/finder"
"android/soong/ui/build"
"android/soong/ui/logger"
+ "android/soong/ui/status"
+ "android/soong/ui/terminal"
"android/soong/ui/tracer"
"android/soong/zip"
)
@@ -47,6 +51,7 @@
var numJobs = flag.Int("j", detectNumJobs(), "number of parallel kati jobs")
var keepArtifacts = flag.Bool("keep", false, "keep archives of artifacts")
+var incremental = flag.Bool("incremental", false, "run in incremental mode (saving intermediates)")
var outDir = flag.String("out", "", "path to store output directories (defaults to tmpdir under $OUT when empty)")
var alternateResultDir = flag.Bool("dist", false, "write select results to $DIST_DIR (or <out>/dist when empty)")
@@ -62,102 +67,31 @@
const errorLeadingLines = 20
const errorTrailingLines = 20
-type Product struct {
- ctx build.Context
- config build.Config
- logFile string
-}
-
-type Status struct {
- cur int
- total int
- failed int
-
- ctx build.Context
- haveBlankLine bool
- smartTerminal bool
-
- lock sync.Mutex
-}
-
-func NewStatus(ctx build.Context) *Status {
- return &Status{
- ctx: ctx,
- haveBlankLine: true,
- smartTerminal: ctx.IsTerminal(),
- }
-}
-
-func (s *Status) SetTotal(total int) {
- s.total = total
-}
-
-func (s *Status) Fail(product string, err error, logFile string) {
- s.Finish(product)
-
- s.lock.Lock()
- defer s.lock.Unlock()
-
- if s.smartTerminal && !s.haveBlankLine {
- fmt.Fprintln(s.ctx.Stdout())
- s.haveBlankLine = true
+func errMsgFromLog(filename string) string {
+ if filename == "" {
+ return ""
}
- s.failed++
- fmt.Fprintln(s.ctx.Stderr(), "FAILED:", product)
- s.ctx.Verboseln("FAILED:", product)
-
- if logFile != "" {
- data, err := ioutil.ReadFile(logFile)
- if err == nil {
- lines := strings.Split(strings.TrimSpace(string(data)), "\n")
- if len(lines) > errorLeadingLines+errorTrailingLines+1 {
- lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
- len(lines)-errorLeadingLines-errorTrailingLines)
-
- lines = append(lines[:errorLeadingLines+1],
- lines[len(lines)-errorTrailingLines:]...)
- }
- for _, line := range lines {
- fmt.Fprintln(s.ctx.Stderr(), "> ", line)
- s.ctx.Verboseln(line)
- }
- }
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return ""
}
- s.ctx.Print(err)
-}
+ lines := strings.Split(strings.TrimSpace(string(data)), "\n")
+ if len(lines) > errorLeadingLines+errorTrailingLines+1 {
+ lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
+ len(lines)-errorLeadingLines-errorTrailingLines)
-func (s *Status) Finish(product string) {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- s.cur++
- line := fmt.Sprintf("[%d/%d] %s", s.cur, s.total, product)
-
- if s.smartTerminal {
- if max, ok := s.ctx.TermWidth(); ok {
- if len(line) > max {
- line = line[:max]
- }
- }
-
- fmt.Fprint(s.ctx.Stdout(), "\r", line, "\x1b[K")
- s.haveBlankLine = false
- } else {
- s.ctx.Println(line)
+ lines = append(lines[:errorLeadingLines+1],
+ lines[len(lines)-errorTrailingLines:]...)
}
-}
-
-func (s *Status) Finished() int {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- if !s.haveBlankLine {
- fmt.Fprintln(s.ctx.Stdout())
- s.haveBlankLine = true
+ var buf strings.Builder
+ for _, line := range lines {
+ buf.WriteString("> ")
+ buf.WriteString(line)
+ buf.WriteString("\n")
}
- return s.failed
+ return buf.String()
}
// TODO(b/70370883): This tool uses a lot of open files -- over the default
@@ -193,8 +127,39 @@
return false
}
+func copyFile(from, to string) error {
+ fromFile, err := os.Open(from)
+ if err != nil {
+ return err
+ }
+ defer fromFile.Close()
+
+ toFile, err := os.Create(to)
+ if err != nil {
+ return err
+ }
+ defer toFile.Close()
+
+ _, err = io.Copy(toFile, fromFile)
+ return err
+}
+
+type mpContext struct {
+ Context context.Context
+ Logger logger.Logger
+ Status status.ToolStatus
+ Tracer tracer.Tracer
+ Finder *finder.Finder
+ Config build.Config
+
+ LogsDir string
+}
+
func main() {
- log := logger.New(os.Stderr)
+ writer := terminal.NewWriter(terminal.StdioImpl{})
+ defer writer.Finish()
+
+ log := logger.New(writer)
defer log.Cleanup()
flag.Parse()
@@ -205,23 +170,34 @@
trace := tracer.New(log)
defer trace.Close()
+ stat := &status.Status{}
+ defer stat.Finish()
+ stat.AddOutput(terminal.NewStatusOutput(writer, "",
+ build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
+
+ var failures failureCount
+ stat.AddOutput(&failures)
+
build.SetupSignals(log, cancel, func() {
trace.Close()
log.Cleanup()
+ stat.Finish()
})
- buildCtx := build.Context{&build.ContextImpl{
- Context: ctx,
- Logger: log,
- Tracer: trace,
- StdioInterface: build.StdioImpl{},
+ buildCtx := build.Context{ContextImpl: &build.ContextImpl{
+ Context: ctx,
+ Logger: log,
+ Tracer: trace,
+ Writer: writer,
+ Status: stat,
}}
- status := NewStatus(buildCtx)
-
config := build.NewConfig(buildCtx)
if *outDir == "" {
- name := "multiproduct-" + time.Now().Format("20060102150405")
+ name := "multiproduct"
+ if !*incremental {
+ name += "-" + time.Now().Format("20060102150405")
+ }
*outDir = filepath.Join(config.OutDir(), name)
@@ -255,6 +231,11 @@
setMaxFiles(log)
+ finder := build.NewSourceFinder(buildCtx, config)
+ defer finder.Shutdown()
+
+ build.FindSources(buildCtx, config, finder)
+
vars, err := build.DumpMakeVars(buildCtx, config, nil, []string{"all_named_products"})
if err != nil {
log.Fatal(err)
@@ -278,7 +259,7 @@
productsList = allProducts
}
- products := make([]string, 0, len(productsList))
+ finalProductsList := make([]string, 0, len(productsList))
skipList := strings.Split(*skipProducts, ",")
skipProduct := func(p string) bool {
for _, s := range skipList {
@@ -290,120 +271,54 @@
}
for _, product := range productsList {
if !skipProduct(product) {
- products = append(products, product)
+ finalProductsList = append(finalProductsList, product)
} else {
log.Verbose("Skipping: ", product)
}
}
- log.Verbose("Got product list: ", products)
+ log.Verbose("Got product list: ", finalProductsList)
- status.SetTotal(len(products))
+ s := buildCtx.Status.StartTool()
+ s.SetTotalActions(len(finalProductsList))
- var wg sync.WaitGroup
- productConfigs := make(chan Product, len(products))
+ mpCtx := &mpContext{
+ Context: ctx,
+ Logger: log,
+ Status: s,
+ Tracer: trace,
- finder := build.NewSourceFinder(buildCtx, config)
- defer finder.Shutdown()
+ Finder: finder,
+ Config: config,
- // Run the product config for every product in parallel
- for _, product := range products {
- wg.Add(1)
- go func(product string) {
- var stdLog string
-
- defer wg.Done()
- defer logger.Recover(func(err error) {
- status.Fail(product, err, stdLog)
- })
-
- productOutDir := filepath.Join(config.OutDir(), product)
- productLogDir := filepath.Join(logsDir, product)
-
- if err := os.MkdirAll(productOutDir, 0777); err != nil {
- log.Fatalf("Error creating out directory: %v", err)
- }
- if err := os.MkdirAll(productLogDir, 0777); err != nil {
- log.Fatalf("Error creating log directory: %v", err)
- }
-
- stdLog = filepath.Join(productLogDir, "std.log")
- f, err := os.Create(stdLog)
- if err != nil {
- log.Fatalf("Error creating std.log: %v", err)
- }
-
- productLog := logger.New(f)
- productLog.SetOutput(filepath.Join(productLogDir, "soong.log"))
-
- productCtx := build.Context{&build.ContextImpl{
- Context: ctx,
- Logger: productLog,
- Tracer: trace,
- StdioInterface: build.NewCustomStdio(nil, f, f),
- Thread: trace.NewThread(product),
- }}
-
- productConfig := build.NewConfig(productCtx)
- productConfig.Environment().Set("OUT_DIR", productOutDir)
- build.FindSources(productCtx, productConfig, finder)
- productConfig.Lunch(productCtx, product, *buildVariant)
-
- build.Build(productCtx, productConfig, build.BuildProductConfig)
- productConfigs <- Product{productCtx, productConfig, stdLog}
- }(product)
+ LogsDir: logsDir,
}
+
+ products := make(chan string, len(productsList))
go func() {
- defer close(productConfigs)
- wg.Wait()
+ defer close(products)
+ for _, product := range finalProductsList {
+ products <- product
+ }
}()
- var wg2 sync.WaitGroup
- // Then run up to numJobs worth of Soong and Kati
+ var wg sync.WaitGroup
for i := 0; i < *numJobs; i++ {
- wg2.Add(1)
+ wg.Add(1)
go func() {
- defer wg2.Done()
- for product := range productConfigs {
- func() {
- defer logger.Recover(func(err error) {
- status.Fail(product.config.TargetProduct(), err, product.logFile)
- })
-
- defer func() {
- if *keepArtifacts {
- args := zip.ZipArgs{
- FileArgs: []zip.FileArg{
- {
- GlobDir: product.config.OutDir(),
- SourcePrefixToStrip: product.config.OutDir(),
- },
- },
- OutputFilePath: filepath.Join(config.OutDir(), product.config.TargetProduct()+".zip"),
- NumParallelJobs: runtime.NumCPU(),
- CompressionLevel: 5,
- }
- if err := zip.Run(args); err != nil {
- log.Fatalf("Error zipping artifacts: %v", err)
- }
- }
- os.RemoveAll(product.config.OutDir())
- }()
-
- buildWhat := 0
- if !*onlyConfig {
- buildWhat |= build.BuildSoong
- if !*onlySoong {
- buildWhat |= build.BuildKati
- }
+ defer wg.Done()
+ for {
+ select {
+ case product := <-products:
+ if product == "" {
+ return
}
- build.Build(product.ctx, product.config, buildWhat)
- status.Finish(product.config.TargetProduct())
- }()
+ buildProduct(mpCtx, product)
+ }
}
}()
}
- wg2.Wait()
+ wg.Wait()
if *alternateResultDir {
args := zip.ZipArgs{
@@ -414,12 +329,140 @@
NumParallelJobs: runtime.NumCPU(),
CompressionLevel: 5,
}
- if err := zip.Run(args); err != nil {
+ if err := zip.Zip(args); err != nil {
log.Fatalf("Error zipping logs: %v", err)
}
}
- if count := status.Finished(); count > 0 {
- log.Fatalln(count, "products failed")
+ s.Finish()
+
+ if failures == 1 {
+ log.Fatal("1 failure")
+ } else if failures > 1 {
+ log.Fatalf("%d failures", failures)
+ } else {
+ writer.Print("Success")
}
}
+
+func buildProduct(mpctx *mpContext, product string) {
+ var stdLog string
+
+ outDir := filepath.Join(mpctx.Config.OutDir(), product)
+ logsDir := filepath.Join(mpctx.LogsDir, product)
+
+ if err := os.MkdirAll(outDir, 0777); err != nil {
+ mpctx.Logger.Fatalf("Error creating out directory: %v", err)
+ }
+ if err := os.MkdirAll(logsDir, 0777); err != nil {
+ mpctx.Logger.Fatalf("Error creating log directory: %v", err)
+ }
+
+ stdLog = filepath.Join(logsDir, "std.log")
+ f, err := os.Create(stdLog)
+ if err != nil {
+ mpctx.Logger.Fatalf("Error creating std.log: %v", err)
+ }
+ defer f.Close()
+
+ log := logger.New(f)
+ defer log.Cleanup()
+ log.SetOutput(filepath.Join(logsDir, "soong.log"))
+
+ action := &status.Action{
+ Description: product,
+ Outputs: []string{product},
+ }
+ mpctx.Status.StartAction(action)
+ defer logger.Recover(func(err error) {
+ mpctx.Status.FinishAction(status.ActionResult{
+ Action: action,
+ Error: err,
+ Output: errMsgFromLog(stdLog),
+ })
+ })
+
+ ctx := build.Context{ContextImpl: &build.ContextImpl{
+ Context: mpctx.Context,
+ Logger: log,
+ Tracer: mpctx.Tracer,
+ Writer: terminal.NewWriter(terminal.NewCustomStdio(nil, f, f)),
+ Thread: mpctx.Tracer.NewThread(product),
+ Status: &status.Status{},
+ }}
+ ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "",
+ build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
+
+ config := build.NewConfig(ctx, flag.Args()...)
+ config.Environment().Set("OUT_DIR", outDir)
+ if !*keepArtifacts {
+ config.Environment().Set("EMPTY_NINJA_FILE", "true")
+ }
+ build.FindSources(ctx, config, mpctx.Finder)
+ config.Lunch(ctx, product, *buildVariant)
+
+ defer func() {
+ if *keepArtifacts {
+ args := zip.ZipArgs{
+ FileArgs: []zip.FileArg{
+ {
+ GlobDir: outDir,
+ SourcePrefixToStrip: outDir,
+ },
+ },
+ OutputFilePath: filepath.Join(mpctx.Config.OutDir(), product+".zip"),
+ NumParallelJobs: runtime.NumCPU(),
+ CompressionLevel: 5,
+ }
+ if err := zip.Zip(args); err != nil {
+ log.Fatalf("Error zipping artifacts: %v", err)
+ }
+ }
+ if !*incremental {
+ os.RemoveAll(outDir)
+ }
+ }()
+
+ buildWhat := build.BuildProductConfig
+ if !*onlyConfig {
+ buildWhat |= build.BuildSoong
+ if !*onlySoong {
+ buildWhat |= build.BuildKati
+ }
+ }
+
+ before := time.Now()
+ build.Build(ctx, config, buildWhat)
+
+ // Save std_full.log if Kati re-read the makefiles
+ if buildWhat&build.BuildKati != 0 {
+ if after, err := os.Stat(config.KatiBuildNinjaFile()); err == nil && after.ModTime().After(before) {
+ err := copyFile(stdLog, filepath.Join(filepath.Dir(stdLog), "std_full.log"))
+ if err != nil {
+ log.Fatalf("Error copying log file: %s", err)
+ }
+ }
+ }
+
+ mpctx.Status.FinishAction(status.ActionResult{
+ Action: action,
+ })
+}
+
+type failureCount int
+
+func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
+
+func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
+ if result.Error != nil {
+ *f += 1
+ }
+}
+
+func (f *failureCount) Message(level status.MsgLevel, message string) {
+ if level >= status.ErrorLvl {
+ *f += 1
+ }
+}
+
+func (f *failureCount) Flush() {}
diff --git a/ui/build/util_linux.go b/cmd/path_interposer/Android.bp
similarity index 73%
copy from ui/build/util_linux.go
copy to cmd/path_interposer/Android.bp
index 0a4e1d2..41a219f 100644
--- a/ui/build/util_linux.go
+++ b/cmd/path_interposer/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All rights reserved.
+// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,10 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package build
-
-import (
- "syscall"
-)
-
-const ioctlGetTermios = syscall.TCGETS
+blueprint_go_binary {
+ name: "path_interposer",
+ deps: ["soong-ui-build-paths"],
+ srcs: ["main.go"],
+ testSrcs: ["main_test.go"],
+}
diff --git a/cmd/path_interposer/main.go b/cmd/path_interposer/main.go
new file mode 100644
index 0000000..cd28b96
--- /dev/null
+++ b/cmd/path_interposer/main.go
@@ -0,0 +1,247 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "syscall"
+
+ "android/soong/ui/build/paths"
+)
+
+func main() {
+ interposer, err := os.Executable()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "Unable to locate interposer executable:", err)
+ os.Exit(1)
+ }
+
+ if fi, err := os.Lstat(interposer); err == nil {
+ if fi.Mode()&os.ModeSymlink != 0 {
+ link, err := os.Readlink(interposer)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "Unable to read link to interposer executable:", err)
+ os.Exit(1)
+ }
+ if filepath.IsAbs(link) {
+ interposer = link
+ } else {
+ interposer = filepath.Join(filepath.Dir(interposer), link)
+ }
+ }
+ } else {
+ fmt.Fprintln(os.Stderr, "Unable to stat interposer executable:", err)
+ os.Exit(1)
+ }
+
+ disableError := false
+ if e, ok := os.LookupEnv("TEMPORARY_DISABLE_PATH_RESTRICTIONS"); ok {
+ disableError = e == "1" || e == "y" || e == "yes" || e == "on" || e == "true"
+ }
+
+ exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{
+ disableError: disableError,
+
+ sendLog: paths.SendLog,
+ config: paths.GetConfig,
+ lookupParents: lookupParents,
+ })
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ }
+ os.Exit(exitCode)
+}
+
+var usage = fmt.Errorf(`To use the PATH interposer:
+ * Write the original PATH variable to <interposer>_origpath
+ * Set up a directory of symlinks to the PATH interposer, and use that in PATH
+
+If a tool isn't in the allowed list, a log will be posted to the unix domain
+socket at <interposer>_log.`)
+
+type mainOpts struct {
+ disableError bool
+
+ sendLog func(logSocket string, entry *paths.LogEntry, done chan interface{})
+ config func(name string) paths.PathConfig
+ lookupParents func() []paths.LogProcess
+}
+
+func Main(stdout, stderr io.Writer, interposer string, args []string, opts mainOpts) (int, error) {
+ base := filepath.Base(args[0])
+
+ origPathFile := interposer + "_origpath"
+ if base == filepath.Base(interposer) {
+ return 1, usage
+ }
+
+ origPath, err := ioutil.ReadFile(origPathFile)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return 1, usage
+ } else {
+ return 1, fmt.Errorf("Failed to read original PATH: %v", err)
+ }
+ }
+
+ cmd := &exec.Cmd{
+ Args: args,
+ Env: os.Environ(),
+
+ Stdin: os.Stdin,
+ Stdout: stdout,
+ Stderr: stderr,
+ }
+
+ if err := os.Setenv("PATH", string(origPath)); err != nil {
+ return 1, fmt.Errorf("Failed to set PATH env: %v", err)
+ }
+
+ if config := opts.config(base); config.Log || config.Error {
+ var procs []paths.LogProcess
+ if opts.lookupParents != nil {
+ procs = opts.lookupParents()
+ }
+
+ if opts.sendLog != nil {
+ waitForLog := make(chan interface{})
+ opts.sendLog(interposer+"_log", &paths.LogEntry{
+ Basename: base,
+ Args: args,
+ Parents: procs,
+ }, waitForLog)
+ defer func() { <-waitForLog }()
+ }
+ if config.Error && !opts.disableError {
+ return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.", base)
+ }
+ }
+
+ cmd.Path, err = exec.LookPath(base)
+ if err != nil {
+ return 1, err
+ }
+
+ if err = cmd.Run(); err != nil {
+ if exitErr, ok := err.(*exec.ExitError); ok {
+ if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
+ if status.Exited() {
+ return status.ExitStatus(), nil
+ } else if status.Signaled() {
+ exitCode := 128 + int(status.Signal())
+ return exitCode, nil
+ } else {
+ return 1, exitErr
+ }
+ } else {
+ return 1, nil
+ }
+ }
+ }
+
+ return 0, nil
+}
+
+type procEntry struct {
+ Pid int
+ Ppid int
+ Command string
+}
+
+func readProcs() map[int]procEntry {
+ cmd := exec.Command("ps", "-o", "pid,ppid,command")
+ data, err := cmd.Output()
+ if err != nil {
+ return nil
+ }
+
+ return parseProcs(data)
+}
+
+func parseProcs(data []byte) map[int]procEntry {
+ lines := bytes.Split(data, []byte("\n"))
+ if len(lines) < 2 {
+ return nil
+ }
+ // Remove the header
+ lines = lines[1:]
+
+ ret := make(map[int]procEntry, len(lines))
+ for _, line := range lines {
+ fields := bytes.SplitN(line, []byte(" "), 2)
+ if len(fields) != 2 {
+ continue
+ }
+
+ pid, err := strconv.Atoi(string(fields[0]))
+ if err != nil {
+ continue
+ }
+
+ line = bytes.TrimLeft(fields[1], " ")
+
+ fields = bytes.SplitN(line, []byte(" "), 2)
+ if len(fields) != 2 {
+ continue
+ }
+
+ ppid, err := strconv.Atoi(string(fields[0]))
+ if err != nil {
+ continue
+ }
+
+ ret[pid] = procEntry{
+ Pid: pid,
+ Ppid: ppid,
+ Command: string(bytes.TrimLeft(fields[1], " ")),
+ }
+ }
+
+ return ret
+}
+
+func lookupParents() []paths.LogProcess {
+ procs := readProcs()
+ if procs == nil {
+ return nil
+ }
+
+ list := []paths.LogProcess{}
+ pid := os.Getpid()
+ for {
+ entry, ok := procs[pid]
+ if !ok {
+ break
+ }
+
+ list = append([]paths.LogProcess{
+ {
+ Pid: pid,
+ Command: entry.Command,
+ },
+ }, list...)
+
+ pid = entry.Ppid
+ }
+
+ return list
+}
diff --git a/cmd/path_interposer/main_test.go b/cmd/path_interposer/main_test.go
new file mode 100644
index 0000000..c89d623
--- /dev/null
+++ b/cmd/path_interposer/main_test.go
@@ -0,0 +1,196 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "android/soong/ui/build/paths"
+)
+
+var tmpDir string
+var origPATH string
+
+func TestMain(m *testing.M) {
+ os.Exit(func() int {
+ var err error
+ tmpDir, err = ioutil.TempDir("", "interposer_test")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ origPATH = os.Getenv("PATH")
+ err = os.Setenv("PATH", "")
+ if err != nil {
+ panic(err)
+ }
+
+ return m.Run()
+ }())
+}
+
+func setup(t *testing.T) string {
+ f, err := ioutil.TempFile(tmpDir, "interposer")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ err = ioutil.WriteFile(f.Name()+"_origpath", []byte(origPATH), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f.Name()
+}
+
+func TestInterposer(t *testing.T) {
+ interposer := setup(t)
+
+ logConfig := func(name string) paths.PathConfig {
+ if name == "true" {
+ return paths.PathConfig{
+ Log: false,
+ Error: false,
+ }
+ } else if name == "path_interposer_test_not_allowed" {
+ return paths.PathConfig{
+ Log: false,
+ Error: true,
+ }
+ }
+ return paths.PathConfig{
+ Log: true,
+ Error: false,
+ }
+ }
+
+ testCases := []struct {
+ name string
+ args []string
+
+ exitCode int
+ err error
+ logEntry string
+ }{
+ {
+ name: "direct call",
+ args: []string{interposer},
+
+ exitCode: 1,
+ err: usage,
+ },
+ {
+ name: "relative call",
+ args: []string{filepath.Base(interposer)},
+
+ exitCode: 1,
+ err: usage,
+ },
+ {
+ name: "true",
+ args: []string{"/my/path/true"},
+ },
+ {
+ name: "relative true",
+ args: []string{"true"},
+ },
+ {
+ name: "exit code",
+ args: []string{"bash", "-c", "exit 42"},
+
+ exitCode: 42,
+ logEntry: "bash",
+ },
+ {
+ name: "signal",
+ args: []string{"bash", "-c", "kill -9 $$"},
+
+ exitCode: 137,
+ logEntry: "bash",
+ },
+ {
+ name: "does not exist",
+ args: []string{"path_interposer_test_does_not_exist"},
+
+ exitCode: 1,
+ err: fmt.Errorf(`exec: "path_interposer_test_does_not_exist": executable file not found in $PATH`),
+ logEntry: "path_interposer_test_does_not_exist",
+ },
+ {
+ name: "not allowed",
+ args: []string{"path_interposer_test_not_allowed"},
+
+ exitCode: 1,
+ err: fmt.Errorf(`"path_interposer_test_not_allowed" is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.`),
+ logEntry: "path_interposer_test_not_allowed",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ logged := false
+ logFunc := func(logSocket string, entry *paths.LogEntry, done chan interface{}) {
+ defer close(done)
+
+ logged = true
+ if entry.Basename != testCase.logEntry {
+ t.Errorf("unexpected log entry:\nwant: %q\n got: %q", testCase.logEntry, entry.Basename)
+ }
+ }
+
+ exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, testCase.args, mainOpts{
+ sendLog: logFunc,
+ config: logConfig,
+ })
+
+ errstr := func(err error) string {
+ if err == nil {
+ return ""
+ }
+ return err.Error()
+ }
+ if errstr(testCase.err) != errstr(err) {
+ t.Errorf("unexpected error:\nwant: %v\n got: %v", testCase.err, err)
+ }
+ if testCase.exitCode != exitCode {
+ t.Errorf("expected exit code %d, got %d", testCase.exitCode, exitCode)
+ }
+ if !logged && testCase.logEntry != "" {
+ t.Errorf("no log entry, but expected %q", testCase.logEntry)
+ }
+ })
+ }
+}
+
+func TestMissingPath(t *testing.T) {
+ interposer := setup(t)
+ err := os.Remove(interposer + "_origpath")
+ if err != nil {
+ t.Fatal("Failed to remove:", err)
+ }
+
+ exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, []string{"true"}, mainOpts{})
+ if err != usage {
+ t.Errorf("Unexpected error:\n got: %v\nwant: %v", err, usage)
+ }
+ if exitCode != 1 {
+ t.Errorf("expected exit code %d, got %d", 1, exitCode)
+ }
+}
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index 078a07d..a399b28 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -15,6 +15,7 @@
package main
import (
+ "archive/zip"
"bufio"
"bytes"
"encoding/xml"
@@ -103,6 +104,24 @@
var excludes = make(Exclude)
+type HostModuleNames map[string]bool
+
+func (n HostModuleNames) IsHostModule(groupId string, artifactId string) bool {
+ _, found := n[groupId+":"+artifactId]
+ return found
+}
+
+func (n HostModuleNames) String() string {
+ return ""
+}
+
+func (n HostModuleNames) Set(v string) error {
+ n[v] = true
+ return nil
+}
+
+var hostModuleNames = HostModuleNames{}
+
var sdkVersion string
var useVersion string
@@ -138,9 +157,10 @@
type Pom struct {
XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"`
- PomFile string `xml:"-"`
- ArtifactFile string `xml:"-"`
- BpTarget string `xml:"-"`
+ PomFile string `xml:"-"`
+ ArtifactFile string `xml:"-"`
+ BpTarget string `xml:"-"`
+ MinSdkVersion string `xml:"-"`
GroupId string `xml:"groupId"`
ArtifactId string `xml:"artifactId"`
@@ -158,6 +178,42 @@
return p.Packaging == "jar"
}
+func (p Pom) IsHostModule() bool {
+ return hostModuleNames.IsHostModule(p.GroupId, p.ArtifactId)
+}
+
+func (p Pom) IsDeviceModule() bool {
+ return !p.IsHostModule()
+}
+
+func (p Pom) ModuleType() string {
+ if p.IsAar() {
+ return "android_library"
+ } else if p.IsHostModule() {
+ return "java_library_host"
+ } else {
+ return "java_library_static"
+ }
+}
+
+func (p Pom) ImportModuleType() string {
+ if p.IsAar() {
+ return "android_library_import"
+ } else if p.IsHostModule() {
+ return "java_import_host"
+ } else {
+ return "java_import"
+ }
+}
+
+func (p Pom) ImportProperty() string {
+ if p.IsAar() {
+ return "aars"
+ } else {
+ return "jars"
+ }
+}
+
func (p Pom) BpName() string {
if p.BpTarget == "" {
p.BpTarget = rewriteNames.MavenToBp(p.GroupId, p.ArtifactId)
@@ -215,26 +271,93 @@
}
}
-var bpTemplate = template.Must(template.New("bp").Parse(`
-{{if .IsAar}}android_library_import{{else}}java_import{{end}} {
- name: "{{.BpName}}-nodeps",
- {{if .IsAar}}aars{{else}}jars{{end}}: ["{{.ArtifactFile}}"],
- sdk_version: "{{.SdkVersion}}",{{if .IsAar}}
- static_libs: [{{range .BpAarDeps}}
- "{{.}}",{{end}}{{range .BpExtraDeps}}
- "{{.}}",{{end}}
- ],{{end}}
+// ExtractMinSdkVersion extracts the minSdkVersion from the AndroidManifest.xml file inside an aar file, or sets it
+// to "current" if it is not present.
+func (p *Pom) ExtractMinSdkVersion() error {
+ aar, err := zip.OpenReader(p.ArtifactFile)
+ if err != nil {
+ return err
+ }
+ defer aar.Close()
+
+ var manifest *zip.File
+ for _, f := range aar.File {
+ if f.Name == "AndroidManifest.xml" {
+ manifest = f
+ break
+ }
+ }
+
+ if manifest == nil {
+ return fmt.Errorf("failed to find AndroidManifest.xml in %s", p.ArtifactFile)
+ }
+
+ r, err := manifest.Open()
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+
+ decoder := xml.NewDecoder(r)
+
+ manifestData := struct {
+ XMLName xml.Name `xml:"manifest"`
+ Uses_sdk struct {
+ MinSdkVersion string `xml:"http://schemas.android.com/apk/res/android minSdkVersion,attr"`
+ } `xml:"uses-sdk"`
+ }{}
+
+ err = decoder.Decode(&manifestData)
+ if err != nil {
+ return err
+ }
+
+ p.MinSdkVersion = manifestData.Uses_sdk.MinSdkVersion
+ if p.MinSdkVersion == "" {
+ p.MinSdkVersion = "current"
+ }
+
+ return nil
}
-{{if .IsAar}}android_library{{else}}java_library_static{{end}} {
- name: "{{.BpName}}",
- sdk_version: "{{.SdkVersion}}",{{if .IsAar}}
- manifest: "manifests/{{.BpName}}/AndroidManifest.xml",{{end}}
+var bpTemplate = template.Must(template.New("bp").Parse(`
+{{.ImportModuleType}} {
+ name: "{{.BpName}}-nodeps",
+ {{.ImportProperty}}: ["{{.ArtifactFile}}"],
+ sdk_version: "{{.SdkVersion}}",
+ {{- if .IsAar}}
+ min_sdk_version: "{{.MinSdkVersion}}",
static_libs: [
- "{{.BpName}}-nodeps",{{range .BpJarDeps}}
- "{{.}}",{{end}}{{range .BpAarDeps}}
- "{{.}}",{{end}}{{range .BpExtraDeps}}
- "{{.}}",{{end}}
+ {{- range .BpAarDeps}}
+ "{{.}}",
+ {{- end}}
+ {{- range .BpExtraDeps}}
+ "{{.}}",
+ {{- end}}
+ ],
+ {{- end}}
+}
+
+{{.ModuleType}} {
+ name: "{{.BpName}}",
+ {{- if .IsDeviceModule}}
+ sdk_version: "{{.SdkVersion}}",
+ {{- if .IsAar}}
+ min_sdk_version: "{{.MinSdkVersion}}",
+ manifest: "manifests/{{.BpName}}/AndroidManifest.xml",
+ {{- end}}
+ {{- end}}
+ static_libs: [
+ "{{.BpName}}-nodeps",
+ {{- range .BpJarDeps}}
+ "{{.}}",
+ {{- end}}
+ {{- range .BpAarDeps}}
+ "{{.}}",
+ {{- end}}
+ {{- range .BpExtraDeps}}
+ "{{.}}",
+ {{- end}}
],
java_version: "1.7",
}
@@ -302,7 +425,7 @@
// Append all current command line args except -regen <file> to the ones from the file
for i := 1; i < len(os.Args); i++ {
- if os.Args[i] == "-regen" {
+ if os.Args[i] == "-regen" || os.Args[i] == "--regen" {
i++
} else {
args = append(args, os.Args[i])
@@ -369,6 +492,7 @@
flag.Var(&excludes, "exclude", "Exclude module")
flag.Var(&extraDeps, "extra-deps", "Extra dependencies needed when depending on a module")
flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
+ flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module")
flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to LOCAL_SDK_VERSION")
flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
flag.Bool("static-deps", false, "Ignored")
@@ -468,13 +592,20 @@
}
for _, pom := range poms {
+ if pom.IsAar() {
+ err := pom.ExtractMinSdkVersion()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error reading manifest for %s: %s", pom.ArtifactFile, err)
+ os.Exit(1)
+ }
+ }
pom.FixDeps(modules)
}
buf := &bytes.Buffer{}
fmt.Fprintln(buf, "// Automatically generated with:")
- fmt.Fprintln(buf, "// pom2bp", strings.Join(proptools.ShellEscape(os.Args[1:]), " "))
+ fmt.Fprintln(buf, "// pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
for _, pom := range poms {
var err error
diff --git a/cmd/pom2mk/pom2mk.go b/cmd/pom2mk/pom2mk.go
index fc83641..94e5619 100644
--- a/cmd/pom2mk/pom2mk.go
+++ b/cmd/pom2mk/pom2mk.go
@@ -483,7 +483,7 @@
}
fmt.Println("# Automatically generated with:")
- fmt.Println("# pom2mk", strings.Join(proptools.ShellEscape(os.Args[1:]), " "))
+ fmt.Println("# pom2mk", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
fmt.Println("LOCAL_PATH := $(call my-dir)")
for _, pom := range poms {
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 0af1886..4167edb 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -24,6 +24,7 @@
"path"
"path/filepath"
"strings"
+ "time"
)
var (
@@ -265,6 +266,15 @@
if err != nil {
return err
}
+
+ // Update the timestamp of the output file in case the tool wrote an old timestamp (for example, tar can extract
+ // files with old timestamps).
+ now := time.Now()
+ err = os.Chtimes(tempPath, now, now)
+ if err != nil {
+ return err
+ }
+
err = os.Rename(tempPath, destPath)
if err != nil {
return err
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index a4c6898..41c7d46 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -75,6 +75,9 @@
bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName)
if docFile != "" {
- writeDocs(ctx, docFile)
+ if err := writeDocs(ctx, docFile); err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
}
}
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index a6686c0..9424b6c 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -19,111 +19,399 @@
"bytes"
"html/template"
"io/ioutil"
+ "path/filepath"
+ "reflect"
+ "sort"
"github.com/google/blueprint/bootstrap"
+ "github.com/google/blueprint/bootstrap/bpdoc"
)
-func writeDocs(ctx *android.Context, filename string) error {
- moduleTypeList, err := bootstrap.ModuleTypeDocs(ctx.Context)
- if err != nil {
- return err
- }
-
- buf := &bytes.Buffer{}
-
- unique := 0
-
- tmpl, err := template.New("file").Funcs(map[string]interface{}{
- "unique": func() int {
- unique++
- return unique
- }}).Parse(fileTemplate)
- if err != nil {
- return err
- }
-
- err = tmpl.Execute(buf, moduleTypeList)
- if err != nil {
- return err
- }
-
- err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
- if err != nil {
- return err
- }
-
- return nil
+type perPackageTemplateData struct {
+ Name string
+ Modules []moduleTypeTemplateData
}
+type moduleTypeTemplateData struct {
+ Name string
+ Synopsis template.HTML
+ Properties []bpdoc.Property
+}
+
+// The properties in this map are displayed first, according to their rank.
+// TODO(jungjw): consider providing module type-dependent ranking
+var propertyRank = map[string]int{
+ "name": 0,
+ "src": 1,
+ "srcs": 2,
+ "defautls": 3,
+ "host_supported": 4,
+ "device_supported": 5,
+}
+
+// For each module type, extract its documentation and convert it to the template data.
+func moduleTypeDocsToTemplates(moduleTypeList []*bpdoc.ModuleType) []moduleTypeTemplateData {
+ result := make([]moduleTypeTemplateData, 0)
+
+ // Combine properties from all PropertyStruct's and reorder them -- first the ones
+ // with rank, then the rest of the properties in alphabetic order.
+ for _, m := range moduleTypeList {
+ item := moduleTypeTemplateData{
+ Name: m.Name,
+ Synopsis: m.Text,
+ Properties: make([]bpdoc.Property, 0),
+ }
+ props := make([]bpdoc.Property, 0)
+ for _, propStruct := range m.PropertyStructs {
+ props = append(props, propStruct.Properties...)
+ }
+ sort.Slice(props, func(i, j int) bool {
+ if rankI, ok := propertyRank[props[i].Name]; ok {
+ if rankJ, ok := propertyRank[props[j].Name]; ok {
+ return rankI < rankJ
+ } else {
+ return true
+ }
+ }
+ if _, ok := propertyRank[props[j].Name]; ok {
+ return false
+ }
+ return props[i].Name < props[j].Name
+ })
+ // Eliminate top-level duplicates. TODO(jungjw): improve bpdoc to handle this.
+ previousPropertyName := ""
+ for _, prop := range props {
+ if prop.Name == previousPropertyName {
+ oldProp := &item.Properties[len(item.Properties)-1].Properties
+ bpdoc.CollapseDuplicateProperties(oldProp, &prop.Properties)
+ } else {
+ item.Properties = append(item.Properties, prop)
+ }
+ previousPropertyName = prop.Name
+ }
+ result = append(result, item)
+ }
+ sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name })
+ return result
+}
+
+func writeDocs(ctx *android.Context, filename string) error {
+ moduleTypeFactories := android.ModuleTypeFactories()
+ bpModuleTypeFactories := make(map[string]reflect.Value)
+ for moduleType, factory := range moduleTypeFactories {
+ bpModuleTypeFactories[moduleType] = reflect.ValueOf(factory)
+ }
+
+ packages, err := bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
+ if err != nil {
+ return err
+ }
+
+ // Produce the top-level, package list page first.
+ tmpl := template.Must(template.Must(template.New("file").Parse(packageListTemplate)).Parse(copyBaseUrl))
+ buf := &bytes.Buffer{}
+ err = tmpl.Execute(buf, packages)
+ if err == nil {
+ err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
+ }
+
+ // Now, produce per-package module lists with detailed information.
+ for _, pkg := range packages {
+ // We need a module name getter/setter function because I couldn't
+ // find a way to keep it in a variable defined within the template.
+ currentModuleName := ""
+ tmpl := template.Must(
+ template.Must(template.New("file").Funcs(map[string]interface{}{
+ "setModule": func(moduleName string) string {
+ currentModuleName = moduleName
+ return ""
+ },
+ "getModule": func() string {
+ return currentModuleName
+ },
+ }).Parse(perPackageTemplate)).Parse(copyBaseUrl))
+ buf := &bytes.Buffer{}
+ modules := moduleTypeDocsToTemplates(pkg.ModuleTypes)
+ data := perPackageTemplateData{Name: pkg.Name, Modules: modules}
+ err = tmpl.Execute(buf, data)
+ if err != nil {
+ return err
+ }
+ pkgFileName := filepath.Join(filepath.Dir(filename), pkg.Name+".html")
+ err = ioutil.WriteFile(pkgFileName, buf.Bytes(), 0666)
+ if err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// TODO(jungjw): Consider ordering by name.
const (
- fileTemplate = `
+ packageListTemplate = `
<html>
<head>
<title>Build Docs</title>
-<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
-<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+<style>
+#main {
+ padding: 48px;
+}
+
+table{
+ table-layout: fixed;
+}
+
+td {
+ word-wrap:break-word;
+}
+
+/* The following entries are copied from source.android.com's css file. */
+td,td code {
+ color: #202124
+}
+
+th,th code {
+ color: #fff;
+ font: 500 16px/24px Roboto,sans-serif
+}
+
+td,table.responsive tr:not(.alt) td td:first-child,table.responsive td tr:not(.alt) td:first-child {
+ background: rgba(255,255,255,.95);
+ vertical-align: top
+}
+
+td,td code {
+ padding: 7px 8px 8px
+}
+
+tr {
+ border: 0;
+ background: #78909c;
+ border-top: 1px solid #cfd8dc
+}
+
+th,td {
+ border: 0;
+ margin: 0;
+ text-align: left
+}
+
+th {
+ height: 48px;
+ padding: 8px;
+ vertical-align: middle
+}
+
+table {
+ border: 0;
+ border-collapse: collapse;
+ border-spacing: 0;
+ font: 14px/20px Roboto,sans-serif;
+ margin: 16px 0;
+ width: 100%
+}
+
+h1 {
+ color: #80868b;
+ font: 300 34px/40px Roboto,sans-serif;
+ letter-spacing: -0.01em;
+ margin: 40px 0 20px
+}
+
+h1,h2,h3,h4,h5,h6 {
+ overflow: hidden;
+ padding: 0;
+ text-overflow: ellipsis
+}
+
+:link,:visited {
+ color: #039be5;
+ outline: 0;
+ text-decoration: none
+}
+
+body,html {
+ color: #202124;
+ font: 400 16px/24px Roboto,sans-serif;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ height: 100%;
+ margin: 0;
+ -webkit-text-size-adjust: 100%;
+ -moz-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ text-size-adjust: 100%
+}
+
+html {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box
+}
+
+*,*::before,*::after {
+ -webkit-box-sizing: inherit;
+ box-sizing: inherit
+}
+
+body,div,dl,dd,form,img,input,figure,menu {
+ margin: 0;
+ padding: 0
+}
+</style>
+{{template "copyBaseUrl"}}
</head>
<body>
-<h1>Build Docs</h1>
-<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
- {{range .}}
- {{ $collapseIndex := unique }}
- <div class="panel panel-default">
- <div class="panel-heading" role="tab" id="heading{{$collapseIndex}}">
- <h2 class="panel-title">
- <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}">
- {{.Name}}
- </a>
- </h2>
- </div>
- </div>
- <div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}">
- <div class="panel-body">
- <p>{{.Text}}</p>
- {{range .PropertyStructs}}
- <p>{{.Text}}</p>
- {{template "properties" .Properties}}
- {{end}}
- </div>
- </div>
- {{end}}
+<div id="main">
+<H1>Soong Modules Reference</H1>
+The latest versions of Android use the Soong build system, which greatly simplifies build
+configuration over the previous Make-based system. This site contains the generated reference
+files for the Soong build system.
+
+<table class="module_types" summary="Table of Soong module types sorted by package">
+ <thead>
+ <tr>
+ <th style="width:20%">Package</th>
+ <th style="width:80%">Module types</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range $pkg := .}}
+ <tr>
+ <td>{{.Path}}</td>
+ <td>
+ {{range $i, $mod := .ModuleTypes}}{{if $i}}, {{end}}<a href="{{$pkg.Name}}.html#{{$mod.Name}}">{{$mod.Name}}</a>{{end}}
+ </td>
+ </tr>
+ {{end}}
+ </tbody>
+</table>
</div>
</body>
</html>
+`
-{{define "properties"}}
- <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
- {{range .}}
- {{$collapseIndex := unique}}
- {{if .Properties}}
- <div class="panel panel-default">
- <div class="panel-heading" role="tab" id="heading{{$collapseIndex}}">
- <h4 class="panel-title">
- <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{$collapseIndex}}" aria-expanded="false" aria-controls="collapse{{$collapseIndex}}">
- {{.Name}}{{range .OtherNames}}, {{.}}{{end}}
- </a>
- </h4>
- </div>
- </div>
- <div id="collapse{{$collapseIndex}}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{{$collapseIndex}}">
- <div class="panel-body">
- <p>{{.Text}}</p>
- {{range .OtherTexts}}<p>{{.}}</p>{{end}}
- {{template "properties" .Properties}}
- </div>
- </div>
- {{else}}
- <div>
- <h4>{{.Name}}{{range .OtherNames}}, {{.}}{{end}}</h4>
- <p>{{.Text}}</p>
- {{range .OtherTexts}}<p>{{.}}</p>{{end}}
- <p><i>Type: {{.Type}}</i></p>
- {{if .Default}}<p><i>Default: {{.Default}}</i></p>{{end}}
- </div>
- {{end}}
- {{end}}
+ perPackageTemplate = `
+<html>
+<head>
+<title>Build Docs</title>
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
+<style>
+.accordion,.simple{margin-left:1.5em;text-indent:-1.5em;margin-top:.25em}
+.collapsible{border-width:0 0 0 1;margin-left:.25em;padding-left:.25em;border-style:solid;
+ border-color:grey;display:none;}
+span.fixed{display: block; float: left; clear: left; width: 1em;}
+ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ width: 30ch;
+ background-color: #f1f1f1;
+ position: fixed;
+ height: 100%;
+ overflow: auto;
+}
+li a {
+ display: block;
+ color: #000;
+ padding: 8px 16px;
+ text-decoration: none;
+}
+
+li a.active {
+ background-color: #4CAF50;
+ color: white;
+}
+
+li a:hover:not(.active) {
+ background-color: #555;
+ color: white;
+}
+</style>
+{{template "copyBaseUrl"}}
+</head>
+<body>
+{{- /* Fixed sidebar with module types */ -}}
+<ul>
+<li><h3>{{.Name}} package</h3></li>
+{{range $moduleType := .Modules}}<li><a href="{{$.Name}}.html#{{$moduleType.Name}}">{{$moduleType.Name}}</a></li>
+{{end -}}
+</ul>
+{{/* Main panel with H1 section per module type */}}
+<div style="margin-left:30ch;padding:1px 16px;">
+{{range $moduleType := .Modules}}
+ {{setModule $moduleType.Name}}
+ <p>
+ <h2 id="{{$moduleType.Name}}">{{$moduleType.Name}}</h2>
+ {{if $moduleType.Synopsis }}{{$moduleType.Synopsis}}{{else}}<i>Missing synopsis</i>{{end}}
+ {{- /* Comma-separated list of module attributes' links module attributes */ -}}
+ <div class="breadcrumb">
+ {{range $i,$prop := $moduleType.Properties }}
+ {{ if gt $i 0 }}, {{end -}}
+ <a href={{$.Name}}.html#{{getModule}}.{{$prop.Name}}>{{$prop.Name}}</a>
+ {{- end -}}
</div>
+ {{- /* Property description */ -}}
+ {{- template "properties" $moduleType.Properties -}}
+{{- end -}}
+
+{{define "properties" -}}
+ {{range .}}
+ {{if .Properties -}}
+ <div class="accordion" id="{{getModule}}.{{.Name}}">
+ <span class="fixed">⊕</span><b>{{.Name}}</b>
+ {{- range .OtherNames -}}, {{.}}{{- end -}}
+ </div>
+ <div class="collapsible">
+ {{- .Text}} {{range .OtherTexts}}{{.}}{{end}}
+ {{template "properties" .Properties -}}
+ </div>
+ {{- else -}}
+ <div class="simple" id="{{getModule}}.{{.Name}}">
+ <span class="fixed"> </span><b>{{.Name}} {{range .OtherNames}}, {{.}}{{end -}}</b>
+ <i>{{.Type}}</i>
+ {{- if .Text -}}{{if ne .Text "\n"}}, {{end}}{{.Text}}{{- end -}}
+ {{- with .OtherTexts -}}{{.}}{{- end -}}
+ {{- if .Default -}}<i>Default: {{.Default}}</i>{{- end -}}
+ </div>
+ {{- end}}
+ {{- end -}}
+{{- end -}}
+</div>
+<script>
+ accordions = document.getElementsByClassName('accordion');
+ for (i=0; i < accordions.length; ++i) {
+ accordions[i].addEventListener("click", function() {
+ var panel = this.nextElementSibling;
+ var child = this.firstElementChild;
+ if (panel.style.display === "block") {
+ panel.style.display = "none";
+ child.textContent = '\u2295';
+ } else {
+ panel.style.display = "block";
+ child.textContent = '\u2296';
+ }
+ });
+ }
+</script>
+</body>
+`
+
+ copyBaseUrl = `
+{{define "copyBaseUrl"}}
+<script type="text/javascript">
+window.addEventListener('message', (e) => {
+ if (e != null && e.data != null && e.data.type === "SET_BASE" && e.data.base != null) {
+ const existingBase = document.querySelector('base');
+ if (existingBase != null) {
+ existingBase.parentElement.removeChild(existingBase);
+ }
+
+ const base = document.createElement('base');
+ base.setAttribute('href', e.data.base);
+ document.head.appendChild(base);
+ }
+});
+</script>
{{end}}
`
)
diff --git a/cmd/soong_env/soong_env.go b/cmd/soong_env/soong_env.go
index 933e525..d305d83 100644
--- a/cmd/soong_env/soong_env.go
+++ b/cmd/soong_env/soong_env.go
@@ -15,7 +15,7 @@
// soong_glob is the command line tool that checks if the list of files matching a glob has
// changed, and only updates the output file list if it has changed. It is used to optimize
// out build.ninja regenerations when non-matching files are added. See
-// android/soong/common/glob.go for a longer description.
+// android/soong/android/glob.go for a longer description.
package main
import (
diff --git a/cmd/soong_ui/Android.bp b/cmd/soong_ui/Android.bp
index f09e42e..4e57bef 100644
--- a/cmd/soong_ui/Android.bp
+++ b/cmd/soong_ui/Android.bp
@@ -17,6 +17,7 @@
deps: [
"soong-ui-build",
"soong-ui-logger",
+ "soong-ui-terminal",
"soong-ui-tracer",
],
srcs: [
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 2ca7ebf..345a821 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -26,6 +26,9 @@
"android/soong/ui/build"
"android/soong/ui/logger"
+ "android/soong/ui/metrics"
+ "android/soong/ui/status"
+ "android/soong/ui/terminal"
"android/soong/ui/tracer"
)
@@ -44,7 +47,26 @@
}
func main() {
- log := logger.New(os.Stderr)
+ buildStarted := time.Now()
+ var stdio terminal.StdioInterface
+ stdio = terminal.StdioImpl{}
+ simpleOutput := false
+ logsPrefix := ""
+
+ // dumpvar uses stdout, everything else should be in stderr
+ if os.Args[1] == "--dumpvar-mode" || os.Args[1] == "--dumpvars-mode" {
+ // Any metrics files add the prefix to distinguish the type of metrics being
+ // collected to further aggregate the metrics. For dump-var mode, it is usually
+ // related to the execution of lunch command.
+ logsPrefix = "dumpvars-"
+ simpleOutput = true
+ stdio = terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
+ }
+
+ writer := terminal.NewWriter(stdio)
+ defer writer.Finish()
+
+ log := logger.New(writer)
defer log.Cleanup()
if len(os.Args) < 2 || !(inList("--make-mode", os.Args) ||
@@ -60,16 +82,29 @@
trace := tracer.New(log)
defer trace.Close()
+ met := metrics.New()
+ met.SetBuildDateTime(buildStarted)
+ met.SetBuildCommand(os.Args)
+
+ stat := &status.Status{}
+ defer stat.Finish()
+ stat.AddOutput(terminal.NewStatusOutput(writer, os.Getenv("NINJA_STATUS"),
+ build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
+ stat.AddOutput(trace.StatusTracer())
+
build.SetupSignals(log, cancel, func() {
trace.Close()
log.Cleanup()
+ stat.Finish()
})
- buildCtx := build.Context{&build.ContextImpl{
- Context: ctx,
- Logger: log,
- Tracer: trace,
- StdioInterface: build.StdioImpl{},
+ buildCtx := build.Context{ContextImpl: &build.ContextImpl{
+ Context: ctx,
+ Logger: log,
+ Metrics: met,
+ Tracer: trace,
+ Writer: writer,
+ Status: stat,
}}
var config build.Config
if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {
@@ -78,25 +113,34 @@
config = build.NewConfig(buildCtx, os.Args[1:]...)
}
- log.SetVerbose(config.IsVerbose())
build.SetupOutDir(buildCtx, config)
+ logsDir := config.OutDir()
if config.Dist() {
- logsDir := filepath.Join(config.DistDir(), "logs")
- os.MkdirAll(logsDir, 0777)
- log.SetOutput(filepath.Join(logsDir, "soong.log"))
- trace.SetOutput(filepath.Join(logsDir, "build.trace"))
- } else {
- log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
- trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
+ logsDir = filepath.Join(config.DistDir(), "logs")
}
+ buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
+ rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
+ soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
+ defer build.UploadMetrics(buildCtx, config, simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+
+ os.MkdirAll(logsDir, 0777)
+
+ log.SetOutput(filepath.Join(logsDir, "soong.log"))
+ trace.SetOutput(filepath.Join(logsDir, "build.trace"))
+ stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
+ stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
+
+ defer met.Dump(soongMetricsFile)
+ defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
+
if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
if !strings.HasSuffix(start, "N") {
if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
log.Verbosef("Took %dms to start up.",
time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
- buildCtx.CompleteTrace("startup", start_time, uint64(time.Now().UnixNano()))
+ buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
}
}
@@ -105,6 +149,11 @@
}
}
+ // Fix up the source tree due to a repo bug where it doesn't remove
+ // linkfiles that have been removed
+ fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
+ fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
+
f := build.NewSourceFinder(buildCtx, config)
defer f.Shutdown()
build.FindSources(buildCtx, config, f)
@@ -114,6 +163,17 @@
} else if os.Args[1] == "--dumpvars-mode" {
dumpVars(buildCtx, config, os.Args[2:])
} else {
+ if config.IsVerbose() {
+ writer.Print("! The argument `showcommands` is no longer supported.")
+ writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:")
+ writer.Print("!")
+ writer.Print(fmt.Sprintf("! gzip -cd %s/verbose.log.gz | less -R", logsDir))
+ writer.Print("!")
+ writer.Print("! Older versions are saved in verbose.log.#.gz files")
+ writer.Print("")
+ time.Sleep(5 * time.Second)
+ }
+
toBuild := build.BuildAll
if config.Checkbuild() {
toBuild |= build.RunBuildTests
@@ -122,6 +182,20 @@
}
}
+func fixBadDanglingLink(ctx build.Context, name string) {
+ _, err := os.Lstat(name)
+ if err != nil {
+ return
+ }
+ _, err = os.Stat(name)
+ if os.IsNotExist(err) {
+ err = os.Remove(name)
+ if err != nil {
+ ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
+ }
+ }
+}
+
func dumpVar(ctx build.Context, config build.Config, args []string) {
flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
flags.Usage = func() {
diff --git a/cmd/zip2zip/zip2zip.go b/cmd/zip2zip/zip2zip.go
index e8ea9b9..491267b 100644
--- a/cmd/zip2zip/zip2zip.go
+++ b/cmd/zip2zip/zip2zip.go
@@ -17,6 +17,7 @@
import (
"flag"
"fmt"
+ "io"
"log"
"os"
"path/filepath"
@@ -39,11 +40,15 @@
staticTime = time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC)
- excludes excludeArgs
+ excludes multiFlag
+ includes multiFlag
+ uncompress multiFlag
)
func init() {
flag.Var(&excludes, "x", "exclude a filespec from the output")
+ flag.Var(&includes, "X", "include a filespec in the output that was previously excluded")
+ flag.Var(&uncompress, "0", "convert a filespec to uncompressed in the output")
}
func main() {
@@ -93,7 +98,7 @@
}()
if err := zip2zip(&reader.Reader, writer, *sortGlobs, *sortJava, *setTime,
- flag.Args(), excludes); err != nil {
+ flag.Args(), excludes, includes, uncompress); err != nil {
log.Fatal(err)
}
@@ -101,11 +106,12 @@
type pair struct {
*zip.File
- newName string
+ newName string
+ uncompress bool
}
func zip2zip(reader *zip.Reader, writer *zip.Writer, sortOutput, sortJava, setTime bool,
- includes []string, excludes []string) error {
+ args []string, excludes, includes multiFlag, uncompresses []string) error {
matches := []pair{}
@@ -121,14 +127,14 @@
}
}
- for _, include := range includes {
+ for _, arg := range args {
// Reserve escaping for future implementation, so make sure no
// one is using \ and expecting a certain behavior.
- if strings.Contains(include, "\\") {
+ if strings.Contains(arg, "\\") {
return fmt.Errorf("\\ characters are not currently supported")
}
- input, output := includeSplit(include)
+ input, output := includeSplit(arg)
var includeMatches []pair
@@ -142,14 +148,19 @@
} else {
if pathtools.IsGlob(input) {
// If the input is a glob then the output is a directory.
- _, name := filepath.Split(file.Name)
- newName = filepath.Join(output, name)
+ rel, err := filepath.Rel(constantPartOfPattern(input), file.Name)
+ if err != nil {
+ return err
+ } else if strings.HasPrefix("../", rel) {
+ return fmt.Errorf("globbed path %q was not in %q", file.Name, constantPartOfPattern(input))
+ }
+ newName = filepath.Join(output, rel)
} else {
// Otherwise it is a file.
newName = output
}
}
- includeMatches = append(includeMatches, pair{file, newName})
+ includeMatches = append(includeMatches, pair{file, newName, false})
}
}
@@ -157,10 +168,10 @@
matches = append(matches, includeMatches...)
}
- if len(includes) == 0 {
+ if len(args) == 0 {
// implicitly match everything
for _, file := range reader.File {
- matches = append(matches, pair{file, file.Name})
+ matches = append(matches, pair{file, file.Name, false})
}
sortMatches(matches)
}
@@ -169,21 +180,18 @@
seen := make(map[string]*zip.File)
for _, match := range matches {
- // Filter out matches whose original file name matches an exclude filter
- excluded := false
- for _, exclude := range excludes {
- if excludeMatch, err := pathtools.Match(exclude, match.File.Name); err != nil {
+ // Filter out matches whose original file name matches an exclude filter, unless it also matches an
+ // include filter
+ if exclude, err := excludes.Match(match.File.Name); err != nil {
+ return err
+ } else if exclude {
+ if include, err := includes.Match(match.File.Name); err != nil {
return err
- } else if excludeMatch {
- excluded = true
- break
+ } else if !include {
+ continue
}
}
- if excluded {
- continue
- }
-
// Check for duplicate output names, ignoring ones that come from the same input zip entry.
if prev, exists := seen[match.newName]; exists {
if prev != match.File {
@@ -193,6 +201,15 @@
}
seen[match.newName] = match.File
+ for _, u := range uncompresses {
+ if uncompressMatch, err := pathtools.Match(u, match.newName); err != nil {
+ return err
+ } else if uncompressMatch {
+ match.uncompress = true
+ break
+ }
+ }
+
matchesAfterExcludes = append(matchesAfterExcludes, match)
}
@@ -200,8 +217,32 @@
if setTime {
match.File.SetModTime(staticTime)
}
- if err := writer.CopyFrom(match.File, match.newName); err != nil {
- return err
+ if match.uncompress && match.File.FileHeader.Method != zip.Store {
+ fh := match.File.FileHeader
+ fh.Name = match.newName
+ fh.Method = zip.Store
+ fh.CompressedSize64 = fh.UncompressedSize64
+
+ zw, err := writer.CreateHeaderAndroid(&fh)
+ if err != nil {
+ return err
+ }
+
+ zr, err := match.File.Open()
+ if err != nil {
+ return err
+ }
+
+ _, err = io.Copy(zw, zr)
+ zr.Close()
+ if err != nil {
+ return err
+ }
+ } else {
+ err := writer.CopyFrom(match.File, match.newName)
+ if err != nil {
+ return err
+ }
}
}
@@ -217,13 +258,48 @@
}
}
-type excludeArgs []string
+type multiFlag []string
-func (e *excludeArgs) String() string {
- return strings.Join(*e, " ")
+func (m *multiFlag) String() string {
+ return strings.Join(*m, " ")
}
-func (e *excludeArgs) Set(s string) error {
- *e = append(*e, s)
+func (m *multiFlag) Set(s string) error {
+ *m = append(*m, s)
return nil
}
+
+func (m *multiFlag) Match(s string) (bool, error) {
+ if m == nil {
+ return false, nil
+ }
+ for _, f := range *m {
+ if match, err := pathtools.Match(f, s); err != nil {
+ return false, err
+ } else if match {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func constantPartOfPattern(pattern string) string {
+ ret := ""
+ for pattern != "" {
+ var first string
+ first, pattern = splitFirst(pattern)
+ if pathtools.IsGlob(first) {
+ return ret
+ }
+ ret = filepath.Join(ret, first)
+ }
+ return ret
+}
+
+func splitFirst(path string) (string, string) {
+ i := strings.IndexRune(path, filepath.Separator)
+ if i < 0 {
+ return path, ""
+ }
+ return path[:i], path[i+1:]
+}
diff --git a/cmd/zip2zip/zip2zip_test.go b/cmd/zip2zip/zip2zip_test.go
index 212ab28..2c4e005 100644
--- a/cmd/zip2zip/zip2zip_test.go
+++ b/cmd/zip2zip/zip2zip_test.go
@@ -26,13 +26,16 @@
var testCases = []struct {
name string
- inputFiles []string
- sortGlobs bool
- sortJava bool
- args []string
- excludes []string
+ inputFiles []string
+ sortGlobs bool
+ sortJava bool
+ args []string
+ excludes []string
+ includes []string
+ uncompresses []string
outputFiles []string
+ storedFiles []string
err error
}{
{
@@ -226,7 +229,7 @@
},
},
{
- name: "excludes with include",
+ name: "excludes with args",
inputFiles: []string{
"a/a",
@@ -240,6 +243,31 @@
},
},
{
+ name: "excludes over args",
+
+ inputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ args: []string{"a/a"},
+ excludes: []string{"a/*"},
+
+ outputFiles: nil,
+ },
+ {
+ name: "excludes with includes",
+
+ inputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ args: nil,
+ excludes: []string{"a/*"},
+ includes: []string{"a/b"},
+
+ outputFiles: []string{"a/b"},
+ },
+ {
name: "excludes with glob",
inputFiles: []string{
@@ -251,6 +279,133 @@
outputFiles: nil,
},
+ {
+ name: "uncompress one",
+
+ inputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ uncompresses: []string{"a/a"},
+
+ outputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ storedFiles: []string{
+ "a/a",
+ },
+ },
+ {
+ name: "uncompress two",
+
+ inputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ uncompresses: []string{"a/a", "a/b"},
+
+ outputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ storedFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ },
+ {
+ name: "uncompress glob",
+
+ inputFiles: []string{
+ "a/a",
+ "a/b",
+ "a/c.so",
+ "a/d.so",
+ },
+ uncompresses: []string{"a/*.so"},
+
+ outputFiles: []string{
+ "a/a",
+ "a/b",
+ "a/c.so",
+ "a/d.so",
+ },
+ storedFiles: []string{
+ "a/c.so",
+ "a/d.so",
+ },
+ },
+ {
+ name: "uncompress rename",
+
+ inputFiles: []string{
+ "a/a",
+ },
+ args: []string{"a/a:a/b"},
+ uncompresses: []string{"a/b"},
+
+ outputFiles: []string{
+ "a/b",
+ },
+ storedFiles: []string{
+ "a/b",
+ },
+ },
+ {
+ name: "recursive glob",
+
+ inputFiles: []string{
+ "a/a/a",
+ "a/a/b",
+ },
+ args: []string{"a/**/*:b"},
+ outputFiles: []string{
+ "b/a/a",
+ "b/a/b",
+ },
+ },
+ {
+ name: "glob",
+
+ inputFiles: []string{
+ "a/a/a",
+ "a/a/b",
+ "a/b",
+ "a/c",
+ },
+ args: []string{"a/*:b"},
+ outputFiles: []string{
+ "b/b",
+ "b/c",
+ },
+ },
+ {
+ name: "top level glob",
+
+ inputFiles: []string{
+ "a",
+ "b",
+ },
+ args: []string{"*:b"},
+ outputFiles: []string{
+ "b/a",
+ "b/b",
+ },
+ },
+ {
+ name: "multilple glob",
+
+ inputFiles: []string{
+ "a/a/a",
+ "a/a/b",
+ },
+ args: []string{"a/*/*:b"},
+ outputFiles: []string{
+ "b/a/a",
+ "b/a/b",
+ },
+ },
}
func errorString(e error) string {
@@ -282,7 +437,8 @@
}
outputWriter := zip.NewWriter(outputBuf)
- err = zip2zip(inputReader, outputWriter, testCase.sortGlobs, testCase.sortJava, false, testCase.args, testCase.excludes)
+ err = zip2zip(inputReader, outputWriter, testCase.sortGlobs, testCase.sortJava, false,
+ testCase.args, testCase.excludes, testCase.includes, testCase.uncompresses)
if errorString(testCase.err) != errorString(err) {
t.Fatalf("Unexpected error:\n got: %q\nwant: %q", errorString(err), errorString(testCase.err))
}
@@ -294,15 +450,64 @@
t.Fatal(err)
}
var outputFiles []string
+ var storedFiles []string
if len(outputReader.File) > 0 {
outputFiles = make([]string, len(outputReader.File))
for i, file := range outputReader.File {
outputFiles[i] = file.Name
+ if file.Method == zip.Store {
+ storedFiles = append(storedFiles, file.Name)
+ }
}
}
if !reflect.DeepEqual(testCase.outputFiles, outputFiles) {
- t.Fatalf("Output file list does not match:\n got: %v\nwant: %v", outputFiles, testCase.outputFiles)
+ t.Fatalf("Output file list does not match:\nwant: %v\n got: %v", testCase.outputFiles, outputFiles)
+ }
+ if !reflect.DeepEqual(testCase.storedFiles, storedFiles) {
+ t.Fatalf("Stored file list does not match:\nwant: %v\n got: %v", testCase.storedFiles, storedFiles)
+ }
+ })
+ }
+}
+
+func TestConstantPartOfPattern(t *testing.T) {
+ testCases := []struct{ in, out string }{
+ {
+ in: "",
+ out: "",
+ },
+ {
+ in: "a",
+ out: "a",
+ },
+ {
+ in: "*",
+ out: "",
+ },
+ {
+ in: "a/a",
+ out: "a/a",
+ },
+ {
+ in: "a/*",
+ out: "a",
+ },
+ {
+ in: "a/*/a",
+ out: "a",
+ },
+ {
+ in: "a/**/*",
+ out: "a",
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.in, func(t *testing.T) {
+ got := constantPartOfPattern(test.in)
+ if got != test.out {
+ t.Errorf("want %q, got %q", test.out, got)
}
})
}
diff --git a/cmd/zipsync/zipsync.go b/cmd/zipsync/zipsync.go
index 1b30016..ea755f5 100644
--- a/cmd/zipsync/zipsync.go
+++ b/cmd/zipsync/zipsync.go
@@ -118,7 +118,10 @@
}
if *outputFile != "" {
- data := strings.Join(files, "\n") + "\n"
+ data := strings.Join(files, "\n")
+ if len(files) > 0 {
+ data += "\n"
+ }
must(ioutil.WriteFile(*outputFile, []byte(data), 0666))
}
}
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp
new file mode 100644
index 0000000..c5f24e2
--- /dev/null
+++ b/dexpreopt/Android.bp
@@ -0,0 +1,15 @@
+bootstrap_go_package {
+ name: "soong-dexpreopt",
+ pkgPath: "android/soong/dexpreopt",
+ srcs: [
+ "config.go",
+ "dexpreopt.go",
+ ],
+ testSrcs: [
+ "dexpreopt_test.go",
+ ],
+ deps: [
+ "blueprint-pathtools",
+ "soong-android",
+ ],
+}
diff --git a/dexpreopt/OWNERS b/dexpreopt/OWNERS
new file mode 100644
index 0000000..166472f
--- /dev/null
+++ b/dexpreopt/OWNERS
@@ -0,0 +1 @@
+per-file * = ngeoffray@google.com,calin@google.com,mathieuc@google.com
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
new file mode 100644
index 0000000..3b77042
--- /dev/null
+++ b/dexpreopt/config.go
@@ -0,0 +1,336 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dexpreopt
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "strings"
+
+ "android/soong/android"
+)
+
+// GlobalConfig stores the configuration for dex preopting set by the product
+type GlobalConfig struct {
+ DefaultNoStripping bool // don't strip dex files by default
+
+ DisablePreopt bool // disable preopt for all modules
+ DisablePreoptModules []string // modules with preopt disabled by product-specific config
+
+ OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server
+
+ GenerateApexImage bool // generate an extra boot image only containing jars from the runtime apex
+ UseApexImage bool // use the apex image by default
+
+ HasSystemOther bool // store odex files that match PatternsOnSystemOther on the system_other partition
+ PatternsOnSystemOther []string // patterns (using '%' to denote a prefix match) to put odex on the system_other partition
+
+ DisableGenerateProfile bool // don't generate profiles
+ ProfileDir string // directory to find profiles in
+
+ BootJars []string // modules for jars that form the boot class path
+
+ RuntimeApexJars []string // modules for jars that are in the runtime apex
+ ProductUpdatableBootModules []string
+ ProductUpdatableBootLocations []string
+
+ SystemServerJars []string // jars that form the system server
+ SystemServerApps []string // apps that are loaded into system server
+ SpeedApps []string // apps that should be speed optimized
+
+ PreoptFlags []string // global dex2oat flags that should be used if no module-specific dex2oat flags are specified
+
+ DefaultCompilerFilter string // default compiler filter to pass to dex2oat, overridden by --compiler-filter= in module-specific dex2oat flags
+ SystemServerCompilerFilter string // default compiler filter to pass to dex2oat for system server jars
+
+ GenerateDMFiles bool // generate Dex Metadata files
+ NeverAllowStripping bool // whether stripping should not be done - used as build time check to make sure dex files are always available
+
+ NoDebugInfo bool // don't generate debug info by default
+ DontResolveStartupStrings bool // don't resolve string literals loaded during application startup.
+ AlwaysSystemServerDebugInfo bool // always generate mini debug info for system server modules (overrides NoDebugInfo=true)
+ NeverSystemServerDebugInfo bool // never generate mini debug info for system server modules (overrides NoDebugInfo=false)
+ AlwaysOtherDebugInfo bool // always generate mini debug info for non-system server modules (overrides NoDebugInfo=true)
+ NeverOtherDebugInfo bool // never generate mini debug info for non-system server modules (overrides NoDebugInfo=true)
+
+ MissingUsesLibraries []string // libraries that may be listed in OptionalUsesLibraries but will not be installed by the product
+
+ IsEng bool // build is a eng variant
+ SanitizeLite bool // build is the second phase of a SANITIZE_LITE build
+
+ DefaultAppImages bool // build app images (TODO: .art files?) by default
+
+ Dex2oatXmx string // max heap size for dex2oat
+ Dex2oatXms string // initial heap size for dex2oat
+
+ EmptyDirectory string // path to an empty directory
+
+ CpuVariant map[android.ArchType]string // cpu variant for each architecture
+ InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
+
+ // Only used for boot image
+ DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file
+ PreloadedClasses android.OptionalPath // path to a preloaded-classes file
+ BootImageProfiles android.Paths // path to a boot-image-profile.txt file
+ UseProfileForBootImage bool // whether a profile should be used to compile the boot image
+ BootFlags string // extra flags to pass to dex2oat for the boot image
+ Dex2oatImageXmx string // max heap size for dex2oat for the boot image
+ Dex2oatImageXms string // initial heap size for dex2oat for the boot image
+
+ Tools Tools // paths to tools possibly used by the generated commands
+}
+
+// Tools contains paths to tools possibly used by the generated commands. If you add a new tool here you MUST add it
+// to the order-only dependency list in DEXPREOPT_GEN_DEPS.
+type Tools struct {
+ Profman android.Path
+ Dex2oat android.Path
+ Aapt android.Path
+ SoongZip android.Path
+ Zip2zip android.Path
+
+ VerifyUsesLibraries android.Path
+ ConstructContext android.Path
+}
+
+type ModuleConfig struct {
+ Name string
+ DexLocation string // dex location on device
+ BuildPath android.OutputPath
+ DexPath android.Path
+ UncompressedDex bool
+ HasApkLibraries bool
+ PreoptFlags []string
+
+ ProfileClassListing android.OptionalPath
+ ProfileIsTextListing bool
+
+ EnforceUsesLibraries bool
+ OptionalUsesLibraries []string
+ UsesLibraries []string
+ LibraryPaths map[string]android.Path
+
+ Archs []android.ArchType
+ DexPreoptImages []android.Path
+
+ PreoptBootClassPathDexFiles android.Paths // file paths of boot class path files
+ PreoptBootClassPathDexLocations []string // virtual locations of boot class path files
+
+ PreoptExtractedApk bool // Overrides OnlyPreoptModules
+
+ NoCreateAppImage bool
+ ForceCreateAppImage bool
+
+ PresignedPrebuilt bool
+
+ NoStripping bool
+ StripInputPath android.Path
+ StripOutputPath android.WritablePath
+}
+
+func constructPath(ctx android.PathContext, path string) android.Path {
+ buildDirPrefix := ctx.Config().BuildDir() + "/"
+ if path == "" {
+ return nil
+ } else if strings.HasPrefix(path, buildDirPrefix) {
+ return android.PathForOutput(ctx, strings.TrimPrefix(path, buildDirPrefix))
+ } else {
+ return android.PathForSource(ctx, path)
+ }
+}
+
+func constructPaths(ctx android.PathContext, paths []string) android.Paths {
+ var ret android.Paths
+ for _, path := range paths {
+ ret = append(ret, constructPath(ctx, path))
+ }
+ return ret
+}
+
+func constructPathMap(ctx android.PathContext, paths map[string]string) map[string]android.Path {
+ ret := map[string]android.Path{}
+ for key, path := range paths {
+ ret[key] = constructPath(ctx, path)
+ }
+ return ret
+}
+
+func constructWritablePath(ctx android.PathContext, path string) android.WritablePath {
+ if path == "" {
+ return nil
+ }
+ return constructPath(ctx, path).(android.WritablePath)
+}
+
+// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig struct. It is used directly in Soong
+// and in dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by Make.
+func LoadGlobalConfig(ctx android.PathContext, path string) (GlobalConfig, error) {
+ type GlobalJSONConfig struct {
+ GlobalConfig
+
+ // Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be
+ // used to construct the real value manually below.
+ DirtyImageObjects string
+ PreloadedClasses string
+ BootImageProfiles []string
+
+ Tools struct {
+ Profman string
+ Dex2oat string
+ Aapt string
+ SoongZip string
+ Zip2zip string
+
+ VerifyUsesLibraries string
+ ConstructContext string
+ }
+ }
+
+ config := GlobalJSONConfig{}
+ err := loadConfig(ctx, path, &config)
+ if err != nil {
+ return config.GlobalConfig, err
+ }
+
+ // Construct paths that require a PathContext.
+ config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
+ config.GlobalConfig.PreloadedClasses = android.OptionalPathForPath(constructPath(ctx, config.PreloadedClasses))
+ config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
+
+ config.GlobalConfig.Tools.Profman = constructPath(ctx, config.Tools.Profman)
+ config.GlobalConfig.Tools.Dex2oat = constructPath(ctx, config.Tools.Dex2oat)
+ config.GlobalConfig.Tools.Aapt = constructPath(ctx, config.Tools.Aapt)
+ config.GlobalConfig.Tools.SoongZip = constructPath(ctx, config.Tools.SoongZip)
+ config.GlobalConfig.Tools.Zip2zip = constructPath(ctx, config.Tools.Zip2zip)
+ config.GlobalConfig.Tools.VerifyUsesLibraries = constructPath(ctx, config.Tools.VerifyUsesLibraries)
+ config.GlobalConfig.Tools.ConstructContext = constructPath(ctx, config.Tools.ConstructContext)
+
+ return config.GlobalConfig, nil
+}
+
+// LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct. It is not used in Soong, which
+// receives a ModuleConfig struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called from oMake to
+// read the module dexpreopt.config written by Make.
+func LoadModuleConfig(ctx android.PathContext, path string) (ModuleConfig, error) {
+ type ModuleJSONConfig struct {
+ ModuleConfig
+
+ // Copies of entries in ModuleConfig that are not constructable without extra parameters. They will be
+ // used to construct the real value manually below.
+ BuildPath string
+ DexPath string
+ ProfileClassListing string
+ LibraryPaths map[string]string
+ DexPreoptImages []string
+ PreoptBootClassPathDexFiles []string
+ StripInputPath string
+ StripOutputPath string
+ }
+
+ config := ModuleJSONConfig{}
+
+ err := loadConfig(ctx, path, &config)
+ if err != nil {
+ return config.ModuleConfig, err
+ }
+
+ // Construct paths that require a PathContext.
+ config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath)
+ config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
+ config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
+ config.ModuleConfig.LibraryPaths = constructPathMap(ctx, config.LibraryPaths)
+ config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
+ config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
+ config.ModuleConfig.StripInputPath = constructPath(ctx, config.StripInputPath)
+ config.ModuleConfig.StripOutputPath = constructWritablePath(ctx, config.StripOutputPath)
+
+ return config.ModuleConfig, nil
+}
+
+func loadConfig(ctx android.PathContext, path string, config interface{}) error {
+ r, err := ctx.Fs().Open(path)
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(data, config)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func GlobalConfigForTests(ctx android.PathContext) GlobalConfig {
+ return GlobalConfig{
+ DefaultNoStripping: false,
+ DisablePreopt: false,
+ DisablePreoptModules: nil,
+ OnlyPreoptBootImageAndSystemServer: false,
+ HasSystemOther: false,
+ PatternsOnSystemOther: nil,
+ DisableGenerateProfile: false,
+ ProfileDir: "",
+ BootJars: nil,
+ RuntimeApexJars: nil,
+ ProductUpdatableBootModules: nil,
+ ProductUpdatableBootLocations: nil,
+ SystemServerJars: nil,
+ SystemServerApps: nil,
+ SpeedApps: nil,
+ PreoptFlags: nil,
+ DefaultCompilerFilter: "",
+ SystemServerCompilerFilter: "",
+ GenerateDMFiles: false,
+ NeverAllowStripping: false,
+ NoDebugInfo: false,
+ DontResolveStartupStrings: false,
+ AlwaysSystemServerDebugInfo: false,
+ NeverSystemServerDebugInfo: false,
+ AlwaysOtherDebugInfo: false,
+ NeverOtherDebugInfo: false,
+ MissingUsesLibraries: nil,
+ IsEng: false,
+ SanitizeLite: false,
+ DefaultAppImages: false,
+ Dex2oatXmx: "",
+ Dex2oatXms: "",
+ EmptyDirectory: "empty_dir",
+ CpuVariant: nil,
+ InstructionSetFeatures: nil,
+ DirtyImageObjects: android.OptionalPath{},
+ PreloadedClasses: android.OptionalPath{},
+ BootImageProfiles: nil,
+ UseProfileForBootImage: false,
+ BootFlags: "",
+ Dex2oatImageXmx: "",
+ Dex2oatImageXms: "",
+ Tools: Tools{
+ Profman: android.PathForTesting("profman"),
+ Dex2oat: android.PathForTesting("dex2oat"),
+ Aapt: android.PathForTesting("aapt"),
+ SoongZip: android.PathForTesting("soong_zip"),
+ Zip2zip: android.PathForTesting("zip2zip"),
+ VerifyUsesLibraries: android.PathForTesting("verify_uses_libraries.sh"),
+ ConstructContext: android.PathForTesting("construct_context.sh"),
+ },
+ }
+}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
new file mode 100644
index 0000000..5b658d9
--- /dev/null
+++ b/dexpreopt/dexpreopt.go
@@ -0,0 +1,622 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The dexpreopt package converts a global dexpreopt config and a module dexpreopt config into rules to perform
+// dexpreopting and to strip the dex files from the APK or JAR.
+//
+// It is used in two places; in the dexpeopt_gen binary for modules defined in Make, and directly linked into Soong.
+//
+// For Make modules it is built into the dexpreopt_gen binary, which is executed as a Make rule using global config and
+// module config specified in JSON files. The binary writes out two shell scripts, only updating them if they have
+// changed. One script takes an APK or JAR as an input and produces a zip file containing any outputs of preopting,
+// in the location they should be on the device. The Make build rules will unzip the zip file into $(PRODUCT_OUT) when
+// installing the APK, which will install the preopt outputs into $(PRODUCT_OUT)/system or $(PRODUCT_OUT)/system_other
+// as necessary. The zip file may be empty if preopting was disabled for any reason. The second script takes an APK or
+// JAR as an input and strips the dex files in it as necessary.
+//
+// The intermediate shell scripts allow changes to this package or to the global config to regenerate the shell scripts
+// but only require re-executing preopting if the script has changed.
+//
+// For Soong modules this package is linked directly into Soong and run from the java package. It generates the same
+// commands as for make, using athe same global config JSON file used by make, but using a module config structure
+// provided by Soong. The generated commands are then converted into Soong rule and written directly to the ninja file,
+// with no extra shell scripts involved.
+package dexpreopt
+
+import (
+ "fmt"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint/pathtools"
+)
+
+const SystemPartition = "/system/"
+const SystemOtherPartition = "/system_other/"
+
+// GenerateStripRule generates a set of commands that will take an APK or JAR as an input and strip the dex files if
+// they are no longer necessary after preopting.
+func GenerateStripRule(global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if _, ok := r.(runtime.Error); ok {
+ panic(r)
+ } else if e, ok := r.(error); ok {
+ err = e
+ rule = nil
+ } else {
+ panic(r)
+ }
+ }
+ }()
+
+ tools := global.Tools
+
+ rule = android.NewRuleBuilder()
+
+ strip := shouldStripDex(module, global)
+
+ if strip {
+ if global.NeverAllowStripping {
+ panic(fmt.Errorf("Stripping requested on %q, though the product does not allow it", module.DexLocation))
+ }
+ // Only strips if the dex files are not already uncompressed
+ rule.Command().
+ Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, module.StripInputPath).
+ Tool(tools.Zip2zip).FlagWithInput("-i ", module.StripInputPath).FlagWithOutput("-o ", module.StripOutputPath).
+ FlagWithArg("-x ", `"classes*.dex"`).
+ Textf(`; else cp -f %s %s; fi`, module.StripInputPath, module.StripOutputPath)
+ } else {
+ rule.Command().Text("cp -f").Input(module.StripInputPath).Output(module.StripOutputPath)
+ }
+
+ return rule, nil
+}
+
+// GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
+// ModuleConfig. The produced files and their install locations will be available through rule.Installs().
+func GenerateDexpreoptRule(ctx android.PathContext,
+ global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) {
+
+ defer func() {
+ if r := recover(); r != nil {
+ if _, ok := r.(runtime.Error); ok {
+ panic(r)
+ } else if e, ok := r.(error); ok {
+ err = e
+ rule = nil
+ } else {
+ panic(r)
+ }
+ }
+ }()
+
+ rule = android.NewRuleBuilder()
+
+ generateProfile := module.ProfileClassListing.Valid() && !global.DisableGenerateProfile
+
+ var profile android.WritablePath
+ if generateProfile {
+ profile = profileCommand(ctx, global, module, rule)
+ }
+
+ if !dexpreoptDisabled(global, module) {
+ // Don't preopt individual boot jars, they will be preopted together.
+ // This check is outside dexpreoptDisabled because they still need to be stripped.
+ if !contains(global.BootJars, module.Name) {
+ appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
+ !module.NoCreateAppImage
+
+ generateDM := shouldGenerateDM(module, global)
+
+ for i, arch := range module.Archs {
+ image := module.DexPreoptImages[i]
+ dexpreoptCommand(ctx, global, module, rule, arch, profile, image, appImage, generateDM)
+ }
+ }
+ }
+
+ return rule, nil
+}
+
+func dexpreoptDisabled(global GlobalConfig, module ModuleConfig) bool {
+ if contains(global.DisablePreoptModules, module.Name) {
+ return true
+ }
+
+ // If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
+ // Also preopt system server jars since selinux prevents system server from loading anything from
+ // /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
+ // or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
+ if global.OnlyPreoptBootImageAndSystemServer && !contains(global.BootJars, module.Name) &&
+ !contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
+ return true
+ }
+
+ return false
+}
+
+func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig,
+ rule *android.RuleBuilder) android.WritablePath {
+
+ profilePath := module.BuildPath.InSameDir(ctx, "profile.prof")
+ profileInstalledPath := module.DexLocation + ".prof"
+
+ if !module.ProfileIsTextListing {
+ rule.Command().FlagWithOutput("touch ", profilePath)
+ }
+
+ cmd := rule.Command().
+ Text(`ANDROID_LOG_TAGS="*:e"`).
+ Tool(global.Tools.Profman)
+
+ if module.ProfileIsTextListing {
+ // The profile is a test listing of classes (used for framework jars).
+ // We need to generate the actual binary profile before being able to compile.
+ cmd.FlagWithInput("--create-profile-from=", module.ProfileClassListing.Path())
+ } else {
+ // The profile is binary profile (used for apps). Run it through profman to
+ // ensure the profile keys match the apk.
+ cmd.
+ Flag("--copy-and-update-profile-key").
+ FlagWithInput("--profile-file=", module.ProfileClassListing.Path())
+ }
+
+ cmd.
+ FlagWithInput("--apk=", module.DexPath).
+ Flag("--dex-location="+module.DexLocation).
+ FlagWithOutput("--reference-profile-file=", profilePath)
+
+ if !module.ProfileIsTextListing {
+ cmd.Text(fmt.Sprintf(`|| echo "Profile out of date for %s"`, module.DexPath))
+ }
+ rule.Install(profilePath, profileInstalledPath)
+
+ return profilePath
+}
+
+func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder,
+ arch android.ArchType, profile, bootImage android.Path, appImage, generateDM bool) {
+
+ // HACK: make soname in Soong-generated .odex files match Make.
+ base := filepath.Base(module.DexLocation)
+ if filepath.Ext(base) == ".jar" {
+ base = "javalib.jar"
+ } else if filepath.Ext(base) == ".apk" {
+ base = "package.apk"
+ }
+
+ toOdexPath := func(path string) string {
+ return filepath.Join(
+ filepath.Dir(path),
+ "oat",
+ arch.String(),
+ pathtools.ReplaceExtension(filepath.Base(path), "odex"))
+ }
+
+ odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
+ odexInstallPath := toOdexPath(module.DexLocation)
+ if odexOnSystemOther(module, global) {
+ odexInstallPath = strings.Replace(odexInstallPath, SystemPartition, SystemOtherPartition, 1)
+ }
+
+ vdexPath := odexPath.ReplaceExtension(ctx, "vdex")
+ vdexInstallPath := pathtools.ReplaceExtension(odexInstallPath, "vdex")
+
+ invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
+
+ // bootImage is .../dex_bootjars/system/framework/arm64/boot.art, but dex2oat wants
+ // .../dex_bootjars/system/framework/boot.art on the command line
+ var bootImageLocation string
+ if bootImage != nil {
+ bootImageLocation = PathToLocation(bootImage, arch)
+ }
+
+ // Lists of used and optional libraries from the build config to be verified against the manifest in the APK
+ var verifyUsesLibs []string
+ var verifyOptionalUsesLibs []string
+
+ // Lists of used and optional libraries from the build config, with optional libraries that are known to not
+ // be present in the current product removed.
+ var filteredUsesLibs []string
+ var filteredOptionalUsesLibs []string
+
+ // The class loader context using paths in the build
+ var classLoaderContextHost android.Paths
+
+ // The class loader context using paths as they will be on the device
+ var classLoaderContextTarget []string
+
+ // Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 28
+ var conditionalClassLoaderContextHost28 android.Paths
+ var conditionalClassLoaderContextTarget28 []string
+
+ // Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 29
+ var conditionalClassLoaderContextHost29 android.Paths
+ var conditionalClassLoaderContextTarget29 []string
+
+ var classLoaderContextHostString string
+
+ if module.EnforceUsesLibraries {
+ verifyUsesLibs = copyOf(module.UsesLibraries)
+ verifyOptionalUsesLibs = copyOf(module.OptionalUsesLibraries)
+
+ filteredOptionalUsesLibs = filterOut(global.MissingUsesLibraries, module.OptionalUsesLibraries)
+ filteredUsesLibs = append(copyOf(module.UsesLibraries), filteredOptionalUsesLibs...)
+
+ // Create class loader context for dex2oat from uses libraries and filtered optional libraries
+ for _, l := range filteredUsesLibs {
+
+ classLoaderContextHost = append(classLoaderContextHost,
+ pathForLibrary(module, l))
+ classLoaderContextTarget = append(classLoaderContextTarget,
+ filepath.Join("/system/framework", l+".jar"))
+ }
+
+ const httpLegacy = "org.apache.http.legacy"
+ const httpLegacyImpl = "org.apache.http.legacy.impl"
+
+ // Fix up org.apache.http.legacy.impl since it should be org.apache.http.legacy in the manifest.
+ replace(verifyUsesLibs, httpLegacyImpl, httpLegacy)
+ replace(verifyOptionalUsesLibs, httpLegacyImpl, httpLegacy)
+
+ if !contains(verifyUsesLibs, httpLegacy) && !contains(verifyOptionalUsesLibs, httpLegacy) {
+ conditionalClassLoaderContextHost28 = append(conditionalClassLoaderContextHost28,
+ pathForLibrary(module, httpLegacyImpl))
+ conditionalClassLoaderContextTarget28 = append(conditionalClassLoaderContextTarget28,
+ filepath.Join("/system/framework", httpLegacyImpl+".jar"))
+ }
+
+ const hidlBase = "android.hidl.base-V1.0-java"
+ const hidlManager = "android.hidl.manager-V1.0-java"
+
+ conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
+ pathForLibrary(module, hidlManager))
+ conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
+ filepath.Join("/system/framework", hidlManager+".jar"))
+ conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
+ pathForLibrary(module, hidlBase))
+ conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
+ filepath.Join("/system/framework", hidlBase+".jar"))
+
+ classLoaderContextHostString = strings.Join(classLoaderContextHost.Strings(), ":")
+ } 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 &.
+ classLoaderContextHostString = `\&`
+ }
+
+ rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
+ rule.Command().FlagWithOutput("rm -f ", odexPath)
+ // Set values in the environment of the rule. These may be modified by construct_context.sh.
+ rule.Command().FlagWithArg("class_loader_context_arg=--class-loader-context=", classLoaderContextHostString)
+ rule.Command().Text(`stored_class_loader_context_arg=""`)
+
+ if module.EnforceUsesLibraries {
+ rule.Command().Textf(`uses_library_names="%s"`, strings.Join(verifyUsesLibs, " "))
+ rule.Command().Textf(`optional_uses_library_names="%s"`, strings.Join(verifyOptionalUsesLibs, " "))
+ rule.Command().Textf(`aapt_binary="%s"`, global.Tools.Aapt)
+ rule.Command().Textf(`dex_preopt_host_libraries="%s"`,
+ strings.Join(classLoaderContextHost.Strings(), " ")).
+ Implicits(classLoaderContextHost)
+ rule.Command().Textf(`dex_preopt_target_libraries="%s"`,
+ strings.Join(classLoaderContextTarget, " "))
+ rule.Command().Textf(`conditional_host_libs_28="%s"`,
+ strings.Join(conditionalClassLoaderContextHost28.Strings(), " ")).
+ Implicits(conditionalClassLoaderContextHost28)
+ rule.Command().Textf(`conditional_target_libs_28="%s"`,
+ strings.Join(conditionalClassLoaderContextTarget28, " "))
+ rule.Command().Textf(`conditional_host_libs_29="%s"`,
+ strings.Join(conditionalClassLoaderContextHost29.Strings(), " ")).
+ Implicits(conditionalClassLoaderContextHost29)
+ rule.Command().Textf(`conditional_target_libs_29="%s"`,
+ strings.Join(conditionalClassLoaderContextTarget29, " "))
+ rule.Command().Text("source").Tool(global.Tools.VerifyUsesLibraries).Input(module.DexPath)
+ rule.Command().Text("source").Tool(global.Tools.ConstructContext)
+ }
+
+ // Devices that do not have a product partition use a symlink from /product to /system/product.
+ // Because on-device dexopt will see dex locations starting with /product, we change the paths
+ // to mimic this behavior.
+ dexLocationArg := module.DexLocation
+ if strings.HasPrefix(dexLocationArg, "/system/product/") {
+ dexLocationArg = strings.TrimPrefix(dexLocationArg, "/system")
+ }
+
+ cmd := rule.Command().
+ Text(`ANDROID_LOG_TAGS="*:e"`).
+ Tool(global.Tools.Dex2oat).
+ Flag("--avoid-storing-invocation").
+ FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
+ Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
+ Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatXmx).
+ Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":").
+ Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
+ Flag("${class_loader_context_arg}").
+ Flag("${stored_class_loader_context_arg}").
+ FlagWithArg("--boot-image=", bootImageLocation).Implicit(bootImage).
+ FlagWithInput("--dex-file=", module.DexPath).
+ FlagWithArg("--dex-location=", dexLocationArg).
+ FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
+ // Pass an empty directory, dex2oat shouldn't be reading arbitrary files
+ FlagWithArg("--android-root=", global.EmptyDirectory).
+ FlagWithArg("--instruction-set=", arch.String()).
+ FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
+ FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
+ Flag("--no-generate-debug-info").
+ Flag("--generate-build-id").
+ Flag("--abort-on-hard-verifier-error").
+ Flag("--force-determinism").
+ FlagWithArg("--no-inline-from=", "core-oj.jar")
+
+ var preoptFlags []string
+ if len(module.PreoptFlags) > 0 {
+ preoptFlags = module.PreoptFlags
+ } else if len(global.PreoptFlags) > 0 {
+ preoptFlags = global.PreoptFlags
+ }
+
+ if len(preoptFlags) > 0 {
+ cmd.Text(strings.Join(preoptFlags, " "))
+ }
+
+ if module.UncompressedDex {
+ cmd.FlagWithArg("--copy-dex-files=", "false")
+ }
+
+ if !anyHavePrefix(preoptFlags, "--compiler-filter=") {
+ var compilerFilter string
+ if contains(global.SystemServerJars, module.Name) {
+ // Jars of system server, use the product option if it is set, speed otherwise.
+ if global.SystemServerCompilerFilter != "" {
+ compilerFilter = global.SystemServerCompilerFilter
+ } else {
+ compilerFilter = "speed"
+ }
+ } else if contains(global.SpeedApps, module.Name) || contains(global.SystemServerApps, module.Name) {
+ // Apps loaded into system server, and apps the product default to being compiled with the
+ // 'speed' compiler filter.
+ compilerFilter = "speed"
+ } else if profile != nil {
+ // For non system server jars, use speed-profile when we have a profile.
+ compilerFilter = "speed-profile"
+ } else if global.DefaultCompilerFilter != "" {
+ compilerFilter = global.DefaultCompilerFilter
+ } else {
+ compilerFilter = "quicken"
+ }
+ cmd.FlagWithArg("--compiler-filter=", compilerFilter)
+ }
+
+ if generateDM {
+ cmd.FlagWithArg("--copy-dex-files=", "false")
+ dmPath := module.BuildPath.InSameDir(ctx, "generated.dm")
+ dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm")
+ tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex")
+ rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath)
+ rule.Command().Tool(global.Tools.SoongZip).
+ FlagWithArg("-L", "9").
+ FlagWithOutput("-o", dmPath).
+ Flag("-j").
+ Input(tmpPath)
+ rule.Install(dmPath, dmInstalledPath)
+ }
+
+ // By default, emit debug info.
+ debugInfo := true
+ if global.NoDebugInfo {
+ // If the global setting suppresses mini-debug-info, disable it.
+ debugInfo = false
+ }
+
+ // PRODUCT_SYSTEM_SERVER_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
+ // PRODUCT_OTHER_JAVA_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
+ if contains(global.SystemServerJars, module.Name) {
+ if global.AlwaysSystemServerDebugInfo {
+ debugInfo = true
+ } else if global.NeverSystemServerDebugInfo {
+ debugInfo = false
+ }
+ } else {
+ if global.AlwaysOtherDebugInfo {
+ debugInfo = true
+ } else if global.NeverOtherDebugInfo {
+ debugInfo = false
+ }
+ }
+
+ // Never enable on eng.
+ if global.IsEng {
+ debugInfo = false
+ }
+
+ if debugInfo {
+ cmd.Flag("--generate-mini-debug-info")
+ } else {
+ cmd.Flag("--no-generate-mini-debug-info")
+ }
+
+ // Set the compiler reason to 'prebuilt' to identify the oat files produced
+ // during the build, as opposed to compiled on the device.
+ cmd.FlagWithArg("--compilation-reason=", "prebuilt")
+
+ if appImage {
+ appImagePath := odexPath.ReplaceExtension(ctx, "art")
+ appImageInstallPath := pathtools.ReplaceExtension(odexInstallPath, "art")
+ cmd.FlagWithOutput("--app-image-file=", appImagePath).
+ FlagWithArg("--image-format=", "lz4")
+ if !global.DontResolveStartupStrings {
+ cmd.FlagWithArg("--resolve-startup-const-strings=", "true")
+ }
+ rule.Install(appImagePath, appImageInstallPath)
+ }
+
+ if profile != nil {
+ cmd.FlagWithInput("--profile-file=", profile)
+ }
+
+ rule.Install(odexPath, odexInstallPath)
+ rule.Install(vdexPath, vdexInstallPath)
+}
+
+// Return if the dex file in the APK should be stripped. If an APK is found to contain uncompressed dex files at
+// dex2oat time it will not be stripped even if strip=true.
+func shouldStripDex(module ModuleConfig, global GlobalConfig) bool {
+ strip := !global.DefaultNoStripping
+
+ if dexpreoptDisabled(global, module) {
+ strip = false
+ }
+
+ if module.NoStripping {
+ strip = false
+ }
+
+ // Don't strip modules that are not on the system partition in case the oat/vdex version in system ROM
+ // doesn't match the one in other partitions. It needs to be able to fall back to the APK for that case.
+ if !strings.HasPrefix(module.DexLocation, SystemPartition) {
+ strip = false
+ }
+
+ // system_other isn't there for an OTA, so don't strip if module is on system, and odex is on system_other.
+ if odexOnSystemOther(module, global) {
+ strip = false
+ }
+
+ if module.HasApkLibraries {
+ strip = false
+ }
+
+ // Don't strip with dex files we explicitly uncompress (dexopt will not store the dex code).
+ if module.UncompressedDex {
+ strip = false
+ }
+
+ if shouldGenerateDM(module, global) {
+ strip = false
+ }
+
+ if module.PresignedPrebuilt {
+ // Only strip out files if we can re-sign the package.
+ strip = false
+ }
+
+ return strip
+}
+
+func shouldGenerateDM(module ModuleConfig, global GlobalConfig) bool {
+ // Generating DM files only makes sense for verify, avoid doing for non verify compiler filter APKs.
+ // No reason to use a dm file if the dex is already uncompressed.
+ return global.GenerateDMFiles && !module.UncompressedDex &&
+ contains(module.PreoptFlags, "--compiler-filter=verify")
+}
+
+func OdexOnSystemOtherByName(name string, dexLocation string, global GlobalConfig) bool {
+ if !global.HasSystemOther {
+ return false
+ }
+
+ if global.SanitizeLite {
+ return false
+ }
+
+ if contains(global.SpeedApps, name) || contains(global.SystemServerApps, name) {
+ return false
+ }
+
+ for _, f := range global.PatternsOnSystemOther {
+ if makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func odexOnSystemOther(module ModuleConfig, global GlobalConfig) bool {
+ return OdexOnSystemOtherByName(module.Name, module.DexLocation, global)
+}
+
+// PathToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art
+func PathToLocation(path android.Path, arch android.ArchType) string {
+ pathArch := filepath.Base(filepath.Dir(path.String()))
+ if pathArch != arch.String() {
+ panic(fmt.Errorf("last directory in %q must be %q", path, arch.String()))
+ }
+ return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String()))
+}
+
+func pathForLibrary(module ModuleConfig, lib string) android.Path {
+ path, ok := module.LibraryPaths[lib]
+ if !ok {
+ panic(fmt.Errorf("unknown library path for %q", lib))
+ }
+ return path
+}
+
+func makefileMatch(pattern, s string) bool {
+ percent := strings.IndexByte(pattern, '%')
+ switch percent {
+ case -1:
+ return pattern == s
+ case len(pattern) - 1:
+ return strings.HasPrefix(s, pattern[:len(pattern)-1])
+ default:
+ panic(fmt.Errorf("unsupported makefile pattern %q", pattern))
+ }
+}
+
+func contains(l []string, s string) bool {
+ for _, e := range l {
+ if e == s {
+ return true
+ }
+ }
+ return false
+}
+
+// remove all elements in a from b, returning a new slice
+func filterOut(a []string, b []string) []string {
+ var ret []string
+ for _, x := range b {
+ if !contains(a, x) {
+ ret = append(ret, x)
+ }
+ }
+ return ret
+}
+
+func replace(l []string, from, to string) {
+ for i := range l {
+ if l[i] == from {
+ l[i] = to
+ }
+ }
+}
+
+var copyOf = android.CopyOf
+
+func anyHavePrefix(l []string, prefix string) bool {
+ for _, x := range l {
+ if strings.HasPrefix(x, prefix) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/dexpreopt/dexpreopt_gen/Android.bp b/dexpreopt/dexpreopt_gen/Android.bp
new file mode 100644
index 0000000..0790391
--- /dev/null
+++ b/dexpreopt/dexpreopt_gen/Android.bp
@@ -0,0 +1,11 @@
+blueprint_go_binary {
+ name: "dexpreopt_gen",
+ srcs: [
+ "dexpreopt_gen.go",
+ ],
+ deps: [
+ "soong-dexpreopt",
+ "blueprint-pathtools",
+ "blueprint-proptools",
+ ],
+}
\ No newline at end of file
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
new file mode 100644
index 0000000..c72f684
--- /dev/null
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -0,0 +1,210 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+
+ "github.com/google/blueprint/pathtools"
+)
+
+var (
+ dexpreoptScriptPath = flag.String("dexpreopt_script", "", "path to output dexpreopt script")
+ stripScriptPath = flag.String("strip_script", "", "path to output strip script")
+ globalConfigPath = flag.String("global", "", "path to global configuration file")
+ moduleConfigPath = flag.String("module", "", "path to module configuration file")
+ outDir = flag.String("out_dir", "", "path to output directory")
+)
+
+type pathContext struct {
+ config android.Config
+}
+
+func (x *pathContext) Fs() pathtools.FileSystem { return pathtools.OsFs }
+func (x *pathContext) Config() android.Config { return x.config }
+func (x *pathContext) AddNinjaFileDeps(...string) {}
+
+func main() {
+ flag.Parse()
+
+ usage := func(err string) {
+ if err != "" {
+ fmt.Println(err)
+ flag.Usage()
+ os.Exit(1)
+ }
+ }
+
+ if flag.NArg() > 0 {
+ usage("unrecognized argument " + flag.Arg(0))
+ }
+
+ if *dexpreoptScriptPath == "" {
+ usage("path to output dexpreopt script is required")
+ }
+
+ if *stripScriptPath == "" {
+ usage("path to output strip script is required")
+ }
+
+ if *globalConfigPath == "" {
+ usage("path to global configuration file is required")
+ }
+
+ if *moduleConfigPath == "" {
+ usage("path to module configuration file is required")
+ }
+
+ ctx := &pathContext{android.TestConfig(*outDir, nil)}
+
+ globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalConfigPath, err)
+ os.Exit(2)
+ }
+
+ moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, *moduleConfigPath)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err)
+ os.Exit(2)
+ }
+
+ // This shouldn't be using *PathForTesting, but it's outside of soong_build so its OK for now.
+ moduleConfig.StripInputPath = android.PathForTesting("$1")
+ moduleConfig.StripOutputPath = android.WritablePathForTesting("$2")
+
+ moduleConfig.DexPath = android.PathForTesting("$1")
+
+ defer func() {
+ if r := recover(); r != nil {
+ switch x := r.(type) {
+ case runtime.Error:
+ panic(x)
+ case error:
+ fmt.Fprintln(os.Stderr, "error:", r)
+ os.Exit(3)
+ default:
+ panic(x)
+ }
+ }
+ }()
+
+ writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath, *stripScriptPath)
+}
+
+func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig,
+ dexpreoptScriptPath, stripScriptPath string) {
+ dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, module)
+ if err != nil {
+ panic(err)
+ }
+
+ installDir := module.BuildPath.InSameDir(ctx, "dexpreopt_install")
+
+ dexpreoptRule.Command().FlagWithArg("rm -rf ", installDir.String())
+ dexpreoptRule.Command().FlagWithArg("mkdir -p ", installDir.String())
+
+ for _, install := range dexpreoptRule.Installs() {
+ installPath := installDir.Join(ctx, strings.TrimPrefix(install.To, "/"))
+ dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
+ dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
+ }
+ dexpreoptRule.Command().Tool(global.Tools.SoongZip).
+ FlagWithArg("-o ", "$2").
+ FlagWithArg("-C ", installDir.String()).
+ FlagWithArg("-D ", installDir.String())
+
+ stripRule, err := dexpreopt.GenerateStripRule(global, module)
+ if err != nil {
+ panic(err)
+ }
+
+ write := func(rule *android.RuleBuilder, file string) {
+ script := &bytes.Buffer{}
+ script.WriteString(scriptHeader)
+ for _, c := range rule.Commands() {
+ script.WriteString(c)
+ script.WriteString("\n\n")
+ }
+
+ depFile := &bytes.Buffer{}
+
+ fmt.Fprint(depFile, `: \`+"\n")
+ for _, tool := range rule.Tools() {
+ fmt.Fprintf(depFile, ` %s \`+"\n", tool)
+ }
+ for _, input := range rule.Inputs() {
+ // Assume the rule that ran the script already has a dependency on the input file passed on the
+ // command line.
+ if input.String() != "$1" {
+ fmt.Fprintf(depFile, ` %s \`+"\n", input)
+ }
+ }
+ depFile.WriteString("\n")
+
+ fmt.Fprintln(script, "rm -f $2.d")
+ // Write the output path unescaped so the $2 gets expanded
+ fmt.Fprintln(script, `echo -n $2 > $2.d`)
+ // Write the rest of the depsfile using cat <<'EOF', which will not do any shell expansion on
+ // the contents to preserve backslashes and special characters in filenames.
+ fmt.Fprintf(script, "cat >> $2.d <<'EOF'\n%sEOF\n", depFile.String())
+
+ err := pathtools.WriteFileIfChanged(file, script.Bytes(), 0755)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ // The written scripts will assume the input is $1 and the output is $2
+ if module.DexPath.String() != "$1" {
+ panic(fmt.Errorf("module.DexPath must be '$1', was %q", module.DexPath))
+ }
+ if module.StripInputPath.String() != "$1" {
+ panic(fmt.Errorf("module.StripInputPath must be '$1', was %q", module.StripInputPath))
+ }
+ if module.StripOutputPath.String() != "$2" {
+ panic(fmt.Errorf("module.StripOutputPath must be '$2', was %q", module.StripOutputPath))
+ }
+
+ write(dexpreoptRule, dexpreoptScriptPath)
+ write(stripRule, stripScriptPath)
+}
+
+const scriptHeader = `#!/bin/bash
+
+err() {
+ errno=$?
+ echo "error: $0:$1 exited with status $errno" >&2
+ echo "error in command:" >&2
+ sed -n -e "$1p" $0 >&2
+ if [ "$errno" -ne 0 ]; then
+ exit $errno
+ else
+ exit 1
+ fi
+}
+
+trap 'err $LINENO' ERR
+
+`
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
new file mode 100644
index 0000000..6dfa9d2
--- /dev/null
+++ b/dexpreopt/dexpreopt_test.go
@@ -0,0 +1,182 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dexpreopt
+
+import (
+ "android/soong/android"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func testModuleConfig(ctx android.PathContext) ModuleConfig {
+ return ModuleConfig{
+ Name: "test",
+ DexLocation: "/system/app/test/test.apk",
+ BuildPath: android.PathForOutput(ctx, "test/test.apk"),
+ DexPath: android.PathForOutput(ctx, "test/dex/test.jar"),
+ UncompressedDex: false,
+ HasApkLibraries: false,
+ PreoptFlags: nil,
+ ProfileClassListing: android.OptionalPath{},
+ ProfileIsTextListing: false,
+ EnforceUsesLibraries: false,
+ OptionalUsesLibraries: nil,
+ UsesLibraries: nil,
+ LibraryPaths: nil,
+ Archs: []android.ArchType{android.Arm},
+ DexPreoptImages: android.Paths{android.PathForTesting("system/framework/arm/boot.art")},
+ PreoptBootClassPathDexFiles: nil,
+ PreoptBootClassPathDexLocations: nil,
+ PreoptExtractedApk: false,
+ NoCreateAppImage: false,
+ ForceCreateAppImage: false,
+ PresignedPrebuilt: false,
+ NoStripping: false,
+ StripInputPath: android.PathForOutput(ctx, "unstripped/test.apk"),
+ StripOutputPath: android.PathForOutput(ctx, "stripped/test.apk"),
+ }
+}
+
+func TestDexPreopt(t *testing.T) {
+ ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+ global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+
+ rule, err := GenerateDexpreoptRule(ctx, global, module)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wantInstalls := android.RuleBuilderInstalls{
+ {android.PathForOutput(ctx, "test/oat/arm/package.odex"), "/system/app/test/oat/arm/test.odex"},
+ {android.PathForOutput(ctx, "test/oat/arm/package.vdex"), "/system/app/test/oat/arm/test.vdex"},
+ }
+
+ if rule.Installs().String() != wantInstalls.String() {
+ t.Errorf("\nwant installs:\n %v\ngot:\n %v", wantInstalls, rule.Installs())
+ }
+}
+
+func TestDexPreoptStrip(t *testing.T) {
+ // Test that we panic if we strip in a configuration where stripping is not allowed.
+ ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+ global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+
+ global.NeverAllowStripping = true
+ module.NoStripping = false
+
+ _, err := GenerateStripRule(global, module)
+ if err == nil {
+ t.Errorf("Expected an error when calling GenerateStripRule on a stripped module")
+ }
+}
+
+func TestDexPreoptSystemOther(t *testing.T) {
+ ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+ global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+
+ global.HasSystemOther = true
+ global.PatternsOnSystemOther = []string{"app/%"}
+
+ rule, err := GenerateDexpreoptRule(ctx, global, module)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wantInstalls := android.RuleBuilderInstalls{
+ {android.PathForOutput(ctx, "test/oat/arm/package.odex"), "/system_other/app/test/oat/arm/test.odex"},
+ {android.PathForOutput(ctx, "test/oat/arm/package.vdex"), "/system_other/app/test/oat/arm/test.vdex"},
+ }
+
+ if rule.Installs().String() != wantInstalls.String() {
+ t.Errorf("\nwant installs:\n %v\ngot:\n %v", wantInstalls, rule.Installs())
+ }
+}
+
+func TestDexPreoptProfile(t *testing.T) {
+ ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+ global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+
+ module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
+
+ rule, err := GenerateDexpreoptRule(ctx, global, module)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wantInstalls := android.RuleBuilderInstalls{
+ {android.PathForOutput(ctx, "test/profile.prof"), "/system/app/test/test.apk.prof"},
+ {android.PathForOutput(ctx, "test/oat/arm/package.art"), "/system/app/test/oat/arm/test.art"},
+ {android.PathForOutput(ctx, "test/oat/arm/package.odex"), "/system/app/test/oat/arm/test.odex"},
+ {android.PathForOutput(ctx, "test/oat/arm/package.vdex"), "/system/app/test/oat/arm/test.vdex"},
+ }
+
+ if rule.Installs().String() != wantInstalls.String() {
+ t.Errorf("\nwant installs:\n %v\ngot:\n %v", wantInstalls, rule.Installs())
+ }
+}
+
+func TestStripDex(t *testing.T) {
+ tests := []struct {
+ name string
+ setup func(global *GlobalConfig, module *ModuleConfig)
+ strip bool
+ }{
+ {
+ name: "default strip",
+ setup: func(global *GlobalConfig, module *ModuleConfig) {},
+ strip: true,
+ },
+ {
+ name: "global no stripping",
+ setup: func(global *GlobalConfig, module *ModuleConfig) { global.DefaultNoStripping = true },
+ strip: false,
+ },
+ {
+ name: "module no stripping",
+ setup: func(global *GlobalConfig, module *ModuleConfig) { module.NoStripping = true },
+ strip: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+
+ ctx := android.PathContextForTesting(android.TestConfig("out", nil), nil)
+ global, module := GlobalConfigForTests(ctx), testModuleConfig(ctx)
+
+ test.setup(&global, &module)
+
+ rule, err := GenerateStripRule(global, module)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if test.strip {
+ want := `zip2zip -i out/unstripped/test.apk -o out/stripped/test.apk -x "classes*.dex"`
+ if len(rule.Commands()) < 1 || !strings.Contains(rule.Commands()[0], want) {
+ t.Errorf("\nwant commands[0] to have:\n %v\ngot:\n %v", want, rule.Commands()[0])
+ }
+ } else {
+ wantCommands := []string{
+ "cp -f out/unstripped/test.apk out/stripped/test.apk",
+ }
+ if !reflect.DeepEqual(rule.Commands(), wantCommands) {
+ t.Errorf("\nwant commands:\n %v\ngot:\n %v", wantCommands, rule.Commands())
+ }
+ }
+ })
+ }
+}
diff --git a/docs/compdb.md b/docs/compdb.md
new file mode 100644
index 0000000..68927ca
--- /dev/null
+++ b/docs/compdb.md
@@ -0,0 +1,27 @@
+# Compdb (compile\_commands.json) Generator
+
+Soong can generate compdb files. This is intended for use with editing tools
+such as YouCompleteMe and other libclang based completers.
+
+compdb file generation is enabled via environment variable:
+
+```bash
+$ export SOONG_GEN_COMPDB=1
+$ export SOONG_GEN_COMPDB_DEBUG=1
+```
+
+One can make soong generate a symlink to the compdb file using an environment
+variable:
+
+```bash
+$ export SOONG_LINK_COMPDB_TO=$ANDROID_HOST_OUT
+```
+
+You can then trigger an empty build:
+
+```bash
+$ make nothing
+```
+
+Note that if you build using mm or other limited makes with these environment
+variables set the compdb will only include files in included modules.
diff --git a/finder/fs/fs.go b/finder/fs/fs.go
index 9c138cd..071f764 100644
--- a/finder/fs/fs.go
+++ b/finder/fs/fs.go
@@ -243,7 +243,6 @@
if parentPath == path {
err = fmt.Errorf("Internal error: %v yields itself as a parent", path)
panic(err.Error())
- return "", fmt.Errorf("Internal error: %v yields itself as a parent", path)
}
parentPath, err = m.followLinks(parentPath, true, count)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 42be88f..87e6747 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "io"
"strings"
"github.com/google/blueprint"
@@ -28,6 +29,8 @@
)
func init() {
+ android.RegisterModuleType("genrule_defaults", defaultsFactory)
+
android.RegisterModuleType("gensrcs", GenSrcsFactory)
android.RegisterModuleType("genrule", GenRuleFactory)
}
@@ -46,16 +49,17 @@
GeneratedDeps() android.Paths
}
+// Alias for android.HostToolProvider
+// Deprecated: use android.HostToolProvider instead.
type HostToolProvider interface {
- HostToolPath() android.OptionalPath
+ android.HostToolProvider
}
type hostToolDependencyTag struct {
blueprint.BaseDependencyTag
+ label string
}
-var hostToolDepTag hostToolDependencyTag
-
type generatorProperties struct {
// The command to run on one or more input files. Cmd supports substitution of a few variables
// (the actual substitution is implemented in GenerateAndroidBuildActions below)
@@ -63,7 +67,7 @@
// Available variables for substitution:
//
// $(location): the path to the first entry in tools or tool_files
- // $(location <label>): the path to the tool or tool_file with name <label>
+ // $(location <label>): the path to the tool, tool_file, input or output with name <label>
// $(in): one or more input files
// $(out): a single output file
// $(depfile): a file to which dependencies will be written, if the depfile property is set to true
@@ -82,17 +86,21 @@
Tools []string
// Local file that is used as the tool
- Tool_files []string
+ Tool_files []string `android:"path"`
// List of directories to export generated headers from
Export_include_dirs []string
// list of input files
- Srcs []string
+ Srcs []string `android:"path,arch_variant"`
+
+ // input files to exclude
+ Exclude_srcs []string `android:"path,arch_variant"`
}
type Module struct {
android.ModuleBase
+ android.DefaultableModuleBase
// For other packages to make their own genrules with extra
// properties
@@ -102,13 +110,16 @@
taskGenerator taskFunc
- deps android.Paths
- rule blueprint.Rule
+ deps android.Paths
+ rule blueprint.Rule
+ rawCommand string
exportedIncludeDirs android.Paths
outputFiles android.Paths
outputDeps android.Paths
+
+ subName string
}
type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) generateTask
@@ -137,18 +148,22 @@
}
func (g *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
- android.ExtractSourcesDeps(ctx, g.properties.Srcs)
- android.ExtractSourcesDeps(ctx, g.properties.Tool_files)
if g, ok := ctx.Module().(*Module); ok {
- if len(g.properties.Tools) > 0 {
+ for _, tool := range g.properties.Tools {
+ tag := hostToolDependencyTag{label: tool}
+ if m := android.SrcIsModule(tool); m != "" {
+ tool = m
+ }
ctx.AddFarVariationDependencies([]blueprint.Variation{
- {"arch", ctx.Config().BuildOsVariant},
- }, hostToolDepTag, g.properties.Tools...)
+ {Mutator: "arch", Variation: ctx.Config().BuildOsVariant},
+ }, tag, tool)
}
}
}
func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ g.subName = ctx.ModuleSubDir()
+
if len(g.properties.Export_include_dirs) > 0 {
for _, dir := range g.properties.Export_include_dirs {
g.exportedIncludeDirs = append(g.exportedIncludeDirs,
@@ -158,18 +173,31 @@
g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, ""))
}
- tools := map[string]android.Path{}
+ locationLabels := map[string][]string{}
+ firstLabel := ""
+
+ addLocationLabel := func(label string, paths []string) {
+ if firstLabel == "" {
+ firstLabel = label
+ }
+ if _, exists := locationLabels[label]; !exists {
+ locationLabels[label] = paths
+ } else {
+ ctx.ModuleErrorf("multiple labels for %q, %q and %q",
+ label, strings.Join(locationLabels[label], " "), strings.Join(paths, " "))
+ }
+ }
if len(g.properties.Tools) > 0 {
+ seenTools := make(map[string]bool)
+
ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
- switch ctx.OtherModuleDependencyTag(module) {
- case android.SourceDepTag:
- // Nothing to do
- case hostToolDepTag:
+ switch tag := ctx.OtherModuleDependencyTag(module).(type) {
+ case hostToolDependencyTag:
tool := ctx.OtherModuleName(module)
var path android.OptionalPath
- if t, ok := module.(HostToolProvider); ok {
+ if t, ok := module.(android.HostToolProvider); ok {
if !t.(android.Module).Enabled() {
if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{tool})
@@ -193,51 +221,89 @@
if path.Valid() {
g.deps = append(g.deps, path.Path())
- if _, exists := tools[tool]; !exists {
- tools[tool] = path.Path()
- } else {
- ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], path.Path().String())
- }
+ addLocationLabel(tag.label, []string{path.Path().String()})
+ seenTools[tag.label] = true
} else {
ctx.ModuleErrorf("host tool %q missing output file", tool)
}
- default:
- ctx.ModuleErrorf("unknown dependency on %q", ctx.OtherModuleName(module))
}
})
+
+ // If AllowMissingDependencies is enabled, the build will not have stopped when
+ // AddFarVariationDependencies was called on a missing tool, which will result in nonsensical
+ // "cmd: unknown location label ..." errors later. Add a dummy file to the local label. The
+ // command that uses this dummy file will never be executed because the rule will be replaced with
+ // an android.Error rule reporting the missing dependencies.
+ if ctx.Config().AllowMissingDependencies() {
+ for _, tool := range g.properties.Tools {
+ if !seenTools[tool] {
+ addLocationLabel(tool, []string{"***missing tool " + tool + "***"})
+ }
+ }
+ }
}
if ctx.Failed() {
return
}
- toolFiles := ctx.ExpandSources(g.properties.Tool_files, nil)
- for _, tool := range toolFiles {
- g.deps = append(g.deps, tool)
- if _, exists := tools[tool.Rel()]; !exists {
- tools[tool.Rel()] = tool
+ for _, toolFile := range g.properties.Tool_files {
+ paths := android.PathsForModuleSrc(ctx, []string{toolFile})
+ g.deps = append(g.deps, paths...)
+ addLocationLabel(toolFile, paths.Strings())
+ }
+
+ var srcFiles android.Paths
+ for _, in := range g.properties.Srcs {
+ paths, missingDeps := android.PathsAndMissingDepsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs)
+ if len(missingDeps) > 0 {
+ if !ctx.Config().AllowMissingDependencies() {
+ panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
+ missingDeps))
+ }
+
+ // If AllowMissingDependencies is enabled, the build will not have stopped when
+ // the dependency was added on a missing SourceFileProducer module, which will result in nonsensical
+ // "cmd: label ":..." has no files" errors later. Add a dummy file to the local label. The
+ // command that uses this dummy file will never be executed because the rule will be replaced with
+ // an android.Error rule reporting the missing dependencies.
+ ctx.AddMissingDependencies(missingDeps)
+ addLocationLabel(in, []string{"***missing srcs " + in + "***"})
} else {
- ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool.Rel()], tool.Rel())
+ srcFiles = append(srcFiles, paths...)
+ addLocationLabel(in, paths.Strings())
}
}
+ task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
+
+ for _, out := range task.out {
+ addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
+ }
+
referencedDepfile := false
- srcFiles := ctx.ExpandSources(g.properties.Srcs, nil)
- task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
-
rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
+ // report the error directly without returning an error to android.Expand to catch multiple errors in a
+ // single run
+ reportError := func(fmt string, args ...interface{}) (string, error) {
+ ctx.PropertyErrorf("cmd", fmt, args...)
+ return "SOONG_ERROR", nil
+ }
+
switch name {
case "location":
- if len(g.properties.Tools) == 0 && len(toolFiles) == 0 {
- return "", fmt.Errorf("at least one `tools` or `tool_files` is required if $(location) is used")
+ if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
+ return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
}
-
- if len(g.properties.Tools) > 0 {
- return tools[g.properties.Tools[0]].String(), nil
- } else {
- return tools[toolFiles[0].Rel()].String(), nil
+ paths := locationLabels[firstLabel]
+ if len(paths) == 0 {
+ return reportError("default label %q has no files", firstLabel)
+ } else if len(paths) > 1 {
+ return reportError("default label %q has multiple files, use $(locations %s) to reference it",
+ firstLabel, firstLabel)
}
+ return locationLabels[firstLabel][0], nil
case "in":
return "${in}", nil
case "out":
@@ -245,7 +311,7 @@
case "depfile":
referencedDepfile = true
if !Bool(g.properties.Depfile) {
- return "", fmt.Errorf("$(depfile) used without depfile property")
+ return reportError("$(depfile) used without depfile property")
}
return "__SBOX_DEPFILE__", nil
case "genDir":
@@ -253,25 +319,42 @@
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if tool, ok := tools[label]; ok {
- return tool.String(), nil
+ if paths, ok := locationLabels[label]; ok {
+ if len(paths) == 0 {
+ return reportError("label %q has no files", label)
+ } else if len(paths) > 1 {
+ return reportError("label %q has multiple files, use $(locations %s) to reference it",
+ label, label)
+ }
+ return paths[0], nil
} else {
- return "", fmt.Errorf("unknown location label %q", label)
+ return reportError("unknown location label %q", label)
}
+ } else if strings.HasPrefix(name, "locations ") {
+ label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
+ if paths, ok := locationLabels[label]; ok {
+ if len(paths) == 0 {
+ return reportError("label %q has no files", label)
+ }
+ return strings.Join(paths, " "), nil
+ } else {
+ return reportError("unknown locations label %q", label)
+ }
+ } else {
+ return reportError("unknown variable '$(%s)'", name)
}
- return "", fmt.Errorf("unknown variable '$(%s)'", name)
}
})
- if Bool(g.properties.Depfile) && !referencedDepfile {
- ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
- }
-
if err != nil {
ctx.PropertyErrorf("cmd", "%s", err.Error())
return
}
+ if Bool(g.properties.Depfile) && !referencedDepfile {
+ ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
+ }
+
// tell the sbox command which directory to use as its sandbox root
buildDir := android.PathForOutput(ctx).String()
sandboxPath := shared.TempDirForOutDir(buildDir)
@@ -286,6 +369,7 @@
genDir := android.PathForModuleGen(ctx)
// Escape the command for the shell
rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
+ g.rawCommand = rawCommand
sandboxCommand := fmt.Sprintf("$sboxCmd --sandbox-path %s --output-root %s -c %s %s $allouts",
sandboxPath, genDir, rawCommand, depfilePlaceholder)
@@ -343,6 +427,38 @@
g.outputDeps = append(g.outputDeps, task.out[0])
}
+// Collect information for opening IDE project files in java/jdeps.go.
+func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
+ for _, src := range g.properties.Srcs {
+ if strings.HasPrefix(src, ":") {
+ src = strings.Trim(src, ":")
+ dpInfo.Deps = append(dpInfo.Deps, src)
+ }
+ }
+}
+
+func (g *Module) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Include: "$(BUILD_PHONY_PACKAGE)",
+ Class: "FAKE",
+ OutputFile: android.OptionalPathForPath(g.outputFiles[0]),
+ SubName: g.subName,
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=", strings.Join(g.outputFiles.Strings(), " "))
+ },
+ },
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ android.WriteAndroidMkData(w, data)
+ if data.SubName != "" {
+ fmt.Fprintln(w, ".PHONY:", name)
+ fmt.Fprintln(w, name, ":", name+g.subName)
+ }
+ },
+ }
+}
+
func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module {
module := &Module{
taskGenerator: taskGenerator,
@@ -394,7 +510,7 @@
}
// escape the command in case for example it contains '#', an odd number of '"', etc
- command = fmt.Sprintf("bash -c %v", proptools.ShellEscape([]string{command})[0])
+ command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command))
commands = append(commands, command)
}
fullCommand := strings.Join(commands, " && ")
@@ -446,13 +562,43 @@
func GenRuleFactory() android.Module {
m := NewGenRule()
android.InitAndroidModule(m)
+ android.InitDefaultableModule(m)
return m
}
type genRuleProperties struct {
// names of the output files that will be generated
- Out []string
+ Out []string `android:"arch_variant"`
}
var Bool = proptools.Bool
var String = proptools.String
+
+//
+// Defaults
+//
+type Defaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+func defaultsFactory() android.Module {
+ return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+ module := &Defaults{}
+
+ module.AddProperties(props...)
+ module.AddProperties(
+ &generatorProperties{},
+ &genRuleProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+
+ return module
+}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
new file mode 100644
index 0000000..0b6952f
--- /dev/null
+++ b/genrule/genrule_test.go
@@ -0,0 +1,561 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package genrule
+
+import (
+ "io/ioutil"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint/proptools"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "genrule_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func testContext(config android.Config, bp string,
+ fs map[string][]byte) *android.TestContext {
+
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
+ ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(GenRuleFactory))
+ ctx.RegisterModuleType("genrule_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
+ ctx.RegisterModuleType("tool", android.ModuleFactoryAdaptor(toolFactory))
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.Register()
+
+ bp += `
+ tool {
+ name: "tool",
+ }
+
+ filegroup {
+ name: "tool_files",
+ srcs: [
+ "tool_file1",
+ "tool_file2",
+ ],
+ }
+
+ filegroup {
+ name: "1tool_file",
+ srcs: [
+ "tool_file1",
+ ],
+ }
+
+ filegroup {
+ name: "ins",
+ srcs: [
+ "in1",
+ "in2",
+ ],
+ }
+
+ filegroup {
+ name: "1in",
+ srcs: [
+ "in1",
+ ],
+ }
+
+ filegroup {
+ name: "empty",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "tool": nil,
+ "tool_file1": nil,
+ "tool_file2": nil,
+ "in1": nil,
+ "in2": nil,
+ }
+
+ for k, v := range fs {
+ mockFS[k] = v
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ return ctx
+}
+
+func TestGenruleCmd(t *testing.T) {
+ testcases := []struct {
+ name string
+ prop string
+
+ allowMissingDependencies bool
+
+ err string
+ expect string
+ }{
+ {
+ name: "empty location tool",
+ prop: `
+ tools: ["tool"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "empty location tool2",
+ prop: `
+ tools: [":tool"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "empty location tool file",
+ prop: `
+ tool_files: ["tool_file1"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "empty location tool file fg",
+ prop: `
+ tool_files: [":1tool_file"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "empty location tool and tool file",
+ prop: `
+ tools: ["tool"],
+ tool_files: ["tool_file1"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool",
+ prop: `
+ tools: ["tool"],
+ out: ["out"],
+ cmd: "$(location tool) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool2",
+ prop: `
+ tools: [":tool"],
+ out: ["out"],
+ cmd: "$(location :tool) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool file",
+ prop: `
+ tool_files: ["tool_file1"],
+ out: ["out"],
+ cmd: "$(location tool_file1) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool file fg",
+ prop: `
+ tool_files: [":1tool_file"],
+ out: ["out"],
+ cmd: "$(location :1tool_file) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool files",
+ prop: `
+ tool_files: [":tool_files"],
+ out: ["out"],
+ cmd: "$(locations :tool_files) > $(out)",
+ `,
+ expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "in1",
+ prop: `
+ srcs: ["in1"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "in1 fg",
+ prop: `
+ srcs: [":1in"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "ins",
+ prop: `
+ srcs: ["in1", "in2"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "ins fg",
+ prop: `
+ srcs: [":ins"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location in1",
+ prop: `
+ srcs: ["in1"],
+ out: ["out"],
+ cmd: "cat $(location in1) > $(out)",
+ `,
+ expect: "cat in1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location in1 fg",
+ prop: `
+ srcs: [":1in"],
+ out: ["out"],
+ cmd: "cat $(location :1in) > $(out)",
+ `,
+ expect: "cat in1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location ins",
+ prop: `
+ srcs: ["in1", "in2"],
+ out: ["out"],
+ cmd: "cat $(location in1) > $(out)",
+ `,
+ expect: "cat in1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location ins fg",
+ prop: `
+ srcs: [":ins"],
+ out: ["out"],
+ cmd: "cat $(locations :ins) > $(out)",
+ `,
+ expect: "cat in1 in2 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "outs",
+ prop: `
+ out: ["out", "out2"],
+ cmd: "echo foo > $(out)",
+ `,
+ expect: "echo foo > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location out",
+ prop: `
+ out: ["out", "out2"],
+ cmd: "echo foo > $(location out2)",
+ `,
+ expect: "echo foo > __SBOX_OUT_DIR__/out2",
+ },
+ {
+ name: "depfile",
+ prop: `
+ out: ["out"],
+ depfile: true,
+ cmd: "echo foo > $(out) && touch $(depfile)",
+ `,
+ expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
+ },
+ {
+ name: "gendir",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
+ `,
+ expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
+ },
+
+ {
+ name: "error empty location",
+ prop: `
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ err: "at least one `tools` or `tool_files` is required if $(location) is used",
+ },
+ {
+ name: "error empty location no files",
+ prop: `
+ tool_files: [":empty"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ err: `default label ":empty" has no files`,
+ },
+ {
+ name: "error empty location multiple files",
+ prop: `
+ tool_files: [":tool_files"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ err: `default label ":tool_files" has multiple files`,
+ },
+ {
+ name: "error location",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(location missing)",
+ `,
+ err: `unknown location label "missing"`,
+ },
+ {
+ name: "error locations",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(locations missing)",
+ `,
+ err: `unknown locations label "missing"`,
+ },
+ {
+ name: "error location no files",
+ prop: `
+ out: ["out"],
+ srcs: [":empty"],
+ cmd: "echo $(location :empty) > $(out)",
+ `,
+ err: `label ":empty" has no files`,
+ },
+ {
+ name: "error locations no files",
+ prop: `
+ out: ["out"],
+ srcs: [":empty"],
+ cmd: "echo $(locations :empty) > $(out)",
+ `,
+ err: `label ":empty" has no files`,
+ },
+ {
+ name: "error location multiple files",
+ prop: `
+ out: ["out"],
+ srcs: [":ins"],
+ cmd: "echo $(location :ins) > $(out)",
+ `,
+ err: `label ":ins" has multiple files`,
+ },
+ {
+ name: "error variable",
+ prop: `
+ out: ["out"],
+ srcs: ["in1"],
+ cmd: "echo $(foo) > $(out)",
+ `,
+ err: `unknown variable '$(foo)'`,
+ },
+ {
+ name: "error depfile",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(out) && touch $(depfile)",
+ `,
+ err: "$(depfile) used without depfile property",
+ },
+ {
+ name: "error no depfile",
+ prop: `
+ out: ["out"],
+ depfile: true,
+ cmd: "echo foo > $(out)",
+ `,
+ err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
+ },
+ {
+ name: "error no out",
+ prop: `
+ cmd: "echo foo > $(out)",
+ `,
+ err: "must have at least one output file",
+ },
+ {
+ name: "srcs allow missing dependencies",
+ prop: `
+ srcs: [":missing"],
+ out: ["out"],
+ cmd: "cat $(location :missing) > $(out)",
+ `,
+
+ allowMissingDependencies: true,
+
+ expect: "cat ***missing srcs :missing*** > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool allow missing dependencies",
+ prop: `
+ tools: [":missing"],
+ out: ["out"],
+ cmd: "$(location :missing) > $(out)",
+ `,
+
+ allowMissingDependencies: true,
+
+ expect: "***missing tool :missing*** > __SBOX_OUT_FILES__",
+ },
+ }
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ config := android.TestArchConfig(buildDir, nil)
+ bp := "genrule {\n"
+ bp += "name: \"gen\",\n"
+ bp += test.prop
+ bp += "}\n"
+
+ config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
+
+ ctx := testContext(config, bp, nil)
+ ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if errs == nil {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+ if errs == nil && test.err != "" {
+ t.Fatalf("want error %q, got no error", test.err)
+ } else if errs != nil && test.err == "" {
+ android.FailIfErrored(t, errs)
+ } else if test.err != "" {
+ if len(errs) != 1 {
+ t.Errorf("want 1 error, got %d errors:", len(errs))
+ for _, err := range errs {
+ t.Errorf(" %s", err.Error())
+ }
+ t.FailNow()
+ }
+ if !strings.Contains(errs[0].Error(), test.err) {
+ t.Fatalf("want %q, got %q", test.err, errs[0].Error())
+ }
+ return
+ }
+
+ gen := ctx.ModuleForTests("gen", "").Module().(*Module)
+ if g, w := gen.rawCommand, "'"+test.expect+"'"; w != g {
+ t.Errorf("want %q, got %q", w, g)
+ }
+ })
+ }
+
+}
+
+func TestGenruleDefaults(t *testing.T) {
+ config := android.TestArchConfig(buildDir, nil)
+ bp := `
+ genrule_defaults {
+ name: "gen_defaults1",
+ cmd: "cp $(in) $(out)",
+ }
+
+ genrule_defaults {
+ name: "gen_defaults2",
+ srcs: ["in1"],
+ }
+
+ genrule {
+ name: "gen",
+ out: ["out"],
+ defaults: ["gen_defaults1", "gen_defaults2"],
+ }
+ `
+ ctx := testContext(config, bp, nil)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if errs == nil {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+ if errs != nil {
+ t.Fatal(errs)
+ }
+ gen := ctx.ModuleForTests("gen", "").Module().(*Module)
+
+ expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
+ if gen.rawCommand != expectedCmd {
+ t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommand)
+ }
+
+ expectedSrcs := []string{"in1"}
+ if !reflect.DeepEqual(expectedSrcs, gen.properties.Srcs) {
+ t.Errorf("Expected srcs: %q, actual: %q", expectedSrcs, gen.properties.Srcs)
+ }
+}
+
+type testTool struct {
+ android.ModuleBase
+ outputFile android.Path
+}
+
+func toolFactory() android.Module {
+ module := &testTool{}
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
+func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ t.outputFile = android.PathForTesting("out", ctx.ModuleName())
+}
+
+func (t *testTool) HostToolPath() android.OptionalPath {
+ return android.OptionalPathForPath(t.outputFile)
+}
+
+var _ android.HostToolProvider = (*testTool)(nil)
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..cc328e0
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,9 @@
+module android/soong
+
+require github.com/golang/protobuf v0.0.0
+
+require github.com/google/blueprint v0.0.0
+
+replace github.com/golang/protobuf v0.0.0 => ../../external/golang-protobuf
+
+replace github.com/google/blueprint v0.0.0 => ../blueprint
diff --git a/jar/jar.go b/jar/jar.go
index 653e5ee..fa0e693 100644
--- a/jar/jar.go
+++ b/jar/jar.go
@@ -17,7 +17,6 @@
import (
"bytes"
"fmt"
- "io/ioutil"
"os"
"strings"
"time"
@@ -81,10 +80,9 @@
return dirHeader
}
-// Convert manifest source path to zip header and contents. If path is empty uses a default
-// manifest.
-func ManifestFileContents(src string) (*zip.FileHeader, []byte, error) {
- b, err := manifestContents(src)
+// Create a manifest zip header and contents using the provided contents if any.
+func ManifestFileContents(contents []byte) (*zip.FileHeader, []byte, error) {
+ b, err := manifestContents(contents)
if err != nil {
return nil, nil, err
}
@@ -100,26 +98,16 @@
return fh, b, nil
}
-// Convert manifest source path to contents. If path is empty uses a default manifest.
-func manifestContents(src string) ([]byte, error) {
- var givenBytes []byte
- var err error
-
- if src != "" {
- givenBytes, err = ioutil.ReadFile(src)
- if err != nil {
- return nil, err
- }
- }
-
+// Create manifest contents, using the provided contents if any.
+func manifestContents(contents []byte) ([]byte, error) {
manifestMarker := []byte("Manifest-Version:")
header := append(manifestMarker, []byte(" 1.0\nCreated-By: soong_zip\n")...)
var finalBytes []byte
- if !bytes.Contains(givenBytes, manifestMarker) {
- finalBytes = append(append(header, givenBytes...), byte('\n'))
+ if !bytes.Contains(contents, manifestMarker) {
+ finalBytes = append(append(header, contents...), byte('\n'))
} else {
- finalBytes = givenBytes
+ finalBytes = contents
}
return finalBytes, nil
diff --git a/java/OWNERS b/java/OWNERS
new file mode 100644
index 0000000..d68a5b0
--- /dev/null
+++ b/java/OWNERS
@@ -0,0 +1 @@
+per-file dexpreopt.go = ngeoffray@google.com,calin@google.com,mathieuc@google.com
diff --git a/java/aapt2.go b/java/aapt2.go
index 70c7507..bcc8e97 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -109,9 +109,35 @@
})
}
+var aapt2CompileZipRule = pctx.AndroidStaticRule("aapt2CompileZip",
+ blueprint.RuleParams{
+ Command: `${config.ZipSyncCmd} -d $resZipDir $in && ` +
+ `${config.Aapt2Cmd} compile -o $out $cFlags --legacy --dir $resZipDir`,
+ CommandDeps: []string{
+ "${config.Aapt2Cmd}",
+ "${config.ZipSyncCmd}",
+ },
+ }, "cFlags", "resZipDir")
+
+func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: aapt2CompileZipRule,
+ Description: "aapt2 compile zip",
+ Input: zip,
+ Output: flata,
+ Args: map[string]string{
+ // Always set --pseudo-localize, it will be stripped out later for release
+ // builds that don't want it.
+ "cFlags": "--pseudo-localize",
+ "resZipDir": android.PathForModuleOut(ctx, "aapt2", "reszip", flata.Base()).String(),
+ },
+ })
+}
+
var aapt2LinkRule = pctx.AndroidStaticRule("aapt2Link",
blueprint.RuleParams{
- Command: `${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions ` +
+ Command: `rm -rf $genDir && ` +
+ `${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions ` +
`--output-text-symbols ${rTxt} $inFlags && ` +
`${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir &&` +
`${config.ExtractJarPackagesCmd} -i $genJar -o $extraPackages --prefix '--extra-packages '`,
@@ -135,7 +161,7 @@
func aapt2Link(ctx android.ModuleContext,
packageRes, genJar, proguardOptions, rTxt, extraPackages android.WritablePath,
flags []string, deps android.Paths,
- compiledRes, compiledOverlay android.Paths) {
+ compiledRes, compiledOverlay android.Paths, splitPackages android.WritablePaths) {
genDir := android.PathForModuleGen(ctx, "aapt2", "R")
@@ -170,12 +196,14 @@
inFlags = append(inFlags, "-R", "@"+overlayFileList.String())
}
+ implicitOutputs := append(splitPackages, proguardOptions, genJar, rTxt, extraPackages)
+
ctx.Build(pctx, android.BuildParams{
Rule: aapt2LinkRule,
Description: "aapt2 link",
Implicits: deps,
Output: packageRes,
- ImplicitOutputs: android.WritablePaths{proguardOptions, genJar, rTxt, extraPackages},
+ ImplicitOutputs: implicitOutputs,
Args: map[string]string{
"flags": strings.Join(flags, " "),
"inFlags": strings.Join(inFlags, " "),
@@ -187,3 +215,18 @@
},
})
}
+
+var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert",
+ blueprint.RuleParams{
+ Command: `${config.Aapt2Cmd} convert --output-format proto $in -o $out`,
+ CommandDeps: []string{"${config.Aapt2Cmd}"},
+ })
+
+func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: aapt2ConvertRule,
+ Input: in,
+ Output: out,
+ Description: "convert to proto",
+ })
+}
diff --git a/java/aar.go b/java/aar.go
index 66f1cab..6273a9b 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -16,6 +16,8 @@
import (
"android/soong/android"
+ "fmt"
+ "path/filepath"
"strings"
"github.com/google/blueprint"
@@ -26,7 +28,9 @@
Dependency
ExportPackage() android.Path
ExportedProguardFlagFiles() android.Paths
+ ExportedRRODirs() []rroDir
ExportedStaticPackages() android.Paths
+ ExportedManifest() android.Path
}
func init() {
@@ -46,16 +50,25 @@
// flags passed to aapt when creating the apk
Aaptflags []string
+ // include all resource configurations, not just the product-configured
+ // ones.
+ Aapt_include_all_resources *bool
+
// list of directories relative to the Blueprints file containing assets.
- // Defaults to "assets"
+ // Defaults to ["assets"] if a directory called assets exists. Set to []
+ // to disable the default.
Asset_dirs []string
// list of directories relative to the Blueprints file containing
- // Android resources
+ // Android resources. Defaults to ["res"] if a directory called res exists.
+ // Set to [] to disable the default.
Resource_dirs []string
+ // list of zip files containing Android resources.
+ Resource_zips []string `android:"path"`
+
// path to AndroidManifest.xml. If unset, defaults to "AndroidManifest.xml".
- Manifest *string
+ Manifest *string `android:"path"`
}
type aapt struct {
@@ -63,19 +76,41 @@
exportPackage android.Path
manifestPath android.Path
proguardOptionsFile android.Path
- rroDirs android.Paths
+ rroDirs []rroDir
rTxt android.Path
extraAaptPackagesFile android.Path
+ noticeFile android.OptionalPath
+ isLibrary bool
+ uncompressedJNI bool
+ useEmbeddedDex bool
+ usesNonSdkApis bool
+
+ splitNames []string
+ splits []split
aaptProperties aaptProperties
}
+type split struct {
+ name string
+ suffix string
+ path android.Path
+}
+
func (a *aapt) ExportPackage() android.Path {
return a.exportPackage
}
-func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkVersion string) (flags []string, deps android.Paths,
- resDirs, overlayDirs []globbedResourceDir, overlayFiles, rroDirs android.Paths, manifestPath android.Path) {
+func (a *aapt) ExportedRRODirs() []rroDir {
+ return a.rroDirs
+}
+
+func (a *aapt) ExportedManifest() android.Path {
+ return a.manifestPath
+}
+
+func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (flags []string,
+ deps android.Paths, resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
hasVersionCode := false
hasVersionName := false
@@ -97,6 +132,7 @@
// Find implicit or explicit asset and resource dirs
assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
+ resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
var linkDeps android.Paths
@@ -116,29 +152,23 @@
assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
}
- // App manifest file
- manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
- manifestPath = android.PathForModuleSrc(ctx, manifestFile)
+ assetDirStrings := assetDirs.Strings()
+ if a.noticeFile.Valid() {
+ assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
+ assetFiles = append(assetFiles, a.noticeFile.Path())
+ }
+
linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
linkDeps = append(linkDeps, manifestPath)
- linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
+ linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
linkDeps = append(linkDeps, assetFiles...)
- transitiveStaticLibs, libDeps, libFlags := aaptLibs(ctx, sdkVersion)
-
- overlayFiles = append(overlayFiles, transitiveStaticLibs...)
- linkDeps = append(linkDeps, libDeps...)
- linkFlags = append(linkFlags, libFlags...)
-
// SDK version flags
- switch sdkVersion {
- case "", "current", "system_current", "test_current":
- sdkVersion = proptools.NinjaEscape([]string{ctx.Config().DefaultAppTargetSdk()})[0]
- }
+ minSdkVersion := sdkVersionOrDefault(ctx, sdkContext.minSdkVersion())
- linkFlags = append(linkFlags, "--min-sdk-version "+sdkVersion)
- linkFlags = append(linkFlags, "--target-sdk-version "+sdkVersion)
+ linkFlags = append(linkFlags, "--min-sdk-version "+minSdkVersion)
+ linkFlags = append(linkFlags, "--target-sdk-version "+minSdkVersion)
// Version code
if !hasVersionCode {
@@ -155,26 +185,39 @@
} else {
versionName = ctx.Config().AppsDefaultVersionName()
}
- versionName = proptools.NinjaEscape([]string{versionName})[0]
+ versionName = proptools.NinjaEscape(versionName)
linkFlags = append(linkFlags, "--version-name ", versionName)
}
- return linkFlags, linkDeps, resDirs, overlayDirs, overlayFiles, rroDirs, manifestPath
+ return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
}
-func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkVersion string) {
- if !ctx.Config().UnbundledBuild() {
- sdkDep := decodeSdkDep(ctx, sdkVersion)
- if sdkDep.frameworkResModule != "" {
- ctx.AddDependency(ctx.Module(), frameworkResTag, sdkDep.frameworkResModule)
- }
+func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkContext sdkContext) {
+ sdkDep := decodeSdkDep(ctx, sdkContext)
+ if sdkDep.frameworkResModule != "" {
+ ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
}
}
-func (a *aapt) buildActions(ctx android.ModuleContext, sdkVersion string, extraLinkFlags ...string) {
- linkFlags, linkDeps, resDirs, overlayDirs, overlayFiles, rroDirs, manifestPath := a.aapt2Flags(ctx, sdkVersion)
+func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, extraLinkFlags ...string) {
+ transitiveStaticLibs, staticLibManifests, staticRRODirs, libDeps, libFlags := aaptLibs(ctx, sdkContext)
+ // App manifest file
+ manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
+ manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
+
+ manifestPath := manifestMerger(ctx, manifestSrcPath, sdkContext, staticLibManifests, a.isLibrary,
+ a.uncompressedJNI, a.useEmbeddedDex, a.usesNonSdkApis)
+
+ linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath)
+
+ rroDirs = append(rroDirs, staticRRODirs...)
+ linkFlags = append(linkFlags, libFlags...)
+ linkDeps = append(linkDeps, libDeps...)
linkFlags = append(linkFlags, extraLinkFlags...)
+ if a.isLibrary {
+ linkFlags = append(linkFlags, "--static-lib")
+ }
packageRes := android.PathForModuleOut(ctx, "package-res.apk")
srcJar := android.PathForModuleGen(ctx, "R.jar")
@@ -183,18 +226,62 @@
// This file isn't used by Soong, but is generated for exporting
extraPackages := android.PathForModuleOut(ctx, "extra_packages")
- var compiledRes, compiledOverlay android.Paths
+ var compiledResDirs []android.Paths
for _, dir := range resDirs {
- compiledRes = append(compiledRes, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
+ compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files).Paths())
}
+
+ for i, zip := range resZips {
+ flata := android.PathForModuleOut(ctx, fmt.Sprintf("reszip.%d.flata", i))
+ aapt2CompileZip(ctx, flata, zip)
+ compiledResDirs = append(compiledResDirs, android.Paths{flata})
+ }
+
+ var compiledRes, compiledOverlay android.Paths
+
+ compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
+
+ if len(transitiveStaticLibs) > 0 {
+ // If we are using static android libraries, every source file becomes an overlay.
+ // This is to emulate old AAPT behavior which simulated library support.
+ for _, compiledResDir := range compiledResDirs {
+ compiledOverlay = append(compiledOverlay, compiledResDir...)
+ }
+ } else if a.isLibrary {
+ // Otherwise, for a static library we treat all the resources equally with no overlay.
+ for _, compiledResDir := range compiledResDirs {
+ compiledRes = append(compiledRes, compiledResDir...)
+ }
+ } else if len(compiledResDirs) > 0 {
+ // Without static libraries, the first directory is our directory, which can then be
+ // overlaid by the rest.
+ compiledRes = append(compiledRes, compiledResDirs[0]...)
+ for _, compiledResDir := range compiledResDirs[1:] {
+ compiledOverlay = append(compiledOverlay, compiledResDir...)
+ }
+ }
+
for _, dir := range overlayDirs {
compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
}
- compiledOverlay = append(compiledOverlay, overlayFiles...)
+ var splitPackages android.WritablePaths
+ var splits []split
+
+ for _, s := range a.splitNames {
+ suffix := strings.Replace(s, ",", "_", -1)
+ path := android.PathForModuleOut(ctx, "package_"+suffix+".apk")
+ linkFlags = append(linkFlags, "--split", path.String()+":"+s)
+ splitPackages = append(splitPackages, path)
+ splits = append(splits, split{
+ name: s,
+ suffix: suffix,
+ path: path,
+ })
+ }
aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, extraPackages,
- linkFlags, linkDeps, compiledRes, compiledOverlay)
+ linkFlags, linkDeps, compiledRes, compiledOverlay, splitPackages)
a.aaptSrcJar = srcJar
a.exportPackage = packageRes
@@ -203,17 +290,18 @@
a.rroDirs = rroDirs
a.extraAaptPackagesFile = extraPackages
a.rTxt = rTxt
+ a.splits = splits
}
// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
-func aaptLibs(ctx android.ModuleContext, sdkVersion string) (transitiveStaticLibs, deps android.Paths,
- flags []string) {
+func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, staticLibManifests android.Paths,
+ staticRRODirs []rroDir, deps android.Paths, flags []string) {
var sharedLibs android.Paths
- sdkDep := decodeSdkDep(ctx, sdkVersion)
+ sdkDep := decodeSdkDep(ctx, sdkContext)
if sdkDep.useFiles {
- sharedLibs = append(sharedLibs, sdkDep.jar)
+ sharedLibs = append(sharedLibs, sdkDep.jars...)
}
ctx.VisitDirectDeps(func(module android.Module) {
@@ -224,14 +312,27 @@
}
switch ctx.OtherModuleDependencyTag(module) {
+ case instrumentationForTag:
+ // Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
case libTag, frameworkResTag:
if exportPackage != nil {
sharedLibs = append(sharedLibs, exportPackage)
}
case staticLibTag:
if exportPackage != nil {
- transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
+ transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
+ staticLibManifests = append(staticLibManifests, aarDep.ExportedManifest())
+
+ outer:
+ for _, d := range aarDep.ExportedRRODirs() {
+ for _, e := range staticRRODirs {
+ if d.path == e.path {
+ continue outer
+ }
+ }
+ staticRRODirs = append(staticRRODirs, d)
+ }
}
}
})
@@ -249,7 +350,7 @@
transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs)
- return transitiveStaticLibs, deps, flags
+ return transitiveStaticLibs, staticLibManifests, staticRRODirs, deps, flags
}
type AndroidLibrary struct {
@@ -277,12 +378,13 @@
func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
a.Module.deps(ctx)
if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
- a.aapt.deps(ctx, String(a.deviceProperties.Sdk_version))
+ a.aapt.deps(ctx, sdkContext(a))
}
}
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- a.aapt.buildActions(ctx, String(a.deviceProperties.Sdk_version), "--static-lib")
+ a.aapt.isLibrary = true
+ a.aapt.buildActions(ctx, sdkContext(a))
ctx.CheckbuildFile(a.proguardOptionsFile)
ctx.CheckbuildFile(a.exportPackage)
@@ -296,7 +398,7 @@
a.Module.compile(ctx, a.aaptSrcJar)
- a.aarFile = android.PathForOutput(ctx, ctx.ModuleName()+".aar")
+ a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
var res android.Paths
if a.androidLibraryProperties.BuildAAR {
BuildAAR(ctx, a.aarFile, a.outputFile, a.manifestPath, a.rTxt, res)
@@ -315,19 +417,26 @@
a.exportedStaticPackages = android.FirstUniquePaths(a.exportedStaticPackages)
}
+// android_library builds and links sources into a `.jar` file for the device along with Android resources.
+//
+// An android_library has a single variant that produces a `.jar` file containing `.class` files that were
+// compiled against the device bootclasspath, along with a `package-res.apk` file containing Android resources compiled
+// with aapt2. This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
+// an android_app module.
func AndroidLibraryFactory() android.Module {
module := &AndroidLibrary{}
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
&module.Module.protoProperties,
&module.aaptProperties,
&module.androidLibraryProperties)
module.androidLibraryProperties.BuildAAR = true
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ InitJavaModule(module, android.DeviceSupported)
return module
}
@@ -336,16 +445,21 @@
//
type AARImportProperties struct {
- Aars []string
+ Aars []string `android:"path"`
- Sdk_version *string
+ Sdk_version *string
+ Min_sdk_version *string
Static_libs []string
Libs []string
+
+ // if set to true, run Jetifier against .aar file. Defaults to false.
+ Jetifier *bool
}
type AARImport struct {
android.ModuleBase
+ android.DefaultableModuleBase
prebuilt android.Prebuilt
properties AARImportProperties
@@ -354,10 +468,26 @@
proguardFlags android.WritablePath
exportPackage android.WritablePath
extraAaptPackagesFile android.WritablePath
+ manifest android.WritablePath
exportedStaticPackages android.Paths
}
+func (a *AARImport) sdkVersion() string {
+ return String(a.properties.Sdk_version)
+}
+
+func (a *AARImport) minSdkVersion() string {
+ if a.properties.Min_sdk_version != nil {
+ return *a.properties.Min_sdk_version
+ }
+ return a.sdkVersion()
+}
+
+func (a *AARImport) targetSdkVersion() string {
+ return a.sdkVersion()
+}
+
var _ AndroidLibraryDependency = (*AARImport)(nil)
func (a *AARImport) ExportPackage() android.Path {
@@ -368,10 +498,18 @@
return android.Paths{a.proguardFlags}
}
+func (a *AARImport) ExportedRRODirs() []rroDir {
+ return nil
+}
+
func (a *AARImport) ExportedStaticPackages() android.Paths {
return a.exportedStaticPackages
}
+func (a *AARImport) ExportedManifest() android.Path {
+ return a.manifest
+}
+
func (a *AARImport) Prebuilt() *android.Prebuilt {
return &a.prebuilt
}
@@ -381,15 +519,15 @@
}
func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
- if !ctx.Config().UnbundledBuild() {
- sdkDep := decodeSdkDep(ctx, String(a.properties.Sdk_version))
+ if !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ sdkDep := decodeSdkDep(ctx, sdkContext(a))
if sdkDep.useModule && sdkDep.frameworkResModule != "" {
- ctx.AddDependency(ctx.Module(), frameworkResTag, sdkDep.frameworkResModule)
+ ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
}
}
- ctx.AddDependency(ctx.Module(), libTag, a.properties.Libs...)
- ctx.AddDependency(ctx.Module(), staticLibTag, a.properties.Static_libs...)
+ ctx.AddVariationDependencies(nil, libTag, a.properties.Libs...)
+ ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs...)
}
// Unzip an AAR into its constituent files and directories. Any files in Outputs that don't exist in the AAR will be
@@ -407,18 +545,25 @@
return
}
- aar := android.PathForModuleSrc(ctx, a.properties.Aars[0])
+ aarName := ctx.ModuleName() + ".aar"
+ var aar android.Path
+ aar = android.PathForModuleSrc(ctx, a.properties.Aars[0])
+ if Bool(a.properties.Jetifier) {
+ inputFile := aar
+ aar = android.PathForModuleOut(ctx, "jetifier", aarName)
+ TransformJetifier(ctx, aar.(android.WritablePath), inputFile)
+ }
extractedAARDir := android.PathForModuleOut(ctx, "aar")
extractedResDir := extractedAARDir.Join(ctx, "res")
a.classpathFile = extractedAARDir.Join(ctx, "classes.jar")
a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
- manifest := extractedAARDir.Join(ctx, "AndroidManifest.xml")
+ a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
ctx.Build(pctx, android.BuildParams{
Rule: unzipAAR,
Input: aar,
- Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, manifest},
+ Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
Description: "unzip AAR",
Args: map[string]string{
"expectedDirs": extractedResDir.String(),
@@ -446,10 +591,13 @@
"--auto-add-overlay",
}
- linkFlags = append(linkFlags, "--manifest "+manifest.String())
- linkDeps = append(linkDeps, manifest)
+ linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
+ linkDeps = append(linkDeps, a.manifest)
- transitiveStaticLibs, libDeps, libFlags := aaptLibs(ctx, String(a.properties.Sdk_version))
+ transitiveStaticLibs, staticLibManifests, staticRRODirs, libDeps, libFlags := aaptLibs(ctx, sdkContext(a))
+
+ _ = staticLibManifests
+ _ = staticRRODirs
linkDeps = append(linkDeps, libDeps...)
linkFlags = append(linkFlags, libFlags...)
@@ -457,7 +605,7 @@
overlayRes := append(android.Paths{flata}, transitiveStaticLibs...)
aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
- linkFlags, linkDeps, nil, overlayRes)
+ linkFlags, linkDeps, nil, overlayRes, nil)
}
var _ Dependency = (*AARImport)(nil)
@@ -470,18 +618,38 @@
return android.Paths{a.classpathFile}
}
+func (a *AARImport) ResourceJars() android.Paths {
+ return nil
+}
+
+func (a *AARImport) ImplementationAndResourcesJars() android.Paths {
+ return android.Paths{a.classpathFile}
+}
+
+func (a *AARImport) DexJar() android.Path {
+ return nil
+}
+
func (a *AARImport) AidlIncludeDirs() android.Paths {
return nil
}
+func (a *AARImport) ExportedSdkLibs() []string {
+ return nil
+}
+
var _ android.PrebuiltInterface = (*Import)(nil)
+// android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
+//
+// This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
+// an android_app module.
func AARImportFactory() android.Module {
module := &AARImport{}
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Aars)
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ InitJavaModule(module, android.DeviceSupported)
return module
}
diff --git a/java/android_manifest.go b/java/android_manifest.go
new file mode 100644
index 0000000..8dc3b47
--- /dev/null
+++ b/java/android_manifest.go
@@ -0,0 +1,113 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+)
+
+var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
+ blueprint.RuleParams{
+ Command: `${config.ManifestFixerCmd} ` +
+ `--minSdkVersion ${minSdkVersion} ` +
+ `--targetSdkVersion ${targetSdkVersion} ` +
+ `--raise-min-sdk-version ` +
+ `$args $in $out`,
+ CommandDeps: []string{"${config.ManifestFixerCmd}"},
+ },
+ "minSdkVersion", "targetSdkVersion", "args")
+
+var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
+ blueprint.RuleParams{
+ Command: `${config.ManifestMergerCmd} --main $in $libs --out $out`,
+ CommandDeps: []string{"${config.ManifestMergerCmd}"},
+ },
+ "libs")
+
+func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
+ staticLibManifests android.Paths, isLibrary, uncompressedJNI, useEmbeddedDex, usesNonSdkApis bool) android.Path {
+
+ var args []string
+ if isLibrary {
+ args = append(args, "--library")
+ } else {
+ minSdkVersion, err := sdkVersionToNumber(ctx, sdkContext.minSdkVersion())
+ if err != nil {
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ }
+ if minSdkVersion >= 23 {
+ args = append(args, fmt.Sprintf("--extract-native-libs=%v", !uncompressedJNI))
+ } else if uncompressedJNI {
+ ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
+ minSdkVersion)
+ }
+ }
+
+ if usesNonSdkApis {
+ args = append(args, "--uses-non-sdk-api")
+ }
+
+ if useEmbeddedDex {
+ args = append(args, "--use-embedded-dex=true")
+ }
+
+ var deps android.Paths
+ targetSdkVersion := sdkVersionOrDefault(ctx, sdkContext.targetSdkVersion())
+ if targetSdkVersion == ctx.Config().PlatformSdkCodename() &&
+ ctx.Config().UnbundledBuild() &&
+ !ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
+ ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
+ apiFingerprint := ApiFingerprintPath(ctx)
+ targetSdkVersion += fmt.Sprintf(".$$(cat %s)", apiFingerprint.String())
+ deps = append(deps, apiFingerprint)
+ }
+
+ // Inject minSdkVersion into the manifest
+ fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: manifestFixerRule,
+ Input: manifest,
+ Implicits: deps,
+ Output: fixedManifest,
+ Args: map[string]string{
+ "minSdkVersion": sdkVersionOrDefault(ctx, sdkContext.minSdkVersion()),
+ "targetSdkVersion": targetSdkVersion,
+ "args": strings.Join(args, " "),
+ },
+ })
+ manifest = fixedManifest
+
+ // Merge static aar dependency manifests if necessary
+ if len(staticLibManifests) > 0 {
+ mergedManifest := android.PathForModuleOut(ctx, "manifest_merger", "AndroidManifest.xml")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: manifestMergerRule,
+ Input: manifest,
+ Implicits: staticLibManifests,
+ Output: mergedManifest,
+ Args: map[string]string{
+ "libs": android.JoinWithPrefix(staticLibManifests.Strings(), "--libs "),
+ },
+ })
+ manifest = mergedManifest
+ }
+
+ return manifest
+}
diff --git a/java/android_resources.go b/java/android_resources.go
index 47535d2..c2bc746 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -41,16 +41,25 @@
return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames)
}
-type overlayGlobResult struct {
- dir string
- paths android.DirectorySortedPaths
+type overlayType int
- // Set to true of the product has selected that values in this overlay should not be moved to
- // Runtime Resource Overlay (RRO) packages.
- excludeFromRRO bool
+const (
+ device overlayType = iota + 1
+ product
+)
+
+type rroDir struct {
+ path android.Path
+ overlayType overlayType
}
-const overlayDataKey = "overlayDataKey"
+type overlayGlobResult struct {
+ dir string
+ paths android.DirectorySortedPaths
+ overlayType overlayType
+}
+
+var overlayDataKey = android.NewOnceKey("overlayDataKey")
type globbedResourceDir struct {
dir android.Path
@@ -58,7 +67,7 @@
}
func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) (res []globbedResourceDir,
- rroDirs android.Paths) {
+ rroDirs []rroDir) {
overlayData := ctx.Config().Get(overlayDataKey).([]overlayGlobResult)
@@ -69,11 +78,12 @@
files := data.paths.PathsInDirectory(filepath.Join(data.dir, dir.String()))
if len(files) > 0 {
overlayModuleDir := android.PathForSource(ctx, data.dir, dir.String())
+
// If enforce RRO is enabled for this module and this overlay is not in the
// exclusion list, ignore the overlay. The list of ignored overlays will be
// passed to Make to be turned into an RRO package.
- if rroEnabled && !data.excludeFromRRO {
- rroDirs = append(rroDirs, overlayModuleDir)
+ if rroEnabled && !ctx.Config().EnforceRROExcludedOverlay(overlayModuleDir.String()) {
+ rroDirs = append(rroDirs, rroDir{overlayModuleDir, data.overlayType})
} else {
res = append(res, globbedResourceDir{
dir: overlayModuleDir,
@@ -94,33 +104,34 @@
func (overlaySingleton) GenerateBuildActions(ctx android.SingletonContext) {
var overlayData []overlayGlobResult
- overlayDirs := ctx.Config().ResourceOverlays()
- for i := range overlayDirs {
- // Iterate backwards through the list of overlay directories so that the later, lower-priority
- // directories in the list show up earlier in the command line to aapt2.
- overlay := overlayDirs[len(overlayDirs)-1-i]
- var result overlayGlobResult
- result.dir = overlay
- // Mark overlays that will not have Runtime Resource Overlays enforced on them
- // based on the product config
- result.excludeFromRRO = ctx.Config().EnforceRROExcludedOverlay(overlay)
+ appendOverlayData := func(overlayDirs []string, t overlayType) {
+ for i := range overlayDirs {
+ // Iterate backwards through the list of overlay directories so that the later, lower-priority
+ // directories in the list show up earlier in the command line to aapt2.
+ overlay := overlayDirs[len(overlayDirs)-1-i]
+ var result overlayGlobResult
+ result.dir = overlay
+ result.overlayType = t
- files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), androidResourceIgnoreFilenames)
- if err != nil {
- ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error())
- continue
- }
- var paths android.Paths
- for _, f := range files {
- if !strings.HasSuffix(f, "/") {
- paths = append(paths, android.PathForSource(ctx, f))
+ files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), androidResourceIgnoreFilenames)
+ if err != nil {
+ ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error())
+ continue
}
+ var paths android.Paths
+ for _, f := range files {
+ if !strings.HasSuffix(f, "/") {
+ paths = append(paths, android.PathForSource(ctx, f))
+ }
+ }
+ result.paths = android.PathsToDirectorySortedPaths(paths)
+ overlayData = append(overlayData, result)
}
- result.paths = android.PathsToDirectorySortedPaths(paths)
- overlayData = append(overlayData, result)
}
+ appendOverlayData(ctx.Config().DeviceResourceOverlays(), device)
+ appendOverlayData(ctx.Config().ProductResourceOverlays(), product)
ctx.Config().Once(overlayDataKey, func() interface{} {
return overlayData
})
diff --git a/java/androidmk.go b/java/androidmk.go
index b85ecb4..45fd1c1 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -19,15 +19,37 @@
"io"
"strings"
- "github.com/google/blueprint/proptools"
-
"android/soong/android"
)
+func (library *Library) AndroidMkHostDex(w io.Writer, name string, data android.AndroidMkData) {
+ if Bool(library.deviceProperties.Hostdex) && !library.Host() {
+ fmt.Fprintln(w, "include $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_MODULE := "+name+"-hostdex")
+ fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS := JAVA_LIBRARIES")
+ if library.dexJarFile != nil {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.dexJarFile.String())
+ } else {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationAndResourcesJar.String())
+ }
+ if library.dexJarFile != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
+ }
+ fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String())
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(data.Required, " "))
+ if r := library.deviceProperties.Target.Hostdex.Required; len(r) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(r, " "))
+ }
+ fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
+ }
+}
+
func (library *Library) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(library.implementationJarFile),
+ OutputFile: android.OptionalPathForPath(library.outputFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
@@ -39,36 +61,35 @@
fmt.Fprintln(w, "LOCAL_LOGTAGS_FILES :=", strings.Join(logtags, " "))
}
- if library.properties.Installable != nil && *library.properties.Installable == false {
+ if library.installFile == nil {
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
}
if library.dexJarFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
- if library.deviceProperties.Dex_preopt.Enabled != nil {
- fmt.Fprintln(w, "LOCAL_DEX_PREOPT :=", *library.deviceProperties.Dex_preopt.Enabled)
- }
- if library.deviceProperties.Dex_preopt.App_image != nil {
- fmt.Fprintln(w, "LOCAL_DEX_PREOPT_APP_IMAGE :=", *library.deviceProperties.Dex_preopt.App_image)
- }
- if library.deviceProperties.Dex_preopt.Profile_guided != nil {
- fmt.Fprintln(w, "LOCAL_DEX_PREOPT_GENERATE_PROFILE :=", *library.deviceProperties.Dex_preopt.Profile_guided)
- }
- if library.deviceProperties.Dex_preopt.Profile != nil {
- fmt.Fprintln(w, "LOCAL_DEX_PREOPT_GENERATE_PROFILE := true")
- fmt.Fprintln(w, "LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING := $(LOCAL_PATH)/"+*library.deviceProperties.Dex_preopt.Profile)
- }
}
- fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(library.deviceProperties.Sdk_version))
+ if len(library.dexpreopter.builtInstalled) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", library.dexpreopter.builtInstalled)
+ }
+ fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.sdkVersion())
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", library.implementationAndResourcesJar.String())
fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
if library.jacocoReportClassesFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", library.jacocoReportClassesFile.String())
}
+ if len(library.exportedSdkLibs) != 0 {
+ fmt.Fprintln(w, "LOCAL_EXPORT_SDK_LIBRARIES :=", strings.Join(library.exportedSdkLibs, " "))
+ }
+
+ if len(library.additionalCheckedModules) != 0 {
+ fmt.Fprintln(w, "LOCAL_ADDITIONAL_CHECKED_MODULE +=", strings.Join(library.additionalCheckedModules.Strings(), " "))
+ }
+
// Temporary hack: export sources used to compile framework.jar to Make
// to be used for droiddoc
// TODO(ccross): remove this once droiddoc is in soong
- if library.Name() == "framework" {
+ if (library.Name() == "framework") || (library.Name() == "framework-annotation-proc") {
fmt.Fprintln(w, "SOONG_FRAMEWORK_SRCS :=", strings.Join(library.compiledJavaSrcs.Strings(), " "))
fmt.Fprintln(w, "SOONG_FRAMEWORK_SRCJARS :=", strings.Join(library.compiledSrcJars.Strings(), " "))
}
@@ -76,27 +97,45 @@
},
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
-
- if proptools.Bool(library.deviceProperties.Hostdex) && !library.Host() {
- fmt.Fprintln(w, "include $(CLEAR_VARS)")
- fmt.Fprintln(w, "LOCAL_MODULE := "+name+"-hostdex")
- fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
- fmt.Fprintln(w, "LOCAL_MODULE_CLASS := JAVA_LIBRARIES")
- fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationJarFile.String())
- if library.properties.Installable != nil && *library.properties.Installable == false {
- fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
- }
- if library.dexJarFile != nil {
- fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", library.dexJarFile.String())
- }
- fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.implementationJarFile.String())
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(data.Required, " "))
- fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
- }
+ library.AndroidMkHostDex(w, name, data)
},
}
}
+// Called for modules that are a component of a test suite.
+func testSuiteComponent(w io.Writer, test_suites []string) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(test_suites, " "))
+ } else {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+ }
+}
+
+func (j *Test) AndroidMk() android.AndroidMkData {
+ data := j.Library.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ testSuiteComponent(w, j.testProperties.Test_suites)
+ if j.testConfig != nil {
+ fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", j.testConfig.String())
+ }
+ })
+
+ androidMkWriteTestData(j.data, &data)
+
+ return data
+}
+
+func (j *TestHelperLibrary) AndroidMk() android.AndroidMkData {
+ data := j.Library.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ testSuiteComponent(w, j.testHelperLibraryProperties.Test_suites)
+ })
+
+ return data
+}
+
func (prebuilt *Import) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
@@ -104,9 +143,32 @@
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := ", !proptools.Bool(prebuilt.properties.Installable))
+ fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := ", !Bool(prebuilt.properties.Installable))
fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.combinedClasspathFile.String())
- fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(prebuilt.properties.Sdk_version))
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.combinedClasspathFile.String())
+ fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
+ },
+ },
+ }
+}
+
+func (prebuilt *DexImport) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "JAVA_LIBRARIES",
+ OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile),
+ Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ if prebuilt.dexJarFile != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", prebuilt.dexJarFile.String())
+ // TODO(b/125517186): export the dex jar as a classes jar to match some mis-uses in Make until
+ // boot_jars_package_check.mk can check dex jars.
+ fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.dexJarFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.dexJarFile.String())
+ }
+ if len(prebuilt.dexpreopter.builtInstalled) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", prebuilt.dexpreopter.builtInstalled)
+ }
},
},
}
@@ -120,12 +182,13 @@
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
- fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.classpathFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", prebuilt.classpathFile.String())
fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", prebuilt.exportPackage.String())
fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", prebuilt.proguardFlags.String())
fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", prebuilt.extraAaptPackagesFile.String())
- fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(prebuilt.properties.Sdk_version))
+ fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", prebuilt.manifest.String())
+ fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", prebuilt.sdkVersion())
},
},
}
@@ -136,8 +199,20 @@
if !binary.isWrapperVariant {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(binary.implementationJarFile),
+ OutputFile: android.OptionalPathForPath(binary.outputFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", binary.headerJarFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", binary.implementationAndResourcesJar.String())
+ if binary.dexJarFile != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", binary.dexJarFile.String())
+ }
+ if len(binary.dexpreopter.builtInstalled) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", binary.dexpreopter.builtInstalled)
+ }
+ },
+ },
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
@@ -171,16 +246,24 @@
Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
+ // TODO(jungjw): This, outputting two LOCAL_MODULE lines, works, but is not ideal. Find a better solution.
+ if app.Name() != app.installApkName {
+ fmt.Fprintln(w, "# Overridden by PRODUCT_PACKAGE_NAME_OVERRIDES")
+ fmt.Fprintln(w, "LOCAL_MODULE :=", app.installApkName)
+ }
fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", app.exportPackage.String())
if app.dexJarFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", app.dexJarFile.String())
}
- if app.implementationJarFile != nil {
- fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", app.implementationJarFile)
+ if app.implementationAndResourcesJar != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", app.implementationAndResourcesJar.String())
}
if app.headerJarFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", app.headerJarFile.String())
}
+ if app.bundleFile != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_BUNDLE :=", app.bundleFile.String())
+ }
if app.jacocoReportClassesFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", app.jacocoReportClassesFile.String())
}
@@ -195,10 +278,24 @@
fmt.Fprintln(w, "LOCAL_NO_STANDARD_LIBRARIES := true")
}
- if len(app.rroDirs) > 0 {
+ filterRRO := func(filter overlayType) android.Paths {
+ var paths android.Paths
+ for _, d := range app.rroDirs {
+ if d.overlayType == filter {
+ paths = append(paths, d.path)
+ }
+ }
// Reverse the order, Soong stores rroDirs in aapt2 order (low to high priority), but Make
// expects it in LOCAL_RESOURCE_DIRS order (high to low priority).
- fmt.Fprintln(w, "LOCAL_SOONG_RRO_DIRS :=", strings.Join(android.ReversePaths(app.rroDirs).Strings(), " "))
+ return android.ReversePaths(paths)
+ }
+ deviceRRODirs := filterRRO(device)
+ if len(deviceRRODirs) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_DEVICE_RRO_DIRS :=", strings.Join(deviceRRODirs.Strings(), " "))
+ }
+ productRRODirs := filterRRO(product)
+ if len(productRRODirs) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_PRODUCT_RRO_DIRS :=", strings.Join(productRRODirs.Strings(), " "))
}
if Bool(app.appProperties.Export_package_resources) {
@@ -211,16 +308,66 @@
fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
}
- fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.pem.String())
+ fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.Pem.String())
+ if overriddenPkgs := app.getOverriddenPackages(); len(overriddenPkgs) > 0 {
+ fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(overriddenPkgs, " "))
+ }
+
+ for _, jniLib := range app.installJniLibs {
+ fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), "+=", jniLib.name)
+ }
+ if len(app.dexpreopter.builtInstalled) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", app.dexpreopter.builtInstalled)
+ }
+ for _, split := range app.aapt.splits {
+ install := "$(LOCAL_MODULE_PATH)/" + strings.TrimSuffix(app.installApkName, ".apk") + split.suffix + ".apk"
+ fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED +=", split.path.String()+":"+install)
+ }
},
},
}
}
+func (a *AndroidApp) getOverriddenPackages() []string {
+ var overridden []string
+ if len(a.appProperties.Overrides) > 0 {
+ overridden = append(overridden, a.appProperties.Overrides...)
+ }
+ if a.Name() != a.installApkName {
+ overridden = append(overridden, a.Name())
+ }
+ return overridden
+}
+
+func (a *AndroidTest) AndroidMk() android.AndroidMkData {
+ data := a.AndroidApp.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ testSuiteComponent(w, a.testProperties.Test_suites)
+ if a.testConfig != nil {
+ fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", a.testConfig.String())
+ }
+ })
+ androidMkWriteTestData(a.data, &data)
+
+ return data
+}
+
+func (a *AndroidTestHelperApp) AndroidMk() android.AndroidMkData {
+ data := a.AndroidApp.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ testSuiteComponent(w, a.appTestHelperAppProperties.Test_suites)
+ })
+
+ return data
+}
+
func (a *AndroidLibrary) AndroidMk() android.AndroidMkData {
data := a.Library.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ if a.aarFile != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_AAR :=", a.aarFile.String())
+ }
if a.proguardDictionary != nil {
fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", a.proguardDictionary.String())
}
@@ -238,7 +385,6 @@
fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=",
strings.Join(a.exportedProguardFlagFiles.Strings(), " "))
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
- fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
})
return data
@@ -247,15 +393,15 @@
func (jd *Javadoc) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(jd.stubsJar),
- Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ OutputFile: android.OptionalPathForPath(jd.stubsSrcJar),
+ Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
- if jd.properties.Installable == nil || *jd.properties.Installable == true {
+ if BoolDefault(jd.properties.Installable, true) {
fmt.Fprintln(w, "LOCAL_DROIDDOC_DOC_ZIP := ", jd.docZip.String())
}
- if jd.stubsJar != nil {
- fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_JAR := ", jd.stubsJar.String())
+ if jd.stubsSrcJar != nil {
+ fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", jd.stubsSrcJar.String())
}
},
},
@@ -265,16 +411,204 @@
func (ddoc *Droiddoc) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(ddoc.stubsJar),
- Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ OutputFile: android.OptionalPathForPath(ddoc.stubsSrcJar),
+ Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
- if ddoc.Javadoc.properties.Installable == nil || *ddoc.Javadoc.properties.Installable == true {
+ if BoolDefault(ddoc.Javadoc.properties.Installable, true) && ddoc.Javadoc.docZip != nil {
fmt.Fprintln(w, "LOCAL_DROIDDOC_DOC_ZIP := ", ddoc.Javadoc.docZip.String())
}
- if ddoc.Javadoc.stubsJar != nil {
- fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_JAR := ", ddoc.Javadoc.stubsJar.String())
+ if ddoc.Javadoc.stubsSrcJar != nil {
+ fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", ddoc.Javadoc.stubsSrcJar.String())
}
+ if ddoc.checkCurrentApiTimestamp != nil {
+ fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-current-api")
+ fmt.Fprintln(w, ddoc.Name()+"-check-current-api:",
+ ddoc.checkCurrentApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: checkapi")
+ fmt.Fprintln(w, "checkapi:",
+ ddoc.checkCurrentApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: droidcore")
+ fmt.Fprintln(w, "droidcore: checkapi")
+ }
+ if ddoc.updateCurrentApiTimestamp != nil {
+ fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-update-current-api")
+ fmt.Fprintln(w, ddoc.Name()+"-update-current-api:",
+ ddoc.updateCurrentApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: update-api")
+ fmt.Fprintln(w, "update-api:",
+ ddoc.updateCurrentApiTimestamp.String())
+ }
+ if ddoc.checkLastReleasedApiTimestamp != nil {
+ fmt.Fprintln(w, ".PHONY:", ddoc.Name()+"-check-last-released-api")
+ fmt.Fprintln(w, ddoc.Name()+"-check-last-released-api:",
+ ddoc.checkLastReleasedApiTimestamp.String())
+
+ if ddoc.Name() == "api-stubs-docs" || ddoc.Name() == "system-api-stubs-docs" {
+ fmt.Fprintln(w, ".PHONY: checkapi")
+ fmt.Fprintln(w, "checkapi:",
+ ddoc.checkLastReleasedApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: droidcore")
+ fmt.Fprintln(w, "droidcore: checkapi")
+ }
+ }
+ apiFilePrefix := "INTERNAL_PLATFORM_"
+ if String(ddoc.properties.Api_tag_name) != "" {
+ apiFilePrefix += String(ddoc.properties.Api_tag_name) + "_"
+ }
+ if ddoc.apiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", ddoc.apiFile.String())
+ }
+ if ddoc.dexApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"DEX_API_FILE := ", ddoc.dexApiFile.String())
+ }
+ if ddoc.privateApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", ddoc.privateApiFile.String())
+ }
+ if ddoc.privateDexApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", ddoc.privateDexApiFile.String())
+ }
+ if ddoc.removedApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", ddoc.removedApiFile.String())
+ }
+ if ddoc.removedDexApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", ddoc.removedDexApiFile.String())
+ }
+ if ddoc.exactApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", ddoc.exactApiFile.String())
+ }
+ if ddoc.proguardFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"PROGUARD_FILE := ", ddoc.proguardFile.String())
+ }
+ },
+ },
+ }
+}
+
+func (dstubs *Droidstubs) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "JAVA_LIBRARIES",
+ OutputFile: android.OptionalPathForPath(dstubs.stubsSrcJar),
+ Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ if dstubs.Javadoc.stubsSrcJar != nil {
+ fmt.Fprintln(w, "LOCAL_DROIDDOC_STUBS_SRCJAR := ", dstubs.Javadoc.stubsSrcJar.String())
+ }
+ if dstubs.apiVersionsXml != nil {
+ fmt.Fprintln(w, "LOCAL_DROIDDOC_API_VERSIONS_XML := ", dstubs.apiVersionsXml.String())
+ }
+ if dstubs.annotationsZip != nil {
+ fmt.Fprintln(w, "LOCAL_DROIDDOC_ANNOTATIONS_ZIP := ", dstubs.annotationsZip.String())
+ }
+ if dstubs.jdiffDocZip != nil {
+ fmt.Fprintln(w, "LOCAL_DROIDDOC_JDIFF_DOC_ZIP := ", dstubs.jdiffDocZip.String())
+ }
+ if dstubs.metadataZip != nil {
+ fmt.Fprintln(w, "LOCAL_DROIDDOC_METADATA_ZIP := ", dstubs.metadataZip.String())
+ }
+ if dstubs.checkCurrentApiTimestamp != nil {
+ fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-current-api")
+ fmt.Fprintln(w, dstubs.Name()+"-check-current-api:",
+ dstubs.checkCurrentApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: checkapi")
+ fmt.Fprintln(w, "checkapi:",
+ dstubs.checkCurrentApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: droidcore")
+ fmt.Fprintln(w, "droidcore: checkapi")
+ }
+ if dstubs.updateCurrentApiTimestamp != nil {
+ fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-update-current-api")
+ fmt.Fprintln(w, dstubs.Name()+"-update-current-api:",
+ dstubs.updateCurrentApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: update-api")
+ fmt.Fprintln(w, "update-api:",
+ dstubs.updateCurrentApiTimestamp.String())
+ }
+ if dstubs.checkLastReleasedApiTimestamp != nil {
+ fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-last-released-api")
+ fmt.Fprintln(w, dstubs.Name()+"-check-last-released-api:",
+ dstubs.checkLastReleasedApiTimestamp.String())
+
+ if dstubs.Name() == "api-stubs-docs" || dstubs.Name() == "system-api-stubs-docs" {
+ fmt.Fprintln(w, ".PHONY: checkapi")
+ fmt.Fprintln(w, "checkapi:",
+ dstubs.checkLastReleasedApiTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY: droidcore")
+ fmt.Fprintln(w, "droidcore: checkapi")
+ }
+ }
+ if dstubs.checkNullabilityWarningsTimestamp != nil {
+ fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-nullability-warnings")
+ fmt.Fprintln(w, dstubs.Name()+"-check-nullability-warnings:",
+ dstubs.checkNullabilityWarningsTimestamp.String())
+
+ fmt.Fprintln(w, ".PHONY:", "droidcore")
+ fmt.Fprintln(w, "droidcore: ", dstubs.Name()+"-check-nullability-warnings")
+ }
+ apiFilePrefix := "INTERNAL_PLATFORM_"
+ if String(dstubs.properties.Api_tag_name) != "" {
+ apiFilePrefix += String(dstubs.properties.Api_tag_name) + "_"
+ }
+ if dstubs.apiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"API_FILE := ", dstubs.apiFile.String())
+ }
+ if dstubs.dexApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"DEX_API_FILE := ", dstubs.dexApiFile.String())
+ }
+ if dstubs.privateApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"PRIVATE_API_FILE := ", dstubs.privateApiFile.String())
+ }
+ if dstubs.privateDexApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"PRIVATE_DEX_API_FILE := ", dstubs.privateDexApiFile.String())
+ }
+ if dstubs.removedApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"REMOVED_API_FILE := ", dstubs.removedApiFile.String())
+ }
+ if dstubs.removedDexApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"REMOVED_DEX_API_FILE := ", dstubs.removedDexApiFile.String())
+ }
+ if dstubs.exactApiFile != nil {
+ fmt.Fprintln(w, apiFilePrefix+"EXACT_API_FILE := ", dstubs.exactApiFile.String())
+ }
+ },
+ },
+ }
+}
+
+func androidMkWriteTestData(data android.Paths, ret *android.AndroidMkData) {
+ var testFiles []string
+ for _, d := range data {
+ testFiles = append(testFiles, d.String()+":"+d.Rel())
+ }
+ if len(testFiles) > 0 {
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUPPORT_FILES := "+strings.Join(testFiles, " "))
+ })
+ }
+}
+
+func (apkSet *AndroidAppSet) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "APPS",
+ OutputFile: android.OptionalPathForPath(apkSet.packedOutput),
+ Include: "$(BUILD_SYSTEM)/soong_android_app_set.mk",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ if apkSet.Privileged() {
+ fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
+ }
+ fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE := ", apkSet.masterFile)
+ fmt.Fprintln(w, "LOCAL_APKCERTS_FILE := ", apkSet.apkcertsFile)
+ fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(apkSet.properties.Overrides, " "))
},
},
}
diff --git a/java/app.go b/java/app.go
index ae0592a..586b66d 100644
--- a/java/app.go
+++ b/java/app.go
@@ -17,26 +17,158 @@
// This file contains the module types for compiling Android apps.
import (
+ "path/filepath"
+ "sort"
+ "strconv"
"strings"
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/cc"
+ "android/soong/tradefed"
)
func init() {
android.RegisterModuleType("android_app", AndroidAppFactory)
+ android.RegisterModuleType("android_test", AndroidTestFactory)
+ android.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory)
+ android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
+ android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
+ android.RegisterModuleType("android_app_set", AndroidApkSetFactory)
+}
+
+type AndroidAppSetProperties struct {
+ // APK Set path
+ Set string
+
+ // Specifies that this app should be installed to the priv-app directory,
+ // where the system will grant it additional privileges not available to
+ // normal apps.
+ Privileged *bool
+
+ // APKs in this set use prerelease SDK version
+ Prerelease *bool
+
+ // Names of modules to be overridden. Listed modules can only be other apps
+ // (in Make or Soong).
+ Overrides []string
+}
+
+type AndroidAppSet struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ prebuilt android.Prebuilt
+
+ properties AndroidAppSetProperties
+ packedOutput android.WritablePath
+ masterFile string
+ apkcertsFile android.ModuleOutPath
+}
+
+func (as *AndroidAppSet) Name() string {
+ return as.prebuilt.Name(as.ModuleBase.Name())
+}
+
+func (as *AndroidAppSet) IsInstallable() bool {
+ return true
+}
+
+func (as *AndroidAppSet) Prebuilt() *android.Prebuilt {
+ return &as.prebuilt
+}
+
+func (as *AndroidAppSet) Privileged() bool {
+ return Bool(as.properties.Privileged)
+}
+
+var TargetCpuAbi = map[string]string{
+ "arm": "ARMEABI_V7A",
+ "arm64": "ARM64_V8A",
+ "x86": "X86",
+ "x86_64": "X86_64",
+}
+
+func SupportedAbis(ctx android.ModuleContext) []string {
+ abiName := func(archVar string, deviceArch string) string {
+ if abi, found := TargetCpuAbi[deviceArch]; found {
+ return abi
+ }
+ ctx.ModuleErrorf("Invalid %s: %s", archVar, deviceArch)
+ return "BAD_ABI"
+ }
+
+ result := []string{abiName("TARGET_ARCH", ctx.DeviceConfig().DeviceArch())}
+ if s := ctx.DeviceConfig().DeviceSecondaryArch(); s != "" {
+ result = append(result, abiName("TARGET_2ND_ARCH", s))
+ }
+ return result
+}
+
+func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ as.packedOutput = android.PathForModuleOut(ctx, "extracted.zip")
+ as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
+ // We are assuming here that the master file in the APK
+ // set has `.apk` suffix. If it doesn't the build will fail.
+ // APK sets containing APEX files are handled elsewhere.
+ as.masterFile = ctx.ModuleName() + ".apk"
+ screenDensities := "all"
+ if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
+ screenDensities = strings.ToUpper(strings.Join(dpis, ","))
+ }
+ // TODO(asmundak): handle locales.
+ // TODO(asmundak): do we support device features
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: extractMatchingApks,
+ Description: "Extract APKs from APK set",
+ Output: as.packedOutput,
+ ImplicitOutput: as.apkcertsFile,
+ Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
+ Args: map[string]string{
+ "abis": strings.Join(SupportedAbis(ctx), ","),
+ "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
+ "screen-densities": screenDensities,
+ "sdk-version": ctx.Config().PlatformSdkVersion(),
+ "stem": ctx.ModuleName(),
+ "apkcerts": as.apkcertsFile.String(),
+ "partition": as.PartitionTag(ctx.DeviceConfig()),
+ },
+ })
+ // TODO(asmundak): add this (it's wrong now, will cause copying extracted.zip)
+ /*
+ var installDir android.InstallPath
+ if Bool(as.properties.Privileged) {
+ installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName())
+ } else if ctx.InstallInTestcases() {
+ installDir = android.PathForModuleInstall(ctx, as.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
+ } else {
+ installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName())
+ }
+ ctx.InstallFile(installDir, as.masterFile", as.packedOutput)
+ */
+}
+
+// android_app_set extracts a set of APKs based on the target device
+// configuration and installs this set as "split APKs".
+// The set will always contain `base-master.apk` and every APK built
+// to the target device. All density-specific APK will be included, too,
+// unless PRODUCT_APPT_PREBUILT_DPI is defined (should contain comma-sepearated
+// list of density names (LDPI, MDPI, HDPI, etc.)
+func AndroidApkSetFactory() android.Module {
+ module := &AndroidAppSet{}
+ module.AddProperties(&module.properties)
+ InitJavaModule(module, android.DeviceSupported)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties.Set)
+ return module
}
// AndroidManifest.xml merging
// package splits
type appProperties struct {
- // path to a certificate, or the name of a certificate in the default
- // certificate directory, or blank to use the default product certificate
- Certificate *string
-
- // paths to extra certificates to sign the apk with
+ // Names of extra android_app_certificate modules to sign the apk with in the form ":module".
Additional_certificates []string
// If set, create package-export.apk, which other packages can
@@ -51,16 +183,65 @@
// list of resource labels to generate individual resource packages
Package_splits []string
- Instrumentation_for *string
+ // Names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // list of native libraries that will be provided in or alongside the resulting jar
+ Jni_libs []string `android:"arch_variant"`
+
+ // Store native libraries uncompressed in the APK and set the android:extractNativeLibs="false" manifest
+ // flag so that they are used from inside the APK at runtime. Defaults to true for android_test modules unless
+ // sdk_version or min_sdk_version is set to a version that doesn't support it (<23), defaults to false for other
+ // module types where the native libraries are generally preinstalled outside the APK.
+ Use_embedded_native_libs *bool
+
+ // Store dex files uncompressed in the APK and set the android:useEmbeddedDex="true" manifest attribute so that
+ // they are used from inside the APK at runtime.
+ Use_embedded_dex *bool
+
+ // Forces native libraries to always be packaged into the APK,
+ // Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
+ // True for android_test* modules.
+ AlwaysPackageNativeLibs bool `blueprint:"mutated"`
+
+ // If set, find and merge all NOTICE files that this module and its dependencies have and store
+ // it in the APK as an asset.
+ Embed_notices *bool
+}
+
+// android_app properties that can be overridden by override_android_app
+type overridableAppProperties struct {
+ // The name of a certificate in the default certificate directory, blank to use the default product certificate,
+ // or an android_app_certificate module name in the form ":module".
+ Certificate *string
+
+ // the package name of this app. The package name in the manifest file is used if one was not given.
+ Package_name *string
}
type AndroidApp struct {
Library
aapt
+ android.OverridableModuleBase
- certificate certificate
+ certificate Certificate
appProperties appProperties
+
+ overridableAppProperties overridableAppProperties
+
+ installJniLibs []jniLib
+
+ bundleFile android.Path
+
+ // the install APK name is normally the same as the module name, but can be overridden with PRODUCT_PACKAGE_NAME_OVERRIDES.
+ installApkName string
+
+ additionalAaptFlags []string
}
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -73,57 +254,136 @@
var _ AndroidLibraryDependency = (*AndroidApp)(nil)
-type certificate struct {
- pem, key android.Path
+type Certificate struct {
+ Pem, Key android.Path
}
func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
a.Module.deps(ctx)
+
if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
- a.aapt.deps(ctx, String(a.deviceProperties.Sdk_version))
+ a.aapt.deps(ctx, sdkContext(a))
+ }
+
+ for _, jniTarget := range ctx.MultiTargets() {
+ variation := []blueprint.Variation{
+ {Mutator: "arch", Variation: jniTarget.String()},
+ {Mutator: "link", Variation: "shared"},
+ }
+ tag := &jniDependencyTag{
+ target: jniTarget,
+ }
+ ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
+ }
+
+ cert := android.SrcIsModule(a.getCertString(ctx))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+
+ for _, cert := range a.appProperties.Additional_certificates {
+ cert = android.SrcIsModule(cert)
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ } else {
+ ctx.PropertyErrorf("additional_certificates",
+ `must be names of android_app_certificate modules in the form ":module"`)
+ }
}
}
func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- var linkFlags []string
- if String(a.appProperties.Instrumentation_for) != "" {
- linkFlags = append(linkFlags,
- "--rename-instrumentation-target-package",
- String(a.appProperties.Instrumentation_for))
- } else {
- a.properties.Instrument = true
+ a.aapt.uncompressedJNI = a.shouldUncompressJNI(ctx)
+ a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
+ a.generateAndroidBuildActions(ctx)
+}
+
+// shouldUncompressJNI returns true if the native libraries should be stored in the APK uncompressed and the
+// extractNativeLibs application flag should be set to false in the manifest.
+func (a *AndroidApp) shouldUncompressJNI(ctx android.ModuleContext) bool {
+ minSdkVersion, err := sdkVersionToNumber(ctx, a.minSdkVersion())
+ if err != nil {
+ ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.minSdkVersion(), err)
}
+ return minSdkVersion >= 23 && Bool(a.appProperties.Use_embedded_native_libs)
+}
+
+// Returns whether this module should have the dex file stored uncompressed in the APK.
+func (a *AndroidApp) shouldUncompressDex(ctx android.ModuleContext) bool {
+ if Bool(a.appProperties.Use_embedded_dex) {
+ return true
+ }
+
+ // Uncompress dex in APKs of privileged apps, and modules used by privileged apps
+ // (even for unbundled builds, they may be preinstalled as prebuilts).
+ if ctx.Config().UncompressPrivAppDex() &&
+ (Bool(a.appProperties.Privileged) ||
+ inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules())) {
+ return true
+ }
+
+ if ctx.Config().UnbundledBuild() {
+ return false
+ }
+
+ // Uncompress if the dex files is preopted on /system.
+ if !a.dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, a.dexpreopter.installPath)) {
+ return true
+ }
+
+ return false
+}
+
+func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
+ a.aapt.usesNonSdkApis = Bool(a.Module.deviceProperties.Platform_apis)
+
+ aaptLinkFlags := []string{}
+
+ // Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided.
hasProduct := false
for _, f := range a.aaptProperties.Aaptflags {
if strings.HasPrefix(f, "--product") {
hasProduct = true
+ break
+ }
+ }
+ if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
+ aaptLinkFlags = append(aaptLinkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
+ }
+
+ if !Bool(a.aaptProperties.Aapt_include_all_resources) {
+ // Product AAPT config
+ for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
+ aaptLinkFlags = append(aaptLinkFlags, "-c", aaptConfig)
+ }
+
+ // Product AAPT preferred config
+ if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 {
+ aaptLinkFlags = append(aaptLinkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
}
}
- // Product characteristics
- if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
- linkFlags = append(linkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
+ manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
+ if overridden || a.overridableAppProperties.Package_name != nil {
+ // The product override variable has a priority over the package_name property.
+ if !overridden {
+ manifestPackageName = *a.overridableAppProperties.Package_name
+ }
+ aaptLinkFlags = append(aaptLinkFlags, "--rename-manifest-package "+manifestPackageName)
}
- // Product AAPT config
- for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
- linkFlags = append(linkFlags, "-c", aaptConfig)
- }
+ aaptLinkFlags = append(aaptLinkFlags, a.additionalAaptFlags...)
- // Product AAPT preferred config
- if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 {
- linkFlags = append(linkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
- }
+ a.aapt.splitNames = a.appProperties.Package_splits
- // TODO: LOCAL_PACKAGE_OVERRIDES
- // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
-
- a.aapt.buildActions(ctx, String(a.deviceProperties.Sdk_version), linkFlags...)
+ a.aapt.buildActions(ctx, sdkContext(a), aaptLinkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
+}
+func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) {
var staticLibProguardFlagFiles android.Paths
ctx.VisitDirectDeps(func(m android.Module) {
if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
@@ -135,66 +395,415 @@
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, staticLibProguardFlagFiles...)
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
+}
+
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
+
+ var installDir string
+ if ctx.ModuleName() == "framework-res" {
+ // framework-res.apk is installed as system/framework/framework-res.apk
+ installDir = "framework"
+ } else if Bool(a.appProperties.Privileged) {
+ installDir = filepath.Join("priv-app", a.installApkName)
+ } else {
+ installDir = filepath.Join("app", a.installApkName)
+ }
+ a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
+ a.dexpreopter.isInstallable = Bool(a.properties.Installable)
+ a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
+ a.deviceProperties.UncompressDex = a.dexpreopter.uncompressedDex
if ctx.ModuleName() != "framework-res" {
a.Module.compile(ctx, a.aaptSrcJar)
}
- c := String(a.appProperties.Certificate)
- switch {
- case c == "":
- pem, key := ctx.Config().DefaultAppCertificate(ctx)
- a.certificate = certificate{pem, key}
- case strings.ContainsRune(c, '/'):
- a.certificate = certificate{
- android.PathForSource(ctx, c+".x509.pem"),
- android.PathForSource(ctx, c+".pk8"),
+ return a.maybeStrippedDexJarFile
+}
+
+func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
+ var jniJarFile android.WritablePath
+ if len(jniLibs) > 0 {
+ embedJni := ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
+ a.appProperties.AlwaysPackageNativeLibs
+ if embedJni {
+ jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
+ TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.shouldUncompressJNI(ctx))
+ } else {
+ a.installJniLibs = jniLibs
}
- default:
+ }
+ return jniJarFile
+}
+
+func (a *AndroidApp) certificateBuildActions(certificateDeps []Certificate, ctx android.ModuleContext) []Certificate {
+ cert := a.getCertString(ctx)
+ certModule := android.SrcIsModule(cert)
+ if certModule != "" {
+ a.certificate = certificateDeps[0]
+ certificateDeps = certificateDeps[1:]
+ } else if cert != "" {
defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
- a.certificate = certificate{
- defaultDir.Join(ctx, c+".x509.pem"),
- defaultDir.Join(ctx, c+".pk8"),
+ a.certificate = Certificate{
+ defaultDir.Join(ctx, cert+".x509.pem"),
+ defaultDir.Join(ctx, cert+".pk8"),
+ }
+ } else {
+ pem, key := ctx.Config().DefaultAppCertificate(ctx)
+ a.certificate = Certificate{pem, key}
+ }
+
+ if !a.Module.Platform() {
+ certPath := a.certificate.Pem.String()
+ systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String()
+ if strings.HasPrefix(certPath, systemCertPath) {
+ enforceSystemCert := ctx.Config().EnforceSystemCertificate()
+ whitelist := ctx.Config().EnforceSystemCertificateWhitelist()
+
+ if enforceSystemCert && !inList(a.Module.Name(), whitelist) {
+ ctx.PropertyErrorf("certificate", "The module in product partition cannot be signed with certificate in system.")
+ }
}
}
- certificates := []certificate{a.certificate}
- for _, c := range a.appProperties.Additional_certificates {
- certificates = append(certificates, certificate{
- android.PathForSource(ctx, c+".x509.pem"),
- android.PathForSource(ctx, c+".pk8"),
- })
+ return append([]Certificate{a.certificate}, certificateDeps...)
+}
+
+func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) android.OptionalPath {
+ if !Bool(a.appProperties.Embed_notices) && !ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+ return android.OptionalPath{}
}
- packageFile := android.PathForModuleOut(ctx, "package.apk")
+ // Collect NOTICE files from all dependencies.
+ seenModules := make(map[android.Module]bool)
+ noticePathSet := make(map[android.Path]bool)
- CreateAppPackage(ctx, packageFile, a.exportPackage, a.outputFile, certificates)
+ ctx.WalkDepsBlueprint(func(child blueprint.Module, parent blueprint.Module) bool {
+ if _, ok := child.(android.Module); !ok {
+ return false
+ }
+ module := child.(android.Module)
+ // Have we already seen this?
+ if _, ok := seenModules[module]; ok {
+ return false
+ }
+ seenModules[module] = true
- a.outputFile = packageFile
+ // Skip host modules.
+ if module.Target().Os.Class == android.Host || module.Target().Os.Class == android.HostCross {
+ return false
+ }
+ path := module.NoticeFile()
+ if path.Valid() {
+ noticePathSet[path.Path()] = true
+ }
+ return true
+ })
+
+ // If the app has one, add it too.
+ if a.NoticeFile().Valid() {
+ noticePathSet[a.NoticeFile().Path()] = true
+ }
+
+ if len(noticePathSet) == 0 {
+ return android.OptionalPath{}
+ }
+ var noticePaths []android.Path
+ for path := range noticePathSet {
+ noticePaths = append(noticePaths, path)
+ }
+ sort.Slice(noticePaths, func(i, j int) bool {
+ return noticePaths[i].String() < noticePaths[j].String()
+ })
+ noticeFile := android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths)
+
+ return android.OptionalPathForPath(noticeFile)
+}
+
+func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
+ // Check if the install APK name needs to be overridden.
+ a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
+
+ var installDir android.OutputPath
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
- ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), ctx.ModuleName()+".apk", a.outputFile)
+ installDir = android.PathForModuleInstall(ctx, "framework")
} else if Bool(a.appProperties.Privileged) {
- ctx.InstallFile(android.PathForModuleInstall(ctx, "priv-app"), ctx.ModuleName()+".apk", a.outputFile)
+ installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
} else {
- ctx.InstallFile(android.PathForModuleInstall(ctx, "app"), ctx.ModuleName()+".apk", a.outputFile)
+ installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
+ }
+
+ a.aapt.noticeFile = a.noticeBuildActions(ctx, installDir)
+
+ // Process all building blocks, from AAPT to certificates.
+ a.aaptBuildActions(ctx)
+
+ a.proguardBuildActions(ctx)
+
+ dexJarFile := a.dexBuildActions(ctx)
+
+ jniLibs, certificateDeps := a.collectAppDeps(ctx)
+ jniJarFile := a.jniBuildActions(jniLibs, ctx)
+
+ if ctx.Failed() {
+ return
+ }
+
+ certificates := a.certificateBuildActions(certificateDeps, ctx)
+
+ // Build a final signed app package.
+ // TODO(jungjw): Consider changing this to installApkName.
+ packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk")
+ CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
+ a.outputFile = packageFile
+
+ for _, split := range a.aapt.splits {
+ // Sign the split APKs
+ packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"_"+split.suffix+".apk")
+ CreateAppPackage(ctx, packageFile, split.path, nil, nil, certificates)
+ a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
+ }
+
+ // Build an app bundle.
+ bundleFile := android.PathForModuleOut(ctx, "base.zip")
+ BuildBundleModule(ctx, bundleFile, a.exportPackage, jniJarFile, dexJarFile)
+ a.bundleFile = bundleFile
+
+ // Install the app package.
+ ctx.InstallFile(installDir, a.installApkName+".apk", a.outputFile)
+ for _, split := range a.aapt.splits {
+ ctx.InstallFile(installDir, a.installApkName+"_"+split.suffix+".apk", split.path)
}
}
+func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Certificate) {
+ var jniLibs []jniLib
+ var certificates []Certificate
+
+ ctx.VisitDirectDeps(func(module android.Module) {
+ otherName := ctx.OtherModuleName(module)
+ tag := ctx.OtherModuleDependencyTag(module)
+
+ if jniTag, ok := tag.(*jniDependencyTag); ok {
+ if dep, ok := module.(*cc.Module); ok {
+ lib := dep.OutputFile()
+ if lib.Valid() {
+ jniLibs = append(jniLibs, jniLib{
+ name: ctx.OtherModuleName(module),
+ path: lib.Path(),
+ target: jniTag.target,
+ })
+ } else {
+ ctx.ModuleErrorf("dependency %q missing output file", otherName)
+ }
+ } else {
+ ctx.ModuleErrorf("jni_libs dependency %q must be a cc library", otherName)
+
+ }
+ } else if tag == certificateTag {
+ if dep, ok := module.(*AndroidAppCertificate); ok {
+ certificates = append(certificates, dep.Certificate)
+ } else {
+ ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
+ }
+ }
+ })
+
+ return jniLibs, certificates
+}
+
+func (a *AndroidApp) getCertString(ctx android.BaseContext) string {
+ certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
+ if overridden {
+ return ":" + certificate
+ }
+ return String(a.overridableAppProperties.Certificate)
+}
+
+// android_app compiles sources and Android resources into an Android application package `.apk` file.
func AndroidAppFactory() android.Module {
module := &AndroidApp{}
- module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
+ module.Module.deviceProperties.Optimize.EnabledByDefault = true
module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
+ module.Module.properties.Instrument = true
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
&module.Module.protoProperties,
&module.aaptProperties,
- &module.appProperties)
+ &module.appProperties,
+ &module.overridableAppProperties)
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
+ return class == android.Device && ctx.Config().DevicePrefer32BitApps()
+ })
+
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitOverridableModule(module, &module.appProperties.Overrides)
+
return module
}
+
+type appTestProperties struct {
+ Instrumentation_for *string
+}
+
+type AndroidTest struct {
+ AndroidApp
+
+ appTestProperties appTestProperties
+
+ testProperties testProperties
+
+ testConfig android.Path
+ data android.Paths
+}
+
+func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Check if the instrumentation target package is overridden before generating build actions.
+ if a.appTestProperties.Instrumentation_for != nil {
+ manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(*a.appTestProperties.Instrumentation_for)
+ if overridden {
+ a.additionalAaptFlags = append(a.additionalAaptFlags, "--rename-instrumentation-target-package "+manifestPackageName)
+ }
+ }
+ a.generateAndroidBuildActions(ctx)
+
+ a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites)
+ a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
+}
+
+func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+ a.AndroidApp.DepsMutator(ctx)
+ if a.appTestProperties.Instrumentation_for != nil {
+ // The android_app dependency listed in instrumentation_for needs to be added to the classpath for javac,
+ // but not added to the aapt2 link includes like a normal android_app or android_library dependency, so
+ // use instrumentationForTag instead of libTag.
+ ctx.AddVariationDependencies(nil, instrumentationForTag, String(a.appTestProperties.Instrumentation_for))
+ }
+}
+
+// android_test compiles test sources and Android resources into an Android application package `.apk` file and
+// creates an `AndroidTest.xml` file to allow running the test with `atest` or a `TEST_MAPPING` file.
+func AndroidTestFactory() android.Module {
+ module := &AndroidTest{}
+
+ module.Module.deviceProperties.Optimize.EnabledByDefault = true
+
+ module.Module.properties.Instrument = true
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+ module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
+ module.appProperties.AlwaysPackageNativeLibs = true
+ module.Module.dexpreopter.isTest = true
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
+ &module.Module.protoProperties,
+ &module.aaptProperties,
+ &module.appProperties,
+ &module.appTestProperties,
+ &module.overridableAppProperties,
+ &module.testProperties)
+
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+type appTestHelperAppProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+}
+
+type AndroidTestHelperApp struct {
+ AndroidApp
+
+ appTestHelperAppProperties appTestHelperAppProperties
+}
+
+// android_test_helper_app compiles sources and Android resources into an Android application package `.apk` file that
+// will be used by tests, but does not produce an `AndroidTest.xml` file so the module will not be run directly as a
+// test.
+func AndroidTestHelperAppFactory() android.Module {
+ module := &AndroidTestHelperApp{}
+
+ module.Module.deviceProperties.Optimize.EnabledByDefault = true
+
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+ module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
+ module.appProperties.AlwaysPackageNativeLibs = true
+ module.Module.dexpreopter.isTest = true
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
+ &module.Module.protoProperties,
+ &module.aaptProperties,
+ &module.appProperties,
+ &module.appTestHelperAppProperties,
+ &module.overridableAppProperties)
+
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+type AndroidAppCertificate struct {
+ android.ModuleBase
+ properties AndroidAppCertificateProperties
+ Certificate Certificate
+}
+
+type AndroidAppCertificateProperties struct {
+ // Name of the certificate files. Extensions .x509.pem and .pk8 will be added to the name.
+ Certificate *string
+}
+
+// android_app_certificate modules can be referenced by the certificates property of android_app modules to select
+// the signing key.
+func AndroidAppCertificateFactory() android.Module {
+ module := &AndroidAppCertificate{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ return module
+}
+
+func (c *AndroidAppCertificate) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ cert := String(c.properties.Certificate)
+ c.Certificate = Certificate{
+ android.PathForModuleSrc(ctx, cert+".x509.pem"),
+ android.PathForModuleSrc(ctx, cert+".pk8"),
+ }
+}
+
+type OverrideAndroidApp struct {
+ android.ModuleBase
+ android.OverrideModuleBase
+}
+
+func (i *OverrideAndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // All the overrides happen in the base module.
+ // TODO(jungjw): Check the base module type.
+}
+
+// override_android_app is used to create an android_app module based on another android_app by overriding
+// some of its properties.
+func OverrideAndroidAppModuleFactory() android.Module {
+ m := &OverrideAndroidApp{}
+ m.AddProperties(&overridableAppProperties{})
+
+ android.InitAndroidModule(m)
+ android.InitOverrideModule(m)
+ return m
+}
diff --git a/java/app_builder.go b/java/app_builder.go
index 954ca44..5bacb67 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -19,21 +19,23 @@
// functions.
import (
+ "path/filepath"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
)
var (
- signapk = pctx.AndroidStaticRule("signapk",
+ Signapk = pctx.AndroidStaticRule("signapk",
blueprint.RuleParams{
Command: `${config.JavaCmd} -Djava.library.path=$$(dirname $signapkJniLibrary) ` +
- `-jar $signapkCmd $certificates $in $out`,
+ `-jar $signapkCmd $flags $certificates $in $out`,
CommandDeps: []string{"$signapkCmd", "$signapkJniLibrary"},
},
- "certificates")
+ "flags", "certificates")
androidManifestMerger = pctx.AndroidStaticRule("androidManifestMerger",
blueprint.RuleParams{
@@ -61,16 +63,19 @@
})
func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
- resJarFile, dexJarFile android.Path, certificates []certificate) {
+ packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate) {
- // TODO(ccross): JNI libs
+ unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
+ unsignedApk := android.PathForModuleOut(ctx, unsignedApkName)
- unsignedApk := android.PathForModuleOut(ctx, "unsigned.apk")
-
- inputs := android.Paths{resJarFile}
+ var inputs android.Paths
if dexJarFile != nil {
inputs = append(inputs, dexJarFile)
}
+ inputs = append(inputs, packageFile)
+ if jniJarFile != nil {
+ inputs = append(inputs, jniJarFile)
+ }
ctx.Build(pctx, android.BuildParams{
Rule: combineApk,
@@ -79,18 +84,18 @@
})
var certificateArgs []string
+ var deps android.Paths
for _, c := range certificates {
- certificateArgs = append(certificateArgs, c.pem.String(), c.key.String())
+ certificateArgs = append(certificateArgs, c.Pem.String(), c.Key.String())
+ deps = append(deps, c.Pem, c.Key)
}
- // TODO(ccross): sometimes uncompress dex
- // TODO(ccross): sometimes strip dex
-
ctx.Build(pctx, android.BuildParams{
- Rule: signapk,
+ Rule: Signapk,
Description: "signapk",
Output: outputFile,
Input: unsignedApk,
+ Implicits: deps,
Args: map[string]string{
"certificates": strings.Join(certificateArgs, " "),
},
@@ -103,10 +108,10 @@
`cp ${manifest} ${outDir}/AndroidManifest.xml && ` +
`cp ${classesJar} ${outDir}/classes.jar && ` +
`cp ${rTxt} ${outDir}/R.txt && ` +
- `${config.SoongZipCmd} -jar -o $out -C ${outDir} -D ${outDir} ${resArgs}`,
+ `${config.SoongZipCmd} -jar -o $out -C ${outDir} -D ${outDir}`,
CommandDeps: []string{"${config.SoongZipCmd}"},
},
- "manifest", "classesJar", "rTxt", "resArgs", "outDir")
+ "manifest", "classesJar", "rTxt", "outDir")
func BuildAAR(ctx android.ModuleContext, outputFile android.WritablePath,
classesJar, manifest, rTxt android.Path, res android.Paths) {
@@ -121,9 +126,10 @@
}
ctx.Build(pctx, android.BuildParams{
- Rule: buildAAR,
- Implicits: deps,
- Output: outputFile,
+ Rule: buildAAR,
+ Description: "aar",
+ Implicits: deps,
+ Output: outputFile,
Args: map[string]string{
"manifest": manifest.String(),
"classesJar": classesJarPath,
@@ -132,3 +138,101 @@
},
})
}
+
+var buildBundleModule = pctx.AndroidStaticRule("buildBundleModule",
+ blueprint.RuleParams{
+ Command: `${config.MergeZipsCmd} ${out} ${in}`,
+ CommandDeps: []string{"${config.MergeZipsCmd}"},
+ })
+
+var bundleMungePackage = pctx.AndroidStaticRule("bundleMungePackage",
+ blueprint.RuleParams{
+ Command: `${config.Zip2ZipCmd} -i ${in} -o ${out} AndroidManifest.xml:manifest/AndroidManifest.xml resources.pb "res/**/*" "assets/**/*"`,
+ CommandDeps: []string{"${config.Zip2ZipCmd}"},
+ })
+
+var bundleMungeDexJar = pctx.AndroidStaticRule("bundleMungeDexJar",
+ blueprint.RuleParams{
+ Command: `${config.Zip2ZipCmd} -i ${in} -o ${out} "classes*.dex:dex/" && ` +
+ `${config.Zip2ZipCmd} -i ${in} -o ${resJar} -x "classes*.dex" "**/*:root/"`,
+ CommandDeps: []string{"${config.Zip2ZipCmd}"},
+ }, "resJar")
+
+// Builds an app into a module suitable for input to bundletool
+func BuildBundleModule(ctx android.ModuleContext, outputFile android.WritablePath,
+ packageFile, jniJarFile, dexJarFile android.Path) {
+
+ protoResJarFile := android.PathForModuleOut(ctx, "package-res.pb.apk")
+ aapt2Convert(ctx, protoResJarFile, packageFile)
+
+ var zips android.Paths
+
+ mungedPackage := android.PathForModuleOut(ctx, "bundle", "apk.zip")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: bundleMungePackage,
+ Input: protoResJarFile,
+ Output: mungedPackage,
+ Description: "bundle apk",
+ })
+ zips = append(zips, mungedPackage)
+
+ if dexJarFile != nil {
+ mungedDexJar := android.PathForModuleOut(ctx, "bundle", "dex.zip")
+ mungedResJar := android.PathForModuleOut(ctx, "bundle", "res.zip")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: bundleMungeDexJar,
+ Input: dexJarFile,
+ Output: mungedDexJar,
+ ImplicitOutput: mungedResJar,
+ Description: "bundle dex",
+ Args: map[string]string{
+ "resJar": mungedResJar.String(),
+ },
+ })
+ zips = append(zips, mungedDexJar, mungedResJar)
+ }
+ if jniJarFile != nil {
+ zips = append(zips, jniJarFile)
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: buildBundleModule,
+ Inputs: zips,
+ Output: outputFile,
+ Description: "bundle",
+ })
+}
+
+func TransformJniLibsToJar(ctx android.ModuleContext, outputFile android.WritablePath,
+ jniLibs []jniLib, uncompressJNI bool) {
+
+ var deps android.Paths
+ jarArgs := []string{
+ "-j", // junk paths, they will be added back with -P arguments
+ }
+
+ if uncompressJNI {
+ jarArgs = append(jarArgs, "-L 0")
+ }
+
+ for _, j := range jniLibs {
+ deps = append(deps, j.path)
+ jarArgs = append(jarArgs,
+ "-P "+targetToJniDir(j.target),
+ "-f "+j.path.String())
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zip,
+ Description: "zip jni libs",
+ Output: outputFile,
+ Implicits: deps,
+ Args: map[string]string{
+ "jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
+ },
+ })
+}
+
+func targetToJniDir(target android.Target) string {
+ return filepath.Join("lib", target.Arch.Abi[0])
+}
diff --git a/java/app_test.go b/java/app_test.go
index 6770119..1d49770 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -16,11 +16,16 @@
import (
"android/soong/android"
+ "android/soong/cc"
+
"fmt"
+ "path/filepath"
"reflect"
"sort"
"strings"
"testing"
+
+ "github.com/google/blueprint/proptools"
)
var (
@@ -71,7 +76,10 @@
foo := ctx.ModuleForTests("foo", "android_common")
- expectedLinkImplicits := []string{"AndroidManifest.xml"}
+ var expectedLinkImplicits []string
+
+ manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml")
+ expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String())
frameworkRes := ctx.ModuleForTests("framework-res", "android_common")
expectedLinkImplicits = append(expectedLinkImplicits,
@@ -102,143 +110,427 @@
}
}
-var testEnforceRROTests = []struct {
- name string
- enforceRROTargets []string
- enforceRROExcludedOverlays []string
- fooOverlayFiles []string
- fooRRODirs []string
- barOverlayFiles []string
- barRRODirs []string
-}{
- {
- name: "no RRO",
- enforceRROTargets: nil,
- enforceRROExcludedOverlays: nil,
- fooOverlayFiles: []string{
- "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
- "device/vendor/blah/overlay/foo/res/values/strings.xml",
- },
- fooRRODirs: nil,
- barOverlayFiles: []string{
- "device/vendor/blah/static_overlay/bar/res/values/strings.xml",
- "device/vendor/blah/overlay/bar/res/values/strings.xml",
- },
- barRRODirs: nil,
- },
- {
- name: "enforce RRO on foo",
- enforceRROTargets: []string{"foo"},
- enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"},
- fooOverlayFiles: []string{
- "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
- },
- fooRRODirs: []string{
- "device/vendor/blah/overlay/foo/res",
- },
- barOverlayFiles: []string{
- "device/vendor/blah/static_overlay/bar/res/values/strings.xml",
- "device/vendor/blah/overlay/bar/res/values/strings.xml",
- },
- barRRODirs: nil,
- },
- {
- name: "enforce RRO on all",
- enforceRROTargets: []string{"*"},
- enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"},
- fooOverlayFiles: []string{
- "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
- },
- fooRRODirs: []string{
- "device/vendor/blah/overlay/foo/res",
- },
- barOverlayFiles: []string{
- "device/vendor/blah/static_overlay/bar/res/values/strings.xml",
- },
- barRRODirs: []string{
- "device/vendor/blah/overlay/bar/res",
- },
- },
+func TestAppSplits(t *testing.T) {
+ ctx := testApp(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ package_splits: ["v4", "v7,hdpi"],
+ }`)
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+
+ expectedOutputs := []string{
+ filepath.Join(buildDir, ".intermediates/foo/android_common/foo.apk"),
+ filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v4.apk"),
+ filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v7_hdpi.apk"),
+ }
+ for _, expectedOutput := range expectedOutputs {
+ foo.Output(expectedOutput)
+ }
+
+ if g, w := foo.Module().(*AndroidApp).Srcs().Strings(), expectedOutputs; !reflect.DeepEqual(g, w) {
+ t.Errorf("want Srcs() = %q, got %q", w, g)
+ }
}
-func TestEnforceRRO(t *testing.T) {
- resourceOverlays := []string{
+func TestAndroidAppSet(t *testing.T) {
+ config := testConfig(nil)
+ config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64")
+ ctx := testAppContext(config, `
+ android_app_set {
+ name: "foo",
+ set: "prebuilts/apks/app.apks",
+ prerelease: true,
+ }`, nil)
+ run(t, ctx, config)
+ module := ctx.ModuleForTests("foo", "android_common")
+ const packedSplitApks = "extracted.zip"
+ params := module.Output(packedSplitApks)
+ if params.Rule == nil {
+ t.Errorf("expected output %s is missing", packedSplitApks)
+ }
+ if s := params.Args["allow-prereleased"]; s != "true" {
+ t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
+ }
+ if s := params.Args["partition"]; s != "system" {
+ t.Errorf("wrong partition value: '%s', expected 'system'", s)
+ }
+}
+
+func TestAndroidAppSet_Variants(t *testing.T) {
+ bp := `
+ android_app_set {
+ name: "foo",
+ set: "prebuilts/apks/app.apks",
+ }`
+ testCases := []struct {
+ name string
+ deviceArch *string
+ deviceSecondaryArch *string
+ aaptPrebuiltDPI []string
+ sdkVersion int
+ expected map[string]string
+ }{
+ {
+ name: "One",
+ deviceArch: proptools.StringPtr("x86"),
+ aaptPrebuiltDPI: []string{"ldpi", "xxhdpi"},
+ sdkVersion: 29,
+ expected: map[string]string{
+ "abis": "X86",
+ "allow-prereleased": "false",
+ "screen-densities": "LDPI,XXHDPI",
+ "sdk-version": "29",
+ "stem": "foo",
+ },
+ },
+ {
+ name: "Two",
+ deviceArch: proptools.StringPtr("x86_64"),
+ deviceSecondaryArch: proptools.StringPtr("x86"),
+ aaptPrebuiltDPI: nil,
+ sdkVersion: 30,
+ expected: map[string]string{
+ "abis": "X86_64,X86",
+ "allow-prereleased": "false",
+ "screen-densities": "all",
+ "sdk-version": "30",
+ "stem": "foo",
+ },
+ },
+ }
+
+ for _, test := range testCases {
+ config := testConfig(nil)
+ config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
+ config.TestProductVariables.DeviceArch = test.deviceArch
+ config.TestProductVariables.DeviceSecondaryArch = test.deviceSecondaryArch
+ ctx := testAppContext(config, bp, nil)
+ run(t, ctx, config)
+ module := ctx.ModuleForTests("foo", "android_common")
+ const packedSplitApks = "extracted.zip"
+ params := module.Output(packedSplitApks)
+ for k, v := range test.expected {
+ if actual := params.Args[k]; actual != v {
+ t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
+ test.name, k, actual, v)
+ }
+ }
+ }
+}
+
+func TestResourceDirs(t *testing.T) {
+ testCases := []struct {
+ name string
+ prop string
+ resources []string
+ }{
+ {
+ name: "no resource_dirs",
+ prop: "",
+ resources: []string{"res/res/values/strings.xml"},
+ },
+ {
+ name: "resource_dirs",
+ prop: `resource_dirs: ["res"]`,
+ resources: []string{"res/res/values/strings.xml"},
+ },
+ {
+ name: "empty resource_dirs",
+ prop: `resource_dirs: []`,
+ resources: nil,
+ },
+ }
+
+ fs := map[string][]byte{
+ "res/res/values/strings.xml": nil,
+ }
+
+ bp := `
+ android_app {
+ name: "foo",
+ %s
+ }
+ `
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ config := testConfig(nil)
+ ctx := testContext(config, fmt.Sprintf(bp, testCase.prop), fs)
+ run(t, ctx, config)
+
+ module := ctx.ModuleForTests("foo", "android_common")
+ resourceList := module.MaybeOutput("aapt2/res.list")
+
+ var resources []string
+ if resourceList.Rule != nil {
+ for _, compiledResource := range resourceList.Inputs.Strings() {
+ resources = append(resources, module.Output(compiledResource).Inputs.Strings()...)
+ }
+ }
+
+ if !reflect.DeepEqual(resources, testCase.resources) {
+ t.Errorf("expected resource files %q, got %q",
+ testCase.resources, resources)
+ }
+ })
+ }
+}
+
+func TestAndroidResources(t *testing.T) {
+ testCases := []struct {
+ name string
+ enforceRROTargets []string
+ enforceRROExcludedOverlays []string
+ resourceFiles map[string][]string
+ overlayFiles map[string][]string
+ rroDirs map[string][]string
+ }{
+ {
+ name: "no RRO",
+ enforceRROTargets: nil,
+ enforceRROExcludedOverlays: nil,
+ resourceFiles: map[string][]string{
+ "foo": nil,
+ "bar": {"bar/res/res/values/strings.xml"},
+ "lib": nil,
+ "lib2": {"lib2/res/res/values/strings.xml"},
+ },
+ overlayFiles: map[string][]string{
+ "foo": {
+ buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ buildDir + "/.intermediates/lib/android_common/package-res.apk",
+ buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "foo/res/res/values/strings.xml",
+ "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
+ "device/vendor/blah/overlay/foo/res/values/strings.xml",
+ "product/vendor/blah/overlay/foo/res/values/strings.xml",
+ },
+ "bar": {
+ "device/vendor/blah/static_overlay/bar/res/values/strings.xml",
+ "device/vendor/blah/overlay/bar/res/values/strings.xml",
+ },
+ "lib": {
+ buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "lib/res/res/values/strings.xml",
+ "device/vendor/blah/overlay/lib/res/values/strings.xml",
+ },
+ },
+ rroDirs: map[string][]string{
+ "foo": nil,
+ "bar": nil,
+ },
+ },
+ {
+ name: "enforce RRO on foo",
+ enforceRROTargets: []string{"foo"},
+ enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"},
+ resourceFiles: map[string][]string{
+ "foo": nil,
+ "bar": {"bar/res/res/values/strings.xml"},
+ "lib": nil,
+ "lib2": {"lib2/res/res/values/strings.xml"},
+ },
+ overlayFiles: map[string][]string{
+ "foo": {
+ buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ buildDir + "/.intermediates/lib/android_common/package-res.apk",
+ buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "foo/res/res/values/strings.xml",
+ "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
+ },
+ "bar": {
+ "device/vendor/blah/static_overlay/bar/res/values/strings.xml",
+ "device/vendor/blah/overlay/bar/res/values/strings.xml",
+ },
+ "lib": {
+ buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "lib/res/res/values/strings.xml",
+ "device/vendor/blah/overlay/lib/res/values/strings.xml",
+ },
+ },
+
+ rroDirs: map[string][]string{
+ "foo": {
+ "device:device/vendor/blah/overlay/foo/res",
+ // Enforce RRO on "foo" could imply RRO on static dependencies, but for now it doesn't.
+ // "device/vendor/blah/overlay/lib/res",
+ "product:product/vendor/blah/overlay/foo/res",
+ },
+ "bar": nil,
+ "lib": nil,
+ },
+ },
+ {
+ name: "enforce RRO on all",
+ enforceRROTargets: []string{"*"},
+ enforceRROExcludedOverlays: []string{
+ // Excluding specific apps/res directories also allowed.
+ "device/vendor/blah/static_overlay/foo",
+ "device/vendor/blah/static_overlay/bar/res",
+ },
+ resourceFiles: map[string][]string{
+ "foo": nil,
+ "bar": {"bar/res/res/values/strings.xml"},
+ "lib": nil,
+ "lib2": {"lib2/res/res/values/strings.xml"},
+ },
+ overlayFiles: map[string][]string{
+ "foo": {
+ buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ buildDir + "/.intermediates/lib/android_common/package-res.apk",
+ buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "foo/res/res/values/strings.xml",
+ "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
+ },
+ "bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
+ "lib": {
+ buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "lib/res/res/values/strings.xml",
+ },
+ },
+ rroDirs: map[string][]string{
+ "foo": {
+ "device:device/vendor/blah/overlay/foo/res",
+ "product:product/vendor/blah/overlay/foo/res",
+ // Lib dep comes after the direct deps
+ "device:device/vendor/blah/overlay/lib/res",
+ },
+ "bar": {"device:device/vendor/blah/overlay/bar/res"},
+ "lib": {"device:device/vendor/blah/overlay/lib/res"},
+ },
+ },
+ }
+
+ deviceResourceOverlays := []string{
"device/vendor/blah/overlay",
"device/vendor/blah/overlay2",
"device/vendor/blah/static_overlay",
}
+ productResourceOverlays := []string{
+ "product/vendor/blah/overlay",
+ }
+
fs := map[string][]byte{
"foo/res/res/values/strings.xml": nil,
"bar/res/res/values/strings.xml": nil,
+ "lib/res/res/values/strings.xml": nil,
+ "lib2/res/res/values/strings.xml": nil,
"device/vendor/blah/overlay/foo/res/values/strings.xml": nil,
"device/vendor/blah/overlay/bar/res/values/strings.xml": nil,
+ "device/vendor/blah/overlay/lib/res/values/strings.xml": nil,
"device/vendor/blah/static_overlay/foo/res/values/strings.xml": nil,
"device/vendor/blah/static_overlay/bar/res/values/strings.xml": nil,
"device/vendor/blah/overlay2/res/values/strings.xml": nil,
+ "product/vendor/blah/overlay/foo/res/values/strings.xml": nil,
}
bp := `
android_app {
name: "foo",
resource_dirs: ["foo/res"],
+ static_libs: ["lib", "lib3"],
}
android_app {
name: "bar",
resource_dirs: ["bar/res"],
}
+
+ android_library {
+ name: "lib",
+ resource_dirs: ["lib/res"],
+ static_libs: ["lib2"],
+ }
+
+ android_library {
+ name: "lib2",
+ resource_dirs: ["lib2/res"],
+ }
+
+ // This library has the same resources as lib (should not lead to dupe RROs)
+ android_library {
+ name: "lib3",
+ resource_dirs: ["lib/res"]
+ }
`
- for _, testCase := range testEnforceRROTests {
+ for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
config := testConfig(nil)
- config.TestProductVariables.ResourceOverlays = &resourceOverlays
+ config.TestProductVariables.DeviceResourceOverlays = deviceResourceOverlays
+ config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
if testCase.enforceRROTargets != nil {
- config.TestProductVariables.EnforceRROTargets = &testCase.enforceRROTargets
+ config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
}
if testCase.enforceRROExcludedOverlays != nil {
- config.TestProductVariables.EnforceRROExcludedOverlays = &testCase.enforceRROExcludedOverlays
+ config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
}
ctx := testAppContext(config, bp, fs)
run(t, ctx, config)
- getOverlays := func(moduleName string) ([]string, []string) {
- module := ctx.ModuleForTests(moduleName, "android_common")
- overlayCompiledPaths := module.Output("aapt2/overlay.list").Inputs.Strings()
+ resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
+ for _, o := range list {
+ res := module.MaybeOutput(o)
+ if res.Rule != nil {
+ // If the overlay is compiled as part of this module (i.e. a .arsc.flat file),
+ // verify the inputs to the .arsc.flat rule.
+ files = append(files, res.Inputs.Strings()...)
+ } else {
+ // Otherwise, verify the full path to the output of the other module
+ files = append(files, o)
+ }
+ }
+ return files
+ }
- var overlayFiles []string
- for _, o := range overlayCompiledPaths {
- overlayFiles = append(overlayFiles, module.Output(o).Inputs.Strings()...)
+ getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) {
+ module := ctx.ModuleForTests(moduleName, "android_common")
+ resourceList := module.MaybeOutput("aapt2/res.list")
+ if resourceList.Rule != nil {
+ resourceFiles = resourceListToFiles(module, resourceList.Inputs.Strings())
+ }
+ overlayList := module.MaybeOutput("aapt2/overlay.list")
+ if overlayList.Rule != nil {
+ overlayFiles = resourceListToFiles(module, overlayList.Inputs.Strings())
}
- rroDirs := module.Module().(*AndroidApp).rroDirs.Strings()
+ for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() {
+ var prefix string
+ if d.overlayType == device {
+ prefix = "device:"
+ } else if d.overlayType == product {
+ prefix = "product:"
+ } else {
+ t.Fatalf("Unexpected overlayType %d", d.overlayType)
+ }
+ rroDirs = append(rroDirs, prefix+d.path.String())
+ }
- return overlayFiles, rroDirs
+ return resourceFiles, overlayFiles, rroDirs
}
- fooOverlayFiles, fooRRODirs := getOverlays("foo")
- barOverlayFiles, barRRODirs := getOverlays("bar")
+ modules := []string{"foo", "bar", "lib", "lib2"}
+ for _, module := range modules {
+ resourceFiles, overlayFiles, rroDirs := getResources(module)
- if !reflect.DeepEqual(fooOverlayFiles, testCase.fooOverlayFiles) {
- t.Errorf("expected foo overlay files:\n %#v\n got:\n %#v",
- testCase.fooOverlayFiles, fooOverlayFiles)
+ if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[module]) {
+ t.Errorf("expected %s resource files:\n %#v\n got:\n %#v",
+ module, testCase.resourceFiles[module], resourceFiles)
+ }
+ if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[module]) {
+ t.Errorf("expected %s overlay files:\n %#v\n got:\n %#v",
+ module, testCase.overlayFiles[module], overlayFiles)
+ }
+ if !reflect.DeepEqual(rroDirs, testCase.rroDirs[module]) {
+ t.Errorf("expected %s rroDirs: %#v\n got:\n %#v",
+ module, testCase.rroDirs[module], rroDirs)
+ }
}
- if !reflect.DeepEqual(fooRRODirs, testCase.fooRRODirs) {
- t.Errorf("expected foo rroDirs: %#v\n got:\n %#v",
- testCase.fooRRODirs, fooRRODirs)
- }
-
- if !reflect.DeepEqual(barOverlayFiles, testCase.barOverlayFiles) {
- t.Errorf("expected bar overlay files:\n %#v\n got:\n %#v",
- testCase.barOverlayFiles, barOverlayFiles)
- }
- if !reflect.DeepEqual(barRRODirs, testCase.barRRODirs) {
- t.Errorf("expected bar rroDirs: %#v\n got:\n %#v",
- testCase.barRRODirs, barRRODirs)
- }
-
})
}
}
@@ -335,3 +627,611 @@
}
}
}
+
+func TestJNIABI(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ android_test {
+ name: "test",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_first",
+ no_framework_libs: true,
+ compile_multilib: "first",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_both",
+ no_framework_libs: true,
+ compile_multilib: "both",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_32",
+ no_framework_libs: true,
+ compile_multilib: "32",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_64",
+ no_framework_libs: true,
+ compile_multilib: "64",
+ jni_libs: ["libjni"],
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ abis []string
+ }{
+ {"test", []string{"arm64-v8a"}},
+ {"test_first", []string{"arm64-v8a"}},
+ {"test_both", []string{"arm64-v8a", "armeabi-v7a"}},
+ {"test_32", []string{"armeabi-v7a"}},
+ {"test_64", []string{"arm64-v8a"}},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ app := ctx.ModuleForTests(test.name, "android_common")
+ jniLibZip := app.Output("jnilibs.zip")
+ var abis []string
+ args := strings.Fields(jniLibZip.Args["jarArgs"])
+ for i := 0; i < len(args); i++ {
+ if args[i] == "-P" {
+ abis = append(abis, filepath.Base(args[i+1]))
+ i++
+ }
+ }
+ if !reflect.DeepEqual(abis, test.abis) {
+ t.Errorf("want abis %v, got %v", test.abis, abis)
+ }
+ })
+ }
+}
+
+func TestJNIPackaging(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ android_app {
+ name: "app",
+ jni_libs: ["libjni"],
+ }
+
+ android_app {
+ name: "app_noembed",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+
+ android_app {
+ name: "app_embed",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: true,
+ }
+
+ android_test {
+ name: "test",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_noembed",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+
+ android_test_helper_app {
+ name: "test_helper",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test_helper_app {
+ name: "test_helper_noembed",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ packaged bool
+ compressed bool
+ }{
+ {"app", false, false},
+ {"app_noembed", false, false},
+ {"app_embed", true, false},
+ {"test", true, false},
+ {"test_noembed", true, true},
+ {"test_helper", true, false},
+ {"test_helper_noembed", true, true},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ app := ctx.ModuleForTests(test.name, "android_common")
+ jniLibZip := app.MaybeOutput("jnilibs.zip")
+ if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
+ t.Errorf("expected jni packaged %v, got %v", w, g)
+ }
+
+ if jniLibZip.Rule != nil {
+ if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
+ t.Errorf("expected jni compressed %v, got %v", w, g)
+ }
+ }
+ })
+ }
+
+}
+
+func TestCertificates(t *testing.T) {
+ testCases := []struct {
+ name string
+ bp string
+ certificateOverride string
+ expected string
+ }{
+ {
+ name: "default",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `,
+ certificateOverride: "",
+ expected: "build/target/product/security/testkey.x509.pem build/target/product/security/testkey.pk8",
+ },
+ {
+ name: "module certificate property",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ certificate: ":new_certificate"
+ }
+
+ android_app_certificate {
+ name: "new_certificate",
+ certificate: "cert/new_cert",
+ }
+ `,
+ certificateOverride: "",
+ expected: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ },
+ {
+ name: "path certificate property",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ certificate: "expiredkey"
+ }
+ `,
+ certificateOverride: "",
+ expected: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
+ },
+ {
+ name: "certificate overrides",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ certificate: "expiredkey"
+ }
+
+ android_app_certificate {
+ name: "new_certificate",
+ certificate: "cert/new_cert",
+ }
+ `,
+ certificateOverride: "foo:new_certificate",
+ expected: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ config := testConfig(nil)
+ if test.certificateOverride != "" {
+ config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
+ }
+ ctx := testAppContext(config, test.bp, nil)
+
+ run(t, ctx, config)
+ foo := ctx.ModuleForTests("foo", "android_common")
+
+ signapk := foo.Output("foo.apk")
+ signFlags := signapk.Args["certificates"]
+ if test.expected != signFlags {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expected, signFlags)
+ }
+ })
+ }
+}
+
+func TestPackageNameOverride(t *testing.T) {
+ testCases := []struct {
+ name string
+ bp string
+ packageNameOverride string
+ expected []string
+ }{
+ {
+ name: "default",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `,
+ packageNameOverride: "",
+ expected: []string{
+ buildDir + "/.intermediates/foo/android_common/foo.apk",
+ buildDir + "/target/product/test_device/system/app/foo/foo.apk",
+ },
+ },
+ {
+ name: "overridden",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `,
+ packageNameOverride: "foo:bar",
+ expected: []string{
+ // The package apk should be still be the original name for test dependencies.
+ buildDir + "/.intermediates/foo/android_common/foo.apk",
+ buildDir + "/target/product/test_device/system/app/bar/bar.apk",
+ },
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ config := testConfig(nil)
+ if test.packageNameOverride != "" {
+ config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
+ }
+ ctx := testAppContext(config, test.bp, nil)
+
+ run(t, ctx, config)
+ foo := ctx.ModuleForTests("foo", "android_common")
+
+ outputs := foo.AllOutputs()
+ outputMap := make(map[string]bool)
+ for _, o := range outputs {
+ outputMap[o] = true
+ }
+ for _, e := range test.expected {
+ if _, exist := outputMap[e]; !exist {
+ t.Errorf("Can't find %q in output files.\nAll outputs:%v", e, outputs)
+ }
+ }
+ })
+ }
+}
+
+func TestInstrumentationTargetOverridden(t *testing.T) {
+ bp := `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+
+ android_test {
+ name: "bar",
+ instrumentation_for: "foo",
+ }
+ `
+ config := testConfig(nil)
+ config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
+ ctx := testAppContext(config, bp, nil)
+
+ run(t, ctx, config)
+
+ bar := ctx.ModuleForTests("bar", "android_common")
+ res := bar.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ e := "--rename-instrumentation-target-package org.dandroid.bp"
+ if !strings.Contains(aapt2Flags, e) {
+ t.Errorf("target package renaming flag, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
+ }
+}
+
+func TestOverrideAndroidApp(t *testing.T) {
+ ctx := testJava(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ certificate: "expiredkey",
+ overrides: ["baz"],
+ }
+
+ override_android_app {
+ name: "bar",
+ base: "foo",
+ certificate: ":new_certificate",
+ }
+
+ android_app_certificate {
+ name: "new_certificate",
+ certificate: "cert/new_cert",
+ }
+
+ override_android_app {
+ name: "baz",
+ base: "foo",
+ package_name: "org.dandroid.bp",
+ }
+ `)
+
+ expectedVariants := []struct {
+ variantName string
+ apkName string
+ apkPath string
+ signFlag string
+ overrides []string
+ aaptFlag string
+ }{
+ {
+ variantName: "android_common",
+ apkPath: "/target/product/test_device/system/app/foo/foo.apk",
+ signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
+ overrides: []string{"baz"},
+ aaptFlag: "",
+ },
+ {
+ variantName: "bar_android_common",
+ apkPath: "/target/product/test_device/system/app/bar/bar.apk",
+ signFlag: "cert/new_cert.x509.pem cert/new_cert.pk8",
+ overrides: []string{"baz", "foo"},
+ aaptFlag: "",
+ },
+ {
+ variantName: "baz_android_common",
+ apkPath: "/target/product/test_device/system/app/baz/baz.apk",
+ signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
+ overrides: []string{"baz", "foo"},
+ aaptFlag: "--rename-manifest-package org.dandroid.bp",
+ },
+ }
+ for _, expected := range expectedVariants {
+ variant := ctx.ModuleForTests("foo", expected.variantName)
+
+ // Check the final apk name
+ outputs := variant.AllOutputs()
+ expectedApkPath := buildDir + expected.apkPath
+ found := false
+ for _, o := range outputs {
+ if o == expectedApkPath {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
+ }
+
+ // Check the certificate paths
+ signapk := variant.Output("foo.apk")
+ signFlag := signapk.Args["certificates"]
+ if expected.signFlag != signFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.signFlag, signFlag)
+ }
+
+ // Check if the overrides field values are correctly aggregated.
+ mod := variant.Module().(*AndroidApp)
+ if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
+ t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
+ expected.overrides, mod.appProperties.Overrides)
+ }
+
+ // Check the package renaming flag, if exists.
+ res := variant.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ if !strings.Contains(aapt2Flags, expected.aaptFlag) {
+ t.Errorf("package renaming flag, %q is missing in aapt2 link flags, %q", expected.aaptFlag, aapt2Flags)
+ }
+ }
+}
+
+func TestEmbedNotice(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ static_libs: ["javalib"],
+ jni_libs: ["libjni"],
+ notice: "APP_NOTICE",
+ embed_notices: true,
+ }
+
+ // No embed_notice flag
+ android_app {
+ name: "bar",
+ srcs: ["a.java"],
+ jni_libs: ["libjni"],
+ notice: "APP_NOTICE",
+ }
+
+ // No NOTICE files
+ android_app {
+ name: "baz",
+ srcs: ["a.java"],
+ embed_notices: true,
+ }
+
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ notice: "LIB_NOTICE",
+ }
+
+ java_library {
+ name: "javalib",
+ srcs: [
+ ":gen",
+ ],
+ }
+
+ genrule {
+ name: "gen",
+ tools: ["gentool"],
+ out: ["gen.java"],
+ notice: "GENRULE_NOTICE",
+ }
+
+ java_binary_host {
+ name: "gentool",
+ srcs: ["b.java"],
+ notice: "TOOL_NOTICE",
+ }
+ `)
+
+ // foo has NOTICE files to process, and embed_notices is true.
+ foo := ctx.ModuleForTests("foo", "android_common")
+ // verify merge notices rule.
+ mergeNotices := foo.Rule("mergeNoticesRule")
+ noticeInputs := mergeNotices.Inputs.Strings()
+ // TOOL_NOTICE should be excluded as it's a host module.
+ if len(mergeNotices.Inputs) != 3 {
+ t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
+ }
+ if !inList("APP_NOTICE", noticeInputs) {
+ t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
+ }
+ if !inList("LIB_NOTICE", noticeInputs) {
+ t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
+ }
+ if !inList("GENRULE_NOTICE", noticeInputs) {
+ t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
+ }
+ // aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
+ res := foo.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
+ if !strings.Contains(aapt2Flags, e) {
+ t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
+ }
+
+ // bar has NOTICE files to process, but embed_notices is not set.
+ bar := ctx.ModuleForTests("bar", "android_common")
+ mergeNotices = bar.MaybeRule("mergeNoticesRule")
+ if mergeNotices.Rule != nil {
+ t.Errorf("mergeNotices shouldn't have run for bar")
+ }
+
+ // baz's embed_notice is true, but it doesn't have any NOTICE files.
+ baz := ctx.ModuleForTests("baz", "android_common")
+ mergeNotices = baz.MaybeRule("mergeNoticesRule")
+ if mergeNotices.Rule != nil {
+ t.Errorf("mergeNotices shouldn't have run for baz")
+ }
+}
+
+func TestUncompressDex(t *testing.T) {
+ testCases := []struct {
+ name string
+ bp string
+
+ uncompressedPlatform bool
+ uncompressedUnbundled bool
+ }{
+ {
+ name: "normal",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `,
+ uncompressedPlatform: true,
+ uncompressedUnbundled: false,
+ },
+ {
+ name: "use_embedded_dex",
+ bp: `
+ android_app {
+ name: "foo",
+ use_embedded_dex: true,
+ srcs: ["a.java"],
+ }
+ `,
+ uncompressedPlatform: true,
+ uncompressedUnbundled: true,
+ },
+ {
+ name: "privileged",
+ bp: `
+ android_app {
+ name: "foo",
+ privileged: true,
+ srcs: ["a.java"],
+ }
+ `,
+ uncompressedPlatform: true,
+ uncompressedUnbundled: true,
+ },
+ }
+
+ test := func(t *testing.T, bp string, want bool, unbundled bool) {
+ t.Helper()
+
+ config := testConfig(nil)
+ if unbundled {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+ }
+
+ ctx := testAppContext(config, bp, nil)
+
+ run(t, ctx, config)
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+ dex := foo.Rule("r8")
+ uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
+ aligned := foo.MaybeRule("zipalign").Rule != nil
+
+ if uncompressedInDexJar != want {
+ t.Errorf("want uncompressed in dex %v, got %v", want, uncompressedInDexJar)
+ }
+
+ if aligned != want {
+ t.Errorf("want aligned %v, got %v", want, aligned)
+ }
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Run("platform", func(t *testing.T) {
+ test(t, tt.bp, tt.uncompressedPlatform, false)
+ })
+ t.Run("unbundled", func(t *testing.T) {
+ test(t, tt.bp, tt.uncompressedUnbundled, true)
+ })
+ })
+ }
+}
diff --git a/java/builder.go b/java/builder.go
index bc95599..0e7574e 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -24,9 +24,10 @@
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/java/config"
+ "android/soong/remoteexec"
)
var (
@@ -38,15 +39,18 @@
// this, all java rules write into separate directories and then are combined into a .jar file
// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
// .srcjar files are unzipped into a temporary directory when compiled with javac.
- javac = pctx.AndroidGomaStaticRule("javac",
+ // TODO(b/143658984): goma can't handle the --system argument to javac.
+ javac, javacRE = remoteexec.StaticRules(pctx, "javac",
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
- `$javacFlags $bootClasspath $classpath ` +
+ `(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
+ `${config.SoongJavacWrapper} $reTemplate${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
+ `$processorpath $processor $javacFlags $bootClasspath $classpath ` +
`-source $javaVersion -target $javaVersion ` +
- `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list && ` +
- `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+ `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
+ `rm -rf "$srcJarDir"`,
CommandDeps: []string{
"${config.JavacCmd}",
"${config.SoongZipCmd}",
@@ -55,52 +59,25 @@
CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
Rspfile: "$out.rsp",
RspfileContent: "$in",
- },
- "javacFlags", "bootClasspath", "classpath", "srcJars", "srcJarDir",
- "outDir", "annoDir", "javaVersion")
+ }, &remoteexec.REParams{
+ Labels: map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
+ ExecStrategy: "${config.REJavacExecStrategy}",
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ }, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
+ "outDir", "annoDir", "javaVersion"}, nil)
- kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
+ extractMatchingApks = pctx.StaticRule(
+ "extractMatchingApks",
blueprint.RuleParams{
- Command: `rm -rf "$outDir" "$srcJarDir" && mkdir -p "$outDir" "$srcJarDir" && ` +
- `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.GenKotlinBuildFileCmd} $classpath $outDir $out.rsp $srcJarDir/list > $outDir/kotlinc-build.xml &&` +
- `${config.KotlincCmd} $kotlincFlags ` +
- `-jvm-target $kotlinJvmTarget -Xbuild-file=$outDir/kotlinc-build.xml && ` +
- `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
- CommandDeps: []string{
- "${config.KotlincCmd}",
- "${config.KotlinCompilerJar}",
- "${config.GenKotlinBuildFileCmd}",
- "${config.SoongZipCmd}",
- "${config.ZipSyncCmd}",
- },
- Rspfile: "$out.rsp",
- RspfileContent: `$in`,
+ Command: `rm -rf "$out" && ` +
+ `${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
+ `-sdk-version=${sdk-version} -abis=${abis} ` +
+ `--screen-densities=${screen-densities} --stem=${stem} ` +
+ `-apkcerts=${apkcerts} -partition=${partition} ` +
+ `${in}`,
+ CommandDeps: []string{"${config.ExtractApksCmd}"},
},
- "kotlincFlags", "classpath", "srcJars", "srcJarDir", "outDir", "kotlinJvmTarget")
-
- errorprone = pctx.AndroidStaticRule("errorprone",
- blueprint.RuleParams{
- Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
- `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.SoongJavacWrapper} ${config.ErrorProneCmd} ` +
- `$javacFlags $bootClasspath $classpath ` +
- `-source $javaVersion -target $javaVersion ` +
- `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list && ` +
- `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
- CommandDeps: []string{
- "${config.JavaCmd}",
- "${config.ErrorProneJavacJar}",
- "${config.ErrorProneJar}",
- "${config.SoongZipCmd}",
- "${config.ZipSyncCmd}",
- },
- CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
- Rspfile: "$out.rsp",
- RspfileContent: "$in",
- },
- "javacFlags", "bootClasspath", "classpath", "srcJars", "srcJarDir",
- "outDir", "annoDir", "javaVersion")
+ "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition")
turbine = pctx.AndroidStaticRule("turbine",
blueprint.RuleParams{
@@ -108,7 +85,7 @@
`${config.JavaCmd} -jar ${config.TurbineJar} --output $out.tmp ` +
`--temp_dir "$outDir" --sources @$out.rsp --source_jars $srcJars ` +
`--javacopts ${config.CommonJdkFlags} ` +
- `$javacFlags -source $javaVersion -target $javaVersion $bootClasspath $classpath && ` +
+ `$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` +
`${config.Ziptime} $out.tmp && ` +
`(if cmp -s $out.tmp $out ; then rm $out.tmp ; else mv $out.tmp $out ; fi )`,
CommandDeps: []string{
@@ -131,6 +108,15 @@
},
"jarArgs")
+ zip = pctx.AndroidStaticRule("zip",
+ blueprint.RuleParams{
+ Command: `${config.SoongZipCmd} -o $out @$out.rsp`,
+ CommandDeps: []string{"${config.SoongZipCmd}"},
+ Rspfile: "$out.rsp",
+ RspfileContent: "$jarArgs",
+ },
+ "jarArgs")
+
combineJar = pctx.AndroidStaticRule("combineJar",
blueprint.RuleParams{
Command: `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out $in`,
@@ -144,57 +130,59 @@
CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
},
"rulesFile")
+
+ packageCheck = pctx.AndroidStaticRule("packageCheck",
+ blueprint.RuleParams{
+ Command: "rm -f $out && " +
+ "${config.PackageCheckCmd} $in $packages && " +
+ "touch $out",
+ CommandDeps: []string{"${config.PackageCheckCmd}"},
+ },
+ "packages")
+
+ jetifier = pctx.AndroidStaticRule("jetifier",
+ blueprint.RuleParams{
+ Command: "${config.JavaCmd} -jar ${config.JetifierJar} -l error -o $out -i $in",
+ CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"},
+ },
+ )
+
+ zipalign = pctx.AndroidStaticRule("zipalign",
+ blueprint.RuleParams{
+ Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
+ "${config.ZipAlign} -f -p 4 $in $out; " +
+ "else " +
+ "cp -f $in $out; " +
+ "fi",
+ CommandDeps: []string{"${config.ZipAlign}"},
+ },
+ )
)
func init() {
+ pctx.Import("android/soong/android")
pctx.Import("android/soong/java/config")
+ pctx.Import("android/soong/remoteexec")
}
type javaBuilderFlags struct {
javacFlags string
bootClasspath classpath
classpath classpath
+ processorPath classpath
+ processor string
systemModules classpath
aidlFlags string
+ aidlDeps android.Paths
javaVersion string
errorProneExtraJavacFlags string
+ errorProneProcessorPath classpath
kotlincFlags string
kotlincClasspath classpath
- protoFlags []string
- protoOutTypeFlag string // The flag itself: --java_out
- protoOutParams string // Parameters to that flag: --java_out=$protoOutParams:$outDir
- protoRoot bool
-}
-
-func TransformKotlinToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
- srcFiles, srcJars android.Paths,
- flags javaBuilderFlags) {
-
- inputs := append(android.Paths(nil), srcFiles...)
-
- var deps android.Paths
- deps = append(deps, flags.kotlincClasspath...)
- deps = append(deps, srcJars...)
-
- ctx.Build(pctx, android.BuildParams{
- Rule: kotlinc,
- Description: "kotlinc",
- Output: outputFile,
- Inputs: inputs,
- Implicits: deps,
- Args: map[string]string{
- "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"),
- "kotlincFlags": flags.kotlincFlags,
- "srcJars": strings.Join(srcJars.Strings(), " "),
- "outDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
- // http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
- "kotlinJvmTarget": "1.8",
- },
- })
+ proto android.ProtoFlags
}
func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
@@ -206,26 +194,24 @@
desc += strconv.Itoa(shardIdx)
}
- transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc, javac)
+ transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
}
func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
- if config.ErrorProneJar == "" {
- ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
- }
+ flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
if len(flags.errorProneExtraJavacFlags) > 0 {
if len(flags.javacFlags) > 0 {
- flags.javacFlags = flags.errorProneExtraJavacFlags + " " + flags.javacFlags
+ flags.javacFlags += " " + flags.errorProneExtraJavacFlags
} else {
flags.javacFlags = flags.errorProneExtraJavacFlags
}
}
transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
- "errorprone", "errorprone", errorprone)
+ "errorprone", "errorprone")
}
func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -242,7 +228,7 @@
// ensure java does not fall back to the default bootclasspath.
bootClasspath = `--bootclasspath ""`
} else {
- bootClasspath = flags.bootClasspath.FormJavaClassPath("--bootclasspath")
+ bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath "), " ")
}
ctx.Build(pctx, android.BuildParams{
@@ -255,7 +241,7 @@
"javacFlags": flags.javacFlags,
"bootClasspath": bootClasspath,
"srcJars": strings.Join(srcJars.Strings(), " "),
- "classpath": flags.classpath.FormJavaClassPath("--classpath"),
+ "classpath": strings.Join(flags.classpath.FormTurbineClasspath("--classpath "), " "),
"outDir": android.PathForModuleOut(ctx, "turbine", "classes").String(),
"javaVersion": flags.javaVersion,
},
@@ -274,7 +260,7 @@
func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath,
shardIdx int, srcFiles, srcJars android.Paths,
flags javaBuilderFlags, deps android.Paths,
- intermediatesDir, desc string, rule blueprint.Rule) {
+ intermediatesDir, desc string) {
deps = append(deps, srcJars...)
@@ -294,6 +280,12 @@
}
deps = append(deps, flags.classpath...)
+ deps = append(deps, flags.processorPath...)
+
+ processor := "-proc:none"
+ if flags.processor != "" {
+ processor = "-processor " + flags.processor
+ }
srcJarDir := "srcjars"
outDir := "classes"
@@ -304,6 +296,10 @@
outDir = filepath.Join(shardDir, outDir)
annoDir = filepath.Join(shardDir, annoDir)
}
+ rule := javac
+ if ctx.Config().IsEnvTrue("RBE_JAVAC") {
+ rule = javacRE
+ }
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: desc,
@@ -314,6 +310,8 @@
"javacFlags": flags.javacFlags,
"bootClasspath": bootClasspath,
"classpath": flags.classpath.FormJavaClassPath("-classpath"),
+ "processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
+ "processor": processor,
"srcJars": strings.Join(srcJars.Strings(), " "),
"srcJarDir": android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(),
"outDir": android.PathForModuleOut(ctx, intermediatesDir, outDir).String(),
@@ -332,13 +330,14 @@
Output: outputFile,
Implicits: deps,
Args: map[string]string{
- "jarArgs": strings.Join(jarArgs, " "),
+ "jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
},
})
}
func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath, desc string,
- jars android.Paths, manifest android.OptionalPath, stripDirs bool, dirsToStrip []string) {
+ jars android.Paths, manifest android.OptionalPath, stripDirEntries bool, filesToStrip []string,
+ dirsToStrip []string) {
var deps android.Paths
@@ -348,17 +347,19 @@
deps = append(deps, manifest.Path())
}
- if dirsToStrip != nil {
- for _, dir := range dirsToStrip {
- jarArgs = append(jarArgs, "-stripDir ", dir)
- }
+ for _, dir := range dirsToStrip {
+ jarArgs = append(jarArgs, "-stripDir ", dir)
+ }
+
+ for _, file := range filesToStrip {
+ jarArgs = append(jarArgs, "-stripFile ", file)
}
// Remove any module-info.class files that may have come from prebuilt jars, they cause problems
// for downstream tools like desugar.
jarArgs = append(jarArgs, "-stripFile module-info.class")
- if stripDirs {
+ if stripDirEntries {
jarArgs = append(jarArgs, "-D")
}
@@ -388,11 +389,57 @@
})
}
+func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath,
+ classesJar android.Path, permittedPackages []string) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: packageCheck,
+ Description: "packageCheck",
+ Output: outputFile,
+ Input: classesJar,
+ Args: map[string]string{
+ "packages": strings.Join(permittedPackages, " "),
+ },
+ })
+}
+
+func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePath,
+ inputFile android.Path) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: jetifier,
+ Description: "jetifier",
+ Output: outputFile,
+ Input: inputFile,
+ })
+}
+
+func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.WriteFile,
+ Description: "manifest",
+ Output: outputFile,
+ Args: map[string]string{
+ "content": "Main-Class: " + mainClass + "\n",
+ },
+ })
+}
+
+func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zipalign,
+ Description: "align",
+ Input: inputFile,
+ Output: outputFile,
+ })
+}
+
type classpath []android.Path
func (x *classpath) FormJavaClassPath(optName string) string {
+ if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
+ optName += " "
+ }
if len(*x) > 0 {
- return optName + " " + strings.Join(x.Strings(), ":")
+ return optName + strings.Join(x.Strings(), ":")
} else {
return ""
}
@@ -413,13 +460,13 @@
}
}
-func (x *classpath) FormDesugarClasspath(optName string) []string {
+func (x *classpath) FormTurbineClasspath(optName string) []string {
if x == nil || *x == nil {
return nil
}
flags := make([]string, len(*x))
for i, v := range *x {
- flags[i] = optName + " " + v.String()
+ flags[i] = optName + v.String()
}
return flags
diff --git a/java/config/config.go b/java/config/config.go
index 980db3b..c3523be 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -22,14 +22,17 @@
_ "github.com/google/blueprint/bootstrap"
"android/soong/android"
+ "android/soong/remoteexec"
)
var (
pctx = android.NewPackageContext("android/soong/java/config")
- DefaultBootclasspathLibraries = []string{"core-oj", "core-libart"}
- DefaultSystemModules = "core-system-modules"
- DefaultLibraries = []string{"ext", "framework", "okhttp"}
+ DefaultBootclasspathLibraries = []string{"core.platform.api.stubs", "core-lambda-stubs"}
+ DefaultSystemModules = "core-platform-api-stubs-system-modules"
+ DefaultLibraries = []string{"ext", "framework", "updatable_media_stubs"}
+ DefaultLambdaStubsLibrary = "core-lambda-stubs"
+ SdkLambdaStubsPath = "prebuilts/sdk/tools/core-lambda-stubs.jar"
DefaultJacocoExcludeFilter = []string{"org.junit.*", "org.jacoco.*", "org.mockito.*"}
@@ -39,8 +42,10 @@
"services",
"android.car",
"android.car7",
+ "conscrypt",
"core-oj",
"core-libart",
+ "updatable-media",
}
)
@@ -49,6 +54,7 @@
pctx.StaticVariable("JavacHeapSize", "2048M")
pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
+ pctx.StaticVariable("DexFlags", "-JXX:+TieredCompilation -JXX:TieredStopAtLevel=1")
pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
`-Xmaxerrs 9999999`,
@@ -87,26 +93,17 @@
pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh")
pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
+ pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh")
pctx.HostBinToolVariable("ExtractJarPackagesCmd", "extract_jar_packages")
pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips")
pctx.HostBinToolVariable("Zip2ZipCmd", "zip2zip")
pctx.HostBinToolVariable("ZipSyncCmd", "zipsync")
- pctx.VariableFunc("DxCmd", func(ctx android.PackageVarContext) string {
- config := ctx.Config()
- if config.IsEnvFalse("USE_D8") {
- if config.UnbundledBuild() || config.IsPdkBuild() {
- return "prebuilts/build-tools/common/bin/dx"
- } else {
- return pctx.HostBinToolPath(ctx, "dx").String()
- }
- } else {
- return pctx.HostBinToolPath(ctx, "d8-compat-dx").String()
- }
- })
+ pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
pctx.HostBinToolVariable("D8Cmd", "d8")
pctx.HostBinToolVariable("R8Cmd", "r8-compat-proguard")
-
+ pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
+ pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
turbine := "turbine.jar"
if ctx.Config().UnbundledBuild() {
@@ -117,18 +114,21 @@
})
pctx.HostJavaToolVariable("JarjarCmd", "jarjar.jar")
- pctx.HostJavaToolVariable("DesugarJar", "desugar.jar")
pctx.HostJavaToolVariable("JsilverJar", "jsilver.jar")
pctx.HostJavaToolVariable("DoclavaJar", "doclava.jar")
+ pctx.HostJavaToolVariable("MetalavaJar", "metalava.jar")
+ pctx.HostJavaToolVariable("DokkaJar", "dokka.jar")
+ pctx.HostJavaToolVariable("JetifierJar", "jetifier.jar")
+ pctx.HostJavaToolVariable("R8Jar", "r8-compat-proguard.jar")
+ pctx.HostJavaToolVariable("D8Jar", "d8.jar")
pctx.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
+ pctx.HostBinToolVariable("DexpreoptGen", "dexpreopt_gen")
- pctx.VariableFunc("JavacWrapper", func(ctx android.PackageVarContext) string {
- if override := ctx.Config().Getenv("JAVAC_WRAPPER"); override != "" {
- return override + " "
- }
- return ""
- })
+ pctx.VariableFunc("REJavaPool", remoteexec.EnvOverrideFunc("RBE_JAVA_POOL", "java16"))
+ pctx.VariableFunc("REJavacExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAVAC_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
+ pctx.VariableFunc("RED8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_D8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
+ pctx.VariableFunc("RER8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_R8_EXEC_STRATEGY", remoteexec.RemoteLocalFallbackExecStrategy))
pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
@@ -143,4 +143,13 @@
}
hostBinToolVariableWithPrebuilt("Aapt2Cmd", "prebuilts/sdk/tools", "aapt2")
+
+ pctx.SourcePathVariable("ManifestFixerCmd", "build/soong/scripts/manifest_fixer.py")
+
+ pctx.HostBinToolVariable("ManifestMergerCmd", "manifest-merger")
+
+ pctx.HostBinToolVariable("ZipAlign", "zipalign")
+
+ pctx.HostBinToolVariable("Class2Greylist", "class2greylist")
+ pctx.HostBinToolVariable("HiddenAPI", "hiddenapi")
}
diff --git a/java/config/error_prone.go b/java/config/error_prone.go
index f203234..48681b5 100644
--- a/java/config/error_prone.go
+++ b/java/config/error_prone.go
@@ -14,34 +14,40 @@
package config
-import "android/soong/android"
+import (
+ "strings"
+
+ "android/soong/android"
+)
var (
// These will be filled out by external/error_prone/soong/error_prone.go if it is available
- ErrorProneJavacJar string
- ErrorProneJar string
- ErrorProneClasspath string
- ErrorProneChecksError string
- ErrorProneFlags string
+ ErrorProneClasspath []string
+ ErrorProneChecksError []string
+ ErrorProneChecksWarning []string
+ ErrorProneChecksDefaultDisabled []string
+ ErrorProneChecksOff []string
+ ErrorProneFlags []string
)
// Wrapper that grabs value of val late so it can be initialized by a later module's init function
-func errorProneVar(name string, val *string) {
+func errorProneVar(name string, val *[]string, sep string) {
pctx.VariableFunc(name, func(android.PackageVarContext) string {
- return *val
+ return strings.Join(*val, sep)
})
}
func init() {
- errorProneVar("ErrorProneJar", &ErrorProneJar)
- errorProneVar("ErrorProneJavacJar", &ErrorProneJavacJar)
- errorProneVar("ErrorProneClasspath", &ErrorProneClasspath)
- errorProneVar("ErrorProneChecksError", &ErrorProneChecksError)
- errorProneVar("ErrorProneFlags", &ErrorProneFlags)
-
- pctx.StaticVariable("ErrorProneCmd",
- "${JavaCmd} -Xmx${JavacHeapSize} -Xbootclasspath/p:${ErrorProneJavacJar} "+
- "-cp ${ErrorProneJar}:${ErrorProneClasspath} "+
- "${ErrorProneFlags} ${CommonJdkFlags} ${ErrorProneChecksError}")
-
+ errorProneVar("ErrorProneClasspath", &ErrorProneClasspath, ":")
+ errorProneVar("ErrorProneChecksError", &ErrorProneChecksError, " ")
+ errorProneVar("ErrorProneChecksWarning", &ErrorProneChecksWarning, " ")
+ errorProneVar("ErrorProneChecksDefaultDisabled", &ErrorProneChecksDefaultDisabled, " ")
+ errorProneVar("ErrorProneChecksOff", &ErrorProneChecksOff, " ")
+ errorProneVar("ErrorProneFlags", &ErrorProneFlags, " ")
+ pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{
+ "${ErrorProneChecksOff}",
+ "${ErrorProneChecksError}",
+ "${ErrorProneChecksWarning}",
+ "${ErrorProneChecksDefaultDisabled}",
+ }, " "))
}
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 35f9e9d..2af7b3c 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -14,12 +14,27 @@
package config
+import "strings"
+
var (
- KotlinStdlibJar = "external/kotlinc/lib/kotlin-stdlib.jar"
+ KotlinStdlibJar = "external/kotlinc/lib/kotlin-stdlib.jar"
+ KotlincIllegalFlags = []string{
+ "-no-jdk",
+ "-no-stdlib",
+ }
)
func init() {
pctx.SourcePathVariable("KotlincCmd", "external/kotlinc/bin/kotlinc")
pctx.SourcePathVariable("KotlinCompilerJar", "external/kotlinc/lib/kotlin-compiler.jar")
+ pctx.SourcePathVariable("KotlinKaptJar", "external/kotlinc/lib/kotlin-annotation-processing.jar")
pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar)
+
+ // These flags silence "Illegal reflective access" warnings when running kotlinc in OpenJDK9
+ pctx.StaticVariable("KotlincSuppressJDK9Warnings", strings.Join([]string{
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
+ "-J--add-opens=java.base/sun.net.www.protocol.jar=ALL-UNNAMED",
+ }, " "))
}
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 27c7daa..6881caf 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -46,37 +46,39 @@
ctx.Strict("JAVADOC", "${JavadocCmd}")
ctx.Strict("COMMON_JDK_FLAGS", "${CommonJdkFlags}")
- if ctx.Config().UseD8Desugar() {
- ctx.Strict("DX", "${D8Cmd}")
- ctx.Strict("DX_COMMAND", "${D8Cmd} -JXms16M -JXmx2048M")
- ctx.Strict("USE_D8_DESUGAR", "true")
- } else {
- ctx.Strict("DX", "${DxCmd}")
- ctx.Strict("DX_COMMAND", "${DxCmd} -JXms16M -JXmx2048M")
- ctx.Strict("USE_D8_DESUGAR", "false")
- }
+ ctx.Strict("DX", "${D8Cmd}")
+ ctx.Strict("DX_COMMAND", "${D8Cmd} -JXms16M -JXmx2048M")
ctx.Strict("R8_COMPAT_PROGUARD", "${R8Cmd}")
ctx.Strict("TURBINE", "${TurbineJar}")
- if ctx.Config().IsEnvTrue("RUN_ERROR_PRONE") {
- ctx.Strict("TARGET_JAVAC", "${ErrorProneCmd}")
- ctx.Strict("HOST_JAVAC", "${ErrorProneCmd}")
- } else {
- ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
- ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
+ if ctx.Config().RunErrorProne() {
+ ctx.Strict("ERROR_PRONE_JARS", strings.Join(ErrorProneClasspath, " "))
+ ctx.Strict("ERROR_PRONE_FLAGS", "${ErrorProneFlags}")
+ ctx.Strict("ERROR_PRONE_CHECKS", "${ErrorProneChecks}")
}
- if ctx.Config().UseOpenJDK9() {
- ctx.Strict("JLINK", "${JlinkCmd}")
- ctx.Strict("JMOD", "${JmodCmd}")
- }
+ ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
+ ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
+
+ ctx.Strict("JLINK", "${JlinkCmd}")
+ ctx.Strict("JMOD", "${JmodCmd}")
ctx.Strict("SOONG_JAVAC_WRAPPER", "${SoongJavacWrapper}")
+ ctx.Strict("DEXPREOPT_GEN", "${DexpreoptGen}")
ctx.Strict("ZIPSYNC", "${ZipSyncCmd}")
ctx.Strict("JACOCO_CLI_JAR", "${JacocoCLIJar}")
ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultJacocoExcludeFilter, ","))
ctx.Strict("EXTRACT_JAR_PACKAGES", "${ExtractJarPackagesCmd}")
+
+ ctx.Strict("MANIFEST_FIXER", "${ManifestFixerCmd}")
+
+ ctx.Strict("ANDROID_MANIFEST_MERGER", "${ManifestMergerCmd}")
+
+ ctx.Strict("CLASS2GREYLIST", "${Class2Greylist}")
+ ctx.Strict("HIDDENAPI", "${HiddenAPI}")
+
+ ctx.Strict("DEX_FLAGS", "${DexFlags}")
}
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
new file mode 100644
index 0000000..9f40a6c
--- /dev/null
+++ b/java/device_host_converter.go
@@ -0,0 +1,131 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+type DeviceHostConverter struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ properties DeviceHostConverterProperties
+
+ headerJars android.Paths
+ implementationJars android.Paths
+ implementationAndResourceJars android.Paths
+ resourceJars android.Paths
+}
+
+type DeviceHostConverterProperties struct {
+ // List of modules whose contents will be visible to modules that depend on this module.
+ Libs []string
+}
+
+type DeviceForHost struct {
+ DeviceHostConverter
+}
+
+// java_device_for_host makes the classes.jar output of a device java_library module available to host
+// java_library modules.
+//
+// It is rarely necessary, and its used is restricted to a few whitelisted projects.
+func DeviceForHostFactory() android.Module {
+ module := &DeviceForHost{}
+
+ module.AddProperties(&module.properties)
+
+ InitJavaModule(module, android.HostSupported)
+ return module
+}
+
+type HostForDevice struct {
+ DeviceHostConverter
+}
+
+// java_host_for_device makes the classes.jar output of a host java_library module available to device
+// java_library modules.
+//
+// It is rarely necessary, and its used is restricted to a few whitelisted projects.
+func HostForDeviceFactory() android.Module {
+ module := &HostForDevice{}
+
+ module.AddProperties(&module.properties)
+
+ InitJavaModule(module, android.DeviceSupported)
+ return module
+}
+
+var deviceHostConverterDepTag = dependencyTag{name: "device_host_converter"}
+
+func (d *DeviceForHost) DepsMutator(ctx android.BottomUpMutatorContext) {
+ variation := []blueprint.Variation{{Mutator: "arch", Variation: "android_common"}}
+ ctx.AddFarVariationDependencies(variation, deviceHostConverterDepTag, d.properties.Libs...)
+}
+
+func (d *HostForDevice) DepsMutator(ctx android.BottomUpMutatorContext) {
+ variation := []blueprint.Variation{{Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant}}
+ ctx.AddFarVariationDependencies(variation, deviceHostConverterDepTag, d.properties.Libs...)
+}
+
+func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if len(d.properties.Libs) < 1 {
+ ctx.PropertyErrorf("libs", "at least one dependency is required")
+ }
+
+ ctx.VisitDirectDepsWithTag(deviceHostConverterDepTag, func(m android.Module) {
+ if dep, ok := m.(Dependency); ok {
+ d.headerJars = append(d.headerJars, dep.HeaderJars()...)
+ d.implementationJars = append(d.implementationJars, dep.ImplementationJars()...)
+ d.implementationAndResourceJars = append(d.implementationAndResourceJars, dep.ImplementationAndResourcesJars()...)
+ d.resourceJars = append(d.resourceJars, dep.ResourceJars()...)
+ } else {
+ ctx.PropertyErrorf("libs", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
+ }
+ })
+}
+
+var _ Dependency = (*DeviceHostConverter)(nil)
+
+func (d *DeviceHostConverter) HeaderJars() android.Paths {
+ return d.headerJars
+}
+
+func (d *DeviceHostConverter) ImplementationJars() android.Paths {
+ return d.implementationJars
+}
+
+func (d *DeviceHostConverter) ResourceJars() android.Paths {
+ return d.resourceJars
+}
+
+func (d *DeviceHostConverter) ImplementationAndResourcesJars() android.Paths {
+ return d.implementationAndResourceJars
+}
+
+func (d *DeviceHostConverter) DexJar() android.Path {
+ return nil
+}
+
+func (d *DeviceHostConverter) AidlIncludeDirs() android.Paths {
+ return nil
+}
+
+func (d *DeviceHostConverter) ExportedSdkLibs() []string {
+ return nil
+}
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
new file mode 100644
index 0000000..146bf6f
--- /dev/null
+++ b/java/device_host_converter_test.go
@@ -0,0 +1,186 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestDeviceForHost(t *testing.T) {
+ bp := `
+ java_library {
+ name: "device_module",
+ srcs: ["a.java"],
+ java_resources: ["java-res/a/a"],
+ }
+
+ java_import {
+ name: "device_import_module",
+ jars: ["a.jar"],
+ }
+
+ java_device_for_host {
+ name: "device_for_host_module",
+ libs: [
+ "device_module",
+ "device_import_module",
+ ],
+ }
+
+ java_library_host {
+ name: "host_module",
+ srcs: ["b.java"],
+ java_resources: ["java-res/b/b"],
+ static_libs: ["device_for_host_module"],
+ }
+ `
+
+ config := testConfig(nil)
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ deviceModule := ctx.ModuleForTests("device_module", "android_common")
+ deviceTurbineCombined := deviceModule.Output("turbine-combined/device_module.jar")
+ deviceJavac := deviceModule.Output("javac/device_module.jar")
+ deviceRes := deviceModule.Output("res/device_module.jar")
+
+ deviceImportModule := ctx.ModuleForTests("device_import_module", "android_common")
+ deviceImportCombined := deviceImportModule.Output("combined/device_import_module.jar")
+
+ hostModule := ctx.ModuleForTests("host_module", config.BuildOsCommonVariant)
+ hostJavac := hostModule.Output("javac/host_module.jar")
+ hostRes := hostModule.Output("res/host_module.jar")
+ combined := hostModule.Output("combined/host_module.jar")
+ resCombined := hostModule.Output("res-combined/host_module.jar")
+
+ // check classpath of host module with dependency on device_for_host_module
+ expectedClasspath := "-classpath " + strings.Join(android.Paths{
+ deviceTurbineCombined.Output,
+ deviceImportCombined.Output,
+ }.Strings(), ":")
+
+ if hostJavac.Args["classpath"] != expectedClasspath {
+ t.Errorf("expected host_module javac classpath:\n%s\ngot:\n%s",
+ expectedClasspath, hostJavac.Args["classpath"])
+ }
+
+ // check host module merged with static dependency implementation jars from device_for_host module
+ expectedInputs := android.Paths{
+ hostJavac.Output,
+ deviceJavac.Output,
+ deviceImportCombined.Output,
+ }
+
+ if !reflect.DeepEqual(combined.Inputs, expectedInputs) {
+ t.Errorf("expected host_module combined inputs:\n%q\ngot:\n%q",
+ expectedInputs, combined.Inputs)
+ }
+
+ // check host module merged with static dependency resource jars from device_for_host module
+ expectedInputs = android.Paths{
+ hostRes.Output,
+ deviceRes.Output,
+ }
+
+ if !reflect.DeepEqual(resCombined.Inputs, expectedInputs) {
+ t.Errorf("expected host_module res combined inputs:\n%q\ngot:\n%q",
+ expectedInputs, resCombined.Inputs)
+ }
+}
+
+func TestHostForDevice(t *testing.T) {
+ bp := `
+ java_library_host {
+ name: "host_module",
+ srcs: ["a.java"],
+ java_resources: ["java-res/a/a"],
+ }
+
+ java_import_host {
+ name: "host_import_module",
+ jars: ["a.jar"],
+ }
+
+ java_host_for_device {
+ name: "host_for_device_module",
+ libs: [
+ "host_module",
+ "host_import_module",
+ ],
+ }
+
+ java_library {
+ name: "device_module",
+ no_framework_libs: true,
+ srcs: ["b.java"],
+ java_resources: ["java-res/b/b"],
+ static_libs: ["host_for_device_module"],
+ }
+ `
+
+ config := testConfig(nil)
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ hostModule := ctx.ModuleForTests("host_module", config.BuildOsCommonVariant)
+ hostJavac := hostModule.Output("javac/host_module.jar")
+ hostRes := hostModule.Output("res/host_module.jar")
+
+ hostImportModule := ctx.ModuleForTests("host_import_module", config.BuildOsCommonVariant)
+ hostImportCombined := hostImportModule.Output("combined/host_import_module.jar")
+
+ deviceModule := ctx.ModuleForTests("device_module", "android_common")
+ deviceJavac := deviceModule.Output("javac/device_module.jar")
+ deviceRes := deviceModule.Output("res/device_module.jar")
+ combined := deviceModule.Output("combined/device_module.jar")
+ resCombined := deviceModule.Output("res-combined/device_module.jar")
+
+ // check classpath of device module with dependency on host_for_device_module
+ expectedClasspath := "-classpath " + strings.Join(android.Paths{
+ hostJavac.Output,
+ hostImportCombined.Output,
+ }.Strings(), ":")
+
+ if deviceJavac.Args["classpath"] != expectedClasspath {
+ t.Errorf("expected device_module javac classpath:\n%s\ngot:\n%s",
+ expectedClasspath, deviceJavac.Args["classpath"])
+ }
+
+ // check device module merged with static dependency implementation jars from host_for_device module
+ expectedInputs := android.Paths{
+ deviceJavac.Output,
+ hostJavac.Output,
+ hostImportCombined.Output,
+ }
+
+ if !reflect.DeepEqual(combined.Inputs, expectedInputs) {
+ t.Errorf("expected device_module combined inputs:\n%q\ngot:\n%q",
+ expectedInputs, combined.Inputs)
+ }
+
+ // check device module merged with static dependency resource jars from host_for_device module
+ expectedInputs = android.Paths{
+ deviceRes.Output,
+ hostRes.Output,
+ }
+
+ if !reflect.DeepEqual(resCombined.Inputs, expectedInputs) {
+ t.Errorf("expected device_module res combined inputs:\n%q\ngot:\n%q",
+ expectedInputs, resCombined.Inputs)
+ }
+}
diff --git a/java/dex.go b/java/dex.go
index 66e71b5..45fa068 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -20,153 +20,98 @@
"github.com/google/blueprint"
"android/soong/android"
+ "android/soong/remoteexec"
)
-var desugar = pctx.AndroidStaticRule("desugar",
- blueprint.RuleParams{
- Command: `rm -rf $dumpDir && mkdir -p $dumpDir && ` +
- `${config.JavaCmd} ` +
- `-Djdk.internal.lambda.dumpProxyClasses=$$(cd $dumpDir && pwd) ` +
- `$javaFlags ` +
- `-jar ${config.DesugarJar} $classpathFlags $desugarFlags ` +
- `-i $in -o $out`,
- CommandDeps: []string{"${config.DesugarJar}", "${config.JavaCmd}"},
- },
- "javaFlags", "classpathFlags", "desugarFlags", "dumpDir")
-
-func (j *Module) desugar(ctx android.ModuleContext, flags javaBuilderFlags,
- classesJar android.Path, jarName string) android.Path {
-
- desugarFlags := []string{
- "--min_sdk_version " + j.minSdkVersionNumber(ctx),
- "--desugar_try_with_resources_if_needed=false",
- "--allow_empty_bootclasspath",
- }
-
- if inList("--core-library", j.deviceProperties.Dxflags) {
- desugarFlags = append(desugarFlags, "--core_library")
- }
-
- desugarJar := android.PathForModuleOut(ctx, "desugar", jarName)
- dumpDir := android.PathForModuleOut(ctx, "desugar", "classes")
-
- javaFlags := ""
- if ctx.Config().UseOpenJDK9() {
- javaFlags = "--add-opens java.base/java.lang.invoke=ALL-UNNAMED"
- }
-
- var classpathFlags []string
- classpathFlags = append(classpathFlags, flags.bootClasspath.FormDesugarClasspath("--bootclasspath_entry")...)
- classpathFlags = append(classpathFlags, flags.classpath.FormDesugarClasspath("--classpath_entry")...)
-
- var deps android.Paths
- deps = append(deps, flags.bootClasspath...)
- deps = append(deps, flags.classpath...)
-
- ctx.Build(pctx, android.BuildParams{
- Rule: desugar,
- Description: "desugar",
- Output: desugarJar,
- Input: classesJar,
- Implicits: deps,
- Args: map[string]string{
- "dumpDir": dumpDir.String(),
- "javaFlags": javaFlags,
- "classpathFlags": strings.Join(classpathFlags, " "),
- "desugarFlags": strings.Join(desugarFlags, " "),
- },
- })
-
- return desugarJar
-}
-
-var dx = pctx.AndroidStaticRule("dx",
+var d8, d8RE = remoteexec.StaticRules(pctx, "d8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `${config.DxCmd} --dex --output=$outDir $dxFlags $in && ` +
- `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
- `${config.MergeZipsCmd} -D -stripFile "*.class" $out $outDir/classes.dex.jar $in`,
- CommandDeps: []string{
- "${config.DxCmd}",
- "${config.SoongZipCmd}",
- "${config.MergeZipsCmd}",
- },
- },
- "outDir", "dxFlags")
-
-var d8 = pctx.AndroidStaticRule("d8",
- blueprint.RuleParams{
- Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `${config.D8Cmd} --output $outDir $dxFlags $in && ` +
- `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
- `${config.MergeZipsCmd} -D -stripFile "*.class" $out $outDir/classes.dex.jar $in`,
+ `$reTemplate${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+ `${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+ `${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
CommandDeps: []string{
"${config.D8Cmd}",
"${config.SoongZipCmd}",
"${config.MergeZipsCmd}",
},
- },
- "outDir", "dxFlags")
+ }, &remoteexec.REParams{
+ Labels: map[string]string{"type": "compile", "compiler": "d8"},
+ Inputs: []string{"${config.D8Jar}"},
+ ExecStrategy: "${config.RED8ExecStrategy}",
+ ToolchainInputs: []string{"${config.JavaCmd}"},
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ }, []string{"outDir", "d8Flags", "zipFlags"}, nil)
-var r8 = pctx.AndroidStaticRule("r8",
+var r8, r8RE = remoteexec.StaticRules(pctx, "r8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `${config.R8Cmd} -injars $in --output $outDir ` +
+ `rm -f "$outDict" && ` +
+ `$reTemplate${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
`--force-proguard-compatibility ` +
+ `--no-data-resources ` +
`-printmapping $outDict ` +
- `$dxFlags $r8Flags && ` +
- `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
- `${config.MergeZipsCmd} -D -stripFile "*.class" $out $outDir/classes.dex.jar $in`,
+ `$r8Flags && ` +
+ `touch "$outDict" && ` +
+ `${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+ `${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
CommandDeps: []string{
"${config.R8Cmd}",
"${config.SoongZipCmd}",
"${config.MergeZipsCmd}",
},
- },
- "outDir", "outDict", "dxFlags", "r8Flags")
+ }, &remoteexec.REParams{
+ Labels: map[string]string{"type": "compile", "compiler": "r8"},
+ Inputs: []string{"$implicits", "${config.R8Jar}"},
+ ExecStrategy: "${config.RER8ExecStrategy}",
+ ToolchainInputs: []string{"${config.JavaCmd}"},
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ }, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
-func (j *Module) dxFlags(ctx android.ModuleContext, fullD8 bool) []string {
+func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
flags := j.deviceProperties.Dxflags
- if fullD8 {
- // Translate all the DX flags to D8 ones until all the build files have been migrated
- // to D8 flags. See: b/69377755
- flags = android.RemoveListFromList(flags,
- []string{"--core-library", "--dex", "--multi-dex"})
- }
+ // Translate all the DX flags to D8 ones until all the build files have been migrated
+ // to D8 flags. See: b/69377755
+ flags = android.RemoveListFromList(flags,
+ []string{"--core-library", "--dex", "--multi-dex"})
if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" {
- if fullD8 {
- flags = append(flags, "--debug")
- } else {
- flags = append(flags, "--no-optimize")
- }
+ flags = append(flags, "--debug")
}
if ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
flags = append(flags,
"--debug",
"--verbose")
- if !fullD8 {
- flags = append(flags,
- "--dump-to="+android.PathForModuleOut(ctx, "classes.lst").String(),
- "--dump-width=1000")
- }
}
- if fullD8 {
- flags = append(flags, "--min-api "+j.minSdkVersionNumber(ctx))
- } else {
- flags = append(flags, "--min-sdk-version="+j.minSdkVersionNumber(ctx))
+ minSdkVersion, err := sdkVersionToNumberAsString(ctx, j.minSdkVersion())
+ if err != nil {
+ ctx.PropertyErrorf("min_sdk_version", "%s", err)
}
+
+ flags = append(flags, "--min-api "+minSdkVersion)
return flags
}
+func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
+ d8Flags := j.dexCommonFlags(ctx)
+
+ d8Flags = append(d8Flags, flags.bootClasspath.FormTurbineClasspath("--lib ")...)
+ d8Flags = append(d8Flags, flags.classpath.FormTurbineClasspath("--lib ")...)
+
+ var d8Deps android.Paths
+ d8Deps = append(d8Deps, flags.bootClasspath...)
+ d8Deps = append(d8Deps, flags.classpath...)
+
+ return d8Flags, d8Deps
+}
+
func (j *Module) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
opt := j.deviceProperties.Optimize
// When an app contains references to APIs that are not in the SDK specified by
// its LOCAL_SDK_VERSION for example added by support library or by runtime
- // classes added by desugar, we artifically raise the "SDK version" "linked" by
+ // classes added by desugaring, we artifically raise the "SDK version" "linked" by
// ProGuard, to
// - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
@@ -176,11 +121,17 @@
proguardRaiseDeps = append(proguardRaiseDeps, dep.(Dependency).HeaderJars()...)
})
+ r8Flags = append(r8Flags, j.dexCommonFlags(ctx)...)
+
r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
r8Flags = append(r8Flags, "-forceprocessing")
+ r8Deps = append(r8Deps, proguardRaiseDeps...)
+ r8Deps = append(r8Deps, flags.bootClasspath...)
+ r8Deps = append(r8Deps, flags.classpath...)
+
flagFiles := android.Paths{
android.PathForSource(ctx, "build/make/core/proguard.flags"),
}
@@ -193,6 +144,8 @@
flagFiles = append(flagFiles, j.extraProguardFlagFiles...)
// TODO(ccross): static android library proguard files
+ flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, j.deviceProperties.Optimize.Proguard_flags_files)...)
+
r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
r8Deps = append(r8Deps, flagFiles...)
@@ -215,63 +168,79 @@
if !Bool(opt.Obfuscate) {
r8Flags = append(r8Flags, "-dontobfuscate")
}
+ // TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
+ // dictionary of the app and move the app from libraryjars to injars.
+
+ // Don't strip out debug information for eng builds.
+ if ctx.Config().Eng() {
+ r8Flags = append(r8Flags, "--debug")
+ }
return r8Flags, r8Deps
}
func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
- classesJar android.Path, jarName string) android.Path {
+ classesJar android.Path, jarName string) android.ModuleOutPath {
- useR8 := Bool(j.deviceProperties.Optimize.Enabled)
- fullD8 := useR8 || ctx.Config().UseD8Desugar()
-
- if !fullD8 {
- classesJar = j.desugar(ctx, flags, classesJar, jarName)
- }
-
- dxFlags := j.dxFlags(ctx, fullD8)
+ useR8 := j.deviceProperties.EffectiveOptimizeEnabled()
// Compile classes.jar into classes.dex and then javalib.jar
javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
outDir := android.PathForModuleOut(ctx, "dex")
+ zipFlags := "--ignore_missing_files"
+ if j.deviceProperties.UncompressDex {
+ zipFlags += " -L 0"
+ }
+
if useR8 {
- // TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
- // dictionary of the app and move the app from libraryjars to injars.
- j.proguardDictionary = android.PathForModuleOut(ctx, "proguard_dictionary")
+ proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
+ j.proguardDictionary = proguardDictionary
r8Flags, r8Deps := j.r8Flags(ctx, flags)
+ rule := r8
+ args := map[string]string{
+ "r8Flags": strings.Join(r8Flags, " "),
+ "zipFlags": zipFlags,
+ "outDict": j.proguardDictionary.String(),
+ "outDir": outDir.String(),
+ }
+ if ctx.Config().IsEnvTrue("RBE_R8") {
+ rule = r8RE
+ args["implicits"] = strings.Join(r8Deps.Strings(), ",")
+ }
ctx.Build(pctx, android.BuildParams{
- Rule: r8,
- Description: "r8",
- Output: javalibJar,
- Input: classesJar,
- Implicits: r8Deps,
- Args: map[string]string{
- "dxFlags": strings.Join(dxFlags, " "),
- "r8Flags": strings.Join(r8Flags, " "),
- "outDict": j.proguardDictionary.String(),
- "outDir": outDir.String(),
- },
+ Rule: rule,
+ Description: "r8",
+ Output: javalibJar,
+ ImplicitOutput: proguardDictionary,
+ Input: classesJar,
+ Implicits: r8Deps,
+ Args: args,
})
} else {
- rule := dx
- desc := "dx"
- if fullD8 {
- rule = d8
- desc = "d8"
+ d8Flags, d8Deps := j.d8Flags(ctx, flags)
+ rule := d8
+ if ctx.Config().IsEnvTrue("RBE_D8") {
+ rule = d8RE
}
ctx.Build(pctx, android.BuildParams{
Rule: rule,
- Description: desc,
+ Description: "d8",
Output: javalibJar,
Input: classesJar,
+ Implicits: d8Deps,
Args: map[string]string{
- "dxFlags": strings.Join(dxFlags, " "),
- "outDir": outDir.String(),
+ "d8Flags": strings.Join(d8Flags, " "),
+ "zipFlags": zipFlags,
+ "outDir": outDir.String(),
},
})
}
+ if j.deviceProperties.UncompressDex {
+ alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
+ TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
+ javalibJar = alignedJavalibJar
+ }
- j.dexJarFile = javalibJar
return javalibJar
}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
new file mode 100644
index 0000000..9141f9e
--- /dev/null
+++ b/java/dexpreopt.go
@@ -0,0 +1,204 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "android/soong/dexpreopt"
+)
+
+type dexpreopter struct {
+ dexpreoptProperties DexpreoptProperties
+
+ installPath android.OutputPath
+ uncompressedDex bool
+ isSDKLibrary bool
+ isTest bool
+ isInstallable bool
+
+ builtInstalled string
+}
+
+type DexpreoptProperties struct {
+ Dex_preopt struct {
+ // If false, prevent dexpreopting and stripping the dex file from the final jar. Defaults to
+ // true.
+ Enabled *bool
+
+ // If true, never strip the dex files from the final jar when dexpreopting. Defaults to false.
+ No_stripping *bool
+
+ // If true, generate an app image (.art file) for this module.
+ App_image *bool
+
+ // If true, use a checked-in profile to guide optimization. Defaults to false unless
+ // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
+ // that matches the name of this module, in which case it is defaulted to true.
+ Profile_guided *bool
+
+ // If set, provides the path to profile relative to the Android.bp file. If not set,
+ // defaults to searching for a file that matches the name of this module in the default
+ // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
+ Profile *string
+ }
+}
+
+func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool {
+ global := dexpreoptGlobalConfig(ctx)
+
+ if global.DisablePreopt {
+ return true
+ }
+
+ if inList(ctx.ModuleName(), global.DisablePreoptModules) {
+ return true
+ }
+
+ if ctx.Config().UnbundledBuild() {
+ return true
+ }
+
+ if d.isTest {
+ return true
+ }
+
+ if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) {
+ return true
+ }
+
+ if !d.isInstallable {
+ return true
+ }
+
+ // TODO: contains no java code
+
+ return false
+}
+
+func odexOnSystemOther(ctx android.ModuleContext, installPath android.OutputPath) bool {
+ return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx))
+}
+
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath {
+ if d.dexpreoptDisabled(ctx) {
+ return dexJarFile
+ }
+
+ global := dexpreoptGlobalConfig(ctx)
+ bootImage := defaultBootImageConfig(ctx)
+ defaultBootImage := bootImage
+ if global.UseApexImage {
+ bootImage = apexBootImageConfig(ctx)
+ }
+
+ var archs []android.ArchType
+ for _, a := range ctx.MultiTargets() {
+ archs = append(archs, a.Arch.ArchType)
+ }
+ if len(archs) == 0 {
+ // assume this is a java library, dexpreopt for all arches for now
+ for _, target := range ctx.Config().Targets[android.Android] {
+ archs = append(archs, target.Arch.ArchType)
+ }
+ if inList(ctx.ModuleName(), global.SystemServerJars) && !d.isSDKLibrary {
+ // If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
+ archs = archs[:1]
+ }
+ }
+ if ctx.Config().SecondArchIsTranslated() {
+ // Only preopt primary arch for translated arch since there is only an image there.
+ archs = archs[:1]
+ }
+
+ var images android.Paths
+ for _, arch := range archs {
+ images = append(images, bootImage.images[arch])
+ }
+
+ dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
+
+ strippedDexJarFile := android.PathForModuleOut(ctx, "dexpreopt", dexJarFile.Base())
+
+ var profileClassListing android.OptionalPath
+ profileIsTextListing := false
+ if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
+ // If dex_preopt.profile_guided is not set, default it based on the existence of the
+ // dexprepot.profile option or the profile class listing.
+ if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
+ profileClassListing = android.OptionalPathForPath(
+ android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
+ profileIsTextListing = true
+ } else {
+ profileClassListing = android.ExistentPathForSource(ctx,
+ global.ProfileDir, ctx.ModuleName()+".prof")
+ }
+ }
+
+ dexpreoptConfig := dexpreopt.ModuleConfig{
+ Name: ctx.ModuleName(),
+ DexLocation: dexLocation,
+ BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
+ DexPath: dexJarFile,
+ UncompressedDex: d.uncompressedDex,
+ HasApkLibraries: false,
+ PreoptFlags: nil,
+
+ ProfileClassListing: profileClassListing,
+ ProfileIsTextListing: profileIsTextListing,
+
+ EnforceUsesLibraries: false,
+ OptionalUsesLibraries: nil,
+ UsesLibraries: nil,
+ LibraryPaths: nil,
+
+ Archs: archs,
+ DexPreoptImages: images,
+
+ // We use the dex paths and dex locations of the default boot image, as it
+ // contains the full dexpreopt boot classpath. Other images may just contain a subset of
+ // the dexpreopt boot classpath.
+ PreoptBootClassPathDexFiles: defaultBootImage.dexPaths.Paths(),
+ PreoptBootClassPathDexLocations: defaultBootImage.dexLocations,
+
+ PreoptExtractedApk: false,
+
+ NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
+ ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
+
+ NoStripping: Bool(d.dexpreoptProperties.Dex_preopt.No_stripping),
+ StripInputPath: dexJarFile,
+ StripOutputPath: strippedDexJarFile.OutputPath,
+ }
+
+ dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, dexpreoptConfig)
+ if err != nil {
+ ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
+ return dexJarFile
+ }
+
+ dexpreoptRule.Build(pctx, ctx, "dexpreopt", "dexpreopt")
+
+ d.builtInstalled = dexpreoptRule.Installs().String()
+
+ stripRule, err := dexpreopt.GenerateStripRule(global, dexpreoptConfig)
+ if err != nil {
+ ctx.ModuleErrorf("error generating dexpreopt strip rule: %s", err.Error())
+ return dexJarFile
+ }
+
+ stripRule.Build(pctx, ctx, "dexpreopt_strip", "dexpreopt strip")
+
+ return strippedDexJarFile
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
new file mode 100644
index 0000000..4d87b2f
--- /dev/null
+++ b/java/dexpreopt_bootjars.go
@@ -0,0 +1,466 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+
+ "github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ android.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+}
+
+// The image "location" is a symbolic path that with multiarchitecture
+// support doesn't really exist on the device. Typically it is
+// /system/framework/boot.art and should be the same for all supported
+// architectures on the device. The concrete architecture specific
+// content actually ends up in a "filename" that contains an
+// architecture specific directory name such as arm, arm64, mips,
+// mips64, x86, x86_64.
+//
+// Here are some example values for an x86_64 / x86 configuration:
+//
+// bootImages["x86_64"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86_64/boot.art"
+// dexpreopt.PathToLocation(bootImages["x86_64"], "x86_64") = "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art"
+//
+// bootImages["x86"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86/boot.art"
+// dexpreopt.PathToLocation(bootImages["x86"])= "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art"
+//
+// The location is passed as an argument to the ART tools like dex2oat instead of the real path. The ART tools
+// will then reconstruct the real path, so the rules must have a dependency on the real path.
+
+type bootImageConfig struct {
+ name string
+ modules []string
+ dexLocations []string
+ dexPaths android.WritablePaths
+ dir android.OutputPath
+ symbolsDir android.OutputPath
+ images map[android.ArchType]android.OutputPath
+}
+
+type bootImage struct {
+ bootImageConfig
+
+ installs map[android.ArchType]android.RuleBuilderInstalls
+ vdexInstalls map[android.ArchType]android.RuleBuilderInstalls
+ unstrippedInstalls map[android.ArchType]android.RuleBuilderInstalls
+
+ profileInstalls android.RuleBuilderInstalls
+}
+
+func newBootImage(ctx android.PathContext, config bootImageConfig) *bootImage {
+ image := &bootImage{
+ bootImageConfig: config,
+
+ installs: make(map[android.ArchType]android.RuleBuilderInstalls),
+ vdexInstalls: make(map[android.ArchType]android.RuleBuilderInstalls),
+ unstrippedInstalls: make(map[android.ArchType]android.RuleBuilderInstalls),
+ }
+
+ return image
+}
+
+func concat(lists ...[]string) []string {
+ var size int
+ for _, l := range lists {
+ size += len(l)
+ }
+ ret := make([]string, 0, size)
+ for _, l := range lists {
+ ret = append(ret, l...)
+ }
+ return ret
+}
+
+func dexpreoptBootJarsFactory() android.Singleton {
+ return &dexpreoptBootJars{}
+}
+
+func skipDexpreoptBootJars(ctx android.PathContext) bool {
+ if ctx.Config().UnbundledBuild() {
+ return true
+ }
+
+ if len(ctx.Config().Targets[android.Android]) == 0 {
+ // Host-only build
+ return true
+ }
+
+ return false
+}
+
+type dexpreoptBootJars struct {
+ defaultBootImage *bootImage
+ otherImages []*bootImage
+}
+
+// dexpreoptBoot singleton rules
+func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
+ if skipDexpreoptBootJars(ctx) {
+ return
+ }
+
+ global := dexpreoptGlobalConfig(ctx)
+
+ // Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
+ // and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
+ // Note: this is technically incorrect. Compiled code contains stack checks which may depend
+ // on ASAN settings.
+ if len(ctx.Config().SanitizeDevice()) == 1 &&
+ ctx.Config().SanitizeDevice()[0] == "address" &&
+ global.SanitizeLite {
+ return
+ }
+
+ // Always create the default boot image first, to get a unique profile rule for all images.
+ d.defaultBootImage = buildBootImage(ctx, defaultBootImageConfig(ctx))
+ if global.GenerateApexImage {
+ d.otherImages = append(d.otherImages, buildBootImage(ctx, apexBootImageConfig(ctx)))
+ }
+
+ dumpOatRules(ctx, d.defaultBootImage)
+}
+
+// buildBootImage takes a bootImageConfig, creates rules to build it, and returns a *bootImage.
+func buildBootImage(ctx android.SingletonContext, config bootImageConfig) *bootImage {
+ global := dexpreoptGlobalConfig(ctx)
+
+ image := newBootImage(ctx, config)
+
+ bootDexJars := make(android.Paths, len(image.modules))
+
+ ctx.VisitAllModules(func(module android.Module) {
+ // Collect dex jar paths for the modules listed above.
+ if j, ok := module.(interface{ DexJar() android.Path }); ok {
+ name := ctx.ModuleName(module)
+ if i := android.IndexList(name, image.modules); i != -1 {
+ bootDexJars[i] = j.DexJar()
+ }
+ }
+ })
+
+ var missingDeps []string
+ // Ensure all modules were converted to paths
+ for i := range bootDexJars {
+ if bootDexJars[i] == nil {
+ if ctx.Config().AllowMissingDependencies() {
+ missingDeps = append(missingDeps, image.modules[i])
+ bootDexJars[i] = android.PathForOutput(ctx, "missing")
+ } else {
+ ctx.Errorf("failed to find dex jar path for module %q",
+ image.modules[i])
+ }
+ }
+ }
+
+ // The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
+ // the bootclasspath modules have been compiled. Copy the dex jars there so the module rules that have
+ // already been set up can find them.
+ for i := range bootDexJars {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: bootDexJars[i],
+ Output: image.dexPaths[i],
+ })
+ }
+
+ profile := bootImageProfileRule(ctx, image, missingDeps)
+
+ if !global.DisablePreopt {
+ targets := ctx.Config().Targets[android.Android]
+ if ctx.Config().SecondArchIsTranslated() {
+ targets = targets[:1]
+ }
+
+ for _, target := range targets {
+ buildBootImageRuleForArch(ctx, image, target.Arch.ArchType, profile, missingDeps)
+ }
+ }
+
+ return image
+}
+
+func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage,
+ arch android.ArchType, profile android.Path, missingDeps []string) {
+
+ global := dexpreoptGlobalConfig(ctx)
+
+ symbolsDir := image.symbolsDir.Join(ctx, "system/framework", arch.String())
+ symbolsFile := symbolsDir.Join(ctx, image.name+".oat")
+ outputDir := image.dir.Join(ctx, "system/framework", arch.String())
+ outputPath := image.images[arch]
+ oatLocation := pathtools.ReplaceExtension(dexpreopt.PathToLocation(outputPath, arch), "oat")
+
+ rule := android.NewRuleBuilder()
+ rule.MissingDeps(missingDeps)
+
+ rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String())
+ rule.Command().Text("rm").Flag("-f").
+ Flag(symbolsDir.Join(ctx, "*.art").String()).
+ Flag(symbolsDir.Join(ctx, "*.oat").String()).
+ Flag(symbolsDir.Join(ctx, "*.invocation").String())
+ rule.Command().Text("rm").Flag("-f").
+ Flag(outputDir.Join(ctx, "*.art").String()).
+ Flag(outputDir.Join(ctx, "*.oat").String()).
+ Flag(outputDir.Join(ctx, "*.invocation").String())
+
+ cmd := rule.Command()
+
+ extraFlags := ctx.Config().Getenv("ART_BOOT_IMAGE_EXTRA_ARGS")
+ if extraFlags == "" {
+ // Use ANDROID_LOG_TAGS to suppress most logging by default...
+ cmd.Text(`ANDROID_LOG_TAGS="*:e"`)
+ } else {
+ // ...unless the boot image is generated specifically for testing, then allow all logging.
+ cmd.Text(`ANDROID_LOG_TAGS="*:v"`)
+ }
+
+ invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
+
+ cmd.Tool(global.Tools.Dex2oat).
+ Flag("--avoid-storing-invocation").
+ FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
+ Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
+ Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx)
+
+ if profile != nil {
+ cmd.FlagWithArg("--compiler-filter=", "speed-profile")
+ cmd.FlagWithInput("--profile-file=", profile)
+ } else if global.PreloadedClasses.Valid() {
+ cmd.FlagWithInput("--image-classes=", global.PreloadedClasses.Path())
+ }
+
+ if global.DirtyImageObjects.Valid() {
+ cmd.FlagWithInput("--dirty-image-objects=", global.DirtyImageObjects.Path())
+ }
+
+ cmd.
+ FlagForEachInput("--dex-file=", image.dexPaths.Paths()).
+ FlagForEachArg("--dex-location=", image.dexLocations).
+ Flag("--generate-debug-info").
+ Flag("--generate-build-id").
+ FlagWithOutput("--oat-symbols=", symbolsFile).
+ Flag("--strip").
+ FlagWithOutput("--oat-file=", outputPath.ReplaceExtension(ctx, "oat")).
+ FlagWithArg("--oat-location=", oatLocation).
+ FlagWithOutput("--image=", outputPath).
+ FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()).
+ FlagWithArg("--instruction-set=", arch.String()).
+ FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
+ FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
+ FlagWithArg("--android-root=", global.EmptyDirectory).
+ FlagWithArg("--no-inline-from=", "core-oj.jar").
+ Flag("--abort-on-hard-verifier-error")
+
+ if global.BootFlags != "" {
+ cmd.Flag(global.BootFlags)
+ }
+
+ if extraFlags != "" {
+ cmd.Flag(extraFlags)
+ }
+
+ cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
+
+ installDir := filepath.Join("/system/framework", arch.String())
+ vdexInstallDir := filepath.Join("/system/framework")
+
+ var extraFiles android.WritablePaths
+ var vdexInstalls android.RuleBuilderInstalls
+ var unstrippedInstalls android.RuleBuilderInstalls
+
+ // dex preopt on the bootclasspath produces multiple files. The first dex file
+ // is converted into to 'name'.art (to match the legacy assumption that 'name'.art
+ // exists), and the rest are converted to 'name'-<jar>.art.
+ // In addition, each .art file has an associated .oat and .vdex file, and an
+ // unstripped .oat file
+ for i, m := range image.modules {
+ name := image.name
+ if i != 0 {
+ name += "-" + m
+ }
+
+ art := outputDir.Join(ctx, name+".art")
+ oat := outputDir.Join(ctx, name+".oat")
+ vdex := outputDir.Join(ctx, name+".vdex")
+ unstrippedOat := symbolsDir.Join(ctx, name+".oat")
+
+ extraFiles = append(extraFiles, art, oat, vdex, unstrippedOat)
+
+ // Install the .oat and .art files.
+ rule.Install(art, filepath.Join(installDir, art.Base()))
+ rule.Install(oat, filepath.Join(installDir, oat.Base()))
+
+ // The vdex files are identical between architectures, install them to a shared location. The Make rules will
+ // only use the install rules for one architecture, and will create symlinks into the architecture-specific
+ // directories.
+ vdexInstalls = append(vdexInstalls,
+ android.RuleBuilderInstall{vdex, filepath.Join(vdexInstallDir, vdex.Base())})
+
+ // Install the unstripped oat files. The Make rules will put these in $(TARGET_OUT_UNSTRIPPED)
+ unstrippedInstalls = append(unstrippedInstalls,
+ android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
+ }
+
+ cmd.ImplicitOutputs(extraFiles)
+
+ rule.Build(pctx, ctx, image.name+"JarsDexpreopt_"+arch.String(), "dexpreopt "+image.name+" jars "+arch.String())
+
+ // save output and installed files for makevars
+ image.installs[arch] = rule.Installs()
+ image.vdexInstalls[arch] = vdexInstalls
+ image.unstrippedInstalls[arch] = unstrippedInstalls
+}
+
+const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
+It is likely that the boot classpath is inconsistent.
+Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
+
+func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
+ global := dexpreoptGlobalConfig(ctx)
+
+ if !global.UseProfileForBootImage || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
+ return nil
+ }
+ return ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
+ tools := global.Tools
+
+ rule := android.NewRuleBuilder()
+ rule.MissingDeps(missingDeps)
+
+ var bootImageProfile android.Path
+ if len(global.BootImageProfiles) > 1 {
+ combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt")
+ rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile)
+ bootImageProfile = combinedBootImageProfile
+ } else if len(global.BootImageProfiles) == 1 {
+ bootImageProfile = global.BootImageProfiles[0]
+ } else {
+ // If not set, use the default. Some branches like master-art-host don't have frameworks/base, so manually
+ // handle the case that the default is missing. Those branches won't attempt to build the profile rule,
+ // and if they do they'll get a missing deps error.
+ defaultProfile := "frameworks/base/config/boot-image-profile.txt"
+ path := android.ExistentPathForSource(ctx, defaultProfile)
+ if path.Valid() {
+ bootImageProfile = path.Path()
+ } else {
+ missingDeps = append(missingDeps, defaultProfile)
+ bootImageProfile = android.PathForOutput(ctx, "missing")
+ }
+ }
+
+ profile := image.dir.Join(ctx, "boot.prof")
+
+ rule.Command().
+ Text(`ANDROID_LOG_TAGS="*:e"`).
+ Tool(tools.Profman).
+ FlagWithInput("--create-profile-from=", bootImageProfile).
+ FlagForEachInput("--apk=", image.dexPaths.Paths()).
+ FlagForEachArg("--dex-location=", image.dexLocations).
+ FlagWithOutput("--reference-profile-file=", profile)
+
+ rule.Install(profile, "/system/etc/boot-image.prof")
+
+ rule.Build(pctx, ctx, "bootJarsProfile", "profile boot jars")
+
+ image.profileInstalls = rule.Installs()
+
+ return profile
+ }).(android.WritablePath)
+}
+
+var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
+
+func dumpOatRules(ctx android.SingletonContext, image *bootImage) {
+ var archs []android.ArchType
+ for arch := range image.images {
+ archs = append(archs, arch)
+ }
+ sort.Slice(archs, func(i, j int) bool { return archs[i].String() < archs[j].String() })
+
+ var allPhonies android.Paths
+ for _, arch := range archs {
+ // Create a rule to call oatdump.
+ output := android.PathForOutput(ctx, "boot."+arch.String()+".oatdump.txt")
+ rule := android.NewRuleBuilder()
+ rule.Command().
+ // TODO: for now, use the debug version for better error reporting
+ Tool(ctx.Config().HostToolPath(ctx, "oatdumpd")).
+ FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPaths.Paths(), ":").
+ FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocations, ":").
+ FlagWithArg("--image=", dexpreopt.PathToLocation(image.images[arch], arch)).Implicit(image.images[arch]).
+ FlagWithOutput("--output=", output).
+ FlagWithArg("--instruction-set=", arch.String())
+ rule.Build(pctx, ctx, "dump-oat-boot-"+arch.String(), "dump oat boot "+arch.String())
+
+ // Create a phony rule that depends on the output file and prints the path.
+ phony := android.PathForPhony(ctx, "dump-oat-boot-"+arch.String())
+ rule = android.NewRuleBuilder()
+ rule.Command().
+ Implicit(output).
+ ImplicitOutput(phony).
+ Text("echo").FlagWithArg("Output in ", output.String())
+ rule.Build(pctx, ctx, "phony-dump-oat-boot-"+arch.String(), "dump oat boot "+arch.String())
+
+ allPhonies = append(allPhonies, phony)
+ }
+
+ phony := android.PathForPhony(ctx, "dump-oat-boot")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Phony,
+ Output: phony,
+ Inputs: allPhonies,
+ Description: "dump-oat-boot",
+ })
+
+}
+
+// Export paths for default boot image to Make
+func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
+ image := d.defaultBootImage
+ if image != nil {
+ ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPaths.Strings(), " "))
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.dexLocations, " "))
+
+ var imageNames []string
+ for _, current := range append(d.otherImages, image) {
+ imageNames = append(imageNames, current.name)
+ var arches []android.ArchType
+ for arch, _ := range current.images {
+ arches = append(arches, arch)
+ }
+
+ sort.Slice(arches, func(i, j int) bool { return arches[i].String() < arches[j].String() })
+
+ for _, arch := range arches {
+ ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.vdexInstalls[arch].String())
+ ctx.Strict("DEXPREOPT_IMAGE_"+current.name+"_"+arch.String(), current.images[arch].String())
+ ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.installs[arch].String())
+ ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.unstrippedInstalls[arch].String())
+ }
+ }
+ ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " "))
+ }
+}
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
new file mode 100644
index 0000000..cbb52f1
--- /dev/null
+++ b/java/dexpreopt_bootjars_test.go
@@ -0,0 +1,113 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "path/filepath"
+ "reflect"
+ "sort"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+)
+
+func TestDexpreoptBootJars(t *testing.T) {
+ bp := `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ api_packages: ["foo"],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ dex_import {
+ name: "baz",
+ jars: ["a.jar"],
+ }
+ `
+
+ config := testConfig(nil)
+
+ pathCtx := android.PathContextForTesting(config, nil)
+ dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
+ dexpreoptConfig.RuntimeApexJars = []string{"foo", "bar", "baz"}
+ setDexpreoptTestGlobalConfig(config, dexpreoptConfig)
+
+ ctx := testContext(config, bp, nil)
+
+ ctx.RegisterSingletonType("dex_bootjars", android.SingletonFactoryAdaptor(dexpreoptBootJarsFactory))
+
+ run(t, ctx, config)
+
+ dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
+
+ bootArt := dexpreoptBootJars.Output("boot.art")
+
+ expectedInputs := []string{
+ "dex_bootjars_input/foo.jar",
+ "dex_bootjars_input/bar.jar",
+ "dex_bootjars_input/baz.jar",
+ }
+
+ for i := range expectedInputs {
+ expectedInputs[i] = filepath.Join(buildDir, "test_device", expectedInputs[i])
+ }
+
+ inputs := bootArt.Implicits.Strings()
+ sort.Strings(inputs)
+ sort.Strings(expectedInputs)
+
+ if !reflect.DeepEqual(inputs, expectedInputs) {
+ t.Errorf("want inputs %q\n got inputs %q", expectedInputs, inputs)
+ }
+
+ expectedOutputs := []string{
+ "dex_bootjars/system/framework/arm64/boot.invocation",
+
+ "dex_bootjars/system/framework/arm64/boot.art",
+ "dex_bootjars/system/framework/arm64/boot-bar.art",
+ "dex_bootjars/system/framework/arm64/boot-baz.art",
+
+ "dex_bootjars/system/framework/arm64/boot.oat",
+ "dex_bootjars/system/framework/arm64/boot-bar.oat",
+ "dex_bootjars/system/framework/arm64/boot-baz.oat",
+
+ "dex_bootjars/system/framework/arm64/boot.vdex",
+ "dex_bootjars/system/framework/arm64/boot-bar.vdex",
+ "dex_bootjars/system/framework/arm64/boot-baz.vdex",
+
+ "dex_bootjars_unstripped/system/framework/arm64/boot.oat",
+ "dex_bootjars_unstripped/system/framework/arm64/boot-bar.oat",
+ "dex_bootjars_unstripped/system/framework/arm64/boot-baz.oat",
+ }
+
+ for i := range expectedOutputs {
+ expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
+ }
+
+ outputs := append(android.WritablePaths{bootArt.Output}, bootArt.ImplicitOutputs...).Strings()
+ sort.Strings(outputs)
+ sort.Strings(expectedOutputs)
+
+ if !reflect.DeepEqual(outputs, expectedOutputs) {
+ t.Errorf("want outputs %q\n got outputs %q", expectedOutputs, outputs)
+ }
+}
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
new file mode 100644
index 0000000..abc5fa1
--- /dev/null
+++ b/java/dexpreopt_config.go
@@ -0,0 +1,211 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "android/soong/dexpreopt"
+ "path/filepath"
+ "strings"
+)
+
+// dexpreoptGlobalConfig returns the global dexpreopt.config. It is loaded once the first time it is called for any
+// ctx.Config(), and returns the same data for all future calls with the same ctx.Config(). A value can be inserted
+// for tests using setDexpreoptTestGlobalConfig.
+func dexpreoptGlobalConfig(ctx android.PathContext) dexpreopt.GlobalConfig {
+ return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
+ if f := ctx.Config().DexpreoptGlobalConfig(); f != "" {
+ ctx.AddNinjaFileDeps(f)
+ globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, f)
+ if err != nil {
+ panic(err)
+ }
+ return globalConfig
+ }
+
+ // No global config filename set, see if there is a test config set
+ return ctx.Config().Once(dexpreoptTestGlobalConfigKey, func() interface{} {
+ // Nope, return a config with preopting disabled
+ return dexpreopt.GlobalConfig{
+ DisablePreopt: true,
+ }
+ })
+ }).(dexpreopt.GlobalConfig)
+}
+
+// setDexpreoptTestGlobalConfig sets a GlobalConfig that future calls to dexpreoptGlobalConfig will return. It must
+// be called before the first call to dexpreoptGlobalConfig for the config.
+func setDexpreoptTestGlobalConfig(config android.Config, globalConfig dexpreopt.GlobalConfig) {
+ config.Once(dexpreoptTestGlobalConfigKey, func() interface{} { return globalConfig })
+}
+
+var dexpreoptGlobalConfigKey = android.NewOnceKey("DexpreoptGlobalConfig")
+var dexpreoptTestGlobalConfigKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
+
+// systemServerClasspath returns the on-device locations of the modules in the system server classpath. It is computed
+// once the first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same
+// ctx.Config().
+func systemServerClasspath(ctx android.PathContext) []string {
+ return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string {
+ global := dexpreoptGlobalConfig(ctx)
+
+ var systemServerClasspathLocations []string
+ for _, m := range global.SystemServerJars {
+ systemServerClasspathLocations = append(systemServerClasspathLocations,
+ filepath.Join("/system/framework", m+".jar"))
+ }
+ return systemServerClasspathLocations
+ })
+}
+
+var systemServerClasspathKey = android.NewOnceKey("systemServerClasspath")
+
+// defaultBootImageConfig returns the bootImageConfig that will be used to dexpreopt modules. It is computed once the
+// first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same
+// ctx.Config().
+func defaultBootImageConfig(ctx android.PathContext) bootImageConfig {
+ return ctx.Config().Once(defaultBootImageConfigKey, func() interface{} {
+ global := dexpreoptGlobalConfig(ctx)
+
+ runtimeModules := global.RuntimeApexJars
+ nonFrameworkModules := concat(runtimeModules, global.ProductUpdatableBootModules)
+ frameworkModules := android.RemoveListFromList(global.BootJars, nonFrameworkModules)
+
+ var nonUpdatableBootModules []string
+ var nonUpdatableBootLocations []string
+
+ for _, m := range runtimeModules {
+ nonUpdatableBootModules = append(nonUpdatableBootModules, m)
+ nonUpdatableBootLocations = append(nonUpdatableBootLocations,
+ filepath.Join("/apex/com.android.runtime/javalib", m+".jar"))
+ }
+
+ for _, m := range frameworkModules {
+ nonUpdatableBootModules = append(nonUpdatableBootModules, m)
+ nonUpdatableBootLocations = append(nonUpdatableBootLocations,
+ filepath.Join("/system/framework", m+".jar"))
+ }
+
+ // The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
+ // the bootclasspath modules have been compiled. Set up known paths for them, the singleton rules will copy
+ // them there.
+ // TODO: use module dependencies instead
+ var nonUpdatableBootDexPaths android.WritablePaths
+ for _, m := range nonUpdatableBootModules {
+ nonUpdatableBootDexPaths = append(nonUpdatableBootDexPaths,
+ android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars_input", m+".jar"))
+ }
+
+ dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars")
+ symbolsDir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_bootjars_unstripped")
+ images := make(map[android.ArchType]android.OutputPath)
+
+ for _, target := range ctx.Config().Targets[android.Android] {
+ images[target.Arch.ArchType] = dir.Join(ctx,
+ "system/framework", target.Arch.ArchType.String()).Join(ctx, "boot.art")
+ }
+
+ return bootImageConfig{
+ name: "boot",
+ modules: nonUpdatableBootModules,
+ dexLocations: nonUpdatableBootLocations,
+ dexPaths: nonUpdatableBootDexPaths,
+ dir: dir,
+ symbolsDir: symbolsDir,
+ images: images,
+ }
+ }).(bootImageConfig)
+}
+
+var defaultBootImageConfigKey = android.NewOnceKey("defaultBootImageConfig")
+
+func apexBootImageConfig(ctx android.PathContext) bootImageConfig {
+ return ctx.Config().Once(apexBootImageConfigKey, func() interface{} {
+ global := dexpreoptGlobalConfig(ctx)
+
+ runtimeModules := global.RuntimeApexJars
+ nonFrameworkModules := concat(runtimeModules, global.ProductUpdatableBootModules)
+ frameworkModules := android.RemoveListFromList(global.BootJars, nonFrameworkModules)
+ imageModules := concat(runtimeModules, frameworkModules)
+
+ var bootLocations []string
+
+ for _, m := range runtimeModules {
+ bootLocations = append(bootLocations,
+ filepath.Join("/apex/com.android.runtime/javalib", m+".jar"))
+ }
+
+ for _, m := range frameworkModules {
+ bootLocations = append(bootLocations,
+ filepath.Join("/system/framework", m+".jar"))
+ }
+
+ // The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
+ // the bootclasspath modules have been compiled. Set up known paths for them, the singleton rules will copy
+ // them there.
+ // TODO: use module dependencies instead
+ var bootDexPaths android.WritablePaths
+ for _, m := range imageModules {
+ bootDexPaths = append(bootDexPaths,
+ android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars_input", m+".jar"))
+ }
+
+ dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars")
+ symbolsDir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "dex_apexjars_unstripped")
+ images := make(map[android.ArchType]android.OutputPath)
+
+ for _, target := range ctx.Config().Targets[android.Android] {
+ images[target.Arch.ArchType] = dir.Join(ctx,
+ "system/framework", target.Arch.ArchType.String(), "apex.art")
+ }
+
+ return bootImageConfig{
+ name: "apex",
+ modules: imageModules,
+ dexLocations: bootLocations,
+ dexPaths: bootDexPaths,
+ dir: dir,
+ symbolsDir: symbolsDir,
+ images: images,
+ }
+ }).(bootImageConfig)
+}
+
+var apexBootImageConfigKey = android.NewOnceKey("apexBootImageConfig")
+
+func defaultBootclasspath(ctx android.PathContext) []string {
+ return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string {
+ global := dexpreoptGlobalConfig(ctx)
+ image := defaultBootImageConfig(ctx)
+ bootclasspath := append(copyOf(image.dexLocations), global.ProductUpdatableBootLocations...)
+ return bootclasspath
+ })
+}
+
+var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath")
+
+var copyOf = android.CopyOf
+
+func init() {
+ android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars)
+}
+
+func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
+ ctx.Strict("PRODUCT_BOOTCLASSPATH", strings.Join(defaultBootclasspath(ctx), ":"))
+ ctx.Strict("PRODUCT_DEX2OAT_BOOTCLASSPATH", strings.Join(defaultBootImageConfig(ctx).dexLocations, ":"))
+ ctx.Strict("PRODUCT_SYSTEM_SERVER_CLASSPATH", strings.Join(systemServerClasspath(ctx), ":"))
+
+ ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules, ":"))
+}
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
new file mode 100644
index 0000000..4af2f5c
--- /dev/null
+++ b/java/dexpreopt_test.go
@@ -0,0 +1,154 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "testing"
+)
+
+func TestDexpreoptEnabled(t *testing.T) {
+ tests := []struct {
+ name string
+ bp string
+ enabled bool
+ }{
+ {
+ name: "app",
+ bp: `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }`,
+ enabled: true,
+ },
+ {
+ name: "installable java library",
+ bp: `
+ java_library {
+ name: "foo",
+ installable: true,
+ srcs: ["a.java"],
+ }`,
+ enabled: true,
+ },
+ {
+ name: "java binary",
+ bp: `
+ java_binary {
+ name: "foo",
+ srcs: ["a.java"],
+ }`,
+ enabled: true,
+ },
+
+ {
+ name: "app without sources",
+ bp: `
+ android_app {
+ name: "foo",
+ }`,
+ // TODO(ccross): this should probably be false
+ enabled: true,
+ },
+ {
+ name: "installable java library without sources",
+ bp: `
+ java_library {
+ name: "foo",
+ installable: true,
+ }`,
+ // TODO(ccross): this should probably be false
+ enabled: true,
+ },
+
+ {
+ name: "static java library",
+ bp: `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ }`,
+ enabled: false,
+ },
+ {
+ name: "java test",
+ bp: `
+ java_test {
+ name: "foo",
+ srcs: ["a.java"],
+ }`,
+ enabled: false,
+ },
+ {
+ name: "android test",
+ bp: `
+ android_test {
+ name: "foo",
+ srcs: ["a.java"],
+ }`,
+ enabled: false,
+ },
+ {
+ name: "android test helper app",
+ bp: `
+ android_test_helper_app {
+ name: "foo",
+ srcs: ["a.java"],
+ }`,
+ enabled: false,
+ },
+ {
+ name: "compile_dex",
+ bp: `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ compile_dex: true,
+ }`,
+ enabled: false,
+ },
+ {
+ name: "dex_import",
+ bp: `
+ dex_import {
+ name: "foo",
+ jars: ["a.jar"],
+ }`,
+ enabled: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ ctx := testJava(t, test.bp)
+
+ dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeDescription("dexpreopt")
+ enabled := dexpreopt.Rule != nil
+
+ if enabled != test.enabled {
+ t.Fatalf("want dexpreopt %s, got %s", enabledString(test.enabled), enabledString(enabled))
+ }
+ })
+
+ }
+}
+
+func enabledString(enabled bool) string {
+ if enabled {
+ return "enabled"
+ } else {
+ return "disabled"
+ }
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 7171c91..f56cae8 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -18,6 +18,8 @@
"android/soong/android"
"android/soong/java/config"
"fmt"
+ "path/filepath"
+ "runtime"
"strings"
"github.com/google/blueprint"
@@ -28,93 +30,460 @@
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.JavadocCmd} -encoding UTF-8 @$out.rsp @$srcJarDir/list ` +
- `$opts $bootclasspathArgs $classpathArgs -sourcepath $sourcepath ` +
+ `${config.SoongJavacWrapper} ${config.JavadocCmd} -encoding UTF-8 @$out.rsp @$srcJarDir/list ` +
+ `$opts $bootclasspathArgs $classpathArgs $sourcepathArgs ` +
`-d $outDir -quiet && ` +
`${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
- `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir`,
+ `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir $postDoclavaCmds && ` +
+ `rm -rf "$srcJarDir"`,
+
CommandDeps: []string{
"${config.ZipSyncCmd}",
"${config.JavadocCmd}",
"${config.SoongZipCmd}",
- "$JsilverJar",
- "$DoclavaJar",
+ },
+ CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ Restat: true,
+ },
+ "outDir", "srcJarDir", "stubsDir", "srcJars", "opts",
+ "bootclasspathArgs", "classpathArgs", "sourcepathArgs", "docZip", "postDoclavaCmds")
+
+ apiCheck = pctx.AndroidStaticRule("apiCheck",
+ blueprint.RuleParams{
+ Command: `( ${config.ApiCheckCmd} -JXmx1024m -J"classpath $classpath" $opts ` +
+ `$apiFile $apiFileToCheck $removedApiFile $removedApiFileToCheck ` +
+ `&& touch $out ) || (echo -e "$msg" ; exit 38)`,
+ CommandDeps: []string{
+ "${config.ApiCheckCmd}",
+ },
+ },
+ "classpath", "opts", "apiFile", "apiFileToCheck", "removedApiFile", "removedApiFileToCheck", "msg")
+
+ updateApi = pctx.AndroidStaticRule("updateApi",
+ blueprint.RuleParams{
+ Command: `( ( cp -f $srcApiFile $destApiFile && cp -f $srcRemovedApiFile $destRemovedApiFile ) ` +
+ `&& touch $out ) || (echo failed to update public API ; exit 38)`,
+ },
+ "srcApiFile", "destApiFile", "srcRemovedApiFile", "destRemovedApiFile")
+
+ metalava = pctx.AndroidStaticRule("metalava",
+ blueprint.RuleParams{
+ Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
+ `mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
+ `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `${config.JavaCmd} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
+ `$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
+ `$opts && ` +
+ `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
+ `(if $writeSdkValues; then ${config.SoongZipCmd} -write_if_changed -d -o $metadataZip ` +
+ `-C $metadataDir -D $metadataDir; fi) && ` +
+ `rm -rf "$srcJarDir"`,
+ CommandDeps: []string{
+ "${config.ZipSyncCmd}",
+ "${config.JavaCmd}",
+ "${config.MetalavaJar}",
+ "${config.SoongZipCmd}",
},
Rspfile: "$out.rsp",
RspfileContent: "$in",
Restat: true,
},
- "outDir", "srcJarDir", "stubsDir", "srcJars", "opts",
- "bootclasspathArgs", "classpathArgs", "sourcepath", "docZip", "JsilverJar", "DoclavaJar")
+ "outDir", "srcJarDir", "stubsDir", "srcJars", "javaVersion", "bootclasspathArgs",
+ "classpathArgs", "sourcepathArgs", "opts", "writeSdkValues", "metadataZip", "metadataDir")
+
+ metalavaApiCheck = pctx.AndroidStaticRule("metalavaApiCheck",
+ blueprint.RuleParams{
+ Command: `( rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` +
+ `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `${config.JavaCmd} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
+ `$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
+ `$opts && touch $out && rm -rf "$srcJarDir") || ` +
+ `( echo -e "$msg" ; exit 38 )`,
+ CommandDeps: []string{
+ "${config.ZipSyncCmd}",
+ "${config.JavaCmd}",
+ "${config.MetalavaJar}",
+ },
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ },
+ "srcJarDir", "srcJars", "javaVersion", "bootclasspathArgs", "classpathArgs", "sourcepathArgs", "opts", "msg")
+
+ nullabilityWarningsCheck = pctx.AndroidStaticRule("nullabilityWarningsCheck",
+ blueprint.RuleParams{
+ Command: `( diff $expected $actual && touch $out ) || ( echo -e "$msg" ; exit 38 )`,
+ },
+ "expected", "actual", "msg")
+
+ dokka = pctx.AndroidStaticRule("dokka",
+ blueprint.RuleParams{
+ Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
+ `mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
+ `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `${config.JavaCmd} -jar ${config.DokkaJar} $srcJarDir ` +
+ `$classpathArgs -format dac -dacRoot /reference/kotlin -output $outDir $opts && ` +
+ `${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
+ `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
+ `rm -rf "$srcJarDir"`,
+ CommandDeps: []string{
+ "${config.ZipSyncCmd}",
+ "${config.DokkaJar}",
+ "${config.MetalavaJar}",
+ "${config.SoongZipCmd}",
+ },
+ Restat: true,
+ },
+ "outDir", "srcJarDir", "stubsDir", "srcJars", "classpathArgs", "opts", "docZip")
)
func init() {
+ android.RegisterModuleType("doc_defaults", DocDefaultsFactory)
+ android.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
+
android.RegisterModuleType("droiddoc", DroiddocFactory)
android.RegisterModuleType("droiddoc_host", DroiddocHostFactory)
- android.RegisterModuleType("droiddoc_template", DroiddocTemplateFactory)
+ android.RegisterModuleType("droiddoc_exported_dir", ExportedDroiddocDirFactory)
android.RegisterModuleType("javadoc", JavadocFactory)
android.RegisterModuleType("javadoc_host", JavadocHostFactory)
+
+ android.RegisterModuleType("droidstubs", DroidstubsFactory)
+ android.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
}
+var (
+ srcsLibTag = dependencyTag{name: "sources from javalib"}
+)
+
type JavadocProperties struct {
// list of source files used to compile the Java module. May be .java, .logtags, .proto,
// or .aidl files.
- Srcs []string `android:"arch_variant"`
+ Srcs []string `android:"path,arch_variant"`
// list of directories rooted at the Android.bp file that will
// be added to the search paths for finding source files when passing package names.
- Local_sourcepaths []string `android:"arch_variant"`
+ Local_sourcepaths []string
// list of source files that should not be used to build the Java module.
// This is most useful in the arch/multilib variants to remove non-common files
// filegroup or genrule can be included within this property.
- Exclude_srcs []string `android:"arch_variant"`
+ Exclude_srcs []string `android:"path,arch_variant"`
- // list of of java libraries that will be in the classpath.
+ // list of java libraries that will be in the classpath.
Libs []string `android:"arch_variant"`
+ // don't build against the default libraries (bootclasspath, ext, and framework for device
+ // targets)
+ No_standard_libs *bool
+
+ // don't build against the framework libraries (ext, and framework for device targets)
+ No_framework_libs *bool
+
+ // the java library (in classpath) for documentation that provides java srcs and srcjars.
+ Srcs_lib *string
+
+ // the base dirs under srcs_lib will be scanned for java srcs.
+ Srcs_lib_whitelist_dirs []string
+
+ // the sub dirs under srcs_lib_whitelist_dirs will be scanned for java srcs.
+ Srcs_lib_whitelist_pkgs []string
+
// If set to false, don't allow this module(-docs.zip) to be exported. Defaults to true.
- Installable *bool `android:"arch_variant"`
+ Installable *bool
// if not blank, set to the version of the sdk to compile against
Sdk_version *string `android:"arch_variant"`
-}
-type DroiddocProperties struct {
- // directory relative to top of the source tree that contains doc templates files.
- Custom_template *string `android:"arch_variant"`
+ Aidl struct {
+ // Top level directories to pass to aidl tool
+ Include_dirs []string
- // directories relative to top of the source tree which contains html/jd files.
- Html_dirs []string `android:"arch_variant"`
+ // Directories rooted at the Android.bp file to pass to aidl tool
+ Local_include_dirs []string
+ }
- // set a value in the Clearsilver hdf namespace.
- Hdf []string `android:"arch_variant"`
-
- // proofread file contains all of the text content of the javadocs concatenated into one file,
- // suitable for spell-checking and other goodness.
- Proofread_file *string `android:"arch_variant"`
-
- // a todo file lists the program elements that are missing documentation.
- // At some point, this might be improved to show more warnings.
- Todo_file *string `android:"arch_variant"`
+ // If not blank, set the java version passed to javadoc as -source
+ Java_version *string
// local files that are used within user customized droiddoc options.
- Arg_files []string `android:"arch_variant"`
+ Arg_files []string `android:"path"`
// user customized droiddoc args.
// Available variables for substitution:
//
// $(location <label>): the path to the arg_files with name <label>
- Args *string `android:"arch_variant"`
+ Args *string
// names of the output files used in args that will be generated
- Out []string `android:"arch_variant"`
+ Out []string
+}
+
+type ApiToCheck struct {
+ // path to the API txt file that the new API extracted from source code is checked
+ // against. The path can be local to the module or from other module (via :module syntax).
+ Api_file *string `android:"path"`
+
+ // path to the API txt file that the new @removed API extractd from source code is
+ // checked against. The path can be local to the module or from other module (via
+ // :module syntax).
+ Removed_api_file *string `android:"path"`
+
+ // If not blank, path to the baseline txt file for approved API check violations.
+ Baseline_file *string `android:"path"`
+
+ // Arguments to the apicheck tool.
+ Args *string
+}
+
+type DroiddocProperties struct {
+ // directory relative to top of the source tree that contains doc templates files.
+ Custom_template *string
+
+ // directories under current module source which contains html/jd files.
+ Html_dirs []string
+
+ // set a value in the Clearsilver hdf namespace.
+ Hdf []string
+
+ // proofread file contains all of the text content of the javadocs concatenated into one file,
+ // suitable for spell-checking and other goodness.
+ Proofread_file *string `android:"path"`
+
+ // a todo file lists the program elements that are missing documentation.
+ // At some point, this might be improved to show more warnings.
+ Todo_file *string `android:"path"`
+
+ // directory under current module source that provide additional resources (images).
+ Resourcesdir *string
+
+ // resources output directory under out/soong/.intermediates.
+ Resourcesoutdir *string
+
+ // if set to true, collect the values used by the Dev tools and
+ // write them in files packaged with the SDK. Defaults to false.
+ Write_sdk_values *bool
+
+ // index.html under current module will be copied to docs out dir, if not null.
+ Static_doc_index_redirect *string `android:"path"`
+
+ // source.properties under current module will be copied to docs out dir, if not null.
+ Static_doc_properties *string `android:"path"`
// a list of files under current module source dir which contains known tags in Java sources.
// filegroup or genrule can be included within this property.
- Knowntags []string `android:"arch_variant"`
+ Knowntags []string `android:"path"`
+
+ // the tag name used to distinguish if the API files belong to public/system/test.
+ Api_tag_name *string
+
+ // the generated public API filename by Doclava.
+ Api_filename *string
+
+ // the generated public Dex API filename by Doclava.
+ Dex_api_filename *string
+
+ // the generated private API filename by Doclava.
+ Private_api_filename *string
+
+ // the generated private Dex API filename by Doclava.
+ Private_dex_api_filename *string
+
+ // the generated removed API filename by Doclava.
+ Removed_api_filename *string
+
+ // the generated removed Dex API filename by Doclava.
+ Removed_dex_api_filename *string
+
+ // mapping of dex signatures to source file and line number. This is a temporary property and
+ // will be deleted; you probably shouldn't be using it.
+ Dex_mapping_filename *string
+
+ // the generated exact API filename by Doclava.
+ Exact_api_filename *string
+
+ // the generated proguard filename by Doclava.
+ Proguard_filename *string
+
+ // if set to false, don't allow droiddoc to generate stubs source files. Defaults to true.
+ Create_stubs *bool
+
+ Check_api struct {
+ Last_released ApiToCheck
+
+ Current ApiToCheck
+
+ // do not perform API check against Last_released, in the case that both two specified API
+ // files by Last_released are modules which don't exist.
+ Ignore_missing_latest_api *bool `blueprint:"mutated"`
+ }
+
+ // if set to true, generate docs through Dokka instead of Doclava.
+ Dokka_enabled *bool
}
+type DroidstubsProperties struct {
+ // the tag name used to distinguish if the API files belong to public/system/test.
+ Api_tag_name *string
+
+ // the generated public API filename by Metalava.
+ Api_filename *string
+
+ // the generated public Dex API filename by Metalava.
+ Dex_api_filename *string
+
+ // the generated private API filename by Metalava.
+ Private_api_filename *string
+
+ // the generated private Dex API filename by Metalava.
+ Private_dex_api_filename *string
+
+ // the generated removed API filename by Metalava.
+ Removed_api_filename *string
+
+ // the generated removed Dex API filename by Metalava.
+ Removed_dex_api_filename *string
+
+ // mapping of dex signatures to source file and line number. This is a temporary property and
+ // will be deleted; you probably shouldn't be using it.
+ Dex_mapping_filename *string
+
+ // the generated exact API filename by Metalava.
+ Exact_api_filename *string
+
+ // the generated proguard filename by Metalava.
+ Proguard_filename *string
+
+ Check_api struct {
+ Last_released ApiToCheck
+
+ Current ApiToCheck
+
+ // do not perform API check against Last_released, in the case that both two specified API
+ // files by Last_released are modules which don't exist.
+ Ignore_missing_latest_api *bool `blueprint:"mutated"`
+ }
+
+ // user can specify the version of previous released API file in order to do compatibility check.
+ Previous_api *string `android:"path"`
+
+ // is set to true, Metalava will allow framework SDK to contain annotations.
+ Annotations_enabled *bool
+
+ // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
+ Merge_annotations_dirs []string
+
+ // a list of top-level directories containing Java stub files to merge show/hide annotations from.
+ Merge_inclusion_annotations_dirs []string
+
+ // a file containing a list of classes to do nullability validation for.
+ Validate_nullability_from_list *string
+
+ // a file containing expected warnings produced by validation of nullability annotations.
+ Check_nullability_warnings *string
+
+ // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
+ Create_doc_stubs *bool
+
+ // is set to true, Metalava will allow framework SDK to contain API levels annotations.
+ Api_levels_annotations_enabled *bool
+
+ // the dirs which Metalava extracts API levels annotations from.
+ Api_levels_annotations_dirs []string
+
+ // if set to true, collect the values used by the Dev tools and
+ // write them in files packaged with the SDK. Defaults to false.
+ Write_sdk_values *bool
+
+ // If set to true, .xml based public API file will be also generated, and
+ // JDiff tool will be invoked to genreate javadoc files. Defaults to false.
+ Jdiff_enabled *bool
+}
+
+//
+// Common flags passed down to build rule
+//
+type droiddocBuilderFlags struct {
+ bootClasspathArgs string
+ classpathArgs string
+ sourcepathArgs string
+ dokkaClasspathArgs string
+ aidlFlags string
+ aidlDeps android.Paths
+
+ doclavaStubsFlags string
+ doclavaDocsFlags string
+ postDoclavaCmds string
+
+ metalavaStubsFlags string
+ metalavaAnnotationsFlags string
+ metalavaMergeAnnoDirFlags string
+ metalavaInclusionAnnotationsFlags string
+ metalavaApiLevelsAnnotationsFlags string
+
+ metalavaApiToXmlFlags string
+}
+
+func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
+ android.InitAndroidArchModule(module, hod, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+}
+
+func apiCheckEnabled(apiToCheck ApiToCheck, apiVersionTag string) bool {
+ if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
+ return true
+ } else if String(apiToCheck.Api_file) != "" {
+ panic("for " + apiVersionTag + " removed_api_file has to be non-empty!")
+ } else if String(apiToCheck.Removed_api_file) != "" {
+ panic("for " + apiVersionTag + " api_file has to be non-empty!")
+ }
+
+ return false
+}
+
+func ignoreMissingModules(ctx android.BottomUpMutatorContext, apiToCheck *ApiToCheck) {
+ api_file := String(apiToCheck.Api_file)
+ removed_api_file := String(apiToCheck.Removed_api_file)
+
+ api_module := android.SrcIsModule(api_file)
+ removed_api_module := android.SrcIsModule(removed_api_file)
+
+ if api_module == "" || removed_api_module == "" {
+ return
+ }
+
+ if ctx.OtherModuleExists(api_module) || ctx.OtherModuleExists(removed_api_module) {
+ return
+ }
+
+ apiToCheck.Api_file = nil
+ apiToCheck.Removed_api_file = nil
+}
+
+type ApiFilePath interface {
+ ApiFilePath() android.Path
+}
+
+func transformUpdateApi(ctx android.ModuleContext, destApiFile, destRemovedApiFile,
+ srcApiFile, srcRemovedApiFile android.Path, output android.WritablePath) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: updateApi,
+ Description: "Update API",
+ Output: output,
+ Implicits: append(android.Paths{}, srcApiFile, srcRemovedApiFile,
+ destApiFile, destRemovedApiFile),
+ Args: map[string]string{
+ "destApiFile": destApiFile.String(),
+ "srcApiFile": srcApiFile.String(),
+ "destRemovedApiFile": destRemovedApiFile.String(),
+ "srcRemovedApiFile": srcRemovedApiFile.String(),
+ },
+ })
+}
+
+//
+// Javadoc
+//
type Javadoc struct {
android.ModuleBase
android.DefaultableModuleBase
@@ -124,20 +493,16 @@
srcJars android.Paths
srcFiles android.Paths
sourcepaths android.Paths
+ argFiles android.Paths
- docZip android.WritablePath
- stubsJar android.WritablePath
+ args string
+
+ docZip android.WritablePath
+ stubsSrcJar android.WritablePath
}
-type Droiddoc struct {
- Javadoc
-
- properties DroiddocProperties
-}
-
-func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
- android.InitAndroidArchModule(module, hod, android.MultilibCommon)
- android.InitDefaultableModule(module)
+func (j *Javadoc) Srcs() android.Paths {
+ return android.Paths{j.stubsSrcJar}
}
func JavadocFactory() android.Module {
@@ -158,6 +523,317 @@
return module
}
+var _ android.SourceFileProducer = (*Javadoc)(nil)
+
+func (j *Javadoc) sdkVersion() string {
+ return String(j.properties.Sdk_version)
+}
+
+func (j *Javadoc) minSdkVersion() string {
+ return j.sdkVersion()
+}
+
+func (j *Javadoc) targetSdkVersion() string {
+ return j.sdkVersion()
+}
+
+func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
+ if ctx.Device() {
+ if !Bool(j.properties.No_standard_libs) {
+ sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ if sdkDep.useDefaultLibs {
+ ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
+ if ctx.Config().TargetOpenJDK9() {
+ ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
+ }
+ if !Bool(j.properties.No_framework_libs) {
+ ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
+ }
+ } else if sdkDep.useModule {
+ if ctx.Config().TargetOpenJDK9() {
+ ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+ }
+ ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.modules...)
+ }
+ }
+ }
+
+ ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+ if j.properties.Srcs_lib != nil {
+ ctx.AddVariationDependencies(nil, srcsLibTag, *j.properties.Srcs_lib)
+ }
+}
+
+func (j *Javadoc) genWhitelistPathPrefixes(whitelistPathPrefixes map[string]bool) {
+ for _, dir := range j.properties.Srcs_lib_whitelist_dirs {
+ for _, pkg := range j.properties.Srcs_lib_whitelist_pkgs {
+ // convert foo.bar.baz to foo/bar/baz
+ pkgAsPath := filepath.Join(strings.Split(pkg, ".")...)
+ prefix := filepath.Join(dir, pkgAsPath)
+ if _, found := whitelistPathPrefixes[prefix]; !found {
+ whitelistPathPrefixes[prefix] = true
+ }
+ }
+ }
+}
+
+func (j *Javadoc) collectAidlFlags(ctx android.ModuleContext, deps deps) droiddocBuilderFlags {
+ var flags droiddocBuilderFlags
+
+ flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
+
+ return flags
+}
+
+func (j *Javadoc) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
+ aidlIncludeDirs android.Paths) (string, android.Paths) {
+
+ aidlIncludes := android.PathsForModuleSrc(ctx, j.properties.Aidl.Local_include_dirs)
+ aidlIncludes = append(aidlIncludes, android.PathsForSource(ctx, j.properties.Aidl.Include_dirs)...)
+
+ var flags []string
+ var deps android.Paths
+
+ if aidlPreprocess.Valid() {
+ flags = append(flags, "-p"+aidlPreprocess.String())
+ deps = append(deps, aidlPreprocess.Path())
+ } else {
+ flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
+ }
+
+ flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
+ flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
+ if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
+ flags = append(flags, "-I"+src.String())
+ }
+
+ return strings.Join(flags, " "), deps
+}
+
+func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths,
+ flags droiddocBuilderFlags) android.Paths {
+
+ outSrcFiles := make(android.Paths, 0, len(srcFiles))
+
+ for _, srcFile := range srcFiles {
+ switch srcFile.Ext() {
+ case ".aidl":
+ javaFile := genAidl(ctx, srcFile, flags.aidlFlags, flags.aidlDeps)
+ outSrcFiles = append(outSrcFiles, javaFile)
+ case ".sysprop":
+ javaFile := genSysprop(ctx, srcFile)
+ outSrcFiles = append(outSrcFiles, javaFile)
+ default:
+ outSrcFiles = append(outSrcFiles, srcFile)
+ }
+ }
+
+ return outSrcFiles
+}
+
+func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps {
+ var deps deps
+
+ sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ if sdkDep.invalidVersion {
+ ctx.AddMissingDependencies(sdkDep.modules)
+ } else if sdkDep.useFiles {
+ deps.bootClasspath = append(deps.bootClasspath, sdkDep.jars...)
+ }
+
+ ctx.VisitDirectDeps(func(module android.Module) {
+ otherName := ctx.OtherModuleName(module)
+ tag := ctx.OtherModuleDependencyTag(module)
+
+ switch tag {
+ case bootClasspathTag:
+ if dep, ok := module.(Dependency); ok {
+ deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
+ } else {
+ panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
+ }
+ case libTag:
+ switch dep := module.(type) {
+ case SdkLibraryDependency:
+ deps.classpath = append(deps.classpath, dep.SdkImplementationJars(ctx, j.sdkVersion())...)
+ case Dependency:
+ deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+ case android.SourceFileProducer:
+ checkProducesJars(ctx, dep)
+ deps.classpath = append(deps.classpath, dep.Srcs()...)
+ default:
+ ctx.ModuleErrorf("depends on non-java module %q", otherName)
+ }
+ case srcsLibTag:
+ switch dep := module.(type) {
+ case Dependency:
+ srcs := dep.(SrcDependency).CompiledSrcs()
+ whitelistPathPrefixes := make(map[string]bool)
+ j.genWhitelistPathPrefixes(whitelistPathPrefixes)
+ for _, src := range srcs {
+ if _, ok := src.(android.WritablePath); ok { // generated sources
+ deps.srcs = append(deps.srcs, src)
+ } else { // select source path for documentation based on whitelist path prefixs.
+ for k := range whitelistPathPrefixes {
+ if strings.HasPrefix(src.Rel(), k) {
+ deps.srcs = append(deps.srcs, src)
+ break
+ }
+ }
+ }
+ }
+ deps.srcJars = append(deps.srcJars, dep.(SrcDependency).CompiledSrcJars()...)
+ default:
+ ctx.ModuleErrorf("depends on non-java module %q", otherName)
+ }
+ case systemModulesTag:
+ if deps.systemModules != nil {
+ panic("Found two system module dependencies")
+ }
+ sm := module.(*SystemModules)
+ if sm.outputFile == nil {
+ panic("Missing directory for system module dependency")
+ }
+ deps.systemModules = sm.outputFile
+ }
+ })
+ // do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
+ // may contain filegroup or genrule.
+ srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+ flags := j.collectAidlFlags(ctx, deps)
+ srcFiles = j.genSources(ctx, srcFiles, flags)
+
+ // srcs may depend on some genrule output.
+ j.srcJars = srcFiles.FilterByExt(".srcjar")
+ j.srcJars = append(j.srcJars, deps.srcJars...)
+
+ j.srcFiles = srcFiles.FilterOutByExt(".srcjar")
+ j.srcFiles = append(j.srcFiles, deps.srcs...)
+
+ j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
+ j.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
+
+ if j.properties.Local_sourcepaths == nil && len(j.srcFiles) > 0 {
+ j.properties.Local_sourcepaths = append(j.properties.Local_sourcepaths, ".")
+ }
+ j.sourcepaths = android.PathsForModuleSrc(ctx, j.properties.Local_sourcepaths)
+
+ j.argFiles = android.PathsForModuleSrc(ctx, j.properties.Arg_files)
+ argFilesMap := map[string]string{}
+ argFileLabels := []string{}
+
+ for _, label := range j.properties.Arg_files {
+ var paths = android.PathsForModuleSrc(ctx, []string{label})
+ if _, exists := argFilesMap[label]; !exists {
+ argFilesMap[label] = strings.Join(paths.Strings(), " ")
+ argFileLabels = append(argFileLabels, label)
+ } else {
+ ctx.ModuleErrorf("multiple arg_files for %q, %q and %q",
+ label, argFilesMap[label], paths)
+ }
+ }
+
+ var err error
+ j.args, err = android.Expand(String(j.properties.Args), func(name string) (string, error) {
+ if strings.HasPrefix(name, "location ") {
+ label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
+ if paths, ok := argFilesMap[label]; ok {
+ return paths, nil
+ } else {
+ return "", fmt.Errorf("unknown location label %q, expecting one of %q",
+ label, strings.Join(argFileLabels, ", "))
+ }
+ } else if name == "genDir" {
+ return android.PathForModuleGen(ctx).String(), nil
+ }
+ return "", fmt.Errorf("unknown variable '$(%s)'", name)
+ })
+
+ if err != nil {
+ ctx.PropertyErrorf("args", "%s", err.Error())
+ }
+
+ return deps
+}
+
+func (j *Javadoc) DepsMutator(ctx android.BottomUpMutatorContext) {
+ j.addDeps(ctx)
+}
+
+func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ deps := j.collectDeps(ctx)
+
+ var implicits android.Paths
+ implicits = append(implicits, deps.bootClasspath...)
+ implicits = append(implicits, deps.classpath...)
+
+ var bootClasspathArgs, classpathArgs, sourcepathArgs string
+
+ javaVersion := getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
+ if len(deps.bootClasspath) > 0 {
+ var systemModules classpath
+ if deps.systemModules != nil {
+ systemModules = append(systemModules, deps.systemModules)
+ }
+ bootClasspathArgs = systemModules.FormJavaSystemModulesPath("--system ", ctx.Device())
+ bootClasspathArgs = bootClasspathArgs + " --patch-module java.base=."
+ }
+ if len(deps.classpath.Strings()) > 0 {
+ classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
+ }
+
+ implicits = append(implicits, j.srcJars...)
+ implicits = append(implicits, j.argFiles...)
+
+ opts := "-source " + javaVersion + " -J-Xmx1024m -XDignore.symbol.file -Xdoclint:none"
+
+ sourcepathArgs = "-sourcepath " + strings.Join(j.sourcepaths.Strings(), ":")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: javadoc,
+ Description: "Javadoc",
+ Output: j.stubsSrcJar,
+ ImplicitOutput: j.docZip,
+ Inputs: j.srcFiles,
+ Implicits: implicits,
+ Args: map[string]string{
+ "outDir": android.PathForModuleOut(ctx, "out").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
+ "stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
+ "srcJars": strings.Join(j.srcJars.Strings(), " "),
+ "opts": opts,
+ "bootclasspathArgs": bootClasspathArgs,
+ "classpathArgs": classpathArgs,
+ "sourcepathArgs": sourcepathArgs,
+ "docZip": j.docZip.String(),
+ },
+ })
+}
+
+//
+// Droiddoc
+//
+type Droiddoc struct {
+ Javadoc
+
+ properties DroiddocProperties
+ apiFile android.WritablePath
+ dexApiFile android.WritablePath
+ privateApiFile android.WritablePath
+ privateDexApiFile android.WritablePath
+ removedApiFile android.WritablePath
+ removedDexApiFile android.WritablePath
+ exactApiFile android.WritablePath
+ apiMappingFile android.WritablePath
+ proguardFile android.WritablePath
+
+ checkCurrentApiTimestamp android.WritablePath
+ updateCurrentApiTimestamp android.WritablePath
+ checkLastReleasedApiTimestamp android.WritablePath
+
+ apiFilePath android.Path
+}
+
func DroiddocFactory() android.Module {
module := &Droiddoc{}
@@ -178,242 +854,112 @@
return module
}
-func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
- if ctx.Device() {
- sdkDep := decodeSdkDep(ctx, String(j.properties.Sdk_version))
- if sdkDep.useDefaultLibs {
- ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
- ctx.AddDependency(ctx.Module(), libTag, []string{"ext", "framework"}...)
- } else if sdkDep.useModule {
- ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
- }
- }
-
- ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
-
- android.ExtractSourcesDeps(ctx, j.properties.Srcs)
-
- // exclude_srcs may contain filegroup or genrule.
- android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
-}
-
-func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps {
- var deps deps
-
- sdkDep := decodeSdkDep(ctx, String(j.properties.Sdk_version))
- if sdkDep.invalidVersion {
- ctx.AddMissingDependencies([]string{sdkDep.module})
- } else if sdkDep.useFiles {
- deps.bootClasspath = append(deps.bootClasspath, sdkDep.jar)
- }
-
- ctx.VisitDirectDeps(func(module android.Module) {
- otherName := ctx.OtherModuleName(module)
- tag := ctx.OtherModuleDependencyTag(module)
-
- switch dep := module.(type) {
- case Dependency:
- switch tag {
- case bootClasspathTag:
- deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
- case libTag:
- deps.classpath = append(deps.classpath, dep.ImplementationJars()...)
- default:
- panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
- }
- case android.SourceFileProducer:
- switch tag {
- case libTag:
- checkProducesJars(ctx, dep)
- deps.classpath = append(deps.classpath, dep.Srcs()...)
- case android.DefaultsDepTag, android.SourceDepTag:
- // Nothing to do
- default:
- ctx.ModuleErrorf("dependency on genrule %q may only be in srcs, libs", otherName)
- }
- default:
- switch tag {
- case android.DefaultsDepTag, android.SourceDepTag, droiddocTemplateTag:
- // Nothing to do
- default:
- ctx.ModuleErrorf("depends on non-java module %q", otherName)
- }
- }
- })
- // do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
- // may contain filegroup or genrule.
- srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
-
- // srcs may depend on some genrule output.
- j.srcJars = srcFiles.FilterByExt(".srcjar")
- j.srcFiles = srcFiles.FilterOutByExt(".srcjar")
-
- j.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip")
- j.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
-
- if j.properties.Local_sourcepaths == nil {
- j.properties.Local_sourcepaths = append(j.properties.Local_sourcepaths, ".")
- }
- j.sourcepaths = android.PathsForModuleSrc(ctx, j.properties.Local_sourcepaths)
- j.sourcepaths = append(j.sourcepaths, deps.bootClasspath...)
- j.sourcepaths = append(j.sourcepaths, deps.classpath...)
-
- return deps
-}
-
-func (j *Javadoc) DepsMutator(ctx android.BottomUpMutatorContext) {
- j.addDeps(ctx)
-}
-
-func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- deps := j.collectDeps(ctx)
-
- var implicits android.Paths
- implicits = append(implicits, deps.bootClasspath...)
- implicits = append(implicits, deps.classpath...)
-
- var bootClasspathArgs, classpathArgs string
- if ctx.Config().UseOpenJDK9() {
- if len(deps.bootClasspath) > 0 {
- // For OpenJDK 9 we use --patch-module to define the core libraries code.
- // TODO(tobiast): Reorganize this when adding proper support for OpenJDK 9
- // modules. Here we treat all code in core libraries as being in java.base
- // to work around the OpenJDK 9 module system. http://b/62049770
- bootClasspathArgs = "--patch-module=java.base=" + strings.Join(deps.bootClasspath.Strings(), ":")
- }
- } else {
- if len(deps.bootClasspath.Strings()) > 0 {
- // For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
- bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
- }
- }
- if len(deps.classpath.Strings()) > 0 {
- classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
- }
-
- implicits = append(implicits, j.srcJars...)
-
- opts := "-J-Xmx1024m -XDignore.symbol.file -Xdoclint:none"
-
- ctx.Build(pctx, android.BuildParams{
- Rule: javadoc,
- Description: "Javadoc",
- Output: j.stubsJar,
- ImplicitOutput: j.docZip,
- Inputs: j.srcFiles,
- Implicits: implicits,
- Args: map[string]string{
- "outDir": android.PathForModuleOut(ctx, "docs", "out").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "docs", "srcjars").String(),
- "stubsDir": android.PathForModuleOut(ctx, "docs", "stubsDir").String(),
- "srcJars": strings.Join(j.srcJars.Strings(), " "),
- "opts": opts,
- "bootClasspathArgs": bootClasspathArgs,
- "classpathArgs": classpathArgs,
- "sourcepath": strings.Join(j.sourcepaths.Strings(), ":"),
- "docZip": j.docZip.String(),
- },
- })
+func (d *Droiddoc) ApiFilePath() android.Path {
+ return d.apiFilePath
}
func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) {
d.Javadoc.addDeps(ctx)
+ if Bool(d.properties.Check_api.Ignore_missing_latest_api) {
+ ignoreMissingModules(ctx, &d.properties.Check_api.Last_released)
+ }
+
+ if String(d.properties.Custom_template) != "" {
+ ctx.AddDependency(ctx.Module(), droiddocTemplateTag, String(d.properties.Custom_template))
+ }
+}
+
+func (d *Droiddoc) initBuilderFlags(ctx android.ModuleContext, implicits *android.Paths,
+ deps deps) (droiddocBuilderFlags, error) {
+ var flags droiddocBuilderFlags
+
+ *implicits = append(*implicits, deps.bootClasspath...)
+ *implicits = append(*implicits, deps.classpath...)
+
+ if len(deps.bootClasspath.Strings()) > 0 {
+ // For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
+ flags.bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
+ }
+ flags.classpathArgs = deps.classpath.FormJavaClassPath("-classpath")
+ // Dokka doesn't support bootClasspath, so combine these two classpath vars for Dokka.
+ dokkaClasspath := classpath{}
+ dokkaClasspath = append(dokkaClasspath, deps.bootClasspath...)
+ dokkaClasspath = append(dokkaClasspath, deps.classpath...)
+ flags.dokkaClasspathArgs = dokkaClasspath.FormJavaClassPath("-classpath")
+
+ // TODO(nanzhang): Remove this if- statement once we finish migration for all Doclava
+ // based stubs generation.
+ // In the future, all the docs generation depends on Metalava stubs (droidstubs) srcjar
+ // dir. We need add the srcjar dir to -sourcepath arg, so that Javadoc can figure out
+ // the correct package name base path.
+ if len(d.Javadoc.properties.Local_sourcepaths) > 0 {
+ flags.sourcepathArgs = "-sourcepath " + strings.Join(d.Javadoc.sourcepaths.Strings(), ":")
+ } else {
+ flags.sourcepathArgs = "-sourcepath " + android.PathForModuleOut(ctx, "srcjars").String()
+ }
+
+ return flags, nil
+}
+
+func (d *Droiddoc) collectDoclavaDocsFlags(ctx android.ModuleContext, implicits *android.Paths,
+ jsilver, doclava android.Path) string {
+
+ *implicits = append(*implicits, jsilver)
+ *implicits = append(*implicits, doclava)
+
+ var date string
+ if runtime.GOOS == "darwin" {
+ date = `date -r`
+ } else {
+ date = `date -d`
+ }
+
+ // Droiddoc always gets "-source 1.8" because it doesn't support 1.9 sources. For modules with 1.9
+ // sources, droiddoc will get sources produced by metalava which will have already stripped out the
+ // 1.9 language features.
+ args := " -source 1.8 -J-Xmx1600m -J-XX:-OmitStackTraceInFastThrow -XDignore.symbol.file " +
+ "-doclet com.google.doclava.Doclava -docletpath " + jsilver.String() + ":" + doclava.String() + " " +
+ "-hdf page.build " + ctx.Config().BuildId() + "-" + ctx.Config().BuildNumberFromFile() + " " +
+ `-hdf page.now "$$(` + date + ` @$$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")" `
+
if String(d.properties.Custom_template) == "" {
// TODO: This is almost always droiddoc-templates-sdk
ctx.PropertyErrorf("custom_template", "must specify a template")
- } else {
- ctx.AddDependency(ctx.Module(), droiddocTemplateTag, String(d.properties.Custom_template))
}
- // extra_arg_files may contains filegroup or genrule.
- android.ExtractSourcesDeps(ctx, d.properties.Arg_files)
-
- // knowntags may contain filegroup or genrule.
- android.ExtractSourcesDeps(ctx, d.properties.Knowntags)
-}
-
-func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- deps := d.Javadoc.collectDeps(ctx)
-
- var implicits android.Paths
- implicits = append(implicits, deps.bootClasspath...)
- implicits = append(implicits, deps.classpath...)
-
- argFiles := ctx.ExpandSources(d.properties.Arg_files, nil)
- argFilesMap := map[string]android.Path{}
-
- for _, f := range argFiles {
- implicits = append(implicits, f)
- if _, exists := argFilesMap[f.Rel()]; !exists {
- argFilesMap[f.Rel()] = f
- } else {
- ctx.ModuleErrorf("multiple arg_files for %q, %q and %q",
- f, argFilesMap[f.Rel()], f.Rel())
- }
- }
-
- args, err := android.Expand(String(d.properties.Args), func(name string) (string, error) {
- if strings.HasPrefix(name, "location ") {
- label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if f, ok := argFilesMap[label]; ok {
- return f.String(), nil
- } else {
- return "", fmt.Errorf("unknown location label %q", label)
- }
- } else if name == "genDir" {
- return android.PathForModuleGen(ctx).String(), nil
- }
- return "", fmt.Errorf("unknown variable '$(%s)'", name)
- })
-
- if err != nil {
- ctx.PropertyErrorf("extra_args", "%s", err.Error())
- return
- }
-
- var bootClasspathArgs, classpathArgs string
- if len(deps.bootClasspath.Strings()) > 0 {
- bootClasspathArgs = "-bootclasspath " + strings.Join(deps.bootClasspath.Strings(), ":")
- }
- if len(deps.classpath.Strings()) > 0 {
- classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
- }
-
- var templateDir string
ctx.VisitDirectDepsWithTag(droiddocTemplateTag, func(m android.Module) {
- if t, ok := m.(*DroiddocTemplate); ok {
- implicits = append(implicits, t.deps...)
- templateDir = t.dir.String()
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ *implicits = append(*implicits, t.deps...)
+ args = args + " -templatedir " + t.dir.String()
} else {
ctx.PropertyErrorf("custom_template", "module %q is not a droiddoc_template", ctx.OtherModuleName(m))
}
})
- var htmlDirArgs string
if len(d.properties.Html_dirs) > 0 {
- htmlDir := android.PathForModuleSrc(ctx, d.properties.Html_dirs[0])
- implicits = append(implicits, ctx.Glob(htmlDir.Join(ctx, "**/*").String(), nil)...)
- htmlDirArgs = "-htmldir " + htmlDir.String()
+ htmlDir := d.properties.Html_dirs[0]
+ *implicits = append(*implicits, android.PathsForModuleSrc(ctx, []string{filepath.Join(d.properties.Html_dirs[0], "**/*")})...)
+ args = args + " -htmldir " + htmlDir
}
- var htmlDir2Args string
if len(d.properties.Html_dirs) > 1 {
- htmlDir2 := android.PathForModuleSrc(ctx, d.properties.Html_dirs[1])
- implicits = append(implicits, ctx.Glob(htmlDir2.Join(ctx, "**/*").String(), nil)...)
- htmlDir2Args = "-htmldir2 " + htmlDir2.String()
+ htmlDir2 := d.properties.Html_dirs[1]
+ *implicits = append(*implicits, android.PathsForModuleSrc(ctx, []string{filepath.Join(htmlDir2, "**/*")})...)
+ args = args + " -htmldir2 " + htmlDir2
}
if len(d.properties.Html_dirs) > 2 {
ctx.PropertyErrorf("html_dirs", "Droiddoc only supports up to 2 html dirs")
}
- knownTags := ctx.ExpandSources(d.properties.Knowntags, nil)
- implicits = append(implicits, knownTags...)
+ knownTags := android.PathsForModuleSrc(ctx, d.properties.Knowntags)
+ *implicits = append(*implicits, knownTags...)
for _, kt := range knownTags {
args = args + " -knowntags " + kt.String()
}
+
for _, hdf := range d.properties.Hdf {
args = args + " -hdf " + hdf
}
@@ -422,6 +968,7 @@
proofreadFile := android.PathForModuleOut(ctx, String(d.properties.Proofread_file))
args = args + " -proofread " + proofreadFile.String()
}
+
if String(d.properties.Todo_file) != "" {
// tricky part:
// we should not compute full path for todo_file through PathForModuleOut().
@@ -429,71 +976,903 @@
args = args + " -todo " + String(d.properties.Todo_file)
}
- implicits = append(implicits, d.Javadoc.srcJars...)
-
- opts := "-source 1.8 -J-Xmx1600m -J-XX:-OmitStackTraceInFastThrow -XDignore.symbol.file " +
- "-doclet com.google.doclava.Doclava -docletpath ${config.JsilverJar}:${config.DoclavaJar} " +
- "-templatedir " + templateDir + " " + htmlDirArgs + " " + htmlDir2Args + " " +
- "-hdf page.build " + ctx.Config().BuildId() + "-" + ctx.Config().BuildNumberFromFile() + " " +
- "-hdf page.now " + `"$$(date -d @$$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")"` + " " +
- args + " -stubs " + android.PathForModuleOut(ctx, "docs", "stubsDir").String()
-
- var implicitOutputs android.WritablePaths
- implicitOutputs = append(implicitOutputs, d.Javadoc.docZip)
- for _, o := range d.properties.Out {
- implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
+ if String(d.properties.Resourcesdir) != "" {
+ // TODO: should we add files under resourcesDir to the implicits? It seems that
+ // resourcesDir is one sub dir of htmlDir
+ resourcesDir := android.PathForModuleSrc(ctx, String(d.properties.Resourcesdir))
+ args = args + " -resourcesdir " + resourcesDir.String()
}
+ if String(d.properties.Resourcesoutdir) != "" {
+ // TODO: it seems -resourceoutdir reference/android/images/ didn't get generated anywhere.
+ args = args + " -resourcesoutdir " + String(d.properties.Resourcesoutdir)
+ }
+ return args
+}
+
+func (d *Droiddoc) collectStubsFlags(ctx android.ModuleContext,
+ implicitOutputs *android.WritablePaths) string {
+ var doclavaFlags string
+ if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Api_filename) != "" {
+ d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
+ doclavaFlags += " -api " + d.apiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.apiFile)
+ d.apiFilePath = d.apiFile
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Removed_api_filename) != "" {
+ d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
+ doclavaFlags += " -removedApi " + d.removedApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.removedApiFile)
+ }
+
+ if String(d.properties.Private_api_filename) != "" {
+ d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename))
+ doclavaFlags += " -privateApi " + d.privateApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.privateApiFile)
+ }
+
+ if String(d.properties.Dex_api_filename) != "" {
+ d.dexApiFile = android.PathForModuleOut(ctx, String(d.properties.Dex_api_filename))
+ doclavaFlags += " -dexApi " + d.dexApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.dexApiFile)
+ }
+
+ if String(d.properties.Private_dex_api_filename) != "" {
+ d.privateDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_dex_api_filename))
+ doclavaFlags += " -privateDexApi " + d.privateDexApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.privateDexApiFile)
+ }
+
+ if String(d.properties.Removed_dex_api_filename) != "" {
+ d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
+ doclavaFlags += " -removedDexApi " + d.removedDexApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.removedDexApiFile)
+ }
+
+ if String(d.properties.Exact_api_filename) != "" {
+ d.exactApiFile = android.PathForModuleOut(ctx, String(d.properties.Exact_api_filename))
+ doclavaFlags += " -exactApi " + d.exactApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.exactApiFile)
+ }
+
+ if String(d.properties.Dex_mapping_filename) != "" {
+ d.apiMappingFile = android.PathForModuleOut(ctx, String(d.properties.Dex_mapping_filename))
+ doclavaFlags += " -apiMapping " + d.apiMappingFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.apiMappingFile)
+ }
+
+ if String(d.properties.Proguard_filename) != "" {
+ d.proguardFile = android.PathForModuleOut(ctx, String(d.properties.Proguard_filename))
+ doclavaFlags += " -proguard " + d.proguardFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.proguardFile)
+ }
+
+ if BoolDefault(d.properties.Create_stubs, true) {
+ doclavaFlags += " -stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+ }
+
+ if Bool(d.properties.Write_sdk_values) {
+ doclavaFlags += " -sdkvalues " + android.PathForModuleOut(ctx, "out").String()
+ }
+
+ return doclavaFlags
+}
+
+func (d *Droiddoc) getPostDoclavaCmds(ctx android.ModuleContext, implicits *android.Paths) string {
+ var cmds string
+ if String(d.properties.Static_doc_index_redirect) != "" {
+ static_doc_index_redirect := ctx.ExpandSource(String(d.properties.Static_doc_index_redirect),
+ "static_doc_index_redirect")
+ *implicits = append(*implicits, static_doc_index_redirect)
+ cmds = cmds + " && cp " + static_doc_index_redirect.String() + " " +
+ android.PathForModuleOut(ctx, "out", "index.html").String()
+ }
+
+ if String(d.properties.Static_doc_properties) != "" {
+ static_doc_properties := ctx.ExpandSource(String(d.properties.Static_doc_properties),
+ "static_doc_properties")
+ *implicits = append(*implicits, static_doc_properties)
+ cmds = cmds + " && cp " + static_doc_properties.String() + " " +
+ android.PathForModuleOut(ctx, "out", "source.properties").String()
+ }
+ return cmds
+}
+
+func (d *Droiddoc) transformDoclava(ctx android.ModuleContext, implicits android.Paths,
+ implicitOutputs android.WritablePaths,
+ bootclasspathArgs, classpathArgs, sourcepathArgs, opts, postDoclavaCmds string) {
ctx.Build(pctx, android.BuildParams{
Rule: javadoc,
- Description: "Droiddoc",
- Output: d.Javadoc.stubsJar,
+ Description: "Doclava",
+ Output: d.Javadoc.stubsSrcJar,
Inputs: d.Javadoc.srcFiles,
Implicits: implicits,
ImplicitOutputs: implicitOutputs,
Args: map[string]string{
- "outDir": android.PathForModuleOut(ctx, "docs", "out").String(),
- "srcJarDir": android.PathForModuleOut(ctx, "docs", "srcjars").String(),
- "stubsDir": android.PathForModuleOut(ctx, "docs", "stubsDir").String(),
+ "outDir": android.PathForModuleOut(ctx, "out").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
+ "stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
"srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
"opts": opts,
- "bootclasspathArgs": bootClasspathArgs,
+ "bootclasspathArgs": bootclasspathArgs,
"classpathArgs": classpathArgs,
- "sourcepath": strings.Join(d.Javadoc.sourcepaths.Strings(), ":"),
+ "sourcepathArgs": sourcepathArgs,
"docZip": d.Javadoc.docZip.String(),
- "JsilverJar": "${config.JsilverJar}",
- "DoclavaJar": "${config.DoclavaJar}",
+ "postDoclavaCmds": postDoclavaCmds,
},
})
}
-var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
+func (d *Droiddoc) transformCheckApi(ctx android.ModuleContext, apiFile, removedApiFile android.Path,
+ checkApiClasspath classpath, msg, opts string, output android.WritablePath) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apiCheck,
+ Description: "Doclava Check API",
+ Output: output,
+ Inputs: nil,
+ Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
+ checkApiClasspath...),
+ Args: map[string]string{
+ "msg": msg,
+ "classpath": checkApiClasspath.FormJavaClassPath(""),
+ "opts": opts,
+ "apiFile": apiFile.String(),
+ "apiFileToCheck": d.apiFile.String(),
+ "removedApiFile": removedApiFile.String(),
+ "removedApiFileToCheck": d.removedApiFile.String(),
+ },
+ })
+}
-type DroiddocTemplateProperties struct {
- // path to the directory containing the droiddoc templates.
+func (d *Droiddoc) transformDokka(ctx android.ModuleContext, implicits android.Paths,
+ classpathArgs, opts string) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: dokka,
+ Description: "Dokka",
+ Output: d.Javadoc.stubsSrcJar,
+ Inputs: d.Javadoc.srcFiles,
+ Implicits: implicits,
+ Args: map[string]string{
+ "outDir": android.PathForModuleOut(ctx, "dokka-out").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "dokka-srcjars").String(),
+ "stubsDir": android.PathForModuleOut(ctx, "dokka-stubsDir").String(),
+ "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
+ "classpathArgs": classpathArgs,
+ "opts": opts,
+ "docZip": d.Javadoc.docZip.String(),
+ },
+ })
+}
+
+func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ deps := d.Javadoc.collectDeps(ctx)
+
+ jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar")
+ doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar")
+ java8Home := ctx.Config().Getenv("ANDROID_JAVA8_HOME")
+ checkApiClasspath := classpath{jsilver, doclava, android.PathForSource(ctx, java8Home, "lib/tools.jar")}
+
+ var implicits android.Paths
+ implicits = append(implicits, d.Javadoc.srcJars...)
+ implicits = append(implicits, d.Javadoc.argFiles...)
+
+ var implicitOutputs android.WritablePaths
+ implicitOutputs = append(implicitOutputs, d.Javadoc.docZip)
+ for _, o := range d.Javadoc.properties.Out {
+ implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
+ }
+
+ flags, err := d.initBuilderFlags(ctx, &implicits, deps)
+ if err != nil {
+ return
+ }
+
+ flags.doclavaStubsFlags = d.collectStubsFlags(ctx, &implicitOutputs)
+ if Bool(d.properties.Dokka_enabled) {
+ d.transformDokka(ctx, implicits, flags.classpathArgs, d.Javadoc.args)
+ } else {
+ flags.doclavaDocsFlags = d.collectDoclavaDocsFlags(ctx, &implicits, jsilver, doclava)
+ flags.postDoclavaCmds = d.getPostDoclavaCmds(ctx, &implicits)
+ d.transformDoclava(ctx, implicits, implicitOutputs, flags.bootClasspathArgs, flags.classpathArgs,
+ flags.sourcepathArgs, flags.doclavaDocsFlags+flags.doclavaStubsFlags+" "+d.Javadoc.args,
+ flags.postDoclavaCmds)
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Current, "current") &&
+ !ctx.Config().IsPdkBuild() {
+ apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file),
+ "check_api.current.api_file")
+ removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Removed_api_file),
+ "check_api.current_removed_api_file")
+
+ d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
+ d.transformCheckApi(ctx, apiFile, removedApiFile, checkApiClasspath,
+ fmt.Sprintf(`\n******************************\n`+
+ `You have tried to change the API from what has been previously approved.\n\n`+
+ `To make these errors go away, you have two choices:\n`+
+ ` 1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
+ ` errors above.\n\n`+
+ ` 2. You can update current.txt by executing the following command:\n`+
+ ` make %s-update-current-api\n\n`+
+ ` To submit the revised current.txt to the main Android repository,\n`+
+ ` you will need approval.\n`+
+ `******************************\n`, ctx.ModuleName()), String(d.properties.Check_api.Current.Args),
+ d.checkCurrentApiTimestamp)
+
+ d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
+ transformUpdateApi(ctx, apiFile, removedApiFile, d.apiFile, d.removedApiFile,
+ d.updateCurrentApiTimestamp)
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") &&
+ !ctx.Config().IsPdkBuild() {
+ apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
+ "check_api.last_released.api_file")
+ removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Removed_api_file),
+ "check_api.last_released.removed_api_file")
+
+ d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
+ d.transformCheckApi(ctx, apiFile, removedApiFile, checkApiClasspath,
+ `\n******************************\n`+
+ `You have tried to change the API from what has been previously released in\n`+
+ `an SDK. Please fix the errors listed above.\n`+
+ `******************************\n`, String(d.properties.Check_api.Last_released.Args),
+ d.checkLastReleasedApiTimestamp)
+ }
+}
+
+//
+// Droidstubs
+//
+type Droidstubs struct {
+ Javadoc
+
+ properties DroidstubsProperties
+ apiFile android.WritablePath
+ apiXmlFile android.WritablePath
+ lastReleasedApiXmlFile android.WritablePath
+ dexApiFile android.WritablePath
+ privateApiFile android.WritablePath
+ privateDexApiFile android.WritablePath
+ removedApiFile android.WritablePath
+ removedDexApiFile android.WritablePath
+ apiMappingFile android.WritablePath
+ exactApiFile android.WritablePath
+ proguardFile android.WritablePath
+ nullabilityWarningsFile android.WritablePath
+
+ checkCurrentApiTimestamp android.WritablePath
+ updateCurrentApiTimestamp android.WritablePath
+ checkLastReleasedApiTimestamp android.WritablePath
+
+ checkNullabilityWarningsTimestamp android.WritablePath
+
+ annotationsZip android.WritablePath
+ apiVersionsXml android.WritablePath
+
+ apiFilePath android.Path
+
+ jdiffDocZip android.WritablePath
+ jdiffStubsSrcJar android.WritablePath
+
+ metadataZip android.WritablePath
+ metadataDir android.WritablePath
+}
+
+func DroidstubsFactory() android.Module {
+ module := &Droidstubs{}
+
+ module.AddProperties(&module.properties,
+ &module.Javadoc.properties)
+
+ InitDroiddocModule(module, android.HostAndDeviceSupported)
+ return module
+}
+
+func DroidstubsHostFactory() android.Module {
+ module := &Droidstubs{}
+
+ module.AddProperties(&module.properties,
+ &module.Javadoc.properties)
+
+ InitDroiddocModule(module, android.HostSupported)
+ return module
+}
+
+func (d *Droidstubs) ApiFilePath() android.Path {
+ return d.apiFilePath
+}
+
+func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
+ d.Javadoc.addDeps(ctx)
+
+ if Bool(d.properties.Check_api.Ignore_missing_latest_api) {
+ ignoreMissingModules(ctx, &d.properties.Check_api.Last_released)
+ }
+
+ if len(d.properties.Merge_annotations_dirs) != 0 {
+ for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
+ }
+ }
+
+ if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
+ for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
+ }
+ }
+
+ if len(d.properties.Api_levels_annotations_dirs) != 0 {
+ for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
+ }
+ }
+}
+
+func (d *Droidstubs) initBuilderFlags(ctx android.ModuleContext, implicits *android.Paths,
+ deps deps) (droiddocBuilderFlags, error) {
+ var flags droiddocBuilderFlags
+
+ *implicits = append(*implicits, deps.bootClasspath...)
+ *implicits = append(*implicits, deps.classpath...)
+
+ // continue to use -bootclasspath even if Metalava under -source 1.9 is enabled
+ // since it doesn't support system modules yet.
+ if len(deps.bootClasspath.Strings()) > 0 {
+ // For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
+ flags.bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
+ }
+ flags.classpathArgs = deps.classpath.FormJavaClassPath("-classpath")
+
+ flags.sourcepathArgs = "-sourcepath \"" + strings.Join(d.Javadoc.sourcepaths.Strings(), ":") + "\""
+ return flags, nil
+}
+
+func (d *Droidstubs) collectStubsFlags(ctx android.ModuleContext,
+ implicitOutputs *android.WritablePaths) string {
+ var metalavaFlags string
+ if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Api_filename) != "" {
+ d.apiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.txt")
+ metalavaFlags = metalavaFlags + " --api " + d.apiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.apiFile)
+ d.apiFilePath = d.apiFile
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Removed_api_filename) != "" {
+ d.removedApiFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_removed.txt")
+ metalavaFlags = metalavaFlags + " --removed-api " + d.removedApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.removedApiFile)
+ }
+
+ if String(d.properties.Private_api_filename) != "" {
+ d.privateApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_api_filename))
+ metalavaFlags = metalavaFlags + " --private-api " + d.privateApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.privateApiFile)
+ }
+
+ if String(d.properties.Dex_api_filename) != "" {
+ d.dexApiFile = android.PathForModuleOut(ctx, String(d.properties.Dex_api_filename))
+ metalavaFlags += " --dex-api " + d.dexApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.dexApiFile)
+ }
+
+ if String(d.properties.Private_dex_api_filename) != "" {
+ d.privateDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Private_dex_api_filename))
+ metalavaFlags = metalavaFlags + " --private-dex-api " + d.privateDexApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.privateDexApiFile)
+ }
+
+ if String(d.properties.Removed_dex_api_filename) != "" {
+ d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
+ metalavaFlags = metalavaFlags + " --removed-dex-api " + d.removedDexApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.removedDexApiFile)
+ }
+
+ if String(d.properties.Exact_api_filename) != "" {
+ d.exactApiFile = android.PathForModuleOut(ctx, String(d.properties.Exact_api_filename))
+ metalavaFlags = metalavaFlags + " --exact-api " + d.exactApiFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.exactApiFile)
+ }
+
+ if String(d.properties.Dex_mapping_filename) != "" {
+ d.apiMappingFile = android.PathForModuleOut(ctx, String(d.properties.Dex_mapping_filename))
+ metalavaFlags = metalavaFlags + " --dex-api-mapping " + d.apiMappingFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.apiMappingFile)
+ }
+
+ if String(d.properties.Proguard_filename) != "" {
+ d.proguardFile = android.PathForModuleOut(ctx, String(d.properties.Proguard_filename))
+ metalavaFlags += " --proguard " + d.proguardFile.String()
+ *implicitOutputs = append(*implicitOutputs, d.proguardFile)
+ }
+
+ if Bool(d.properties.Write_sdk_values) {
+ d.metadataDir = android.PathForModuleOut(ctx, "metadata")
+ metalavaFlags = metalavaFlags + " --sdk-values " + d.metadataDir.String()
+ }
+
+ if Bool(d.properties.Create_doc_stubs) {
+ metalavaFlags += " --doc-stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+ } else {
+ metalavaFlags += " --stubs " + android.PathForModuleOut(ctx, "stubsDir").String()
+ }
+ return metalavaFlags
+}
+
+func (d *Droidstubs) collectAnnotationsFlags(ctx android.ModuleContext,
+ implicits *android.Paths, implicitOutputs *android.WritablePaths) (string, string) {
+ var flags, mergeAnnoDirFlags string
+ if Bool(d.properties.Annotations_enabled) {
+ flags += " --include-annotations"
+ validatingNullability :=
+ strings.Contains(d.Javadoc.args, "--validate-nullability-from-merged-stubs") ||
+ String(d.properties.Validate_nullability_from_list) != ""
+ migratingNullability := String(d.properties.Previous_api) != ""
+ if !(migratingNullability || validatingNullability) {
+ ctx.PropertyErrorf("previous_api",
+ "has to be non-empty if annotations was enabled (unless validating nullability)")
+ }
+ if migratingNullability {
+ previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
+ *implicits = append(*implicits, previousApi)
+ flags += " --migrate-nullness " + previousApi.String()
+ }
+ if s := String(d.properties.Validate_nullability_from_list); s != "" {
+ flags += " --validate-nullability-from-list " + android.PathForModuleSrc(ctx, s).String()
+ }
+ if validatingNullability {
+ d.nullabilityWarningsFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_nullability_warnings.txt")
+ *implicitOutputs = append(*implicitOutputs, d.nullabilityWarningsFile)
+ flags += " --nullability-warnings-txt " + d.nullabilityWarningsFile.String()
+ }
+
+ d.annotationsZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
+ *implicitOutputs = append(*implicitOutputs, d.annotationsZip)
+
+ flags += " --extract-annotations " + d.annotationsZip.String()
+
+ if len(d.properties.Merge_annotations_dirs) == 0 {
+ ctx.PropertyErrorf("merge_annotations_dirs",
+ "has to be non-empty if annotations was enabled!")
+ }
+ ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ *implicits = append(*implicits, t.deps...)
+ mergeAnnoDirFlags += " --merge-qualifier-annotations " + t.dir.String()
+ } else {
+ ctx.PropertyErrorf("merge_annotations_dirs",
+ "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+ flags += mergeAnnoDirFlags
+ // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
+ flags += " --hide HiddenTypedefConstant --hide SuperfluousPrefix --hide AnnotationExtraction"
+ }
+
+ return flags, mergeAnnoDirFlags
+}
+
+func (d *Droidstubs) collectInclusionAnnotationsFlags(ctx android.ModuleContext,
+ implicits *android.Paths, implicitOutputs *android.WritablePaths) string {
+ var flags string
+ ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ *implicits = append(*implicits, t.deps...)
+ flags += " --merge-inclusion-annotations " + t.dir.String()
+ } else {
+ ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
+ "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+
+ return flags
+}
+
+func (d *Droidstubs) collectAPILevelsAnnotationsFlags(ctx android.ModuleContext,
+ implicits *android.Paths, implicitOutputs *android.WritablePaths) string {
+ var flags string
+ if Bool(d.properties.Api_levels_annotations_enabled) {
+ d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
+ *implicitOutputs = append(*implicitOutputs, d.apiVersionsXml)
+
+ if len(d.properties.Api_levels_annotations_dirs) == 0 {
+ ctx.PropertyErrorf("api_levels_annotations_dirs",
+ "has to be non-empty if api levels annotations was enabled!")
+ }
+
+ flags = " --generate-api-levels " + d.apiVersionsXml.String() + " --apply-api-levels " +
+ d.apiVersionsXml.String() + " --current-version " + ctx.Config().PlatformSdkVersion() +
+ " --current-codename " + ctx.Config().PlatformSdkCodename() + " "
+
+ ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ var androidJars android.Paths
+ for _, dep := range t.deps {
+ if strings.HasSuffix(dep.String(), "android.jar") {
+ androidJars = append(androidJars, dep)
+ }
+ }
+ *implicits = append(*implicits, androidJars...)
+ flags += " --android-jar-pattern " + t.dir.String() + "/%/public/android.jar "
+ } else {
+ ctx.PropertyErrorf("api_levels_annotations_dirs",
+ "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+
+ }
+
+ return flags
+}
+
+func (d *Droidstubs) collectApiToXmlFlags(ctx android.ModuleContext, implicits *android.Paths,
+ implicitOutputs *android.WritablePaths) string {
+ var flags string
+ if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
+ if d.apiFile.String() == "" {
+ ctx.ModuleErrorf("API signature file has to be specified in Metalava when jdiff is enabled.")
+ }
+
+ d.apiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_api.xml")
+ *implicitOutputs = append(*implicitOutputs, d.apiXmlFile)
+
+ flags = " --api-xml " + d.apiXmlFile.String()
+
+ if String(d.properties.Check_api.Last_released.Api_file) == "" {
+ ctx.PropertyErrorf("check_api.last_released.api_file",
+ "has to be non-empty if jdiff was enabled!")
+ }
+ lastReleasedApi := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
+ "check_api.last_released.api_file")
+ *implicits = append(*implicits, lastReleasedApi)
+
+ d.lastReleasedApiXmlFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_last_released_api.xml")
+ *implicitOutputs = append(*implicitOutputs, d.lastReleasedApiXmlFile)
+
+ flags += " --convert-to-jdiff " + lastReleasedApi.String() + " " +
+ d.lastReleasedApiXmlFile.String()
+ }
+
+ return flags
+}
+
+func (d *Droidstubs) transformMetalava(ctx android.ModuleContext, implicits android.Paths,
+ implicitOutputs android.WritablePaths, javaVersion,
+ bootclasspathArgs, classpathArgs, sourcepathArgs, opts string) {
+
+ var writeSdkValues, metadataZip, metadataDir string
+ if Bool(d.properties.Write_sdk_values) {
+ writeSdkValues = "true"
+ d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
+ metadataZip = d.metadataZip.String()
+ metadataDir = d.metadataDir.String()
+ implicitOutputs = append(implicitOutputs, d.metadataZip)
+ } else {
+ writeSdkValues = "false"
+ metadataZip = ""
+ metadataDir = ""
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: metalava,
+ Description: "Metalava",
+ Output: d.Javadoc.stubsSrcJar,
+ Inputs: d.Javadoc.srcFiles,
+ Implicits: implicits,
+ ImplicitOutputs: implicitOutputs,
+ Args: map[string]string{
+ "outDir": android.PathForModuleOut(ctx, "out").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
+ "stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
+ "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
+ "javaVersion": javaVersion,
+ "bootclasspathArgs": bootclasspathArgs,
+ "classpathArgs": classpathArgs,
+ "sourcepathArgs": sourcepathArgs,
+ "opts": opts,
+ "writeSdkValues": writeSdkValues,
+ "metadataZip": metadataZip,
+ "metadataDir": metadataDir,
+ },
+ })
+}
+
+func (d *Droidstubs) transformCheckApi(ctx android.ModuleContext,
+ apiFile, removedApiFile android.Path, baselineFile android.OptionalPath, updatedBaselineOut android.WritablePath, implicits android.Paths,
+ javaVersion, bootclasspathArgs, classpathArgs, sourcepathArgs, opts, subdir, msg string,
+ output android.WritablePath) {
+
+ implicits = append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile}, implicits...)
+ var implicitOutputs android.WritablePaths
+
+ if baselineFile.Valid() {
+ implicits = append(implicits, baselineFile.Path())
+ implicitOutputs = append(implicitOutputs, updatedBaselineOut)
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: metalavaApiCheck,
+ Description: "Metalava Check API",
+ Output: output,
+ Inputs: d.Javadoc.srcFiles,
+ Implicits: implicits,
+ ImplicitOutputs: implicitOutputs,
+ Args: map[string]string{
+ "srcJarDir": android.PathForModuleOut(ctx, subdir, "srcjars").String(),
+ "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
+ "javaVersion": javaVersion,
+ "bootclasspathArgs": bootclasspathArgs,
+ "classpathArgs": classpathArgs,
+ "sourcepathArgs": sourcepathArgs,
+ "opts": opts,
+ "msg": msg,
+ },
+ })
+}
+
+func (d *Droidstubs) transformJdiff(ctx android.ModuleContext, implicits android.Paths,
+ implicitOutputs android.WritablePaths,
+ bootclasspathArgs, classpathArgs, sourcepathArgs, opts string) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: javadoc,
+ Description: "Jdiff",
+ Output: d.jdiffStubsSrcJar,
+ Inputs: d.Javadoc.srcFiles,
+ Implicits: implicits,
+ ImplicitOutputs: implicitOutputs,
+ Args: map[string]string{
+ "outDir": android.PathForModuleOut(ctx, "jdiff-out").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "jdiff-srcjars").String(),
+ "stubsDir": android.PathForModuleOut(ctx, "jdiff-stubsDir").String(),
+ "srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
+ "opts": opts,
+ "bootclasspathArgs": bootclasspathArgs,
+ "classpathArgs": classpathArgs,
+ "sourcepathArgs": sourcepathArgs,
+ "docZip": d.jdiffDocZip.String(),
+ },
+ })
+}
+
+func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ deps := d.Javadoc.collectDeps(ctx)
+
+ javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
+
+ var implicits android.Paths
+ implicits = append(implicits, d.Javadoc.srcJars...)
+ implicits = append(implicits, d.Javadoc.argFiles...)
+
+ var implicitOutputs android.WritablePaths
+ for _, o := range d.Javadoc.properties.Out {
+ implicitOutputs = append(implicitOutputs, android.PathForModuleGen(ctx, o))
+ }
+
+ flags, err := d.initBuilderFlags(ctx, &implicits, deps)
+ metalavaCheckApiImplicits := implicits
+ jdiffImplicits := implicits
+
+ if err != nil {
+ return
+ }
+
+ flags.metalavaStubsFlags = d.collectStubsFlags(ctx, &implicitOutputs)
+ flags.metalavaAnnotationsFlags, flags.metalavaMergeAnnoDirFlags =
+ d.collectAnnotationsFlags(ctx, &implicits, &implicitOutputs)
+ flags.metalavaInclusionAnnotationsFlags = d.collectInclusionAnnotationsFlags(ctx, &implicits, &implicitOutputs)
+ flags.metalavaApiLevelsAnnotationsFlags = d.collectAPILevelsAnnotationsFlags(ctx, &implicits, &implicitOutputs)
+ flags.metalavaApiToXmlFlags = d.collectApiToXmlFlags(ctx, &implicits, &implicitOutputs)
+
+ if strings.Contains(d.Javadoc.args, "--generate-documentation") {
+ // Currently Metalava have the ability to invoke Javadoc in a seperate process.
+ // Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
+ // "--generate-documentation" arg. This is not needed when Metalava removes this feature.
+ d.Javadoc.args = d.Javadoc.args + " -nodocs "
+ }
+ d.transformMetalava(ctx, implicits, implicitOutputs, javaVersion,
+ flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs,
+ flags.metalavaStubsFlags+flags.metalavaAnnotationsFlags+flags.metalavaInclusionAnnotationsFlags+
+ flags.metalavaApiLevelsAnnotationsFlags+flags.metalavaApiToXmlFlags+" "+d.Javadoc.args)
+
+ if apiCheckEnabled(d.properties.Check_api.Current, "current") &&
+ !ctx.Config().IsPdkBuild() {
+ apiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Api_file),
+ "check_api.current.api_file")
+ removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Current.Removed_api_file),
+ "check_api.current_removed_api_file")
+ baselineFile := ctx.ExpandOptionalSource(d.properties.Check_api.Current.Baseline_file,
+ "check_api.current.baseline_file")
+
+ d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
+ opts := " " + d.Javadoc.args + " --check-compatibility:api:current " + apiFile.String() +
+ " --check-compatibility:removed:current " + removedApiFile.String() +
+ flags.metalavaInclusionAnnotationsFlags + flags.metalavaMergeAnnoDirFlags + " "
+ baselineOut := android.PathForModuleOut(ctx, "current_baseline.txt")
+ if baselineFile.Valid() {
+ opts = opts + "--baseline " + baselineFile.String() + " --update-baseline " + baselineOut.String() + " "
+ }
+
+ d.transformCheckApi(ctx, apiFile, removedApiFile, baselineFile, baselineOut, metalavaCheckApiImplicits,
+ javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts, "current-apicheck",
+ fmt.Sprintf(`\n******************************\n`+
+ `You have tried to change the API from what has been previously approved.\n\n`+
+ `To make these errors go away, you have two choices:\n`+
+ ` 1. You can add '@hide' javadoc comments to the methods, etc. listed in the\n`+
+ ` errors above.\n\n`+
+ ` 2. You can update current.txt by executing the following command:\n`+
+ ` make %s-update-current-api\n\n`+
+ ` To submit the revised current.txt to the main Android repository,\n`+
+ ` you will need approval.\n`+
+ `******************************\n`, ctx.ModuleName()),
+ d.checkCurrentApiTimestamp)
+
+ d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
+ transformUpdateApi(ctx, apiFile, removedApiFile, d.apiFile, d.removedApiFile,
+ d.updateCurrentApiTimestamp)
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") &&
+ !ctx.Config().IsPdkBuild() {
+ apiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Api_file),
+ "check_api.last_released.api_file")
+ removedApiFile := ctx.ExpandSource(String(d.properties.Check_api.Last_released.Removed_api_file),
+ "check_api.last_released.removed_api_file")
+ baselineFile := ctx.ExpandOptionalSource(d.properties.Check_api.Last_released.Baseline_file,
+ "check_api.last_released.baseline_file")
+
+ d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
+ opts := " " + d.Javadoc.args + " --check-compatibility:api:released " + apiFile.String() +
+ flags.metalavaInclusionAnnotationsFlags + " --check-compatibility:removed:released " +
+ removedApiFile.String() + flags.metalavaMergeAnnoDirFlags + " "
+ baselineOut := android.PathForModuleOut(ctx, "last_released_baseline.txt")
+ if baselineFile.Valid() {
+ opts = opts + "--baseline " + baselineFile.String() + " --update-baseline " + baselineOut.String() + " "
+ }
+
+ d.transformCheckApi(ctx, apiFile, removedApiFile, baselineFile, baselineOut, metalavaCheckApiImplicits,
+ javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts, "last-apicheck",
+ `\n******************************\n`+
+ `You have tried to change the API from what has been previously released in\n`+
+ `an SDK. Please fix the errors listed above.\n`+
+ `******************************\n`,
+ d.checkLastReleasedApiTimestamp)
+ }
+
+ if String(d.properties.Check_nullability_warnings) != "" {
+ if d.nullabilityWarningsFile == nil {
+ ctx.PropertyErrorf("check_nullability_warnings",
+ "Cannot specify check_nullability_warnings unless validating nullability")
+ }
+ checkNullabilityWarnings := ctx.ExpandSource(String(d.properties.Check_nullability_warnings),
+ "check_nullability_warnings")
+ d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "check_nullability_warnings.timestamp")
+ msg := fmt.Sprintf(`\n******************************\n`+
+ `The warnings encountered during nullability annotation validation did\n`+
+ `not match the checked in file of expected warnings. The diffs are shown\n`+
+ `above. You have two options:\n`+
+ ` 1. Resolve the differences by editing the nullability annotations.\n`+
+ ` 2. Update the file of expected warnings by running:\n`+
+ ` cp %s %s\n`+
+ ` and submitting the updated file as part of your change.`,
+ d.nullabilityWarningsFile, checkNullabilityWarnings)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: nullabilityWarningsCheck,
+ Description: "Nullability Warnings Check",
+ Output: d.checkNullabilityWarningsTimestamp,
+ Implicits: android.Paths{checkNullabilityWarnings, d.nullabilityWarningsFile},
+ Args: map[string]string{
+ "expected": checkNullabilityWarnings.String(),
+ "actual": d.nullabilityWarningsFile.String(),
+ "msg": msg,
+ },
+ })
+ }
+
+ if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
+
+ // Please sync with android-api-council@ before making any changes for the name of jdiffDocZip below
+ // since there's cron job downstream that fetch this .zip file periodically.
+ // See b/116221385 for reference.
+ d.jdiffDocZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-docs.zip")
+ d.jdiffStubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"jdiff-stubs.srcjar")
+
+ var jdiffImplicitOutputs android.WritablePaths
+ jdiffImplicitOutputs = append(jdiffImplicitOutputs, d.jdiffDocZip)
+
+ jdiff := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jdiff.jar")
+ jdiffImplicits = append(jdiffImplicits, android.Paths{jdiff, d.apiXmlFile, d.lastReleasedApiXmlFile}...)
+
+ opts := " -encoding UTF-8 -source 1.8 -J-Xmx1600m -XDignore.symbol.file " +
+ "-doclet jdiff.JDiff -docletpath " + jdiff.String() + " -quiet " +
+ "-newapi " + strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext()) +
+ " -newapidir " + filepath.Dir(d.apiXmlFile.String()) +
+ " -oldapi " + strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext()) +
+ " -oldapidir " + filepath.Dir(d.lastReleasedApiXmlFile.String())
+
+ d.transformJdiff(ctx, jdiffImplicits, jdiffImplicitOutputs, flags.bootClasspathArgs, flags.classpathArgs,
+ flags.sourcepathArgs, opts)
+ }
+}
+
+//
+// Exported Droiddoc Directory
+//
+var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
+var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
+var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
+var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
+
+type ExportedDroiddocDirProperties struct {
+ // path to the directory containing Droiddoc related files.
Path *string
}
-type DroiddocTemplate struct {
+type ExportedDroiddocDir struct {
android.ModuleBase
- properties DroiddocTemplateProperties
+ properties ExportedDroiddocDirProperties
deps android.Paths
dir android.Path
}
-func DroiddocTemplateFactory() android.Module {
- module := &DroiddocTemplate{}
+func ExportedDroiddocDirFactory() android.Module {
+ module := &ExportedDroiddocDir{}
module.AddProperties(&module.properties)
android.InitAndroidModule(module)
return module
}
-func (d *DroiddocTemplate) DepsMutator(android.BottomUpMutatorContext) {}
+func (d *ExportedDroiddocDir) DepsMutator(android.BottomUpMutatorContext) {}
-func (d *DroiddocTemplate) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- path := android.PathForModuleSrc(ctx, String(d.properties.Path))
- d.dir = path
- d.deps = ctx.Glob(path.Join(ctx, "**/*").String(), nil)
+func (d *ExportedDroiddocDir) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ path := String(d.properties.Path)
+ d.dir = android.PathForModuleSrc(ctx, path)
+ d.deps = android.PathsForModuleSrc(ctx, []string{filepath.Join(path, "**/*")})
+}
+
+//
+// Defaults
+//
+type DocDefaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func (*DocDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+func DocDefaultsFactory() android.Module {
+ module := &DocDefaults{}
+
+ module.AddProperties(
+ &JavadocProperties{},
+ &DroiddocProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+
+ return module
+}
+
+func StubsDefaultsFactory() android.Module {
+ module := &DocDefaults{}
+
+ module.AddProperties(
+ &JavadocProperties{},
+ &DroidstubsProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+
+ return module
}
diff --git a/java/gen.go b/java/gen.go
index a993829..0977628 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -14,10 +14,6 @@
package java
-// This file generates the final rules for compiling all C/C++. All properties related to
-// compiling should have been translated into builderFlags or another argument to the Transform*
-// functions.
-
import (
"github.com/google/blueprint"
@@ -26,6 +22,7 @@
func init() {
pctx.HostBinToolVariable("aidlCmd", "aidl")
+ pctx.HostBinToolVariable("syspropCmd", "sysprop_java")
pctx.SourcePathVariable("logtagsCmd", "build/tools/java-event-log-tags.py")
pctx.SourcePathVariable("mergeLogtagsCmd", "build/tools/merge-event-log-tags.py")
}
@@ -49,9 +46,20 @@
Command: "$mergeLogtagsCmd -o $out $in",
CommandDeps: []string{"$mergeLogtagsCmd"},
})
+
+ sysprop = pctx.AndroidStaticRule("sysprop",
+ blueprint.RuleParams{
+ Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
+ `$syspropCmd --java-output-dir $out.tmp $in && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
+ CommandDeps: []string{
+ "$syspropCmd",
+ "${config.SoongZipCmd}",
+ },
+ })
)
-func genAidl(ctx android.ModuleContext, aidlFile android.Path, aidlFlags string) android.Path {
+func genAidl(ctx android.ModuleContext, aidlFile android.Path, aidlFlags string, deps android.Paths) android.Path {
javaFile := android.GenPathWithExt(ctx, "aidl", aidlFile, "java")
depFile := javaFile.String() + ".d"
@@ -60,6 +68,7 @@
Description: "aidl " + aidlFile.Rel(),
Output: javaFile,
Input: aidlFile,
+ Implicits: deps,
Args: map[string]string{
"depFile": depFile,
"aidlFlags": aidlFlags,
@@ -82,6 +91,19 @@
return javaFile
}
+func genSysprop(ctx android.ModuleContext, syspropFile android.Path) android.Path {
+ srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: sysprop,
+ Description: "sysprop_java " + syspropFile.Rel(),
+ Output: srcJarFile,
+ Input: syspropFile,
+ })
+
+ return srcJarFile
+}
+
func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths,
flags javaBuilderFlags) android.Paths {
@@ -90,14 +112,17 @@
for _, srcFile := range srcFiles {
switch srcFile.Ext() {
case ".aidl":
- javaFile := genAidl(ctx, srcFile, flags.aidlFlags)
+ javaFile := genAidl(ctx, srcFile, flags.aidlFlags, flags.aidlDeps)
outSrcFiles = append(outSrcFiles, javaFile)
case ".logtags":
j.logtagsSrcs = append(j.logtagsSrcs, srcFile)
javaFile := genLogtags(ctx, srcFile)
outSrcFiles = append(outSrcFiles, javaFile)
case ".proto":
- srcJarFile := genProto(ctx, srcFile, flags)
+ srcJarFile := genProto(ctx, srcFile, flags.proto)
+ outSrcFiles = append(outSrcFiles, srcJarFile)
+ case ".sysprop":
+ srcJarFile := genSysprop(ctx, srcFile)
outSrcFiles = append(outSrcFiles, srcJarFile)
default:
outSrcFiles = append(outSrcFiles, srcFile)
diff --git a/java/genrule.go b/java/genrule.go
index 80b7030..25494ec 100644
--- a/java/genrule.go
+++ b/java/genrule.go
@@ -21,11 +21,41 @@
func init() {
android.RegisterModuleType("java_genrule", genRuleFactory)
+ android.RegisterModuleType("java_genrule_host", genRuleFactoryHost)
}
// java_genrule is a genrule that can depend on other java_* objects.
-// The cmd may be run multiple times, once for each of the different host/device
-// variations.
+//
+// By default a java_genrule has a single variant that will run against the device variant of its dependencies and
+// produce an output that can be used as an input to a device java rule.
+//
+// Specifying `host_supported: true` will produce two variants, one that uses device dependencie sand one that uses
+// host dependencies. Each variant will run the command.
+//
+// Use a java_genrule instead of a genrule when it needs to depend on or be depended on by other java modules, unless
+// the dependency is for a generated source file.
+//
+// Examples:
+//
+// Use a java_genrule to package generated java resources:
+//
+// java_genrule {
+// name: "generated_resources",
+// tools: [
+// "generator",
+// "soong_zip",
+// ],
+// srcs: ["generator_inputs/**/*"],
+// out: ["generated_android_icu4j_resources.jar"],
+// cmd: "$(location generator) $(in) -o $(genDir) " +
+// "&& $(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
+// }
+//
+// java_library {
+// name: "lib_with_generated_resources",
+// srcs: ["src/**/*.java"],
+// static_libs: ["generated_resources"],
+// }
func genRuleFactory() android.Module {
module := genrule.NewGenRule()
@@ -33,3 +63,15 @@
return module
}
+
+// java_genrule_host is a genrule that can depend on other java_* objects.
+//
+// A java_genrule_host has a single variant that will run against the host variant of its dependencies and
+// produce an output that can be used as an input to a host java rule.
+func genRuleFactoryHost() android.Module {
+ module := genrule.NewGenRule()
+
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+
+ return module
+}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
new file mode 100644
index 0000000..6020aba
--- /dev/null
+++ b/java/hiddenapi.go
@@ -0,0 +1,185 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "strings"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+)
+
+var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", blueprint.RuleParams{
+ Command: "${config.Class2Greylist} --stub-api-flags ${stubAPIFlags} $in $outFlag $out",
+ CommandDeps: []string{"${config.Class2Greylist}"},
+}, "outFlag", "stubAPIFlags")
+
+type hiddenAPI struct {
+ flagsCSVPath android.Path
+ metadataCSVPath android.Path
+ bootDexJarPath android.Path
+}
+
+func (h *hiddenAPI) flagsCSV() android.Path {
+ return h.flagsCSVPath
+}
+
+func (h *hiddenAPI) metadataCSV() android.Path {
+ return h.metadataCSVPath
+}
+
+func (h *hiddenAPI) bootDexJar() android.Path {
+ return h.bootDexJarPath
+}
+
+type hiddenAPIIntf interface {
+ flagsCSV() android.Path
+ metadataCSV() android.Path
+ bootDexJar() android.Path
+}
+
+var _ hiddenAPIIntf = (*hiddenAPI)(nil)
+
+func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, dexJar android.ModuleOutPath, implementationJar android.Path,
+ uncompressDex bool) android.ModuleOutPath {
+
+ if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+ name := ctx.ModuleName()
+
+ // Modules whose names are of the format <x>-hiddenapi provide hiddenapi information
+ // for the boot jar module <x>. Otherwise, the module provides information for itself.
+ // Either way extract the name of the boot jar module.
+ bootJarName := strings.TrimSuffix(name, "-hiddenapi")
+
+ // If this module is on the boot jars list (or providing information for a module
+ // on the list) then extract the hiddenapi information from it, and if necessary
+ // encode that information in the generated dex file.
+ //
+ // It is important that hiddenapi information is only gathered for/from modules on
+ // that are actually on the boot jars list because the runtime only enforces access
+ // to the hidden API for the bootclassloader. If information is gathered for modules
+ // not on the list then that will cause failures in the CtsHiddenApiBlacklist...
+ // tests.
+ if inList(bootJarName, ctx.Config().BootJars()) {
+ // Derive the greylist from classes jar.
+ flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
+ metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv")
+ hiddenAPIGenerateCSV(ctx, flagsCSV, metadataCSV, implementationJar)
+ h.flagsCSVPath = flagsCSV
+ h.metadataCSVPath = metadataCSV
+
+ // If this module is actually on the boot jars list and not providing
+ // hiddenapi information for a module on the boot jars list then encode
+ // the gathered information in the generated dex file.
+ if name == bootJarName {
+ hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar")
+ h.bootDexJarPath = dexJar
+ hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex)
+ dexJar = hiddenAPIJar
+ }
+ }
+ }
+
+ return dexJar
+}
+
+func hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, metadataCSV android.WritablePath,
+ classesJar android.Path) {
+
+ stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: hiddenAPIGenerateCSVRule,
+ Description: "hiddenapi flags",
+ Input: classesJar,
+ Output: flagsCSV,
+ Implicit: stubFlagsCSV,
+ Args: map[string]string{
+ "outFlag": "--write-flags-csv",
+ "stubAPIFlags": stubFlagsCSV.String(),
+ },
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: hiddenAPIGenerateCSVRule,
+ Description: "hiddenapi metadata",
+ Input: classesJar,
+ Output: metadataCSV,
+ Implicit: stubFlagsCSV,
+ Args: map[string]string{
+ "outFlag": "--write-metadata-csv",
+ "stubAPIFlags": stubFlagsCSV.String(),
+ },
+ })
+
+}
+
+var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
+ Command: `rm -rf $tmpDir && mkdir -p $tmpDir && mkdir $tmpDir/dex-input && mkdir $tmpDir/dex-output && ` +
+ `unzip -o -q $in 'classes*.dex' -d $tmpDir/dex-input && ` +
+ `for INPUT_DEX in $$(find $tmpDir/dex-input -maxdepth 1 -name 'classes*.dex' | sort); do ` +
+ ` echo "--input-dex=$${INPUT_DEX}"; ` +
+ ` echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})"; ` +
+ `done | xargs ${config.HiddenAPI} encode --api-flags=$flagsCsv $hiddenapiFlags && ` +
+ `${config.SoongZipCmd} $soongZipFlags -o $tmpDir/dex.jar -C $tmpDir/dex-output -f "$tmpDir/dex-output/classes*.dex" && ` +
+ `${config.MergeZipsCmd} -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" $out $tmpDir/dex.jar $in`,
+ CommandDeps: []string{
+ "${config.HiddenAPI}",
+ "${config.SoongZipCmd}",
+ "${config.MergeZipsCmd}",
+ },
+}, "flagsCsv", "hiddenapiFlags", "tmpDir", "soongZipFlags")
+
+func hiddenAPIEncodeDex(ctx android.ModuleContext, output android.WritablePath, dexInput android.Path,
+ uncompressDex bool) {
+
+ flagsCSV := hiddenAPISingletonPaths(ctx).flags
+
+ // The encode dex rule requires unzipping and rezipping the classes.dex files, ensure that if it was uncompressed
+ // in the input it stays uncompressed in the output.
+ soongZipFlags := ""
+ hiddenapiFlags := ""
+ tmpOutput := output
+ tmpDir := android.PathForModuleOut(ctx, "hiddenapi", "dex")
+ if uncompressDex {
+ soongZipFlags = "-L 0"
+ tmpOutput = android.PathForModuleOut(ctx, "hiddenapi", "unaligned", "unaligned.jar")
+ tmpDir = android.PathForModuleOut(ctx, "hiddenapi", "unaligned")
+ }
+ // If frameworks/base doesn't exist we must be building with the 'master-art' manifest.
+ // Disable assertion that all methods/fields have hidden API flags assigned.
+ if !ctx.Config().FrameworksBaseDirExists(ctx) {
+ hiddenapiFlags = "--no-force-assign-all"
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: hiddenAPIEncodeDexRule,
+ Description: "hiddenapi encode dex",
+ Input: dexInput,
+ Output: tmpOutput,
+ Implicit: flagsCSV,
+ Args: map[string]string{
+ "flagsCsv": flagsCSV.String(),
+ "tmpDir": tmpDir.String(),
+ "soongZipFlags": soongZipFlags,
+ "hiddenapiFlags": hiddenapiFlags,
+ },
+ })
+
+ if uncompressDex {
+ TransformZipAlign(ctx, output, tmpOutput)
+ }
+}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
new file mode 100644
index 0000000..09936ea
--- /dev/null
+++ b/java/hiddenapi_singleton.go
@@ -0,0 +1,309 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+}
+
+type hiddenAPISingletonPathsStruct struct {
+ stubFlags android.OutputPath
+ flags android.OutputPath
+ metadata android.OutputPath
+}
+
+var hiddenAPISingletonPathsKey = android.NewOnceKey("hiddenAPISingletonPathsKey")
+
+// hiddenAPISingletonPaths creates all the paths for singleton files the first time it is called, which may be
+// from a ModuleContext that needs to reference a file that will be created by a singleton rule that hasn't
+// yet been created.
+func hiddenAPISingletonPaths(ctx android.PathContext) hiddenAPISingletonPathsStruct {
+ return ctx.Config().Once(hiddenAPISingletonPathsKey, func() interface{} {
+ return hiddenAPISingletonPathsStruct{
+ stubFlags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-stub-flags.txt"),
+ flags: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-flags.csv"),
+ metadata: android.PathForOutput(ctx, "hiddenapi", "hiddenapi-greylist.csv"),
+ }
+ }).(hiddenAPISingletonPathsStruct)
+}
+
+func hiddenAPISingletonFactory() android.Singleton {
+ return &hiddenAPISingleton{}
+}
+
+type hiddenAPISingleton struct {
+ flags, metadata android.Path
+}
+
+// hiddenAPI singleton rules
+func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ // Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true
+ if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+ return
+ }
+
+ stubFlagsRule(ctx)
+
+ // These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
+ if ctx.Config().FrameworksBaseDirExists(ctx) {
+ h.flags = flagsRule(ctx)
+ h.metadata = metadataRule(ctx)
+ } else {
+ h.flags = emptyFlagsRule(ctx)
+ }
+}
+
+// Export paths to Make. INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
+// Both paths are used to call dist-for-goals.
+func (h *hiddenAPISingleton) MakeVars(ctx android.MakeVarsContext) {
+ if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+ return
+ }
+
+ ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
+
+ if h.metadata != nil {
+ ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA", h.metadata.String())
+ }
+}
+
+// stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
+// modules.
+func stubFlagsRule(ctx android.SingletonContext) {
+ // Public API stubs
+ publicStubModules := []string{
+ "android_stubs_current",
+ }
+
+ // Add the android.test.base to the set of stubs only if the android.test.base module is on
+ // the boot jars list as the runtime will only enforce hiddenapi access against modules on
+ // that list.
+ if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ publicStubModules = append(publicStubModules, "android.test.base.stubs")
+ }
+
+ // System API stubs
+ systemStubModules := []string{
+ "android_system_stubs_current",
+ }
+
+ // Test API stubs
+ testStubModules := []string{
+ "android_test_stubs_current",
+ }
+
+ // Core Platform API stubs
+ corePlatformStubModules := []string{
+ "core.platform.api.stubs",
+ }
+
+ // Allow products to define their own stubs for custom product jars that apps can use.
+ publicStubModules = append(publicStubModules, ctx.Config().ProductHiddenAPIStubs()...)
+ systemStubModules = append(systemStubModules, ctx.Config().ProductHiddenAPIStubsSystem()...)
+ testStubModules = append(testStubModules, ctx.Config().ProductHiddenAPIStubsTest()...)
+ if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") {
+ publicStubModules = append(publicStubModules, "jacoco-stubs")
+ }
+
+ publicStubPaths := make(android.Paths, len(publicStubModules))
+ systemStubPaths := make(android.Paths, len(systemStubModules))
+ testStubPaths := make(android.Paths, len(testStubModules))
+ corePlatformStubPaths := make(android.Paths, len(corePlatformStubModules))
+
+ moduleListToPathList := map[*[]string]android.Paths{
+ &publicStubModules: publicStubPaths,
+ &systemStubModules: systemStubPaths,
+ &testStubModules: testStubPaths,
+ &corePlatformStubModules: corePlatformStubPaths,
+ }
+
+ var bootDexJars android.Paths
+
+ ctx.VisitAllModules(func(module android.Module) {
+ // Collect dex jar paths for the modules listed above.
+ if j, ok := module.(Dependency); ok {
+ name := ctx.ModuleName(module)
+ for moduleList, pathList := range moduleListToPathList {
+ if i := android.IndexList(name, *moduleList); i != -1 {
+ pathList[i] = j.DexJar()
+ }
+ }
+ }
+
+ // Collect dex jar paths for modules that had hiddenapi encode called on them.
+ if h, ok := module.(hiddenAPIIntf); ok {
+ if jar := h.bootDexJar(); jar != nil {
+ bootDexJars = append(bootDexJars, jar)
+ }
+ }
+ })
+
+ var missingDeps []string
+ // Ensure all modules were converted to paths
+ for moduleList, pathList := range moduleListToPathList {
+ for i := range pathList {
+ if pathList[i] == nil {
+ pathList[i] = android.PathForOutput(ctx, "missing")
+ if ctx.Config().AllowMissingDependencies() {
+ missingDeps = append(missingDeps, (*moduleList)[i])
+ } else {
+ ctx.Errorf("failed to find dex jar path for module %q",
+ (*moduleList)[i])
+ }
+ }
+ }
+ }
+
+ // Singleton rule which applies hiddenapi on all boot class path dex files.
+ rule := android.NewRuleBuilder()
+
+ outputPath := hiddenAPISingletonPaths(ctx).stubFlags
+ tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
+
+ rule.MissingDeps(missingDeps)
+
+ rule.Command().
+ Tool(pctx.HostBinToolPath(ctx, "hiddenapi")).
+ Text("list").
+ FlagForEachInput("--boot-dex=", bootDexJars).
+ FlagWithInputList("--public-stub-classpath=", publicStubPaths, ":").
+ FlagWithInputList("--system-stub-classpath=", systemStubPaths, ":").
+ FlagWithInputList("--test-stub-classpath=", testStubPaths, ":").
+ FlagWithInputList("--core-platform-stub-classpath=", corePlatformStubPaths, ":").
+ FlagWithOutput("--out-api-flags=", tempPath)
+
+ commitChangeForRestat(rule, tempPath, outputPath)
+
+ rule.Build(pctx, ctx, "hiddenAPIStubFlagsFile", "hiddenapi stub flags")
+}
+
+// flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
+// the greylists.
+func flagsRule(ctx android.SingletonContext) android.Path {
+ var flagsCSV android.Paths
+
+ var greylistIgnoreConflicts android.Path
+
+ ctx.VisitAllModules(func(module android.Module) {
+ if h, ok := module.(hiddenAPIIntf); ok {
+ if csv := h.flagsCSV(); csv != nil {
+ flagsCSV = append(flagsCSV, csv)
+ }
+ } else if ds, ok := module.(*Droidstubs); ok && ctx.ModuleName(module) == "hiddenapi-lists-docs" {
+ greylistIgnoreConflicts = ds.removedDexApiFile
+ }
+ })
+
+ if greylistIgnoreConflicts == nil {
+ ctx.Errorf("failed to find removed_dex_api_filename from hiddenapi-lists-docs module")
+ return nil
+ }
+
+ rule := android.NewRuleBuilder()
+
+ outputPath := hiddenAPISingletonPaths(ctx).flags
+ tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
+
+ stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
+
+ rule.Command().
+ Tool(android.PathForSource(ctx, "frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py")).
+ FlagWithInput("--csv ", stubFlags).
+ Inputs(flagsCSV).
+ FlagWithInput("--greylist ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist.txt")).
+ FlagWithInput("--greylist-ignore-conflicts ",
+ greylistIgnoreConflicts).
+ FlagWithInput("--greylist-max-p ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-p.txt")).
+ FlagWithInput("--greylist-max-o-ignore-conflicts ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-o.txt")).
+ FlagWithInput("--blacklist ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blacklist.txt")).
+ FlagWithInput("--greylist-packages ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-packages.txt")).
+ FlagWithOutput("--output ", tempPath)
+
+ commitChangeForRestat(rule, tempPath, outputPath)
+
+ rule.Build(pctx, ctx, "hiddenAPIFlagsFile", "hiddenapi flags")
+
+ return outputPath
+}
+
+// emptyFlagsRule creates a rule to build an empty hiddenapi-flags.csv, which is needed by master-art-host builds that
+// have a partial manifest without frameworks/base but still need to build a boot image.
+func emptyFlagsRule(ctx android.SingletonContext) android.Path {
+ rule := android.NewRuleBuilder()
+
+ outputPath := hiddenAPISingletonPaths(ctx).flags
+
+ rule.Command().Text("rm").Flag("-f").Output(outputPath)
+ rule.Command().Text("touch").Output(outputPath)
+
+ rule.Build(pctx, ctx, "emptyHiddenAPIFlagsFile", "empty hiddenapi flags")
+
+ return outputPath
+}
+
+// metadataRule creates a rule to build hiddenapi-greylist.csv out of the metadata.csv files generated for boot image
+// modules.
+func metadataRule(ctx android.SingletonContext) android.Path {
+ var metadataCSV android.Paths
+
+ ctx.VisitAllModules(func(module android.Module) {
+ if h, ok := module.(hiddenAPIIntf); ok {
+ if csv := h.metadataCSV(); csv != nil {
+ metadataCSV = append(metadataCSV, csv)
+ }
+ }
+ })
+
+ rule := android.NewRuleBuilder()
+
+ outputPath := hiddenAPISingletonPaths(ctx).metadata
+
+ rule.Command().
+ Tool(android.PathForSource(ctx, "frameworks/base/tools/hiddenapi/merge_csv.py")).
+ Inputs(metadataCSV).
+ Text(">").
+ Output(outputPath)
+
+ rule.Build(pctx, ctx, "hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
+
+ return outputPath
+}
+
+// commitChangeForRestat adds a command to a rule that updates outputPath from tempPath if they are different. It
+// also marks the rule as restat and marks the tempPath as a temporary file that should not be considered an output of
+// the rule.
+func commitChangeForRestat(rule *android.RuleBuilder, tempPath, outputPath android.WritablePath) {
+ rule.Restat()
+ rule.Temporary(tempPath)
+ rule.Command().
+ Text("(").
+ Text("if").
+ Text("cmp -s").Input(tempPath).Output(outputPath).Text(";").
+ Text("then").
+ Text("rm").Input(tempPath).Text(";").
+ Text("else").
+ Text("mv").Input(tempPath).Output(outputPath).Text(";").
+ Text("fi").
+ Text(")")
+}
diff --git a/java/jacoco.go b/java/jacoco.go
index 541a84a..8b6d4ac 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -106,7 +106,7 @@
return nil, err
}
}
- return proptools.NinjaAndShellEscape(specs), nil
+ return proptools.NinjaAndShellEscapeList(specs), nil
}
func jacocoFilterToSpec(filter string) (string, error) {
diff --git a/java/java.go b/java/java.go
index ddfb09a..480518e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -29,18 +29,25 @@
"android/soong/android"
"android/soong/java/config"
+ "android/soong/tradefed"
)
func init() {
android.RegisterModuleType("java_defaults", defaultsFactory)
- android.RegisterModuleType("java_library", LibraryFactory(true))
- android.RegisterModuleType("java_library_static", LibraryFactory(false))
+ android.RegisterModuleType("java_library", LibraryFactory)
+ android.RegisterModuleType("java_library_static", LibraryStaticFactory)
android.RegisterModuleType("java_library_host", LibraryHostFactory)
android.RegisterModuleType("java_binary", BinaryFactory)
android.RegisterModuleType("java_binary_host", BinaryHostFactory)
+ android.RegisterModuleType("java_test", TestFactory)
+ android.RegisterModuleType("java_test_helper_library", TestHelperLibraryFactory)
+ android.RegisterModuleType("java_test_host", TestHostFactory)
android.RegisterModuleType("java_import", ImportFactory)
android.RegisterModuleType("java_import_host", ImportFactoryHost)
+ android.RegisterModuleType("java_device_for_host", DeviceForHostFactory)
+ android.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
+ android.RegisterModuleType("dex_import", DexImportFactory)
android.RegisterSingletonType("logtags", LogtagsSingleton)
}
@@ -57,11 +64,11 @@
type CompilerProperties struct {
// list of source files used to compile the Java module. May be .java, .logtags, .proto,
// or .aidl files.
- Srcs []string `android:"arch_variant"`
+ Srcs []string `android:"path,arch_variant"`
// list of source files that should not be used to build the Java module.
// This is most useful in the arch/multilib variants to remove non-common files
- Exclude_srcs []string `android:"arch_variant"`
+ Exclude_srcs []string `android:"path,arch_variant"`
// list of directories containing Java resources
Java_resource_dirs []string `android:"arch_variant"`
@@ -70,22 +77,24 @@
Exclude_java_resource_dirs []string `android:"arch_variant"`
// list of files to use as Java resources
- Java_resources []string `android:"arch_variant"`
+ Java_resources []string `android:"path,arch_variant"`
- // list of files that should be excluded from java_resources
- Exclude_java_resources []string `android:"arch_variant"`
+ // list of files that should be excluded from java_resources and java_resource_dirs
+ Exclude_java_resources []string `android:"path,arch_variant"`
- // don't build against the default libraries (bootclasspath, legacy-test, core-junit,
- // ext, and framework for device targets)
+ // don't build against the default libraries (bootclasspath, ext, and framework for device
+ // targets)
No_standard_libs *bool
- // don't build against the framework libraries (legacy-test, core-junit,
- // ext, and framework for device targets)
+ // don't build against the framework libraries (ext, and framework for device targets)
No_framework_libs *bool
// list of module-specific flags that will be used for javac compiles
Javacflags []string `android:"arch_variant"`
+ // list of module-specific flags that will be used for kotlinc compiles
+ Kotlincflags []string `android:"arch_variant"`
+
// list of of java libraries that will be in the classpath
Libs []string `android:"arch_variant"`
@@ -93,25 +102,27 @@
Static_libs []string `android:"arch_variant"`
// manifest file to be included in resulting jar
- Manifest *string
+ Manifest *string `android:"path"`
// if not blank, run jarjar using the specified rules file
- Jarjar_rules *string `android:"arch_variant"`
+ Jarjar_rules *string `android:"path,arch_variant"`
// If not blank, set the java version passed to javac as -source and -target
Java_version *string
- // If set to false, don't allow this module to be installed. Defaults to true.
+ // If set to true, allow this module to be dexed and installed on devices. Has no
+ // effect on host modules, which are always considered installable.
Installable *bool
// If set to true, include sources used to compile the module in to the final jar
Include_srcs *bool
- // List of modules to use as annotation processors
- Annotation_processors []string
+ // If not empty, classes are restricted to the specified packages and their sub-packages.
+ // This restriction is checked after applying jarjar rules and including static libs.
+ Permitted_packages []string
- // List of classes to pass to javac to use as annotation processors
- Annotation_processor_classes []string
+ // List of modules to use as annotation processors
+ Plugins []string
// The number of Java source entries each Javac instance can process
Javac_shard_size *int64
@@ -121,12 +132,21 @@
Openjdk9 struct {
// List of source files that should only be used when passing -source 1.9
- Srcs []string
+ Srcs []string `android:"path"`
// List of javac flags that should only be used when passing -source 1.9
Javacflags []string
}
+ // When compiling language level 9+ .java code in packages that are part of
+ // a system module, patch_module names the module that your sources and
+ // dependencies should be patched into. The Android runtime currently
+ // doesn't implement the JEP 261 module system so this option is only
+ // supported at compile time. It should only be needed to compile tests in
+ // packages that exist in libcore and which are inconvenient to move
+ // elsewhere.
+ Patch_module *string `android:"arch_variant"`
+
Jacoco struct {
// List of classes to include for instrumentation with jacoco to collect coverage
// information at runtime when building with coverage enabled. If unset defaults to all
@@ -156,15 +176,30 @@
}
Instrument bool `blueprint:"mutated"`
+
+ // List of files to include in the META-INF/services folder of the resulting jar.
+ Services []string `android:"path,arch_variant"`
}
type CompilerDeviceProperties struct {
// list of module-specific flags that will be used for dex compiles
Dxflags []string `android:"arch_variant"`
- // if not blank, set to the version of the sdk to compile against
+ // if not blank, set to the version of the sdk to compile against. Defaults to compiling against the current
+ // sdk if platform_apis is not set.
Sdk_version *string
+ // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+ // Defaults to sdk_version if not set.
+ Min_sdk_version *string
+
+ // if not blank, set the targetSdkVersion in the AndroidManifest.xml.
+ // Defaults to sdk_version if not set.
+ Target_sdk_version *string
+
+ // if true, compile against the platform APIs instead of an SDK.
+ Platform_apis *bool
+
Aidl struct {
// Top level directories to pass to aidl tool
Include_dirs []string
@@ -178,34 +213,30 @@
// whether to generate traces (for systrace) for this interface
Generate_traces *bool
+
+ // whether to generate Binder#GetTransaction name method.
+ Generate_get_transaction_name *bool
}
// If true, export a copy of the module as a -hostdex module for host testing.
Hostdex *bool
- Dex_preopt struct {
- // If false, prevent dexpreopting and stripping the dex file from the final jar. Defaults to
- // true.
- Enabled *bool
-
- // If true, generate an app image (.art file) for this module.
- App_image *bool
-
- // If true, use a checked-in profile to guide optimization. Defaults to false unless
- // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
- // that matches the name of this module, in which case it is defaulted to true.
- Profile_guided *bool
-
- // If set, provides the path to profile relative to the Android.bp file. If not set,
- // defaults to searching for a file that matches the name of this module in the default
- // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
- Profile *string
+ Target struct {
+ Hostdex struct {
+ // Additional required dependencies to add to -hostdex modules.
+ Required []string
+ }
}
+ // If set to true, compile dex regardless of installable. Defaults to false.
+ Compile_dex *bool
+
Optimize struct {
- // If false, disable all optimization. Defaults to true for apps, false for
- // libraries and tests.
+ // If false, disable all optimization. Defaults to true for android_app and android_test
+ // modules, false for java_library and java_test modules.
Enabled *bool
+ // True if the module containing this has it set by default.
+ EnabledByDefault bool `blueprint:"mutated"`
// If true, optimize for size by removing unused code. Defaults to true for apps,
// false for libraries and tests.
@@ -225,11 +256,18 @@
Proguard_flags []string
// Specifies the locations of files containing proguard flags.
- Proguard_flags_files []string
+ Proguard_flags_files []string `android:"path"`
}
// When targeting 1.9, override the modules to use with --system
System_modules *string
+
+ UncompressDex bool `blueprint:"mutated"`
+ IsSDKLibrary bool `blueprint:"mutated"`
+}
+
+func (me *CompilerDeviceProperties) EffectiveOptimizeEnabled() bool {
+ return BoolDefault(me.Optimize.Enabled, me.Optimize.EnabledByDefault)
}
// Module contains the properties and members used by all java module types
@@ -241,23 +279,36 @@
protoProperties android.ProtoProperties
deviceProperties CompilerDeviceProperties
- // header jar file suitable for inserting into the bootclasspath/classpath of another compile
+ // jar file containing header classes including static library dependencies, suitable for
+ // inserting into the bootclasspath/classpath of another compile
headerJarFile android.Path
- // full implementation jar file suitable for static dependency of another module compile
+ // jar file containing implementation classes including static library dependencies but no
+ // resources
implementationJarFile android.Path
- // output file containing classes.dex
+ // jar file containing only resources including from static library dependencies
+ resourceJar android.Path
+
+ // jar file containing implementation classes and resources including static library
+ // dependencies
+ implementationAndResourcesJar android.Path
+
+ // output file containing classes.dex and resources
dexJarFile android.Path
+ // output file that contains classes.dex if it should be in the output file
+ maybeStrippedDexJarFile android.Path
+
// output file containing uninstrumented classes that will be instrumented by jacoco
jacocoReportClassesFile android.Path
// output file containing mapping of obfuscated names
proguardDictionary android.Path
- // output file suitable for installing or running
- outputFile android.Path
+ // output file of the module, which may be a classes jar or a dex jar
+ outputFile android.Path
+ extraOutputFiles android.Paths
exportAidlIncludeDirs android.Paths
@@ -272,10 +323,33 @@
// list of extra progurad flag files
extraProguardFlagFiles android.Paths
+
+ // manifest file to use instead of properties.Manifest
+ overrideManifest android.OptionalPath
+
+ // list of SDK lib names that this java moudule is exporting
+ exportedSdkLibs []string
+
+ // list of source files, collected from compiledJavaSrcs and compiledSrcJars
+ // filter out Exclude_srcs, will be used by android.IDEInfo struct
+ expandIDEInfoCompiledSrcs []string
+
+ // expanded Jarjar_rules
+ expandJarjarRules android.Path
+
+ // list of additional targets for checkbuild
+ additionalCheckedModules android.Paths
+
+ hiddenAPI
+ dexpreopter
}
func (j *Module) Srcs() android.Paths {
- return android.Paths{j.implementationJarFile}
+ return append(android.Paths{j.outputFile}, j.extraOutputFiles...)
+}
+
+func (j *Module) DexJarFile() android.Path {
+ return j.dexJarFile
}
var _ android.SourceFileProducer = (*Module)(nil)
@@ -283,9 +357,33 @@
type Dependency interface {
HeaderJars() android.Paths
ImplementationJars() android.Paths
+ ResourceJars() android.Paths
+ ImplementationAndResourcesJars() android.Paths
+ DexJar() android.Path
AidlIncludeDirs() android.Paths
+ ExportedSdkLibs() []string
}
+type SdkLibraryDependency interface {
+ SdkHeaderJars(ctx android.BaseContext, sdkVersion string) android.Paths
+ SdkImplementationJars(ctx android.BaseContext, sdkVersion string) android.Paths
+}
+
+type SrcDependency interface {
+ CompiledSrcs() android.Paths
+ CompiledSrcJars() android.Paths
+}
+
+func (j *Module) CompiledSrcs() android.Paths {
+ return j.compiledJavaSrcs
+}
+
+func (j *Module) CompiledSrcJars() android.Paths {
+ return j.compiledSrcJars
+}
+
+var _ SrcDependency = (*Module)(nil)
+
func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
android.InitAndroidArchModule(module, hod, android.MultilibCommon)
android.InitDefaultableModule(module)
@@ -296,40 +394,42 @@
name string
}
+type jniDependencyTag struct {
+ blueprint.BaseDependencyTag
+ target android.Target
+}
+
var (
- staticLibTag = dependencyTag{name: "staticlib"}
- libTag = dependencyTag{name: "javalib"}
- bootClasspathTag = dependencyTag{name: "bootclasspath"}
- systemModulesTag = dependencyTag{name: "system modules"}
- frameworkResTag = dependencyTag{name: "framework-res"}
- kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
- proguardRaiseTag = dependencyTag{name: "proguard-raise"}
+ staticLibTag = dependencyTag{name: "staticlib"}
+ libTag = dependencyTag{name: "javalib"}
+ pluginTag = dependencyTag{name: "plugin"}
+ bootClasspathTag = dependencyTag{name: "bootclasspath"}
+ systemModulesTag = dependencyTag{name: "system modules"}
+ frameworkResTag = dependencyTag{name: "framework-res"}
+ frameworkApkTag = dependencyTag{name: "framework-apk"}
+ kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
+ kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"}
+ proguardRaiseTag = dependencyTag{name: "proguard-raise"}
+ certificateTag = dependencyTag{name: "certificate"}
+ instrumentationForTag = dependencyTag{name: "instrumentation_for"}
)
type sdkDep struct {
useModule, useFiles, useDefaultLibs, invalidVersion bool
- module string
+ modules []string
systemModules string
frameworkResModule string
- jar android.Path
- aidl android.Path
+ jars android.Paths
+ aidl android.OptionalPath
}
-func sdkStringToNumber(ctx android.BaseContext, v string) int {
- switch v {
- case "", "current", "system_current", "test_current", "core_current":
- return android.FutureApiLevel
- default:
- if i, err := strconv.Atoi(android.GetNumericSdkVersion(v)); err != nil {
- ctx.PropertyErrorf("sdk_version", "invalid sdk version")
- return -1
- } else {
- return i
- }
- }
+type jniLib struct {
+ name string
+ path android.Path
+ target android.Target
}
func (j *Module) shouldInstrument(ctx android.BaseContext) bool {
@@ -342,141 +442,66 @@
ctx.Config().UnbundledBuild())
}
-func decodeSdkDep(ctx android.BaseContext, v string) sdkDep {
- i := sdkStringToNumber(ctx, v)
- if i == -1 {
- // Invalid sdk version, error handled by sdkStringToNumber.
- return sdkDep{}
+func (j *Module) sdkVersion() string {
+ return String(j.deviceProperties.Sdk_version)
+}
+
+func (j *Module) minSdkVersion() string {
+ if j.deviceProperties.Min_sdk_version != nil {
+ return *j.deviceProperties.Min_sdk_version
}
+ return j.sdkVersion()
+}
- // Ensures that the specificed system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor apks)
- // or PRODUCT_SYSTEMSDK_VERSIONS (for other apks or when BOARD_SYSTEMSDK_VERSIONS is not set)
- if strings.HasPrefix(v, "system_") && i != android.FutureApiLevel {
- allowed_versions := ctx.DeviceConfig().PlatformSystemSdkVersions()
- if ctx.DeviceSpecific() || ctx.SocSpecific() {
- if len(ctx.DeviceConfig().SystemSdkVersions()) > 0 {
- allowed_versions = ctx.DeviceConfig().SystemSdkVersions()
- }
- }
- version := strings.TrimPrefix(v, "system_")
- if len(allowed_versions) > 0 && !android.InList(version, allowed_versions) {
- ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
- v, allowed_versions)
- }
+func (j *Module) targetSdkVersion() string {
+ if j.deviceProperties.Target_sdk_version != nil {
+ return *j.deviceProperties.Target_sdk_version
}
-
- toFile := func(v string) sdkDep {
- isCore := strings.HasPrefix(v, "core_")
- if isCore {
- v = strings.TrimPrefix(v, "core_")
- }
- dir := filepath.Join("prebuilts/sdk", v)
- jar := filepath.Join(dir, "android.jar")
- if isCore {
- jar = filepath.Join(dir, "core.jar")
- }
- aidl := filepath.Join(dir, "framework.aidl")
- jarPath := android.ExistentPathForSource(ctx, jar)
- aidlPath := android.ExistentPathForSource(ctx, aidl)
-
- if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
- return sdkDep{
- invalidVersion: true,
- module: "sdk_v" + v,
- }
- }
-
- if !jarPath.Valid() {
- ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, jar)
- return sdkDep{}
- }
-
- if !aidlPath.Valid() {
- ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, aidl)
- return sdkDep{}
- }
-
- return sdkDep{
- useFiles: true,
- jar: jarPath.Path(),
- aidl: aidlPath.Path(),
- }
- }
-
- //toModule := func(m string) sdkDep {
- // return sdkDep{
- // useModule: true,
- // module: m,
- // systemModules: m + "_system_modules",
- // frameworkResModule: r,
- // }
- //}
-
- if ctx.Config().UnbundledBuild() && v != "" {
- return toFile(v)
- }
-
- switch v {
- case "":
- return sdkDep{
- useDefaultLibs: true,
- frameworkResModule: "framework-res",
- }
- // TODO(ccross): re-enable these once we generate stubs, until then
- // use the stubs in prebuilts/sdk/*current
- //case "current":
- // return toModule("android_stubs_current", "framework-res")
- //case "system_current":
- // return toModule("android_system_stubs_current", "framework-res")
- //case "test_current":
- // return toModule("android_test_stubs_current", "framework-res")
- default:
- return toFile(v)
- }
+ return j.sdkVersion()
}
func (j *Module) deps(ctx android.BottomUpMutatorContext) {
if ctx.Device() {
- if !proptools.Bool(j.properties.No_standard_libs) {
- sdkDep := decodeSdkDep(ctx, String(j.deviceProperties.Sdk_version))
+ if !Bool(j.properties.No_standard_libs) {
+ sdkDep := decodeSdkDep(ctx, sdkContext(j))
if sdkDep.useDefaultLibs {
- ctx.AddDependency(ctx.Module(), bootClasspathTag, config.DefaultBootclasspathLibraries...)
- if ctx.Config().TargetOpenJDK9() {
- ctx.AddDependency(ctx.Module(), systemModulesTag, config.DefaultSystemModules)
- }
- if !proptools.Bool(j.properties.No_framework_libs) {
- ctx.AddDependency(ctx.Module(), libTag, config.DefaultLibraries...)
+ ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
+ ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
+ if !Bool(j.properties.No_framework_libs) {
+ ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
}
} else if sdkDep.useModule {
- if ctx.Config().TargetOpenJDK9() {
- ctx.AddDependency(ctx.Module(), systemModulesTag, sdkDep.systemModules)
- }
- ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
- if Bool(j.deviceProperties.Optimize.Enabled) {
- ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultBootclasspathLibraries...)
- ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultLibraries...)
+ ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+ ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.modules...)
+ if j.deviceProperties.EffectiveOptimizeEnabled() {
+ ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultBootclasspathLibraries...)
+ ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
}
}
} else if j.deviceProperties.System_modules == nil {
ctx.PropertyErrorf("no_standard_libs",
"system_modules is required to be set when no_standard_libs is true, did you mean no_framework_libs?")
- } else if *j.deviceProperties.System_modules != "none" && ctx.Config().TargetOpenJDK9() {
- ctx.AddDependency(ctx.Module(), systemModulesTag, *j.deviceProperties.System_modules)
+ } else if *j.deviceProperties.System_modules != "none" {
+ ctx.AddVariationDependencies(nil, systemModulesTag, *j.deviceProperties.System_modules)
}
- if ctx.ModuleName() == "framework" {
- ctx.AddDependency(ctx.Module(), frameworkResTag, "framework-res")
+ if (ctx.ModuleName() == "framework") || (ctx.ModuleName() == "framework-annotation-proc") {
+ ctx.AddVariationDependencies(nil, frameworkResTag, "framework-res")
+ }
+ if ctx.ModuleName() == "android_stubs_current" ||
+ ctx.ModuleName() == "android_system_stubs_current" ||
+ ctx.ModuleName() == "android_test_stubs_current" {
+ ctx.AddVariationDependencies(nil, frameworkApkTag, "framework-res")
}
}
- ctx.AddDependency(ctx.Module(), libTag, j.properties.Libs...)
- ctx.AddDependency(ctx.Module(), staticLibTag, j.properties.Static_libs...)
- ctx.AddDependency(ctx.Module(), libTag, j.properties.Annotation_processors...)
+ ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+ ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
- android.ExtractSourcesDeps(ctx, j.properties.Srcs)
- android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
- android.ExtractSourcesDeps(ctx, j.properties.Java_resources)
- android.ExtractSourceDeps(ctx, j.properties.Manifest)
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant},
+ }, pluginTag, j.properties.Plugins...)
+ android.ProtoDeps(ctx, &j.protoProperties)
if j.hasSrcExt(".proto") {
protoDeps(ctx, &j.protoProperties)
}
@@ -484,11 +509,14 @@
if j.hasSrcExt(".kt") {
// TODO(ccross): move this to a mutator pass that can tell if generated sources contain
// Kotlin files
- ctx.AddDependency(ctx.Module(), kotlinStdlibTag, "kotlin-stdlib")
+ ctx.AddVariationDependencies(nil, kotlinStdlibTag, "kotlin-stdlib")
+ if len(j.properties.Plugins) > 0 {
+ ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
+ }
}
if j.shouldInstrumentStatic(ctx) {
- ctx.AddDependency(ctx.Module(), staticLibTag, "jacocoagent")
+ ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
}
}
@@ -519,7 +547,7 @@
}
func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
- aidlIncludeDirs android.Paths) []string {
+ aidlIncludeDirs android.Paths) (string, android.Paths) {
aidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Local_include_dirs)
aidlIncludes = append(aidlIncludes,
@@ -528,14 +556,23 @@
android.PathsForSource(ctx, j.deviceProperties.Aidl.Include_dirs)...)
var flags []string
+ var deps android.Paths
+
if aidlPreprocess.Valid() {
flags = append(flags, "-p"+aidlPreprocess.String())
- } else {
+ deps = append(deps, aidlPreprocess.Path())
+ } else if len(aidlIncludeDirs) > 0 {
flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I"))
}
- flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
- flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
+ if len(j.exportAidlIncludeDirs) > 0 {
+ flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I"))
+ }
+
+ if len(aidlIncludes) > 0 {
+ flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I"))
+ }
+
flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String())
if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() {
flags = append(flags, "-I"+src.String())
@@ -545,20 +582,30 @@
flags = append(flags, "-t")
}
- return flags
+ if Bool(j.deviceProperties.Aidl.Generate_get_transaction_name) {
+ flags = append(flags, "--transaction_names")
+ }
+
+ return strings.Join(flags, " "), deps
}
type deps struct {
classpath classpath
bootClasspath classpath
+ processorPath classpath
+ processorClasses []string
staticJars android.Paths
staticHeaderJars android.Paths
- staticJarResources android.Paths
+ staticResourceJars android.Paths
aidlIncludeDirs android.Paths
+ srcs android.Paths
srcJars android.Paths
systemModules android.Path
aidlPreprocess android.OptionalPath
kotlinStdlib android.Paths
+ kotlinAnnotations android.Paths
+
+ disableTurbine bool
}
func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
@@ -570,31 +617,112 @@
}
}
+type linkType int
+
+const (
+ javaCore linkType = iota
+ javaSdk
+ javaSystem
+ javaPlatform
+)
+
+func getLinkType(m *Module, name string) (ret linkType, stubs bool) {
+ ver := m.sdkVersion()
+ switch {
+ case name == "core.current.stubs" || name == "core.platform.api.stubs" ||
+ name == "stub-annotations" || name == "private-stub-annotations-jar" ||
+ name == "core-lambda-stubs":
+ return javaCore, true
+ case ver == "core_current":
+ return javaCore, false
+ case name == "android_system_stubs_current":
+ return javaSystem, true
+ case strings.HasPrefix(ver, "system_"):
+ return javaSystem, false
+ case name == "android_test_stubs_current":
+ return javaSystem, true
+ case strings.HasPrefix(ver, "test_"):
+ return javaPlatform, false
+ case name == "android_stubs_current":
+ return javaSdk, true
+ case ver == "current":
+ return javaSdk, false
+ case ver == "":
+ return javaPlatform, false
+ default:
+ if _, err := strconv.Atoi(ver); err != nil {
+ panic(fmt.Errorf("expected sdk_version to be a number, got %q", ver))
+ }
+ return javaSdk, false
+ }
+}
+
func checkLinkType(ctx android.ModuleContext, from *Module, to *Library, tag dependencyTag) {
- if strings.HasPrefix(String(from.deviceProperties.Sdk_version), "core_") {
- if !strings.HasPrefix(String(to.deviceProperties.Sdk_version), "core_") {
- ctx.ModuleErrorf("depends on other library %q using non-core Java APIs",
+ if ctx.Host() {
+ return
+ }
+
+ myLinkType, stubs := getLinkType(from, ctx.ModuleName())
+ if stubs {
+ return
+ }
+ otherLinkType, _ := getLinkType(&to.Module, ctx.OtherModuleName(to))
+ commonMessage := "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source."
+
+ switch myLinkType {
+ case javaCore:
+ if otherLinkType != javaCore {
+ ctx.ModuleErrorf("compiles against core Java API, but dependency %q is compiling against non-core Java APIs."+commonMessage,
ctx.OtherModuleName(to))
}
+ break
+ case javaSdk:
+ if otherLinkType != javaCore && otherLinkType != javaSdk {
+ ctx.ModuleErrorf("compiles against Android API, but dependency %q is compiling against non-public Android API."+commonMessage,
+ ctx.OtherModuleName(to))
+ }
+ break
+ case javaSystem:
+ if otherLinkType == javaPlatform {
+ ctx.ModuleErrorf("compiles against system API, but dependency %q is compiling against private API."+commonMessage,
+ ctx.OtherModuleName(to))
+ }
+ break
+ case javaPlatform:
+ // no restriction on link-type
+ break
}
}
func (j *Module) collectDeps(ctx android.ModuleContext) deps {
var deps deps
- sdkDep := decodeSdkDep(ctx, String(j.deviceProperties.Sdk_version))
- if sdkDep.invalidVersion {
- ctx.AddMissingDependencies([]string{sdkDep.module})
- } else if sdkDep.useFiles {
- // sdkDep.jar is actually equivalent to turbine header.jar.
- deps.classpath = append(deps.classpath, sdkDep.jar)
- deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, sdkDep.aidl)
+ if ctx.Device() {
+ sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ if sdkDep.invalidVersion {
+ ctx.AddMissingDependencies(sdkDep.modules)
+ } else if sdkDep.useFiles {
+ // sdkDep.jar is actually equivalent to turbine header.jar.
+ deps.classpath = append(deps.classpath, sdkDep.jars...)
+ deps.aidlPreprocess = sdkDep.aidl
+ } else {
+ deps.aidlPreprocess = sdkDep.aidl
+ }
}
ctx.VisitDirectDeps(func(module android.Module) {
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
+ if _, ok := tag.(*jniDependencyTag); ok {
+ // Handled by AndroidApp.collectAppDeps
+ return
+ }
+ if tag == certificateTag {
+ // Handled by AndroidApp.collectAppDeps
+ return
+ }
+
if to, ok := module.(*Library); ok {
switch tag {
case bootClasspathTag, libTag, staticLibTag:
@@ -602,29 +730,66 @@
}
}
switch dep := module.(type) {
+ case SdkLibraryDependency:
+ switch tag {
+ case libTag:
+ deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
+ // names of sdk libs that are directly depended are exported
+ j.exportedSdkLibs = append(j.exportedSdkLibs, otherName)
+ default:
+ ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
+ }
case Dependency:
switch tag {
case bootClasspathTag:
deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
- case libTag:
+ case libTag, instrumentationForTag:
deps.classpath = append(deps.classpath, dep.HeaderJars()...)
+ // sdk lib names from dependencies are re-exported
+ j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+ deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
case staticLibTag:
deps.classpath = append(deps.classpath, dep.HeaderJars()...)
deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
+ deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...)
+ // sdk lib names from dependencies are re-exported
+ j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+ deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
+ case pluginTag:
+ if plugin, ok := dep.(*Plugin); ok {
+ deps.processorPath = append(deps.processorPath, dep.ImplementationAndResourcesJars()...)
+ if plugin.pluginProperties.Processor_class != nil {
+ deps.processorClasses = append(deps.processorClasses, *plugin.pluginProperties.Processor_class)
+ }
+ deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
+ } else {
+ ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
+ }
case frameworkResTag:
- if ctx.ModuleName() == "framework" {
+ if (ctx.ModuleName() == "framework") || (ctx.ModuleName() == "framework-annotation-proc") {
// framework.jar has a one-off dependency on the R.java and Manifest.java files
// generated by framework-res.apk
deps.srcJars = append(deps.srcJars, dep.(*AndroidApp).aaptSrcJar)
}
+ case frameworkApkTag:
+ if ctx.ModuleName() == "android_stubs_current" ||
+ ctx.ModuleName() == "android_system_stubs_current" ||
+ ctx.ModuleName() == "android_test_stubs_current" {
+ // framework stubs.jar need to depend on framework-res.apk, in order to pull the
+ // resource files out of there for aapt.
+ //
+ // Normally the package rule runs aapt, which includes the resource,
+ // but we're not running that in our package rule so just copy in the
+ // resource files here.
+ deps.staticResourceJars = append(deps.staticResourceJars, dep.(*AndroidApp).exportPackage)
+ }
case kotlinStdlibTag:
deps.kotlinStdlib = dep.HeaderJars()
- default:
- panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
+ case kotlinAnnotationsTag:
+ deps.kotlinAnnotations = dep.HeaderJars()
}
- deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
case android.SourceFileProducer:
switch tag {
case libTag:
@@ -635,10 +800,6 @@
deps.classpath = append(deps.classpath, dep.Srcs()...)
deps.staticJars = append(deps.staticJars, dep.Srcs()...)
deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
- case android.DefaultsDepTag, android.SourceDepTag:
- // Nothing to do
- default:
- ctx.ModuleErrorf("dependency on genrule %q may only be in srcs, libs, or static_libs", otherName)
}
default:
switch tag {
@@ -659,16 +820,54 @@
}
})
+ j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
+
return deps
}
+func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) string {
+ var ret string
+ v := sdkContext.sdkVersion()
+ // For PDK builds, use the latest SDK version instead of "current"
+ if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
+ sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
+ latestSdkVersion := 0
+ if len(sdkVersions) > 0 {
+ latestSdkVersion = sdkVersions[len(sdkVersions)-1]
+ }
+ v = strconv.Itoa(latestSdkVersion)
+ }
+
+ sdk, err := sdkVersionToNumber(ctx, v)
+ if err != nil {
+ ctx.PropertyErrorf("sdk_version", "%s", err)
+ }
+ if javaVersion != "" {
+ ret = javaVersion
+ } else if ctx.Device() && sdk <= 23 {
+ ret = "1.7"
+ } else if ctx.Device() && sdk <= 28 || !ctx.Config().TargetOpenJDK9() {
+ ret = "1.8"
+ } else if ctx.Device() && sdkContext.sdkVersion() != "" && sdk == android.FutureApiLevel {
+ // TODO(ccross): once we generate stubs we should be able to use 1.9 for sdk_version: "current"
+ ret = "1.8"
+ } else {
+ ret = "1.9"
+ }
+
+ return ret
+}
+
func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
var flags javaBuilderFlags
+ // javaVersion flag.
+ flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
+
// javac flags.
javacFlags := j.properties.Javacflags
- if ctx.Config().TargetOpenJDK9() {
+ if flags.javaVersion == "1.9" {
javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
}
if ctx.Config().MinimizeJavaDebugInfo() {
@@ -676,36 +875,31 @@
// disk and memory usage.
javacFlags = append(javacFlags, "-g:source,lines")
}
- if len(javacFlags) > 0 {
- // optimization.
- ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
- flags.javacFlags = "$javacFlags"
- }
- if len(j.properties.Errorprone.Javacflags) > 0 {
- flags.errorProneExtraJavacFlags = strings.Join(j.properties.Errorprone.Javacflags, " ")
- }
+ if ctx.Config().RunErrorProne() {
+ if config.ErrorProneClasspath == nil {
+ ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
+ }
- // javaVersion flag.
- sdk := sdkStringToNumber(ctx, String(j.deviceProperties.Sdk_version))
- if j.properties.Java_version != nil {
- flags.javaVersion = *j.properties.Java_version
- } else if ctx.Device() && sdk <= 23 {
- flags.javaVersion = "1.7"
- } else if ctx.Device() && sdk <= 26 || !ctx.Config().TargetOpenJDK9() {
- flags.javaVersion = "1.8"
- } else if ctx.Device() && String(j.deviceProperties.Sdk_version) != "" && sdk == android.FutureApiLevel {
- // TODO(ccross): once we generate stubs we should be able to use 1.9 for sdk_version: "current"
- flags.javaVersion = "1.8"
- } else {
- flags.javaVersion = "1.9"
+ errorProneFlags := []string{
+ "-Xplugin:ErrorProne",
+ "${config.ErrorProneChecks}",
+ }
+ errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...)
+
+ flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " +
+ "'" + strings.Join(errorProneFlags, " ") + "'"
+ flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
}
// classpath
flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...)
flags.classpath = append(flags.classpath, deps.classpath...)
+ flags.processorPath = append(flags.processorPath, deps.processorPath...)
- if len(flags.bootClasspath) == 0 && ctx.Host() && !ctx.Config().TargetOpenJDK9() &&
+ flags.processor = strings.Join(deps.processorClasses, ",")
+
+ if len(flags.bootClasspath) == 0 && ctx.Host() && flags.javaVersion != "1.9" &&
!Bool(j.properties.No_standard_libs) &&
inList(flags.javaVersion, []string{"1.6", "1.7", "1.8"}) {
// Give host-side tools a version of OpenJDK's standard libraries
@@ -731,17 +925,30 @@
}
}
+ if j.properties.Patch_module != nil && flags.javaVersion == "1.9" {
+ // Manually specify build directory in case it is not under the repo root.
+ // (javac doesn't seem to expand into symbolc links when searching for patch-module targets, so
+ // just adding a symlink under the root doesn't help.)
+ patchPaths := ".:" + ctx.Config().BuildDir()
+ classPath := flags.classpath.FormJavaClassPath("")
+ if classPath != "" {
+ patchPaths += ":" + classPath
+ }
+ javacFlags = append(javacFlags, "--patch-module="+String(j.properties.Patch_module)+"="+patchPaths)
+ }
+
// systemModules
if deps.systemModules != nil {
flags.systemModules = append(flags.systemModules, deps.systemModules)
}
// aidl flags.
- aidlFlags := j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
- if len(aidlFlags) > 0 {
+ flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
+
+ if len(javacFlags) > 0 {
// optimization.
- ctx.Variable(pctx, "aidlFlags", strings.Join(aidlFlags, " "))
- flags.aidlFlags = "$aidlFlags"
+ ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
+ flags.javacFlags = "$javacFlags"
}
return flags
@@ -754,10 +961,10 @@
deps := j.collectDeps(ctx)
flags := j.collectBuilderFlags(ctx, deps)
- if ctx.Config().TargetOpenJDK9() {
+ if flags.javaVersion == "1.9" {
j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
}
- srcFiles := ctx.ExpandSources(j.properties.Srcs, j.properties.Exclude_srcs)
+ srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
if hasSrcExt(srcFiles.Strings(), ".proto") {
flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
}
@@ -768,7 +975,13 @@
srcJars = append(srcJars, deps.srcJars...)
srcJars = append(srcJars, extraSrcJars...)
- var jars android.Paths
+ // Collect source files from compiledJavaSrcs, compiledSrcJars and filter out Exclude_srcs
+ // that IDEInfo struct will use
+ j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.Strings()...)
+
+ if j.properties.Jarjar_rules != nil {
+ j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
+ }
jarName := ctx.ModuleName() + ".jar"
@@ -782,64 +995,84 @@
}
}
+ var kotlinJars android.Paths
+
if srcFiles.HasExt(".kt") {
+ // user defined kotlin flags.
+ kotlincFlags := j.properties.Kotlincflags
+ CheckKotlincFlags(ctx, kotlincFlags)
+
// If there are kotlin files, compile them first but pass all the kotlin and java files
// kotlinc will use the java files to resolve types referenced by the kotlin files, but
// won't emit any classes for them.
-
- flags.kotlincFlags = "-no-stdlib"
+ kotlincFlags = append(kotlincFlags, "-no-stdlib")
if ctx.Device() {
- flags.kotlincFlags += " -no-jdk"
+ kotlincFlags = append(kotlincFlags, "-no-jdk")
+ }
+ if len(kotlincFlags) > 0 {
+ // optimization.
+ ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " "))
+ flags.kotlincFlags += "$kotlincFlags"
}
var kotlinSrcFiles android.Paths
kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
- flags.kotlincClasspath = append(flags.kotlincClasspath, deps.kotlinStdlib...)
- flags.kotlincClasspath = append(flags.kotlincClasspath, deps.classpath...)
+ flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
+ flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
+
+ flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
+ flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
+
+ if len(flags.processorPath) > 0 {
+ // Use kapt for annotation processing
+ kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
+ kotlinKapt(ctx, kaptSrcJar, kotlinSrcFiles, srcJars, flags)
+ srcJars = append(srcJars, kaptSrcJar)
+ // Disable annotation processing in javac, it's already been handled by kapt
+ flags.processorPath = nil
+ flags.processor = ""
+ }
kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
- TransformKotlinToClasses(ctx, kotlinJar, kotlinSrcFiles, srcJars, flags)
+ kotlinCompile(ctx, kotlinJar, kotlinSrcFiles, srcJars, flags)
if ctx.Failed() {
return
}
// Make javac rule depend on the kotlinc rule
- flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
flags.classpath = append(flags.classpath, kotlinJar)
+
// Jar kotlin classes into the final jar after javac
- jars = append(jars, kotlinJar)
- jars = append(jars, deps.kotlinStdlib...)
+ kotlinJars = append(kotlinJars, kotlinJar)
+ kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
}
+ jars := append(android.Paths(nil), kotlinJars...)
+
// Store the list of .java files that was passed to javac
j.compiledJavaSrcs = uniqueSrcFiles
j.compiledSrcJars = srcJars
enable_sharding := false
- if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") {
+ if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine {
if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
enable_sharding = true
- if len(j.properties.Annotation_processors) != 0 ||
- len(j.properties.Annotation_processor_classes) != 0 {
- ctx.PropertyErrorf("javac_shard_size",
- "%q cannot be set when annotation processors are enabled.",
- j.properties.Javac_shard_size)
- }
+ // Formerly, there was a check here that prevented annotation processors
+ // from being used when sharding was enabled, as some annotation processors
+ // do not function correctly in sharded environments. It was removed to
+ // allow for the use of annotation processors that do function correctly
+ // with sharding enabled. See: b/77284273.
}
- // If sdk jar is java module, then directly return classesJar as header.jar
- if j.Name() != "android_stubs_current" && j.Name() != "android_system_stubs_current" &&
- j.Name() != "android_test_stubs_current" {
- j.headerJarFile = j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName)
- if ctx.Failed() {
- return
- }
+ j.headerJarFile = j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars)
+ if ctx.Failed() {
+ return
}
}
if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
var extraJarDeps android.Paths
- if ctx.Config().IsEnvTrue("RUN_ERROR_PRONE") {
+ if ctx.Config().RunErrorProne() {
// If error-prone is enabled, add an additional rule to compile the java files into
// a separate set of classes (so that they don't overwrite the normal ones and require
// a rebuild when error-prone is turned off).
@@ -877,7 +1110,8 @@
}
}
- dirArgs, dirDeps := ResourceDirsToJarArgs(ctx, j.properties.Java_resource_dirs, j.properties.Exclude_java_resource_dirs)
+ dirArgs, dirDeps := ResourceDirsToJarArgs(ctx, j.properties.Java_resource_dirs,
+ j.properties.Exclude_java_resource_dirs, j.properties.Exclude_java_resources)
fileArgs, fileDeps := ResourceFilesToJarArgs(ctx, j.properties.Java_resources, j.properties.Exclude_java_resources)
var resArgs []string
@@ -889,7 +1123,7 @@
resArgs = append(resArgs, fileArgs...)
resDeps = append(resDeps, fileDeps...)
- if proptools.Bool(j.properties.Include_srcs) {
+ if Bool(j.properties.Include_srcs) {
srcArgs, srcDeps := SourceFilesToJarArgs(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
resArgs = append(resArgs, srcArgs...)
resDeps = append(resDeps, srcDeps...)
@@ -898,47 +1132,109 @@
if len(resArgs) > 0 {
resourceJar := android.PathForModuleOut(ctx, "res", jarName)
TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
+ j.resourceJar = resourceJar
if ctx.Failed() {
return
}
-
- jars = append(jars, resourceJar)
}
- // static classpath jars have the resources in them, so the resource jars aren't necessary here
+ if len(deps.staticResourceJars) > 0 {
+ var jars android.Paths
+ if j.resourceJar != nil {
+ jars = append(jars, j.resourceJar)
+ }
+ jars = append(jars, deps.staticResourceJars...)
+
+ combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
+ TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{},
+ false, nil, nil)
+ j.resourceJar = combinedJar
+ }
+
jars = append(jars, deps.staticJars...)
- var manifest android.OptionalPath
- if j.properties.Manifest != nil {
- manifest = android.OptionalPathForPath(ctx.ExpandSource(*j.properties.Manifest, "manifest"))
+ manifest := j.overrideManifest
+ if !manifest.Valid() && j.properties.Manifest != nil {
+ manifest = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *j.properties.Manifest))
+ }
+
+ services := android.PathsForModuleSrc(ctx, j.properties.Services)
+ if len(services) > 0 {
+ servicesJar := android.PathForModuleOut(ctx, "services", jarName)
+ var zipargs []string
+ for _, file := range services {
+ serviceFile := file.String()
+ zipargs = append(zipargs, "-C", filepath.Dir(serviceFile), "-f", serviceFile)
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zip,
+ Output: servicesJar,
+ Implicits: services,
+ Args: map[string]string{
+ "jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "),
+ },
+ })
+ jars = append(jars, servicesJar)
}
// Combine the classes built from sources, any manifests, and any static libraries into
// classes.jar. If there is only one input jar this step will be skipped.
- var outputFile android.Path
+ var outputFile android.ModuleOutPath
if len(jars) == 1 && !manifest.Valid() {
- // Optimization: skip the combine step if there is nothing to do
- // TODO(ccross): this leaves any module-info.class files, but those should only come from
- // prebuilt dependencies until we support modules in the platform build, so there shouldn't be
- // any if len(jars) == 1.
- outputFile = jars[0]
+ if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok {
+ // Optimization: skip the combine step if there is nothing to do
+ // TODO(ccross): this leaves any module-info.class files, but those should only come from
+ // prebuilt dependencies until we support modules in the platform build, so there shouldn't be
+ // any if len(jars) == 1.
+ outputFile = moduleOutPath
+ } else {
+ combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: jars[0],
+ Output: combinedJar,
+ })
+ outputFile = combinedJar
+ }
} else {
combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
- TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest, false, nil)
+ TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
+ false, nil, nil)
outputFile = combinedJar
}
- if j.properties.Jarjar_rules != nil {
- jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
+ // jarjar implementation jar if necessary
+ if j.expandJarjarRules != nil {
// Transform classes.jar into classes-jarjar.jar
jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName)
- TransformJarJar(ctx, jarjarFile, outputFile, jarjar_rules)
+ TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules)
outputFile = jarjarFile
+
+ // jarjar resource jar if necessary
+ if j.resourceJar != nil {
+ resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName)
+ TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, j.expandJarjarRules)
+ j.resourceJar = resourceJarJarFile
+ }
+
if ctx.Failed() {
return
}
}
+
+ // Check package restrictions if necessary.
+ if len(j.properties.Permitted_packages) > 0 {
+ // Check packages and copy to package-checked file.
+ pkgckFile := android.PathForModuleOut(ctx, "package-check.stamp")
+ CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
+ j.additionalCheckedModules = append(j.additionalCheckedModules, pkgckFile)
+
+ if ctx.Failed() {
+ return
+ }
+ }
+
j.implementationJarFile = outputFile
if j.headerJarFile == nil {
j.headerJarFile = j.implementationJarFile
@@ -954,18 +1250,94 @@
outputFile = j.instrument(ctx, flags, outputFile, jarName)
}
- if ctx.Device() && j.installable() {
- outputFile = j.compileDex(ctx, flags, outputFile, jarName)
+ // merge implementation jar with resources if necessary
+ implementationAndResourcesJar := outputFile
+ if j.resourceJar != nil {
+ jars := android.Paths{implementationAndResourcesJar, j.resourceJar}
+ combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
+ TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{},
+ false, nil, nil)
+ implementationAndResourcesJar = combinedJar
+ }
+
+ j.implementationAndResourcesJar = implementationAndResourcesJar
+
+ if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.deviceProperties.Compile_dex)) {
+ // Dex compilation
+ var dexOutputFile android.ModuleOutPath
+ dexOutputFile = j.compileDex(ctx, flags, outputFile, jarName)
if ctx.Failed() {
return
}
+
+ // Hidden API CSV generation and dex encoding
+ dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
+ j.deviceProperties.UncompressDex)
+
+ // merge dex jar with resources if necessary
+ if j.resourceJar != nil {
+ jars := android.Paths{dexOutputFile, j.resourceJar}
+ combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
+ TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
+ false, nil, nil)
+ if j.deviceProperties.UncompressDex {
+ combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
+ TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
+ dexOutputFile = combinedAlignedJar
+ } else {
+ dexOutputFile = combinedJar
+ }
+ }
+
+ j.dexJarFile = dexOutputFile
+
+ // Dexpreopting
+ dexOutputFile = j.dexpreopt(ctx, dexOutputFile)
+
+ j.maybeStrippedDexJarFile = dexOutputFile
+
+ outputFile = dexOutputFile
+
+ if ctx.Failed() {
+ return
+ }
+ } else {
+ outputFile = implementationAndResourcesJar
}
+
ctx.CheckbuildFile(outputFile)
- j.outputFile = outputFile
+
+ // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
+ j.outputFile = outputFile.WithoutRel()
+}
+
+// Check for invalid kotlinc flags. Only use this for flags explicitly passed by the user,
+// since some of these flags may be used internally.
+func CheckKotlincFlags(ctx android.ModuleContext, flags []string) {
+ for _, flag := range flags {
+ flag = strings.TrimSpace(flag)
+
+ if !strings.HasPrefix(flag, "-") {
+ ctx.PropertyErrorf("kotlincflags", "Flag `%s` must start with `-`", flag)
+ } else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
+ ctx.PropertyErrorf("kotlincflags",
+ "Bad flag: `%s`, only use internal compiler for consistency.", flag)
+ } else if inList(flag, config.KotlincIllegalFlags) {
+ ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
+ } else if flag == "-include-runtime" {
+ ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
+ } else {
+ args := strings.Split(flag, " ")
+ if args[0] == "-kotlin-home" {
+ ctx.PropertyErrorf("kotlincflags",
+ "Bad flag: `%s`, kotlin home already set to default (path to kotlinc in the repo).", flag)
+ }
+ }
+ }
}
func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
- deps deps, flags javaBuilderFlags, jarName string) android.Path {
+ deps deps, flags javaBuilderFlags, jarName string, extraJars android.Paths) android.Path {
var jars android.Paths
if len(srcFiles) > 0 || len(srcJars) > 0 {
@@ -978,6 +1350,8 @@
jars = append(jars, turbineJar)
}
+ jars = append(jars, extraJars...)
+
// Combine any static header libraries into classes-header.jar. If there is only
// one input jar this step will be skipped.
var headerJar android.Path
@@ -986,14 +1360,14 @@
// we cannot skip the combine step for now if there is only one jar
// since we have to strip META-INF/TRANSITIVE dir from turbine.jar
combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
- TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{}, false, []string{"META-INF"})
+ TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{},
+ false, nil, []string{"META-INF"})
headerJar = combinedJar
- if j.properties.Jarjar_rules != nil {
- jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
+ if j.expandJarjarRules != nil {
// Transform classes.jar into classes-jarjar.jar
jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName)
- TransformJarJar(ctx, jarjarFile, headerJar, jarjar_rules)
+ TransformJarJar(ctx, jarjarFile, headerJar, j.expandJarjarRules)
headerJar = jarjarFile
if ctx.Failed() {
return nil
@@ -1004,7 +1378,7 @@
}
func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
- classesJar android.Path, jarName string) android.Path {
+ classesJar android.Path, jarName string) android.ModuleOutPath {
specs := j.jacocoModuleToZipCommand(ctx)
@@ -1018,55 +1392,116 @@
return instrumentedJar
}
-// Returns a sdk version as a string that is guaranteed to be a parseable as a number. For
-// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns "10000".
-func (j *Module) minSdkVersionNumber(ctx android.ModuleContext) string {
- switch String(j.deviceProperties.Sdk_version) {
- case "", "current", "test_current", "system_current", "core_current":
- return strconv.Itoa(ctx.Config().DefaultAppTargetSdkInt())
- default:
- return android.GetNumericSdkVersion(String(j.deviceProperties.Sdk_version))
- }
-}
-
-func (j *Module) installable() bool {
- return j.properties.Installable == nil || *j.properties.Installable
-}
-
-var _ Dependency = (*Library)(nil)
+var _ Dependency = (*Module)(nil)
func (j *Module) HeaderJars() android.Paths {
+ if j.headerJarFile == nil {
+ return nil
+ }
return android.Paths{j.headerJarFile}
}
func (j *Module) ImplementationJars() android.Paths {
+ if j.implementationJarFile == nil {
+ return nil
+ }
return android.Paths{j.implementationJarFile}
}
+func (j *Module) DexJar() android.Path {
+ return j.dexJarFile
+}
+
+func (j *Module) ResourceJars() android.Paths {
+ if j.resourceJar == nil {
+ return nil
+ }
+ return android.Paths{j.resourceJar}
+}
+
+func (j *Module) ImplementationAndResourcesJars() android.Paths {
+ if j.implementationAndResourcesJar == nil {
+ return nil
+ }
+ return android.Paths{j.implementationAndResourcesJar}
+}
+
func (j *Module) AidlIncludeDirs() android.Paths {
+ // exportAidlIncludeDirs is type android.Paths already
return j.exportAidlIncludeDirs
}
+func (j *Module) ExportedSdkLibs() []string {
+ // exportedSdkLibs is type []string
+ return j.exportedSdkLibs
+}
+
var _ logtagsProducer = (*Module)(nil)
func (j *Module) logtags() android.Paths {
return j.logtagsSrcs
}
+// Collect information for opening IDE project files in java/jdeps.go.
+func (j *Module) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
+ dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
+ dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
+ if j.expandJarjarRules != nil {
+ dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
+ }
+}
+
+func (j *Module) CompilerDeps() []string {
+ jdeps := []string{}
+ jdeps = append(jdeps, j.properties.Libs...)
+ jdeps = append(jdeps, j.properties.Static_libs...)
+ return jdeps
+}
+
//
// Java libraries (.jar file)
//
type Library struct {
Module
+
+ InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
+}
+
+func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool {
+ // Store uncompressed (and do not strip) dex files from boot class path jars.
+ if inList(ctx.ModuleName(), ctx.Config().BootJars()) {
+ return true
+ }
+
+ // Store uncompressed dex files that are preopted on /system.
+ if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, dexpreopter.installPath)) {
+ return true
+ }
+ if ctx.Config().UncompressPrivAppDex() &&
+ inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules()) {
+ return true
+ }
+
+ return false
}
func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", ctx.ModuleName()+".jar")
+ j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
+ j.dexpreopter.isInstallable = Bool(j.properties.Installable)
+ j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
+ j.deviceProperties.UncompressDex = j.dexpreopter.uncompressedDex
j.compile(ctx)
- if j.installable() {
+ if (Bool(j.properties.Installable) || ctx.Host()) && !android.DirectlyInAnyApex(ctx, ctx.ModuleName()) {
+ var extraInstallDeps android.Paths
+ if j.InstallMixin != nil {
+ extraInstallDeps = j.InstallMixin(ctx, j.outputFile)
+ }
j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
- ctx.ModuleName()+".jar", j.outputFile)
+ ctx.ModuleName()+".jar", j.outputFile, extraInstallDeps...)
}
}
@@ -1074,24 +1509,39 @@
j.deps(ctx)
}
-func LibraryFactory(installable bool) func() android.Module {
- return func() android.Module {
- module := &Library{}
+// java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well.
+//
+// By default, a java_library has a single variant that produces a `.jar` file containing `.class` files that were
+// compiled against the device bootclasspath. This jar is not suitable for installing on a device, but can be used
+// as a `static_libs` dependency of another module.
+//
+// Specifying `installable: true` will product a `.jar` file containing `classes.dex` files, suitable for installing on
+// a device.
+//
+// Specifying `host_supported: true` will produce two variants, one compiled against the device bootclasspath and one
+// compiled against the host bootclasspath.
+func LibraryFactory() android.Module {
+ module := &Library{}
- if !installable {
- module.properties.Installable = proptools.BoolPtr(false)
- }
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
+ &module.Module.protoProperties)
- module.AddProperties(
- &module.Module.properties,
- &module.Module.deviceProperties,
- &module.Module.protoProperties)
-
- InitJavaModule(module, android.HostAndDeviceSupported)
- return module
- }
+ InitJavaModule(module, android.HostAndDeviceSupported)
+ return module
}
+// java_library_static is an obsolete alias for java_library.
+func LibraryStaticFactory() android.Module {
+ return LibraryFactory()
+}
+
+// java_library_host builds and links sources into a `.jar` file for the host.
+//
+// A java_library_host has a single variant that produces a `.jar` file containing `.class` files that were
+// compiled against the host bootclasspath.
func LibraryHostFactory() android.Module {
module := &Library{}
@@ -1099,6 +1549,121 @@
&module.Module.properties,
&module.Module.protoProperties)
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+
+ InitJavaModule(module, android.HostSupported)
+ return module
+}
+
+//
+// Java Tests
+//
+
+type testProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+
+ // the name of the test configuration (for example "AndroidTest.xml") that should be
+ // installed with the module.
+ Test_config *string `android:"path,arch_variant"`
+
+ // the name of the test configuration template (for example "AndroidTestTemplate.xml") that
+ // should be installed with the module.
+ Test_config_template *string `android:"path,arch_variant"`
+
+ // list of files or filegroup modules that provide data that should be installed alongside
+ // the test
+ Data []string `android:"path"`
+}
+
+type testHelperLibraryProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+}
+
+type Test struct {
+ Library
+
+ testProperties testProperties
+
+ testConfig android.Path
+ data android.Paths
+}
+
+type TestHelperLibrary struct {
+ Library
+
+ testHelperLibraryProperties testHelperLibraryProperties
+}
+
+func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, j.testProperties.Test_suites)
+ j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
+
+ j.Library.GenerateAndroidBuildActions(ctx)
+}
+
+func (j *TestHelperLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.Library.GenerateAndroidBuildActions(ctx)
+}
+
+// java_test builds a and links sources into a `.jar` file for the device, and possibly for the host as well, and
+// creates an `AndroidTest.xml` file to allow running the test with `atest` or a `TEST_MAPPING` file.
+//
+// By default, a java_test has a single variant that produces a `.jar` file containing `classes.dex` files that were
+// compiled against the device bootclasspath.
+//
+// Specifying `host_supported: true` will produce two variants, one compiled against the device bootclasspath and one
+// compiled against the host bootclasspath.
+func TestFactory() android.Module {
+ module := &Test{}
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
+ &module.Module.protoProperties,
+ &module.testProperties)
+
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+ module.Module.dexpreopter.isTest = true
+
+ InitJavaModule(module, android.HostAndDeviceSupported)
+ return module
+}
+
+// java_test_helper_library creates a java library and makes sure that it is added to the appropriate test suite.
+func TestHelperLibraryFactory() android.Module {
+ module := &TestHelperLibrary{}
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
+ &module.Module.protoProperties,
+ &module.testHelperLibraryProperties)
+
+ InitJavaModule(module, android.HostAndDeviceSupported)
+ return module
+}
+
+// java_test_host builds a and links sources into a `.jar` file for the host, and creates an `AndroidTest.xml` file to
+// allow running the test with `atest` or a `TEST_MAPPING` file.
+//
+// A java_test_host has a single variant that produces a `.jar` file containing `.class` files that were
+// compiled against the host bootclasspath.
+func TestHostFactory() android.Module {
+ module := &Test{}
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.protoProperties,
+ &module.testProperties)
+
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+
InitJavaModule(module, android.HostSupported)
return module
}
@@ -1109,7 +1674,10 @@
type binaryProperties struct {
// installable script to execute the resulting jar
- Wrapper *string
+ Wrapper *string `android:"path"`
+
+ // Name of the class containing main to be inserted into the manifest as Main-Class.
+ Main_class *string
}
type Binary struct {
@@ -1130,13 +1698,22 @@
func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if ctx.Arch().ArchType == android.Common {
// Compile the jar
+ if j.binaryProperties.Main_class != nil {
+ if j.properties.Manifest != nil {
+ ctx.PropertyErrorf("main_class", "main_class cannot be used when manifest is set")
+ }
+ manifestFile := android.PathForModuleOut(ctx, "manifest.txt")
+ GenerateMainClassManifest(ctx, manifestFile, String(j.binaryProperties.Main_class))
+ j.overrideManifest = android.OptionalPathForPath(manifestFile)
+ }
+
j.Library.GenerateAndroidBuildActions(ctx)
} else {
// Handle the binary wrapper
j.isWrapperVariant = true
if j.binaryProperties.Wrapper != nil {
- j.wrapperFile = ctx.ExpandSource(*j.binaryProperties.Wrapper, "wrapper")
+ j.wrapperFile = android.PathForModuleSrc(ctx, *j.binaryProperties.Wrapper)
} else {
j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
}
@@ -1153,34 +1730,48 @@
func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
if ctx.Arch().ArchType == android.Common {
j.deps(ctx)
- } else {
- android.ExtractSourceDeps(ctx, j.binaryProperties.Wrapper)
}
}
+// java_binary builds a `.jar` file and a shell script that executes it for the device, and possibly for the host
+// as well.
+//
+// By default, a java_binary has a single variant that produces a `.jar` file containing `classes.dex` files that were
+// compiled against the device bootclasspath.
+//
+// Specifying `host_supported: true` will produce two variants, one compiled against the device bootclasspath and one
+// compiled against the host bootclasspath.
func BinaryFactory() android.Module {
module := &Binary{}
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
&module.Module.protoProperties,
&module.binaryProperties)
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
android.InitDefaultableModule(module)
return module
}
+// java_binary_host builds a `.jar` file and a shell script that executes it for the host.
+//
+// A java_binary_host has a single variant that produces a `.jar` file containing `.class` files that were
+// compiled against the host bootclasspath.
func BinaryHostFactory() android.Module {
module := &Binary{}
module.AddProperties(
&module.Module.properties,
- &module.Module.deviceProperties,
&module.Module.protoProperties,
&module.binaryProperties)
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst)
android.InitDefaultableModule(module)
return module
@@ -1191,21 +1782,42 @@
//
type ImportProperties struct {
- Jars []string
+ Jars []string `android:"path"`
Sdk_version *string
Installable *bool
+
+ // List of shared java libs that this module has dependencies to
+ Libs []string
+
+ // List of files to remove from the jar file(s)
+ Exclude_files []string
+
+ // List of directories to remove from the jar file(s)
+ Exclude_dirs []string
+
+ // if set to true, run Jetifier against .jar file. Defaults to false.
+ Jetifier *bool
}
type Import struct {
android.ModuleBase
+ android.DefaultableModuleBase
prebuilt android.Prebuilt
properties ImportProperties
- classpathFiles android.Paths
combinedClasspathFile android.Path
+ exportedSdkLibs []string
+}
+
+func (j *Import) sdkVersion() string {
+ return String(j.properties.Sdk_version)
+}
+
+func (j *Import) minSdkVersion() string {
+ return j.sdkVersion()
}
func (j *Import) Prebuilt() *android.Prebuilt {
@@ -1221,49 +1833,251 @@
}
func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
}
func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
+ jars := android.PathsForModuleSrc(ctx, j.properties.Jars)
- outputFile := android.PathForModuleOut(ctx, "classes.jar")
- TransformJarsToJar(ctx, outputFile, "for prebuilts", j.classpathFiles, android.OptionalPath{}, false, nil)
+ jarName := ctx.ModuleName() + ".jar"
+ outputFile := android.PathForModuleOut(ctx, "combined", jarName)
+ TransformJarsToJar(ctx, outputFile, "for prebuilts", jars, android.OptionalPath{},
+ false, j.properties.Exclude_files, j.properties.Exclude_dirs)
+ if Bool(j.properties.Jetifier) {
+ inputFile := outputFile
+ outputFile = android.PathForModuleOut(ctx, "jetifier", jarName)
+ TransformJetifier(ctx, outputFile, inputFile)
+ }
j.combinedClasspathFile = outputFile
+
+ ctx.VisitDirectDeps(func(module android.Module) {
+ otherName := ctx.OtherModuleName(module)
+ tag := ctx.OtherModuleDependencyTag(module)
+
+ switch dep := module.(type) {
+ case Dependency:
+ switch tag {
+ case libTag, staticLibTag:
+ // sdk lib names from dependencies are re-exported
+ j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+ }
+ case SdkLibraryDependency:
+ switch tag {
+ case libTag:
+ // names of sdk libs that are directly depended are exported
+ j.exportedSdkLibs = append(j.exportedSdkLibs, otherName)
+ }
+ }
+ })
+
+ j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
+ if Bool(j.properties.Installable) {
+ ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
+ ctx.ModuleName()+".jar", outputFile)
+ }
}
var _ Dependency = (*Import)(nil)
func (j *Import) HeaderJars() android.Paths {
- return j.classpathFiles
+ if j.combinedClasspathFile == nil {
+ return nil
+ }
+ return android.Paths{j.combinedClasspathFile}
}
func (j *Import) ImplementationJars() android.Paths {
- return j.classpathFiles
+ if j.combinedClasspathFile == nil {
+ return nil
+ }
+ return android.Paths{j.combinedClasspathFile}
+}
+
+func (j *Import) ResourceJars() android.Paths {
+ return nil
+}
+
+func (j *Import) ImplementationAndResourcesJars() android.Paths {
+ if j.combinedClasspathFile == nil {
+ return nil
+ }
+ return android.Paths{j.combinedClasspathFile}
+}
+
+func (j *Import) DexJar() android.Path {
+ return nil
}
func (j *Import) AidlIncludeDirs() android.Paths {
return nil
}
+func (j *Import) ExportedSdkLibs() []string {
+ return j.exportedSdkLibs
+}
+
+// Add compile time check for interface implementation
+var _ android.IDEInfo = (*Import)(nil)
+var _ android.IDECustomizedModuleName = (*Import)(nil)
+
+// Collect information for opening IDE project files in java/jdeps.go.
+const (
+ removedPrefix = "prebuilt_"
+)
+
+func (j *Import) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...)
+}
+
+func (j *Import) IDECustomizedModuleName() string {
+ // TODO(b/113562217): Extract the base module name from the Import name, often the Import name
+ // has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better
+ // solution to get the Import name.
+ name := j.Name()
+ if strings.HasPrefix(name, removedPrefix) {
+ name = strings.TrimPrefix(name, removedPrefix)
+ }
+ return name
+}
+
var _ android.PrebuiltInterface = (*Import)(nil)
+// java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module.
+//
+// By default, a java_import has a single variant that expects a `.jar` file containing `.class` files that were
+// compiled against an Android classpath.
+//
+// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
+// for host modules.
func ImportFactory() android.Module {
module := &Import{}
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Jars)
- android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
+// java_import imports one or more `.jar` files into the build graph as if they were built by a java_library_host
+// module.
+//
+// A java_import_host has a single variant that expects a `.jar` file containing `.class` files that were
+// compiled against a host bootclasspath.
func ImportFactoryHost() android.Module {
module := &Import{}
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Jars)
- android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+ InitJavaModule(module, android.HostSupported)
+ return module
+}
+
+// dex_import module
+
+type DexImportProperties struct {
+ Jars []string
+}
+
+type DexImport struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ prebuilt android.Prebuilt
+
+ properties DexImportProperties
+
+ dexJarFile android.Path
+ maybeStrippedDexJarFile android.Path
+
+ dexpreopter
+}
+
+func (j *DexImport) Prebuilt() *android.Prebuilt {
+ return &j.prebuilt
+}
+
+func (j *DexImport) PrebuiltSrcs() []string {
+ return j.properties.Jars
+}
+
+func (j *DexImport) Name() string {
+ return j.prebuilt.Name(j.ModuleBase.Name())
+}
+
+func (j *DexImport) DepsMutator(ctx android.BottomUpMutatorContext) {
+ android.ExtractSourcesDeps(ctx, j.properties.Jars)
+}
+
+func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if len(j.properties.Jars) != 1 {
+ ctx.PropertyErrorf("jars", "exactly one jar must be provided")
+ }
+
+ j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", ctx.ModuleName()+".jar")
+ j.dexpreopter.isInstallable = true
+ j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
+
+ inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars")
+ dexOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar")
+
+ if j.dexpreopter.uncompressedDex {
+ rule := android.NewRuleBuilder()
+
+ temporary := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar.unaligned")
+ rule.Temporary(temporary)
+
+ // use zip2zip to uncompress classes*.dex files
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ FlagWithInput("-i ", inputJar).
+ FlagWithOutput("-o ", temporary).
+ FlagWithArg("-0 ", "'classes*.dex'")
+
+ // use zipalign to align uncompressed classes*.dex files
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "zipalign")).
+ Flag("-f").
+ Text("4").
+ Input(temporary).
+ Output(dexOutputFile)
+
+ rule.DeleteTemporaryFiles()
+
+ rule.Build(pctx, ctx, "uncompress_dex", "uncompress dex")
+ } else {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: inputJar,
+ Output: dexOutputFile,
+ })
+ }
+
+ j.dexJarFile = dexOutputFile
+
+ dexOutputFile = j.dexpreopt(ctx, dexOutputFile)
+
+ j.maybeStrippedDexJarFile = dexOutputFile
+
+ ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
+ ctx.ModuleName()+".jar", dexOutputFile)
+}
+
+func (j *DexImport) DexJar() android.Path {
+ return j.dexJarFile
+}
+
+// dex_import imports a `.jar` file containing classes.dex files.
+//
+// A dex_import module cannot be used as a dependency of a java_* or android_* module, it can only be installed
+// to the device.
+func DexImportFactory() android.Module {
+ module := &DexImport{}
+
+ module.AddProperties(&module.properties)
+
+ android.InitPrebuiltModule(module, &module.properties.Jars)
+ InitJavaModule(module, android.DeviceSupported)
return module
}
@@ -1278,9 +2092,37 @@
func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
-func (d *Defaults) DepsMutator(ctx android.BottomUpMutatorContext) {
-}
-
+// java_defaults provides a set of properties that can be inherited by other java or android modules.
+//
+// A module can use the properties from a java_defaults module using `defaults: ["defaults_module_name"]`. Each
+// property in the defaults module that exists in the depending module will be prepended to the depending module's
+// value for that property.
+//
+// Example:
+//
+// java_defaults {
+// name: "example_defaults",
+// srcs: ["common/**/*.java"],
+// javacflags: ["-Xlint:all"],
+// aaptflags: ["--auto-add-overlay"],
+// }
+//
+// java_library {
+// name: "example",
+// defaults: ["example_defaults"],
+// srcs: ["example/**/*.java"],
+// }
+//
+// is functionally identical to:
+//
+// java_library {
+// name: "example",
+// srcs: [
+// "common/**/*.java",
+// "example/**/*.java",
+// ],
+// javacflags: ["-Xlint:all"],
+// }
func defaultsFactory() android.Module {
return DefaultsFactory()
}
@@ -1292,7 +2134,17 @@
module.AddProperties(
&CompilerProperties{},
&CompilerDeviceProperties{},
+ &DexpreoptProperties{},
&android.ProtoProperties{},
+ &aaptProperties{},
+ &androidLibraryProperties{},
+ &appProperties{},
+ &appTestProperties{},
+ &overridableAppProperties{},
+ &ImportProperties{},
+ &AARImportProperties{},
+ &sdkLibraryProperties{},
+ &DexImportProperties{},
)
android.InitDefaultsModule(module)
@@ -1301,5 +2153,6 @@
}
var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
var String = proptools.String
var inList = android.InList
diff --git a/java/java_resources.go b/java/java_resources.go
index a596fd7..7161168 100644
--- a/java/java_resources.go
+++ b/java/java_resources.go
@@ -19,6 +19,8 @@
"path/filepath"
"strings"
+ "github.com/google/blueprint/pathtools"
+
"android/soong/android"
)
@@ -32,31 +34,40 @@
}
func ResourceDirsToJarArgs(ctx android.ModuleContext,
- resourceDirs, excludeDirs []string) (args []string, deps android.Paths) {
- var excludes []string
+ resourceDirs, excludeResourceDirs, excludeResourceFiles []string) (args []string, deps android.Paths) {
+ var excludeDirs []string
+ var excludeFiles []string
- for _, exclude := range excludeDirs {
- excludes = append(excludes,
- filepath.Join(android.PathForModuleSrc(ctx, exclude).String(), "**/*"))
+ for _, exclude := range excludeResourceDirs {
+ dirs := ctx.Glob(android.PathForSource(ctx, ctx.ModuleDir()).Join(ctx, exclude).String(), nil)
+ for _, dir := range dirs {
+ excludeDirs = append(excludeDirs, dir.String())
+ excludeFiles = append(excludeFiles, dir.(android.SourcePath).Join(ctx, "**/*").String())
+ }
}
- excludes = append(excludes, resourceExcludes...)
+ excludeFiles = append(excludeFiles, android.PathsForModuleSrc(ctx, excludeResourceFiles).Strings()...)
- for _, dir := range resourceDirs {
- dir := android.PathForModuleSrc(ctx, dir).String()
- files := ctx.Glob(filepath.Join(dir, "**/*"), excludes)
+ excludeFiles = append(excludeFiles, resourceExcludes...)
- deps = append(deps, files...)
+ for _, resourceDir := range resourceDirs {
+ // resourceDir may be a glob, resolve it first
+ dirs := ctx.Glob(android.PathForSource(ctx, ctx.ModuleDir()).Join(ctx, resourceDir).String(), excludeDirs)
+ for _, dir := range dirs {
+ files := ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), excludeFiles)
- if len(files) > 0 {
- args = append(args, "-C", dir)
+ deps = append(deps, files...)
- for _, f := range files {
- path := f.String()
- if !strings.HasPrefix(path, dir) {
- panic(fmt.Errorf("path %q does not start with %q", path, dir))
+ if len(files) > 0 {
+ args = append(args, "-C", dir.String())
+
+ for _, f := range files {
+ path := f.String()
+ if !strings.HasPrefix(path, dir.String()) {
+ panic(fmt.Errorf("path %q does not start with %q", path, dir))
+ }
+ args = append(args, "-f", pathtools.MatchEscape(path))
}
- args = append(args, "-f", path)
}
}
}
@@ -85,7 +96,7 @@
func resourceFilesToJarArgs(ctx android.ModuleContext,
res, exclude []string) (args []string, deps android.Paths) {
- files := ctx.ExpandSources(res, exclude)
+ files := android.PathsForModuleSrcExcludes(ctx, res, exclude)
lastDir := ""
for i, f := range files {
@@ -98,7 +109,7 @@
if i == 0 || dir != lastDir {
args = append(args, "-C", dir)
}
- args = append(args, "-f", path)
+ args = append(args, "-f", pathtools.MatchEscape(path))
lastDir = dir
}
diff --git a/java/java_test.go b/java/java_test.go
index fb8cc94..50b2f69 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -15,16 +15,17 @@
package java
import (
- "android/soong/android"
- "android/soong/genrule"
- "fmt"
"io/ioutil"
"os"
"path/filepath"
- "reflect"
"strconv"
"strings"
"testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/dexpreopt"
+ "android/soong/genrule"
)
var buildDir string
@@ -53,16 +54,7 @@
}
func testConfig(env map[string]string) android.Config {
- if env == nil {
- env = make(map[string]string)
- }
- if env["ANDROID_JAVA8_HOME"] == "" {
- env["ANDROID_JAVA8_HOME"] = "jdk8"
- }
- config := android.TestArchConfig(buildDir, env)
- config.TestProductVariables.DeviceSystemSdkVersions = &[]string{"14", "15"}
- return config
-
+ return TestConfig(buildDir, env)
}
func testContext(config android.Config, bp string,
@@ -70,96 +62,113 @@
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
+ ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(AndroidAppCertificateFactory))
ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory))
+ ctx.RegisterModuleType("android_test", android.ModuleFactoryAdaptor(AndroidTestFactory))
+ ctx.RegisterModuleType("android_test_helper_app", android.ModuleFactoryAdaptor(AndroidTestHelperAppFactory))
+ ctx.RegisterModuleType("java_binary", android.ModuleFactoryAdaptor(BinaryFactory))
ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
- ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory(true)))
+ ctx.RegisterModuleType("java_device_for_host", android.ModuleFactoryAdaptor(DeviceForHostFactory))
+ ctx.RegisterModuleType("java_host_for_device", android.ModuleFactoryAdaptor(HostForDeviceFactory))
+ ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory))
ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
+ ctx.RegisterModuleType("java_test", android.ModuleFactoryAdaptor(TestFactory))
ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
+ ctx.RegisterModuleType("java_import_host", android.ModuleFactoryAdaptor(ImportFactoryHost))
ctx.RegisterModuleType("java_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(SystemModulesFactory))
ctx.RegisterModuleType("java_genrule", android.ModuleFactoryAdaptor(genRuleFactory))
- ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(genrule.FileGroupFactory))
+ ctx.RegisterModuleType("java_plugin", android.ModuleFactoryAdaptor(PluginFactory))
+ ctx.RegisterModuleType("dex_import", android.ModuleFactoryAdaptor(DexImportFactory))
+ ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(genrule.GenRuleFactory))
ctx.RegisterModuleType("droiddoc", android.ModuleFactoryAdaptor(DroiddocFactory))
ctx.RegisterModuleType("droiddoc_host", android.ModuleFactoryAdaptor(DroiddocHostFactory))
- ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(DroiddocTemplateFactory))
+ ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(ExportedDroiddocDirFactory))
+ ctx.RegisterModuleType("java_sdk_library", android.ModuleFactoryAdaptor(SdkLibraryFactory))
+ ctx.RegisterModuleType("override_android_app", android.ModuleFactoryAdaptor(OverrideAndroidAppModuleFactory))
+ ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(PrebuiltApisFactory))
+ ctx.RegisterModuleType("android_app_set", android.ModuleFactoryAdaptor(AndroidApkSetFactory))
ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(android.RegisterOverridePreArchMutators)
+ ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("prebuilt_apis", PrebuiltApisMutator).Parallel()
+ ctx.TopDown("java_sdk_library", SdkLibraryMutator).Parallel()
+ })
ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
- ctx.Register()
+ ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(sdkPreSingletonFactory))
- extraModules := []string{
- "core-oj",
- "core-libart",
- "framework",
- "ext",
- "okhttp",
- "android_stubs_current",
- "android_system_stubs_current",
- "android_test_stubs_current",
- "kotlin-stdlib",
- }
+ // Register module types and mutators from cc needed for JNI testing
+ ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+ ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
+ ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+ ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
+ ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("link", cc.LinkageMutator).Parallel()
+ ctx.BottomUp("begin", cc.BeginMutator).Parallel()
+ })
- for _, extra := range extraModules {
- bp += fmt.Sprintf(`
- java_library {
- name: "%s",
- srcs: ["a.java"],
- no_standard_libs: true,
- system_modules: "core-system-modules",
- }
- `, extra)
- }
-
- bp += `
- android_app {
- name: "framework-res",
- no_framework_libs: true,
- }
- `
-
- systemModules := []string{
- "core-system-modules",
- "android_stubs_current_system_modules",
- "android_system_stubs_current_system_modules",
- "android_test_stubs_current_system_modules",
- }
-
- for _, extra := range systemModules {
- bp += fmt.Sprintf(`
- java_system_modules {
- name: "%s",
- }
- `, extra)
- }
+ bp += GatherRequiredDepsForTest()
mockFS := map[string][]byte{
- "Android.bp": []byte(bp),
- "a.java": nil,
- "b.java": nil,
- "c.java": nil,
- "b.kt": nil,
- "a.jar": nil,
- "b.jar": nil,
- "java-res/a": nil,
- "java-res/b": nil,
- "java-res2/a": nil,
- "java-fg/a.java": nil,
- "java-fg/b.java": nil,
- "java-fg/c.java": nil,
+ "Android.bp": []byte(bp),
+ "a.java": nil,
+ "b.java": nil,
+ "c.java": nil,
+ "b.kt": nil,
+ "a.jar": nil,
+ "b.jar": nil,
+ "APP_NOTICE": nil,
+ "GENRULE_NOTICE": nil,
+ "LIB_NOTICE": nil,
+ "TOOL_NOTICE": nil,
+ "java-res/a/a": nil,
+ "java-res/b/b": nil,
+ "java-res2/a": nil,
+ "java-fg/a.java": nil,
+ "java-fg/b.java": nil,
+ "java-fg/c.java": nil,
+ "api/current.txt": nil,
+ "api/removed.txt": nil,
+ "api/system-current.txt": nil,
+ "api/system-removed.txt": nil,
+ "api/test-current.txt": nil,
+ "api/test-removed.txt": nil,
+ "framework/aidl/a.aidl": nil,
- "prebuilts/sdk/14/android.jar": nil,
- "prebuilts/sdk/14/framework.aidl": nil,
- "prebuilts/sdk/current/android.jar": nil,
- "prebuilts/sdk/current/framework.aidl": nil,
- "prebuilts/sdk/current/core.jar": nil,
- "prebuilts/sdk/system_current/android.jar": nil,
- "prebuilts/sdk/system_current/framework.aidl": nil,
- "prebuilts/sdk/system_14/android.jar": nil,
- "prebuilts/sdk/system_14/framework.aidl": nil,
- "prebuilts/sdk/test_current/android.jar": nil,
- "prebuilts/sdk/test_current/framework.aidl": nil,
+ "prebuilts/sdk/14/public/android.jar": nil,
+ "prebuilts/sdk/14/public/framework.aidl": nil,
+ "prebuilts/sdk/14/system/android.jar": nil,
+ "prebuilts/sdk/17/public/android.jar": nil,
+ "prebuilts/sdk/17/public/framework.aidl": nil,
+ "prebuilts/sdk/17/system/android.jar": nil,
+ "prebuilts/sdk/25/public/android.jar": nil,
+ "prebuilts/sdk/25/public/framework.aidl": nil,
+ "prebuilts/sdk/25/system/android.jar": nil,
+ "prebuilts/sdk/current/core/android.jar": nil,
+ "prebuilts/sdk/current/public/android.jar": nil,
+ "prebuilts/sdk/current/public/framework.aidl": nil,
+ "prebuilts/sdk/current/public/core.jar": nil,
+ "prebuilts/sdk/current/system/android.jar": nil,
+ "prebuilts/sdk/current/test/android.jar": nil,
+ "prebuilts/sdk/28/public/api/foo.txt": nil,
+ "prebuilts/sdk/28/system/api/foo.txt": nil,
+ "prebuilts/sdk/28/test/api/foo.txt": nil,
+ "prebuilts/sdk/28/public/api/foo-removed.txt": nil,
+ "prebuilts/sdk/28/system/api/foo-removed.txt": nil,
+ "prebuilts/sdk/28/test/api/foo-removed.txt": nil,
+ "prebuilts/sdk/28/public/api/bar.txt": nil,
+ "prebuilts/sdk/28/system/api/bar.txt": nil,
+ "prebuilts/sdk/28/test/api/bar.txt": nil,
+ "prebuilts/sdk/28/public/api/bar-removed.txt": nil,
+ "prebuilts/sdk/28/system/api/bar-removed.txt": nil,
+ "prebuilts/sdk/28/test/api/bar-removed.txt": nil,
+ "prebuilts/sdk/tools/core-lambda-stubs.jar": nil,
+ "prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),
+
+ "prebuilts/apks/app.apks": nil,
// For framework-res, which is an implicit dependency for framework
"AndroidManifest.xml": nil,
@@ -172,11 +181,16 @@
"jdk8/jre/lib/jce.jar": nil,
"jdk8/jre/lib/rt.jar": nil,
+ "jdk8/lib/tools.jar": nil,
"bar-doc/a.java": nil,
"bar-doc/b.java": nil,
+ "bar-doc/IFoo.aidl": nil,
"bar-doc/known_oj_tags.txt": nil,
"external/doclava/templates-sdk": nil,
+
+ "cert/new_cert.x509.pem": nil,
+ "cert/new_cert.pk8": nil,
}
for k, v := range fs {
@@ -190,12 +204,38 @@
func run(t *testing.T, ctx *android.TestContext, config android.Config) {
t.Helper()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+
+ pathCtx := android.PathContextForTesting(config, nil)
+ setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
+
+ ctx.Register()
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
}
+func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
+ t.Helper()
+ config := testConfig(nil)
+ ctx := testContext(config, bp, nil)
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return ctx, config
+ }
+ _, errs = ctx.PrepareBuildActions(config)
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return ctx, config
+ }
+
+ t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+
+ return ctx, config
+}
+
func testJava(t *testing.T, bp string) *android.TestContext {
t.Helper()
config := testConfig(nil)
@@ -211,9 +251,6 @@
return name
case strings.HasSuffix(name, ".jar"):
return name
- case name == "android_stubs_current" || name == "android_system_stubs_current" ||
- name == "android_test_stubs_current":
- return filepath.Join(buildDir, ".intermediates", name, "android_common", "javac", name+".jar")
default:
return filepath.Join(buildDir, ".intermediates", name, "android_common", "turbine-combined", name+".jar")
}
@@ -311,218 +348,6 @@
}
-var classpathTestcases = []struct {
- name string
- moduleType string
- host android.OsClass
- properties string
- bootclasspath []string
- system string
- classpath []string
-}{
- {
- name: "default",
- bootclasspath: []string{"core-oj", "core-libart"},
- system: "core-system-modules",
- classpath: []string{"ext", "framework", "okhttp"},
- },
- {
- name: "blank sdk version",
- properties: `sdk_version: "",`,
- bootclasspath: []string{"core-oj", "core-libart"},
- system: "core-system-modules",
- classpath: []string{"ext", "framework", "okhttp"},
- },
- {
-
- name: "sdk v14",
- properties: `sdk_version: "14",`,
- bootclasspath: []string{`""`},
- system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/14/android.jar"},
- },
- {
-
- name: "current",
- properties: `sdk_version: "current",`,
- bootclasspath: []string{`""`},
- system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/current/android.jar"},
- },
- {
-
- name: "system_current",
- properties: `sdk_version: "system_current",`,
- bootclasspath: []string{`""`},
- system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/system_current/android.jar"},
- },
- {
-
- name: "system_14",
- properties: `sdk_version: "system_14",`,
- bootclasspath: []string{`""`},
- system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/system_14/android.jar"},
- },
- {
-
- name: "test_current",
- properties: `sdk_version: "test_current",`,
- bootclasspath: []string{`""`},
- system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/test_current/android.jar"},
- },
- {
-
- name: "core_current",
- properties: `sdk_version: "core_current",`,
- bootclasspath: []string{`""`},
- system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/current/core.jar"},
- },
- {
-
- name: "nostdlib",
- properties: `no_standard_libs: true, system_modules: "none"`,
- system: "none",
- bootclasspath: []string{`""`},
- classpath: []string{},
- },
- {
-
- name: "nostdlib system_modules",
- properties: `no_standard_libs: true, system_modules: "core-system-modules"`,
- system: "core-system-modules",
- bootclasspath: []string{`""`},
- classpath: []string{},
- },
- {
-
- name: "host default",
- moduleType: "java_library_host",
- properties: ``,
- host: android.Host,
- bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
- classpath: []string{},
- },
- {
- name: "host nostdlib",
- moduleType: "java_library_host",
- host: android.Host,
- properties: `no_standard_libs: true`,
- classpath: []string{},
- },
- {
-
- name: "host supported default",
- host: android.Host,
- properties: `host_supported: true,`,
- classpath: []string{},
- bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
- },
- {
- name: "host supported nostdlib",
- host: android.Host,
- properties: `host_supported: true, no_standard_libs: true, system_modules: "none"`,
- classpath: []string{},
- },
-}
-
-func TestClasspath(t *testing.T) {
- for _, testcase := range classpathTestcases {
- t.Run(testcase.name, func(t *testing.T) {
- moduleType := "java_library"
- if testcase.moduleType != "" {
- moduleType = testcase.moduleType
- }
-
- bp := moduleType + ` {
- name: "foo",
- srcs: ["a.java"],
- ` + testcase.properties + `
- }`
-
- variant := "android_common"
- if testcase.host == android.Host {
- variant = android.BuildOs.String() + "_common"
- }
-
- convertModulesToPaths := func(cp []string) []string {
- ret := make([]string, len(cp))
- for i, e := range cp {
- ret[i] = moduleToPath(e)
- }
- return ret
- }
-
- bootclasspath := convertModulesToPaths(testcase.bootclasspath)
- classpath := convertModulesToPaths(testcase.classpath)
-
- bc := strings.Join(bootclasspath, ":")
- if bc != "" {
- bc = "-bootclasspath " + bc
- }
-
- c := strings.Join(classpath, ":")
- if c != "" {
- c = "-classpath " + c
- }
- system := ""
- if testcase.system == "none" {
- system = "--system=none"
- } else if testcase.system != "" {
- system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system") + "/"
- }
-
- t.Run("1.8", func(t *testing.T) {
- // Test default javac 1.8
- ctx := testJava(t, bp)
-
- javac := ctx.ModuleForTests("foo", variant).Rule("javac")
-
- got := javac.Args["bootClasspath"]
- if got != bc {
- t.Errorf("bootclasspath expected %q != got %q", bc, got)
- }
-
- got = javac.Args["classpath"]
- if got != c {
- t.Errorf("classpath expected %q != got %q", c, got)
- }
-
- var deps []string
- if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
- deps = append(deps, bootclasspath...)
- }
- deps = append(deps, classpath...)
-
- if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
- t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
- }
- })
-
- // Test again with javac 1.9
- t.Run("1.9", func(t *testing.T) {
- config := testConfig(map[string]string{"EXPERIMENTAL_USE_OPENJDK9": "true"})
- ctx := testContext(config, bp, nil)
- run(t, ctx, config)
-
- javac := ctx.ModuleForTests("foo", variant).Rule("javac")
- got := javac.Args["bootClasspath"]
- expected := system
- if testcase.system == "bootclasspath" {
- expected = bc
- }
- if got != expected {
- t.Errorf("bootclasspath expected %q != got %q", expected, got)
- }
- })
- })
- }
-
-}
-
func TestPrebuilts(t *testing.T) {
ctx := testJava(t, `
java_library {
@@ -541,19 +366,27 @@
name: "baz",
jars: ["b.jar"],
}
+
+ dex_import {
+ name: "qux",
+ jars: ["b.jar"],
+ }
`)
javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
+ barJar := ctx.ModuleForTests("bar", "android_common").Rule("combineJar").Output
+ bazJar := ctx.ModuleForTests("baz", "android_common").Rule("combineJar").Output
- bar := "a.jar"
- if !strings.Contains(javac.Args["classpath"], bar) {
- t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], bar)
+ if !strings.Contains(javac.Args["classpath"], barJar.String()) {
+ t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String())
}
- if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != "b.jar" {
- t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, "b.jar")
+ if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != bazJar.String() {
+ t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
}
+
+ ctx.ModuleForTests("qux", "android_common").Rule("Cp")
}
func TestDefaults(t *testing.T) {
@@ -563,6 +396,7 @@
srcs: ["a.java"],
libs: ["bar"],
static_libs: ["baz"],
+ optimize: {enabled: false},
}
java_library {
@@ -579,6 +413,22 @@
name: "baz",
srcs: ["c.java"],
}
+
+ android_test {
+ name: "atestOptimize",
+ defaults: ["defaults"],
+ optimize: {enabled: true},
+ }
+
+ android_test {
+ name: "atestNoOptimize",
+ defaults: ["defaults"],
+ }
+
+ android_test {
+ name: "atestDefault",
+ srcs: ["a.java"],
+ }
`)
javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
@@ -597,6 +447,21 @@
if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
}
+
+ atestOptimize := ctx.ModuleForTests("atestOptimize", "android_common").MaybeRule("r8")
+ if atestOptimize.Output == nil {
+ t.Errorf("atestOptimize should optimize APK")
+ }
+
+ atestNoOptimize := ctx.ModuleForTests("atestNoOptimize", "android_common").MaybeRule("d8")
+ if atestNoOptimize.Output == nil {
+ t.Errorf("atestNoOptimize should not optimize APK")
+ }
+
+ atestDefault := ctx.ModuleForTests("atestDefault", "android_common").MaybeRule("r8")
+ if atestDefault.Output == nil {
+ t.Errorf("atestDefault should optimize APK")
+ }
}
func TestResources(t *testing.T) {
@@ -610,13 +475,13 @@
// Test that a module with java_resource_dirs includes the files
name: "resource dirs",
prop: `java_resource_dirs: ["java-res"]`,
- args: "-C java-res -f java-res/a -f java-res/b",
+ args: "-C java-res -f java-res/a/a -f java-res/b/b",
},
{
// Test that a module with java_resources includes the files
name: "resource files",
- prop: `java_resources: ["java-res/a", "java-res/b"]`,
- args: "-C . -f java-res/a -f java-res/b",
+ prop: `java_resources: ["java-res/a/a", "java-res/b/b"]`,
+ args: "-C . -f java-res/a/a -f java-res/b/b",
},
{
// Test that a module with a filegroup in java_resources includes the files with the
@@ -627,9 +492,9 @@
filegroup {
name: "foo-res",
path: "java-res",
- srcs: ["java-res/a", "java-res/b"],
+ srcs: ["java-res/a/a", "java-res/b/b"],
}`,
- args: "-C java-res -f java-res/a -f java-res/b",
+ args: "-C java-res -f java-res/a/a -f java-res/b/b",
},
{
// Test that a module with "include_srcs: true" includes its source files in the resources jar
@@ -637,6 +502,42 @@
prop: `include_srcs: true`,
args: "-C . -f a.java -f b.java -f c.java",
},
+ {
+ // Test that a module with wildcards in java_resource_dirs has the correct path prefixes
+ name: "wildcard dirs",
+ prop: `java_resource_dirs: ["java-res/*"]`,
+ args: "-C java-res/a -f java-res/a/a -C java-res/b -f java-res/b/b",
+ },
+ {
+ // Test that a module exclude_java_resource_dirs excludes the files
+ name: "wildcard dirs",
+ prop: `java_resource_dirs: ["java-res/*"], exclude_java_resource_dirs: ["java-res/b"]`,
+ args: "-C java-res/a -f java-res/a/a",
+ },
+ {
+ // Test wildcards in java_resources
+ name: "wildcard files",
+ prop: `java_resources: ["java-res/**/*"]`,
+ args: "-C . -f java-res/a/a -f java-res/b/b",
+ },
+ {
+ // Test exclude_java_resources with java_resources
+ name: "wildcard files with exclude",
+ prop: `java_resources: ["java-res/**/*"], exclude_java_resources: ["java-res/b/*"]`,
+ args: "-C . -f java-res/a/a",
+ },
+ {
+ // Test exclude_java_resources with java_resource_dirs
+ name: "resource dirs with exclude files",
+ prop: `java_resource_dirs: ["java-res"], exclude_java_resources: ["java-res/b/b"]`,
+ args: "-C java-res -f java-res/a/a",
+ },
+ {
+ // Test exclude_java_resource_dirs with java_resource_dirs
+ name: "resource dirs with exclude files",
+ prop: `java_resource_dirs: ["java-res", "java-res2"], exclude_java_resource_dirs: ["java-res2"]`,
+ args: "-C java-res -f java-res/a/a -f java-res/b/b",
+ },
}
for _, test := range table {
@@ -653,7 +554,7 @@
}
`+test.extra)
- foo := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
+ foo := ctx.ModuleForTests("foo", "android_common").Output("withres/foo.jar")
fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
if !inList(fooRes.Output.String(), foo.Inputs.Strings()) {
@@ -669,42 +570,6 @@
}
}
-func TestExcludeResources(t *testing.T) {
- ctx := testJava(t, `
- java_library {
- name: "foo",
- srcs: ["a.java"],
- java_resource_dirs: ["java-res", "java-res2"],
- exclude_java_resource_dirs: ["java-res2"],
- }
-
- java_library {
- name: "bar",
- srcs: ["a.java"],
- java_resources: ["java-res/*"],
- exclude_java_resources: ["java-res/b"],
- }
- `)
-
- fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
-
- expected := "-C java-res -f java-res/a -f java-res/b"
- if fooRes.Args["jarArgs"] != expected {
- t.Errorf("foo resource jar args %q is not %q",
- fooRes.Args["jarArgs"], expected)
-
- }
-
- barRes := ctx.ModuleForTests("bar", "android_common").Output("res/bar.jar")
-
- expected = "-C . -f java-res/a"
- if barRes.Args["jarArgs"] != expected {
- t.Errorf("bar resource jar args %q is not %q",
- barRes.Args["jarArgs"], expected)
-
- }
-}
-
func TestGeneratedSources(t *testing.T) {
ctx := testJava(t, `
java_library {
@@ -738,79 +603,19 @@
}
}
-func TestKotlin(t *testing.T) {
- ctx := testJava(t, `
- java_library {
- name: "foo",
- srcs: ["a.java", "b.kt"],
- }
-
- java_library {
- name: "bar",
- srcs: ["b.kt"],
- libs: ["foo"],
- static_libs: ["baz"],
- }
-
- java_library {
- name: "baz",
- srcs: ["c.java"],
- }
- `)
-
- fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
- fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
- fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
-
- if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
- fooKotlinc.Inputs[1].String() != "b.kt" {
- t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
- }
-
- if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
- t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
- }
-
- if !strings.Contains(fooJavac.Args["classpath"], fooKotlinc.Output.String()) {
- t.Errorf("foo classpath %v does not contain %q",
- fooJavac.Args["classpath"], fooKotlinc.Output.String())
- }
-
- if !inList(fooKotlinc.Output.String(), fooJar.Inputs.Strings()) {
- t.Errorf("foo jar inputs %v does not contain %q",
- fooJar.Inputs.Strings(), fooKotlinc.Output.String())
- }
-
- fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
- bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
- barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
-
- if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
- t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
- }
-
- if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
- t.Errorf(`expected %q in bar implicits %v`,
- fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
- }
-
- if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
- t.Errorf(`expected %q in bar implicits %v`,
- bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
- }
-}
-
func TestTurbine(t *testing.T) {
ctx := testJava(t, `
java_library {
name: "foo",
srcs: ["a.java"],
+ sdk_version: "14",
}
java_library {
name: "bar",
srcs: ["b.java"],
static_libs: ["foo"],
+ sdk_version: "14",
}
java_library {
@@ -841,9 +646,9 @@
if len(barTurbineCombined.Inputs) != 2 || barTurbineCombined.Inputs[1].String() != fooHeaderJar {
t.Errorf("bar turbine combineJar inputs %v does not contain %q", barTurbineCombined.Inputs, fooHeaderJar)
}
- if !strings.Contains(bazJavac.Args["classpath"], "prebuilts/sdk/14/android.jar") {
+ if !strings.Contains(bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar") {
t.Errorf("baz javac classpath %v does not contain %q", bazJavac.Args["classpath"],
- "prebuilts/sdk/14/android.jar")
+ "prebuilts/sdk/14/public/android.jar")
}
}
@@ -875,6 +680,7 @@
name: "bar-doc",
srcs: [
"bar-doc/*.java",
+ "bar-doc/IFoo.aidl",
],
exclude_srcs: [
"bar-doc/b.java"
@@ -897,6 +703,14 @@
if stubsJar != barDoc.Output.String() {
t.Errorf("expected stubs Jar [%q], got %q", stubsJar, barDoc.Output.String())
}
+ inputs := ctx.ModuleForTests("bar-doc", "android_common").Rule("javadoc").Inputs
+ var javaSrcs []string
+ for _, i := range inputs {
+ javaSrcs = append(javaSrcs, i.Base())
+ }
+ if len(javaSrcs) != 2 || javaSrcs[0] != "a.java" || javaSrcs[1] != "IFoo.java" {
+ t.Errorf("inputs of bar-doc must be []string{\"a.java\", \"IFoo.java\", but was %#v.", javaSrcs)
+ }
}
func TestJarGenrules(t *testing.T) {
@@ -978,3 +792,196 @@
t.Errorf(`foo inputs %v != ["java-fg/c.java"]`, javac.Inputs)
}
}
+
+func TestJavaSdkLibrary(t *testing.T) {
+ ctx := testJava(t, `
+ droiddoc_template {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["foo"],
+ }
+ java_sdk_library {
+ name: "bar",
+ srcs: ["a.java", "b.java"],
+ api_packages: ["bar"],
+ }
+ java_library {
+ name: "baz",
+ srcs: ["c.java"],
+ libs: ["foo", "bar"],
+ sdk_version: "system_current",
+ }
+ java_library {
+ name: "qux",
+ srcs: ["c.java"],
+ libs: ["baz"],
+ sdk_version: "system_current",
+ }
+ `)
+
+ // check the existence of the internal modules
+ ctx.ModuleForTests("foo", "android_common")
+ ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkSystemApiSuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkTestApiSuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkDocsSuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkSystemApiSuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkTestApiSuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_arm64_armv8-a")
+ ctx.ModuleForTests("foo.api.public.28", "")
+ ctx.ModuleForTests("foo.api.system.28", "")
+ ctx.ModuleForTests("foo.api.test.28", "")
+
+ bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
+ // tests if baz is actually linked to the stubs lib
+ if !strings.Contains(bazJavac.Args["classpath"], "foo.stubs.system.jar") {
+ t.Errorf("baz javac classpath %v does not contain %q", bazJavac.Args["classpath"],
+ "foo.stubs.system.jar")
+ }
+ // ... and not to the impl lib
+ if strings.Contains(bazJavac.Args["classpath"], "foo.jar") {
+ t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"],
+ "foo.jar")
+ }
+ // test if baz is not linked to the system variant of foo
+ if strings.Contains(bazJavac.Args["classpath"], "foo.stubs.jar") {
+ t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"],
+ "foo.stubs.jar")
+ }
+
+ // test if baz has exported SDK lib names foo and bar to qux
+ qux := ctx.ModuleForTests("qux", "android_common")
+ if quxLib, ok := qux.Module().(*Library); ok {
+ sdkLibs := quxLib.ExportedSdkLibs()
+ if len(sdkLibs) != 2 || !android.InList("foo", sdkLibs) || !android.InList("bar", sdkLibs) {
+ t.Errorf("qux should export \"foo\" and \"bar\" but exports %v", sdkLibs)
+ }
+ }
+}
+
+var compilerFlagsTestCases = []struct {
+ in string
+ out bool
+}{
+ {
+ in: "a",
+ out: false,
+ },
+ {
+ in: "-a",
+ out: true,
+ },
+ {
+ in: "-no-jdk",
+ out: false,
+ },
+ {
+ in: "-no-stdlib",
+ out: false,
+ },
+ {
+ in: "-kotlin-home",
+ out: false,
+ },
+ {
+ in: "-kotlin-home /some/path",
+ out: false,
+ },
+ {
+ in: "-include-runtime",
+ out: false,
+ },
+ {
+ in: "-Xintellij-plugin-root",
+ out: false,
+ },
+}
+
+type mockContext struct {
+ android.ModuleContext
+ result bool
+}
+
+func (ctx *mockContext) PropertyErrorf(property, format string, args ...interface{}) {
+ // CheckBadCompilerFlags calls this function when the flag should be rejected
+ ctx.result = false
+}
+
+func TestCompilerFlags(t *testing.T) {
+ for _, testCase := range compilerFlagsTestCases {
+ ctx := &mockContext{result: true}
+ CheckKotlincFlags(ctx, []string{testCase.in})
+ if ctx.result != testCase.out {
+ t.Errorf("incorrect output:")
+ t.Errorf(" input: %#v", testCase.in)
+ t.Errorf(" expected: %#v", testCase.out)
+ t.Errorf(" got: %#v", ctx.result)
+ }
+ }
+}
+
+// TODO(jungjw): Consider making this more robust by ignoring path order.
+func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName string, expected string) {
+ variables := ctx.ModuleForTests(moduleName, "android_common").Module().VariablesForTests()
+ flags := strings.Split(variables["javacFlags"], " ")
+ got := ""
+ for _, flag := range flags {
+ keyEnd := strings.Index(flag, "=")
+ if keyEnd > -1 && flag[:keyEnd] == "--patch-module" {
+ got = flag[keyEnd+1:]
+ break
+ }
+ }
+ if expected != got {
+ t.Errorf("Unexpected patch-module flag for module %q - expected %q, but got %q", moduleName, expected, got)
+ }
+}
+
+func TestPatchModule(t *testing.T) {
+ bp := `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ no_standard_libs: true,
+ system_modules: "none",
+ patch_module: "java.base",
+ }
+
+ java_library {
+ name: "baz",
+ srcs: ["c.java"],
+ patch_module: "java.base",
+ }
+ `
+
+ t.Run("1.8", func(t *testing.T) {
+ // Test default javac 1.8
+ ctx := testJava(t, bp)
+
+ checkPatchModuleFlag(t, ctx, "foo", "")
+ checkPatchModuleFlag(t, ctx, "bar", "")
+ checkPatchModuleFlag(t, ctx, "baz", "")
+ })
+
+ t.Run("1.9", func(t *testing.T) {
+ // Test again with javac 1.9
+ config := testConfig(map[string]string{"EXPERIMENTAL_USE_OPENJDK9": "true"})
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ checkPatchModuleFlag(t, ctx, "foo", "")
+ expected := "java.base=.:" + buildDir
+ checkPatchModuleFlag(t, ctx, "bar", expected)
+ expected = "java.base=" + strings.Join([]string{".", buildDir, moduleToPath("ext"), moduleToPath("framework"), moduleToPath("updatable_media_stubs")}, ":")
+ checkPatchModuleFlag(t, ctx, "baz", expected)
+ })
+}
diff --git a/java/jdeps.go b/java/jdeps.go
new file mode 100644
index 0000000..18498be
--- /dev/null
+++ b/java/jdeps.go
@@ -0,0 +1,113 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+
+ "android/soong/android"
+)
+
+// This singleton generates android java dependency into to a json file. It does so for each
+// blueprint Android.bp resulting in a java.Module when either make, mm, mma, mmm or mmma is
+// called. Dependency info file is generated in $OUT/module_bp_java_depend.json.
+
+func init() {
+ android.RegisterSingletonType("jdeps_generator", jDepsGeneratorSingleton)
+}
+
+func jDepsGeneratorSingleton() android.Singleton {
+ return &jdepsGeneratorSingleton{}
+}
+
+type jdepsGeneratorSingleton struct {
+}
+
+const (
+ // Environment variables used to modify behavior of this singleton.
+ envVariableCollectJavaDeps = "SOONG_COLLECT_JAVA_DEPS"
+ jdepsJsonFileName = "module_bp_java_deps.json"
+)
+
+func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ if !ctx.Config().IsEnvTrue(envVariableCollectJavaDeps) {
+ return
+ }
+
+ moduleInfos := make(map[string]android.IdeInfo)
+
+ ctx.VisitAllModules(func(module android.Module) {
+ if !module.Enabled() {
+ return
+ }
+
+ ideInfoProvider, ok := module.(android.IDEInfo)
+ if !ok {
+ return
+ }
+ name := ideInfoProvider.BaseModuleName()
+ ideModuleNameProvider, ok := module.(android.IDECustomizedModuleName)
+ if ok {
+ name = ideModuleNameProvider.IDECustomizedModuleName()
+ }
+
+ dpInfo := moduleInfos[name]
+ ideInfoProvider.IDEInfo(&dpInfo)
+ dpInfo.Deps = android.FirstUniqueStrings(dpInfo.Deps)
+ dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
+ dpInfo.Aidl_include_dirs = android.FirstUniqueStrings(dpInfo.Aidl_include_dirs)
+ dpInfo.Jarjar_rules = android.FirstUniqueStrings(dpInfo.Jarjar_rules)
+ dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars)
+ moduleInfos[name] = dpInfo
+
+ mkProvider, ok := module.(android.AndroidMkDataProvider)
+ if !ok {
+ return
+ }
+ data := mkProvider.AndroidMk()
+ if data.Class != "" {
+ dpInfo.Classes = append(dpInfo.Classes, data.Class)
+ }
+
+ if dep, ok := module.(Dependency); ok {
+ dpInfo.Installed_paths = append(dpInfo.Installed_paths, dep.ImplementationJars().Strings()...)
+ }
+ dpInfo.Classes = android.FirstUniqueStrings(dpInfo.Classes)
+ dpInfo.Installed_paths = android.FirstUniqueStrings(dpInfo.Installed_paths)
+ moduleInfos[name] = dpInfo
+ })
+
+ jfpath := android.PathForOutput(ctx, jdepsJsonFileName).String()
+ err := createJsonFile(moduleInfos, jfpath)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+}
+
+func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath string) error {
+ file, err := os.Create(jfpath)
+ if err != nil {
+ return fmt.Errorf("Failed to create file: %s, relative: %v", jdepsJsonFileName, err)
+ }
+ defer file.Close()
+ buf, err := json.MarshalIndent(moduleInfos, "", "\t")
+ if err != nil {
+ return fmt.Errorf("Write file failed: %s, relative: %v", jdepsJsonFileName, err)
+ }
+ fmt.Fprintf(file, string(buf))
+ return nil
+}
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
new file mode 100644
index 0000000..874d1d7
--- /dev/null
+++ b/java/jdeps_test.go
@@ -0,0 +1,87 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestCollectJavaLibraryPropertiesAddLibsDeps(t *testing.T) {
+ expected := []string{"Foo", "Bar"}
+ module := LibraryFactory().(*Library)
+ module.properties.Libs = append(module.properties.Libs, expected...)
+ dpInfo := &android.IdeInfo{}
+
+ module.IDEInfo(dpInfo)
+
+ if !reflect.DeepEqual(dpInfo.Deps, expected) {
+ t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected)
+ }
+}
+
+func TestCollectJavaLibraryPropertiesAddStaticLibsDeps(t *testing.T) {
+ expected := []string{"Foo", "Bar"}
+ module := LibraryFactory().(*Library)
+ module.properties.Static_libs = append(module.properties.Static_libs, expected...)
+ dpInfo := &android.IdeInfo{}
+
+ module.IDEInfo(dpInfo)
+
+ if !reflect.DeepEqual(dpInfo.Deps, expected) {
+ t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected)
+ }
+}
+
+func TestCollectJavaLibraryPropertiesAddScrs(t *testing.T) {
+ expected := []string{"Foo", "Bar"}
+ module := LibraryFactory().(*Library)
+ module.expandIDEInfoCompiledSrcs = append(module.expandIDEInfoCompiledSrcs, expected...)
+ dpInfo := &android.IdeInfo{}
+
+ module.IDEInfo(dpInfo)
+
+ if !reflect.DeepEqual(dpInfo.Srcs, expected) {
+ t.Errorf("Library.IDEInfo() Srcs = %v, want %v", dpInfo.Srcs, expected)
+ }
+}
+
+func TestCollectJavaLibraryPropertiesAddAidlIncludeDirs(t *testing.T) {
+ expected := []string{"Foo", "Bar"}
+ module := LibraryFactory().(*Library)
+ module.deviceProperties.Aidl.Include_dirs = append(module.deviceProperties.Aidl.Include_dirs, expected...)
+ dpInfo := &android.IdeInfo{}
+
+ module.IDEInfo(dpInfo)
+
+ if !reflect.DeepEqual(dpInfo.Aidl_include_dirs, expected) {
+ t.Errorf("Library.IDEInfo() Aidl_include_dirs = %v, want %v", dpInfo.Aidl_include_dirs, expected)
+ }
+}
+
+func TestCollectJavaLibraryPropertiesAddJarjarRules(t *testing.T) {
+ expected := "Jarjar_rules.txt"
+ module := LibraryFactory().(*Library)
+ module.expandJarjarRules = android.PathForTesting(expected)
+ dpInfo := &android.IdeInfo{}
+
+ module.IDEInfo(dpInfo)
+
+ if dpInfo.Jarjar_rules[0] != expected {
+ t.Errorf("Library.IDEInfo() Jarjar_rules = %v, want %v", dpInfo.Jarjar_rules[0], expected)
+ }
+}
diff --git a/java/kotlin.go b/java/kotlin.go
new file mode 100644
index 0000000..58dc64c
--- /dev/null
+++ b/java/kotlin.go
@@ -0,0 +1,178 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/binary"
+ "strings"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports{Goma: true},
+ blueprint.RuleParams{
+ Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" && mkdir -p "$classesDir" "$srcJarDir" && ` +
+ `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `${config.GenKotlinBuildFileCmd} $classpath $classesDir $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
+ `${config.KotlincCmd} ${config.JavacHeapFlags} $kotlincFlags ` +
+ `-jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir && ` +
+ `rm -rf "$srcJarDir"`,
+ CommandDeps: []string{
+ "${config.KotlincCmd}",
+ "${config.KotlinCompilerJar}",
+ "${config.GenKotlinBuildFileCmd}",
+ "${config.SoongZipCmd}",
+ "${config.ZipSyncCmd}",
+ },
+ Rspfile: "$out.rsp",
+ RspfileContent: `$in`,
+ },
+ "kotlincFlags", "classpath", "srcJars", "srcJarDir", "classesDir", "kotlinJvmTarget", "kotlinBuildFile")
+
+// kotlinCompile takes .java and .kt sources and srcJars, and compiles the .kt sources into a classes jar in outputFile.
+func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars android.Paths,
+ flags javaBuilderFlags) {
+
+ var deps android.Paths
+ deps = append(deps, flags.kotlincClasspath...)
+ deps = append(deps, srcJars...)
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: kotlinc,
+ Description: "kotlinc",
+ Output: outputFile,
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: map[string]string{
+ "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"),
+ "kotlincFlags": flags.kotlincFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "classesDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
+ "kotlinBuildFile": android.PathForModuleOut(ctx, "kotlinc-build.xml").String(),
+ // http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
+ "kotlinJvmTarget": "1.8",
+ },
+ })
+}
+
+var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: true},
+ blueprint.RuleParams{
+ Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && mkdir -p "$srcJarDir" "$kaptDir" && ` +
+ `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `${config.GenKotlinBuildFileCmd} $classpath "" $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
+ `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} $kotlincFlags ` +
+ `-Xplugin=${config.KotlinKaptJar} ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:classes=$kaptDir/classes ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:stubs=$kaptDir/stubs ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:javacArguments=$encodedJavacFlags ` +
+ `$kaptProcessorPath ` +
+ `$kaptProcessor ` +
+ `-Xbuild-file=$kotlinBuildFile && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $kaptDir/sources -D $kaptDir/sources && ` +
+ `rm -rf "$srcJarDir"`,
+ CommandDeps: []string{
+ "${config.KotlincCmd}",
+ "${config.KotlinCompilerJar}",
+ "${config.KotlinKaptJar}",
+ "${config.GenKotlinBuildFileCmd}",
+ "${config.SoongZipCmd}",
+ "${config.ZipSyncCmd}",
+ },
+ Rspfile: "$out.rsp",
+ RspfileContent: `$in`,
+ },
+ "kotlincFlags", "encodedJavacFlags", "kaptProcessorPath", "kaptProcessor",
+ "classpath", "srcJars", "srcJarDir", "kaptDir", "kotlinJvmTarget", "kotlinBuildFile")
+
+// kotlinKapt performs Kotlin-compatible annotation processing. It takes .kt and .java sources and srcjars, and runs
+// annotation processors over all of them, producing a srcjar of generated code in outputFile. The srcjar should be
+// added as an additional input to kotlinc and javac rules, and the javac rule should have annotation processing
+// disabled.
+func kotlinKapt(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars android.Paths,
+ flags javaBuilderFlags) {
+
+ var deps android.Paths
+ deps = append(deps, flags.kotlincClasspath...)
+ deps = append(deps, srcJars...)
+ deps = append(deps, flags.processorPath...)
+
+ kaptProcessorPath := flags.processorPath.FormTurbineClasspath("-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=")
+
+ kaptProcessor := ""
+ if flags.processor != "" {
+ kaptProcessor = "-P plugin:org.jetbrains.kotlin.kapt3:processors=" + flags.processor
+ }
+
+ encodedJavacFlags := kaptEncodeFlags([][2]string{
+ {"-source", flags.javaVersion},
+ {"-target", flags.javaVersion},
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: kapt,
+ Description: "kapt",
+ Output: outputFile,
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: map[string]string{
+ "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"),
+ "kotlincFlags": flags.kotlincFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "srcJarDir": android.PathForModuleOut(ctx, "kapt", "srcJars").String(),
+ "kotlinBuildFile": android.PathForModuleOut(ctx, "kapt", "build.xml").String(),
+ "kaptProcessorPath": strings.Join(kaptProcessorPath, " "),
+ "kaptProcessor": kaptProcessor,
+ "kaptDir": android.PathForModuleOut(ctx, "kapt/gen").String(),
+ "encodedJavacFlags": encodedJavacFlags,
+ },
+ })
+}
+
+// kapt converts a list of key, value pairs into a base64 encoded Java serialization, which is what kapt expects.
+func kaptEncodeFlags(options [][2]string) string {
+ buf := &bytes.Buffer{}
+
+ binary.Write(buf, binary.BigEndian, uint32(len(options)))
+ for _, option := range options {
+ binary.Write(buf, binary.BigEndian, uint16(len(option[0])))
+ buf.WriteString(option[0])
+ binary.Write(buf, binary.BigEndian, uint16(len(option[1])))
+ buf.WriteString(option[1])
+ }
+
+ header := &bytes.Buffer{}
+ header.Write([]byte{0xac, 0xed, 0x00, 0x05}) // java serialization header
+
+ if buf.Len() < 256 {
+ header.WriteByte(0x77) // blockdata
+ header.WriteByte(byte(buf.Len()))
+ } else {
+ header.WriteByte(0x7a) // blockdatalong
+ binary.Write(header, binary.BigEndian, uint32(buf.Len()))
+ }
+
+ return base64.StdEncoding.EncodeToString(append(header.Bytes(), buf.Bytes()...))
+}
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
new file mode 100644
index 0000000..e0eb0c0
--- /dev/null
+++ b/java/kotlin_test.go
@@ -0,0 +1,214 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestKotlin(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.kt"],
+ libs: ["foo"],
+ static_libs: ["baz"],
+ }
+
+ java_library {
+ name: "baz",
+ srcs: ["c.java"],
+ }
+ `)
+
+ fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
+ fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
+
+ if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
+ fooKotlinc.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
+ }
+
+ if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
+ }
+
+ if !strings.Contains(fooJavac.Args["classpath"], fooKotlinc.Output.String()) {
+ t.Errorf("foo classpath %v does not contain %q",
+ fooJavac.Args["classpath"], fooKotlinc.Output.String())
+ }
+
+ if !inList(fooKotlinc.Output.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %q",
+ fooJar.Inputs.Strings(), fooKotlinc.Output.String())
+ }
+
+ fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
+ bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
+ barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
+
+ if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
+ t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
+ }
+
+ if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
+ t.Errorf(`expected %q in bar implicits %v`,
+ fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
+ }
+
+ if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
+ t.Errorf(`expected %q in bar implicits %v`,
+ bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
+ }
+}
+
+func TestKapt(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ plugins: ["bar"],
+ }
+
+ java_plugin {
+ name: "bar",
+ processor_class: "com.bar",
+ srcs: ["b.java"],
+ }
+ `)
+
+ buildOS := android.BuildOs.String()
+
+ kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
+ kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
+ javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+
+ bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+
+ // Test that the kotlin and java sources are passed to kapt and kotlinc
+ if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs)
+ }
+ if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
+ }
+
+ // Test that only the java sources are passed to javac
+ if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+ }
+
+ // Test that the kapt srcjar is a dependency of kotlinc and javac rules
+ if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) {
+ t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings())
+ }
+ if !inList(kapt.Output.String(), javac.Implicits.Strings()) {
+ t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings())
+ }
+
+ // Test that the kapt srcjar is extracted by the kotlinc and javac rules
+ if kotlinc.Args["srcJars"] != kapt.Output.String() {
+ t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ }
+ if javac.Args["srcJars"] != kapt.Output.String() {
+ t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ }
+
+ // Test that the processors are passed to kapt
+ expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar
+ if kapt.Args["kaptProcessorPath"] != expectedProcessorPath {
+ t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"])
+ }
+ expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar"
+ if kapt.Args["kaptProcessor"] != expectedProcessor {
+ t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"])
+ }
+
+ // Test that the processors are not passed to javac
+ if javac.Args["processorPath"] != "" {
+ t.Errorf("expected processorPath '', got %q", javac.Args["processorPath"])
+ }
+ if javac.Args["processor"] != "-proc:none" {
+ t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"])
+ }
+}
+
+func TestKaptEncodeFlags(t *testing.T) {
+ // Compares the kaptEncodeFlags against the results of the example implementation at
+ // https://kotlinlang.org/docs/reference/kapt.html#apjavac-options-encoding
+ tests := []struct {
+ in [][2]string
+ out string
+ }{
+ {
+ // empty input
+ in: [][2]string{},
+ out: "rO0ABXcEAAAAAA==",
+ },
+ {
+ // common input
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ },
+ out: "rO0ABXcgAAAAAgAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjg=",
+ },
+ {
+ // input that serializes to a 255 byte block
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ {"a", strings.Repeat("b", 218)},
+ },
+ out: "rO0ABXf/AAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA2mJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJi",
+ },
+ {
+ // input that serializes to a 256 byte block
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ {"a", strings.Repeat("b", 219)},
+ },
+ out: "rO0ABXoAAAEAAAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA22JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYg==",
+ },
+ {
+ // input that serializes to a 257 byte block
+ in: [][2]string{
+ {"-source", "1.8"},
+ {"-target", "1.8"},
+ {"a", strings.Repeat("b", 220)},
+ },
+ out: "rO0ABXoAAAEBAAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA3GJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI=",
+ },
+ }
+
+ for i, test := range tests {
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ got := kaptEncodeFlags(test.in)
+ if got != test.out {
+ t.Errorf("\nwant %q\n got %q", test.out, got)
+ }
+ })
+ }
+}
diff --git a/java/plugin.go b/java/plugin.go
new file mode 100644
index 0000000..a5e8292
--- /dev/null
+++ b/java/plugin.go
@@ -0,0 +1,50 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import "android/soong/android"
+
+func init() {
+ android.RegisterModuleType("java_plugin", PluginFactory)
+}
+
+// A java_plugin module describes a host java library that will be used by javac as an annotation processor.
+func PluginFactory() android.Module {
+ module := &Plugin{}
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.protoProperties,
+ &module.pluginProperties)
+
+ InitJavaModule(module, android.HostSupported)
+ return module
+}
+
+type Plugin struct {
+ Library
+
+ pluginProperties PluginProperties
+}
+
+type PluginProperties struct {
+ // The optional name of the class that javac will use to run the annotation processor.
+ Processor_class *string
+
+ // If true, assume the annotation processor will generate classes that are referenced from outside the module.
+ // This necessitates disabling the turbine optimization on modules that use this plugin, which will reduce
+ // parallelism and cause more recompilation for modules that depend on modules that use this plugin.
+ Generates_api *bool
+}
diff --git a/java/plugin_test.go b/java/plugin_test.go
new file mode 100644
index 0000000..d1aef2c
--- /dev/null
+++ b/java/plugin_test.go
@@ -0,0 +1,123 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "testing"
+)
+
+func TestNoPlugin(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `)
+
+ javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+
+ if turbine.Rule == nil {
+ t.Errorf("expected turbine to be enabled")
+ }
+
+ if javac.Args["processsorpath"] != "" {
+ t.Errorf("want empty processorpath, got %q", javac.Args["processorpath"])
+ }
+
+ if javac.Args["processor"] != "-proc:none" {
+ t.Errorf("want '-proc:none' argument, got %q", javac.Args["processor"])
+ }
+}
+
+func TestPlugin(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ plugins: ["bar"],
+ }
+
+ java_plugin {
+ name: "bar",
+ processor_class: "com.bar",
+ srcs: ["b.java"],
+ }
+ `)
+
+ buildOS := android.BuildOs.String()
+
+ javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+
+ if turbine.Rule == nil {
+ t.Errorf("expected turbine to be enabled")
+ }
+
+ bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+
+ if !inList(bar, javac.Implicits.Strings()) {
+ t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
+ }
+
+ if javac.Args["processorpath"] != "-processorpath "+bar {
+ t.Errorf("foo processorpath %q != '-processorpath %s'", javac.Args["processorpath"], bar)
+ }
+
+ if javac.Args["processor"] != "-processor com.bar" {
+ t.Errorf("foo processor %q != '-processor com.bar'", javac.Args["processor"])
+ }
+}
+
+func TestPluginGeneratesApi(t *testing.T) {
+ ctx := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ plugins: ["bar"],
+ }
+
+ java_plugin {
+ name: "bar",
+ processor_class: "com.bar",
+ generates_api: true,
+ srcs: ["b.java"],
+ }
+ `)
+
+ buildOS := android.BuildOs.String()
+
+ javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+
+ if turbine.Rule != nil {
+ t.Errorf("expected turbine to be disabled")
+ }
+
+ bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+
+ if !inList(bar, javac.Implicits.Strings()) {
+ t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
+ }
+
+ if javac.Args["processorpath"] != "-processorpath "+bar {
+ t.Errorf("foo processorpath %q != '-processorpath %s'", javac.Args["processorpath"], bar)
+ }
+
+ if javac.Args["processor"] != "-processor com.bar" {
+ t.Errorf("foo processor %q != '-processor com.bar'", javac.Args["processor"])
+ }
+}
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
new file mode 100644
index 0000000..c370811
--- /dev/null
+++ b/java/prebuilt_apis.go
@@ -0,0 +1,195 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ android.RegisterModuleType("prebuilt_apis", PrebuiltApisFactory)
+
+ android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("prebuilt_apis", PrebuiltApisMutator).Parallel()
+ })
+}
+
+type prebuiltApisProperties struct {
+ // list of api version directories
+ Api_dirs []string
+}
+
+type prebuiltApis struct {
+ android.ModuleBase
+ properties prebuiltApisProperties
+}
+
+func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // no need to implement
+}
+
+func parseJarPath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) {
+ elements := strings.Split(path, "/")
+
+ apiver = elements[0]
+ scope = elements[1]
+
+ module = strings.TrimSuffix(elements[2], ".jar")
+ return
+}
+
+func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) {
+ elements := strings.Split(path, "/")
+ apiver = elements[0]
+
+ scope = elements[1]
+ if scope != "public" && scope != "system" && scope != "test" {
+ ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
+ return
+ }
+
+ // elements[2] is string literal "api". skipping.
+ module = strings.TrimSuffix(elements[3], ".txt")
+ return
+}
+
+func createImport(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+ props := struct {
+ Name *string
+ Jars []string
+ Sdk_version *string
+ Installable *bool
+ }{}
+ props.Name = proptools.StringPtr(mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module)
+ props.Jars = append(props.Jars, path)
+ // TODO(hansson): change to scope after migration is done.
+ props.Sdk_version = proptools.StringPtr("current")
+ props.Installable = proptools.BoolPtr(false)
+
+ mctx.CreateModule(android.ModuleFactoryAdaptor(ImportFactory), &props)
+}
+
+func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+ fgName := module + ".api." + scope + "." + apiver
+ filegroupProps := struct {
+ Name *string
+ Srcs []string
+ }{}
+ filegroupProps.Name = proptools.StringPtr(fgName)
+ filegroupProps.Srcs = []string{path}
+ mctx.CreateModule(android.ModuleFactoryAdaptor(android.FileGroupFactory), &filegroupProps)
+}
+
+func getPrebuiltFiles(mctx android.TopDownMutatorContext, name string) []string {
+ mydir := mctx.ModuleDir() + "/"
+ var files []string
+ for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
+ for _, scope := range []string{"public", "system", "test", "core"} {
+ vfiles, err := mctx.GlobWithDeps(mydir+apiver+"/"+scope+"/"+name, nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, mydir+apiver+"/"+scope, err)
+ }
+ files = append(files, vfiles...)
+ }
+ }
+ return files
+}
+
+func prebuiltSdkStubs(mctx android.TopDownMutatorContext) {
+ mydir := mctx.ModuleDir() + "/"
+ // <apiver>/<scope>/<module>.jar
+ files := getPrebuiltFiles(mctx, "*.jar")
+
+ for _, f := range files {
+ // create a Import module for each jar file
+ localPath := strings.TrimPrefix(f, mydir)
+ module, apiver, scope := parseJarPath(mctx, localPath)
+ createImport(mctx, module, scope, apiver, localPath)
+ }
+}
+
+func prebuiltApiFiles(mctx android.TopDownMutatorContext) {
+ mydir := mctx.ModuleDir() + "/"
+ // <apiver>/<scope>/api/<module>.txt
+ files := getPrebuiltFiles(mctx, "api/*.txt")
+
+ if len(files) == 0 {
+ mctx.ModuleErrorf("no api file found under %q", mydir)
+ }
+
+ // construct a map to find out the latest api file path
+ // for each (<module>, <scope>) pair.
+ type latestApiInfo struct {
+ module string
+ scope string
+ apiver string
+ path string
+ }
+ m := make(map[string]latestApiInfo)
+
+ for _, f := range files {
+ // create a filegroup for each api txt file
+ localPath := strings.TrimPrefix(f, mydir)
+ module, apiver, scope := parseApiFilePath(mctx, localPath)
+ createFilegroup(mctx, module, scope, apiver, localPath)
+
+ // find the latest apiver
+ key := module + "." + scope
+ info, ok := m[key]
+ if !ok {
+ m[key] = latestApiInfo{module, scope, apiver, localPath}
+ } else if len(apiver) > len(info.apiver) || (len(apiver) == len(info.apiver) &&
+ strings.Compare(apiver, info.apiver) > 0) {
+ info.apiver = apiver
+ info.path = localPath
+ m[key] = info
+ }
+ }
+ // create filegroups for the latest version of (<module>, <scope>) pairs
+ // sort the keys in order to make build.ninja stable
+ keys := make([]string, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ info := m[k]
+ createFilegroup(mctx, info.module, info.scope, "latest", info.path)
+ }
+}
+
+func PrebuiltApisMutator(mctx android.TopDownMutatorContext) {
+ if _, ok := mctx.Module().(*prebuiltApis); ok {
+ prebuiltApiFiles(mctx)
+ prebuiltSdkStubs(mctx)
+ }
+}
+
+// prebuilt_apis is a meta-module that generates filegroup modules for all
+// API txt files found under the directory where the Android.bp is located.
+// Specifically, an API file located at ./<ver>/<scope>/api/<module>.txt
+// generates a filegroup module named <module>-api.<scope>.<ver>.
+//
+// It also creates <module>-api.<scope>.latest for the latest <ver>.
+func PrebuiltApisFactory() android.Module {
+ module := &prebuiltApis{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ return module
+}
diff --git a/java/proto.go b/java/proto.go
index 2991ad9..37de1d2 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -15,104 +15,83 @@
package java
import (
- "strings"
-
- "github.com/google/blueprint"
- "github.com/google/blueprint/proptools"
-
"android/soong/android"
)
-func init() {
- pctx.HostBinToolVariable("protocCmd", "aprotoc")
-}
-
-var (
- proto = pctx.AndroidStaticRule("protoc",
- blueprint.RuleParams{
- Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
- `$protocCmd $protoOut=$protoOutParams:$out.tmp -I $protoBase $protoFlags $in && ` +
- `${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
- CommandDeps: []string{
- "$protocCmd",
- "${config.SoongZipCmd}",
- },
- }, "protoBase", "protoFlags", "protoOut", "protoOutParams")
-)
-
-func genProto(ctx android.ModuleContext, protoFile android.Path, flags javaBuilderFlags) android.Path {
+func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags) android.Path {
srcJarFile := android.GenPathWithExt(ctx, "proto", protoFile, "srcjar")
- var protoBase string
- if flags.protoRoot {
- protoBase = "."
- } else {
- protoBase = strings.TrimSuffix(protoFile.String(), protoFile.Rel())
- }
+ outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
+ depFile := srcJarFile.ReplaceExtension(ctx, "srcjar.d")
- ctx.Build(pctx, android.BuildParams{
- Rule: proto,
- Description: "protoc " + protoFile.Rel(),
- Output: srcJarFile,
- Input: protoFile,
- Args: map[string]string{
- "protoBase": protoBase,
- "protoOut": flags.protoOutTypeFlag,
- "protoOutParams": flags.protoOutParams,
- "protoFlags": strings.Join(flags.protoFlags, " "),
- },
- })
+ rule := android.NewRuleBuilder()
+
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+ rule.Command().Text("mkdir -p").Flag(outDir.String())
+
+ android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+
+ // Proto generated java files have an unknown package name in the path, so package the entire output directory
+ // into a srcjar.
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ Flag("-jar").
+ FlagWithOutput("-o ", srcJarFile).
+ FlagWithArg("-C ", outDir.String()).
+ FlagWithArg("-D ", outDir.String())
+
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+
+ rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
return srcJarFile
}
func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
- switch proptools.String(p.Proto.Type) {
- case "micro":
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-micro")
- case "nano":
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-nano")
- case "lite", "":
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-lite")
- case "full":
- if ctx.Host() {
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-full")
- } else {
- ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
+ if String(p.Proto.Plugin) == "" {
+ switch String(p.Proto.Type) {
+ case "micro":
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
+ case "nano":
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
+ case "lite", "":
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
+ case "full":
+ if ctx.Host() {
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-full")
+ } else {
+ ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
+ }
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
}
- default:
- ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- proptools.String(p.Proto.Type))
}
}
func protoFlags(ctx android.ModuleContext, j *CompilerProperties, p *android.ProtoProperties,
flags javaBuilderFlags) javaBuilderFlags {
- switch proptools.String(p.Proto.Type) {
- case "micro":
- flags.protoOutTypeFlag = "--javamicro_out"
- case "nano":
- flags.protoOutTypeFlag = "--javanano_out"
- case "lite":
- flags.protoOutTypeFlag = "--java_out"
- flags.protoOutParams = "lite"
- case "full", "":
- flags.protoOutTypeFlag = "--java_out"
- default:
- ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- proptools.String(p.Proto.Type))
- }
+ flags.proto = android.GetProtoFlags(ctx, p)
- if len(j.Proto.Output_params) > 0 {
- if flags.protoOutParams != "" {
- flags.protoOutParams += ","
+ if String(p.Proto.Plugin) == "" {
+ switch String(p.Proto.Type) {
+ case "micro":
+ flags.proto.OutTypeFlag = "--javamicro_out"
+ case "nano":
+ flags.proto.OutTypeFlag = "--javanano_out"
+ case "lite":
+ flags.proto.OutTypeFlag = "--java_out"
+ flags.proto.OutParams = append(flags.proto.OutParams, "lite")
+ case "full", "":
+ flags.proto.OutTypeFlag = "--java_out"
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
}
- flags.protoOutParams += strings.Join(j.Proto.Output_params, ",")
}
- flags.protoFlags = android.ProtoFlags(ctx, p)
- flags.protoRoot = android.ProtoCanonicalPathFromRoot(ctx, p)
+ flags.proto.OutParams = append(flags.proto.OutParams, j.Proto.Output_params...)
return flags
}
diff --git a/java/sdk.go b/java/sdk.go
new file mode 100644
index 0000000..e93f8fb
--- /dev/null
+++ b/java/sdk.go
@@ -0,0 +1,375 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "android/soong/java/config"
+ "fmt"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/google/blueprint/pathtools"
+)
+
+func init() {
+ android.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
+ android.RegisterSingletonType("sdk", sdkSingletonFactory)
+ android.RegisterMakeVarsProvider(pctx, sdkMakeVars)
+}
+
+var sdkVersionsKey = android.NewOnceKey("sdkVersionsKey")
+var sdkFrameworkAidlPathKey = android.NewOnceKey("sdkFrameworkAidlPathKey")
+var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
+
+type sdkContext interface {
+ // sdkVersion eturns the sdk_version property of the current module, or an empty string if it is not set.
+ sdkVersion() string
+ // minSdkVersion returns the min_sdk_version property of the current module, or sdkVersion() if it is not set.
+ minSdkVersion() string
+ // targetSdkVersion returns the target_sdk_version property of the current module, or sdkVersion() if it is not set.
+ targetSdkVersion() string
+}
+
+func sdkVersionOrDefault(ctx android.BaseContext, v string) string {
+ switch v {
+ case "", "current", "system_current", "test_current", "core_current":
+ return ctx.Config().DefaultAppTargetSdk()
+ default:
+ return v
+ }
+}
+
+// Returns a sdk version as a number. For modules targeting an unreleased SDK (meaning it does not yet have a number)
+// it returns android.FutureApiLevel (10000).
+func sdkVersionToNumber(ctx android.BaseContext, v string) (int, error) {
+ switch v {
+ case "", "current", "test_current", "system_current", "core_current":
+ return ctx.Config().DefaultAppTargetSdkInt(), nil
+ default:
+ n := android.GetNumericSdkVersion(v)
+ if i, err := strconv.Atoi(n); err != nil {
+ return -1, fmt.Errorf("invalid sdk version %q", n)
+ } else {
+ return i, nil
+ }
+ }
+}
+
+func sdkVersionToNumberAsString(ctx android.BaseContext, v string) (string, error) {
+ n, err := sdkVersionToNumber(ctx, v)
+ if err != nil {
+ return "", err
+ }
+ return strconv.Itoa(n), nil
+}
+
+func decodeSdkDep(ctx android.BaseContext, sdkContext sdkContext) sdkDep {
+ v := sdkContext.sdkVersion()
+ // For PDK builds, use the latest SDK version instead of "current"
+ if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
+ sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
+ latestSdkVersion := 0
+ if len(sdkVersions) > 0 {
+ latestSdkVersion = sdkVersions[len(sdkVersions)-1]
+ }
+ v = strconv.Itoa(latestSdkVersion)
+ }
+
+ numericSdkVersion, err := sdkVersionToNumber(ctx, v)
+ if err != nil {
+ ctx.PropertyErrorf("sdk_version", "%s", err)
+ return sdkDep{}
+ }
+
+ toPrebuilt := func(sdk string) sdkDep {
+ var api, v string
+ if strings.Contains(sdk, "_") {
+ t := strings.Split(sdk, "_")
+ api = t[0]
+ v = t[1]
+ } else {
+ api = "public"
+ v = sdk
+ }
+ dir := filepath.Join("prebuilts", "sdk", v, api)
+ jar := filepath.Join(dir, "android.jar")
+ // There's no aidl for other SDKs yet.
+ // TODO(77525052): Add aidl files for other SDKs too.
+ public_dir := filepath.Join("prebuilts", "sdk", v, "public")
+ aidl := filepath.Join(public_dir, "framework.aidl")
+ jarPath := android.ExistentPathForSource(ctx, jar)
+ aidlPath := android.ExistentPathForSource(ctx, aidl)
+ lambdaStubsPath := android.PathForSource(ctx, config.SdkLambdaStubsPath)
+
+ if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
+ return sdkDep{
+ invalidVersion: true,
+ modules: []string{fmt.Sprintf("sdk_%s_%s_android", api, v)},
+ }
+ }
+
+ if !jarPath.Valid() {
+ ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, jar)
+ return sdkDep{}
+ }
+
+ if !aidlPath.Valid() {
+ ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", v, aidl)
+ return sdkDep{}
+ }
+
+ return sdkDep{
+ useFiles: true,
+ jars: android.Paths{jarPath.Path(), lambdaStubsPath},
+ aidl: android.OptionalPathForPath(aidlPath.Path()),
+ }
+ }
+
+ toModule := func(m, r string, aidl android.Path) sdkDep {
+ ret := sdkDep{
+ useModule: true,
+ modules: []string{m, config.DefaultLambdaStubsLibrary},
+ systemModules: m + "_system_modules",
+ frameworkResModule: r,
+ aidl: android.OptionalPathForPath(aidl),
+ }
+
+ if m == "core.current.stubs" {
+ ret.systemModules = "core-system-modules"
+ } else if m == "core.platform.api.stubs" {
+ ret.systemModules = "core-platform-api-stubs-system-modules"
+ }
+ return ret
+ }
+
+ // Ensures that the specificed system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor apks)
+ // or PRODUCT_SYSTEMSDK_VERSIONS (for other apks or when BOARD_SYSTEMSDK_VERSIONS is not set)
+ if strings.HasPrefix(v, "system_") && numericSdkVersion != android.FutureApiLevel {
+ allowed_versions := ctx.DeviceConfig().PlatformSystemSdkVersions()
+ if ctx.DeviceSpecific() || ctx.SocSpecific() {
+ if len(ctx.DeviceConfig().SystemSdkVersions()) > 0 {
+ allowed_versions = ctx.DeviceConfig().SystemSdkVersions()
+ }
+ }
+ if len(allowed_versions) > 0 && !android.InList(strconv.Itoa(numericSdkVersion), allowed_versions) {
+ ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
+ v, allowed_versions)
+ }
+ }
+
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() && v != "" {
+ return toPrebuilt(v)
+ }
+
+ switch v {
+ case "":
+ return sdkDep{
+ useDefaultLibs: true,
+ frameworkResModule: "framework-res",
+ }
+ case "current":
+ return toModule("android_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
+ case "system_current":
+ return toModule("android_system_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
+ case "test_current":
+ return toModule("android_test_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
+ case "core_current":
+ return toModule("core.current.stubs", "", nil)
+ default:
+ return toPrebuilt(v)
+ }
+}
+
+func sdkPreSingletonFactory() android.Singleton {
+ return sdkPreSingleton{}
+}
+
+type sdkPreSingleton struct{}
+
+func (sdkPreSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ sdkJars, err := ctx.GlobWithDeps("prebuilts/sdk/*/public/android.jar", nil)
+ if err != nil {
+ ctx.Errorf("failed to glob prebuilts/sdk/*/public/android.jar: %s", err.Error())
+ }
+
+ var sdkVersions []int
+ for _, sdkJar := range sdkJars {
+ dir := filepath.Base(filepath.Dir(filepath.Dir(sdkJar)))
+ v, err := strconv.Atoi(dir)
+ if scerr, ok := err.(*strconv.NumError); ok && scerr.Err == strconv.ErrSyntax {
+ continue
+ } else if err != nil {
+ ctx.Errorf("invalid sdk jar %q, %s, %v", sdkJar, err.Error())
+ }
+ sdkVersions = append(sdkVersions, v)
+ }
+
+ sort.Ints(sdkVersions)
+
+ ctx.Config().Once(sdkVersionsKey, func() interface{} { return sdkVersions })
+}
+
+func sdkSingletonFactory() android.Singleton {
+ return sdkSingleton{}
+}
+
+type sdkSingleton struct{}
+
+func (sdkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+ return
+ }
+
+ createSdkFrameworkAidl(ctx)
+ createAPIFingerprint(ctx)
+}
+
+// Create framework.aidl by extracting anything that implements android.os.Parcelable from the SDK stubs modules.
+func createSdkFrameworkAidl(ctx android.SingletonContext) {
+ stubsModules := []string{
+ "android_stubs_current",
+ "android_test_stubs_current",
+ "android_system_stubs_current",
+ }
+
+ stubsJars := make([]android.Paths, len(stubsModules))
+
+ ctx.VisitAllModules(func(module android.Module) {
+ // Collect dex jar paths for the modules listed above.
+ if j, ok := module.(Dependency); ok {
+ name := ctx.ModuleName(module)
+ if i := android.IndexList(name, stubsModules); i != -1 {
+ stubsJars[i] = j.HeaderJars()
+ }
+ }
+ })
+
+ var missingDeps []string
+
+ for i := range stubsJars {
+ if stubsJars[i] == nil {
+ if ctx.Config().AllowMissingDependencies() {
+ missingDeps = append(missingDeps, stubsModules[i])
+ } else {
+ ctx.Errorf("failed to find dex jar path for module %q",
+ stubsModules[i])
+ }
+ }
+ }
+
+ rule := android.NewRuleBuilder()
+ rule.MissingDeps(missingDeps)
+
+ var aidls android.Paths
+ for _, jars := range stubsJars {
+ for _, jar := range jars {
+ aidl := android.PathForOutput(ctx, "aidl", pathtools.ReplaceExtension(jar.Base(), "aidl"))
+
+ rule.Command().
+ Text("rm -f").Output(aidl)
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "sdkparcelables")).
+ Input(jar).
+ Output(aidl)
+
+ aidls = append(aidls, aidl)
+ }
+ }
+
+ combinedAidl := sdkFrameworkAidlPath(ctx)
+ tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
+
+ rule.Command().
+ Text("rm -f").Output(tempPath)
+ rule.Command().
+ Text("cat").
+ Inputs(aidls).
+ Text("| sort -u >").
+ Output(tempPath)
+
+ commitChangeForRestat(rule, tempPath, combinedAidl)
+
+ rule.Build(pctx, ctx, "framework_aidl", "generate framework.aidl")
+}
+
+func sdkFrameworkAidlPath(ctx android.PathContext) android.OutputPath {
+ return ctx.Config().Once(sdkFrameworkAidlPathKey, func() interface{} {
+ return android.PathForOutput(ctx, "framework.aidl")
+ }).(android.OutputPath)
+}
+
+// Create api_fingerprint.txt
+func createAPIFingerprint(ctx android.SingletonContext) {
+ out := ApiFingerprintPath(ctx)
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().
+ Text("rm -f").Output(out)
+ cmd := rule.Command()
+
+ if ctx.Config().PlatformSdkCodename() == "REL" {
+ cmd.Text("echo REL >").Output(out)
+ } else if ctx.Config().IsPdkBuild() {
+ // TODO: get this from the PDK artifacts?
+ cmd.Text("echo PDK >").Output(out)
+ } else if !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ in, err := ctx.GlobWithDeps("frameworks/base/api/*current.txt", nil)
+ if err != nil {
+ ctx.Errorf("error globbing API files: %s", err)
+ }
+
+ cmd.Text("cat").
+ Inputs(android.PathsForSource(ctx, in)).
+ Text("|")
+
+ if runtime.GOOS == "darwin" {
+ cmd.Text("md5")
+ } else {
+ cmd.Text("md5sum")
+ }
+
+ cmd.Text("| cut -d' ' -f1 >").
+ Output(out)
+ } else {
+ // Unbundled build
+ // TODO: use a prebuilt api_fingerprint.txt from prebuilts/sdk/current.txt once we have one
+ cmd.Text("echo").
+ Flag(ctx.Config().PlatformPreviewSdkVersion()).
+ Text(">").
+ Output(out)
+ }
+
+ rule.Build(pctx, ctx, "api_fingerprint", "generate api_fingerprint.txt")
+}
+
+func ApiFingerprintPath(ctx android.PathContext) android.OutputPath {
+ return ctx.Config().Once(apiFingerprintPathKey, func() interface{} {
+ return android.PathForOutput(ctx, "api_fingerprint.txt")
+ }).(android.OutputPath)
+}
+
+func sdkMakeVars(ctx android.MakeVarsContext) {
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() || ctx.Config().IsPdkBuild() {
+ return
+ }
+
+ ctx.Strict("FRAMEWORK_AIDL", sdkFrameworkAidlPath(ctx).String())
+ ctx.Strict("API_FINGERPRINT", ApiFingerprintPath(ctx).String())
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
new file mode 100644
index 0000000..84be4dd
--- /dev/null
+++ b/java/sdk_library.go
@@ -0,0 +1,762 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+ "android/soong/genrule"
+ "fmt"
+ "io"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ sdkStubsLibrarySuffix = ".stubs"
+ sdkSystemApiSuffix = ".system"
+ sdkTestApiSuffix = ".test"
+ sdkDocsSuffix = ".docs"
+ sdkXmlFileSuffix = ".xml"
+)
+
+type stubsLibraryDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+type syspropLibraryInterface interface {
+ SyspropJavaModule() *SdkLibrary
+}
+
+var (
+ publicApiStubsTag = dependencyTag{name: "public"}
+ systemApiStubsTag = dependencyTag{name: "system"}
+ testApiStubsTag = dependencyTag{name: "test"}
+ publicApiFileTag = dependencyTag{name: "publicApi"}
+ systemApiFileTag = dependencyTag{name: "systemApi"}
+ testApiFileTag = dependencyTag{name: "testApi"}
+)
+
+type apiScope int
+
+const (
+ apiScopePublic apiScope = iota
+ apiScopeSystem
+ apiScopeTest
+)
+
+var (
+ javaSdkLibrariesLock sync.Mutex
+)
+
+// java_sdk_library is to make a Java library that implements optional platform APIs to apps.
+// It is actually a wrapper of several modules: 1) stubs library that clients are linked against
+// to, 2) droiddoc module that internally generates API stubs source files, 3) the real runtime
+// shared library that implements the APIs, and 4) XML file for adding the runtime lib to the
+// classpath at runtime if requested via <uses-library>.
+//
+// TODO: these are big features that are currently missing
+// 1) disallowing linking to the runtime shared lib
+// 2) HTML generation
+
+func init() {
+ android.RegisterModuleType("java_sdk_library", SdkLibraryFactory)
+
+ android.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("java_sdk_library", SdkLibraryMutator).Parallel()
+ })
+
+ android.RegisterMakeVarsProvider(pctx, func(ctx android.MakeVarsContext) {
+ javaSdkLibraries := javaSdkLibraries(ctx.Config())
+ sort.Strings(*javaSdkLibraries)
+ ctx.Strict("JAVA_SDK_LIBRARIES", strings.Join(*javaSdkLibraries, " "))
+ })
+}
+
+type sdkLibraryProperties struct {
+ // list of optional source files that are part of API but not part of runtime library.
+ Api_srcs []string `android:"arch_variant"`
+
+ // List of Java libraries that will be in the classpath when building stubs
+ Stub_only_libs []string `android:"arch_variant"`
+
+ // list of package names that will be documented and publicized as API
+ Api_packages []string
+
+ // list of package names that must be hidden from the API
+ Hidden_api_packages []string
+
+ // local files that are used within user customized droiddoc options.
+ Droiddoc_option_files []string
+
+ // additional droiddoc options
+ // Available variables for substitution:
+ //
+ // $(location <label>): the path to the droiddoc_option_files with name <label>
+ Droiddoc_options []string
+
+ // the java library (in classpath) for documentation that provides java srcs and srcjars.
+ Srcs_lib *string
+
+ // the base dirs under srcs_lib will be scanned for java srcs.
+ Srcs_lib_whitelist_dirs []string
+
+ // the sub dirs under srcs_lib_whitelist_dirs will be scanned for java srcs.
+ // Defaults to "android.annotation".
+ Srcs_lib_whitelist_pkgs []string
+
+ // a list of top-level directories containing files to merge qualifier annotations
+ // (i.e. those intended to be included in the stubs written) from.
+ Merge_annotations_dirs []string
+
+ // a list of top-level directories containing Java stub files to merge show/hide annotations from.
+ Merge_inclusion_annotations_dirs []string
+
+ // If set to true, the path of dist files is apistubs/core. Defaults to false.
+ Core_lib *bool
+
+ // don't create dist rules.
+ No_dist *bool `blueprint:"mutated"`
+
+ // TODO: determines whether to create HTML doc or not
+ //Html_doc *bool
+}
+
+type SdkLibrary struct {
+ Library
+
+ sdkLibraryProperties sdkLibraryProperties
+
+ publicApiStubsPath android.Paths
+ systemApiStubsPath android.Paths
+ testApiStubsPath android.Paths
+
+ publicApiStubsImplPath android.Paths
+ systemApiStubsImplPath android.Paths
+ testApiStubsImplPath android.Paths
+
+ publicApiFilePath android.Path
+ systemApiFilePath android.Path
+ testApiFilePath android.Path
+}
+
+var _ Dependency = (*SdkLibrary)(nil)
+var _ SdkLibraryDependency = (*SdkLibrary)(nil)
+
+func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ useBuiltStubs := !ctx.Config().UnbundledBuildUsePrebuiltSdks()
+ // Add dependencies to the stubs library
+ if useBuiltStubs {
+ ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+ }
+ ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
+
+ if !Bool(module.properties.No_standard_libs) {
+ if useBuiltStubs {
+ ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+ ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
+ }
+ ctx.AddVariationDependencies(nil, systemApiFileTag, module.docsName(apiScopeSystem))
+ ctx.AddVariationDependencies(nil, testApiFileTag, module.docsName(apiScopeTest))
+ }
+
+ module.Library.deps(ctx)
+}
+
+func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ module.Library.GenerateAndroidBuildActions(ctx)
+
+ // Record the paths to the header jars of the library (stubs and impl).
+ // When this java_sdk_library is dependened from others via "libs" property,
+ // the recorded paths will be returned depending on the link type of the caller.
+ ctx.VisitDirectDeps(func(to android.Module) {
+ otherName := ctx.OtherModuleName(to)
+ tag := ctx.OtherModuleDependencyTag(to)
+
+ if lib, ok := to.(Dependency); ok {
+ switch tag {
+ case publicApiStubsTag:
+ module.publicApiStubsPath = lib.HeaderJars()
+ module.publicApiStubsImplPath = lib.ImplementationJars()
+ case systemApiStubsTag:
+ module.systemApiStubsPath = lib.HeaderJars()
+ module.systemApiStubsImplPath = lib.ImplementationJars()
+ case testApiStubsTag:
+ module.testApiStubsPath = lib.HeaderJars()
+ module.testApiStubsImplPath = lib.ImplementationJars()
+ }
+ }
+ if doc, ok := to.(ApiFilePath); ok {
+ switch tag {
+ case publicApiFileTag:
+ module.publicApiFilePath = doc.ApiFilePath()
+ case systemApiFileTag:
+ module.systemApiFilePath = doc.ApiFilePath()
+ case testApiFileTag:
+ module.testApiFilePath = doc.ApiFilePath()
+ default:
+ ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
+ }
+ }
+ })
+}
+
+func (module *SdkLibrary) AndroidMk() android.AndroidMkData {
+ data := module.Library.AndroidMk()
+ data.Required = append(data.Required, module.xmlFileName())
+
+ data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ android.WriteAndroidMkData(w, data)
+
+ module.Library.AndroidMkHostDex(w, name, data)
+ if !Bool(module.sdkLibraryProperties.No_dist) {
+ // Create a phony module that installs the impl library, for the case when this lib is
+ // in PRODUCT_PACKAGES.
+ owner := module.ModuleBase.Owner()
+ if owner == "" {
+ if Bool(module.sdkLibraryProperties.Core_lib) {
+ owner = "core"
+ } else {
+ owner = "android"
+ }
+ }
+ // Create dist rules to install the stubs libs to the dist dir
+ if len(module.publicApiStubsPath) == 1 {
+ fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+ module.publicApiStubsImplPath.Strings()[0]+
+ ":"+path.Join("apistubs", owner, "public",
+ module.BaseModuleName()+".jar")+")")
+ }
+ if len(module.systemApiStubsPath) == 1 {
+ fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+ module.systemApiStubsImplPath.Strings()[0]+
+ ":"+path.Join("apistubs", owner, "system",
+ module.BaseModuleName()+".jar")+")")
+ }
+ if len(module.testApiStubsPath) == 1 {
+ fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+ module.testApiStubsImplPath.Strings()[0]+
+ ":"+path.Join("apistubs", owner, "test",
+ module.BaseModuleName()+".jar")+")")
+ }
+ if module.publicApiFilePath != nil {
+ fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+ module.publicApiFilePath.String()+
+ ":"+path.Join("apistubs", owner, "public", "api",
+ module.BaseModuleName()+".txt")+")")
+ }
+ if module.systemApiFilePath != nil {
+ fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+ module.systemApiFilePath.String()+
+ ":"+path.Join("apistubs", owner, "system", "api",
+ module.BaseModuleName()+".txt")+")")
+ }
+ if module.testApiFilePath != nil {
+ fmt.Fprintln(w, "$(call dist-for-goals,sdk win_sdk,"+
+ module.testApiFilePath.String()+
+ ":"+path.Join("apistubs", owner, "test", "api",
+ module.BaseModuleName()+".txt")+")")
+ }
+ }
+ }
+ return data
+}
+
+// Module name of the stubs library
+func (module *SdkLibrary) stubsName(apiScope apiScope) string {
+ stubsName := module.BaseModuleName() + sdkStubsLibrarySuffix
+ switch apiScope {
+ case apiScopeSystem:
+ stubsName = stubsName + sdkSystemApiSuffix
+ case apiScopeTest:
+ stubsName = stubsName + sdkTestApiSuffix
+ }
+ return stubsName
+}
+
+// Module name of the docs
+func (module *SdkLibrary) docsName(apiScope apiScope) string {
+ docsName := module.BaseModuleName() + sdkDocsSuffix
+ switch apiScope {
+ case apiScopeSystem:
+ docsName = docsName + sdkSystemApiSuffix
+ case apiScopeTest:
+ docsName = docsName + sdkTestApiSuffix
+ }
+ return docsName
+}
+
+// Module name of the runtime implementation library
+func (module *SdkLibrary) implName() string {
+ return module.BaseModuleName()
+}
+
+// File path to the runtime implementation library
+func (module *SdkLibrary) implPath() string {
+ partition := "system"
+ if module.SocSpecific() {
+ partition = "vendor"
+ } else if module.DeviceSpecific() {
+ partition = "odm"
+ } else if module.ProductSpecific() {
+ partition = "product"
+ }
+ return "/" + partition + "/framework/" + module.implName() + ".jar"
+}
+
+// Module name of the XML file for the lib
+func (module *SdkLibrary) xmlFileName() string {
+ return module.BaseModuleName() + sdkXmlFileSuffix
+}
+
+// SDK version that the stubs library is built against. Note that this is always
+// *current. Older stubs library built with a numberd SDK version is created from
+// the prebuilt jar.
+func (module *SdkLibrary) sdkVersion(apiScope apiScope) string {
+ switch apiScope {
+ case apiScopePublic:
+ return "current"
+ case apiScopeSystem:
+ return "system_current"
+ case apiScopeTest:
+ return "test_current"
+ default:
+ return "current"
+ }
+}
+
+// $(INTERNAL_PLATFORM_<apiTagName>_API_FILE) points to the generated
+// api file for the current source
+// TODO: remove this when apicheck is done in soong
+func (module *SdkLibrary) apiTagName(apiScope apiScope) string {
+ apiTagName := strings.Replace(strings.ToUpper(module.BaseModuleName()), ".", "_", -1)
+ switch apiScope {
+ case apiScopeSystem:
+ apiTagName = apiTagName + "_SYSTEM"
+ case apiScopeTest:
+ apiTagName = apiTagName + "_TEST"
+ }
+ return apiTagName
+}
+
+func (module *SdkLibrary) latestApiFilegroupName(apiScope apiScope) string {
+ name := ":" + module.BaseModuleName() + ".api."
+ switch apiScope {
+ case apiScopePublic:
+ name = name + "public"
+ case apiScopeSystem:
+ name = name + "system"
+ case apiScopeTest:
+ name = name + "test"
+ }
+ name = name + ".latest"
+ return name
+}
+
+func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope apiScope) string {
+ name := ":" + module.BaseModuleName() + "-removed.api."
+ switch apiScope {
+ case apiScopePublic:
+ name = name + "public"
+ case apiScopeSystem:
+ name = name + "system"
+ case apiScopeTest:
+ name = name + "test"
+ }
+ name = name + ".latest"
+ return name
+}
+
+// Creates a static java library that has API stubs
+func (module *SdkLibrary) createStubsLibrary(mctx android.TopDownMutatorContext, apiScope apiScope) {
+ props := struct {
+ Name *string
+ Srcs []string
+ Sdk_version *string
+ Libs []string
+ Soc_specific *bool
+ Device_specific *bool
+ Product_specific *bool
+ Compile_dex *bool
+ No_standard_libs *bool
+ System_modules *string
+ Java_version *string
+ Product_variables struct {
+ Unbundled_build struct {
+ Enabled *bool
+ }
+ Pdk struct {
+ Enabled *bool
+ }
+ }
+ Openjdk9 struct {
+ Srcs []string
+ Javacflags []string
+ }
+ }{}
+
+ props.Name = proptools.StringPtr(module.stubsName(apiScope))
+ // sources are generated from the droiddoc
+ props.Srcs = []string{":" + module.docsName(apiScope)}
+ props.Sdk_version = proptools.StringPtr(module.sdkVersion(apiScope))
+ props.Libs = module.sdkLibraryProperties.Stub_only_libs
+ // Unbundled apps will use the prebult one from /prebuilts/sdk
+ if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
+ }
+ props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
+ props.No_standard_libs = module.Library.Module.properties.No_standard_libs
+ props.System_modules = module.Library.Module.deviceProperties.System_modules
+ props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
+ props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
+ props.Java_version = module.Library.Module.properties.Java_version
+ if module.Library.Module.deviceProperties.Compile_dex != nil {
+ props.Compile_dex = module.Library.Module.deviceProperties.Compile_dex
+ }
+
+ if module.SocSpecific() {
+ props.Soc_specific = proptools.BoolPtr(true)
+ } else if module.DeviceSpecific() {
+ props.Device_specific = proptools.BoolPtr(true)
+ } else if module.ProductSpecific() {
+ props.Product_specific = proptools.BoolPtr(true)
+ }
+
+ mctx.CreateModule(android.ModuleFactoryAdaptor(LibraryFactory), &props)
+}
+
+// Creates a droiddoc module that creates stubs source files from the given full source
+// files
+func (module *SdkLibrary) createDocs(mctx android.TopDownMutatorContext, apiScope apiScope) {
+ props := struct {
+ Name *string
+ Srcs []string
+ Installable *bool
+ Srcs_lib *string
+ Srcs_lib_whitelist_dirs []string
+ Srcs_lib_whitelist_pkgs []string
+ Libs []string
+ Arg_files []string
+ Args *string
+ Api_tag_name *string
+ Api_filename *string
+ Removed_api_filename *string
+ No_standard_libs *bool
+ Java_version *string
+ Merge_annotations_dirs []string
+ Merge_inclusion_annotations_dirs []string
+ Check_api struct {
+ Current ApiToCheck
+ Last_released ApiToCheck
+ Ignore_missing_latest_api *bool
+ }
+ Aidl struct {
+ Include_dirs []string
+ Local_include_dirs []string
+ }
+ }{}
+
+ props.Name = proptools.StringPtr(module.docsName(apiScope))
+ props.Srcs = append(props.Srcs, module.Library.Module.properties.Srcs...)
+ props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
+ props.Installable = proptools.BoolPtr(false)
+ // A droiddoc module has only one Libs property and doesn't distinguish between
+ // shared libs and static libs. So we need to add both of these libs to Libs property.
+ props.Libs = module.Library.Module.properties.Libs
+ props.Libs = append(props.Libs, module.Library.Module.properties.Static_libs...)
+ props.Aidl.Include_dirs = module.Library.Module.deviceProperties.Aidl.Include_dirs
+ props.Aidl.Local_include_dirs = module.Library.Module.deviceProperties.Aidl.Local_include_dirs
+ props.No_standard_libs = module.Library.Module.properties.No_standard_libs
+ props.Java_version = module.Library.Module.properties.Java_version
+
+ props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
+ props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
+
+ droiddocArgs := " --stub-packages " + strings.Join(module.sdkLibraryProperties.Api_packages, ":") +
+ " " + android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package ") +
+ " " + android.JoinWithPrefix(module.sdkLibraryProperties.Droiddoc_options, " ") +
+ " --hide MissingPermission --hide BroadcastBehavior " +
+ "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
+ "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
+
+ switch apiScope {
+ case apiScopeSystem:
+ droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi"
+ case apiScopeTest:
+ droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.TestApi"
+ }
+ props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
+ props.Args = proptools.StringPtr(droiddocArgs)
+
+ // List of APIs identified from the provided source files are created. They are later
+ // compared against to the not-yet-released (a.k.a current) list of APIs and to the
+ // last-released (a.k.a numbered) list of API.
+ currentApiFileName := "current.txt"
+ removedApiFileName := "removed.txt"
+ switch apiScope {
+ case apiScopeSystem:
+ currentApiFileName = "system-" + currentApiFileName
+ removedApiFileName = "system-" + removedApiFileName
+ case apiScopeTest:
+ currentApiFileName = "test-" + currentApiFileName
+ removedApiFileName = "test-" + removedApiFileName
+ }
+ currentApiFileName = path.Join("api", currentApiFileName)
+ removedApiFileName = path.Join("api", removedApiFileName)
+ // TODO(jiyong): remove these three props
+ props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
+ props.Api_filename = proptools.StringPtr(currentApiFileName)
+ props.Removed_api_filename = proptools.StringPtr(removedApiFileName)
+
+ // check against the not-yet-release API
+ props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
+ props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
+
+ // check against the latest released API
+ props.Check_api.Last_released.Api_file = proptools.StringPtr(
+ module.latestApiFilegroupName(apiScope))
+ props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+ module.latestRemovedApiFilegroupName(apiScope))
+ props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
+ props.Srcs_lib = module.sdkLibraryProperties.Srcs_lib
+ props.Srcs_lib_whitelist_dirs = module.sdkLibraryProperties.Srcs_lib_whitelist_dirs
+ props.Srcs_lib_whitelist_pkgs = module.sdkLibraryProperties.Srcs_lib_whitelist_pkgs
+
+ mctx.CreateModule(android.ModuleFactoryAdaptor(DroidstubsFactory), &props)
+}
+
+// Creates the xml file that publicizes the runtime library
+func (module *SdkLibrary) createXmlFile(mctx android.TopDownMutatorContext) {
+ template := `
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<permissions>
+ <library name="%s" file="%s"/>
+</permissions>
+`
+ // genrule to generate the xml file content from the template above
+ // TODO: preserve newlines in the generate xml file. Newlines are being squashed
+ // in the ninja file. Do we need to have an external tool for this?
+ xmlContent := fmt.Sprintf(template, module.BaseModuleName(), module.implPath())
+ genruleProps := struct {
+ Name *string
+ Cmd *string
+ Out []string
+ }{}
+ genruleProps.Name = proptools.StringPtr(module.xmlFileName() + "-gen")
+ genruleProps.Cmd = proptools.StringPtr("echo '" + xmlContent + "' > $(out)")
+ genruleProps.Out = []string{module.xmlFileName()}
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProps)
+
+ // creates a prebuilt_etc module to actually place the xml file under
+ // <partition>/etc/permissions
+ etcProps := struct {
+ Name *string
+ Src *string
+ Sub_dir *string
+ Soc_specific *bool
+ Device_specific *bool
+ Product_specific *bool
+ }{}
+ etcProps.Name = proptools.StringPtr(module.xmlFileName())
+ etcProps.Src = proptools.StringPtr(":" + module.xmlFileName() + "-gen")
+ etcProps.Sub_dir = proptools.StringPtr("permissions")
+ if module.SocSpecific() {
+ etcProps.Soc_specific = proptools.BoolPtr(true)
+ } else if module.DeviceSpecific() {
+ etcProps.Device_specific = proptools.BoolPtr(true)
+ } else if module.ProductSpecific() {
+ etcProps.Product_specific = proptools.BoolPtr(true)
+ }
+ mctx.CreateModule(android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory), &etcProps)
+}
+
+func (module *SdkLibrary) PrebuiltJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+ var api, v string
+ if sdkVersion == "" {
+ api = "system"
+ v = "current"
+ } else if strings.Contains(sdkVersion, "_") {
+ t := strings.Split(sdkVersion, "_")
+ api = t[0]
+ v = t[1]
+ } else {
+ api = "public"
+ v = sdkVersion
+ }
+ dir := filepath.Join("prebuilts", "sdk", v, api)
+ jar := filepath.Join(dir, module.BaseModuleName()+".jar")
+ jarPath := android.ExistentPathForSource(ctx, jar)
+ if !jarPath.Valid() {
+ ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", v, jar)
+ return nil
+ }
+ return android.Paths{jarPath.Path()}
+}
+
+// to satisfy SdkLibraryDependency interface
+func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+ // This module is just a wrapper for the stubs.
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ return module.PrebuiltJars(ctx, sdkVersion)
+ } else {
+ if strings.HasPrefix(sdkVersion, "system_") {
+ return module.systemApiStubsPath
+ } else if sdkVersion == "" {
+ return module.Library.HeaderJars()
+ } else {
+ return module.publicApiStubsPath
+ }
+ }
+}
+
+// to satisfy SdkLibraryDependency interface
+func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+ // This module is just a wrapper for the stubs.
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+ return module.PrebuiltJars(ctx, sdkVersion)
+ } else {
+ if strings.HasPrefix(sdkVersion, "system_") {
+ return module.systemApiStubsImplPath
+ } else if sdkVersion == "" {
+ return module.Library.ImplementationJars()
+ } else {
+ return module.publicApiStubsImplPath
+ }
+ }
+}
+
+func (module *SdkLibrary) SetNoDist() {
+ module.sdkLibraryProperties.No_dist = proptools.BoolPtr(true)
+}
+
+var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
+
+func javaSdkLibraries(config android.Config) *[]string {
+ return config.Once(javaSdkLibrariesKey, func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
+// For a java_sdk_library module, create internal modules for stubs, docs,
+// runtime libs and xml file. If requested, the stubs and docs are created twice
+// once for public API level and once for system API level
+func SdkLibraryMutator(mctx android.TopDownMutatorContext) {
+ if module, ok := mctx.Module().(*SdkLibrary); ok {
+ module.createInternalModules(mctx)
+ } else if module, ok := mctx.Module().(syspropLibraryInterface); ok {
+ module.SyspropJavaModule().createInternalModules(mctx)
+ }
+}
+
+func (module *SdkLibrary) createInternalModules(mctx android.TopDownMutatorContext) {
+ if len(module.Library.Module.properties.Srcs) == 0 {
+ mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
+ }
+
+ if len(module.sdkLibraryProperties.Api_packages) == 0 {
+ mctx.PropertyErrorf("api_packages", "java_sdk_library must specify api_packages")
+ }
+
+ missing_current_api := false
+
+ for _, scope := range []string{"", "system-", "test-"} {
+ for _, api := range []string{"current.txt", "removed.txt"} {
+ path := path.Join(mctx.ModuleDir(), "api", scope+api)
+ p := android.ExistentPathForSource(mctx, path)
+ if !p.Valid() {
+ mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
+ missing_current_api = true
+ }
+ }
+ }
+
+ if missing_current_api {
+ script := "build/soong/scripts/gen-java-current-api-files.sh"
+ p := android.ExistentPathForSource(mctx, script)
+
+ if !p.Valid() {
+ panic(fmt.Sprintf("script file %s doesn't exist", script))
+ }
+
+ mctx.ModuleErrorf("One or more current api files are missing. "+
+ "You can update them by:\n"+
+ "%s %q && m update-api", script, mctx.ModuleDir())
+ return
+ }
+
+ // for public API stubs
+ module.createStubsLibrary(mctx, apiScopePublic)
+ module.createDocs(mctx, apiScopePublic)
+
+ if !Bool(module.properties.No_standard_libs) {
+ // for system API stubs
+ module.createStubsLibrary(mctx, apiScopeSystem)
+ module.createDocs(mctx, apiScopeSystem)
+
+ // for test API stubs
+ module.createStubsLibrary(mctx, apiScopeTest)
+ module.createDocs(mctx, apiScopeTest)
+
+ // for runtime
+ module.createXmlFile(mctx)
+ }
+
+ // record java_sdk_library modules so that they are exported to make
+ javaSdkLibraries := javaSdkLibraries(mctx.Config())
+ javaSdkLibrariesLock.Lock()
+ defer javaSdkLibrariesLock.Unlock()
+ *javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
+}
+
+func (module *SdkLibrary) InitSdkLibraryProperties() {
+ module.AddProperties(
+ &module.sdkLibraryProperties,
+ &module.Library.Module.properties,
+ &module.Library.Module.dexpreoptProperties,
+ &module.Library.Module.deviceProperties,
+ &module.Library.Module.protoProperties,
+ )
+
+ module.Library.Module.properties.Installable = proptools.BoolPtr(true)
+ module.Library.Module.deviceProperties.IsSDKLibrary = true
+}
+
+func SdkLibraryFactory() android.Module {
+ module := &SdkLibrary{}
+ module.InitSdkLibraryProperties()
+ InitJavaModule(module, android.HostAndDeviceSupported)
+ return module
+}
diff --git a/java/sdk_test.go b/java/sdk_test.go
new file mode 100644
index 0000000..e446129
--- /dev/null
+++ b/java/sdk_test.go
@@ -0,0 +1,345 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/java/config"
+)
+
+func TestClasspath(t *testing.T) {
+ var classpathTestcases = []struct {
+ name string
+ unbundled bool
+ pdk bool
+ moduleType string
+ host android.OsClass
+ properties string
+ bootclasspath []string
+ system string
+ classpath []string
+ aidl string
+ }{
+ {
+ name: "default",
+ bootclasspath: config.DefaultBootclasspathLibraries,
+ system: config.DefaultSystemModules,
+ classpath: config.DefaultLibraries,
+ aidl: "-Iframework/aidl",
+ },
+ {
+ name: "blank sdk version",
+ properties: `sdk_version: "",`,
+ bootclasspath: config.DefaultBootclasspathLibraries,
+ system: config.DefaultSystemModules,
+ classpath: config.DefaultLibraries,
+ aidl: "-Iframework/aidl",
+ },
+ {
+
+ name: "sdk v25",
+ properties: `sdk_version: "25",`,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/25/public/framework.aidl",
+ },
+ {
+
+ name: "current",
+ properties: `sdk_version: "current",`,
+ bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ aidl: "-p" + buildDir + "/framework.aidl",
+ },
+ {
+
+ name: "system_current",
+ properties: `sdk_version: "system_current",`,
+ bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ aidl: "-p" + buildDir + "/framework.aidl",
+ },
+ {
+
+ name: "system_25",
+ properties: `sdk_version: "system_25",`,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/25/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/25/public/framework.aidl",
+ },
+ {
+
+ name: "test_current",
+ properties: `sdk_version: "test_current",`,
+ bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ aidl: "-p" + buildDir + "/framework.aidl",
+ },
+ {
+
+ name: "core_current",
+ properties: `sdk_version: "core_current",`,
+ bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ },
+ {
+
+ name: "nostdlib",
+ properties: `no_standard_libs: true, system_modules: "none"`,
+ system: "none",
+ bootclasspath: []string{`""`},
+ classpath: []string{},
+ },
+ {
+
+ name: "nostdlib system_modules",
+ properties: `no_standard_libs: true, system_modules: "core-platform-api-stubs-system-modules"`,
+ system: "core-platform-api-stubs-system-modules",
+ bootclasspath: []string{`""`},
+ classpath: []string{},
+ },
+ {
+
+ name: "host default",
+ moduleType: "java_library_host",
+ properties: ``,
+ host: android.Host,
+ bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
+ classpath: []string{},
+ },
+ {
+ name: "host nostdlib",
+ moduleType: "java_library_host",
+ host: android.Host,
+ properties: `no_standard_libs: true`,
+ classpath: []string{},
+ },
+ {
+
+ name: "host supported default",
+ host: android.Host,
+ properties: `host_supported: true,`,
+ classpath: []string{},
+ bootclasspath: []string{"jdk8/jre/lib/jce.jar", "jdk8/jre/lib/rt.jar"},
+ },
+ {
+ name: "host supported nostdlib",
+ host: android.Host,
+ properties: `host_supported: true, no_standard_libs: true, system_modules: "none"`,
+ classpath: []string{},
+ },
+ {
+
+ name: "unbundled sdk v25",
+ unbundled: true,
+ properties: `sdk_version: "25",`,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/25/public/framework.aidl",
+ },
+ {
+
+ name: "unbundled current",
+ unbundled: true,
+ properties: `sdk_version: "current",`,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/current/public/framework.aidl",
+ },
+
+ {
+ name: "pdk default",
+ pdk: true,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/25/public/framework.aidl",
+ },
+ {
+ name: "pdk current",
+ pdk: true,
+ properties: `sdk_version: "current",`,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/25/public/framework.aidl",
+ },
+ {
+ name: "pdk 25",
+ pdk: true,
+ properties: `sdk_version: "25",`,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ aidl: "-pprebuilts/sdk/25/public/framework.aidl",
+ },
+ }
+
+ for _, testcase := range classpathTestcases {
+ t.Run(testcase.name, func(t *testing.T) {
+ moduleType := "java_library"
+ if testcase.moduleType != "" {
+ moduleType = testcase.moduleType
+ }
+
+ bp := moduleType + ` {
+ name: "foo",
+ srcs: ["a.java"],
+ target: {
+ android: {
+ srcs: ["bar-doc/IFoo.aidl"],
+ },
+ },
+ ` + testcase.properties + `
+ }`
+
+ variant := "android_common"
+ if testcase.host == android.Host {
+ variant = android.BuildOs.String() + "_common"
+ }
+
+ convertModulesToPaths := func(cp []string) []string {
+ ret := make([]string, len(cp))
+ for i, e := range cp {
+ ret[i] = moduleToPath(e)
+ }
+ return ret
+ }
+
+ bootclasspath := convertModulesToPaths(testcase.bootclasspath)
+ classpath := convertModulesToPaths(testcase.classpath)
+
+ bc := strings.Join(bootclasspath, ":")
+ if bc != "" {
+ bc = "-bootclasspath " + bc
+ }
+
+ c := strings.Join(classpath, ":")
+ if c != "" {
+ c = "-classpath " + c
+ }
+ system := ""
+ if testcase.system == "none" {
+ system = "--system=none"
+ } else if testcase.system != "" {
+ system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system") + "/"
+ }
+
+ checkClasspath := func(t *testing.T, ctx *android.TestContext) {
+ javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+
+ got := javac.Args["bootClasspath"]
+ if got != bc {
+ t.Errorf("bootclasspath expected %q != got %q", bc, got)
+ }
+
+ got = javac.Args["classpath"]
+ if got != c {
+ t.Errorf("classpath expected %q != got %q", c, got)
+ }
+
+ var deps []string
+ if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
+ deps = append(deps, bootclasspath...)
+ }
+ deps = append(deps, classpath...)
+
+ if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
+ t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
+ }
+ }
+
+ t.Run("1.8", func(t *testing.T) {
+ // Test default javac 1.8
+ config := testConfig(nil)
+ if testcase.unbundled {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+ }
+ if testcase.pdk {
+ config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+ }
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ checkClasspath(t, ctx)
+
+ if testcase.host != android.Host {
+ aidl := ctx.ModuleForTests("foo", variant).Rule("aidl")
+
+ aidlFlags := aidl.Args["aidlFlags"]
+ // Trim trailing "-I." to avoid having to specify it in every test
+ aidlFlags = strings.TrimSpace(strings.TrimSuffix(aidlFlags, "-I."))
+
+ if g, w := aidlFlags, testcase.aidl; g != w {
+ t.Errorf("want aidl flags %q, got %q", w, g)
+ }
+ }
+ })
+
+ // Test again with javac 1.9
+ t.Run("1.9", func(t *testing.T) {
+ config := testConfig(map[string]string{"EXPERIMENTAL_USE_OPENJDK9": "true"})
+ if testcase.unbundled {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+ }
+ if testcase.pdk {
+ config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+ }
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+ got := javac.Args["bootClasspath"]
+ expected := system
+ if testcase.system == "bootclasspath" {
+ expected = bc
+ }
+ if got != expected {
+ t.Errorf("bootclasspath expected %q != got %q", expected, got)
+ }
+ })
+
+ // Test again with PLATFORM_VERSION_CODENAME=REL
+ t.Run("REL", func(t *testing.T) {
+ config := testConfig(nil)
+ config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
+ config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
+
+ if testcase.unbundled {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+ }
+ if testcase.pdk {
+ config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+ }
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ checkClasspath(t, ctx)
+ })
+ })
+ }
+
+}
diff --git a/java/support_libraries.go b/java/support_libraries.go
index 320afae..5a72f41 100644
--- a/java/support_libraries.go
+++ b/java/support_libraries.go
@@ -28,9 +28,8 @@
func supportLibrariesMakeVarsProvider(ctx android.MakeVarsContext) {
var supportAars, supportJars []string
- sctx := ctx.SingletonContext()
- sctx.VisitAllModules(func(module android.Module) {
- dir := sctx.ModuleDir(module)
+ ctx.VisitAllModules(func(module android.Module) {
+ dir := ctx.ModuleDir(module)
switch {
case strings.HasPrefix(dir, "prebuilts/sdk/current/extras"),
dir == "prebuilts/sdk/current/androidx",
@@ -43,7 +42,7 @@
return
}
- name := sctx.ModuleName(module)
+ name := ctx.ModuleName(module)
if strings.HasSuffix(name, "-nodeps") {
return
}
@@ -54,7 +53,7 @@
case *Library, *Import:
supportJars = append(supportJars, name)
default:
- sctx.ModuleErrorf(module, "unknown module type %t", module)
+ ctx.ModuleErrorf(module, "unknown module type %t", module)
}
})
diff --git a/java/system_modules.go b/java/system_modules.go
index 943eaeb..9ee0307 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -119,29 +119,26 @@
jars = append(jars, android.PathsForModuleSrc(ctx, system.properties.Jars)...)
- if ctx.Config().TargetOpenJDK9() {
- system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
- }
+ system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
}
func (system *SystemModules) DepsMutator(ctx android.BottomUpMutatorContext) {
- ctx.AddDependency(ctx.Module(), libTag, system.properties.Libs...)
+ ctx.AddVariationDependencies(nil, libTag, system.properties.Libs...)
}
func (system *SystemModules) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
- if system.outputFile != nil {
- makevar := "SOONG_SYSTEM_MODULES_" + name
- fmt.Fprintln(w)
- fmt.Fprintln(w, makevar, ":=", system.outputFile.String())
- fmt.Fprintln(w, ".KATI_READONLY", ":=", makevar)
- fmt.Fprintln(w, name+":", "$("+makevar+")")
- fmt.Fprintln(w)
- makevar = "SOONG_SYSTEM_MODULES_LIBS_" + name
- fmt.Fprintln(w, makevar, ":=", strings.Join(system.properties.Libs, " "))
- fmt.Fprintln(w, ".KATI_READONLY :=", makevar)
- }
+ makevar := "SOONG_SYSTEM_MODULES_" + name
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, makevar, ":=", system.outputFile.String())
+ fmt.Fprintln(w, ".KATI_READONLY", ":=", makevar)
+ fmt.Fprintln(w, name+":", "$("+makevar+")")
+ fmt.Fprintln(w, ".PHONY:", name)
+ fmt.Fprintln(w)
+ makevar = "SOONG_SYSTEM_MODULES_LIBS_" + name
+ fmt.Fprintln(w, makevar, ":=", strings.Join(system.properties.Libs, " "))
+ fmt.Fprintln(w, ".KATI_READONLY :=", makevar)
},
}
}
diff --git a/java/testing.go b/java/testing.go
new file mode 100644
index 0000000..6b35bd0
--- /dev/null
+++ b/java/testing.go
@@ -0,0 +1,98 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "fmt"
+
+ "android/soong/android"
+)
+
+func TestConfig(buildDir string, env map[string]string) android.Config {
+ if env == nil {
+ env = make(map[string]string)
+ }
+ if env["ANDROID_JAVA8_HOME"] == "" {
+ env["ANDROID_JAVA8_HOME"] = "jdk8"
+ }
+ config := android.TestArchConfig(buildDir, env)
+
+ return config
+}
+
+func GatherRequiredDepsForTest() string {
+ var bp string
+
+ extraModules := []string{
+ "core-lambda-stubs",
+ "ext",
+ "updatable_media_stubs",
+ "android_stubs_current",
+ "android_system_stubs_current",
+ "android_test_stubs_current",
+ "core.current.stubs",
+ "core.platform.api.stubs",
+ "kotlin-stdlib",
+ "kotlin-annotations",
+ }
+
+ for _, extra := range extraModules {
+ bp += fmt.Sprintf(`
+ java_library {
+ name: "%s",
+ srcs: ["a.java"],
+ no_standard_libs: true,
+ sdk_version: "core_current",
+ system_modules: "core-platform-api-stubs-system-modules",
+ }
+ `, extra)
+ }
+
+ bp += `
+ java_library {
+ name: "framework",
+ srcs: ["a.java"],
+ no_standard_libs: true,
+ sdk_version: "core_current",
+ system_modules: "core-platform-api-stubs-system-modules",
+ aidl: {
+ export_include_dirs: ["framework/aidl"],
+ },
+ }
+
+ android_app {
+ name: "framework-res",
+ no_framework_libs: true,
+ }
+ `
+
+ systemModules := []string{
+ "core-system-modules",
+ "core-platform-api-stubs-system-modules",
+ "android_stubs_current_system_modules",
+ "android_system_stubs_current_system_modules",
+ "android_test_stubs_current_system_modules",
+ }
+
+ for _, extra := range systemModules {
+ bp += fmt.Sprintf(`
+ java_system_modules {
+ name: "%s",
+ }
+ `, extra)
+ }
+
+ return bp
+}
diff --git a/java/tradefed.go b/java/tradefed.go
new file mode 100644
index 0000000..ebbdec1
--- /dev/null
+++ b/java/tradefed.go
@@ -0,0 +1,37 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("tradefed_java_library_host", tradefedJavaLibraryFactory)
+}
+
+// tradefed_java_library_factory wraps java_library and installs an additional
+// copy of the output jar to $HOST_OUT/tradefed.
+func tradefedJavaLibraryFactory() android.Module {
+ module := LibraryHostFactory().(*Library)
+ module.InstallMixin = tradefedJavaLibraryInstall
+ return module
+}
+
+func tradefedJavaLibraryInstall(ctx android.ModuleContext, path android.Path) android.Paths {
+ installedPath := ctx.InstallFile(android.PathForModuleInstall(ctx, "tradefed"),
+ ctx.ModuleName()+".jar", path)
+ return android.Paths{installedPath}
+}
diff --git a/phony/phony.go b/phony/phony.go
index a39b5d5..ed6a2fe 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -23,7 +23,7 @@
)
func init() {
- android.RegisterModuleType("phony", phonyFactory)
+ android.RegisterModuleType("phony", PhonyFactory)
}
type phony struct {
@@ -31,16 +31,13 @@
requiredModuleNames []string
}
-func phonyFactory() android.Module {
+func PhonyFactory() android.Module {
module := &phony{}
- android.InitAndroidModule(module)
+ android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
return module
}
-func (p *phony) DepsMutator(ctx android.BottomUpMutatorContext) {
-}
-
func (p *phony) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.requiredModuleNames = ctx.RequiredModuleNames()
if len(p.requiredModuleNames) == 0 {
@@ -54,6 +51,9 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ if p.Host() {
+ fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+ }
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(p.requiredModuleNames, " "))
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
},
diff --git a/python/androidmk.go b/python/androidmk.go
index 5fa01ab..1e51e7b 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -66,6 +66,16 @@
fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
strings.Join(p.binaryDecorator.binaryProperties.Test_suites, " "))
}
+ // If the test config has an explicit config specified use it.
+ if p.testProperties.Test_config != nil {
+ fmt.Fprintln(w, "LOCAL_TEST_CONFIG :=",
+ *p.testProperties.Test_config)
+ } else {
+ if p.testConfig != nil {
+ fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=",
+ p.testConfig.String())
+ }
+ }
})
base.subAndroidMk(ret, p.binaryDecorator.pythonInstaller)
}
@@ -77,6 +87,7 @@
ret.OutputFile = android.OptionalPathForPath(installer.path)
}
+ ret.Required = append(ret.Required, "libc++")
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
path := installer.path.RelPathString()
dir, file := filepath.Split(path)
@@ -85,5 +96,6 @@
fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file))
fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+ fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(installer.androidMkSharedLibs, " "))
})
}
diff --git a/python/binary.go b/python/binary.go
index 0314edb..140f07a 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,8 +18,6 @@
import (
"fmt"
- "path/filepath"
- "strings"
"android/soong/android"
)
@@ -44,6 +42,11 @@
// list of compatibility suites (for example "cts", "vts") that the module should be
// installed into.
Test_suites []string `android:"arch_variant"`
+
+ // whether to use `main` when starting the executable. The default is true, when set to
+ // false it will act much like the normal `python` executable, but with the sources and
+ // libraries automatically included in the PYTHONPATH.
+ Autorun *bool `android:"arch_variant"`
}
type binaryDecorator struct {
@@ -76,93 +79,55 @@
return module.Init()
}
+func (binary *binaryDecorator) autorun() bool {
+ return BoolDefault(binary.binaryProperties.Autorun, true)
+}
+
func (binary *binaryDecorator) bootstrapperProps() []interface{} {
return []interface{}{&binary.binaryProperties}
}
-func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actual_version string,
- embedded_launcher bool, srcsPathMappings []pathMapping, parSpec parSpec,
- depsPyRunfiles []string, depsParSpecs []parSpec) android.OptionalPath {
- // no Python source file for compiling .par file.
- if len(srcsPathMappings) == 0 {
- return android.OptionalPath{}
+func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
+ embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
+ depsSrcsZips android.Paths) android.OptionalPath {
+
+ main := ""
+ if binary.autorun() {
+ main = binary.getPyMainFile(ctx, srcsPathMappings)
}
- // the runfiles packages needs to be populated with "__init__.py".
- newPyPkgs := []string{}
- // the set to de-duplicate the new Python packages above.
- newPyPkgSet := make(map[string]bool)
- // the runfiles dirs have been treated as packages.
- existingPyPkgSet := make(map[string]bool)
-
- wholePyRunfiles := []string{}
- for _, path := range srcsPathMappings {
- wholePyRunfiles = append(wholePyRunfiles, path.dest)
- }
- wholePyRunfiles = append(wholePyRunfiles, depsPyRunfiles...)
-
- // find all the runfiles dirs which have been treated as packages.
- for _, path := range wholePyRunfiles {
- if filepath.Base(path) != initFileName {
- continue
- }
- existingPyPkg := PathBeforeLastSlash(path)
- if _, found := existingPyPkgSet[existingPyPkg]; found {
- panic(fmt.Errorf("found init file path duplicates: %q for module: %q.",
- path, ctx.ModuleName()))
- } else {
- existingPyPkgSet[existingPyPkg] = true
- }
- parentPath := PathBeforeLastSlash(existingPyPkg)
- populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
- }
-
- // create new packages under runfiles tree.
- for _, path := range wholePyRunfiles {
- if filepath.Base(path) == initFileName {
- continue
- }
- parentPath := PathBeforeLastSlash(path)
- populateNewPyPkgs(parentPath, existingPyPkgSet, newPyPkgSet, &newPyPkgs)
- }
-
- main := binary.getPyMainFile(ctx, srcsPathMappings)
- if main == "" {
- return android.OptionalPath{}
- }
-
- var launcher_path android.Path
- if embedded_launcher {
+ var launcherPath android.OptionalPath
+ if embeddedLauncher {
ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
if provider, ok := m.(IntermPathProvider); ok {
- if launcher_path != nil {
+ if launcherPath.Valid() {
panic(fmt.Errorf("launcher path was found before: %q",
- launcher_path))
+ launcherPath))
}
- launcher_path = provider.IntermPathForModuleOut().Path()
+ launcherPath = provider.IntermPathForModuleOut()
}
})
}
- binFile := registerBuildActionForParFile(ctx, embedded_launcher, launcher_path,
- binary.getHostInterpreterName(ctx, actual_version),
- main, binary.getStem(ctx), newPyPkgs, append(depsParSpecs, parSpec))
+ binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
+ binary.getHostInterpreterName(ctx, actualVersion),
+ main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
return android.OptionalPathForPath(binFile)
}
// get host interpreter name.
func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
- actual_version string) string {
+ actualVersion string) string {
var interp string
- switch actual_version {
+ switch actualVersion {
case pyVersion2:
interp = "python2.7"
case pyVersion3:
interp = "python3"
default:
panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
- actual_version, ctx.ModuleName()))
+ actualVersion, ctx.ModuleName()))
}
return interp
@@ -196,30 +161,3 @@
return stem + String(binary.binaryProperties.Suffix)
}
-
-// Sets the given directory and all its ancestor directories as Python packages.
-func populateNewPyPkgs(pkgPath string, existingPyPkgSet,
- newPyPkgSet map[string]bool, newPyPkgs *[]string) {
- for pkgPath != "" {
- if _, found := existingPyPkgSet[pkgPath]; found {
- break
- }
- if _, found := newPyPkgSet[pkgPath]; !found {
- newPyPkgSet[pkgPath] = true
- *newPyPkgs = append(*newPyPkgs, pkgPath)
- // Gets its ancestor directory by trimming last slash.
- pkgPath = PathBeforeLastSlash(pkgPath)
- } else {
- break
- }
- }
-}
-
-// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". However,
-// the PathBeforeLastSlash() will return "" for both cases above.
-func PathBeforeLastSlash(path string) string {
- if idx := strings.LastIndex(path, "/"); idx != -1 {
- return path[:idx]
- }
- return ""
-}
diff --git a/python/builder.go b/python/builder.go
index f194354..36baecd 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -17,7 +17,6 @@
// This file contains Ninja build actions for building Python program.
import (
- "fmt"
"strings"
"android/soong/android"
@@ -29,160 +28,116 @@
var (
pctx = android.NewPackageContext("android/soong/python")
- host_par = pctx.AndroidStaticRule("host_par",
+ zip = pctx.AndroidStaticRule("zip",
blueprint.RuleParams{
- Command: `touch $initFile && ` +
- `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' $template > $stub && ` +
- `$parCmd -o $parFile $parArgs && echo '#!/usr/bin/env python' | cat - $parFile > $out && ` +
- `chmod +x $out && (rm -f $initFile; rm -f $stub; rm -f $parFile)`,
- CommandDeps: []string{"$parCmd", "$template"},
- },
- "initFile", "interp", "main", "template", "stub", "parCmd", "parFile", "parArgs")
-
- embedded_par = pctx.AndroidStaticRule("embedded_par",
- blueprint.RuleParams{
- Command: `touch $initFile && ` +
- `echo '$main' > $entry_point && ` +
- `$parCmd -o $parFile $parArgs && cat $launcher | cat - $parFile > $out && ` +
- `chmod +x $out && (rm -f $initFile; rm -f $entry_point; rm -f $parFile)`,
+ Command: `$parCmd -o $out $args`,
CommandDeps: []string{"$parCmd"},
},
- "initFile", "main", "entry_point", "parCmd", "parFile", "parArgs", "launcher")
+ "args")
+
+ combineZip = pctx.AndroidStaticRule("combineZip",
+ blueprint.RuleParams{
+ Command: `$mergeParCmd $out $in`,
+ CommandDeps: []string{"$mergeParCmd"},
+ },
+ )
+
+ hostPar = pctx.AndroidStaticRule("hostPar",
+ blueprint.RuleParams{
+ Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' $template > $stub && ` +
+ `echo "#!/usr/bin/env python" >${out}.prefix &&` +
+ `$mergeParCmd -p --prefix ${out}.prefix -pm $stub $out $srcsZips && ` +
+ `chmod +x $out && (rm -f $stub; rm -f ${out}.prefix)`,
+ CommandDeps: []string{"$mergeParCmd"},
+ },
+ "interp", "main", "template", "stub", "srcsZips")
+
+ embeddedPar = pctx.AndroidStaticRule("embeddedPar",
+ blueprint.RuleParams{
+ Command: `rm -f $out.main && ` +
+ `sed 's/ENTRY_POINT/$main/' build/soong/python/scripts/main.py >$out.main &&` +
+ `$mergeParCmd -p -pm $out.main --prefix $launcher $out $srcsZips && ` +
+ `chmod +x $out && rm -rf $out.main`,
+ CommandDeps: []string{"$mergeParCmd", "$parCmd", "build/soong/python/scripts/main.py"},
+ },
+ "main", "srcsZips", "launcher")
+
+ embeddedParNoMain = pctx.AndroidStaticRule("embeddedParNoMain",
+ blueprint.RuleParams{
+ Command: `$mergeParCmd -p --prefix $launcher $out $srcsZips && ` +
+ `chmod +x $out`,
+ CommandDeps: []string{"$mergeParCmd"},
+ },
+ "srcsZips", "launcher")
)
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
- pctx.Import("android/soong/common")
+ pctx.Import("android/soong/android")
pctx.HostBinToolVariable("parCmd", "soong_zip")
+ pctx.HostBinToolVariable("mergeParCmd", "merge_zips")
}
-type fileListSpec struct {
- fileList android.Path
- relativeRoot string
-}
-
-type parSpec struct {
- rootPrefix string
-
- fileListSpecs []fileListSpec
-}
-
-func (p parSpec) soongParArgs() string {
- ret := `-P ` + p.rootPrefix
-
- for _, spec := range p.fileListSpecs {
- ret += ` -C ` + spec.relativeRoot + ` -l ` + spec.fileList.String()
- }
-
- return ret
-}
-
-func registerBuildActionForModuleFileList(ctx android.ModuleContext,
- name string, files android.Paths) android.Path {
- fileList := android.PathForModuleOut(ctx, name+".list")
-
- content := []string{}
- for _, file := range files {
- content = append(content, file.String())
- }
-
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Description: "generate " + fileList.Rel(),
- Output: fileList,
- Implicits: files,
- Args: map[string]string{
- "content": strings.Join(content, `\n`),
- },
- })
-
- return fileList
-}
-
-func registerBuildActionForParFile(ctx android.ModuleContext, embedded_launcher bool,
- launcher_path android.Path, interpreter, main, binName string,
- newPyPkgs []string, parSpecs []parSpec) android.Path {
-
- // .intermediate output path for __init__.py
- initFile := android.PathForModuleOut(ctx, initFileName).String()
-
- // .intermediate output path for par file.
- parFile := android.PathForModuleOut(ctx, binName+parFileExt)
+func registerBuildActionForParFile(ctx android.ModuleContext, embeddedLauncher bool,
+ launcherPath android.OptionalPath, interpreter, main, binName string,
+ srcsZips android.Paths) android.Path {
// .intermediate output path for bin executable.
binFile := android.PathForModuleOut(ctx, binName)
// implicit dependency for parFile build action.
- implicits := android.Paths{}
- for _, p := range parSpecs {
- for _, f := range p.fileListSpecs {
- implicits = append(implicits, f.fileList)
- }
- }
+ implicits := srcsZips
- parArgs := []string{}
- parArgs = append(parArgs, `-P "" `+`-C `+strings.TrimSuffix(initFile, initFileName)+` -f `+initFile)
- for _, pkg := range newPyPkgs {
- parArgs = append(parArgs, `-P `+pkg+` -f `+initFile)
- }
- for _, p := range parSpecs {
- parArgs = append(parArgs, p.soongParArgs())
- }
-
- if !embedded_launcher {
+ if !embeddedLauncher {
// the path of stub_template_host.txt from source tree.
template := android.PathForSource(ctx, stubTemplateHost)
+ implicits = append(implicits, template)
// intermediate output path for __main__.py
stub := android.PathForModuleOut(ctx, mainFileName).String()
- // added stub file to the soong_zip args.
- parArgs = append(parArgs, `-P "" `+`-C `+strings.TrimSuffix(stub, mainFileName)+` -f `+stub)
-
ctx.Build(pctx, android.BuildParams{
- Rule: host_par,
+ Rule: hostPar,
Description: "host python archive",
Output: binFile,
Implicits: implicits,
Args: map[string]string{
- "initFile": initFile,
"interp": strings.Replace(interpreter, "/", `\/`, -1),
- // we need remove "runfiles/" suffix since stub script starts
- // searching for main file in each sub-dir of "runfiles" directory tree.
- "main": strings.Replace(strings.TrimPrefix(main, runFiles+"/"),
- "/", `\/`, -1),
+ "main": strings.Replace(main, "/", `\/`, -1),
"template": template.String(),
"stub": stub,
- "parFile": parFile.String(),
- "parArgs": strings.Join(parArgs, " "),
+ "srcsZips": strings.Join(srcsZips.Strings(), " "),
},
})
- } else {
- // added launcher_path to the implicits Ninja dependencies.
- implicits = append(implicits, launcher_path)
+ } else if launcherPath.Valid() {
+ // added launcherPath to the implicits Ninja dependencies.
+ implicits = append(implicits, launcherPath.Path())
- // .intermediate output path for entry_point.txt
- entryPoint := android.PathForModuleOut(ctx, entryPointFile).String()
-
- // added entry_point file to the soong_zip args.
- parArgs = append(parArgs, `-P "" `+`-C `+fmt.Sprintf(
- "%q", strings.TrimSuffix(entryPoint, entryPointFile))+` -f `+entryPoint)
-
- ctx.Build(pctx, android.BuildParams{
- Rule: embedded_par,
- Description: "embedded python archive",
- Output: binFile,
- Implicits: implicits,
- Args: map[string]string{
- "initFile": initFile,
- "main": main,
- "entry_point": entryPoint,
- "parFile": parFile.String(),
- "parArgs": strings.Join(parArgs, " "),
- "launcher": launcher_path.String(),
- },
- })
+ if main == "" {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: embeddedParNoMain,
+ Description: "embedded python archive",
+ Output: binFile,
+ Implicits: implicits,
+ Args: map[string]string{
+ "srcsZips": strings.Join(srcsZips.Strings(), " "),
+ "launcher": launcherPath.String(),
+ },
+ })
+ } else {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: embeddedPar,
+ Description: "embedded python archive",
+ Output: binFile,
+ Implicits: implicits,
+ Args: map[string]string{
+ "main": strings.Replace(strings.TrimSuffix(main, pyExt), "/", ".", -1),
+ "srcsZips": strings.Join(srcsZips.Strings(), " "),
+ "launcher": launcherPath.String(),
+ },
+ })
+ }
}
return binFile
diff --git a/python/defaults.go b/python/defaults.go
index 641aca4..dba23a7 100644
--- a/python/defaults.go
+++ b/python/defaults.go
@@ -30,9 +30,6 @@
func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
-func (d *Defaults) DepsMutator(ctx android.BottomUpMutatorContext) {
-}
-
func defaultsFactory() android.Module {
return DefaultsFactory()
}
diff --git a/python/installer.go b/python/installer.go
index ab3d9b4..62f36f4 100644
--- a/python/installer.go
+++ b/python/installer.go
@@ -34,6 +34,8 @@
relative string
path android.OutputPath
+
+ androidMkSharedLibs []string
}
func NewPythonInstaller(dir, dir64 string) *pythonInstaller {
@@ -59,3 +61,7 @@
func (installer *pythonInstaller) install(ctx android.ModuleContext, file android.Path) {
installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
}
+
+func (installer *pythonInstaller) setAndroidMkSharedLibs(sharedLibs []string) {
+ installer.androidMkSharedLibs = sharedLibs
+}
diff --git a/python/proto.go b/python/proto.go
new file mode 100644
index 0000000..85ed1a5
--- /dev/null
+++ b/python/proto.go
@@ -0,0 +1,50 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package python
+
+import (
+ "android/soong/android"
+)
+
+func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags, pkgPath string) android.Path {
+ srcsZipFile := android.PathForModuleGen(ctx, protoFile.Base()+".srcszip")
+
+ outDir := srcsZipFile.ReplaceExtension(ctx, "tmp")
+ depFile := srcsZipFile.ReplaceExtension(ctx, "srcszip.d")
+
+ rule := android.NewRuleBuilder()
+
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+ rule.Command().Text("mkdir -p").Flag(outDir.String())
+
+ android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+
+ // Proto generated python files have an unknown package name in the path, so package the entire output directory
+ // into a srcszip.
+ zipCmd := rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ FlagWithOutput("-o ", srcsZipFile)
+ if pkgPath != "" {
+ zipCmd.FlagWithArg("-P ", pkgPath)
+ }
+ zipCmd.FlagWithArg("-C ", outDir.String()).
+ FlagWithArg("-D ", outDir.String())
+
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+
+ rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
+
+ return srcsZipFile
+}
diff --git a/python/python.go b/python/python.go
index ed879eb..ad08909 100644
--- a/python/python.go
+++ b/python/python.go
@@ -43,11 +43,11 @@
// non-empty list of .py files under this strict Python version.
// srcs may reference the outputs of other modules that produce source files like genrule
// or filegroup using the syntax ":module".
- Srcs []string `android:"arch_variant"`
+ Srcs []string `android:"path,arch_variant"`
// list of source files that should not be used to build the Python module.
// This is most useful in the arch/multilib variants to remove non-common files
- Exclude_srcs []string `android:"arch_variant"`
+ Exclude_srcs []string `android:"path,arch_variant"`
// list of the Python libraries under this Python version.
Libs []string `android:"arch_variant"`
@@ -63,8 +63,7 @@
// files of the current module.
// eg. Pkg_path = "a/b/c"; Other packages can reference this module by using
// (from a.b.c import ...) statement.
- // if left unspecified, all the source/data files of current module are copied to
- // "runfiles/" tree directory directly.
+ // if left unspecified, all the source/data files path is unchanged within zip file.
Pkg_path *string `android:"arch_variant"`
// true, if the Python module is used internally, eg, Python std libs.
@@ -75,15 +74,15 @@
// srcs may reference the outputs of other modules that produce source files like genrule
// or filegroup using the syntax ":module".
// Srcs has to be non-empty.
- Srcs []string `android:"arch_variant"`
+ Srcs []string `android:"path,arch_variant"`
// list of source files that should not be used to build the C/C++ module.
// This is most useful in the arch/multilib variants to remove non-common files
- Exclude_srcs []string `android:"arch_variant"`
+ Exclude_srcs []string `android:"path,arch_variant"`
// list of files or filegroup modules that provide data that should be installed alongside
// the test. the file extension can be arbitrary except for (.py).
- Data []string `android:"arch_variant"`
+ Data []string `android:"path,arch_variant"`
// list of the Python libraries compatible both with Python2 and Python3.
Libs []string `android:"arch_variant"`
@@ -111,7 +110,8 @@
android.ModuleBase
android.DefaultableModuleBase
- properties BaseProperties
+ properties BaseProperties
+ protoProperties android.ProtoProperties
// initialize before calling Init
hod android.HostOrDeviceSupported
@@ -132,18 +132,15 @@
// pathMapping: <dest: runfile_path, src: source_path>
dataPathMappings []pathMapping
- // soong_zip arguments of all its dependencies.
- depsParSpecs []parSpec
+ // the zip filepath for zipping current module source/data files.
+ srcsZip android.Path
- // Python runfiles paths of all its dependencies.
- depsPyRunfiles []string
+ // dependency modules' zip filepath for zipping current module source/data files.
+ depsSrcsZips android.Paths
// (.intermediate) module output path as installation source.
installSource android.OptionalPath
- // the soong_zip arguments for zipping current module source/data files.
- parSpec parSpec
-
subAndroidMkOnce map[subAndroidMkProvider]bool
}
@@ -156,19 +153,22 @@
type bootstrapper interface {
bootstrapperProps() []interface{}
- bootstrap(ctx android.ModuleContext, Actual_version string, embedded_launcher bool,
- srcsPathMappings []pathMapping, parSpec parSpec,
- depsPyRunfiles []string, depsParSpecs []parSpec) android.OptionalPath
+ bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
+ srcsPathMappings []pathMapping, srcsZip android.Path,
+ depsSrcsZips android.Paths) android.OptionalPath
+
+ autorun() bool
}
type installer interface {
install(ctx android.ModuleContext, path android.Path)
+ setAndroidMkSharedLibs(sharedLibs []string)
}
type PythonDependency interface {
GetSrcsPathMappings() []pathMapping
GetDataPathMappings() []pathMapping
- GetParSpec() parSpec
+ GetSrcsZip() android.Path
}
func (p *Module) GetSrcsPathMappings() []pathMapping {
@@ -179,8 +179,8 @@
return p.dataPathMappings
}
-func (p *Module) GetParSpec() parSpec {
- return p.parSpec
+func (p *Module) GetSrcsZip() android.Path {
+ return p.srcsZip
}
var _ PythonDependency = (*Module)(nil)
@@ -189,7 +189,7 @@
func (p *Module) Init() android.Module {
- p.AddProperties(&p.properties)
+ p.AddProperties(&p.properties, &p.protoProperties)
if p.bootstrapper != nil {
p.AddProperties(p.bootstrapper.bootstrapperProps()...)
}
@@ -206,18 +206,19 @@
}
var (
- pythonLibTag = dependencyTag{name: "pythonLib"}
- launcherTag = dependencyTag{name: "launcher"}
- pyIdentifierRegexp = regexp.MustCompile(`^([a-z]|[A-Z]|_)([a-z]|[A-Z]|[0-9]|_)*$`)
- pyExt = ".py"
- pyVersion2 = "PY2"
- pyVersion3 = "PY3"
- initFileName = "__init__.py"
- mainFileName = "__main__.py"
- entryPointFile = "entry_point.txt"
- parFileExt = ".zip"
- runFiles = "runfiles"
- internal = "internal"
+ pythonLibTag = dependencyTag{name: "pythonLib"}
+ launcherTag = dependencyTag{name: "launcher"}
+ launcherSharedLibTag = dependencyTag{name: "launcherSharedLib"}
+ pyIdentifierRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+ pyExt = ".py"
+ protoExt = ".proto"
+ pyVersion2 = "PY2"
+ pyVersion3 = "PY3"
+ initFileName = "__init__.py"
+ mainFileName = "__main__.py"
+ entryPointFile = "entry_point.txt"
+ parFileExt = ".zip"
+ internal = "internal"
)
// create version variants for modules.
@@ -253,43 +254,78 @@
func (p *Module) isEmbeddedLauncherEnabled(actual_version string) bool {
switch actual_version {
case pyVersion2:
- return proptools.Bool(p.properties.Version.Py2.Embedded_launcher)
+ return Bool(p.properties.Version.Py2.Embedded_launcher)
case pyVersion3:
- return proptools.Bool(p.properties.Version.Py3.Embedded_launcher)
+ return Bool(p.properties.Version.Py3.Embedded_launcher)
}
return false
}
-func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
- // deps from "data".
- android.ExtractSourcesDeps(ctx, p.properties.Data)
- // deps from "srcs".
- android.ExtractSourcesDeps(ctx, p.properties.Srcs)
- android.ExtractSourcesDeps(ctx, p.properties.Exclude_srcs)
+func hasSrcExt(srcs []string, ext string) bool {
+ for _, src := range srcs {
+ if filepath.Ext(src) == ext {
+ return true
+ }
+ }
+ return false
+}
+
+func (p *Module) hasSrcExt(ctx android.BottomUpMutatorContext, ext string) bool {
+ if hasSrcExt(p.properties.Srcs, protoExt) {
+ return true
+ }
switch p.properties.Actual_version {
case pyVersion2:
- // deps from "version.py2.srcs" property.
- android.ExtractSourcesDeps(ctx, p.properties.Version.Py2.Srcs)
- android.ExtractSourcesDeps(ctx, p.properties.Version.Py2.Exclude_srcs)
+ return hasSrcExt(p.properties.Version.Py2.Srcs, protoExt)
+ case pyVersion3:
+ return hasSrcExt(p.properties.Version.Py3.Srcs, protoExt)
+ default:
+ panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
+ p.properties.Actual_version, ctx.ModuleName()))
+ }
+}
+func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+ android.ProtoDeps(ctx, &p.protoProperties)
+
+ if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
+ ctx.AddVariationDependencies(nil, pythonLibTag, "libprotobuf-python")
+ }
+ switch p.properties.Actual_version {
+ case pyVersion2:
ctx.AddVariationDependencies(nil, pythonLibTag,
uniqueLibs(ctx, p.properties.Libs, "version.py2.libs",
p.properties.Version.Py2.Libs)...)
if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion2) {
ctx.AddVariationDependencies(nil, pythonLibTag, "py2-stdlib")
+
+ launcherModule := "py2-launcher"
+ if p.bootstrapper.autorun() {
+ launcherModule = "py2-launcher-autorun"
+ }
ctx.AddFarVariationDependencies([]blueprint.Variation{
- {"arch", ctx.Target().String()},
- }, launcherTag, "py2-launcher")
+ {Mutator: "arch", Variation: ctx.Target().String()},
+ }, launcherTag, launcherModule)
+
+ // Add py2-launcher shared lib dependencies. Ideally, these should be
+ // derived from the `shared_libs` property of "py2-launcher". However, we
+ // cannot read the property at this stage and it will be too late to add
+ // dependencies later.
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Target().String()},
+ }, launcherSharedLibTag, "libsqlite")
+
+ if ctx.Target().Os.Bionic() {
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Target().String()},
+ }, launcherSharedLibTag, "libc", "libdl", "libm")
+ }
}
case pyVersion3:
- // deps from "version.py3.srcs" property.
- android.ExtractSourcesDeps(ctx, p.properties.Version.Py3.Srcs)
- android.ExtractSourcesDeps(ctx, p.properties.Version.Py3.Exclude_srcs)
-
ctx.AddVariationDependencies(nil, pythonLibTag,
uniqueLibs(ctx, p.properties.Libs, "version.py3.libs",
p.properties.Version.Py3.Libs)...)
@@ -336,20 +372,31 @@
func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.GeneratePythonBuildActions(ctx)
+ // Only Python binaries and test has non-empty bootstrapper.
if p.bootstrapper != nil {
+ p.walkTransitiveDeps(ctx)
// TODO(nanzhang): Since embedded launcher is not supported for Python3 for now,
// so we initialize "embedded_launcher" to false.
- embedded_launcher := false
+ embeddedLauncher := false
if p.properties.Actual_version == pyVersion2 {
- embedded_launcher = p.isEmbeddedLauncherEnabled(pyVersion2)
+ embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion2)
}
p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
- embedded_launcher, p.srcsPathMappings, p.parSpec, p.depsPyRunfiles,
- p.depsParSpecs)
+ embeddedLauncher, p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
}
- if p.installer != nil && p.installSource.Valid() {
- p.installer.install(ctx, p.installSource.Path())
+ if p.installer != nil {
+ var sharedLibs []string
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ if ctx.OtherModuleDependencyTag(dep) == launcherSharedLibTag {
+ sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+ }
+ })
+ p.installer.setAndroidMkSharedLibs(sharedLibs)
+
+ if p.installSource.Valid() {
+ p.installer.install(ctx, p.installSource.Path())
+ }
}
}
@@ -369,65 +416,60 @@
panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
p.properties.Actual_version, ctx.ModuleName()))
}
- expandedSrcs := ctx.ExpandSources(srcs, exclude_srcs)
- if len(expandedSrcs) == 0 {
+ expandedSrcs := android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
+ requiresSrcs := true
+ if p.bootstrapper != nil && !p.bootstrapper.autorun() {
+ requiresSrcs = false
+ }
+ if len(expandedSrcs) == 0 && requiresSrcs {
ctx.ModuleErrorf("doesn't have any source files!")
}
// expand data files from "data" property.
- expandedData := ctx.ExpandSources(p.properties.Data, nil)
+ expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
// sanitize pkg_path.
- pkg_path := String(p.properties.Pkg_path)
- if pkg_path != "" {
- pkg_path = filepath.Clean(String(p.properties.Pkg_path))
- if pkg_path == ".." || strings.HasPrefix(pkg_path, "../") ||
- strings.HasPrefix(pkg_path, "/") {
+ pkgPath := String(p.properties.Pkg_path)
+ if pkgPath != "" {
+ pkgPath = filepath.Clean(String(p.properties.Pkg_path))
+ if pkgPath == ".." || strings.HasPrefix(pkgPath, "../") ||
+ strings.HasPrefix(pkgPath, "/") {
ctx.PropertyErrorf("pkg_path",
"%q must be a relative path contained in par file.",
String(p.properties.Pkg_path))
return
}
if p.properties.Is_internal != nil && *p.properties.Is_internal {
- // pkg_path starts from "internal/" implicitly.
- pkg_path = filepath.Join(internal, pkg_path)
- } else {
- // pkg_path starts from "runfiles/" implicitly.
- pkg_path = filepath.Join(runFiles, pkg_path)
+ pkgPath = filepath.Join(internal, pkgPath)
}
} else {
if p.properties.Is_internal != nil && *p.properties.Is_internal {
- // pkg_path starts from "runfiles/" implicitly.
- pkg_path = internal
- } else {
- // pkg_path starts from "runfiles/" implicitly.
- pkg_path = runFiles
+ pkgPath = internal
}
}
- p.genModulePathMappings(ctx, pkg_path, expandedSrcs, expandedData)
+ p.genModulePathMappings(ctx, pkgPath, expandedSrcs, expandedData)
- p.parSpec = p.dumpFileList(ctx, pkg_path)
-
- p.uniqWholeRunfilesTree(ctx)
+ p.srcsZip = p.createSrcsZip(ctx, pkgPath)
}
// generate current module unique pathMappings: <dest: runfiles_path, src: source_path>
// for python/data files.
-func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkg_path string,
+func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
expandedSrcs, expandedData android.Paths) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
- // check duplicates.
+ // check current module duplicates.
destToPySrcs := make(map[string]string)
destToPyData := make(map[string]string)
for _, s := range expandedSrcs {
- if s.Ext() != pyExt {
- ctx.PropertyErrorf("srcs", "found non (.py) file: %q!", s.String())
+ if s.Ext() != pyExt && s.Ext() != protoExt {
+ ctx.PropertyErrorf("srcs", "found non (.py|.proto) file: %q!", s.String())
continue
}
- runfilesPath := filepath.Join(pkg_path, s.Rel())
- identifiers := strings.Split(strings.TrimSuffix(runfilesPath, pyExt), "/")
+ runfilesPath := filepath.Join(pkgPath, s.Rel())
+ identifiers := strings.Split(strings.TrimSuffix(runfilesPath,
+ filepath.Ext(runfilesPath)), "/")
for _, token := range identifiers {
if !pyIdentifierRegexp.MatchString(token) {
ctx.PropertyErrorf("srcs", "the path %q contains invalid token %q.",
@@ -441,56 +483,96 @@
}
for _, d := range expandedData {
- if d.Ext() == pyExt {
- ctx.PropertyErrorf("data", "found (.py) file: %q!", d.String())
+ if d.Ext() == pyExt || d.Ext() == protoExt {
+ ctx.PropertyErrorf("data", "found (.py|.proto) file: %q!", d.String())
continue
}
- runfilesPath := filepath.Join(pkg_path, d.Rel())
+ runfilesPath := filepath.Join(pkgPath, d.Rel())
if fillInMap(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) {
p.dataPathMappings = append(p.dataPathMappings,
pathMapping{dest: runfilesPath, src: d})
}
}
-
}
-// register build actions to dump filelist to disk.
-func (p *Module) dumpFileList(ctx android.ModuleContext, pkg_path string) parSpec {
+// register build actions to zip current module's sources.
+func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
relativeRootMap := make(map[string]android.Paths)
- // the soong_zip params in order to pack current module's Python/data files.
- ret := parSpec{rootPrefix: pkg_path}
-
pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
+ var protoSrcs android.Paths
// "srcs" or "data" properties may have filegroup so it might happen that
// the relative root for each source path is different.
for _, path := range pathMappings {
- var relativeRoot string
- relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel())
- if v, found := relativeRootMap[relativeRoot]; found {
- relativeRootMap[relativeRoot] = append(v, path.src)
+ if path.src.Ext() == protoExt {
+ protoSrcs = append(protoSrcs, path.src)
} else {
- relativeRootMap[relativeRoot] = android.Paths{path.src}
+ var relativeRoot string
+ relativeRoot = strings.TrimSuffix(path.src.String(), path.src.Rel())
+ if v, found := relativeRootMap[relativeRoot]; found {
+ relativeRootMap[relativeRoot] = append(v, path.src)
+ } else {
+ relativeRootMap[relativeRoot] = android.Paths{path.src}
+ }
+ }
+ }
+ var zips android.Paths
+ if len(protoSrcs) > 0 {
+ protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
+ protoFlags.OutTypeFlag = "--python_out"
+
+ for _, srcFile := range protoSrcs {
+ zip := genProto(ctx, srcFile, protoFlags, pkgPath)
+ zips = append(zips, zip)
}
}
- var keys []string
+ if len(relativeRootMap) > 0 {
+ var keys []string
- // in order to keep stable order of soong_zip params, we sort the keys here.
- for k := range relativeRootMap {
- keys = append(keys, k)
+ // in order to keep stable order of soong_zip params, we sort the keys here.
+ for k := range relativeRootMap {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ parArgs := []string{}
+ if pkgPath != "" {
+ parArgs = append(parArgs, `-P `+pkgPath)
+ }
+ implicits := android.Paths{}
+ for _, k := range keys {
+ parArgs = append(parArgs, `-C `+k)
+ for _, path := range relativeRootMap[k] {
+ parArgs = append(parArgs, `-f `+path.String())
+ implicits = append(implicits, path)
+ }
+ }
+
+ origSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".py.srcszip")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zip,
+ Description: "python library archive",
+ Output: origSrcsZip,
+ Implicits: implicits,
+ Args: map[string]string{
+ "args": strings.Join(parArgs, " "),
+ },
+ })
+ zips = append(zips, origSrcsZip)
}
- sort.Strings(keys)
-
- for _, k := range keys {
- // use relative root as filelist name.
- fileListPath := registerBuildActionForModuleFileList(
- ctx, strings.Replace(k, "/", "_", -1), relativeRootMap[k])
- ret.fileListSpecs = append(ret.fileListSpecs,
- fileListSpec{fileList: fileListPath, relativeRoot: k})
+ if len(zips) == 1 {
+ return zips[0]
+ } else {
+ combinedSrcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszip")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: combineZip,
+ Description: "combine python library archive",
+ Output: combinedSrcsZip,
+ Inputs: zips,
+ })
+ return combinedSrcsZip
}
-
- return ret
}
func isPythonLibModule(module blueprint.Module) bool {
@@ -504,8 +586,9 @@
return false
}
-// check Python source/data files duplicates from current module and its whole dependencies.
-func (p *Module) uniqWholeRunfilesTree(ctx android.ModuleContext) {
+// check Python source/data files duplicates for whole runfiles tree since Python binary/test
+// need collect and zip all srcs of whole transitive dependencies to a final par file.
+func (p *Module) walkTransitiveDeps(ctx android.ModuleContext) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check duplicates.
destToPySrcs := make(map[string]string)
@@ -518,46 +601,46 @@
destToPyData[path.dest] = path.src.String()
}
+ seen := make(map[android.Module]bool)
+
// visit all its dependencies in depth first.
- ctx.VisitDepsDepthFirst(func(module android.Module) {
- if ctx.OtherModuleDependencyTag(module) != pythonLibTag {
- return
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ if ctx.OtherModuleDependencyTag(child) != pythonLibTag {
+ return false
}
- // Python module cannot depend on modules, except for Python library.
- if !isPythonLibModule(module) {
+ if seen[child] {
+ return false
+ }
+ seen[child] = true
+ // Python modules only can depend on Python libraries.
+ if !isPythonLibModule(child) {
panic(fmt.Errorf(
"the dependency %q of module %q is not Python library!",
- ctx.ModuleName(), ctx.OtherModuleName(module)))
+ ctx.ModuleName(), ctx.OtherModuleName(child)))
}
- if dep, ok := module.(PythonDependency); ok {
+ if dep, ok := child.(PythonDependency); ok {
srcs := dep.GetSrcsPathMappings()
for _, path := range srcs {
if !fillInMap(ctx, destToPySrcs,
- path.dest, path.src.String(), ctx.ModuleName(),
- ctx.OtherModuleName(module)) {
+ path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) {
continue
}
- // binary needs the Python runfiles paths from all its
- // dependencies to fill __init__.py in each runfiles dir.
- p.depsPyRunfiles = append(p.depsPyRunfiles, path.dest)
}
data := dep.GetDataPathMappings()
for _, path := range data {
fillInMap(ctx, destToPyData,
- path.dest, path.src.String(), ctx.ModuleName(),
- ctx.OtherModuleName(module))
+ path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
}
- // binary needs the soong_zip arguments from all its
- // dependencies to generate executable par file.
- p.depsParSpecs = append(p.depsParSpecs, dep.GetParSpec())
+ p.depsSrcsZips = append(p.depsSrcsZips, dep.GetSrcsZip())
}
+ return true
})
}
func fillInMap(ctx android.ModuleContext, m map[string]string,
key, value, curModule, otherModule string) bool {
if oldValue, found := m[key]; found {
- ctx.ModuleErrorf("found two files to be placed at the same runfiles location %q."+
+ ctx.ModuleErrorf("found two files to be placed at the same location within zip %q."+
" First file: in module %s at path %q."+
" Second file: in module %s at path %q.",
key, curModule, oldValue, otherModule, value)
@@ -574,4 +657,5 @@
}
var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/python/python_test.go b/python/python_test.go
index ad80167..e5fe126 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -29,12 +29,11 @@
)
type pyModule struct {
- name string
- actualVersion string
- pyRunfiles []string
- depsPyRunfiles []string
- parSpec string
- depsParSpecs []string
+ name string
+ actualVersion string
+ pyRunfiles []string
+ srcsZip string
+ depsSrcsZips []string
}
var (
@@ -45,12 +44,12 @@
badIdentifierErrTemplate = moduleVariantErrTemplate +
"srcs: the path %q contains invalid token %q."
dupRunfileErrTemplate = moduleVariantErrTemplate +
- "found two files to be placed at the same runfiles location %q." +
+ "found two files to be placed at the same location within zip %q." +
" First file: in module %s at path %q." +
" Second file: in module %s at path %q."
noSrcFileErr = moduleVariantErrTemplate + "doesn't have any source files!"
- badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py) file: %q!"
- badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!"
+ badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
+ badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!"
bpFile = "Blueprints"
data = []struct {
@@ -176,11 +175,11 @@
},
errors: []string{
fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
- "lib1", "PY3", "runfiles/a/b/c/-e/f/file1.py", "-e"),
+ "lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"),
fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
- "lib1", "PY3", "runfiles/a/b/c/.file1.py", ".file1"),
+ "lib1", "PY3", "a/b/c/.file1.py", ".file1"),
fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
- "lib1", "PY3", "runfiles/a/b/c/123/file1.py", "123"),
+ "lib1", "PY3", "a/b/c/123/file1.py", "123"),
},
},
{
@@ -213,7 +212,7 @@
},
errors: []string{
fmt.Sprintf(dupRunfileErrTemplate, "dir/Blueprints:9:6",
- "lib2", "PY3", "runfiles/a/b/c/file1.py", "lib2", "dir/file1.py",
+ "lib2", "PY3", "a/b/c/file1.py", "lib2", "dir/file1.py",
"lib1", "dir/c/file1.py"),
},
},
@@ -308,19 +307,15 @@
name: "bin",
actualVersion: "PY3",
pyRunfiles: []string{
- "runfiles/e/default.py",
- "runfiles/e/bin.py",
- "runfiles/e/default_py3.py",
- "runfiles/e/file4.py",
+ "e/default.py",
+ "e/bin.py",
+ "e/default_py3.py",
+ "e/file4.py",
},
- depsPyRunfiles: []string{
- "runfiles/a/b/file1.py",
- "runfiles/c/d/file2.py",
- },
- parSpec: "-P runfiles/e -C dir/ -l @prefix@/.intermediates/dir/bin/PY3/dir_.list",
- depsParSpecs: []string{
- "-P runfiles/a/b -C dir/ -l @prefix@/.intermediates/dir/lib5/PY3/dir_.list",
- "-P runfiles/c/d -C dir/ -l @prefix@/.intermediates/dir/lib6/PY3/dir_.list",
+ srcsZip: "@prefix@/.intermediates/dir/bin/PY3/bin.py.srcszip",
+ depsSrcsZips: []string{
+ "@prefix@/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
+ "@prefix@/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
},
},
},
@@ -356,8 +351,9 @@
testErrs = append(testErrs,
expectModule(t, ctx, buildDir, e.name,
e.actualVersion,
- e.pyRunfiles, e.depsPyRunfiles,
- e.parSpec, e.depsParSpecs)...)
+ e.srcsZip,
+ e.pyRunfiles,
+ e.depsSrcsZips)...)
}
}
android.FailIfErrored(t, testErrs)
@@ -388,9 +384,8 @@
return
}
-func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant string,
- expPyRunfiles, expDepsPyRunfiles []string,
- expParSpec string, expDepsParSpecs []string) (testErrs []error) {
+func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant, expectedSrcsZip string,
+ expectedPyRunfiles, expectedDepsSrcsZips []string) (testErrs []error) {
module := ctx.ModuleForTests(name, variant)
base, baseOk := module.Module().(*Module)
@@ -398,47 +393,36 @@
t.Fatalf("%s is not Python module!", name)
}
- actPyRunfiles := []string{}
+ actualPyRunfiles := []string{}
for _, path := range base.srcsPathMappings {
- actPyRunfiles = append(actPyRunfiles, path.dest)
+ actualPyRunfiles = append(actualPyRunfiles, path.dest)
}
- if !reflect.DeepEqual(actPyRunfiles, expPyRunfiles) {
+ if !reflect.DeepEqual(actualPyRunfiles, expectedPyRunfiles) {
testErrs = append(testErrs, errors.New(fmt.Sprintf(
`binary "%s" variant "%s" has unexpected pyRunfiles: %q!`,
base.Name(),
base.properties.Actual_version,
- actPyRunfiles)))
+ actualPyRunfiles)))
}
- if !reflect.DeepEqual(base.depsPyRunfiles, expDepsPyRunfiles) {
+ if base.srcsZip.String() != strings.Replace(expectedSrcsZip, "@prefix@", buildDir, 1) {
testErrs = append(testErrs, errors.New(fmt.Sprintf(
- `binary "%s" variant "%s" has unexpected depsPyRunfiles: %q!`,
+ `binary "%s" variant "%s" has unexpected srcsZip: %q!`,
base.Name(),
base.properties.Actual_version,
- base.depsPyRunfiles)))
+ base.srcsZip)))
}
- if base.parSpec.soongParArgs() != strings.Replace(expParSpec, "@prefix@", buildDir, 1) {
+ for i, _ := range expectedDepsSrcsZips {
+ expectedDepsSrcsZips[i] = strings.Replace(expectedDepsSrcsZips[i], "@prefix@", buildDir, 1)
+ }
+ if !reflect.DeepEqual(base.depsSrcsZips.Strings(), expectedDepsSrcsZips) {
testErrs = append(testErrs, errors.New(fmt.Sprintf(
- `binary "%s" variant "%s" has unexpected parSpec: %q!`,
+ `binary "%s" variant "%s" has unexpected depsSrcsZips: %q!`,
base.Name(),
base.properties.Actual_version,
- base.parSpec.soongParArgs())))
- }
-
- actDepsParSpecs := []string{}
- for i, p := range base.depsParSpecs {
- actDepsParSpecs = append(actDepsParSpecs, p.soongParArgs())
- expDepsParSpecs[i] = strings.Replace(expDepsParSpecs[i], "@prefix@", buildDir, 1)
- }
-
- if !reflect.DeepEqual(actDepsParSpecs, expDepsParSpecs) {
- testErrs = append(testErrs, errors.New(fmt.Sprintf(
- `binary "%s" variant "%s" has unexpected depsParSpecs: %q!`,
- base.Name(),
- base.properties.Actual_version,
- actDepsParSpecs)))
+ base.depsSrcsZips)))
}
return
diff --git a/python/scripts/main.py b/python/scripts/main.py
new file mode 100644
index 0000000..225dbe4
--- /dev/null
+++ b/python/scripts/main.py
@@ -0,0 +1,12 @@
+import runpy
+import sys
+
+sys.argv[0] = __loader__.archive
+
+# Set sys.executable to None. The real executable is available as
+# sys.argv[0], and too many things assume sys.executable is a regular Python
+# binary, which isn't available. By setting it to None we get clear errors
+# when people try to use it.
+sys.executable = None
+
+runpy._run_module_as_main("ENTRY_POINT", alter_argv=False)
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index b90a28b..a48a86f 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -11,15 +11,15 @@
PYTHON_BINARY = '%interpreter%'
MAIN_FILE = '%main%'
PYTHON_PATH = 'PYTHONPATH'
-ZIP_RUNFILES_DIRECTORY_NAME = 'runfiles'
+
+# Don't imply 'import site' on initialization
+PYTHON_ARG = '-S'
def SearchPathEnv(name):
search_path = os.getenv('PATH', os.defpath).split(os.pathsep)
for directory in search_path:
if directory == '': continue
path = os.path.join(directory, name)
- if os.path.islink(path):
- path = os.path.realpath(path)
# Check if path is actual executable file.
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
@@ -38,12 +38,13 @@
temp_dir = tempfile.mkdtemp("", "Soong.python_")
zf = zipfile.ZipFile(os.path.dirname(__file__))
zf.extractall(temp_dir)
- return os.path.join(temp_dir, ZIP_RUNFILES_DIRECTORY_NAME)
+ return temp_dir
def Main():
args = sys.argv[1:]
new_env = {}
+ runfiles_path = None
try:
runfiles_path = ExtractRunfiles()
@@ -75,7 +76,7 @@
python_program = FindPythonBinary()
if python_program is None:
raise AssertionError('Could not find python binary: ' + PYTHON_BINARY)
- args = [python_program, main_filepath] + args
+ args = [python_program, PYTHON_ARG, main_filepath] + args
os.environ.update(new_env)
@@ -85,7 +86,8 @@
except:
raise
finally:
- shutil.rmtree(os.path.dirname(runfiles_path), True)
+ if runfiles_path is not None:
+ shutil.rmtree(runfiles_path, True)
if __name__ == '__main__':
Main()
diff --git a/python/test.go b/python/test.go
index 825e63c..55b0ab5 100644
--- a/python/test.go
+++ b/python/test.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
+ "android/soong/tradefed"
)
// This file contains the module types for building Python test.
@@ -25,11 +26,32 @@
android.RegisterModuleType("python_test", PythonTestFactory)
}
+type TestProperties struct {
+ // the name of the test configuration (for example "AndroidTest.xml") that should be
+ // installed with the module.
+ Test_config *string `android:"arch_variant"`
+
+ // the name of the test configuration template (for example "AndroidTestTemplate.xml") that
+ // should be installed with the module.
+ Test_config_template *string `android:"arch_variant"`
+}
+
type testDecorator struct {
*binaryDecorator
+
+ testProperties TestProperties
+
+ testConfig android.Path
+}
+
+func (test *testDecorator) bootstrapperProps() []interface{} {
+ return append(test.binaryDecorator.bootstrapperProps(), &test.testProperties)
}
func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
+ test.testConfig = tradefed.AutoGenPythonBinaryHostTestConfig(ctx, test.testProperties.Test_config,
+ test.testProperties.Test_config_template, test.binaryDecorator.binaryProperties.Test_suites)
+
test.binaryDecorator.pythonInstaller.dir = "nativetest"
test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
diff --git a/ui/build/util_linux.go b/python/tests/Android.bp
similarity index 60%
copy from ui/build/util_linux.go
copy to python/tests/Android.bp
index 0a4e1d2..1f4305c 100644
--- a/ui/build/util_linux.go
+++ b/python/tests/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All rights reserved.
+// Copyright 2019 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,10 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package build
+python_test_host {
+ name: "par_test",
+ main: "par_test.py",
+ srcs: [
+ "par_test.py",
+ "testpkg/par_test.py",
+ ],
-import (
- "syscall"
-)
-
-const ioctlGetTermios = syscall.TCGETS
+ version: {
+ py2: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
diff --git a/python/tests/par_test.py b/python/tests/par_test.py
new file mode 100644
index 0000000..1fafe0f
--- /dev/null
+++ b/python/tests/par_test.py
@@ -0,0 +1,50 @@
+# Copyright 2019 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import site
+import sys
+
+# This file checks the visible python state against expected values when run
+# inside a hermetic par file.
+
+failed = False
+def assert_equal(what, a, b):
+ global failed
+ if a != b:
+ print("Expected %s('%s') == '%s'" % (what, a, b))
+ failed = True
+
+assert_equal("__name__", __name__, "__main__")
+assert_equal("os.path.basename(__file__)", os.path.basename(__file__), "par_test.py")
+
+archive = os.path.dirname(__file__)
+
+assert_equal("__package__", __package__, "")
+assert_equal("sys.argv[0]", sys.argv[0], archive)
+assert_equal("sys.executable", sys.executable, None)
+assert_equal("sys.exec_prefix", sys.exec_prefix, archive)
+assert_equal("sys.prefix", sys.prefix, archive)
+assert_equal("__loader__.archive", __loader__.archive, archive)
+assert_equal("site.ENABLE_USER_SITE", site.ENABLE_USER_SITE, None)
+
+assert_equal("len(sys.path)", len(sys.path), 3)
+assert_equal("sys.path[0]", sys.path[0], archive)
+assert_equal("sys.path[1]", sys.path[1], os.path.join(archive, "internal"))
+assert_equal("sys.path[2]", sys.path[2], os.path.join(archive, "internal", "stdlib"))
+
+if failed:
+ sys.exit(1)
+
+import testpkg.par_test
diff --git a/python/tests/runtest.sh b/python/tests/runtest.sh
new file mode 100755
index 0000000..a319558
--- /dev/null
+++ b/python/tests/runtest.sh
@@ -0,0 +1,39 @@
+#!/bin/bash -e
+#
+# Copyright 2019 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# This is just a helper to run the tests under a few different environments
+#
+
+if [ -z $ANDROID_HOST_OUT ]; then
+ echo "Must be run after running lunch"
+ exit 1
+fi
+
+if [ ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ]; then
+ echo "Run 'm par_test' first"
+ exit 1
+fi
+
+export LD_LIBRARY_PATH=$ANDROID_HOST_OUT/lib64
+
+set -x
+
+PYTHONHOME= PYTHONPATH= $ANDROID_HOST_OUT/nativetest64/par_test/par_test
+PYTHONHOME=/usr $ANDROID_HOST_OUT/nativetest64/par_test/par_test
+PYTHONPATH=/usr $ANDROID_HOST_OUT/nativetest64/par_test/par_test
+
+echo "Passed!"
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
new file mode 100644
index 0000000..22dd095
--- /dev/null
+++ b/python/tests/testpkg/par_test.py
@@ -0,0 +1,37 @@
+# Copyright 2018 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+# This file checks the visible python state against expected values when run
+# inside a hermetic par file.
+
+failed = False
+def assert_equal(what, a, b):
+ global failed
+ if a != b:
+ print("Expected %s('%s') == '%s'" % (what, a, b))
+ failed = True
+
+archive = sys.modules["__main__"].__loader__.archive
+
+assert_equal("__name__", __name__, "testpkg.par_test")
+assert_equal("__file__", __file__, os.path.join(archive, "testpkg/par_test.py"))
+assert_equal("__package__", __package__, "testpkg")
+assert_equal("__loader__.archive", __loader__.archive, archive)
+assert_equal("__loader__.prefix", __loader__.prefix, "testpkg/")
+
+if failed:
+ sys.exit(1)
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
new file mode 100644
index 0000000..860db55
--- /dev/null
+++ b/remoteexec/remoteexec.go
@@ -0,0 +1,167 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package remoteexec
+
+import (
+ "sort"
+ "strings"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+const (
+ // ContainerImageKey is the key identifying the container image in the platform spec.
+ ContainerImageKey = "container-image"
+
+ // PoolKey is the key identifying the pool to use for remote execution.
+ PoolKey = "Pool"
+
+ // DefaultImage is the default container image used for Android remote execution. The
+ // image was built with the Dockerfile at
+ // https://android.googlesource.com/platform/prebuilts/remoteexecution-client/+/refs/heads/master/docker/Dockerfile
+ DefaultImage = "docker://gcr.io/androidbuild-re-dockerimage/android-build-remoteexec-image@sha256:582efb38f0c229ea39952fff9e132ccbe183e14869b39888010dacf56b360d62"
+
+ // DefaultWrapperPath is the default path to the remote execution wrapper.
+ DefaultWrapperPath = "prebuilts/remoteexecution-client/live/rewrapper"
+
+ // DefaultPool is the name of the pool to use for remote execution when none is specified.
+ DefaultPool = "default"
+
+ // LocalExecStrategy is the exec strategy to indicate that the action should be run locally.
+ LocalExecStrategy = "local"
+
+ // RemoteExecStrategy is the exec strategy to indicate that the action should be run
+ // remotely.
+ RemoteExecStrategy = "remote"
+
+ // RemoteLocalFallbackExecStrategy is the exec strategy to indicate that the action should
+ // be run remotely and fallback to local execution if remote fails.
+ RemoteLocalFallbackExecStrategy = "remote_local_fallback"
+)
+
+var (
+ defaultLabels = map[string]string{"type": "tool"}
+ defaultExecStrategy = LocalExecStrategy
+ pctx = android.NewPackageContext("android/soong/remoteexec")
+)
+
+// REParams holds information pertinent to the remote execution of a rule.
+type REParams struct {
+ // Platform is the key value pair used for remotely executing the action.
+ Platform map[string]string
+ // Labels is a map of labels that identify the rule.
+ Labels map[string]string
+ // ExecStrategy is the remote execution strategy: remote, local, or remote_local_fallback.
+ ExecStrategy string
+ // Inputs is a list of input paths or ninja variables.
+ Inputs []string
+ // RSPFile is the name of the ninja variable used by the rule as a placeholder for an rsp
+ // input.
+ RSPFile string
+ // OutputFiles is a list of output file paths or ninja variables as placeholders for rule
+ // outputs.
+ OutputFiles []string
+ // ToolchainInputs is a list of paths or ninja variables pointing to the location of
+ // toolchain binaries used by the rule.
+ ToolchainInputs []string
+}
+
+func init() {
+ pctx.VariableFunc("Wrapper", func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv("RBE_WRAPPER"); override != "" {
+ return override
+ }
+ return DefaultWrapperPath
+ })
+}
+
+// Generate the remote execution wrapper template to be added as a prefix to the rule's command.
+func (r *REParams) Template() string {
+ template := "${remoteexec.Wrapper}"
+
+ var kvs []string
+ labels := r.Labels
+ if len(labels) == 0 {
+ labels = defaultLabels
+ }
+ for k, v := range labels {
+ kvs = append(kvs, k+"="+v)
+ }
+ sort.Strings(kvs)
+ template += " --labels=" + strings.Join(kvs, ",")
+
+ var platform []string
+ for k, v := range r.Platform {
+ if v == "" {
+ continue
+ }
+ platform = append(platform, k+"="+v)
+ }
+ if _, ok := r.Platform[ContainerImageKey]; !ok {
+ platform = append(platform, ContainerImageKey+"="+DefaultImage)
+ }
+ if platform != nil {
+ sort.Strings(platform)
+ template += " --platform=\"" + strings.Join(platform, ",") + "\""
+ }
+
+ strategy := r.ExecStrategy
+ if strategy == "" {
+ strategy = defaultExecStrategy
+ }
+ template += " --exec_strategy=" + strategy
+
+ if len(r.Inputs) > 0 {
+ template += " --inputs=" + strings.Join(r.Inputs, ",")
+ }
+
+ if r.RSPFile != "" {
+ template += " --input_list_paths=" + r.RSPFile
+ }
+
+ if len(r.OutputFiles) > 0 {
+ template += " --output_files=" + strings.Join(r.OutputFiles, ",")
+ }
+
+ if len(r.ToolchainInputs) > 0 {
+ template += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
+ }
+
+ return template + " -- "
+}
+
+// StaticRules returns a pair of rules based on the given RuleParams, where the first rule is a
+// locally executable rule and the second rule is a remotely executable rule.
+func StaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams *REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
+ ruleParamsRE := ruleParams
+ ruleParams.Command = strings.ReplaceAll(ruleParams.Command, "$reTemplate", "")
+ ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, "$reTemplate", reParams.Template())
+
+ return ctx.AndroidStaticRule(name, ruleParams, commonArgs...),
+ ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
+}
+
+// EnvOverrideFunc retrieves a variable func that evaluates to the value of the given environment
+// variable if set, otherwise the given default.
+func EnvOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
+ return func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv(envVar); override != "" {
+ return override
+ }
+ return defaultVal
+ }
+}
diff --git a/remoteexec/remoteexec_test.go b/remoteexec/remoteexec_test.go
new file mode 100644
index 0000000..30e891c
--- /dev/null
+++ b/remoteexec/remoteexec_test.go
@@ -0,0 +1,83 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package remoteexec
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestTemplate(t *testing.T) {
+ tests := []struct {
+ name string
+ params *REParams
+ want string
+ }{
+ {
+ name: "basic",
+ params: &REParams{
+ Labels: map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"},
+ Inputs: []string{"$in"},
+ OutputFiles: []string{"$out"},
+ Platform: map[string]string{
+ ContainerImageKey: DefaultImage,
+ PoolKey: "default",
+ },
+ },
+ want: fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage),
+ },
+ {
+ name: "all params",
+ params: &REParams{
+ Labels: map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"},
+ Inputs: []string{"$in"},
+ OutputFiles: []string{"$out"},
+ ExecStrategy: "remote",
+ RSPFile: "$out.rsp",
+ ToolchainInputs: []string{"clang++"},
+ Platform: map[string]string{
+ ContainerImageKey: DefaultImage,
+ PoolKey: "default",
+ },
+ },
+ want: fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage),
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ if got := test.params.Template(); got != test.want {
+ t.Errorf("Template() returned\n%s\nwant\n%s", got, test.want)
+ }
+ })
+ }
+}
+
+func TestTemplateDeterminism(t *testing.T) {
+ r := &REParams{
+ Labels: map[string]string{"type": "compile", "lang": "cpp", "compiler": "clang"},
+ Inputs: []string{"$in"},
+ OutputFiles: []string{"$out"},
+ Platform: map[string]string{
+ ContainerImageKey: DefaultImage,
+ PoolKey: "default",
+ },
+ }
+ want := fmt.Sprintf("${remoteexec.Wrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage)
+ for i := 0; i < 1000; i++ {
+ if got := r.Template(); got != want {
+ t.Fatalf("Template() returned\n%s\nwant\n%s", got, want)
+ }
+ }
+}
diff --git a/scripts/OWNERS b/scripts/OWNERS
new file mode 100644
index 0000000..076b3f5
--- /dev/null
+++ b/scripts/OWNERS
@@ -0,0 +1 @@
+per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index d150451..947458a 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -34,11 +34,6 @@
SOONG_NDK_OUT=${OUT_DIR}/soong/ndk
rm -rf ${SOONG_OUT}
mkdir -p ${SOONG_OUT}
-cat > ${SOONG_OUT}/soong.config << EOF
-{
- "Ndk_abis": true
-}
-EOF
# We only really need to set some of these variables, but soong won't merge this
# with the defaults, so we need to write out all the defaults with our values
@@ -48,23 +43,13 @@
"Platform_sdk_version": ${PLATFORM_SDK_VERSION},
"Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
- "DeviceName": "flounder",
- "DeviceArch": "arm64",
- "DeviceArchVariant": "armv8-a",
- "DeviceCpuVariant": "denver64",
- "DeviceAbi": [
- "arm64-v8a"
- ],
- "DeviceSecondaryArch": "arm",
- "DeviceSecondaryArchVariant": "armv7-a-neon",
- "DeviceSecondaryCpuVariant": "denver",
- "DeviceSecondaryAbi": [
- "armeabi-v7a"
- ],
+ "DeviceName": "generic_arm64",
"HostArch": "x86_64",
- "HostSecondaryArch": "x86",
"Malloc_not_svelte": false,
- "Safestack": false
+ "Safestack": false,
+
+ "Ndk_abis": true,
+ "Exclude_draft_ndk_apis": true
}
EOF
m --skip-make ${SOONG_OUT}/ndk.timestamp
diff --git a/scripts/build_broken_logs.go b/scripts/build_broken_logs.go
new file mode 100644
index 0000000..f081f26
--- /dev/null
+++ b/scripts/build_broken_logs.go
@@ -0,0 +1,270 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This is a script that can be used to analyze the results from
+// build/soong/build_test.bash and recommend what devices need changes to their
+// BUILD_BROKEN_* flags.
+//
+// To use, download the logs.zip from one or more branches, and extract them
+// into subdirectories of the current directory. So for example, I have:
+//
+// ./aosp-master/aosp_arm/std_full.log
+// ./aosp-master/aosp_arm64/std_full.log
+// ./aosp-master/...
+// ./internal-master/aosp_arm/std_full.log
+// ./internal-master/aosp_arm64/std_full.log
+// ./internal-master/...
+//
+// Then I use `go run path/to/build_broken_logs.go *`
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+)
+
+func main() {
+ for _, branch := range os.Args[1:] {
+ fmt.Printf("\nBranch %s:\n", branch)
+ PrintResults(ParseBranch(branch))
+ }
+}
+
+type BuildBrokenBehavior int
+
+const (
+ DefaultFalse BuildBrokenBehavior = iota
+ DefaultTrue
+ DefaultDeprecated
+)
+
+var buildBrokenSettings = []struct {
+ name string
+ behavior BuildBrokenBehavior
+ warnings []string
+}{
+ {
+ name: "BUILD_BROKEN_DUP_COPY_HEADERS",
+ behavior: DefaultDeprecated,
+ warnings: []string{"Duplicate header copy:"},
+ },
+ {
+ name: "BUILD_BROKEN_DUP_RULES",
+ behavior: DefaultFalse,
+ warnings: []string{"overriding commands for target"},
+ },
+ {
+ name: "BUILD_BROKEN_ANDROIDMK_EXPORTS",
+ behavior: DefaultFalse,
+ warnings: []string{"export_keyword"},
+ },
+ {
+ name: "BUILD_BROKEN_PHONY_TARGETS",
+ behavior: DefaultFalse,
+ warnings: []string{
+ "depends on PHONY target",
+ "looks like a real file",
+ "writing to readonly directory",
+ },
+ },
+ {
+ name: "BUILD_BROKEN_ENG_DEBUG_TAGS",
+ behavior: DefaultTrue,
+ warnings: []string{
+ "Changes.md#LOCAL_MODULE_TAGS",
+ },
+ },
+ {
+ name: "BUILD_BROKEN_USES_NETWORK",
+ behavior: DefaultDeprecated,
+ },
+}
+
+type ProductBranch struct {
+ Branch string
+ Name string
+}
+
+type ProductLog struct {
+ ProductBranch
+ Log
+ Device string
+}
+
+type Log struct {
+ BuildBroken []*bool
+ HasBroken []bool
+}
+
+func Merge(l, l2 Log) Log {
+ if len(l.BuildBroken) == 0 {
+ l.BuildBroken = make([]*bool, len(buildBrokenSettings))
+ }
+ if len(l.HasBroken) == 0 {
+ l.HasBroken = make([]bool, len(buildBrokenSettings))
+ }
+
+ if len(l.BuildBroken) != len(l2.BuildBroken) || len(l.HasBroken) != len(l2.HasBroken) {
+ panic("mis-matched logs")
+ }
+
+ for i, v := range l.BuildBroken {
+ if v == nil {
+ l.BuildBroken[i] = l2.BuildBroken[i]
+ }
+ }
+ for i := range l.HasBroken {
+ l.HasBroken[i] = l.HasBroken[i] || l2.HasBroken[i]
+ }
+
+ return l
+}
+
+func PrintResults(products []ProductLog) {
+ devices := map[string]Log{}
+ deviceNames := []string{}
+
+ for _, product := range products {
+ device := product.Device
+ if _, ok := devices[device]; !ok {
+ deviceNames = append(deviceNames, device)
+ }
+ devices[device] = Merge(devices[device], product.Log)
+ }
+
+ sort.Strings(deviceNames)
+
+ for i, setting := range buildBrokenSettings {
+ printed := false
+
+ for _, device := range deviceNames {
+ log := devices[device]
+
+ if setting.behavior == DefaultTrue {
+ if log.BuildBroken[i] == nil || *log.BuildBroken[i] == false {
+ if log.HasBroken[i] {
+ printed = true
+ fmt.Printf(" %s needs to set %s := true\n", device, setting.name)
+ }
+ } else if !log.HasBroken[i] {
+ printed = true
+ fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name)
+ }
+ } else if setting.behavior == DefaultFalse {
+ if log.BuildBroken[i] == nil {
+ // Nothing to be done
+ } else if *log.BuildBroken[i] == false {
+ printed = true
+ fmt.Printf(" %s sets %s := false, which is the default and can be removed\n", device, setting.name)
+ } else if !log.HasBroken[i] {
+ printed = true
+ fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name)
+ }
+ } else if setting.behavior == DefaultDeprecated {
+ if log.BuildBroken[i] != nil {
+ printed = true
+ if log.HasBroken[i] {
+ fmt.Printf(" %s sets %s := %v, which is deprecated, but has failures\n", device, setting.name, *log.BuildBroken[i])
+ } else {
+ fmt.Printf(" %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[i])
+ }
+ }
+ }
+ }
+
+ if printed {
+ fmt.Println()
+ }
+ }
+}
+
+func ParseBranch(name string) []ProductLog {
+ products, err := filepath.Glob(filepath.Join(name, "*"))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ ret := []ProductLog{}
+ for _, product := range products {
+ product = filepath.Base(product)
+
+ ret = append(ret, ParseProduct(ProductBranch{Branch: name, Name: product}))
+ }
+ return ret
+}
+
+func ParseProduct(p ProductBranch) ProductLog {
+ soongLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "soong.log"))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ ret := ProductLog{
+ ProductBranch: p,
+ Log: Log{
+ BuildBroken: make([]*bool, len(buildBrokenSettings)),
+ HasBroken: make([]bool, len(buildBrokenSettings)),
+ },
+ }
+
+ lines := strings.Split(string(soongLog), "\n")
+ for _, line := range lines {
+ fields := strings.Split(line, " ")
+ if len(fields) != 5 {
+ continue
+ }
+
+ if fields[3] == "TARGET_DEVICE" {
+ ret.Device = fields[4]
+ }
+
+ if strings.HasPrefix(fields[3], "BUILD_BROKEN_") {
+ for i, setting := range buildBrokenSettings {
+ if setting.name == fields[3] {
+ ret.BuildBroken[i] = ParseBoolPtr(fields[4])
+ }
+ }
+ }
+ }
+
+ stdLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "std_full.log"))
+ if err != nil {
+ log.Fatal(err)
+ }
+ stdStr := string(stdLog)
+
+ for i, setting := range buildBrokenSettings {
+ for _, warning := range setting.warnings {
+ if strings.Contains(stdStr, warning) {
+ ret.HasBroken[i] = true
+ }
+ }
+ }
+
+ return ret
+}
+
+func ParseBoolPtr(str string) *bool {
+ var ret *bool
+ if str != "" {
+ b := str == "true"
+ ret = &b
+ }
+ return ret
+}
diff --git a/scripts/clang-tidy.sh b/scripts/clang-tidy.sh
new file mode 100755
index 0000000..04d0bdd
--- /dev/null
+++ b/scripts/clang-tidy.sh
@@ -0,0 +1,37 @@
+#!/bin/bash -e
+
+# Copyright 2018 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Wrapper script to remove clang compiler flags rejected by clang-tidy.
+# Inputs:
+# Environment:
+# CLANG_TIDY: path to the real clang-tidy program
+
+# clang-tidy doesn't recognize every flag that clang compiler does.
+# It gives clang-diagnostic-unused-command-line-argument warnings
+# to -Wa,* flags.
+# The -flto flags caused clang-tidy to ignore the -I flags,
+# see https://bugs.llvm.org/show_bug.cgi?id=38332.
+# -fsanitize and -fwhole-program-vtables need -flto.
+args=("${@}")
+n=${#args[@]}
+for ((i=0; i<$n; ++i)); do
+ case ${args[i]} in
+ -Wa,*|-flto|-flto=*|-fsanitize=*|-fsanitize-*|-fwhole-program-vtables)
+ unset args[i]
+ ;;
+ esac
+done
+${CLANG_TIDY} "${args[@]}"
diff --git a/scripts/copygcclib.sh b/scripts/gen-java-current-api-files.sh
similarity index 63%
rename from scripts/copygcclib.sh
rename to scripts/gen-java-current-api-files.sh
index 28359fc..517d391 100755
--- a/scripts/copygcclib.sh
+++ b/scripts/gen-java-current-api-files.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-# Copyright 2017 Google Inc. All rights reserved.
+# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,8 +14,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-OUT=$1
-shift
-LIBPATH=$($@ | sed -e "s|^$PWD/||")
-cp -f $LIBPATH $OUT
-echo "$OUT: $LIBPATH" > ${OUT}.d
+if [[ -z "$1" ]]; then
+ echo "usage: $0 <modulePath>" >&2
+ exit 1
+fi
+
+api_dir=$1/api
+
+mkdir -p "$api_dir"
+
+scopes=("" system- test-)
+apis=(current removed)
+
+for scope in "${scopes[@]}"; do
+ for api in "${apis[@]}"; do
+ touch "${api_dir}/${scope}${api}.txt"
+ done
+done
diff --git a/scripts/gen-kotlin-build-file.sh b/scripts/gen-kotlin-build-file.sh
index f077a0c..1e03f72 100755
--- a/scripts/gen-kotlin-build-file.sh
+++ b/scripts/gen-kotlin-build-file.sh
@@ -16,7 +16,7 @@
# Generates kotlinc module xml file to standard output based on rsp files
-if [ -z "$1" ]; then
+if [[ -z "$1" ]]; then
echo "usage: $0 <classpath> <outDir> <rspFiles>..." >&2
exit 1
fi
@@ -30,24 +30,36 @@
out_dir=$2
shift 2
-# Path in the build file are relative to the build file, we need to make them absolute.
-prefix=`pwd`
+# Path in the build file may be relative to the build file, we need to make them
+# absolute
+prefix="$(pwd)"
+
+get_abs_path () {
+ local file="$1"
+ if [[ "${file:0:1}" == '/' ]] ; then
+ echo "${file}"
+ else
+ echo "${prefix}/${file}"
+ fi
+}
# Print preamble
echo "<modules><module name=\"name\" type=\"java-production\" outputDir=\"${out_dir}\">"
# Print classpath entries
-for file in $(echo $classpath | tr ":" "\n"); do
- echo " <classpath path=\"${prefix}/${file}\"/>"
+for file in $(echo "$classpath" | tr ":" "\n"); do
+ path="$(get_abs_path "$file")"
+ echo " <classpath path=\"${path}\"/>"
done
# For each rsp file, print source entries
while (( "$#" )); do
- for file in $(cat $1); do
+ for file in $(cat "$1"); do
+ path="$(get_abs_path "$file")"
if [[ $file == *.java ]]; then
- echo " <javaSourceRoots path=\"${prefix}/${file}\"/>"
+ echo " <javaSourceRoots path=\"${path}\"/>"
elif [[ $file == *.kt ]]; then
- echo " <sources path=\"${prefix}/${file}\"/>"
+ echo " <sources path=\"${path}\"/>"
else
echo "Unknown source file type ${file}"
exit 1
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
new file mode 100755
index 0000000..83868e6
--- /dev/null
+++ b/scripts/manifest_fixer.py
@@ -0,0 +1,368 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A tool for inserting values from the build system into a manifest."""
+
+from __future__ import print_function
+import argparse
+import sys
+from xml.dom import minidom
+
+
+android_ns = 'http://schemas.android.com/apk/res/android'
+
+
+def get_children_with_tag(parent, tag_name):
+ children = []
+ for child in parent.childNodes:
+ if child.nodeType == minidom.Node.ELEMENT_NODE and \
+ child.tagName == tag_name:
+ children.append(child)
+ return children
+
+
+def find_child_with_attribute(element, tag_name, namespace_uri,
+ attr_name, value):
+ for child in get_children_with_tag(element, tag_name):
+ attr = child.getAttributeNodeNS(namespace_uri, attr_name)
+ if attr is not None and attr.value == value:
+ return child
+ return None
+
+
+def parse_args():
+ """Parse commandline arguments."""
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
+ help='specify minSdkVersion used by the build system')
+ parser.add_argument('--targetSdkVersion', default='', dest='target_sdk_version',
+ help='specify targetSdkVersion used by the build system')
+ parser.add_argument('--raise-min-sdk-version', dest='raise_min_sdk_version', action='store_true',
+ help='raise the minimum sdk version in the manifest if necessary')
+ parser.add_argument('--library', dest='library', action='store_true',
+ help='manifest is for a static library')
+ parser.add_argument('--uses-library', dest='uses_libraries', action='append',
+ help='specify additional <uses-library> tag to add. android:requred is set to true')
+ parser.add_argument('--optional-uses-library', dest='optional_uses_libraries', action='append',
+ help='specify additional <uses-library> tag to add. android:requred is set to false')
+ parser.add_argument('--uses-non-sdk-api', dest='uses_non_sdk_api', action='store_true',
+ help='manifest is for a package built against the platform')
+ parser.add_argument('--use-embedded-dex', dest='use_embedded_dex', action='store_true',
+ help=('specify if the app wants to use embedded dex and avoid extracted,'
+ 'locally compiled code. Must not conflict if already declared '
+ 'in the manifest.'))
+ parser.add_argument('--extract-native-libs', dest='extract_native_libs',
+ default=None, type=lambda x: (str(x).lower() == 'true'),
+ help=('specify if the app wants to use embedded native libraries. Must not conflict '
+ 'if already declared in the manifest.'))
+ parser.add_argument('input', help='input AndroidManifest.xml file')
+ parser.add_argument('output', help='output AndroidManifest.xml file')
+ return parser.parse_args()
+
+
+def parse_manifest(doc):
+ """Get the manifest element."""
+
+ manifest = doc.documentElement
+ if manifest.tagName != 'manifest':
+ raise RuntimeError('expected manifest tag at root')
+ return manifest
+
+
+def ensure_manifest_android_ns(doc):
+ """Make sure the manifest tag defines the android namespace."""
+
+ manifest = parse_manifest(doc)
+
+ ns = manifest.getAttributeNodeNS(minidom.XMLNS_NAMESPACE, 'android')
+ if ns is None:
+ attr = doc.createAttributeNS(minidom.XMLNS_NAMESPACE, 'xmlns:android')
+ attr.value = android_ns
+ manifest.setAttributeNode(attr)
+ elif ns.value != android_ns:
+ raise RuntimeError('manifest tag has incorrect android namespace ' +
+ ns.value)
+
+
+def as_int(s):
+ try:
+ i = int(s)
+ except ValueError:
+ return s, False
+ return i, True
+
+
+def compare_version_gt(a, b):
+ """Compare two SDK versions.
+
+ Compares a and b, treating codenames like 'Q' as higher
+ than numerical versions like '28'.
+
+ Returns True if a > b
+
+ Args:
+ a: value to compare
+ b: value to compare
+ Returns:
+ True if a is a higher version than b
+ """
+
+ a, a_is_int = as_int(a.upper())
+ b, b_is_int = as_int(b.upper())
+
+ if a_is_int == b_is_int:
+ # Both are codenames or both are versions, compare directly
+ return a > b
+ else:
+ # One is a codename, the other is not. Return true if
+ # b is an integer version
+ return b_is_int
+
+
+def get_indent(element, default_level):
+ indent = ''
+ if element is not None and element.nodeType == minidom.Node.TEXT_NODE:
+ text = element.nodeValue
+ indent = text[:len(text)-len(text.lstrip())]
+ if not indent or indent == '\n':
+ # 1 indent = 4 space
+ indent = '\n' + (' ' * default_level * 4)
+ return indent
+
+
+def raise_min_sdk_version(doc, min_sdk_version, target_sdk_version, library):
+ """Ensure the manifest contains a <uses-sdk> tag with a minSdkVersion.
+
+ Args:
+ doc: The XML document. May be modified by this function.
+ min_sdk_version: The requested minSdkVersion attribute.
+ target_sdk_version: The requested targetSdkVersion attribute.
+ Raises:
+ RuntimeError: invalid manifest
+ """
+
+ manifest = parse_manifest(doc)
+
+ # Get or insert the uses-sdk element
+ uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
+ if len(uses_sdk) > 1:
+ raise RuntimeError('found multiple uses-sdk elements')
+ elif len(uses_sdk) == 1:
+ element = uses_sdk[0]
+ else:
+ element = doc.createElement('uses-sdk')
+ indent = get_indent(manifest.firstChild, 1)
+ manifest.insertBefore(element, manifest.firstChild)
+
+ # Insert an indent before uses-sdk to line it up with the indentation of the
+ # other children of the <manifest> tag.
+ manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
+
+ # Get or insert the minSdkVersion attribute. If it is already present, make
+ # sure it as least the requested value.
+ min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion')
+ if min_attr is None:
+ min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
+ min_attr.value = min_sdk_version
+ element.setAttributeNode(min_attr)
+ else:
+ if compare_version_gt(min_sdk_version, min_attr.value):
+ min_attr.value = min_sdk_version
+
+ # Insert the targetSdkVersion attribute if it is missing. If it is already
+ # present leave it as is.
+ target_attr = element.getAttributeNodeNS(android_ns, 'targetSdkVersion')
+ if target_attr is None:
+ target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion')
+ if library:
+ # TODO(b/117122200): libraries shouldn't set targetSdkVersion at all, but
+ # ManifestMerger treats minSdkVersion="Q" as targetSdkVersion="Q" if it
+ # is empty. Set it to something low so that it will be overriden by the
+ # main manifest, but high enough that it doesn't cause implicit
+ # permissions grants.
+ target_attr.value = '15'
+ else:
+ target_attr.value = target_sdk_version
+ element.setAttributeNode(target_attr)
+
+
+def add_uses_libraries(doc, new_uses_libraries, required):
+ """Add additional <uses-library> tags
+
+ Args:
+ doc: The XML document. May be modified by this function.
+ new_uses_libraries: The names of libraries to be added by this function.
+ required: The value of android:required attribute. Can be true or false.
+ Raises:
+ RuntimeError: Invalid manifest
+ """
+
+ manifest = parse_manifest(doc)
+ elems = get_children_with_tag(manifest, 'application')
+ application = elems[0] if len(elems) == 1 else None
+ if len(elems) > 1:
+ raise RuntimeError('found multiple <application> tags')
+ elif not elems:
+ application = doc.createElement('application')
+ indent = get_indent(manifest.firstChild, 1)
+ first = manifest.firstChild
+ manifest.insertBefore(doc.createTextNode(indent), first)
+ manifest.insertBefore(application, first)
+
+ indent = get_indent(application.firstChild, 2)
+
+ last = application.lastChild
+ if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
+ last = None
+
+ for name in new_uses_libraries:
+ if find_child_with_attribute(application, 'uses-library', android_ns,
+ 'name', name) is not None:
+ # If the uses-library tag of the same 'name' attribute value exists,
+ # respect it.
+ continue
+
+ ul = doc.createElement('uses-library')
+ ul.setAttributeNS(android_ns, 'android:name', name)
+ ul.setAttributeNS(android_ns, 'android:required', str(required).lower())
+
+ application.insertBefore(doc.createTextNode(indent), last)
+ application.insertBefore(ul, last)
+
+ # align the closing tag with the opening tag if it's not
+ # indented
+ if application.lastChild.nodeType != minidom.Node.TEXT_NODE:
+ indent = get_indent(application.previousSibling, 1)
+ application.appendChild(doc.createTextNode(indent))
+
+def add_uses_non_sdk_api(doc):
+ """Add android:usesNonSdkApi=true attribute to <application>.
+
+ Args:
+ doc: The XML document. May be modified by this function.
+ Raises:
+ RuntimeError: Invalid manifest
+ """
+
+ manifest = parse_manifest(doc)
+ elems = get_children_with_tag(manifest, 'application')
+ application = elems[0] if len(elems) == 1 else None
+ if len(elems) > 1:
+ raise RuntimeError('found multiple <application> tags')
+ elif not elems:
+ application = doc.createElement('application')
+ indent = get_indent(manifest.firstChild, 1)
+ first = manifest.firstChild
+ manifest.insertBefore(doc.createTextNode(indent), first)
+ manifest.insertBefore(application, first)
+
+ attr = application.getAttributeNodeNS(android_ns, 'usesNonSdkApi')
+ if attr is None:
+ attr = doc.createAttributeNS(android_ns, 'android:usesNonSdkApi')
+ attr.value = 'true'
+ application.setAttributeNode(attr)
+
+
+def add_use_embedded_dex(doc):
+ manifest = parse_manifest(doc)
+ elems = get_children_with_tag(manifest, 'application')
+ application = elems[0] if len(elems) == 1 else None
+ if len(elems) > 1:
+ raise RuntimeError('found multiple <application> tags')
+ elif not elems:
+ application = doc.createElement('application')
+ indent = get_indent(manifest.firstChild, 1)
+ first = manifest.firstChild
+ manifest.insertBefore(doc.createTextNode(indent), first)
+ manifest.insertBefore(application, first)
+
+ attr = application.getAttributeNodeNS(android_ns, 'useEmbeddedDex')
+ if attr is None:
+ attr = doc.createAttributeNS(android_ns, 'android:useEmbeddedDex')
+ attr.value = 'true'
+ application.setAttributeNode(attr)
+ elif attr.value != 'true':
+ raise RuntimeError('existing attribute mismatches the option of --use-embedded-dex')
+
+
+def add_extract_native_libs(doc, extract_native_libs):
+ manifest = parse_manifest(doc)
+ elems = get_children_with_tag(manifest, 'application')
+ application = elems[0] if len(elems) == 1 else None
+ if len(elems) > 1:
+ raise RuntimeError('found multiple <application> tags')
+ elif not elems:
+ application = doc.createElement('application')
+ indent = get_indent(manifest.firstChild, 1)
+ first = manifest.firstChild
+ manifest.insertBefore(doc.createTextNode(indent), first)
+ manifest.insertBefore(application, first)
+
+ value = str(extract_native_libs).lower()
+ attr = application.getAttributeNodeNS(android_ns, 'extractNativeLibs')
+ if attr is None:
+ attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs')
+ attr.value = value
+ application.setAttributeNode(attr)
+ elif attr.value != value:
+ raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' %
+ (attr.value, value))
+
+
+def write_xml(f, doc):
+ f.write('<?xml version="1.0" encoding="utf-8"?>\n')
+ for node in doc.childNodes:
+ f.write(node.toxml(encoding='utf-8') + '\n')
+
+
+def main():
+ """Program entry point."""
+ try:
+ args = parse_args()
+
+ doc = minidom.parse(args.input)
+
+ ensure_manifest_android_ns(doc)
+
+ if args.raise_min_sdk_version:
+ raise_min_sdk_version(doc, args.min_sdk_version, args.target_sdk_version, args.library)
+
+ if args.uses_libraries:
+ add_uses_libraries(doc, args.uses_libraries, True)
+
+ if args.optional_uses_libraries:
+ add_uses_libraries(doc, args.optional_uses_libraries, False)
+
+ if args.uses_non_sdk_api:
+ add_uses_non_sdk_api(doc)
+
+ if args.use_embedded_dex:
+ add_use_embedded_dex(doc)
+
+ if args.extract_native_libs is not None:
+ add_extract_native_libs(doc, args.extract_native_libs)
+
+ with open(args.output, 'wb') as f:
+ write_xml(f, doc)
+
+ # pylint: disable=broad-except
+ except Exception as err:
+ print('error: ' + str(err), file=sys.stderr)
+ sys.exit(-1)
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
new file mode 100755
index 0000000..4ad9afa
--- /dev/null
+++ b/scripts/manifest_fixer_test.py
@@ -0,0 +1,428 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Unit tests for manifest_fixer_test.py."""
+
+import StringIO
+import sys
+import unittest
+from xml.dom import minidom
+
+import manifest_fixer
+
+sys.dont_write_bytecode = True
+
+
+class CompareVersionGtTest(unittest.TestCase):
+ """Unit tests for compare_version_gt function."""
+
+ def test_sdk(self):
+ """Test comparing sdk versions."""
+ self.assertTrue(manifest_fixer.compare_version_gt('28', '27'))
+ self.assertFalse(manifest_fixer.compare_version_gt('27', '28'))
+ self.assertFalse(manifest_fixer.compare_version_gt('28', '28'))
+
+ def test_codename(self):
+ """Test comparing codenames."""
+ self.assertTrue(manifest_fixer.compare_version_gt('Q', 'P'))
+ self.assertFalse(manifest_fixer.compare_version_gt('P', 'Q'))
+ self.assertFalse(manifest_fixer.compare_version_gt('Q', 'Q'))
+
+ def test_sdk_codename(self):
+ """Test comparing sdk versions with codenames."""
+ self.assertTrue(manifest_fixer.compare_version_gt('Q', '28'))
+ self.assertFalse(manifest_fixer.compare_version_gt('28', 'Q'))
+
+ def test_compare_numeric(self):
+ """Test that numbers are compared in numeric and not lexicographic order."""
+ self.assertTrue(manifest_fixer.compare_version_gt('18', '8'))
+
+
+class RaiseMinSdkVersionTest(unittest.TestCase):
+ """Unit tests for raise_min_sdk_version function."""
+
+ def raise_min_sdk_version_test(self, input_manifest, min_sdk_version,
+ target_sdk_version, library):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.raise_min_sdk_version(doc, min_sdk_version,
+ target_sdk_version, library)
+ output = StringIO.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ '%s'
+ '</manifest>\n')
+
+ # pylint: disable=redefined-builtin
+ def uses_sdk(self, min=None, target=None, extra=''):
+ attrs = ''
+ if min:
+ attrs += ' android:minSdkVersion="%s"' % (min)
+ if target:
+ attrs += ' android:targetSdkVersion="%s"' % (target)
+ if extra:
+ attrs += ' ' + extra
+ return ' <uses-sdk%s/>\n' % (attrs)
+
+ def test_no_uses_sdk(self):
+ """Tests inserting a uses-sdk element into a manifest."""
+
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
+ self.assertEqual(output, expected)
+
+ def test_no_min(self):
+ """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
+
+ manifest_input = self.manifest_tmpl % ' <uses-sdk extra="foo"/>\n'
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28',
+ extra='extra="foo"')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
+ self.assertEqual(output, expected)
+
+ def test_raise_min(self):
+ """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
+ self.assertEqual(output, expected)
+
+ def test_raise(self):
+ """Tests raising a minSdkVersion attribute."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
+ self.assertEqual(output, expected)
+
+ def test_no_raise_min(self):
+ """Tests a minSdkVersion that doesn't need raising."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+ output = self.raise_min_sdk_version_test(manifest_input, '27', '27', False)
+ self.assertEqual(output, expected)
+
+ def test_raise_codename(self):
+ """Tests raising a minSdkVersion attribute to a codename."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
+ expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P')
+ output = self.raise_min_sdk_version_test(manifest_input, 'P', 'P', False)
+ self.assertEqual(output, expected)
+
+ def test_no_raise_codename(self):
+ """Tests a minSdkVersion codename that doesn't need raising."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='P')
+ expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
+ self.assertEqual(output, expected)
+
+ def test_target(self):
+ """Tests an existing targetSdkVersion is preserved."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
+ self.assertEqual(output, expected)
+
+ def test_no_target(self):
+ """Tests inserting targetSdkVersion when minSdkVersion exists."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
+ self.assertEqual(output, expected)
+
+ def test_target_no_min(self):
+ """"Tests inserting targetSdkVersion when minSdkVersion exists."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
+ self.assertEqual(output, expected)
+
+ def test_no_target_no_min(self):
+ """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
+
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
+ self.assertEqual(output, expected)
+
+ def test_library_no_target(self):
+ """Tests inserting targetSdkVersion when minSdkVersion exists."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
+ self.assertEqual(output, expected)
+
+ def test_library_target_no_min(self):
+ """Tests inserting targetSdkVersion when minSdkVersion exists."""
+
+ manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
+ self.assertEqual(output, expected)
+
+ def test_library_no_target_no_min(self):
+ """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
+
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
+ self.assertEqual(output, expected)
+
+ def test_extra(self):
+ """Tests that extra attributes and elements are maintained."""
+
+ manifest_input = self.manifest_tmpl % (
+ ' <!-- comment -->\n'
+ ' <uses-sdk android:minSdkVersion="27" extra="foo"/>\n'
+ ' <application/>\n')
+
+ # pylint: disable=line-too-long
+ expected = self.manifest_tmpl % (
+ ' <!-- comment -->\n'
+ ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29" extra="foo"/>\n'
+ ' <application/>\n')
+
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
+
+ self.assertEqual(output, expected)
+
+ def test_indent(self):
+ """Tests that an inserted element copies the existing indentation."""
+
+ manifest_input = self.manifest_tmpl % ' <!-- comment -->\n'
+
+ # pylint: disable=line-too-long
+ expected = self.manifest_tmpl % (
+ ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29"/>\n'
+ ' <!-- comment -->\n')
+
+ output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
+
+ self.assertEqual(output, expected)
+
+
+class AddUsesLibrariesTest(unittest.TestCase):
+ """Unit tests for add_uses_libraries function."""
+
+ def run_test(self, input_manifest, new_uses_libraries):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.add_uses_libraries(doc, new_uses_libraries)
+ output = StringIO.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ ' <application>\n'
+ '%s'
+ ' </application>\n'
+ '</manifest>\n')
+
+ def uses_libraries(self, name_required_pairs):
+ ret = ''
+ for name, required in name_required_pairs:
+ ret += (
+ ' <uses-library android:name="%s" android:required="%s"/>\n'
+ ) % (name, required)
+
+ return ret
+
+ def test_empty(self):
+ """Empty new_uses_libraries must not touch the manifest."""
+ manifest_input = self.manifest_tmpl % self.uses_libraries([
+ ('foo', 'true'),
+ ('bar', 'false')])
+ expected = manifest_input
+ output = self.run_test(manifest_input, [])
+ self.assertEqual(output, expected)
+
+ def test_not_overwrite(self):
+ """new_uses_libraries must not overwrite existing tags."""
+ manifest_input = self.manifest_tmpl % self.uses_libraries([
+ ('foo', 'true'),
+ ('bar', 'false')])
+ expected = manifest_input
+ output = self.run_test(manifest_input, ['foo', 'bar'])
+ self.assertEqual(output, expected)
+
+ def test_add(self):
+ """New names are added with 'required:true'."""
+ manifest_input = self.manifest_tmpl % self.uses_libraries([
+ ('foo', 'true'),
+ ('bar', 'false')])
+ expected = self.manifest_tmpl % self.uses_libraries([
+ ('foo', 'true'),
+ ('bar', 'false'),
+ ('baz', 'true'),
+ ('qux', 'true')])
+ output = self.run_test(manifest_input, ['bar', 'baz', 'qux'])
+ self.assertEqual(output, expected)
+
+ def test_no_application(self):
+ """When there is no <application> tag, the tag is added."""
+ manifest_input = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android='
+ '"http://schemas.android.com/apk/res/android">\n'
+ '</manifest>\n')
+ expected = self.manifest_tmpl % self.uses_libraries([
+ ('foo', 'true'),
+ ('bar', 'true')])
+ output = self.run_test(manifest_input, ['foo', 'bar'])
+ self.assertEqual(output, expected)
+
+ def test_empty_application(self):
+ """Even when here is an empty <application/> tag, the libs are added."""
+ manifest_input = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android='
+ '"http://schemas.android.com/apk/res/android">\n'
+ ' <application/>\n'
+ '</manifest>\n')
+ expected = self.manifest_tmpl % self.uses_libraries([
+ ('foo', 'true'),
+ ('bar', 'true')])
+ output = self.run_test(manifest_input, ['foo', 'bar'])
+ self.assertEqual(output, expected)
+
+
+class AddUsesNonSdkApiTest(unittest.TestCase):
+ """Unit tests for add_uses_libraries function."""
+
+ def run_test(self, input_manifest):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.add_uses_non_sdk_api(doc)
+ output = StringIO.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ ' <application%s/>\n'
+ '</manifest>\n')
+
+ def uses_non_sdk_api(self, value):
+ return ' android:usesNonSdkApi="true"' if value else ''
+
+ def test_set_true(self):
+ """Empty new_uses_libraries must not touch the manifest."""
+ manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(False)
+ expected = self.manifest_tmpl % self.uses_non_sdk_api(True)
+ output = self.run_test(manifest_input)
+ self.assertEqual(output, expected)
+
+ def test_already_set(self):
+ """new_uses_libraries must not overwrite existing tags."""
+ manifest_input = self.manifest_tmpl % self.uses_non_sdk_api(True)
+ expected = manifest_input
+ output = self.run_test(manifest_input)
+ self.assertEqual(output, expected)
+
+
+class UseEmbeddedDexTest(unittest.TestCase):
+ """Unit tests for add_use_embedded_dex function."""
+
+ def run_test(self, input_manifest):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.add_use_embedded_dex(doc)
+ output = StringIO.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ ' <application%s/>\n'
+ '</manifest>\n')
+
+ def use_embedded_dex(self, value):
+ return ' android:useEmbeddedDex="%s"' % value
+
+ def test_manifest_with_undeclared_preference(self):
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.use_embedded_dex('true')
+ output = self.run_test(manifest_input)
+ self.assertEqual(output, expected)
+
+ def test_manifest_with_use_embedded_dex(self):
+ manifest_input = self.manifest_tmpl % self.use_embedded_dex('true')
+ expected = manifest_input
+ output = self.run_test(manifest_input)
+ self.assertEqual(output, expected)
+
+ def test_manifest_with_not_use_embedded_dex(self):
+ manifest_input = self.manifest_tmpl % self.use_embedded_dex('false')
+ self.assertRaises(RuntimeError, self.run_test, manifest_input)
+
+
+class AddExtractNativeLibsTest(unittest.TestCase):
+ """Unit tests for add_extract_native_libs function."""
+
+ def run_test(self, input_manifest, value):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.add_extract_native_libs(doc, value)
+ output = StringIO.StringIO()
+ manifest_fixer.write_xml(output, doc)
+ return output.getvalue()
+
+ manifest_tmpl = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+ ' <application%s/>\n'
+ '</manifest>\n')
+
+ def extract_native_libs(self, value):
+ return ' android:extractNativeLibs="%s"' % value
+
+ def test_set_true(self):
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.extract_native_libs('true')
+ output = self.run_test(manifest_input, True)
+ self.assertEqual(output, expected)
+
+ def test_set_false(self):
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.extract_native_libs('false')
+ output = self.run_test(manifest_input, False)
+ self.assertEqual(output, expected)
+
+ def test_match(self):
+ manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
+ expected = manifest_input
+ output = self.run_test(manifest_input, True)
+ self.assertEqual(output, expected)
+
+ def test_conflict(self):
+ manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
+ self.assertRaises(RuntimeError, self.run_test, manifest_input, False)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/scripts/mergenotice.py b/scripts/mergenotice.py
new file mode 100755
index 0000000..407ae8c
--- /dev/null
+++ b/scripts/mergenotice.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""
+Merges input notice files to the output file while ignoring duplicated files
+This script shouldn't be confused with build/make/tools/generate-notice-files.py
+which is responsible for creating the final notice file for all artifacts
+installed. This script has rather limited scope; it is meant to create a merged
+notice file for a set of modules that are packaged together, e.g. in an APEX.
+The merged notice file does not reveal the individual files in the package.
+"""
+
+import sys
+import argparse
+
+def get_args():
+ parser = argparse.ArgumentParser(description='Merge notice files.')
+ parser.add_argument('--output', help='output file path.')
+ parser.add_argument('inputs', metavar='INPUT', nargs='+',
+ help='input notice file')
+ return parser.parse_args()
+
+def main(argv):
+ args = get_args()
+
+ processed = set()
+ with open(args.output, 'w+') as output:
+ for input in args.inputs:
+ with open(input, 'r') as f:
+ data = f.read().strip()
+ if data not in processed:
+ processed.add(data)
+ output.write('%s\n\n' % data)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/scripts/microfactory.bash b/scripts/microfactory.bash
index 65ba55d..4bb6058 100644
--- a/scripts/microfactory.bash
+++ b/scripts/microfactory.bash
@@ -59,7 +59,7 @@
BUILDDIR=$(getoutdir) \
SRCDIR=${TOP} \
BLUEPRINTDIR=${TOP}/build/blueprint \
- EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong" \
+ EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path github.com/golang/protobuf=${TOP}/external/golang-protobuf" \
build_go $@
}
diff --git a/scripts/package-check.sh b/scripts/package-check.sh
new file mode 100755
index 0000000..f982e82
--- /dev/null
+++ b/scripts/package-check.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+if [[ $# -le 1 ]]; then
+ cat <<EOF
+Usage:
+ package-check.sh <jar-file> <package-list>
+Checks that the class files in the <jar file> are in the <package-list> or
+sub-packages.
+EOF
+ exit 1
+fi
+
+jar_file=$1
+shift
+if [[ ! -f ${jar_file} ]]; then
+ echo "jar file \"${jar_file}\" does not exist."
+ exit 1
+fi
+
+prefixes=()
+while [[ $# -ge 1 ]]; do
+ package="$1"
+ if [[ "${package}" = */* ]]; then
+ echo "Invalid package \"${package}\". Use dot notation for packages."
+ exit 1
+ fi
+ # Transform to a slash-separated path and add a trailing slash to enforce
+ # package name boundary.
+ prefixes+=("${package//\./\/}/")
+ shift
+done
+
+# Get the file names from the jar file.
+zip_contents=`zipinfo -1 $jar_file`
+
+# Check all class file names against the expected prefixes.
+old_ifs=${IFS}
+IFS=$'\n'
+for zip_entry in ${zip_contents}; do
+ # Check the suffix.
+ if [[ "${zip_entry}" = *.class ]]; then
+ # Match against prefixes.
+ found=false
+ for prefix in ${prefixes[@]}; do
+ if [[ "${zip_entry}" = "${prefix}"* ]]; then
+ found=true
+ break
+ fi
+ done
+ if [[ "${found}" == "false" ]]; then
+ echo "Class file ${zip_entry} is outside specified packages."
+ exit 1
+ fi
+ fi
+done
+IFS=${old_ifs}
diff --git a/scripts/setup_go_workspace_for_soong.sh b/scripts/setup_go_workspace_for_soong.sh
index e2fb9fa..6374aae 100755
--- a/scripts/setup_go_workspace_for_soong.sh
+++ b/scripts/setup_go_workspace_for_soong.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
-# Copyright 2017 Google Inc. All rights reserved.
+# Copyright 2019 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,23 +15,174 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-#mounts the components of soong into a directory structure that Go tools and editors expect
+# Mounts the components of soong into a directory structure that Go tools
+# and editors expect.
-#move to the script's directory
-cd "$(dirname $0)"
-SCRIPT_PATH="$PWD"
-#find the root of the Repo checkout
-cd "${SCRIPT_PATH}"/../../..
-ANDROID_PATH="${PWD}"
-OUTPUT_PATH="$(echo ${GOPATH} | sed 's/\:.*//')" #if GOPATH contains multiple paths, use the first one
-
-if [ -z "${OUTPUT_PATH}" ]; then
- echo "Error; could not determine the desired location at which to create a Go-compatible workspace. Please update GOPATH to specify the desired destination directory"
+#####################################################################
+# Print the message to stderr with the prefix ERROR and abort this
+# script.
+#####################################################################
+function log_FATAL() {
+ echo "ERROR:" "$*" >&2
exit 1
-fi
+}
-function confirm() {
+#####################################################################
+# Print the message to stderr with the prefix WARN
+#####################################################################
+function log_WARN() {
+ echo "WARN:" "$*" >&2
+}
+
+
+#####################################################################
+# Print the message with the prefix INFO.
+#####################################################################
+function log_INFO() {
+ echo "INFO:" "$*"
+}
+
+
+#####################################################################
+# Find the root project directory of this repo. This is done by
+# finding the directory of where this script lives and then go up one
+# directory to check the ".repo" directory exist. If not, keep going
+# up until we find the ".repo" file or we reached to the filesystem
+# root. Project root directory is printed to stdout.
+#####################################################################
+function root_dir() (
+ local dir
+ if ! dir="$("${readlink}" -e $(dirname "$0"))"; then
+ log_FATAL "failed to read the script's current directory."
+ fi
+
+ dir=${dir}/../../..
+ if ! dir="$("${readlink}" -e "${dir}")"; then
+ log_FATAL "Cannot find the root project directory"
+ fi
+
+ echo "${dir}"
+)
+
+
+#####################################################################
+# executes a shell command by printing out to the screen first and
+# then evaluating the command.
+#####################################################################
+function execute() {
+ echo "$@"
+ eval "$@"
+}
+
+
+#####################################################################
+# Returns the source directory of a passed in path from BIND_PATHS
+# array.
+#####################################################################
+function bind_path_src_dir() (
+ local -r bind_path="$1"
+ echo "${bind_path/%|*/}"
+)
+
+
+#####################################################################
+# Returns the destination directory of a passed in path from
+# BIND_PATHS array.
+#####################################################################
+function bind_path_dst_dir() (
+ local -r bind_path="$1"
+ echo "${bind_path/#*|}"
+)
+
+
+#####################################################################
+# Executes the bindfs command in linux. Expects $1 to be src
+# directory and $2 to be destination directory.
+#####################################################################
+function linux_bind_dir() (
+ execute bindfs "$1" "$2"
+)
+
+#####################################################################
+# Executes the fusermount -u command in linux. Expects $1 to be the
+# destination directory.
+#####################################################################
+function linux_unbind_dir() (
+ execute fusermount -u "$1"
+)
+
+#####################################################################
+# Executes the bindfs command in darwin. Expects $1 to be src
+# directory and $2 to be destination directory.
+#####################################################################
+function darwin_bind_dir() (
+ execute bindfs -o allow_recursion -n "$1" "$2"
+)
+
+
+#####################################################################
+# Execute the umount command in darwin to unbind a directory. Expects
+# $1 to be the destination directory
+#####################################################################
+function darwin_unbind_dir() (
+ execute umount -f "$1"
+)
+
+
+#####################################################################
+# Bind all the paths that are specified in the BIND_PATHS array.
+#####################################################################
+function bind_all() (
+ local src_dir
+ local dst_dir
+
+ for path in ${BIND_PATHS[@]}; do
+ src_dir=$(bind_path_src_dir "${path}")
+
+ dst_dir=$(bind_path_dst_dir "${path}")
+ mkdir -p "${dst_dir}"
+
+ "${bind_dir}" ${src_dir} "${dst_dir}"
+ done
+
+ echo
+ log_INFO "Created GOPATH-compatible directory structure at ${OUTPUT_PATH}."
+)
+
+
+#####################################################################
+# Unbind all the paths that are specified in the BIND_PATHS array.
+#####################################################################
+function unbind_all() (
+ local dst_dir
+ local exit_code=0
+
+ # need to go into reverse since several parent directory may have been
+ # first before the child one.
+ for (( i=${#BIND_PATHS[@]}-1; i>=0; i-- )); do
+ dst_dir=$(bind_path_dst_dir "${BIND_PATHS[$i]}")
+
+ # continue to unmount even one of them fails
+ if ! "${unbind_dir}" "${dst_dir}"; then
+ log_WARN "Failed to umount ${dst_dir}."
+ exit_code=1
+ fi
+ done
+
+ if [[ ${exit_code} -ne 0 ]]; then
+ exit ${exit_code}
+ fi
+
+ echo
+ log_INFO "Unmounted the GOPATH-compatible directory structure at ${OUTPUT_PATH}."
+)
+
+
+#####################################################################
+# Asks the user to create the GOPATH-compatible directory structure.
+#####################################################################
+function confirm() (
while true; do
echo "Will create GOPATH-compatible directory structure at ${OUTPUT_PATH}"
echo -n "Ok [Y/n]?"
@@ -42,48 +193,162 @@
if [ "${decision}" == "n" ]; then
return 1
else
- echo "Invalid choice ${decision}; choose either 'y' or 'n'"
+ log_WARN "Invalid choice ${decision}; choose either 'y' or 'n'"
fi
fi
done
+)
+
+
+#####################################################################
+# Help function.
+#####################################################################
+function help() (
+ cat <<EOF
+Mounts the components of soong into a directory structure that Go tools
+and editors expect.
+
+ --help
+ This help
+
+ --bind
+ Create the directory structure that Go tools and editors expect by
+ binding the one to aosp build directory.
+
+ --unbind
+ Reverse operation of bind.
+
+If no flags were specified, the --bind one is selected by default.
+EOF
+)
+
+
+#####################################################################
+# Parse the arguments passed in to this script.
+#####################################################################
+function parse_arguments() {
+ while [[ -n "$1" ]]; do
+ case "$1" in
+ --bind)
+ ACTION="bind"
+ shift
+ ;;
+ --unbind)
+ ACTION="unbind"
+ shift
+ ;;
+ --help )
+ help
+ shift
+ exit 0
+ ;;
+ *)
+ log_WARN "Unknown option: $1"
+ help
+ exit 1
+ ;;
+ esac
+ done
+
+ if [[ -z "${ACTION}" ]]; then
+ ACTION=bind
+ fi
}
-function bindAll() {
- bindOne "${ANDROID_PATH}/build/blueprint" "${OUTPUT_PATH}/src/github.com/google/blueprint"
- bindOne "${ANDROID_PATH}/build/soong" "${OUTPUT_PATH}/src/android/soong"
- bindOne "${ANDROID_PATH}/art/build" "${OUTPUT_PATH}/src/android/soong/art"
- bindOne "${ANDROID_PATH}/external/golang-protobuf" "${OUTPUT_PATH}/src/github.com/golang/protobuf"
- bindOne "${ANDROID_PATH}/external/llvm/soong" "${OUTPUT_PATH}/src/android/soong/llvm"
- bindOne "${ANDROID_PATH}/external/clang/soong" "${OUTPUT_PATH}/src/android/soong/clang"
- echo
- echo "Created GOPATH-compatible directory structure at ${OUTPUT_PATH}"
-}
+#####################################################################
+# Verifies that a list of required binaries are installed in the
+# host in order to run this script.
+#####################################################################
+function check_exec_existence() (
+ function check() {
+ if ! hash "$1" &>/dev/null; then
+ log_FATAL "missing $1"
+ fi
+ }
-function bindOne() {
- #causes $newPath to mirror $existingPath
- existingPath="$1"
- newPath="$2"
- mkdir -p "$newPath"
- case $(uname -s) in
+ local bins
+ case "${os_type}" in
Darwin)
- echoAndDo bindfs -o allow_recursion -n "${existingPath}" "${newPath}"
+ bins=("bindfs" "greadlink")
;;
Linux)
- echoAndDo bindfs "${existingPath}" "${newPath}"
+ bins=("bindfs" "fusermount")
;;
+ *)
+ log_FATAL "${os_type} is not a recognized system."
esac
+
+ for bin in "${bins[@]}"; do
+ check "${bin}"
+ done
+)
+
+
+function main() {
+ parse_arguments "$@"
+
+ check_exec_existence
+
+ if [[ "${ACTION}" == "bind" ]]; then
+ if confirm; then
+ echo
+ bind_all
+ else
+ echo "skipping due to user request"
+ exit 1
+ fi
+ else
+ echo
+ unbind_all
+ fi
}
-function echoAndDo() {
- echo "$@"
- eval "$@"
-}
+readonly os_type="$(uname -s)"
+case "${os_type}" in
+ Darwin)
+ bind_dir=darwin_bind_dir
+ unbind_dir=darwin_unbind_dir
+ readlink=greadlink
+ ;;
+ Linux)
+ bind_dir=linux_bind_dir
+ unbind_dir=linux_unbind_dir
+ readlink=readlink
+ ;;
+ *)
+ log_FATAL "${os_type} is not a recognized system."
+esac
+readonly bind_dir
+readonly unbind_dir
+readonly readlink
-if confirm; then
- echo
- bindAll
-else
- echo "skipping due to user request"
- exit 1
+
+if ! ANDROID_PATH="$(root_dir)"; then
+ log_FATAL "failed to find the root of the repo checkout"
fi
+readonly ANDROID_PATH
+
+#if GOPATH contains multiple paths, use the first one
+if ! OUTPUT_PATH="$(echo ${GOPATH} | sed 's/\:.*//')"; then
+ log_FATAL "failed to extract the first GOPATH environment variable"
+fi
+readonly OUTPUT_PATH
+if [ -z "${OUTPUT_PATH}" ]; then
+ log_FATAL "Could not determine the desired location at which to create a" \
+ "Go-compatible workspace. Please update GOPATH to specify the" \
+ "desired destination directory."
+fi
+
+# Below are the paths to bind from src to dst. The paths are separated by |
+# where the left side is the source and the right side is destination.
+readonly BIND_PATHS=(
+ "${ANDROID_PATH}/build/blueprint|${OUTPUT_PATH}/src/github.com/google/blueprint"
+ "${ANDROID_PATH}/build/soong|${OUTPUT_PATH}/src/android/soong"
+ "${ANDROID_PATH}/art/build|${OUTPUT_PATH}/src/android/soong/art"
+ "${ANDROID_PATH}/external/golang-protobuf|${OUTPUT_PATH}/src/github.com/golang/protobuf"
+ "${ANDROID_PATH}/external/llvm/soong|${OUTPUT_PATH}/src/android/soong/llvm"
+ "${ANDROID_PATH}/external/clang/soong|${OUTPUT_PATH}/src/android/soong/clang"
+)
+
+main "$@"
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 75e6994..0f77da8 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -17,73 +17,143 @@
# Script to handle the various ways soong may need to strip binaries
# Inputs:
# Environment:
+# CLANG_BIN: path to the clang bin directory
# CROSS_COMPILE: prefix added to readelf, objcopy tools
+# XZ: path to the xz binary
# Arguments:
# -i ${file}: input file (required)
# -o ${file}: output file (required)
# -d ${file}: deps file (required)
-# --keep-symbols
-# --keep-mini-debug-info
+# -k symbols: Symbols to keep (optional)
# --add-gnu-debuglink
+# --keep-mini-debug-info
+# --keep-symbols
+# --use-gnu-strip
+# --remove-build-id
-OPTSTRING=d:i:o:-:
+set -o pipefail
+
+OPTSTRING=d:i:o:k:-:
usage() {
cat <<EOF
-Usage: strip.sh [options] -i in-file -o out-file -d deps-file
+Usage: strip.sh [options] -k symbols -i in-file -o out-file -d deps-file
Options:
- --keep-symbols Keep symbols in out-file
- --keep-mini-debug-info Keep compressed debug info in out-file
--add-gnu-debuglink Add a gnu-debuglink section to out-file
+ --keep-mini-debug-info Keep compressed debug info in out-file
+ --keep-symbols Keep symbols in out-file
+ --use-gnu-strip Use strip/objcopy instead of llvm-{strip,objcopy}
+ --remove-build-id Remove the gnu build-id section in out-file
EOF
exit 1
}
+# Without --use-gnu-strip, GNU strip is replaced with llvm-strip to work around
+# old GNU strip bug on lld output files, b/80093681.
+# Similary, calls to objcopy are replaced with llvm-objcopy,
+# with some exceptions.
+
do_strip() {
- "${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
+ # ${CROSS_COMPILE}strip --strip-all does not strip .ARM.attributes,
+ # so we tell llvm-strip to keep it too.
+ if [ -z "${use_gnu_strip}" ]; then
+ "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes "${infile}" -o "${outfile}.tmp"
+ else
+ "${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
+ fi
}
do_strip_keep_symbols() {
- "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" \
- `"${CROSS_COMPILE}readelf" -S "${infile}" | awk '/.debug_/ {print "-R " $2}' | xargs`
+ REMOVE_SECTIONS=`"${CROSS_COMPILE}readelf" -S "${infile}" | awk '/.debug_/ {print "--remove-section " $2}' | xargs`
+ if [ -z "${use_gnu_strip}" ]; then
+ "${CLANG_BIN}/llvm-objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
+ else
+ "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
+ fi
+}
+
+do_strip_keep_symbol_list() {
+ if [ -z "${use_gnu_strip}" ]; then
+ echo "do_strip_keep_symbol_list does not work with llvm-objcopy"
+ echo "http://b/131631155"
+ usage
+ fi
+
+ echo "${symbols_to_keep}" | tr ',' '\n' > "${outfile}.symbolList"
+ KEEP_SYMBOLS="-w --strip-unneeded-symbol=* --keep-symbols="
+ KEEP_SYMBOLS+="${outfile}.symbolList"
+
+ "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" ${KEEP_SYMBOLS}
}
do_strip_keep_mini_debug_info() {
rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
- if "${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp"; then
+ local fail=
+ if [ -z "${use_gnu_strip}" ]; then
+ "${CLANG_BIN}/llvm-strip" --strip-all -keep-section=.ARM.attributes -remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+ else
+ "${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp" || fail=true
+ fi
+ if [ -z $fail ]; then
+ # Current prebult llvm-objcopy does not support the following flags:
+ # --only-keep-debug --rename-section --keep-symbols
+ # For the following use cases, ${CROSS_COMPILE}objcopy does fine with lld linked files,
+ # except the --add-section flag.
"${CROSS_COMPILE}objcopy" --only-keep-debug "${infile}" "${outfile}.debug"
- "${CROSS_COMPILE}nm" -D "${infile}" --format=posix --defined-only | awk '{ print $$1 }' | sort >"${outfile}.dynsyms"
- "${CROSS_COMPILE}nm" "${infile}" --format=posix --defined-only | awk '{ if ($$2 == "T" || $$2 == "t" || $$2 == "D") print $$1 }' | sort > "${outfile}.funcsyms"
+ "${CROSS_COMPILE}nm" -D "${infile}" --format=posix --defined-only 2> /dev/null | awk '{ print $1 }' | sort >"${outfile}.dynsyms"
+ "${CROSS_COMPILE}nm" "${infile}" --format=posix --defined-only | awk '{ if ($2 == "T" || $2 == "t" || $2 == "D") print $1 }' | sort > "${outfile}.funcsyms"
comm -13 "${outfile}.dynsyms" "${outfile}.funcsyms" > "${outfile}.keep_symbols"
echo >> "${outfile}.keep_symbols" # Ensure that the keep_symbols file is not empty.
"${CROSS_COMPILE}objcopy" --rename-section .debug_frame=saved_debug_frame "${outfile}.debug" "${outfile}.mini_debuginfo"
"${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
"${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo"
- xz "${outfile}.mini_debuginfo"
- "${CROSS_COMPILE}objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
+ "${XZ}" "${outfile}.mini_debuginfo"
+ if [ -z "${use_gnu_strip}" ]; then
+ "${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
+ else
+ "${CROSS_COMPILE}objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp"
+ fi
+ rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
else
cp -f "${infile}" "${outfile}.tmp"
fi
}
do_add_gnu_debuglink() {
- "${CROSS_COMPILE}objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+ if [ -z "${use_gnu_strip}" ]; then
+ "${CLANG_BIN}/llvm-objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+ else
+ "${CROSS_COMPILE}objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+ fi
+}
+
+do_remove_build_id() {
+ if [ -z "${use_gnu_strip}" ]; then
+ "${CLANG_BIN}/llvm-strip" -remove-section=.note.gnu.build-id "${outfile}.tmp" -o "${outfile}.tmp.no-build-id"
+ else
+ "${CROSS_COMPILE}strip" --remove-section=.note.gnu.build-id "${outfile}.tmp" -o "${outfile}.tmp.no-build-id"
+ fi
+ rm -f "${outfile}.tmp"
+ mv "${outfile}.tmp.no-build-id" "${outfile}.tmp"
}
while getopts $OPTSTRING opt; do
case "$opt" in
- d) depsfile="${OPTARG}" ;;
- i) infile="${OPTARG}" ;;
- o) outfile="${OPTARG}" ;;
- -)
- case "${OPTARG}" in
- keep-symbols) keep_symbols=true ;;
- keep-mini-debug-info) keep_mini_debug_info=true ;;
- add-gnu-debuglink) add_gnu_debuglink=true ;;
- *) echo "Unknown option --${OPTARG}"; usage ;;
- esac;;
- ?) usage ;;
- *) echo "'${opt}' '${OPTARG}'"
+ d) depsfile="${OPTARG}" ;;
+ i) infile="${OPTARG}" ;;
+ o) outfile="${OPTARG}" ;;
+ k) symbols_to_keep="${OPTARG}" ;;
+ -)
+ case "${OPTARG}" in
+ add-gnu-debuglink) add_gnu_debuglink=true ;;
+ keep-mini-debug-info) keep_mini_debug_info=true ;;
+ keep-symbols) keep_symbols=true ;;
+ remove-build-id) remove_build_id=true ;;
+ use-gnu-strip) use_gnu_strip=true ;;
+ *) echo "Unknown option --${OPTARG}"; usage ;;
+ esac;;
+ ?) usage ;;
+ *) echo "'${opt}' '${OPTARG}'"
esac
done
@@ -107,6 +177,11 @@
usage
fi
+if [ ! -z "${symbols_to_keep}" -a ! -z "${keep_symbols}" ]; then
+ echo "--keep-symbols and -k cannot be used together"
+ usage
+fi
+
if [ ! -z "${add_gnu_debuglink}" -a ! -z "${keep_mini_debug_info}" ]; then
echo "--add-gnu-debuglink cannot be used with --keep-mini-debug-info"
usage
@@ -116,6 +191,8 @@
if [ ! -z "${keep_symbols}" ]; then
do_strip_keep_symbols
+elif [ ! -z "${symbols_to_keep}" ]; then
+ do_strip_keep_symbol_list
elif [ ! -z "${keep_mini_debug_info}" ]; then
do_strip_keep_mini_debug_info
else
@@ -126,15 +203,25 @@
do_add_gnu_debuglink
fi
+if [ ! -z "${remove_build_id}" ]; then
+ do_remove_build_id
+fi
+
rm -f "${outfile}"
mv "${outfile}.tmp" "${outfile}"
+if [ -z "${use_gnu_strip}" ]; then
+ USED_STRIP_OBJCOPY="${CLANG_BIN}/llvm-strip ${CLANG_BIN}/llvm-objcopy"
+else
+ USED_STRIP_OBJCOPY="${CROSS_COMPILE}strip"
+fi
+
cat <<EOF > "${depsfile}"
${outfile}: \
${infile} \
${CROSS_COMPILE}nm \
${CROSS_COMPILE}objcopy \
${CROSS_COMPILE}readelf \
- ${CROSS_COMPILE}strip
+ ${USED_STRIP_OBJCOPY}
EOF
diff --git a/scripts/system-clang-format b/scripts/system-clang-format
new file mode 100644
index 0000000..55773a2
--- /dev/null
+++ b/scripts/system-clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+ContinuationIndentWidth: 8
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
diff --git a/scripts/system-clang-format-2 b/scripts/system-clang-format-2
new file mode 100644
index 0000000..ede5d7e
--- /dev/null
+++ b/scripts/system-clang-format-2
@@ -0,0 +1,9 @@
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
diff --git a/scripts/toc.sh b/scripts/toc.sh
index 7b2224c..8b1d25f 100755
--- a/scripts/toc.sh
+++ b/scripts/toc.sh
@@ -22,6 +22,7 @@
# -i ${file}: input file (required)
# -o ${file}: output file (required)
# -d ${file}: deps file (required)
+# --elf | --macho | --pe: format (required)
OPTSTRING=d:i:o:-:
@@ -36,13 +37,34 @@
do_elf() {
("${CROSS_COMPILE}readelf" -d "${infile}" | grep SONAME || echo "No SONAME for ${infile}") > "${outfile}.tmp"
"${CROSS_COMPILE}readelf" --dyn-syms "${infile}" | awk '{$2=""; $3=""; print}' >> "${outfile}.tmp"
+
+ cat <<EOF > "${depsfile}"
+${outfile}: \\
+ ${CROSS_COMPILE}readelf \\
+EOF
}
do_macho() {
- otool -l "${infile}" | grep LC_ID_DYLIB -A 5 > "${outfile}.tmp"
- nm -gP "${infile}" | cut -f1-2 -d" " | grep -v 'U$' >> "${outfile}.tmp"
+ "${CROSS_COMPILE}/otool" -l "${infile}" | grep LC_ID_DYLIB -A 5 > "${outfile}.tmp"
+ "${CROSS_COMPILE}/nm" -gP "${infile}" | cut -f1-2 -d" " | (grep -v 'U$' >> "${outfile}.tmp" || true)
+
+ cat <<EOF > "${depsfile}"
+${outfile}: \\
+ ${CROSS_COMPILE}/otool \\
+ ${CROSS_COMPILE}/nm \\
+EOF
}
+do_pe() {
+ "${CROSS_COMPILE}objdump" -x "${infile}" | grep "^Name" | cut -f3 -d" " > "${outfile}.tmp"
+ "${CROSS_COMPILE}nm" -g -f p "${infile}" | cut -f1-2 -d" " >> "${outfile}.tmp"
+
+ cat <<EOF > "${depsfile}"
+${outfile}: \\
+ ${CROSS_COMPILE}objdump \\
+ ${CROSS_COMPILE}nm \\
+EOF
+}
while getopts $OPTSTRING opt; do
case "$opt" in
@@ -51,6 +73,9 @@
o) outfile="${OPTARG}" ;;
-)
case "${OPTARG}" in
+ elf) elf=1 ;;
+ macho) macho=1 ;;
+ pe) pe=1 ;;
*) echo "Unknown option --${OPTARG}"; usage ;;
esac;;
?) usage ;;
@@ -58,21 +83,26 @@
esac
done
-if [ -z "${infile}" ]; then
+if [ -z "${infile:-}" ]; then
echo "-i argument is required"
usage
fi
-if [ -z "${outfile}" ]; then
+if [ -z "${outfile:-}" ]; then
echo "-o argument is required"
usage
fi
-if [ -z "${depsfile}" ]; then
+if [ -z "${depsfile:-}" ]; then
echo "-d argument is required"
usage
fi
+if [ -z "${CROSS_COMPILE:-}" ]; then
+ echo "CROSS_COMPILE environment variable must be set"
+ usage
+fi
+
rm -f "${outfile}.tmp"
cat <<EOF > "${depsfile}"
@@ -80,7 +110,15 @@
${CROSS_COMPILE}readelf \\
EOF
-do_elf
+if [ -n "${elf:-}" ]; then
+ do_elf
+elif [ -n "${macho:-}" ]; then
+ do_macho
+elif [ -n "${pe:-}" ]; then
+ do_pe
+else
+ echo "--elf, --macho or --pe is required"; usage
+fi
if cmp "${outfile}" "${outfile}.tmp" > /dev/null 2> /dev/null; then
rm -f "${outfile}.tmp"
diff --git a/soong_ui.bash b/soong_ui.bash
index a39aa9c..c1c236b 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -23,7 +23,7 @@
function gettop
{
local TOPFILE=build/soong/root.bp
- if [ -z "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
+ if [ -n "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
# The following circumlocution ensures we remove symlinks from TOP.
(cd $TOP; PWD= /bin/pwd)
else
diff --git a/cmd/symbol_inject/Android.bp b/symbol_inject/Android.bp
similarity index 90%
rename from cmd/symbol_inject/Android.bp
rename to symbol_inject/Android.bp
index a2ea12b..8308043 100644
--- a/cmd/symbol_inject/Android.bp
+++ b/symbol_inject/Android.bp
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-blueprint_go_binary {
- name: "symbol_inject",
+bootstrap_go_package {
+ name: "soong-symbol_inject",
+ pkgPath: "android/soong/symbol_inject",
srcs: [
"symbol_inject.go",
"elf.go",
diff --git a/ui/build/util_linux.go b/symbol_inject/cmd/Android.bp
similarity index 74%
copy from ui/build/util_linux.go
copy to symbol_inject/cmd/Android.bp
index 0a4e1d2..ee2f259 100644
--- a/ui/build/util_linux.go
+++ b/symbol_inject/cmd/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All rights reserved.
+// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package build
-
-import (
- "syscall"
-)
-
-const ioctlGetTermios = syscall.TCGETS
+blueprint_go_binary {
+ name: "symbol_inject",
+ deps: ["soong-symbol_inject"],
+ srcs: [
+ "symbol_inject.go",
+ ],
+}
diff --git a/symbol_inject/cmd/symbol_inject.go b/symbol_inject/cmd/symbol_inject.go
new file mode 100644
index 0000000..1397b37
--- /dev/null
+++ b/symbol_inject/cmd/symbol_inject.go
@@ -0,0 +1,97 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "android/soong/symbol_inject"
+)
+
+var (
+ input = flag.String("i", "", "input file")
+ output = flag.String("o", "", "output file")
+ symbol = flag.String("s", "", "symbol to inject into")
+ from = flag.String("from", "", "optional existing value of the symbol for verification")
+ value = flag.String("v", "", "value to inject into symbol")
+
+ dump = flag.Bool("dump", false, "dump the symbol table for copying into a test")
+)
+
+func main() {
+ flag.Parse()
+
+ usageError := func(s string) {
+ fmt.Fprintln(os.Stderr, s)
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ if *input == "" {
+ usageError("-i is required")
+ }
+
+ if !*dump {
+ if *output == "" {
+ usageError("-o is required")
+ }
+
+ if *symbol == "" {
+ usageError("-s is required")
+ }
+
+ if *value == "" {
+ usageError("-v is required")
+ }
+ }
+
+ r, err := os.Open(*input)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(2)
+ }
+ defer r.Close()
+
+ if *dump {
+ err := symbol_inject.DumpSymbols(r)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(6)
+ }
+ return
+ }
+
+ w, err := os.OpenFile(*output, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(3)
+ }
+ defer w.Close()
+
+ file, err := symbol_inject.OpenFile(r)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(4)
+ }
+
+ err = symbol_inject.InjectStringSymbol(file, w, *symbol, *value, *from)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Remove(*output)
+ os.Exit(5)
+ }
+}
diff --git a/cmd/symbol_inject/elf.go b/symbol_inject/elf.go
similarity index 99%
rename from cmd/symbol_inject/elf.go
rename to symbol_inject/elf.go
index d94877d..8742cbd 100644
--- a/cmd/symbol_inject/elf.go
+++ b/symbol_inject/elf.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"debug/elf"
diff --git a/cmd/symbol_inject/elf_symboldata_test.go b/symbol_inject/elf_symboldata_test.go
similarity index 99%
rename from cmd/symbol_inject/elf_symboldata_test.go
rename to symbol_inject/elf_symboldata_test.go
index 9ba7153..b2f1148 100644
--- a/cmd/symbol_inject/elf_symboldata_test.go
+++ b/symbol_inject/elf_symboldata_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import "debug/elf"
diff --git a/cmd/symbol_inject/elf_test.go b/symbol_inject/elf_test.go
similarity index 98%
rename from cmd/symbol_inject/elf_test.go
rename to symbol_inject/elf_test.go
index 30b46a5..aceee44 100644
--- a/cmd/symbol_inject/elf_test.go
+++ b/symbol_inject/elf_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"strconv"
diff --git a/cmd/symbol_inject/macho.go b/symbol_inject/macho.go
similarity index 98%
rename from cmd/symbol_inject/macho.go
rename to symbol_inject/macho.go
index be49f8b..6ee3f4f 100644
--- a/cmd/symbol_inject/macho.go
+++ b/symbol_inject/macho.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"debug/macho"
diff --git a/cmd/symbol_inject/macho_symboldata_test.go b/symbol_inject/macho_symboldata_test.go
similarity index 99%
rename from cmd/symbol_inject/macho_symboldata_test.go
rename to symbol_inject/macho_symboldata_test.go
index 3100a81..7336a27 100644
--- a/cmd/symbol_inject/macho_symboldata_test.go
+++ b/symbol_inject/macho_symboldata_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"debug/macho"
diff --git a/cmd/symbol_inject/macho_test.go b/symbol_inject/macho_test.go
similarity index 98%
rename from cmd/symbol_inject/macho_test.go
rename to symbol_inject/macho_test.go
index 7acab23..50df131 100644
--- a/cmd/symbol_inject/macho_test.go
+++ b/symbol_inject/macho_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"debug/macho"
diff --git a/cmd/symbol_inject/pe.go b/symbol_inject/pe.go
similarity index 98%
rename from cmd/symbol_inject/pe.go
rename to symbol_inject/pe.go
index 12f35ee..58cf91a 100644
--- a/cmd/symbol_inject/pe.go
+++ b/symbol_inject/pe.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"debug/pe"
diff --git a/cmd/symbol_inject/pe_symboldata_test.go b/symbol_inject/pe_symboldata_test.go
similarity index 99%
rename from cmd/symbol_inject/pe_symboldata_test.go
rename to symbol_inject/pe_symboldata_test.go
index edc1c97..5c0fd70 100644
--- a/cmd/symbol_inject/pe_symboldata_test.go
+++ b/symbol_inject/pe_symboldata_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"debug/pe"
diff --git a/cmd/symbol_inject/pe_test.go b/symbol_inject/pe_test.go
similarity index 99%
rename from cmd/symbol_inject/pe_test.go
rename to symbol_inject/pe_test.go
index 21a0bc4..df7bac3 100644
--- a/cmd/symbol_inject/pe_test.go
+++ b/symbol_inject/pe_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"debug/pe"
diff --git a/cmd/symbol_inject/symbol_inject.go b/symbol_inject/symbol_inject.go
similarity index 70%
rename from cmd/symbol_inject/symbol_inject.go
rename to symbol_inject/symbol_inject.go
index d0f01c5..2a3d67e 100644
--- a/cmd/symbol_inject/symbol_inject.go
+++ b/symbol_inject/symbol_inject.go
@@ -12,25 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"bytes"
- "flag"
+ "encoding/binary"
"fmt"
"io"
"math"
- "os"
-)
-
-var (
- input = flag.String("i", "", "input file")
- output = flag.String("o", "", "output file")
- symbol = flag.String("s", "", "symbol to inject into")
- from = flag.String("from", "", "optional existing value of the symbol for verification")
- value = flag.String("v", "", "value to inject into symbol")
-
- dump = flag.Bool("dump", false, "dump the symbol table for copying into a test")
)
var maxUint64 uint64 = math.MaxUint64
@@ -39,71 +28,7 @@
error
}
-func main() {
- flag.Parse()
-
- usageError := func(s string) {
- fmt.Fprintln(os.Stderr, s)
- flag.Usage()
- os.Exit(1)
- }
-
- if *input == "" {
- usageError("-i is required")
- }
-
- if !*dump {
- if *output == "" {
- usageError("-o is required")
- }
-
- if *symbol == "" {
- usageError("-s is required")
- }
-
- if *value == "" {
- usageError("-v is required")
- }
- }
-
- r, err := os.Open(*input)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(2)
- }
- defer r.Close()
-
- if *dump {
- err := dumpSymbols(r)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(6)
- }
- return
- }
-
- w, err := os.OpenFile(*output, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(3)
- }
- defer w.Close()
-
- file, err := openFile(r)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(4)
- }
-
- err = injectSymbol(file, w, *symbol, *value, *from)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Remove(*output)
- os.Exit(5)
- }
-}
-
-func openFile(r io.ReaderAt) (*File, error) {
+func OpenFile(r io.ReaderAt) (*File, error) {
file, err := elfSymbolsFromFile(r)
if elfError, ok := err.(cantParseError); ok {
// Try as a mach-o file
@@ -126,7 +51,7 @@
return file, err
}
-func injectSymbol(file *File, w io.Writer, symbol, value, from string) error {
+func InjectStringSymbol(file *File, w io.Writer, symbol, value, from string) error {
offset, size, err := findSymbol(file, symbol)
if err != nil {
return err
@@ -151,13 +76,29 @@
}
}
- return copyAndInject(file.r, w, offset, size, value)
-}
-
-func copyAndInject(r io.ReaderAt, w io.Writer, offset, size uint64, value string) (err error) {
buf := make([]byte, size)
copy(buf, value)
+ return copyAndInject(file.r, w, offset, buf)
+}
+
+func InjectUint64Symbol(file *File, w io.Writer, symbol string, value uint64) error {
+ offset, size, err := findSymbol(file, symbol)
+ if err != nil {
+ return err
+ }
+
+ if size != 8 {
+ return fmt.Errorf("symbol %q is not a uint64, it is %d bytes long", symbol, size)
+ }
+
+ buf := make([]byte, 8)
+ binary.LittleEndian.PutUint64(buf, value)
+
+ return copyAndInject(file.r, w, offset, buf)
+}
+
+func copyAndInject(r io.ReaderAt, w io.Writer, offset uint64, buf []byte) (err error) {
// Copy the first bytes up to the symbol offset
_, err = io.Copy(w, io.NewSectionReader(r, 0, int64(offset)))
@@ -167,7 +108,7 @@
}
// Write the remainder of the file
- pos := int64(offset + size)
+ pos := int64(offset) + int64(len(buf))
if err == nil {
_, err = io.Copy(w, io.NewSectionReader(r, pos, 1<<63-1-pos))
}
@@ -239,7 +180,7 @@
Size uint64
}
-func dumpSymbols(r io.ReaderAt) error {
+func DumpSymbols(r io.ReaderAt) error {
err := dumpElfSymbols(r)
if elfError, ok := err.(cantParseError); ok {
// Try as a mach-o file
diff --git a/cmd/symbol_inject/symbol_inject_test.go b/symbol_inject/symbol_inject_test.go
similarity index 77%
rename from cmd/symbol_inject/symbol_inject_test.go
rename to symbol_inject/symbol_inject_test.go
index dbee39a..6607e65 100644
--- a/cmd/symbol_inject/symbol_inject_test.go
+++ b/symbol_inject/symbol_inject_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+package symbol_inject
import (
"bytes"
@@ -23,32 +23,23 @@
func TestCopyAndInject(t *testing.T) {
s := "abcdefghijklmnopqrstuvwxyz"
testCases := []struct {
- offset, size uint64
- value string
- expected string
+ offset uint64
+ buf string
+ expected string
}{
{
offset: 0,
- size: 1,
- value: "A",
+ buf: "A",
expected: "Abcdefghijklmnopqrstuvwxyz",
},
{
offset: 1,
- size: 1,
- value: "B",
- expected: "aBcdefghijklmnopqrstuvwxyz",
- },
- {
- offset: 1,
- size: 1,
- value: "BCD",
+ buf: "B",
expected: "aBcdefghijklmnopqrstuvwxyz",
},
{
offset: 25,
- size: 1,
- value: "Z",
+ buf: "Z",
expected: "abcdefghijklmnopqrstuvwxyZ",
},
}
@@ -57,7 +48,7 @@
t.Run(strconv.Itoa(i), func(t *testing.T) {
in := bytes.NewReader([]byte(s))
out := &bytes.Buffer{}
- copyAndInject(in, out, testCase.offset, testCase.size, testCase.value)
+ copyAndInject(in, out, testCase.offset, []byte(testCase.buf))
if out.String() != testCase.expected {
t.Errorf("expected %s, got %s", testCase.expected, out.String())
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
new file mode 100644
index 0000000..08845b7
--- /dev/null
+++ b/sysprop/sysprop_library.go
@@ -0,0 +1,138 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sysprop
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/java"
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+type syspropLibrary struct {
+ java.SdkLibrary
+
+ commonProperties commonProperties
+ syspropLibraryProperties syspropLibraryProperties
+}
+
+type syspropLibraryProperties struct {
+ // Determine who owns this sysprop library. Possible values are
+ // "Platform", "Vendor", or "Odm"
+ Property_owner string
+
+ // list of package names that will be documented and publicized as API
+ Api_packages []string
+}
+
+type commonProperties struct {
+ Srcs []string
+ Recovery *bool
+ Recovery_available *bool
+ Vendor_available *bool
+}
+
+var (
+ Bool = proptools.Bool
+ syspropCcTag = dependencyTag{name: "syspropCc"}
+)
+
+func init() {
+ android.RegisterModuleType("sysprop_library", syspropLibraryFactory)
+}
+
+func (m *syspropLibrary) CcModuleName() string {
+ return "lib" + m.Name()
+}
+
+func (m *syspropLibrary) SyspropJavaModule() *java.SdkLibrary {
+ return &m.SdkLibrary
+}
+
+func syspropLibraryFactory() android.Module {
+ m := &syspropLibrary{}
+
+ m.AddProperties(
+ &m.commonProperties,
+ &m.syspropLibraryProperties,
+ )
+ m.InitSdkLibraryProperties()
+ m.SetNoDist()
+ android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, "common")
+ android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) })
+
+ return m
+}
+
+func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
+ if len(m.commonProperties.Srcs) == 0 {
+ ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
+ }
+
+ if len(m.syspropLibraryProperties.Api_packages) == 0 {
+ ctx.PropertyErrorf("api_packages", "sysprop_library must specify api_packages")
+ }
+
+ socSpecific := ctx.SocSpecific()
+ deviceSpecific := ctx.DeviceSpecific()
+ productSpecific := ctx.ProductSpecific()
+
+ owner := m.syspropLibraryProperties.Property_owner
+
+ switch owner {
+ case "Platform":
+ // Every partition can access platform-defined properties
+ break
+ case "Vendor":
+ // System can't access vendor's properties
+ if !socSpecific && !deviceSpecific && !productSpecific {
+ ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " +
+ "System can't access sysprop_library owned by Vendor")
+ }
+ case "Odm":
+ // Only vendor can access Odm-defined properties
+ if !socSpecific && !deviceSpecific {
+ ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " +
+ "Odm-defined properties should be accessed only in Vendor or Odm")
+ }
+ default:
+ ctx.PropertyErrorf("property_owner",
+ "Unknown value %s: must be one of Platform, Vendor or Odm", owner)
+ }
+
+ ccProps := struct {
+ Name *string
+ Soc_specific *bool
+ Device_specific *bool
+ Product_specific *bool
+ Sysprop struct {
+ Platform *bool
+ }
+ }{}
+
+ ccProps.Name = proptools.StringPtr(m.CcModuleName())
+ ccProps.Soc_specific = proptools.BoolPtr(socSpecific)
+ ccProps.Device_specific = proptools.BoolPtr(deviceSpecific)
+ ccProps.Product_specific = proptools.BoolPtr(productSpecific)
+ ccProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
+
+ ctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &m.commonProperties, &ccProps)
+}
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
new file mode 100644
index 0000000..07406b3
--- /dev/null
+++ b/sysprop/sysprop_test.go
@@ -0,0 +1,365 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sysprop
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/java"
+
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_sysprop_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func testContext(config android.Config, bp string,
+ fs map[string][]byte) *android.TestContext {
+
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(java.AndroidAppFactory))
+ ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(java.ExportedDroiddocDirFactory))
+ ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(java.LibraryFactory))
+ ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(java.SystemModulesFactory))
+ ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(java.PrebuiltApisFactory))
+ ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("load_hooks", android.LoadHookMutator).Parallel()
+ })
+ ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
+ ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("prebuilt_apis", java.PrebuiltApisMutator).Parallel()
+ ctx.TopDown("java_sdk_library", java.SdkLibraryMutator).Parallel()
+ })
+
+ ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+ ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
+ ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+ ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
+ ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
+ ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+ ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("image", cc.ImageMutator).Parallel()
+ ctx.BottomUp("link", cc.LinkageMutator).Parallel()
+ ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
+ ctx.BottomUp("version", cc.VersionMutator).Parallel()
+ ctx.BottomUp("begin", cc.BeginMutator).Parallel()
+ ctx.BottomUp("sysprop", cc.SyspropMutator).Parallel()
+ })
+
+ ctx.RegisterModuleType("sysprop_library", android.ModuleFactoryAdaptor(syspropLibraryFactory))
+
+ ctx.Register()
+
+ bp += java.GatherRequiredDepsForTest()
+ bp += cc.GatherRequiredDepsForTest(android.Android)
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "a.java": nil,
+ "b.java": nil,
+ "c.java": nil,
+ "d.cpp": nil,
+ "api/current.txt": nil,
+ "api/removed.txt": nil,
+ "api/system-current.txt": nil,
+ "api/system-removed.txt": nil,
+ "api/test-current.txt": nil,
+ "api/test-removed.txt": nil,
+ "framework/aidl/a.aidl": nil,
+
+ "prebuilts/sdk/current/core/android.jar": nil,
+ "prebuilts/sdk/current/public/android.jar": nil,
+ "prebuilts/sdk/current/public/framework.aidl": nil,
+ "prebuilts/sdk/current/public/core.jar": nil,
+ "prebuilts/sdk/current/system/android.jar": nil,
+ "prebuilts/sdk/current/test/android.jar": nil,
+ "prebuilts/sdk/28/public/api/sysprop-platform.txt": nil,
+ "prebuilts/sdk/28/system/api/sysprop-platform.txt": nil,
+ "prebuilts/sdk/28/test/api/sysprop-platform.txt": nil,
+ "prebuilts/sdk/28/public/api/sysprop-platform-removed.txt": nil,
+ "prebuilts/sdk/28/system/api/sysprop-platform-removed.txt": nil,
+ "prebuilts/sdk/28/test/api/sysprop-platform-removed.txt": nil,
+ "prebuilts/sdk/28/public/api/sysprop-platform-on-product.txt": nil,
+ "prebuilts/sdk/28/system/api/sysprop-platform-on-product.txt": nil,
+ "prebuilts/sdk/28/test/api/sysprop-platform-on-product.txt": nil,
+ "prebuilts/sdk/28/public/api/sysprop-platform-on-product-removed.txt": nil,
+ "prebuilts/sdk/28/system/api/sysprop-platform-on-product-removed.txt": nil,
+ "prebuilts/sdk/28/test/api/sysprop-platform-on-product-removed.txt": nil,
+ "prebuilts/sdk/28/public/api/sysprop-vendor.txt": nil,
+ "prebuilts/sdk/28/system/api/sysprop-vendor.txt": nil,
+ "prebuilts/sdk/28/test/api/sysprop-vendor.txt": nil,
+ "prebuilts/sdk/28/public/api/sysprop-vendor-removed.txt": nil,
+ "prebuilts/sdk/28/system/api/sysprop-vendor-removed.txt": nil,
+ "prebuilts/sdk/28/test/api/sysprop-vendor-removed.txt": nil,
+ "prebuilts/sdk/tools/core-lambda-stubs.jar": nil,
+ "prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["28", "current"],}`),
+
+ // For framework-res, which is an implicit dependency for framework
+ "AndroidManifest.xml": nil,
+ "build/target/product/security/testkey": nil,
+
+ "build/soong/scripts/jar-wrapper.sh": nil,
+
+ "build/make/core/proguard.flags": nil,
+ "build/make/core/proguard_basic_keeps.flags": nil,
+
+ "jdk8/jre/lib/jce.jar": nil,
+ "jdk8/jre/lib/rt.jar": nil,
+ "jdk8/lib/tools.jar": nil,
+
+ "bar-doc/a.java": nil,
+ "bar-doc/b.java": nil,
+ "bar-doc/IFoo.aidl": nil,
+ "bar-doc/known_oj_tags.txt": nil,
+ "external/doclava/templates-sdk": nil,
+
+ "cert/new_cert.x509.pem": nil,
+ "cert/new_cert.pk8": nil,
+
+ "android/sysprop/PlatformProperties.sysprop": nil,
+ "com/android/VendorProperties.sysprop": nil,
+ }
+
+ for k, v := range fs {
+ mockFS[k] = v
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ return ctx
+}
+
+func run(t *testing.T, ctx *android.TestContext, config android.Config) {
+ t.Helper()
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+}
+
+func testConfig(env map[string]string) android.Config {
+ config := java.TestConfig(buildDir, env)
+
+ config.TestProductVariables.DeviceSystemSdkVersions = []string{"28"}
+ config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER")
+
+ return config
+
+}
+
+func test(t *testing.T, bp string) *android.TestContext {
+ t.Helper()
+ config := testConfig(nil)
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ return ctx
+}
+
+func TestSyspropLibrary(t *testing.T) {
+ ctx := test(t, `
+ sysprop_library {
+ name: "sysprop-platform",
+ srcs: ["android/sysprop/PlatformProperties.sysprop"],
+ api_packages: ["android.sysprop"],
+ property_owner: "Platform",
+ vendor_available: true,
+ }
+
+ sysprop_library {
+ name: "sysprop-platform-on-product",
+ srcs: ["android/sysprop/PlatformProperties.sysprop"],
+ api_packages: ["android.sysprop"],
+ property_owner: "Platform",
+ product_specific: true,
+ }
+
+ sysprop_library {
+ name: "sysprop-vendor",
+ srcs: ["com/android/VendorProperties.sysprop"],
+ api_packages: ["com.android"],
+ property_owner: "Vendor",
+ product_specific: true,
+ vendor_available: true,
+ }
+
+ java_library {
+ name: "java-platform",
+ srcs: ["c.java"],
+ sdk_version: "system_current",
+ libs: ["sysprop-platform"],
+ }
+
+ java_library {
+ name: "java-product",
+ srcs: ["c.java"],
+ sdk_version: "system_current",
+ product_specific: true,
+ libs: ["sysprop-platform", "sysprop-vendor"],
+ }
+
+ java_library {
+ name: "java-vendor",
+ srcs: ["c.java"],
+ sdk_version: "system_current",
+ soc_specific: true,
+ libs: ["sysprop-platform", "sysprop-vendor"],
+ }
+
+ cc_library {
+ name: "cc-client-platform",
+ srcs: ["d.cpp"],
+ static_libs: ["sysprop-platform"],
+ }
+
+ cc_library_static {
+ name: "cc-client-platform-static",
+ srcs: ["d.cpp"],
+ whole_static_libs: ["sysprop-platform"],
+ }
+
+ cc_library {
+ name: "cc-client-product",
+ srcs: ["d.cpp"],
+ product_specific: true,
+ static_libs: ["sysprop-platform-on-product", "sysprop-vendor"],
+ }
+
+ cc_library {
+ name: "cc-client-vendor",
+ srcs: ["d.cpp"],
+ soc_specific: true,
+ static_libs: ["sysprop-platform", "sysprop-vendor"],
+ }
+
+ cc_library_headers {
+ name: "libbase_headers",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_library {
+ name: "liblog",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+
+ llndk_library {
+ name: "liblog",
+ symbol_file: "",
+ }
+ `)
+
+ for _, variant := range []string{
+ "android_arm_armv7-a-neon_core_shared",
+ "android_arm_armv7-a-neon_core_static",
+ "android_arm_armv7-a-neon_vendor_shared",
+ "android_arm_armv7-a-neon_vendor_static",
+ "android_arm64_armv8-a_core_shared",
+ "android_arm64_armv8-a_core_static",
+ "android_arm64_armv8-a_vendor_shared",
+ "android_arm64_armv8-a_vendor_static",
+ } {
+ // Check for generated cc_library
+ ctx.ModuleForTests("libsysprop-platform", variant)
+ ctx.ModuleForTests("libsysprop-vendor", variant)
+ }
+
+ ctx.ModuleForTests("sysprop-platform", "android_common")
+ ctx.ModuleForTests("sysprop-vendor", "android_common")
+
+ // Check for exported includes
+ coreVariant := "android_arm64_armv8-a_core_static"
+ vendorVariant := "android_arm64_armv8-a_vendor_static"
+
+ platformInternalPath := "libsysprop-platform/android_arm64_armv8-a_core_static/gen/sysprop/include"
+ platformSystemCorePath := "libsysprop-platform/android_arm64_armv8-a_core_static/gen/sysprop/system/include"
+ platformSystemVendorPath := "libsysprop-platform/android_arm64_armv8-a_vendor_static/gen/sysprop/system/include"
+
+ platformOnProductPath := "libsysprop-platform-on-product/android_arm64_armv8-a_core_static/gen/sysprop/system/include"
+
+ vendorInternalPath := "libsysprop-vendor/android_arm64_armv8-a_vendor_static/gen/sysprop/include"
+ vendorSystemPath := "libsysprop-vendor/android_arm64_armv8-a_core_static/gen/sysprop/system/include"
+
+ platformClient := ctx.ModuleForTests("cc-client-platform", coreVariant)
+ platformFlags := platformClient.Rule("cc").Args["cFlags"]
+
+ // platform should use platform's internal header
+ if !strings.Contains(platformFlags, platformInternalPath) {
+ t.Errorf("flags for platform must contain %#v, but was %#v.",
+ platformInternalPath, platformFlags)
+ }
+
+ platformStaticClient := ctx.ModuleForTests("cc-client-platform-static", coreVariant)
+ platformStaticFlags := platformStaticClient.Rule("cc").Args["cFlags"]
+
+ // platform-static should use platform's internal header
+ if !strings.Contains(platformStaticFlags, platformInternalPath) {
+ t.Errorf("flags for platform-static must contain %#v, but was %#v.",
+ platformInternalPath, platformStaticFlags)
+ }
+
+ productClient := ctx.ModuleForTests("cc-client-product", coreVariant)
+ productFlags := productClient.Rule("cc").Args["cFlags"]
+
+ // Product should use platform's and vendor's system headers
+ if !strings.Contains(productFlags, platformOnProductPath) ||
+ !strings.Contains(productFlags, vendorSystemPath) {
+ t.Errorf("flags for product must contain %#v and %#v, but was %#v.",
+ platformSystemCorePath, vendorSystemPath, productFlags)
+ }
+
+ vendorClient := ctx.ModuleForTests("cc-client-vendor", vendorVariant)
+ vendorFlags := vendorClient.Rule("cc").Args["cFlags"]
+
+ // Vendor should use platform's system header and vendor's internal header
+ if !strings.Contains(vendorFlags, platformSystemVendorPath) ||
+ !strings.Contains(vendorFlags, vendorInternalPath) {
+ t.Errorf("flags for vendor must contain %#v and %#v, but was %#v.",
+ platformSystemVendorPath, vendorInternalPath, vendorFlags)
+ }
+}
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
new file mode 100644
index 0000000..952b022
--- /dev/null
+++ b/tradefed/autogen.go
@@ -0,0 +1,202 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tradefed
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func getTestConfigTemplate(ctx android.ModuleContext, prop *string) android.OptionalPath {
+ return ctx.ExpandOptionalSource(prop, "test_config_template")
+}
+
+func getTestConfig(ctx android.ModuleContext, prop *string) android.Path {
+ if p := ctx.ExpandOptionalSource(prop, "test_config"); p.Valid() {
+ return p.Path()
+ } else if p := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml"); p.Valid() {
+ return p.Path()
+ }
+ return nil
+}
+
+var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{
+ Command: "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g' $template > $out",
+ CommandDeps: []string{"$template"},
+}, "name", "template", "extraConfigs")
+
+func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string) (path android.Path, autogenPath android.WritablePath) {
+ if p := getTestConfig(ctx, prop); p != nil {
+ return p, nil
+ } else if !android.InList("cts", testSuites) {
+ outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config")
+ return nil, outputFile
+ } else {
+ // CTS modules can be used for test data, so test config files must be
+ // explicitly created using AndroidTest.xml
+ // TODO(b/112602712): remove the path check
+ return nil, nil
+ }
+}
+
+type Config interface {
+ Config() string
+}
+
+type Option struct {
+ Name string
+ Value string
+}
+
+var _ Config = Option{}
+
+func (o Option) Config() string {
+ return fmt.Sprintf(`<option name="%s" value="%s" />`, o.Name, o.Value)
+}
+
+type Preparer struct {
+ Class string
+}
+
+var _ Config = Preparer{}
+
+func (p Preparer) Config() string {
+ return fmt.Sprintf(`<target_preparer class="%s" />`, p.Class)
+}
+
+func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config) {
+ var configStrings []string
+ for _, config := range configs {
+ configStrings = append(configStrings, config.Config())
+ }
+ extraConfigs := strings.Join(configStrings, "\n ")
+ extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: autogenTestConfig,
+ Description: "test config",
+ Output: output,
+ Args: map[string]string{
+ "name": ctx.ModuleName(),
+ "template": template,
+ "extraConfigs": extraConfigs,
+ },
+ })
+}
+
+func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
+ testConfigTemplateProp *string, testSuites []string, config []Config) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+ if autogenPath != nil {
+ templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if templatePath.Valid() {
+ autogenTemplate(ctx, autogenPath, templatePath.String(), config)
+ } else {
+ if ctx.Device() {
+ autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}", config)
+ } else {
+ autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}", config)
+ }
+ }
+ return autogenPath
+ }
+ return path
+}
+
+func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
+ testConfigTemplateProp *string, testSuites []string, configs []Config) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+ if autogenPath != nil {
+ templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if templatePath.Valid() {
+ autogenTemplate(ctx, autogenPath, templatePath.String(), configs)
+ } else {
+ autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", configs)
+ }
+ return autogenPath
+ }
+ return path
+}
+
+func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+ if autogenPath != nil {
+ templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if templatePath.Valid() {
+ autogenTemplate(ctx, autogenPath, templatePath.String(), nil)
+ } else {
+ if ctx.Device() {
+ autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", nil)
+ } else {
+ autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil)
+ }
+ }
+ return autogenPath
+ }
+ return path
+}
+
+func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string,
+ testConfigTemplateProp *string, testSuites []string) android.Path {
+
+ path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+ if autogenPath != nil {
+ templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if templatePath.Valid() {
+ autogenTemplate(ctx, autogenPath, templatePath.String(), nil)
+ } else {
+ autogenTemplate(ctx, autogenPath, "${PythonBinaryHostTestConfigTemplate}", nil)
+ }
+ return autogenPath
+ }
+ return path
+}
+
+var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
+ Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template",
+ CommandDeps: []string{
+ "${AutoGenTestConfigScript}",
+ "${EmptyTestConfig}",
+ "$template",
+ },
+}, "name", "template")
+
+func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, manifest android.Path, testSuites []string) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
+ if autogenPath != nil {
+ template := "${InstrumentationTestConfigTemplate}"
+ moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if moduleTemplate.Valid() {
+ template = moduleTemplate.String()
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: autogenInstrumentationTest,
+ Description: "test config",
+ Input: manifest,
+ Output: autogenPath,
+ Args: map[string]string{
+ "name": ctx.ModuleName(),
+ "template": template,
+ },
+ })
+ return autogenPath
+ }
+ return path
+}
diff --git a/tradefed/config.go b/tradefed/config.go
new file mode 100644
index 0000000..141e0c5
--- /dev/null
+++ b/tradefed/config.go
@@ -0,0 +1,36 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tradefed
+
+import (
+ "android/soong/android"
+)
+
+var (
+ pctx = android.NewPackageContext("android/soong/tradefed")
+)
+
+func init() {
+ pctx.SourcePathVariable("AutoGenTestConfigScript", "build/make/tools/auto_gen_test_config.py")
+ pctx.SourcePathVariable("InstrumentationTestConfigTemplate", "build/make/core/instrumentation_test_config_template.xml")
+ pctx.SourcePathVariable("JavaTestConfigTemplate", "build/make/core/java_test_config_template.xml")
+ pctx.SourcePathVariable("JavaHostTestConfigTemplate", "build/make/core/java_host_test_config_template.xml")
+ pctx.SourcePathVariable("NativeBenchmarkTestConfigTemplate", "build/make/core/native_benchmark_test_config_template.xml")
+ pctx.SourcePathVariable("NativeHostTestConfigTemplate", "build/make/core/native_host_test_config_template.xml")
+ pctx.SourcePathVariable("NativeTestConfigTemplate", "build/make/core/native_test_config_template.xml")
+ pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
+
+ pctx.SourcePathVariable("EmptyTestConfig", "build/make/core/empty_test_config.xml")
+}
diff --git a/tradefed/makevars.go b/tradefed/makevars.go
new file mode 100644
index 0000000..aad7273
--- /dev/null
+++ b/tradefed/makevars.go
@@ -0,0 +1,36 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tradefed
+
+import (
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
+}
+
+func makeVarsProvider(ctx android.MakeVarsContext) {
+ ctx.Strict("AUTOGEN_TEST_CONFIG_SCRIPT", "${AutoGenTestConfigScript}")
+ ctx.Strict("INSTRUMENTATION_TEST_CONFIG_TEMPLATE", "${InstrumentationTestConfigTemplate}")
+ ctx.Strict("JAVA_HOST_TEST_CONFIG_TEMPLATE", "${JavaHostTestConfigTemplate}")
+ ctx.Strict("JAVA_TEST_CONFIG_TEMPLATE", "${JavaTestConfigTemplate}")
+ ctx.Strict("NATIVE_BENCHMARK_TEST_CONFIG_TEMPLATE", "${NativeBenchmarkTestConfigTemplate}")
+ ctx.Strict("NATIVE_HOST_TEST_CONFIG_TEMPLATE", "${NativeHostTestConfigTemplate}")
+ ctx.Strict("NATIVE_TEST_CONFIG_TEMPLATE", "${NativeTestConfigTemplate}")
+ ctx.Strict("PYTHON_BINARY_HOST_TEST_CONFIG_TEMPLATE", "${PythonBinaryHostTestConfigTemplate}")
+
+ ctx.Strict("EMPTY_TEST_CONFIG", "${EmptyTestConfig}")
+}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 5809894..cb2b76c 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -13,10 +13,26 @@
// limitations under the License.
bootstrap_go_package {
+ name: "soong-ui-build-paths",
+ pkgPath: "android/soong/ui/build/paths",
+ srcs: [
+ "paths/config.go",
+ "paths/logs.go",
+ ],
+ testSrcs: [
+ "paths/logs_test.go",
+ ],
+}
+
+bootstrap_go_package {
name: "soong-ui-build",
pkgPath: "android/soong/ui/build",
deps: [
+ "soong-ui-build-paths",
"soong-ui-logger",
+ "soong-ui-metrics",
+ "soong-ui-status",
+ "soong-ui-terminal",
"soong-ui-tracer",
"soong-shared",
"soong-finder",
@@ -31,30 +47,34 @@
"environment.go",
"exec.go",
"finder.go",
+ "goma.go",
"kati.go",
"ninja.go",
+ "path.go",
"proc_sync.go",
+ "rbe.go",
"signal.go",
"soong.go",
"test_build.go",
+ "upload.go",
"util.go",
],
testSrcs: [
"config_test.go",
"environment_test.go",
+ "rbe_test.go",
+ "upload_test.go",
"util_test.go",
"proc_sync_test.go",
],
darwin: {
srcs: [
"sandbox_darwin.go",
- "util_darwin.go"
],
},
linux: {
srcs: [
"sandbox_linux.go",
- "util_linux.go"
],
},
}
diff --git a/ui/build/build.go b/ui/build/build.go
index 78eb6a3..58b6da9 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -37,10 +37,13 @@
var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
builddir = {{.OutDir}}
-{{if .HasKatiSuffix}}include {{.KatiNinjaFile}}
+pool local_pool
+ depth = {{.Parallel}}
+build _kati_always_build_: phony
+{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
+subninja {{.KatiPackageNinjaFile}}
{{end -}}
-include {{.SoongNinjaFile}}
-build {{.CombinedNinjaFile}}: phony {{.SoongNinjaFile}}
+subninja {{.SoongNinjaFile}}
`))
func createCombinedBuildNinjaFile(ctx Context, config Config) {
@@ -72,6 +75,17 @@
BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
)
+func checkProblematicFiles(ctx Context) {
+ files := []string{"Android.mk", "CleanSpec.mk"}
+ for _, file := range files {
+ if _, err := os.Stat(file); !os.IsNotExist(err) {
+ absolute := absPath(ctx, file)
+ ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file)
+ ctx.Fatalf(" rm %s\n", absolute)
+ }
+ }
+}
+
func checkCaseSensitivity(ctx Context, config Config) {
outDir := config.OutDir()
lowerCase := filepath.Join(outDir, "casecheck.txt")
@@ -106,9 +120,7 @@
func help(ctx Context, config Config, what int) {
cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
cmd.Sandbox = dumpvarsSandbox
- cmd.Stdout = ctx.Stdout()
- cmd.Stderr = ctx.Stderr()
- cmd.RunOrFatal()
+ cmd.RunAndPrintOrFatal()
}
// Build the tree. The 'what' argument can be used to chose which components of
@@ -134,12 +146,26 @@
buildLock := BecomeSingletonOrFail(ctx, config)
defer buildLock.Unlock()
+ checkProblematicFiles(ctx)
+
SetupOutDir(ctx, config)
checkCaseSensitivity(ctx, config)
ensureEmptyDirectoriesExist(ctx, config.TempDir())
+ SetupPath(ctx, config)
+
+ if config.StartGoma() {
+ // Ensure start Goma compiler_proxy
+ startGoma(ctx, config)
+ }
+
+ if config.StartRBE() {
+ // Ensure RBE proxy is started
+ startRBE(ctx, config)
+ }
+
if what&BuildProductConfig != 0 {
// Run make for product config
runMakeProductConfig(ctx, config)
@@ -162,7 +188,10 @@
if what&BuildKati != 0 {
// Run ckati
- runKati(ctx, config)
+ genKatiSuffix(ctx, config)
+ runKatiCleanSpec(ctx, config)
+ runKatiBuild(ctx, config)
+ runKatiPackage(ctx, config)
ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
} else {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 24a8c7a..c47f614 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -20,6 +20,8 @@
"os"
"path/filepath"
"strings"
+
+ "android/soong/ui/metrics"
)
func removeGlobs(ctx Context, globs ...string) {
@@ -102,12 +104,14 @@
productOut("skin"),
productOut("obj/NOTICE_FILES"),
productOut("obj/PACKAGING"),
+ productOut("ramdisk"),
productOut("recovery"),
productOut("root"),
productOut("system"),
productOut("system_other"),
productOut("vendor"),
productOut("product"),
+ productOut("product_services"),
productOut("oem"),
productOut("obj/FAKE"),
productOut("breakpad"),
@@ -156,7 +160,7 @@
return
}
- ctx.BeginTrace("installclean")
+ ctx.BeginTrace(metrics.PrimaryNinja, "installclean")
defer ctx.EndTrace()
prevConfig := strings.TrimPrefix(strings.TrimSuffix(string(prev), suffix), prefix)
diff --git a/ui/build/config.go b/ui/build/config.go
index 27ed8e9..28ce57d 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,6 +15,7 @@
package build
import (
+ "fmt"
"io/ioutil"
"log"
"os"
@@ -25,6 +26,10 @@
"time"
"android/soong/shared"
+
+ "github.com/golang/protobuf/proto"
+
+ smpb "android/soong/ui/metrics/metrics_proto"
)
type Config struct{ *configImpl }
@@ -34,6 +39,7 @@
arguments []string
goma bool
environ *Environment
+ distDir string
// From the arguments
parallel int
@@ -44,10 +50,19 @@
skipMake bool
// From the product config
- katiArgs []string
- ninjaArgs []string
- katiSuffix string
- targetDevice string
+ katiArgs []string
+ ninjaArgs []string
+ katiSuffix string
+ targetDevice string
+ targetDeviceDir string
+
+ pdkBuild bool
+
+ brokenDupRules bool
+ brokenPhonyTargets bool
+ brokenUsesNetwork bool
+
+ pathReplaced bool
}
const srcDirFileCheck = "build/soong/root.bp"
@@ -78,6 +93,12 @@
ret.environ.Set("OUT_DIR", outDir)
}
+ if distDir, ok := ret.environ.Get("DIST_DIR"); ok {
+ ret.distDir = filepath.Clean(distDir)
+ } else {
+ ret.distDir = filepath.Join(ret.OutDir(), "dist")
+ }
+
ret.environ.Unset(
// We're already using it
"USE_SOONG_UI",
@@ -99,9 +120,15 @@
// We handle this above
"OUT_DIR_COMMON_BASE",
+ // This is handled above too, and set for individual commands later
+ "DIST_DIR",
+
// Variables that have caused problems in the past
+ "CDPATH",
"DISPLAY",
"GREP_OPTIONS",
+ "NDK_ROOT",
+ "POSIXLY_CORRECT",
// Drop make flags
"MAKEFLAGS",
@@ -110,11 +137,34 @@
// Set in envsetup.sh, reset in makefiles
"ANDROID_JAVA_TOOLCHAIN",
+
+ // Set by envsetup.sh, but shouldn't be used inside the build because envsetup.sh is optional
+ "ANDROID_BUILD_TOP",
+ "ANDROID_HOST_OUT",
+ "ANDROID_PRODUCT_OUT",
+ "ANDROID_HOST_OUT_TESTCASES",
+ "ANDROID_TARGET_OUT_TESTCASES",
+ "ANDROID_TOOLCHAIN",
+ "ANDROID_TOOLCHAIN_2ND_ARCH",
+ "ANDROID_DEV_SCRIPTS",
+ "ANDROID_EMULATOR_PREBUILTS",
+ "ANDROID_PRE_BUILD_PATHS",
+
+ // Only set in multiproduct_kati after config generation
+ "EMPTY_NINJA_FILE",
)
+ if ret.UseGoma() || ret.ForceUseGoma() {
+ ctx.Println("Goma for Android has been deprecated and replaced with RBE. See go/rbe_for_android for instructions on how to use RBE.")
+ ctx.Fatalln("USE_GOMA / FORCE_USE_GOMA flag is no longer supported.")
+ }
+
// Tell python not to spam the source tree with .pyc files.
ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
+ tmpDir := absPath(ctx, ret.TempDir())
+ ret.environ.Set("TMPDIR", tmpDir)
+
// Precondition: the current directory is the top of the source tree
if _, err := os.Stat(srcDirFileCheck); err != nil {
if os.IsNotExist(err) {
@@ -154,19 +204,7 @@
if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
return override
}
- v, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK9")
- if !ok {
- v2, ok2 := ret.environ.Get("RUN_ERROR_PRONE")
- if ok2 && (v2 == "true") {
- v = "false"
- } else {
- v = "1.8"
- }
- }
- if v != "false" {
- return java9Home
- }
- return java8Home
+ return java9Home
}()
absJavaHome := absPath(ctx, javaHome)
@@ -191,13 +229,37 @@
} else {
content = strconv.FormatInt(time.Now().Unix(), 10)
}
+
err := ioutil.WriteFile(buildDateTimeFile, []byte(content), 0777)
if err != nil {
ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
}
ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
- return Config{ret}
+ if ret.UseRBE() {
+ for k, v := range getRBEVars(ctx, Config{ret}) {
+ ret.environ.Set(k, v)
+ }
+ }
+
+ c := Config{ret}
+ storeConfigMetrics(ctx, c)
+ return c
+}
+
+// storeConfigMetrics selects a set of configuration information and store in
+// the metrics system for further analysis.
+func storeConfigMetrics(ctx Context, config Config) {
+ if ctx.Metrics == nil {
+ return
+ }
+
+ b := &smpb.BuildConfig{
+ ForceUseGoma: proto.Bool(config.ForceUseGoma()),
+ UseGoma: proto.Bool(config.UseGoma()),
+ UseRbe: proto.Bool(config.UseRBE()),
+ }
+ ctx.Metrics.BuildConfig(b)
}
func (c *configImpl) parseArgs(ctx Context, args []string) {
@@ -235,10 +297,10 @@
}
} else if k, v, ok := decodeKeyValue(arg); ok && len(k) > 0 {
c.environ.Set(k, v)
+ } else if arg == "dist" {
+ c.dist = true
} else {
- if arg == "dist" {
- c.dist = true
- } else if arg == "checkbuild" {
+ if arg == "checkbuild" {
c.checkbuild = true
}
c.arguments = append(c.arguments, arg)
@@ -282,6 +344,9 @@
// for others)
if inList("C.UTF-8", locales) {
c.environ.Set("LANG", "C.UTF-8")
+ } else if inList("C.utf8", locales) {
+ // These normalize to the same thing
+ c.environ.Set("LANG", "C.UTF-8")
} else if inList("en_US.UTF-8", locales) {
c.environ.Set("LANG", "en_US.UTF-8")
} else if inList("en_US.utf8", locales) {
@@ -353,16 +418,13 @@
func (c *configImpl) OutDir() string {
if outDir, ok := c.environ.Get("OUT_DIR"); ok {
- return outDir
+ return filepath.Clean(outDir)
}
return "out"
}
func (c *configImpl) DistDir() string {
- if distDir, ok := c.environ.Get("DIST_DIR"); ok {
- return distDir
- }
- return filepath.Join(c.OutDir(), "dist")
+ return c.distDir
}
func (c *configImpl) NinjaArgs() []string {
@@ -439,6 +501,18 @@
return c.parallel
}
+// ForceUseGoma determines whether we should override Goma deprecation
+// and use Goma for the current build or not.
+func (c *configImpl) ForceUseGoma() bool {
+ if v, ok := c.environ.Get("FORCE_USE_GOMA"); ok {
+ v = strings.TrimSpace(v)
+ if v != "" && v != "false" {
+ return true
+ }
+ }
+ return false
+}
+
func (c *configImpl) UseGoma() bool {
if v, ok := c.environ.Get("USE_GOMA"); ok {
v = strings.TrimSpace(v)
@@ -449,6 +523,117 @@
return false
}
+func (c *configImpl) StartGoma() bool {
+ if !c.UseGoma() {
+ return false
+ }
+
+ if v, ok := c.environ.Get("NOSTART_GOMA"); ok {
+ v = strings.TrimSpace(v)
+ if v != "" && v != "false" {
+ return false
+ }
+ }
+ return true
+}
+
+func (c *configImpl) UseRBE() bool {
+ if v, ok := c.environ.Get("USE_RBE"); ok {
+ v = strings.TrimSpace(v)
+ if v != "" && v != "false" {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *configImpl) StartRBE() bool {
+ if !c.UseRBE() {
+ return false
+ }
+
+ if v, ok := c.environ.Get("NOSTART_RBE"); ok {
+ v = strings.TrimSpace(v)
+ if v != "" && v != "false" {
+ return false
+ }
+ }
+ return true
+}
+
+func (c *configImpl) logDir() string {
+ if c.Dist() {
+ return filepath.Join(c.DistDir(), "logs")
+ }
+ return c.OutDir()
+}
+
+func (c *configImpl) rbeStatsOutputDir() string {
+ for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} {
+ if v, ok := c.environ.Get(f); ok {
+ return v
+ }
+ }
+ return c.logDir()
+}
+
+func (c *configImpl) rbeLogPath() string {
+ for _, f := range []string{"RBE_log_path", "FLAG_log_path"} {
+ if v, ok := c.environ.Get(f); ok {
+ return v
+ }
+ }
+ return fmt.Sprintf("text://%v/reproxy_log.txt", c.logDir())
+}
+
+func (c *configImpl) rbeExecRoot() string {
+ for _, f := range []string{"RBE_exec_root", "FLAG_exec_root"} {
+ if v, ok := c.environ.Get(f); ok {
+ return v
+ }
+ }
+ wd, err := os.Getwd()
+ if err != nil {
+ return ""
+ }
+ return wd
+}
+
+func (c *configImpl) rbeDir() string {
+ if v, ok := c.environ.Get("RBE_DIR"); ok {
+ return v
+ }
+ return "prebuilts/remoteexecution-client/live/"
+}
+
+func (c *configImpl) rbeReproxy() string {
+ for _, f := range []string{"RBE_re_proxy", "FLAG_re_proxy"} {
+ if v, ok := c.environ.Get(f); ok {
+ return v
+ }
+ }
+ return filepath.Join(c.rbeDir(), "reproxy")
+}
+
+func (c *configImpl) rbeAuth() (string, string) {
+ credFlags := []string{"use_application_default_credentials", "use_gce_credentials", "credential_file"}
+ for _, cf := range credFlags {
+ for _, f := range []string{"RBE_" + cf, "FLAG_" + cf} {
+ if v, ok := c.environ.Get(f); ok {
+ v = strings.TrimSpace(v)
+ if v != "" && v != "false" && v != "0" {
+ return "RBE_" + cf, v
+ }
+ }
+ }
+ }
+ return "RBE_use_application_default_credentials", "true"
+}
+
+func (c *configImpl) UseRemoteBuild() bool {
+ return c.UseGoma() || c.UseRBE()
+}
+
// RemoteParallel controls how many remote jobs (i.e., commands which contain
// gomacc) are run in parallel. Note the parallelism of all other jobs is
// still limited by Parallel()
@@ -485,8 +670,12 @@
return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh")
}
-func (c *configImpl) KatiNinjaFile() string {
- return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+".ninja")
+func (c *configImpl) KatiBuildNinjaFile() string {
+ return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiBuildSuffix+".ninja")
+}
+
+func (c *configImpl) KatiPackageNinjaFile() string {
+ return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja")
}
func (c *configImpl) SoongNinjaFile() string {
@@ -516,6 +705,10 @@
return filepath.Join(c.ProductOut(), "previous_build_config.mk")
}
+func (c *configImpl) KatiPackageMkDir() string {
+ return filepath.Join(c.ProductOut(), "obj", "CONFIG", "kati_packaging")
+}
+
func (c *configImpl) hostOutRoot() string {
return filepath.Join(c.OutDir(), "host")
}
@@ -554,3 +747,50 @@
}
return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)
}
+
+func (c *configImpl) SetBuildBrokenDupRules(val bool) {
+ c.brokenDupRules = val
+}
+
+func (c *configImpl) BuildBrokenDupRules() bool {
+ return c.brokenDupRules
+}
+
+func (c *configImpl) SetBuildBrokenPhonyTargets(val bool) {
+ c.brokenPhonyTargets = val
+}
+
+func (c *configImpl) BuildBrokenPhonyTargets() bool {
+ return c.brokenPhonyTargets
+}
+
+func (c *configImpl) SetBuildBrokenUsesNetwork(val bool) {
+ c.brokenUsesNetwork = val
+}
+
+func (c *configImpl) BuildBrokenUsesNetwork() bool {
+ return c.brokenUsesNetwork
+}
+
+func (c *configImpl) SetTargetDeviceDir(dir string) {
+ c.targetDeviceDir = dir
+}
+
+func (c *configImpl) TargetDeviceDir() string {
+ return c.targetDeviceDir
+}
+
+func (c *configImpl) SetPdkBuild(pdk bool) {
+ c.pdkBuild = pdk
+}
+
+func (c *configImpl) IsPdkBuild() bool {
+ return c.pdkBuild
+}
+
+func (c *configImpl) MetricsUploaderApp() string {
+ if p, ok := c.environ.Get("ANDROID_ENABLE_METRICS_UPLOAD"); ok {
+ return p
+ }
+ return ""
+}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index e4eab94..e1fb271 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -22,13 +22,16 @@
"testing"
"android/soong/ui/logger"
+ "android/soong/ui/status"
+ "android/soong/ui/terminal"
)
func testContext() Context {
return Context{&ContextImpl{
- Context: context.Background(),
- Logger: logger.New(&bytes.Buffer{}),
- StdioInterface: NewCustomStdio(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}),
+ Context: context.Background(),
+ Logger: logger.New(&bytes.Buffer{}),
+ Writer: terminal.NewWriter(terminal.NewCustomStdio(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{})),
+ Status: &status.Status{},
}}
}
diff --git a/ui/build/context.go b/ui/build/context.go
index 0636631..249e898 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -16,45 +16,16 @@
import (
"context"
- "io"
- "os"
- "time"
"android/soong/ui/logger"
+ "android/soong/ui/metrics"
+ "android/soong/ui/metrics/metrics_proto"
+ "android/soong/ui/status"
+ "android/soong/ui/terminal"
"android/soong/ui/tracer"
)
-type StdioInterface interface {
- Stdin() io.Reader
- Stdout() io.Writer
- Stderr() io.Writer
-}
-
-type StdioImpl struct{}
-
-func (StdioImpl) Stdin() io.Reader { return os.Stdin }
-func (StdioImpl) Stdout() io.Writer { return os.Stdout }
-func (StdioImpl) Stderr() io.Writer { return os.Stderr }
-
-var _ StdioInterface = StdioImpl{}
-
-type customStdio struct {
- stdin io.Reader
- stdout io.Writer
- stderr io.Writer
-}
-
-func NewCustomStdio(stdin io.Reader, stdout, stderr io.Writer) StdioInterface {
- return customStdio{stdin, stdout, stderr}
-}
-
-func (c customStdio) Stdin() io.Reader { return c.stdin }
-func (c customStdio) Stdout() io.Writer { return c.stdout }
-func (c customStdio) Stderr() io.Writer { return c.stderr }
-
-var _ StdioInterface = customStdio{}
-
-// Context combines a context.Context, logger.Logger, and StdIO redirection.
+// Context combines a context.Context, logger.Logger, and terminal.Writer.
// These all are agnostic of the current build, and may be used for multiple
// builds, while the Config objects contain per-build information.
type Context struct{ *ContextImpl }
@@ -62,16 +33,22 @@
context.Context
logger.Logger
- StdioInterface
+ Metrics *metrics.Metrics
+
+ Writer terminal.Writer
+ Status *status.Status
Thread tracer.Thread
Tracer tracer.Tracer
}
// BeginTrace starts a new Duration Event.
-func (c ContextImpl) BeginTrace(name string) {
+func (c ContextImpl) BeginTrace(name, desc string) {
if c.Tracer != nil {
- c.Tracer.Begin(name, c.Thread)
+ c.Tracer.Begin(desc, c.Thread)
+ }
+ if c.Metrics != nil {
+ c.Metrics.TimeTracer.Begin(name, desc, c.Thread)
}
}
@@ -80,36 +57,23 @@
if c.Tracer != nil {
c.Tracer.End(c.Thread)
}
+ if c.Metrics != nil {
+ c.Metrics.SetTimeMetrics(c.Metrics.TimeTracer.End(c.Thread))
+ }
}
// CompleteTrace writes a trace with a beginning and end times.
-func (c ContextImpl) CompleteTrace(name string, begin, end uint64) {
+func (c ContextImpl) CompleteTrace(name, desc string, begin, end uint64) {
if c.Tracer != nil {
- c.Tracer.Complete(name, c.Thread, begin, end)
+ c.Tracer.Complete(desc, c.Thread, begin, end)
}
-}
-
-// ImportNinjaLog imports a .ninja_log file into the tracer.
-func (c ContextImpl) ImportNinjaLog(filename string, startOffset time.Time) {
- if c.Tracer != nil {
- c.Tracer.ImportNinjaLog(c.Thread, filename, startOffset)
+ if c.Metrics != nil {
+ realTime := end - begin
+ c.Metrics.SetTimeMetrics(
+ metrics_proto.PerfInfo{
+ Desc: &desc,
+ Name: &name,
+ StartTime: &begin,
+ RealTime: &realTime})
}
}
-
-func (c ContextImpl) IsTerminal() bool {
- if term, ok := os.LookupEnv("TERM"); ok {
- return term != "dumb" && isTerminal(c.Stdout()) && isTerminal(c.Stderr())
- }
- return false
-}
-
-func (c ContextImpl) IsErrTerminal() bool {
- if term, ok := os.LookupEnv("TERM"); ok {
- return term != "dumb" && isTerminal(c.Stderr())
- }
- return false
-}
-
-func (c ContextImpl) TermWidth() (int, bool) {
- return termWidth(c.Stdout())
-}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index a0e1eca..2524e27 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -18,6 +18,9 @@
"bytes"
"fmt"
"strings"
+
+ "android/soong/ui/metrics"
+ "android/soong/ui/status"
)
// DumpMakeVars can be used to extract the values of Make variables after the
@@ -30,12 +33,44 @@
//
// vars is the list of variables to read. The values will be put in the
// returned map.
+//
+// variables controlled by soong_ui directly are now returned without needing
+// to call into make, to retain compatibility.
func DumpMakeVars(ctx Context, config Config, goals, vars []string) (map[string]string, error) {
- return dumpMakeVars(ctx, config, goals, vars, false)
+ soongUiVars := map[string]func() string{
+ "OUT_DIR": func() string { return config.OutDir() },
+ "DIST_DIR": func() string { return config.DistDir() },
+ }
+
+ makeVars := make([]string, 0, len(vars))
+ for _, v := range vars {
+ if _, ok := soongUiVars[v]; !ok {
+ makeVars = append(makeVars, v)
+ }
+ }
+
+ var ret map[string]string
+ if len(makeVars) > 0 {
+ var err error
+ ret, err = dumpMakeVars(ctx, config, goals, makeVars, false)
+ if err != nil {
+ return ret, err
+ }
+ } else {
+ ret = make(map[string]string)
+ }
+
+ for _, v := range vars {
+ if f, ok := soongUiVars[v]; ok {
+ ret[v] = f()
+ }
+ }
+
+ return ret, nil
}
func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool) (map[string]string, error) {
- ctx.BeginTrace("dumpvars")
+ ctx.BeginTrace(metrics.RunKati, "dumpvars")
defer ctx.EndTrace()
cmd := Command(ctx, config, "dumpvars",
@@ -46,7 +81,6 @@
"dump-many-vars",
"MAKECMDGOALS="+strings.Join(goals, " "))
cmd.Environment.Set("CALLED_FROM_SETUP", "true")
- cmd.Environment.Set("BUILD_SYSTEM", "build/make/core")
if write_soong_vars {
cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true")
}
@@ -60,7 +94,7 @@
}
cmd.StartOrFatal()
// TODO: error out when Stderr contains any content
- katiRewriteOutput(ctx, pipe)
+ status.KatiReader(ctx.Status.StartTool(), pipe)
cmd.WaitOrFatal()
ret := make(map[string]string, len(vars))
@@ -80,6 +114,9 @@
return nil, fmt.Errorf("Failed to parse make line: %q", line)
}
}
+ if ctx.Metrics != nil {
+ ctx.Metrics.SetMetadataMetrics(ret)
+ }
return ret, nil
}
@@ -140,7 +177,10 @@
// compiler wrappers set up by make
"CC_WRAPPER",
"CXX_WRAPPER",
+ "RBE_WRAPPER",
"JAVAC_WRAPPER",
+ "R8_WRAPPER",
+ "D8_WRAPPER",
// ccache settings
"CCACHE_COMPILERCHECK",
@@ -156,6 +196,24 @@
// To find target/product/<DEVICE>
"TARGET_DEVICE",
+
+ // So that later Kati runs can find BoardConfig.mk faster
+ "TARGET_DEVICE_DIR",
+
+ // Whether --werror_overriding_commands will work
+ "BUILD_BROKEN_DUP_RULES",
+
+ // Used to turn on --werror_ options in Kati
+ "BUILD_BROKEN_PHONY_TARGETS",
+
+ // Whether to enable the network during the build
+ "BUILD_BROKEN_USES_NETWORK",
+
+ // Not used, but useful to be in the soong.log
+ "BOARD_VNDK_VERSION",
+ "BUILD_BROKEN_ANDROIDMK_EXPORTS",
+ "BUILD_BROKEN_DUP_COPY_HEADERS",
+ "BUILD_BROKEN_ENG_DEBUG_TAGS",
}, exportEnvVars...), BannerVars...)
make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true)
@@ -163,11 +221,13 @@
ctx.Fatalln("Error dumping make vars:", err)
}
+ env := config.Environment()
// Print the banner like make does
- fmt.Fprintln(ctx.Stdout(), Banner(make_vars))
+ if !env.IsEnvTrue("ANDROID_QUIET_BUILD") {
+ ctx.Writer.Print(Banner(make_vars))
+ }
// Populate the environment
- env := config.Environment()
for _, name := range exportEnvVars {
if make_vars[name] == "" {
env.Unset(name)
@@ -179,4 +239,10 @@
config.SetKatiArgs(strings.Fields(make_vars["KATI_GOALS"]))
config.SetNinjaArgs(strings.Fields(make_vars["NINJA_GOALS"]))
config.SetTargetDevice(make_vars["TARGET_DEVICE"])
+ config.SetTargetDeviceDir(make_vars["TARGET_DEVICE_DIR"])
+
+ config.SetPdkBuild(make_vars["TARGET_BUILD_PDK"] == "true")
+ config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] == "true")
+ config.SetBuildBrokenPhonyTargets(make_vars["BUILD_BROKEN_PHONY_TARGETS"] == "true")
+ config.SetBuildBrokenUsesNetwork(make_vars["BUILD_BROKEN_USES_NETWORK"] == "true")
}
diff --git a/ui/build/environment.go b/ui/build/environment.go
index 8589937..d8ff7f2 100644
--- a/ui/build/environment.go
+++ b/ui/build/environment.go
@@ -75,6 +75,17 @@
*e = out
}
+// Allow removes all keys that are not present in the input list
+func (e *Environment) Allow(keys ...string) {
+ out := (*e)[:0]
+ for _, env := range *e {
+ if key, _, ok := decodeKeyValue(env); ok && inList(key, keys) {
+ out = append(out, env)
+ }
+ }
+ *e = out
+}
+
// Environ returns the []string required for exec.Cmd.Env
func (e *Environment) Environ() []string {
return []string(*e)
@@ -134,7 +145,7 @@
if cmd[0] == "unset" {
str, ok := singleUnquote(cmd[1])
if !ok {
- fmt.Errorf("Failed to unquote kati line: %q", text)
+ return fmt.Errorf("Failed to unquote kati line: %q", text)
}
e.Unset(str)
} else if cmd[0] == "export" {
diff --git a/ui/build/environment_test.go b/ui/build/environment_test.go
index 0294dac..37f500f 100644
--- a/ui/build/environment_test.go
+++ b/ui/build/environment_test.go
@@ -56,6 +56,15 @@
}
}
+func TestEnvAllow(t *testing.T) {
+ initial := &Environment{"TEST=1", "TEST2=0", "TEST3=2"}
+ initial.Allow("TEST3", "TEST")
+ got := initial.Environ()
+ if len(got) != 2 || got[0] != "TEST=1" || got[1] != "TEST3=2" {
+ t.Errorf("Expected [TEST=1 TEST3=2], got: %v", got)
+ }
+}
+
const testKatiEnvFileContents = `#!/bin/sh
# Generated by kati unknown
diff --git a/ui/build/exec.go b/ui/build/exec.go
index 90fb19d..5c312bc 100644
--- a/ui/build/exec.go
+++ b/ui/build/exec.go
@@ -122,3 +122,20 @@
c.reportError(err)
return ret
}
+
+// RunAndPrintOrFatal will run the command, then after finishing
+// print any output, then handling any errors with a call to
+// ctx.Fatal
+func (c *Cmd) RunAndPrintOrFatal() {
+ ret, err := c.CombinedOutput()
+ st := c.ctx.Status.StartTool()
+ if len(ret) > 0 {
+ if err != nil {
+ st.Error(string(ret))
+ } else {
+ st.Print(string(ret))
+ }
+ }
+ st.Finish()
+ c.reportError(err)
+}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 3bd6d87..0f34b5e 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -23,6 +23,8 @@
"os"
"path/filepath"
"strings"
+
+ "android/soong/ui/metrics"
)
// This file provides an interface to the Finder for use in Soong UI
@@ -31,7 +33,7 @@
// NewSourceFinder returns a new Finder configured to search for source files.
// Callers of NewSourceFinder should call <f.Shutdown()> when done
func NewSourceFinder(ctx Context, config Config) (f *finder.Finder) {
- ctx.BeginTrace("find modules")
+ ctx.BeginTrace(metrics.RunSetupTool, "find modules")
defer ctx.EndTrace()
dir, err := os.Getwd()
@@ -56,7 +58,15 @@
RootDirs: []string{"."},
ExcludeDirs: []string{".git", ".repo"},
PruneFiles: pruneFiles,
- IncludeFiles: []string{"Android.mk", "Android.bp", "Blueprints", "CleanSpec.mk", "TEST_MAPPING"},
+ IncludeFiles: []string{
+ "Android.mk",
+ "AndroidProducts.mk",
+ "Android.bp",
+ "Blueprints",
+ "CleanSpec.mk",
+ "OWNERS",
+ "TEST_MAPPING",
+ },
}
dumpDir := config.FileListDir()
f, err = finder.New(cacheParams, filesystem, logger.New(ioutil.Discard),
@@ -81,16 +91,30 @@
ctx.Fatalf("Could not export module list: %v", err)
}
+ androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk")
+ androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...)
+ androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...)
+ err = dumpListToFile(androidProductsMks, filepath.Join(dumpDir, "AndroidProducts.mk.list"))
+ if err != nil {
+ ctx.Fatalf("Could not export product list: %v", err)
+ }
+
cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
- dumpListToFile(cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
+ err = dumpListToFile(cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
if err != nil {
ctx.Fatalf("Could not export module list: %v", err)
}
+ owners := f.FindNamedAt(".", "OWNERS")
+ err = dumpListToFile(owners, filepath.Join(dumpDir, "OWNERS.list"))
+ if err != nil {
+ ctx.Fatalf("Could not find OWNERS: %v", err)
+ }
+
testMappings := f.FindNamedAt(".", "TEST_MAPPING")
err = dumpListToFile(testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
if err != nil {
- ctx.Fatalf("Could not find modules: %v", err)
+ ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
}
androidBps := f.FindNamedAt(".", "Android.bp")
diff --git a/ui/build/goma.go b/ui/build/goma.go
new file mode 100644
index 0000000..ff0b40e
--- /dev/null
+++ b/ui/build/goma.go
@@ -0,0 +1,79 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+ "fmt"
+ "math"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "android/soong/ui/metrics"
+)
+
+const gomaCtlScript = "goma_ctl.py"
+const gomaLeastNProcs = 2500
+const gomaLeastNFiles = 16000
+
+// ulimit returns ulimit result for |opt|.
+// if the resource is unlimited, it returns math.MaxInt32 so that a caller do
+// not need special handling of the returned value.
+//
+// Note that since go syscall package do not have RLIMIT_NPROC constant,
+// we use bash ulimit instead.
+func ulimitOrFatal(ctx Context, config Config, opt string) int {
+ commandText := fmt.Sprintf("ulimit %s", opt)
+ cmd := Command(ctx, config, commandText, "bash", "-c", commandText)
+ output := strings.TrimRight(string(cmd.CombinedOutputOrFatal()), "\n")
+ ctx.Verbose(output + "\n")
+ ctx.Verbose("done\n")
+
+ if output == "unlimited" {
+ return math.MaxInt32
+ }
+ num, err := strconv.Atoi(output)
+ if err != nil {
+ ctx.Fatalf("ulimit returned unexpected value: %s: %v\n", opt, err)
+ }
+ return num
+}
+
+func startGoma(ctx Context, config Config) {
+ ctx.BeginTrace(metrics.RunSetupTool, "goma_ctl")
+ defer ctx.EndTrace()
+
+ if u := ulimitOrFatal(ctx, config, "-u"); u < gomaLeastNProcs {
+ ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, gomaLeastNProcs)
+ }
+ if n := ulimitOrFatal(ctx, config, "-n"); n < gomaLeastNFiles {
+ ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, gomaLeastNFiles)
+ }
+
+ var gomaCtl string
+ if gomaDir, ok := config.Environment().Get("GOMA_DIR"); ok {
+ gomaCtl = filepath.Join(gomaDir, gomaCtlScript)
+ } else if home, ok := config.Environment().Get("HOME"); ok {
+ gomaCtl = filepath.Join(home, "goma", gomaCtlScript)
+ } else {
+ ctx.Fatalln("goma_ctl.py not found")
+ }
+
+ cmd := Command(ctx, config, "goma_ctl.py ensure_start", gomaCtl, "ensure_start")
+
+ if output, err := cmd.CombinedOutput(); err != nil {
+ ctx.Fatalf("goma_ctl.py ensure_start failed with: %v\n%s\n", err, output)
+ }
+}
diff --git a/ui/build/kati.go b/ui/build/kati.go
index e4715bb..959d0bd 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -15,19 +15,24 @@
package build
import (
- "bufio"
"crypto/md5"
"fmt"
- "io"
"io/ioutil"
+ "os"
+ "os/user"
"path/filepath"
- "regexp"
- "strconv"
"strings"
+
+ "android/soong/ui/metrics"
+ "android/soong/ui/status"
)
var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_")
+const katiBuildSuffix = ""
+const katiCleanspecSuffix = "-cleanspec"
+const katiPackageSuffix = "-package"
+
// genKatiSuffix creates a suffix for kati-generated files so that we can cache
// them based on their inputs. So this should encode all common changes to Kati
// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command
@@ -50,7 +55,7 @@
ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix)
ctx.Verbosef("Replacing with: %q", shortSuffix)
- if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
+ if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiBuildNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
ctx.Println("Error writing suffix file:", err)
}
} else {
@@ -58,42 +63,30 @@
}
}
-func runKati(ctx Context, config Config) {
- genKatiSuffix(ctx, config)
-
- runKatiCleanSpec(ctx, config)
-
- ctx.BeginTrace("kati")
- defer ctx.EndTrace()
-
+func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
executable := config.PrebuiltBuildTool("ckati")
- args := []string{
+ args = append([]string{
"--ninja",
"--ninja_dir=" + config.OutDir(),
- "--ninja_suffix=" + config.KatiSuffix(),
+ "--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
+ "--no_ninja_prelude",
"--regen",
"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
"--detect_android_echo",
"--color_warnings",
"--gen_all_targets",
+ "--use_find_emulator",
"--werror_find_emulator",
+ "--no_builtin_rules",
+ "--werror_suffix_rules",
+ "--warn_real_to_phony",
+ "--warn_phony_looks_real",
+ "--top_level_phony",
"--kati_stats",
- "-f", "build/make/core/main.mk",
- }
+ }, args...)
- if !config.Environment().IsFalse("KATI_EMULATE_FIND") {
- args = append(args, "--use_find_emulator")
- }
-
- args = append(args, config.KatiArgs()...)
-
- args = append(args,
- "BUILDING_WITH_NINJA=true",
- "SOONG_ANDROID_MK="+config.SoongAndroidMk(),
- "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk())
-
- if config.UseGoma() {
- args = append(args, "-j"+strconv.Itoa(config.Parallel()))
+ if config.Environment().IsEnvTrue("EMPTY_NINJA_FILE") {
+ args = append(args, "--empty_ninja_file")
}
cmd := Command(ctx, config, "ckati", executable, args...)
@@ -104,99 +97,118 @@
}
cmd.Stderr = cmd.Stdout
+ envFunc(cmd.Environment)
+
+ if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
+ u, err := user.Current()
+ if err != nil {
+ ctx.Println("Failed to get current user")
+ }
+ cmd.Environment.Set("BUILD_USERNAME", u.Username)
+ }
+
+ if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
+ hostname, err := os.Hostname()
+ if err != nil {
+ ctx.Println("Failed to read hostname")
+ }
+ cmd.Environment.Set("BUILD_HOSTNAME", hostname)
+ }
+
cmd.StartOrFatal()
- katiRewriteOutput(ctx, pipe)
+ status.KatiReader(ctx.Status.StartTool(), pipe)
cmd.WaitOrFatal()
}
-var katiIncludeRe = regexp.MustCompile(`^(\[\d+/\d+] )?including [^ ]+ ...$`)
-var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
+func runKatiBuild(ctx Context, config Config) {
+ ctx.BeginTrace(metrics.RunKati, "kati build")
+ defer ctx.EndTrace()
-func katiRewriteOutput(ctx Context, pipe io.ReadCloser) {
- haveBlankLine := true
- smartTerminal := ctx.IsTerminal()
- errSmartTerminal := ctx.IsErrTerminal()
-
- scanner := bufio.NewScanner(pipe)
- for scanner.Scan() {
- line := scanner.Text()
- verbose := katiIncludeRe.MatchString(line)
-
- // Only put kati debug/stat lines in our verbose log
- if katiLogRe.MatchString(line) {
- ctx.Verbose(line)
- continue
- }
-
- // For verbose lines, write them on the current line without a newline,
- // then overwrite them if the next thing we're printing is another
- // verbose line.
- if smartTerminal && verbose {
- // Limit line width to the terminal width, otherwise we'll wrap onto
- // another line and we won't delete the previous line.
- //
- // Run this on every line in case the window has been resized while
- // we're printing. This could be optimized to only re-run when we
- // get SIGWINCH if it ever becomes too time consuming.
- if max, ok := termWidth(ctx.Stdout()); ok {
- if len(line) > max {
- // Just do a max. Ninja elides the middle, but that's
- // more complicated and these lines aren't that important.
- line = line[:max]
- }
- }
-
- // Move to the beginning on the line, print the output, then clear
- // the rest of the line.
- fmt.Fprint(ctx.Stdout(), "\r", line, "\x1b[K")
- haveBlankLine = false
- continue
- } else if smartTerminal && !haveBlankLine {
- // If we've previously written a verbose message, send a newline to save
- // that message instead of overwriting it.
- fmt.Fprintln(ctx.Stdout())
- haveBlankLine = true
- } else if !errSmartTerminal {
- // Most editors display these as garbage, so strip them out.
- line = string(stripAnsiEscapes([]byte(line)))
- }
-
- // Assume that non-verbose lines are important enough for stderr
- fmt.Fprintln(ctx.Stderr(), line)
+ args := []string{
+ "--writable", config.OutDir() + "/",
+ "-f", "build/make/core/main.mk",
}
- // Save our last verbose line.
- if !haveBlankLine {
- fmt.Fprintln(ctx.Stdout())
+ // PDK builds still uses a few implicit rules
+ if !config.IsPdkBuild() {
+ args = append(args, "--werror_implicit_rules")
}
+
+ if !config.BuildBrokenDupRules() {
+ args = append(args, "--werror_overriding_commands")
+ }
+
+ if !config.BuildBrokenPhonyTargets() {
+ args = append(args,
+ "--werror_real_to_phony",
+ "--werror_phony_looks_real",
+ "--werror_writable")
+ }
+
+ args = append(args, config.KatiArgs()...)
+
+ args = append(args,
+ "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
+ "SOONG_ANDROID_MK="+config.SoongAndroidMk(),
+ "TARGET_DEVICE_DIR="+config.TargetDeviceDir(),
+ "KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())
+
+ runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
+}
+
+func runKatiPackage(ctx Context, config Config) {
+ ctx.BeginTrace(metrics.RunKati, "kati package")
+ defer ctx.EndTrace()
+
+ args := []string{
+ "--writable", config.DistDir() + "/",
+ "--werror_writable",
+ "--werror_implicit_rules",
+ "--werror_overriding_commands",
+ "--werror_real_to_phony",
+ "--werror_phony_looks_real",
+ "-f", "build/make/packaging/main.mk",
+ "KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
+ }
+
+ runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) {
+ env.Allow([]string{
+ // Some generic basics
+ "LANG",
+ "LC_MESSAGES",
+ "PATH",
+ "PWD",
+ "TMPDIR",
+
+ // Tool configs
+ "JAVA_HOME",
+ "PYTHONDONTWRITEBYTECODE",
+
+ // Build configuration
+ "ANDROID_BUILD_SHELL",
+ "DIST_DIR",
+ "OUT_DIR",
+ }...)
+
+ if config.Dist() {
+ env.Set("DIST", "true")
+ env.Set("DIST_DIR", config.DistDir())
+ }
+ })
}
func runKatiCleanSpec(ctx Context, config Config) {
- ctx.BeginTrace("kati cleanspec")
+ ctx.BeginTrace(metrics.RunKati, "kati cleanspec")
defer ctx.EndTrace()
- executable := config.PrebuiltBuildTool("ckati")
- args := []string{
- "--ninja",
- "--ninja_dir=" + config.OutDir(),
- "--ninja_suffix=" + config.KatiSuffix() + "-cleanspec",
- "--regen",
- "--detect_android_echo",
- "--color_warnings",
- "--gen_all_targets",
- "--werror_find_emulator",
- "--use_find_emulator",
+ runKati(ctx, config, katiCleanspecSuffix, []string{
+ "--werror_writable",
+ "--werror_implicit_rules",
+ "--werror_overriding_commands",
+ "--werror_real_to_phony",
+ "--werror_phony_looks_real",
"-f", "build/make/core/cleanbuild.mk",
- "BUILDING_WITH_NINJA=true",
"SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
- }
-
- cmd := Command(ctx, config, "ckati", executable, args...)
- cmd.Sandbox = katiCleanSpecSandbox
- cmd.Stdout = ctx.Stdout()
- cmd.Stderr = ctx.Stderr()
-
- // Kati leaks memory, so ensure leak detection is turned off
- cmd.Environment.Set("ASAN_OPTIONS", "detect_leaks=0")
- cmd.RunOrFatal()
+ "TARGET_DEVICE_DIR=" + config.TargetDeviceDir(),
+ }, func(env *Environment) {})
}
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 96b5e9d..7497c94 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -21,21 +21,29 @@
"strconv"
"strings"
"time"
+
+ "android/soong/ui/metrics"
+ "android/soong/ui/status"
)
func runNinja(ctx Context, config Config) {
- ctx.BeginTrace("ninja")
+ ctx.BeginTrace(metrics.PrimaryNinja, "ninja")
defer ctx.EndTrace()
+ fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
+ nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
+ defer nr.Close()
+
executable := config.PrebuiltBuildTool("ninja")
args := []string{
"-d", "keepdepfile",
+ "--frontend_file", fifo,
}
args = append(args, config.NinjaArgs()...)
var parallel int
- if config.UseGoma() {
+ if config.UseGoma() || config.UseRBE() {
parallel = config.RemoteParallel()
} else {
parallel = config.Parallel()
@@ -47,16 +55,18 @@
args = append(args, "-f", config.CombinedNinjaFile())
- if config.IsVerbose() {
- args = append(args, "-v")
- }
- args = append(args, "-w", "dupbuild=err")
+ args = append(args,
+ "-w", "dupbuild=err",
+ "-w", "missingdepfile=err")
cmd := Command(ctx, config, "ninja", executable, args...)
+ cmd.Sandbox = ninjaSandbox
if config.HasKatiSuffix() {
cmd.Environment.AppendFromKati(config.KatiEnvFile())
}
+ cmd.Environment.Set("DIST_DIR", config.DistDir())
+
// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
// used in the past to specify extra ninja arguments.
if extra, ok := cmd.Environment.Get("NINJA_ARGS"); ok {
@@ -66,13 +76,6 @@
cmd.Args = append(cmd.Args, strings.Fields(extra)...)
}
- if _, ok := cmd.Environment.Get("NINJA_STATUS"); !ok {
- cmd.Environment.Set("NINJA_STATUS", "[%p %f/%t] ")
- }
-
- cmd.Stdin = ctx.Stdin()
- cmd.Stdout = ctx.Stdout()
- cmd.Stderr = ctx.Stderr()
logPath := filepath.Join(config.OutDir(), ".ninja_log")
ninjaHeartbeatDuration := time.Minute * 5
if overrideText, ok := cmd.Environment.Get("NINJA_HEARTBEAT_INTERVAL"); ok {
@@ -99,10 +102,8 @@
}
}()
- startTime := time.Now()
- defer ctx.ImportNinjaLog(logPath, startTime)
-
- cmd.RunOrFatal()
+ ctx.Status.Status("Starting ninja...")
+ cmd.RunAndPrintOrFatal()
}
type statusChecker struct {
diff --git a/ui/build/path.go b/ui/build/path.go
new file mode 100644
index 0000000..0e1c02c
--- /dev/null
+++ b/ui/build/path.go
@@ -0,0 +1,157 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+
+ "github.com/google/blueprint/microfactory"
+
+ "android/soong/ui/build/paths"
+ "android/soong/ui/metrics"
+)
+
+func parsePathDir(dir string) []string {
+ f, err := os.Open(dir)
+ if err != nil {
+ return nil
+ }
+ defer f.Close()
+
+ if s, err := f.Stat(); err != nil || !s.IsDir() {
+ return nil
+ }
+
+ infos, err := f.Readdir(-1)
+ if err != nil {
+ return nil
+ }
+
+ ret := make([]string, 0, len(infos))
+ for _, info := range infos {
+ if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
+ ret = append(ret, info.Name())
+ }
+ }
+ return ret
+}
+
+func SetupPath(ctx Context, config Config) {
+ if config.pathReplaced {
+ return
+ }
+
+ ctx.BeginTrace(metrics.RunSetupTool, "path")
+ defer ctx.EndTrace()
+
+ origPath, _ := config.Environment().Get("PATH")
+ myPath := filepath.Join(config.OutDir(), ".path")
+ interposer := myPath + "_interposer"
+
+ var cfg microfactory.Config
+ cfg.Map("android/soong", "build/soong")
+ cfg.TrimPath, _ = filepath.Abs(".")
+ if _, err := microfactory.Build(&cfg, interposer, "android/soong/cmd/path_interposer"); err != nil {
+ ctx.Fatalln("Failed to build path interposer:", err)
+ }
+
+ if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
+ ctx.Fatalln("Failed to write original path:", err)
+ }
+
+ entries, err := paths.LogListener(ctx.Context, interposer+"_log")
+ if err != nil {
+ ctx.Fatalln("Failed to listen for path logs:", err)
+ }
+
+ go func() {
+ for log := range entries {
+ curPid := os.Getpid()
+ for i, proc := range log.Parents {
+ if proc.Pid == curPid {
+ log.Parents = log.Parents[i:]
+ break
+ }
+ }
+ procPrints := []string{
+ "See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
+ }
+ if len(log.Parents) > 0 {
+ procPrints = append(procPrints, "Process tree:")
+ for i, proc := range log.Parents {
+ procPrints = append(procPrints, fmt.Sprintf("%s→ %s", strings.Repeat(" ", i), proc.Command))
+ }
+ }
+
+ config := paths.GetConfig(log.Basename)
+ if config.Error {
+ ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
+ for _, line := range procPrints {
+ ctx.Println(line)
+ }
+ } else {
+ ctx.Verbosef("Unknown PATH tool %q used: %#v", log.Basename, log.Args)
+ for _, line := range procPrints {
+ ctx.Verboseln(line)
+ }
+ }
+ }
+ }()
+
+ ensureEmptyDirectoriesExist(ctx, myPath)
+
+ var execs []string
+ for _, pathEntry := range filepath.SplitList(origPath) {
+ if pathEntry == "" {
+ // Ignore the current directory
+ continue
+ }
+ // TODO(dwillemsen): remove path entries under TOP? or anything
+ // that looks like an android source dir? They won't exist on
+ // the build servers, since they're added by envsetup.sh.
+ // (Except for the JDK, which is configured in ui/build/config.go)
+
+ execs = append(execs, parsePathDir(pathEntry)...)
+ }
+
+ allowAllSymlinks := config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS")
+ for _, name := range execs {
+ if !paths.GetConfig(name).Symlink && !allowAllSymlinks {
+ continue
+ }
+
+ err := os.Symlink("../.path_interposer", filepath.Join(myPath, name))
+ // Intentionally ignore existing files -- that means that we
+ // just created it, and the first one should win.
+ if err != nil && !os.IsExist(err) {
+ ctx.Fatalln("Failed to create symlink:", err)
+ }
+ }
+
+ myPath, _ = filepath.Abs(myPath)
+
+ // We put some prebuilts in $PATH, since it's infeasible to add dependencies for all of
+ // them.
+ prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
+ myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+
+ config.Environment().Set("PATH", myPath)
+ config.pathReplaced = true
+}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
new file mode 100644
index 0000000..f4bb89f
--- /dev/null
+++ b/ui/build/paths/config.go
@@ -0,0 +1,194 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package paths
+
+import "runtime"
+
+type PathConfig struct {
+ // Whether to create the symlink in the new PATH for this tool.
+ Symlink bool
+
+ // Whether to log about usages of this tool to the soong.log
+ Log bool
+
+ // Whether to exit with an error instead of invoking the underlying tool.
+ Error bool
+
+ // Whether we use a linux-specific prebuilt for this tool. On Darwin,
+ // we'll allow the host executable instead.
+ LinuxOnlyPrebuilt bool
+}
+
+var Allowed = PathConfig{
+ Symlink: true,
+ Log: false,
+ Error: false,
+}
+
+var Forbidden = PathConfig{
+ Symlink: false,
+ Log: true,
+ Error: true,
+}
+
+var Log = PathConfig{
+ Symlink: true,
+ Log: true,
+ Error: false,
+}
+
+// The configuration used if the tool is not listed in the config below.
+// Currently this will create the symlink, but log and error when it's used. In
+// the future, I expect the symlink to be removed, and this will be equivalent
+// to Forbidden.
+var Missing = PathConfig{
+ Symlink: true,
+ Log: true,
+ Error: true,
+}
+
+var LinuxOnlyPrebuilt = PathConfig{
+ Symlink: false,
+ Log: true,
+ Error: true,
+ LinuxOnlyPrebuilt: true,
+}
+
+func GetConfig(name string) PathConfig {
+ if config, ok := Configuration[name]; ok {
+ return config
+ }
+ return Missing
+}
+
+var Configuration = map[string]PathConfig{
+ "bash": Allowed,
+ "bc": Allowed,
+ "bzip2": Allowed,
+ "date": Allowed,
+ "dd": Allowed,
+ "diff": Allowed,
+ "egrep": Allowed,
+ "expr": Allowed,
+ "find": Allowed,
+ "fuser": Allowed,
+ "getopt": Allowed,
+ "git": Allowed,
+ "grep": Allowed,
+ "gzip": Allowed,
+ "hexdump": Allowed,
+ "jar": Allowed,
+ "java": Allowed,
+ "javap": Allowed,
+ "lsof": Allowed,
+ "m4": Allowed,
+ "openssl": Allowed,
+ "patch": Allowed,
+ "pstree": Allowed,
+ "python3": Allowed,
+ "realpath": Allowed,
+ "rsync": Allowed,
+ "sed": Allowed,
+ "sh": Allowed,
+ "tar": Allowed,
+ "timeout": Allowed,
+ "tr": Allowed,
+ "unzip": Allowed,
+ "xz": Allowed,
+ "zip": Allowed,
+ "zipinfo": Allowed,
+
+ // Host toolchain is removed. In-tree toolchain should be used instead.
+ // GCC also can't find cc1 with this implementation.
+ "ar": Forbidden,
+ "as": Forbidden,
+ "cc": Forbidden,
+ "clang": Forbidden,
+ "clang++": Forbidden,
+ "gcc": Forbidden,
+ "g++": Forbidden,
+ "ld": Forbidden,
+ "ld.bfd": Forbidden,
+ "ld.gold": Forbidden,
+ "pkg-config": Forbidden,
+
+ // On Linux we'll use the toybox versions of these instead.
+ "basename": LinuxOnlyPrebuilt,
+ "cat": LinuxOnlyPrebuilt,
+ "chmod": LinuxOnlyPrebuilt,
+ "cmp": LinuxOnlyPrebuilt,
+ "cp": LinuxOnlyPrebuilt,
+ "comm": LinuxOnlyPrebuilt,
+ "cut": LinuxOnlyPrebuilt,
+ "dirname": LinuxOnlyPrebuilt,
+ "du": LinuxOnlyPrebuilt,
+ "echo": LinuxOnlyPrebuilt,
+ "env": LinuxOnlyPrebuilt,
+ "head": LinuxOnlyPrebuilt,
+ "getconf": LinuxOnlyPrebuilt,
+ "hostname": LinuxOnlyPrebuilt,
+ "id": LinuxOnlyPrebuilt,
+ "ln": LinuxOnlyPrebuilt,
+ "ls": LinuxOnlyPrebuilt,
+ "md5sum": LinuxOnlyPrebuilt,
+ "mkdir": LinuxOnlyPrebuilt,
+ "mktemp": LinuxOnlyPrebuilt,
+ "mv": LinuxOnlyPrebuilt,
+ "od": LinuxOnlyPrebuilt,
+ "paste": LinuxOnlyPrebuilt,
+ "pgrep": LinuxOnlyPrebuilt,
+ "pkill": LinuxOnlyPrebuilt,
+ "ps": LinuxOnlyPrebuilt,
+ "pwd": LinuxOnlyPrebuilt,
+ "readlink": LinuxOnlyPrebuilt,
+ "rm": LinuxOnlyPrebuilt,
+ "rmdir": LinuxOnlyPrebuilt,
+ "seq": LinuxOnlyPrebuilt,
+ "setsid": LinuxOnlyPrebuilt,
+ "sha1sum": LinuxOnlyPrebuilt,
+ "sha256sum": LinuxOnlyPrebuilt,
+ "sha512sum": LinuxOnlyPrebuilt,
+ "sleep": LinuxOnlyPrebuilt,
+ "sort": LinuxOnlyPrebuilt,
+ "stat": LinuxOnlyPrebuilt,
+ "tail": LinuxOnlyPrebuilt,
+ "tee": LinuxOnlyPrebuilt,
+ "touch": LinuxOnlyPrebuilt,
+ "true": LinuxOnlyPrebuilt,
+ "uname": LinuxOnlyPrebuilt,
+ "uniq": LinuxOnlyPrebuilt,
+ "unix2dos": LinuxOnlyPrebuilt,
+ "wc": LinuxOnlyPrebuilt,
+ "whoami": LinuxOnlyPrebuilt,
+ "which": LinuxOnlyPrebuilt,
+ "xargs": LinuxOnlyPrebuilt,
+ "xxd": LinuxOnlyPrebuilt,
+}
+
+func init() {
+ if runtime.GOOS == "darwin" {
+ Configuration["md5"] = Allowed
+ Configuration["sw_vers"] = Allowed
+ Configuration["xcrun"] = Allowed
+
+ // We don't have darwin prebuilts for some tools (like toybox),
+ // so allow the host versions.
+ for name, config := range Configuration {
+ if config.LinuxOnlyPrebuilt {
+ Configuration[name] = Allowed
+ }
+ }
+ }
+}
diff --git a/ui/build/paths/logs.go b/ui/build/paths/logs.go
new file mode 100644
index 0000000..6c24968
--- /dev/null
+++ b/ui/build/paths/logs.go
@@ -0,0 +1,211 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package paths
+
+import (
+ "context"
+ "encoding/gob"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+ "runtime"
+ "sync"
+ "syscall"
+ "time"
+)
+
+type LogProcess struct {
+ Pid int
+ Command string
+}
+
+type LogEntry struct {
+ Basename string
+ Args []string
+ Parents []LogProcess
+}
+
+const timeoutDuration = time.Duration(100) * time.Millisecond
+
+type socketAddrFunc func(string) (string, func(), error)
+
+func procFallback(name string) (string, func(), error) {
+ d, err := os.Open(filepath.Dir(name))
+ if err != nil {
+ return "", func() {}, err
+ }
+
+ return fmt.Sprintf("/proc/self/fd/%d/%s", d.Fd(), filepath.Base(name)), func() {
+ d.Close()
+ }, nil
+}
+
+func tmpFallback(name string) (addr string, cleanup func(), err error) {
+ d, err := ioutil.TempDir("/tmp", "log_sock")
+ if err != nil {
+ cleanup = func() {}
+ return
+ }
+ cleanup = func() {
+ os.RemoveAll(d)
+ }
+
+ dir := filepath.Dir(name)
+
+ absDir, err := filepath.Abs(dir)
+ if err != nil {
+ return
+ }
+
+ err = os.Symlink(absDir, filepath.Join(d, "d"))
+ if err != nil {
+ return
+ }
+
+ addr = filepath.Join(d, "d", filepath.Base(name))
+
+ return
+}
+
+func getSocketAddr(name string) (string, func(), error) {
+ maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
+
+ if len(name) < maxNameLen {
+ return name, func() {}, nil
+ }
+
+ if runtime.GOOS == "linux" {
+ addr, cleanup, err := procFallback(name)
+ if err == nil {
+ if len(addr) < maxNameLen {
+ return addr, cleanup, nil
+ }
+ }
+ cleanup()
+ }
+
+ addr, cleanup, err := tmpFallback(name)
+ if err == nil {
+ if len(addr) < maxNameLen {
+ return addr, cleanup, nil
+ }
+ }
+ cleanup()
+
+ return name, func() {}, fmt.Errorf("Path to socket is still over size limit, fallbacks failed.")
+}
+
+func dial(name string, lookup socketAddrFunc, timeout time.Duration) (net.Conn, error) {
+ socket, cleanup, err := lookup(name)
+ defer cleanup()
+ if err != nil {
+ return nil, err
+ }
+
+ dialer := &net.Dialer{
+ Timeout: timeout,
+ }
+ return dialer.Dial("unix", socket)
+}
+
+func listen(name string, lookup socketAddrFunc) (net.Listener, error) {
+ socket, cleanup, err := lookup(name)
+ defer cleanup()
+ if err != nil {
+ return nil, err
+ }
+
+ return net.Listen("unix", socket)
+}
+
+func SendLog(logSocket string, entry *LogEntry, done chan interface{}) {
+ sendLog(logSocket, getSocketAddr, timeoutDuration, entry, done)
+}
+
+func sendLog(logSocket string, lookup socketAddrFunc, timeout time.Duration, entry *LogEntry, done chan interface{}) {
+ defer close(done)
+
+ conn, err := dial(logSocket, lookup, timeout)
+ if err != nil {
+ return
+ }
+ defer conn.Close()
+
+ if timeout != 0 {
+ conn.SetDeadline(time.Now().Add(timeout))
+ }
+
+ enc := gob.NewEncoder(conn)
+ enc.Encode(entry)
+}
+
+func LogListener(ctx context.Context, logSocket string) (chan *LogEntry, error) {
+ return logListener(ctx, logSocket, getSocketAddr)
+}
+
+func logListener(ctx context.Context, logSocket string, lookup socketAddrFunc) (chan *LogEntry, error) {
+ ret := make(chan *LogEntry, 5)
+
+ if err := os.Remove(logSocket); err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+
+ ln, err := listen(logSocket, lookup)
+ if err != nil {
+ return nil, err
+ }
+
+ go func() {
+ for {
+ select {
+ case <-ctx.Done():
+ ln.Close()
+ }
+ }
+ }()
+
+ go func() {
+ var wg sync.WaitGroup
+ defer func() {
+ wg.Wait()
+ close(ret)
+ }()
+
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ ln.Close()
+ break
+ }
+ conn.SetDeadline(time.Now().Add(timeoutDuration))
+ wg.Add(1)
+
+ go func() {
+ defer wg.Done()
+ defer conn.Close()
+
+ dec := gob.NewDecoder(conn)
+ entry := &LogEntry{}
+ if err := dec.Decode(entry); err != nil {
+ return
+ }
+ ret <- entry
+ }()
+ }
+ }()
+ return ret, nil
+}
diff --git a/ui/build/paths/logs_test.go b/ui/build/paths/logs_test.go
new file mode 100644
index 0000000..3b1005f
--- /dev/null
+++ b/ui/build/paths/logs_test.go
@@ -0,0 +1,154 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package paths
+
+import (
+ "context"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestSendLog(t *testing.T) {
+ t.Run("Short name", func(t *testing.T) {
+ d, err := ioutil.TempDir("", "s")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(d)
+ f := filepath.Join(d, "s")
+
+ testSendLog(t, f, getSocketAddr)
+ })
+
+ testLongName := func(t *testing.T, lookup socketAddrFunc) {
+ d, err := ioutil.TempDir("", strings.Repeat("s", 150))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(d)
+ f := filepath.Join(d, strings.Repeat("s", 10))
+
+ testSendLog(t, f, lookup)
+ }
+
+ // Using a name longer than the ~100 limit of the underlying calls to bind, etc
+ t.Run("Long name", func(t *testing.T) {
+ testLongName(t, getSocketAddr)
+ })
+
+ if runtime.GOOS == "linux" {
+ t.Run("Long name proc fallback", func(t *testing.T) {
+ testLongName(t, procFallback)
+ })
+ }
+
+ t.Run("Long name tmp fallback", func(t *testing.T) {
+ testLongName(t, tmpFallback)
+ })
+}
+
+func testSendLog(t *testing.T, socket string, lookup socketAddrFunc) {
+ recv, err := logListener(context.Background(), socket, lookup)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ go func() {
+ for i := 0; i < 10; i++ {
+ sendLog(socket, lookup, 0, &LogEntry{
+ Basename: "test",
+ Args: []string{"foo", "bar"},
+ }, make(chan interface{}))
+ }
+ }()
+
+ count := 0
+ for {
+ entry := <-recv
+ if entry == nil {
+ if count != 10 {
+ t.Errorf("Expected 10 logs, got %d", count)
+ }
+ return
+ }
+
+ ref := LogEntry{
+ Basename: "test",
+ Args: []string{"foo", "bar"},
+ }
+ if !reflect.DeepEqual(ref, *entry) {
+ t.Fatalf("Bad log entry: %v", entry)
+ }
+ count++
+
+ if count == 10 {
+ return
+ }
+ }
+}
+
+func TestSendLogError(t *testing.T) {
+ d, err := ioutil.TempDir("", "log_socket")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(d)
+
+ // Missing log sockets should not block waiting for the timeout to elapse
+ t.Run("Missing file", func(t *testing.T) {
+ sendLog(filepath.Join(d, "missing"), getSocketAddr, 0, &LogEntry{}, make(chan interface{}))
+ })
+
+ // Non-sockets should not block waiting for the timeout to elapse
+ t.Run("Regular file", func(t *testing.T) {
+ f := filepath.Join(d, "file")
+ if fp, err := os.Create(f); err == nil {
+ fp.Close()
+ } else {
+ t.Fatal(err)
+ }
+
+ sendLog(f, getSocketAddr, 0, &LogEntry{}, make(chan interface{}))
+ })
+
+ // If the reader is stuck, we should be able to make progress
+ t.Run("Reader not reading", func(t *testing.T) {
+ f := filepath.Join(d, "sock1")
+
+ ln, err := listen(f, getSocketAddr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ done := make(chan bool, 1)
+ go func() {
+ for i := 0; i < 10; i++ {
+ sendLog(f, getSocketAddr, timeoutDuration, &LogEntry{
+ // Ensure a relatively large payload
+ Basename: strings.Repeat(" ", 100000),
+ }, make(chan interface{}))
+ }
+ done <- true
+ }()
+
+ <-done
+ })
+}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
new file mode 100644
index 0000000..6a26063
--- /dev/null
+++ b/ui/build/rbe.go
@@ -0,0 +1,130 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+ "fmt"
+ "math/rand"
+ "os"
+ "path/filepath"
+ "time"
+
+ "android/soong/ui/metrics"
+)
+
+const (
+ rbeLeastNProcs = 2500
+ rbeLeastNFiles = 16000
+
+ // prebuilt RBE binaries
+ bootstrapCmd = "bootstrap"
+
+ // RBE metrics proto buffer file
+ rbeMetricsPBFilename = "rbe_metrics.pb"
+)
+
+func rbeCommand(ctx Context, config Config, rbeCmd string) string {
+ var cmdPath string
+ if rbeDir := config.rbeDir(); rbeDir != "" {
+ cmdPath = filepath.Join(rbeDir, rbeCmd)
+ } else {
+ ctx.Fatalf("rbe command path not found")
+ }
+
+ if _, err := os.Stat(cmdPath); err != nil && os.IsNotExist(err) {
+ ctx.Fatalf("rbe command %q not found", rbeCmd)
+ }
+
+ return cmdPath
+}
+
+func getRBEVars(ctx Context, config Config) map[string]string {
+ rand.Seed(time.Now().UnixNano())
+ vars := map[string]string{
+ "RBE_log_path": config.rbeLogPath(),
+ "RBE_log_dir": config.logDir(),
+ "RBE_re_proxy": config.rbeReproxy(),
+ "RBE_exec_root": config.rbeExecRoot(),
+ "RBE_output_dir": config.rbeStatsOutputDir(),
+ }
+ if config.StartRBE() {
+ vars["RBE_server_address"] = fmt.Sprintf("unix://%v/reproxy_%v.sock", absPath(ctx, config.TempDir()), rand.Intn(1000))
+ }
+ k, v := config.rbeAuth()
+ vars[k] = v
+ return vars
+}
+
+func startRBE(ctx Context, config Config) {
+ ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
+ defer ctx.EndTrace()
+
+ if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
+ ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
+ }
+ if n := ulimitOrFatal(ctx, config, "-n"); n < rbeLeastNFiles {
+ ctx.Fatalf("max open files is insufficient: %d; want >= %d.\n", n, rbeLeastNFiles)
+ }
+
+ cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))
+
+ if output, err := cmd.CombinedOutput(); err != nil {
+ ctx.Fatalf("rbe bootstrap failed with: %v\n%s\n", err, output)
+ }
+}
+
+func stopRBE(ctx Context, config Config) {
+ cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
+ if output, err := cmd.CombinedOutput(); err != nil {
+ ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
+ }
+}
+
+// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
+// The protobuf file is created if RBE is enabled and the proxy service has
+// started. The proxy service is shutdown in order to dump the RBE metrics to the
+// protobuf file.
+func DumpRBEMetrics(ctx Context, config Config, filename string) {
+ ctx.BeginTrace(metrics.RunShutdownTool, "dump_rbe_metrics")
+ defer ctx.EndTrace()
+
+ // Remove the previous metrics file in case there is a failure or RBE has been
+ // disable for this run.
+ os.Remove(filename)
+
+ // If RBE is not enabled then there are no metrics to generate.
+ // If RBE does not require to start, the RBE proxy maybe started
+ // manually for debugging purpose and can generate the metrics
+ // afterwards.
+ if !config.StartRBE() {
+ return
+ }
+
+ outputDir := config.rbeStatsOutputDir()
+ if outputDir == "" {
+ ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
+ }
+ metricsFile := filepath.Join(outputDir, rbeMetricsPBFilename)
+
+ // Stop the proxy first in order to generate the RBE metrics protobuf file.
+ stopRBE(ctx, config)
+
+ if metricsFile == filename {
+ return
+ }
+ if _, err := copyFile(metricsFile, filename); err != nil {
+ ctx.Fatalf("failed to copy %q to %q: %v\n", metricsFile, filename, err)
+ }
+}
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
new file mode 100644
index 0000000..d3a52b7
--- /dev/null
+++ b/ui/build/rbe_test.go
@@ -0,0 +1,135 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "android/soong/ui/logger"
+)
+
+func TestDumpRBEMetrics(t *testing.T) {
+ ctx := testContext()
+ tests := []struct {
+ description string
+ env []string
+ generated bool
+ }{{
+ description: "RBE disabled",
+ env: []string{
+ "NOSTART_RBE=true",
+ },
+ }, {
+ description: "rbe metrics generated",
+ env: []string{
+ "USE_RBE=true",
+ },
+ generated: true,
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "tmpdir")
+ if err != nil {
+ t.Fatalf("failed to make temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
+ if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(rbeBootstrapProgram), 0755); err != nil {
+ t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
+ }
+
+ env := Environment(tt.env)
+ env.Set("OUT_DIR", tmpDir)
+ env.Set("RBE_DIR", tmpDir)
+ env.Set("RBE_output_dir", tmpDir)
+ config := Config{&configImpl{
+ environ: &env,
+ }}
+
+ rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
+ DumpRBEMetrics(ctx, config, rbeMetricsFilename)
+
+ // Validate that the rbe metrics file exists if RBE is enabled.
+ if _, err := os.Stat(rbeMetricsFilename); err == nil {
+ if !tt.generated {
+ t.Errorf("got true, want false for rbe metrics file %s to exist.", rbeMetricsFilename)
+ }
+ } else if os.IsNotExist(err) {
+ if tt.generated {
+ t.Errorf("got false, want true for rbe metrics file %s to exist.", rbeMetricsFilename)
+ }
+ } else {
+ t.Errorf("unknown error found on checking %s exists: %v", rbeMetricsFilename, err)
+ }
+ })
+ }
+}
+
+func TestDumpRBEMetricsErrors(t *testing.T) {
+ ctx := testContext()
+ tests := []struct {
+ description string
+ bootstrapProgram string
+ expectedErr string
+ }{{
+ description: "stopRBE failed",
+ bootstrapProgram: "#!/bin/bash\nexit 1\n",
+ expectedErr: "shutdown failed",
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ got := err.Error()
+ if !strings.Contains(got, tt.expectedErr) {
+ t.Errorf("got %q, want %q to be contained in error", got, tt.expectedErr)
+ }
+ })
+
+ tmpDir, err := ioutil.TempDir("", "tmpdir")
+ if err != nil {
+ t.Fatalf("failed to make temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ rbeBootstrapCmd := filepath.Join(tmpDir, bootstrapCmd)
+ if err := ioutil.WriteFile(rbeBootstrapCmd, []byte(tt.bootstrapProgram), 0755); err != nil {
+ t.Fatalf("failed to create a fake bootstrap command file %s: %v", rbeBootstrapCmd, err)
+ }
+
+ env := &Environment{}
+ env.Set("USE_RBE", "true")
+ env.Set("OUT_DIR", tmpDir)
+ env.Set("RBE_DIR", tmpDir)
+
+ config := Config{&configImpl{
+ environ: env,
+ }}
+
+ rbeMetricsFilename := filepath.Join(tmpDir, rbeMetricsPBFilename)
+ DumpRBEMetrics(ctx, config, rbeMetricsFilename)
+ t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
+ })
+ }
+}
+
+var rbeBootstrapProgram = fmt.Sprintf("#!/bin/bash\necho 1 > $RBE_output_dir/%s\n", rbeMetricsPBFilename)
diff --git a/ui/build/sandbox/darwin/global.sb b/ui/build/sandbox/darwin/global.sb
index 47d0c43..e32b64b 100644
--- a/ui/build/sandbox/darwin/global.sb
+++ b/ui/build/sandbox/darwin/global.sb
@@ -35,6 +35,12 @@
(global-name-regex #"^com\.apple\.distributed_notifications") ; xcodebuild in Soong
)
+; Allow suid /bin/ps to function
+(allow process-exec (literal "/bin/ps") (with no-sandbox))
+
+; Allow path_interposer unix domain socket without logging
+(allow network-outbound (literal (string-append (param "OUT_DIR") "/.path_interposer_log")))
+
; Allow executing any file
(allow process-exec*)
(allow process-fork)
diff --git a/ui/build/sandbox_darwin.go b/ui/build/sandbox_darwin.go
index 7e75167..43c5480 100644
--- a/ui/build/sandbox_darwin.go
+++ b/ui/build/sandbox_darwin.go
@@ -21,12 +21,12 @@
type Sandbox string
const (
- noSandbox = ""
- globalSandbox = "build/soong/ui/build/sandbox/darwin/global.sb"
- dumpvarsSandbox = globalSandbox
- soongSandbox = globalSandbox
- katiSandbox = globalSandbox
- katiCleanSpecSandbox = globalSandbox
+ noSandbox = ""
+ globalSandbox = "build/soong/ui/build/sandbox/darwin/global.sb"
+ dumpvarsSandbox = globalSandbox
+ soongSandbox = globalSandbox
+ katiSandbox = globalSandbox
+ ninjaSandbox = noSandbox
)
var sandboxExecPath string
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index f2bfac2..b94db74 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -14,20 +14,165 @@
package build
-type Sandbox bool
-
-const (
- noSandbox = false
- globalSandbox = false
- dumpvarsSandbox = false
- soongSandbox = false
- katiSandbox = false
- katiCleanSpecSandbox = false
+import (
+ "bytes"
+ "os"
+ "os/exec"
+ "os/user"
+ "strings"
+ "sync"
)
+type Sandbox struct {
+ Enabled bool
+ DisableWhenUsingGoma bool
+
+ AllowBuildBrokenUsesNetwork bool
+}
+
+var (
+ noSandbox = Sandbox{}
+ basicSandbox = Sandbox{
+ Enabled: true,
+ }
+
+ dumpvarsSandbox = basicSandbox
+ katiSandbox = basicSandbox
+ soongSandbox = basicSandbox
+ ninjaSandbox = Sandbox{
+ Enabled: true,
+ DisableWhenUsingGoma: true,
+
+ AllowBuildBrokenUsesNetwork: true,
+ }
+)
+
+const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
+
+var sandboxConfig struct {
+ once sync.Once
+
+ working bool
+ group string
+}
+
func (c *Cmd) sandboxSupported() bool {
- return false
+ if !c.Sandbox.Enabled {
+ return false
+ }
+
+ // Goma is incompatible with PID namespaces and Mount namespaces. b/122767582
+ if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() {
+ return false
+ }
+
+ sandboxConfig.once.Do(func() {
+ sandboxConfig.group = "nogroup"
+ if _, err := user.LookupGroup(sandboxConfig.group); err != nil {
+ sandboxConfig.group = "nobody"
+ }
+
+ cmd := exec.CommandContext(c.ctx.Context, nsjailPath,
+ "-H", "android-build",
+ "-e",
+ "-u", "nobody",
+ "-g", sandboxConfig.group,
+ "-B", "/",
+ "--disable_clone_newcgroup",
+ "--",
+ "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
+ cmd.Env = c.config.Environment().Environ()
+
+ c.ctx.Verboseln(cmd.Args)
+ data, err := cmd.CombinedOutput()
+ if err == nil && bytes.Contains(data, []byte("Android Success")) {
+ sandboxConfig.working = true
+ return
+ }
+
+ c.ctx.Println("Build sandboxing disabled due to nsjail error. This may become fatal in the future.")
+ c.ctx.Println("Please let us know why nsjail doesn't work in your environment at:")
+ c.ctx.Println(" https://groups.google.com/forum/#!forum/android-building")
+ c.ctx.Println(" https://issuetracker.google.com/issues/new?component=381517")
+
+ for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
+ c.ctx.Verboseln(line)
+ }
+
+ if err == nil {
+ c.ctx.Verboseln("nsjail exited successfully, but without the correct output")
+ } else if e, ok := err.(*exec.ExitError); ok {
+ c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String())
+ } else {
+ c.ctx.Verbosef("nsjail failed with %v", err)
+ }
+ })
+
+ return sandboxConfig.working
}
func (c *Cmd) wrapSandbox() {
+ wd, _ := os.Getwd()
+
+ sandboxArgs := []string{
+ // The executable to run
+ "-x", c.Path,
+
+ // Set the hostname to something consistent
+ "-H", "android-build",
+
+ // Use the current working dir
+ "--cwd", wd,
+
+ // No time limit
+ "-t", "0",
+
+ // Keep all environment variables, we already filter them out
+ // in soong_ui
+ "-e",
+
+ // Mount /proc read-write, necessary to run a nested nsjail or minijail0
+ "--proc_rw",
+
+ // Use a consistent user & group.
+ // Note that these are mapped back to the real UID/GID when
+ // doing filesystem operations, so they're rather arbitrary.
+ "-u", "nobody",
+ "-g", sandboxConfig.group,
+
+ // Set high values, as nsjail uses low defaults.
+ "--rlimit_as", "soft",
+ "--rlimit_core", "soft",
+ "--rlimit_cpu", "soft",
+ "--rlimit_fsize", "soft",
+ "--rlimit_nofile", "soft",
+
+ // For now, just map everything. Eventually we should limit this, especially to make most things readonly.
+ "-B", "/",
+
+ // Disable newcgroup for now, since it may require newer kernels
+ // TODO: try out cgroups
+ "--disable_clone_newcgroup",
+
+ // Only log important warnings / errors
+ "-q",
+ }
+
+ if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
+ c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
+ c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
+ sandboxArgs = append(sandboxArgs, "-N")
+ }
+
+ // Stop nsjail from parsing arguments
+ sandboxArgs = append(sandboxArgs, "--")
+
+ c.Args = append(sandboxArgs, c.Args[1:]...)
+ c.Path = nsjailPath
+
+ env := Environment(c.Env)
+ if _, hasUser := env.Get("USER"); hasUser {
+ env.Set("USER", "nobody")
+ }
+ c.Env = []string(env)
}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index cbb75c7..2ce1ac9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -18,17 +18,20 @@
"os"
"path/filepath"
"strconv"
- "time"
+ "strings"
"github.com/google/blueprint/microfactory"
+
+ "android/soong/ui/metrics"
+ "android/soong/ui/status"
)
func runSoong(ctx Context, config Config) {
- ctx.BeginTrace("soong")
+ ctx.BeginTrace(metrics.RunSoong, "soong")
defer ctx.EndTrace()
func() {
- ctx.BeginTrace("blueprint bootstrap")
+ ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
defer ctx.EndTrace()
cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", "-t")
@@ -41,13 +44,12 @@
cmd.Environment.Set("SRCDIR", ".")
cmd.Environment.Set("TOPNAME", "Android.bp")
cmd.Sandbox = soongSandbox
- cmd.Stdout = ctx.Stdout()
- cmd.Stderr = ctx.Stderr()
- cmd.RunOrFatal()
+
+ cmd.RunAndPrintOrFatal()
}()
func() {
- ctx.BeginTrace("environment check")
+ ctx.BeginTrace(metrics.RunSoong, "environment check")
defer ctx.EndTrace()
envFile := filepath.Join(config.SoongOutDir(), ".soong.environment")
@@ -56,12 +58,18 @@
if _, err := os.Stat(envTool); err == nil {
cmd := Command(ctx, config, "soong_env", envTool, envFile)
cmd.Sandbox = soongSandbox
- cmd.Stdout = ctx.Stdout()
- cmd.Stderr = ctx.Stderr()
+
+ var buf strings.Builder
+ cmd.Stdout = &buf
+ cmd.Stderr = &buf
if err := cmd.Run(); err != nil {
ctx.Verboseln("soong_env failed, forcing manifest regeneration")
os.Remove(envFile)
}
+
+ if buf.Len() > 0 {
+ ctx.Verboseln(buf.String())
+ }
} else {
ctx.Verboseln("Missing soong_env tool, forcing manifest regeneration")
os.Remove(envFile)
@@ -71,41 +79,48 @@
}
}()
+ var cfg microfactory.Config
+ cfg.Map("github.com/google/blueprint", "build/blueprint")
+
+ cfg.TrimPath = absPath(ctx, ".")
+
func() {
- ctx.BeginTrace("minibp")
+ ctx.BeginTrace(metrics.RunSoong, "minibp")
defer ctx.EndTrace()
- var cfg microfactory.Config
- cfg.Map("github.com/google/blueprint", "build/blueprint")
-
- cfg.TrimPath = absPath(ctx, ".")
-
minibp := filepath.Join(config.SoongOutDir(), ".minibootstrap/minibp")
if _, err := microfactory.Build(&cfg, minibp, "github.com/google/blueprint/bootstrap/minibp"); err != nil {
ctx.Fatalln("Failed to build minibp:", err)
}
}()
- ninja := func(name, file string) {
- ctx.BeginTrace(name)
+ func() {
+ ctx.BeginTrace(metrics.RunSoong, "bpglob")
defer ctx.EndTrace()
+ bpglob := filepath.Join(config.SoongOutDir(), ".minibootstrap/bpglob")
+ if _, err := microfactory.Build(&cfg, bpglob, "github.com/google/blueprint/bootstrap/bpglob"); err != nil {
+ ctx.Fatalln("Failed to build bpglob:", err)
+ }
+ }()
+
+ ninja := func(name, file string) {
+ ctx.BeginTrace(metrics.RunSoong, name)
+ defer ctx.EndTrace()
+
+ fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
+ nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
+ defer nr.Close()
+
cmd := Command(ctx, config, "soong "+name,
config.PrebuiltBuildTool("ninja"),
"-d", "keepdepfile",
"-w", "dupbuild=err",
"-j", strconv.Itoa(config.Parallel()),
+ "--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), file))
- if config.IsVerbose() {
- cmd.Args = append(cmd.Args, "-v")
- }
cmd.Sandbox = soongSandbox
- cmd.Stdin = ctx.Stdin()
- cmd.Stdout = ctx.Stdout()
- cmd.Stderr = ctx.Stderr()
-
- defer ctx.ImportNinjaLog(filepath.Join(config.OutDir(), ".ninja_log"), time.Now())
- cmd.RunOrFatal()
+ cmd.RunAndPrintOrFatal()
}
ninja("minibootstrap", ".minibootstrap/build.ninja")
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 940f0c8..5109465 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -16,9 +16,14 @@
import (
"bufio"
+ "fmt"
"path/filepath"
"runtime"
+ "sort"
"strings"
+
+ "android/soong/ui/metrics"
+ "android/soong/ui/status"
)
// Checks for files in the out directory that have a rule that depends on them but no rule to
@@ -33,9 +38,15 @@
return
}
- ctx.BeginTrace("test for dangling rules")
+ ctx.BeginTrace(metrics.TestRun, "test for dangling rules")
defer ctx.EndTrace()
+ ts := ctx.Status.StartTool()
+ action := &status.Action{
+ Description: "Test for dangling rules",
+ }
+ ts.StartAction(action)
+
// Get a list of leaf nodes in the dependency graph from ninja
executable := config.PrebuiltBuildTool("ninja")
@@ -56,7 +67,7 @@
bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
- var danglingRules []string
+ danglingRules := make(map[string]bool)
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
@@ -70,16 +81,30 @@
// full build rules in the primary build.ninja file.
continue
}
- danglingRules = append(danglingRules, line)
+ danglingRules[line] = true
}
cmd.WaitOrFatal()
- if len(danglingRules) > 0 {
- ctx.Println("Dependencies in out found with no rule to create them:")
- for _, dep := range danglingRules {
- ctx.Println(dep)
- }
- ctx.Fatal("")
+ var danglingRulesList []string
+ for rule := range danglingRules {
+ danglingRulesList = append(danglingRulesList, rule)
}
+ sort.Strings(danglingRulesList)
+
+ if len(danglingRulesList) > 0 {
+ sb := &strings.Builder{}
+ title := "Dependencies in out found with no rule to create them:"
+ fmt.Fprintln(sb, title)
+ for _, dep := range danglingRulesList {
+ fmt.Fprintln(sb, " ", dep)
+ }
+ ts.FinishAction(status.ActionResult{
+ Action: action,
+ Error: fmt.Errorf(title),
+ Output: sb.String(),
+ })
+ ctx.Fatal("stopping")
+ }
+ ts.FinishAction(status.ActionResult{Action: action})
}
diff --git a/ui/build/upload.go b/ui/build/upload.go
new file mode 100644
index 0000000..d7c57b8
--- /dev/null
+++ b/ui/build/upload.go
@@ -0,0 +1,113 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+// This file contains the functionality to upload data from one location to
+// another.
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "android/soong/ui/metrics"
+ "github.com/golang/protobuf/proto"
+
+ upload_proto "android/soong/ui/metrics/upload_proto"
+)
+
+const (
+ uploadPbFilename = ".uploader.pb"
+)
+
+var (
+ // For testing purpose
+ getTmpDir = ioutil.TempDir
+)
+
+// UploadMetrics uploads a set of metrics files to a server for analysis. An
+// uploader full path is required to be specified in order to upload the set
+// of metrics files. This is accomplished by defining the ANDROID_ENABLE_METRICS_UPLOAD
+// environment variable. The metrics files are copied to a temporary directory
+// and the uploader is then executed in the background to allow the user to continue
+// working.
+func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStarted time.Time, files ...string) {
+ ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
+ defer ctx.EndTrace()
+
+ uploader := config.MetricsUploaderApp()
+ // No metrics to upload if the path to the uploader was not specified.
+ if uploader == "" {
+ return
+ }
+
+ // Some files may not exist. For example, build errors protobuf file
+ // may not exist since the build was successful.
+ var metricsFiles []string
+ for _, f := range files {
+ if _, err := os.Stat(f); err == nil {
+ metricsFiles = append(metricsFiles, f)
+ }
+ }
+
+ if len(metricsFiles) == 0 {
+ return
+ }
+
+ // The temporary directory cannot be deleted as the metrics uploader is started
+ // in the background and requires to exist until the operation is done. The
+ // uploader can delete the directory as it is specified in the upload proto.
+ tmpDir, err := getTmpDir("", "upload_metrics")
+ if err != nil {
+ ctx.Fatalf("failed to create a temporary directory to store the list of metrics files: %v\n", err)
+ }
+
+ for i, src := range metricsFiles {
+ dst := filepath.Join(tmpDir, filepath.Base(src))
+ if _, err := copyFile(src, dst); err != nil {
+ ctx.Fatalf("failed to copy %q to %q: %v\n", src, dst, err)
+ }
+ metricsFiles[i] = dst
+ }
+
+ // For platform builds, the branch and target name is hardcoded to specific
+ // values for later extraction of the metrics in the data metrics pipeline.
+ data, err := proto.Marshal(&upload_proto.Upload{
+ CreationTimestampMs: proto.Uint64(uint64(buildStarted.UnixNano() / int64(time.Millisecond))),
+ CompletionTimestampMs: proto.Uint64(uint64(time.Now().UnixNano() / int64(time.Millisecond))),
+ BranchName: proto.String("developer-metrics"),
+ TargetName: proto.String("platform-build-systems-metrics"),
+ MetricsFiles: metricsFiles,
+ DirectoriesToDelete: []string{tmpDir},
+ })
+ if err != nil {
+ ctx.Fatalf("failed to marshal metrics upload proto buffer message: %v\n", err)
+ }
+
+ pbFile := filepath.Join(tmpDir, uploadPbFilename)
+ if err := ioutil.WriteFile(pbFile, data, 0644); err != nil {
+ ctx.Fatalf("failed to write the marshaled metrics upload protobuf to %q: %v\n", pbFile, err)
+ }
+
+ // Start the uploader in the background as it takes several milliseconds to start the uploader
+ // and prepare the metrics for upload. This affects small commands like "lunch".
+ cmd := Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile)
+ if forceDumbOutput {
+ cmd.RunOrFatal()
+ } else {
+ cmd.RunAndPrintOrFatal()
+ }
+}
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
new file mode 100644
index 0000000..eb2dafa
--- /dev/null
+++ b/ui/build/upload_test.go
@@ -0,0 +1,156 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+ "errors"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+
+ "android/soong/ui/logger"
+)
+
+func TestUploadMetrics(t *testing.T) {
+ ctx := testContext()
+ tests := []struct {
+ description string
+ uploader string
+ createFiles bool
+ files []string
+ }{{
+ description: "ANDROID_ENABLE_METRICS_UPLOAD not set",
+ }, {
+ description: "no metrics files to upload",
+ uploader: "fake",
+ }, {
+ description: "non-existent metrics files no upload",
+ uploader: "fake",
+ files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+ }, {
+ description: "trigger upload",
+ uploader: "echo",
+ createFiles: true,
+ files: []string{"metrics_file_1", "metrics_file_2"},
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ t.Fatalf("got unexpected error: %v", err)
+ })
+
+ outDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("failed to create out directory: %v", outDir)
+ }
+ defer os.RemoveAll(outDir)
+
+ // Supply our own getTmpDir to delete the temp dir once the test is done.
+ orgGetTmpDir := getTmpDir
+ getTmpDir = func(string, string) (string, error) {
+ retDir := filepath.Join(outDir, "tmp_upload_dir")
+ if err := os.Mkdir(retDir, 0755); err != nil {
+ t.Fatalf("failed to create temporary directory %q: %v", retDir, err)
+ }
+ return retDir, nil
+ }
+ defer func() { getTmpDir = orgGetTmpDir }()
+
+ metricsUploadDir := filepath.Join(outDir, ".metrics_uploader")
+ if err := os.Mkdir(metricsUploadDir, 0755); err != nil {
+ t.Fatalf("failed to create %q directory for oauth valid check: %v", metricsUploadDir, err)
+ }
+
+ var metricsFiles []string
+ if tt.createFiles {
+ for _, f := range tt.files {
+ filename := filepath.Join(outDir, f)
+ metricsFiles = append(metricsFiles, filename)
+ if err := ioutil.WriteFile(filename, []byte("test file"), 0644); err != nil {
+ t.Fatalf("failed to create a fake metrics file %q for uploading: %v", filename, err)
+ }
+ }
+ }
+
+ config := Config{&configImpl{
+ environ: &Environment{
+ "OUT_DIR=" + outDir,
+ "ANDROID_ENABLE_METRICS_UPLOAD=" + tt.uploader,
+ },
+ }}
+
+ UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
+ })
+ }
+}
+
+func TestUploadMetricsErrors(t *testing.T) {
+ ctx := testContext()
+ tests := []struct {
+ description string
+ tmpDir string
+ tmpDirErr error
+ expectedErr string
+ }{{
+ description: "getTmpDir returned error",
+ tmpDirErr: errors.New("getTmpDir failed"),
+ expectedErr: "getTmpDir failed",
+ }, {
+ description: "copyFile operation error",
+ tmpDir: "/fake_dir",
+ expectedErr: "failed to copy",
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ got := err.Error()
+ if !strings.Contains(got, tt.expectedErr) {
+ t.Errorf("got %q, want %q to be contained in error", got, tt.expectedErr)
+ }
+ })
+
+ outDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("failed to create out directory: %v", outDir)
+ }
+ defer os.RemoveAll(outDir)
+
+ orgGetTmpDir := getTmpDir
+ getTmpDir = func(string, string) (string, error) {
+ return tt.tmpDir, tt.tmpDirErr
+ }
+ defer func() { getTmpDir = orgGetTmpDir }()
+
+ metricsFile := filepath.Join(outDir, "metrics_file_1")
+ if err := ioutil.WriteFile(metricsFile, []byte("test file"), 0644); err != nil {
+ t.Fatalf("failed to create a fake metrics file %q for uploading: %v", metricsFile, err)
+ }
+
+ config := Config{&configImpl{
+ environ: &Environment{
+ "ANDROID_ENABLE_METRICS_UPLOAD=fake",
+ "OUT_DIR=/bad",
+ }}}
+
+ UploadMetrics(ctx, config, true, time.Now(), metricsFile)
+ t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
+ })
+ }
+}
diff --git a/ui/build/util.go b/ui/build/util.go
index f698ccd..14ab6ef 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -15,13 +15,10 @@
package build
import (
- "bytes"
"io"
"os"
"path/filepath"
"strings"
- "syscall"
- "unsafe"
)
func absPath(ctx Context, p string) string {
@@ -62,9 +59,24 @@
func ensureEmptyDirectoriesExist(ctx Context, dirs ...string) {
// remove all the directories
for _, dir := range dirs {
- err := os.RemoveAll(dir)
- if err != nil {
- ctx.Fatalf("Error removing %s: %q\n", dir, err)
+ seenErr := map[string]bool{}
+ for {
+ err := os.RemoveAll(dir)
+ if err == nil {
+ break
+ }
+
+ if pathErr, ok := err.(*os.PathError); !ok ||
+ dir == pathErr.Path || seenErr[pathErr.Path] {
+
+ ctx.Fatalf("Error removing %s: %q\n", dir, err)
+ } else {
+ seenErr[pathErr.Path] = true
+ err = os.Chmod(filepath.Dir(pathErr.Path), 0700)
+ if err != nil {
+ ctx.Fatal(err)
+ }
+ }
}
}
// recreate all the directories
@@ -103,80 +115,19 @@
return str[:idx], str[idx+1:], true
}
-func isTerminal(w io.Writer) bool {
- if f, ok := w.(*os.File); ok {
- var termios syscall.Termios
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
- ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
- 0, 0, 0)
- return err == 0
+// copyFile copies a file from src to dst. filepath.Dir(dst) must exist.
+func copyFile(src, dst string) (int64, error) {
+ source, err := os.Open(src)
+ if err != nil {
+ return 0, err
}
- return false
-}
+ defer source.Close()
-func termWidth(w io.Writer) (int, bool) {
- if f, ok := w.(*os.File); ok {
- var winsize struct {
- ws_row, ws_column uint16
- ws_xpixel, ws_ypixel uint16
- }
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
- syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
- 0, 0, 0)
- return int(winsize.ws_column), err == 0
+ destination, err := os.Create(dst)
+ if err != nil {
+ return 0, err
}
- return 0, false
-}
+ defer destination.Close()
-// stripAnsiEscapes strips ANSI control codes from a byte array in place.
-func stripAnsiEscapes(input []byte) []byte {
- // read represents the remaining part of input that needs to be processed.
- read := input
- // write represents where we should be writing in input.
- // It will share the same backing store as input so that we make our modifications
- // in place.
- write := input
-
- // advance will copy count bytes from read to write and advance those slices
- advance := func(write, read []byte, count int) ([]byte, []byte) {
- copy(write, read[:count])
- return write[count:], read[count:]
- }
-
- for {
- // Find the next escape sequence
- i := bytes.IndexByte(read, 0x1b)
- // If it isn't found, or if there isn't room for <ESC>[, finish
- if i == -1 || i+1 >= len(read) {
- copy(write, read)
- break
- }
-
- // Not a CSI code, continue searching
- if read[i+1] != '[' {
- write, read = advance(write, read, i+1)
- continue
- }
-
- // Found a CSI code, advance up to the <ESC>
- write, read = advance(write, read, i)
-
- // Find the end of the CSI code
- i = bytes.IndexFunc(read, func(r rune) bool {
- return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
- })
- if i == -1 {
- // We didn't find the end of the code, just remove the rest
- i = len(read) - 1
- }
-
- // Strip off the end marker too
- i = i + 1
-
- // Skip the reader forward and reduce final length by that amount
- read = read[i:]
- input = input[:len(input)-i]
- }
-
- return input
+ return io.Copy(destination, source)
}
diff --git a/ui/build/util_test.go b/ui/build/util_test.go
index e85eada..b22e997 100644
--- a/ui/build/util_test.go
+++ b/ui/build/util_test.go
@@ -14,49 +14,108 @@
package build
-import "testing"
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
-func TestStripAnsiEscapes(t *testing.T) {
- testcases := []struct {
- input string
- output string
- }{
- {
- "",
- "",
- },
- {
- "This is a test",
- "This is a test",
- },
- {
- "interrupted: \x1b[12",
- "interrupted: ",
- },
- {
- "other \x1bescape \x1b",
- "other \x1bescape \x1b",
- },
- { // from pretty-error macro
- "\x1b[1mart/Android.mk: \x1b[31merror:\x1b[0m\x1b[1m art: test error \x1b[0m",
- "art/Android.mk: error: art: test error ",
- },
- { // from envsetup.sh make wrapper
- "\x1b[0;31m#### make failed to build some targets (2 seconds) ####\x1b[00m",
- "#### make failed to build some targets (2 seconds) ####",
- },
- { // from clang (via ninja testcase)
- "\x1b[1maffixmgr.cxx:286:15: \x1b[0m\x1b[0;1;35mwarning: \x1b[0m\x1b[1musing the result... [-Wparentheses]\x1b[0m",
- "affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]",
- },
+ "android/soong/ui/logger"
+)
+
+func TestEnsureEmptyDirs(t *testing.T) {
+ ctx := testContext()
+ defer logger.Recover(func(err error) {
+ t.Error(err)
+ })
+
+ tmpDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatal(err)
}
- for _, tc := range testcases {
- got := string(stripAnsiEscapes([]byte(tc.input)))
- if got != tc.output {
- t.Errorf("output strings didn't match\n"+
- "input: %#v\n"+
- " want: %#v\n"+
- " got: %#v", tc.input, tc.output, got)
+ defer func() {
+ err := os.RemoveAll(tmpDir)
+ if err != nil {
+ t.Errorf("Error removing tmpDir: %v", err)
}
+ }()
+
+ ensureEmptyDirectoriesExist(ctx, filepath.Join(tmpDir, "a/b"))
+
+ err = os.Chmod(filepath.Join(tmpDir, "a"), 0555)
+ if err != nil {
+ t.Fatalf("Failed to chown: %v", err)
+ }
+
+ ensureEmptyDirectoriesExist(ctx, filepath.Join(tmpDir, "a"))
+}
+
+func TestCopyFile(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "test_copy_file")
+ if err != nil {
+ t.Fatalf("failed to create temporary directory to hold test text files: %v", err)
+ }
+ defer os.Remove(tmpDir)
+
+ data := []byte("fake data")
+ src := filepath.Join(tmpDir, "src.txt")
+ if err := ioutil.WriteFile(src, data, 0755); err != nil {
+ t.Fatalf("failed to create a src file %q for copying: %v", src, err)
+ }
+
+ dst := filepath.Join(tmpDir, "dst.txt")
+
+ l, err := copyFile(src, dst)
+ if err != nil {
+ t.Fatalf("got %v, expecting nil error on copyFile operation", err)
+ }
+
+ if l != int64(len(data)) {
+ t.Errorf("got %d, expecting %d for copied bytes", l, len(data))
+ }
+
+ dstData, err := ioutil.ReadFile(dst)
+ if err != nil {
+ t.Fatalf("got %v, expecting nil error reading dst %q file", err, dst)
+ }
+
+ if bytes.Compare(data, dstData) != 0 {
+ t.Errorf("got %q, expecting data %q from dst %q text file", string(data), string(dstData), dst)
+ }
+}
+
+func TestCopyFileErrors(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "test_copy_file_errors")
+ if err != nil {
+ t.Fatalf("failed to create temporary directory to hold test text files: %v", err)
+ }
+ defer os.Remove(tmpDir)
+
+ srcExists := filepath.Join(tmpDir, "src_exist.txt")
+ if err := ioutil.WriteFile(srcExists, []byte("fake data"), 0755); err != nil {
+ t.Fatalf("failed to create a src file %q for copying: %v", srcExists, err)
+ }
+
+ tests := []struct {
+ description string
+ src string
+ dst string
+ }{{
+ description: "src file does not exist",
+ src: "/src/not/exist",
+ dst: "/dst/not/exist",
+ }, {
+ description: "dst directory does not exist",
+ src: srcExists,
+ dst: "/dst/not/exist",
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ if _, err := copyFile(tt.src, tt.dst); err == nil {
+ t.Errorf("got nil, expecting error")
+ }
+ })
}
}
diff --git a/ui/logger/logger.go b/ui/logger/logger.go
index c763e50..9b26ae8 100644
--- a/ui/logger/logger.go
+++ b/ui/logger/logger.go
@@ -73,7 +73,9 @@
}
// fatalLog is the type used when Fatal[f|ln]
-type fatalLog error
+type fatalLog struct {
+ error
+}
func fileRotation(from, baseName, ext string, cur, max int) error {
newName := baseName + "." + strconv.Itoa(cur) + ext
@@ -178,12 +180,16 @@
return s
}
+type panicWriter struct{}
+
+func (panicWriter) Write([]byte) (int, error) { panic("write to panicWriter") }
+
// Close disables logging to the file and closes the file handle.
func (s *stdLogger) Close() {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.file != nil {
- s.fileLogger.SetOutput(ioutil.Discard)
+ s.fileLogger.SetOutput(panicWriter{})
s.file.Close()
s.file = nil
}
@@ -273,7 +279,7 @@
func (s *stdLogger) Fatal(v ...interface{}) {
output := fmt.Sprint(v...)
s.Output(2, output)
- panic(fatalLog(errors.New(output)))
+ panic(fatalLog{errors.New(output)})
}
// Fatalf is equivalent to Printf() followed by a call to panic() that
@@ -281,7 +287,7 @@
func (s *stdLogger) Fatalf(format string, v ...interface{}) {
output := fmt.Sprintf(format, v...)
s.Output(2, output)
- panic(fatalLog(errors.New(output)))
+ panic(fatalLog{errors.New(output)})
}
// Fatalln is equivalent to Println() followed by a call to panic() that
@@ -289,7 +295,7 @@
func (s *stdLogger) Fatalln(v ...interface{}) {
output := fmt.Sprintln(v...)
s.Output(2, output)
- panic(fatalLog(errors.New(output)))
+ panic(fatalLog{errors.New(output)})
}
// Panic is equivalent to Print() followed by a call to panic().
diff --git a/ui/logger/logger_test.go b/ui/logger/logger_test.go
index bdf0231..044e6f0 100644
--- a/ui/logger/logger_test.go
+++ b/ui/logger/logger_test.go
@@ -196,3 +196,17 @@
log.Panic("Test")
t.Errorf("Should not get here")
}
+
+func TestRuntimePanic(t *testing.T) {
+ defer func() {
+ if p := recover(); p == nil {
+ t.Errorf("Panic not thrown")
+ }
+ }()
+ defer Recover(func(err error) {
+ t.Errorf("Recover function should not be called")
+ })
+ var i *int
+ *i = 0
+ t.Errorf("Should not get here")
+}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
new file mode 100644
index 0000000..8188a69
--- /dev/null
+++ b/ui/metrics/Android.bp
@@ -0,0 +1,49 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+bootstrap_go_package {
+ name: "soong-ui-metrics",
+ pkgPath: "android/soong/ui/metrics",
+ deps: [
+ "golang-protobuf-proto",
+ "soong-ui-metrics_upload_proto",
+ "soong-ui-metrics_proto",
+ "soong-ui-tracer",
+ ],
+ srcs: [
+ "metrics.go",
+ "time.go",
+ ],
+ testSrcs: [
+ "time_test.go",
+ ],
+}
+
+bootstrap_go_package {
+ name: "soong-ui-metrics_proto",
+ pkgPath: "android/soong/ui/metrics/metrics_proto",
+ deps: ["golang-protobuf-proto"],
+ srcs: [
+ "metrics_proto/metrics.pb.go",
+ ],
+}
+
+bootstrap_go_package {
+ name: "soong-ui-metrics_upload_proto",
+ pkgPath: "android/soong/ui/metrics/upload_proto",
+ deps: ["golang-protobuf-proto"],
+ srcs: [
+ "upload_proto/upload.pb.go",
+ ],
+}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
new file mode 100644
index 0000000..4de5a70
--- /dev/null
+++ b/ui/metrics/metrics.go
@@ -0,0 +1,171 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metrics
+
+import (
+ "io/ioutil"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+
+ "android/soong/ui/metrics/metrics_proto"
+
+ "github.com/golang/protobuf/proto"
+)
+
+const (
+ PrimaryNinja = "ninja"
+ RunKati = "kati"
+ RunSetupTool = "setup"
+ RunShutdownTool = "shutdown"
+ RunSoong = "soong"
+ TestRun = "test"
+ Total = "total"
+)
+
+type Metrics struct {
+ metrics metrics_proto.MetricsBase
+ TimeTracer TimeTracer
+}
+
+func New() (metrics *Metrics) {
+ m := &Metrics{
+ metrics: metrics_proto.MetricsBase{},
+ TimeTracer: &timeTracerImpl{},
+ }
+ return m
+}
+
+func (m *Metrics) SetTimeMetrics(perf metrics_proto.PerfInfo) {
+ switch perf.GetName() {
+ case RunKati:
+ m.metrics.KatiRuns = append(m.metrics.KatiRuns, &perf)
+ break
+ case RunSoong:
+ m.metrics.SoongRuns = append(m.metrics.SoongRuns, &perf)
+ break
+ case PrimaryNinja:
+ m.metrics.NinjaRuns = append(m.metrics.NinjaRuns, &perf)
+ break
+ default:
+ // ignored
+ }
+}
+
+func (m *Metrics) BuildConfig(b *metrics_proto.BuildConfig) {
+ m.metrics.BuildConfig = b
+}
+
+func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
+ for k, v := range metadata {
+ switch k {
+ case "BUILD_ID":
+ m.metrics.BuildId = proto.String(v)
+ break
+ case "PLATFORM_VERSION_CODENAME":
+ m.metrics.PlatformVersionCodename = proto.String(v)
+ break
+ case "TARGET_PRODUCT":
+ m.metrics.TargetProduct = proto.String(v)
+ break
+ case "TARGET_BUILD_VARIANT":
+ switch v {
+ case "user":
+ m.metrics.TargetBuildVariant = metrics_proto.MetricsBase_USER.Enum()
+ case "userdebug":
+ m.metrics.TargetBuildVariant = metrics_proto.MetricsBase_USERDEBUG.Enum()
+ case "eng":
+ m.metrics.TargetBuildVariant = metrics_proto.MetricsBase_ENG.Enum()
+ default:
+ // ignored
+ }
+ case "TARGET_ARCH":
+ m.metrics.TargetArch = m.getArch(v)
+ case "TARGET_ARCH_VARIANT":
+ m.metrics.TargetArchVariant = proto.String(v)
+ case "TARGET_CPU_VARIANT":
+ m.metrics.TargetCpuVariant = proto.String(v)
+ case "HOST_ARCH":
+ m.metrics.HostArch = m.getArch(v)
+ case "HOST_2ND_ARCH":
+ m.metrics.Host_2NdArch = m.getArch(v)
+ case "HOST_OS_EXTRA":
+ m.metrics.HostOsExtra = proto.String(v)
+ case "HOST_CROSS_OS":
+ m.metrics.HostCrossOs = proto.String(v)
+ case "HOST_CROSS_ARCH":
+ m.metrics.HostCrossArch = proto.String(v)
+ case "HOST_CROSS_2ND_ARCH":
+ m.metrics.HostCross_2NdArch = proto.String(v)
+ case "OUT_DIR":
+ m.metrics.OutDir = proto.String(v)
+ default:
+ // ignored
+ }
+ }
+}
+
+func (m *Metrics) getArch(arch string) *metrics_proto.MetricsBase_ARCH {
+ switch arch {
+ case "arm":
+ return metrics_proto.MetricsBase_ARM.Enum()
+ case "arm64":
+ return metrics_proto.MetricsBase_ARM64.Enum()
+ case "x86":
+ return metrics_proto.MetricsBase_X86.Enum()
+ case "x86_64":
+ return metrics_proto.MetricsBase_X86_64.Enum()
+ default:
+ return metrics_proto.MetricsBase_UNKNOWN.Enum()
+ }
+}
+
+func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) {
+ m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
+}
+
+func (m *Metrics) Serialize() (data []byte, err error) {
+ return proto.Marshal(&m.metrics)
+}
+
+func (m *Metrics) SetBuildCommand(cmd []string) {
+ m.metrics.BuildCommand = proto.String(strings.Join(cmd, " "))
+}
+
+// exports the output to the file at outputPath
+func (m *Metrics) Dump(outputPath string) error {
+ // ignore the error if the hostname could not be retrieved as it
+ // is not a critical metric to extract.
+ if hostname, err := os.Hostname(); err == nil {
+ m.metrics.Hostname = proto.String(hostname)
+ }
+ m.metrics.HostOs = proto.String(runtime.GOOS)
+ data, err := m.Serialize()
+ if err != nil {
+ return err
+ }
+ tempPath := outputPath + ".tmp"
+ err = ioutil.WriteFile(tempPath, []byte(data), 0777)
+ if err != nil {
+ return err
+ }
+ err = os.Rename(tempPath, outputPath)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
new file mode 100644
index 0000000..338a922
--- /dev/null
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -0,0 +1,663 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: metrics.proto
+
+package metrics_proto
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type MetricsBase_BUILDVARIANT int32
+
+const (
+ MetricsBase_USER MetricsBase_BUILDVARIANT = 0
+ MetricsBase_USERDEBUG MetricsBase_BUILDVARIANT = 1
+ MetricsBase_ENG MetricsBase_BUILDVARIANT = 2
+)
+
+var MetricsBase_BUILDVARIANT_name = map[int32]string{
+ 0: "USER",
+ 1: "USERDEBUG",
+ 2: "ENG",
+}
+
+var MetricsBase_BUILDVARIANT_value = map[string]int32{
+ "USER": 0,
+ "USERDEBUG": 1,
+ "ENG": 2,
+}
+
+func (x MetricsBase_BUILDVARIANT) Enum() *MetricsBase_BUILDVARIANT {
+ p := new(MetricsBase_BUILDVARIANT)
+ *p = x
+ return p
+}
+
+func (x MetricsBase_BUILDVARIANT) String() string {
+ return proto.EnumName(MetricsBase_BUILDVARIANT_name, int32(x))
+}
+
+func (x *MetricsBase_BUILDVARIANT) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MetricsBase_BUILDVARIANT_value, data, "MetricsBase_BUILDVARIANT")
+ if err != nil {
+ return err
+ }
+ *x = MetricsBase_BUILDVARIANT(value)
+ return nil
+}
+
+func (MetricsBase_BUILDVARIANT) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{0, 0}
+}
+
+type MetricsBase_ARCH int32
+
+const (
+ MetricsBase_UNKNOWN MetricsBase_ARCH = 0
+ MetricsBase_ARM MetricsBase_ARCH = 1
+ MetricsBase_ARM64 MetricsBase_ARCH = 2
+ MetricsBase_X86 MetricsBase_ARCH = 3
+ MetricsBase_X86_64 MetricsBase_ARCH = 4
+)
+
+var MetricsBase_ARCH_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "ARM",
+ 2: "ARM64",
+ 3: "X86",
+ 4: "X86_64",
+}
+
+var MetricsBase_ARCH_value = map[string]int32{
+ "UNKNOWN": 0,
+ "ARM": 1,
+ "ARM64": 2,
+ "X86": 3,
+ "X86_64": 4,
+}
+
+func (x MetricsBase_ARCH) Enum() *MetricsBase_ARCH {
+ p := new(MetricsBase_ARCH)
+ *p = x
+ return p
+}
+
+func (x MetricsBase_ARCH) String() string {
+ return proto.EnumName(MetricsBase_ARCH_name, int32(x))
+}
+
+func (x *MetricsBase_ARCH) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MetricsBase_ARCH_value, data, "MetricsBase_ARCH")
+ if err != nil {
+ return err
+ }
+ *x = MetricsBase_ARCH(value)
+ return nil
+}
+
+func (MetricsBase_ARCH) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{0, 1}
+}
+
+type ModuleTypeInfo_BUILDSYSTEM int32
+
+const (
+ ModuleTypeInfo_UNKNOWN ModuleTypeInfo_BUILDSYSTEM = 0
+ ModuleTypeInfo_SOONG ModuleTypeInfo_BUILDSYSTEM = 1
+ ModuleTypeInfo_MAKE ModuleTypeInfo_BUILDSYSTEM = 2
+)
+
+var ModuleTypeInfo_BUILDSYSTEM_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "SOONG",
+ 2: "MAKE",
+}
+
+var ModuleTypeInfo_BUILDSYSTEM_value = map[string]int32{
+ "UNKNOWN": 0,
+ "SOONG": 1,
+ "MAKE": 2,
+}
+
+func (x ModuleTypeInfo_BUILDSYSTEM) Enum() *ModuleTypeInfo_BUILDSYSTEM {
+ p := new(ModuleTypeInfo_BUILDSYSTEM)
+ *p = x
+ return p
+}
+
+func (x ModuleTypeInfo_BUILDSYSTEM) String() string {
+ return proto.EnumName(ModuleTypeInfo_BUILDSYSTEM_name, int32(x))
+}
+
+func (x *ModuleTypeInfo_BUILDSYSTEM) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(ModuleTypeInfo_BUILDSYSTEM_value, data, "ModuleTypeInfo_BUILDSYSTEM")
+ if err != nil {
+ return err
+ }
+ *x = ModuleTypeInfo_BUILDSYSTEM(value)
+ return nil
+}
+
+func (ModuleTypeInfo_BUILDSYSTEM) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{3, 0}
+}
+
+type MetricsBase struct {
+ // Timestamp generated when the build starts.
+ BuildDateTimestamp *int64 `protobuf:"varint,1,opt,name=build_date_timestamp,json=buildDateTimestamp" json:"build_date_timestamp,omitempty"`
+ // It is usually used to specify the branch name [and release candidate].
+ BuildId *string `protobuf:"bytes,2,opt,name=build_id,json=buildId" json:"build_id,omitempty"`
+ // The platform version codename, eg. P, Q, REL.
+ PlatformVersionCodename *string `protobuf:"bytes,3,opt,name=platform_version_codename,json=platformVersionCodename" json:"platform_version_codename,omitempty"`
+ // The target product information, eg. aosp_arm.
+ TargetProduct *string `protobuf:"bytes,4,opt,name=target_product,json=targetProduct" json:"target_product,omitempty"`
+ // The target build variant information, eg. eng.
+ TargetBuildVariant *MetricsBase_BUILDVARIANT `protobuf:"varint,5,opt,name=target_build_variant,json=targetBuildVariant,enum=build_metrics.MetricsBase_BUILDVARIANT,def=2" json:"target_build_variant,omitempty"`
+ // The target arch information, eg. arm.
+ TargetArch *MetricsBase_ARCH `protobuf:"varint,6,opt,name=target_arch,json=targetArch,enum=build_metrics.MetricsBase_ARCH,def=0" json:"target_arch,omitempty"`
+ // The target arch variant information, eg. armv7-a-neon.
+ TargetArchVariant *string `protobuf:"bytes,7,opt,name=target_arch_variant,json=targetArchVariant" json:"target_arch_variant,omitempty"`
+ // The target cpu variant information, eg. generic.
+ TargetCpuVariant *string `protobuf:"bytes,8,opt,name=target_cpu_variant,json=targetCpuVariant" json:"target_cpu_variant,omitempty"`
+ // The host arch information, eg. x86_64.
+ HostArch *MetricsBase_ARCH `protobuf:"varint,9,opt,name=host_arch,json=hostArch,enum=build_metrics.MetricsBase_ARCH,def=0" json:"host_arch,omitempty"`
+ // The host 2nd arch information, eg. x86.
+ Host_2NdArch *MetricsBase_ARCH `protobuf:"varint,10,opt,name=host_2nd_arch,json=host2ndArch,enum=build_metrics.MetricsBase_ARCH,def=0" json:"host_2nd_arch,omitempty"`
+ // The host os information, eg. linux.
+ HostOs *string `protobuf:"bytes,11,opt,name=host_os,json=hostOs" json:"host_os,omitempty"`
+ // The host os extra information, eg. Linux-4.17.0-3rodete2-amd64-x86_64-Debian-GNU.
+ HostOsExtra *string `protobuf:"bytes,12,opt,name=host_os_extra,json=hostOsExtra" json:"host_os_extra,omitempty"`
+ // The host cross os information, eg. windows.
+ HostCrossOs *string `protobuf:"bytes,13,opt,name=host_cross_os,json=hostCrossOs" json:"host_cross_os,omitempty"`
+ // The host cross arch information, eg. x86.
+ HostCrossArch *string `protobuf:"bytes,14,opt,name=host_cross_arch,json=hostCrossArch" json:"host_cross_arch,omitempty"`
+ // The host cross 2nd arch information, eg. x86_64.
+ HostCross_2NdArch *string `protobuf:"bytes,15,opt,name=host_cross_2nd_arch,json=hostCross2ndArch" json:"host_cross_2nd_arch,omitempty"`
+ // The directory for generated built artifacts installation, eg. out.
+ OutDir *string `protobuf:"bytes,16,opt,name=out_dir,json=outDir" json:"out_dir,omitempty"`
+ // The metrics for calling various tools (microfactory) before Soong_UI starts.
+ SetupTools []*PerfInfo `protobuf:"bytes,17,rep,name=setup_tools,json=setupTools" json:"setup_tools,omitempty"`
+ // The metrics for calling Kati by multiple times.
+ KatiRuns []*PerfInfo `protobuf:"bytes,18,rep,name=kati_runs,json=katiRuns" json:"kati_runs,omitempty"`
+ // The metrics for calling Soong.
+ SoongRuns []*PerfInfo `protobuf:"bytes,19,rep,name=soong_runs,json=soongRuns" json:"soong_runs,omitempty"`
+ // The metrics for calling Ninja.
+ NinjaRuns []*PerfInfo `protobuf:"bytes,20,rep,name=ninja_runs,json=ninjaRuns" json:"ninja_runs,omitempty"`
+ BuildConfig *BuildConfig `protobuf:"bytes,23,opt,name=build_config,json=buildConfig" json:"build_config,omitempty"`
+ // The hostname of the machine.
+ Hostname *string `protobuf:"bytes,24,opt,name=hostname" json:"hostname,omitempty"`
+ // The build command that the user entered to the build system.
+ BuildCommand *string `protobuf:"bytes,26,opt,name=build_command,json=buildCommand" json:"build_command,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *MetricsBase) Reset() { *m = MetricsBase{} }
+func (m *MetricsBase) String() string { return proto.CompactTextString(m) }
+func (*MetricsBase) ProtoMessage() {}
+func (*MetricsBase) Descriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{0}
+}
+
+func (m *MetricsBase) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_MetricsBase.Unmarshal(m, b)
+}
+func (m *MetricsBase) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_MetricsBase.Marshal(b, m, deterministic)
+}
+func (m *MetricsBase) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MetricsBase.Merge(m, src)
+}
+func (m *MetricsBase) XXX_Size() int {
+ return xxx_messageInfo_MetricsBase.Size(m)
+}
+func (m *MetricsBase) XXX_DiscardUnknown() {
+ xxx_messageInfo_MetricsBase.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MetricsBase proto.InternalMessageInfo
+
+const Default_MetricsBase_TargetBuildVariant MetricsBase_BUILDVARIANT = MetricsBase_ENG
+const Default_MetricsBase_TargetArch MetricsBase_ARCH = MetricsBase_UNKNOWN
+const Default_MetricsBase_HostArch MetricsBase_ARCH = MetricsBase_UNKNOWN
+const Default_MetricsBase_Host_2NdArch MetricsBase_ARCH = MetricsBase_UNKNOWN
+
+func (m *MetricsBase) GetBuildDateTimestamp() int64 {
+ if m != nil && m.BuildDateTimestamp != nil {
+ return *m.BuildDateTimestamp
+ }
+ return 0
+}
+
+func (m *MetricsBase) GetBuildId() string {
+ if m != nil && m.BuildId != nil {
+ return *m.BuildId
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetPlatformVersionCodename() string {
+ if m != nil && m.PlatformVersionCodename != nil {
+ return *m.PlatformVersionCodename
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetTargetProduct() string {
+ if m != nil && m.TargetProduct != nil {
+ return *m.TargetProduct
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetTargetBuildVariant() MetricsBase_BUILDVARIANT {
+ if m != nil && m.TargetBuildVariant != nil {
+ return *m.TargetBuildVariant
+ }
+ return Default_MetricsBase_TargetBuildVariant
+}
+
+func (m *MetricsBase) GetTargetArch() MetricsBase_ARCH {
+ if m != nil && m.TargetArch != nil {
+ return *m.TargetArch
+ }
+ return Default_MetricsBase_TargetArch
+}
+
+func (m *MetricsBase) GetTargetArchVariant() string {
+ if m != nil && m.TargetArchVariant != nil {
+ return *m.TargetArchVariant
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetTargetCpuVariant() string {
+ if m != nil && m.TargetCpuVariant != nil {
+ return *m.TargetCpuVariant
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetHostArch() MetricsBase_ARCH {
+ if m != nil && m.HostArch != nil {
+ return *m.HostArch
+ }
+ return Default_MetricsBase_HostArch
+}
+
+func (m *MetricsBase) GetHost_2NdArch() MetricsBase_ARCH {
+ if m != nil && m.Host_2NdArch != nil {
+ return *m.Host_2NdArch
+ }
+ return Default_MetricsBase_Host_2NdArch
+}
+
+func (m *MetricsBase) GetHostOs() string {
+ if m != nil && m.HostOs != nil {
+ return *m.HostOs
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetHostOsExtra() string {
+ if m != nil && m.HostOsExtra != nil {
+ return *m.HostOsExtra
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetHostCrossOs() string {
+ if m != nil && m.HostCrossOs != nil {
+ return *m.HostCrossOs
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetHostCrossArch() string {
+ if m != nil && m.HostCrossArch != nil {
+ return *m.HostCrossArch
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetHostCross_2NdArch() string {
+ if m != nil && m.HostCross_2NdArch != nil {
+ return *m.HostCross_2NdArch
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetOutDir() string {
+ if m != nil && m.OutDir != nil {
+ return *m.OutDir
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetSetupTools() []*PerfInfo {
+ if m != nil {
+ return m.SetupTools
+ }
+ return nil
+}
+
+func (m *MetricsBase) GetKatiRuns() []*PerfInfo {
+ if m != nil {
+ return m.KatiRuns
+ }
+ return nil
+}
+
+func (m *MetricsBase) GetSoongRuns() []*PerfInfo {
+ if m != nil {
+ return m.SoongRuns
+ }
+ return nil
+}
+
+func (m *MetricsBase) GetNinjaRuns() []*PerfInfo {
+ if m != nil {
+ return m.NinjaRuns
+ }
+ return nil
+}
+
+func (m *MetricsBase) GetBuildConfig() *BuildConfig {
+ if m != nil {
+ return m.BuildConfig
+ }
+ return nil
+}
+
+func (m *MetricsBase) GetHostname() string {
+ if m != nil && m.Hostname != nil {
+ return *m.Hostname
+ }
+ return ""
+}
+
+func (m *MetricsBase) GetBuildCommand() string {
+ if m != nil && m.BuildCommand != nil {
+ return *m.BuildCommand
+ }
+ return ""
+}
+
+type BuildConfig struct {
+ UseGoma *bool `protobuf:"varint,1,opt,name=use_goma,json=useGoma" json:"use_goma,omitempty"`
+ UseRbe *bool `protobuf:"varint,2,opt,name=use_rbe,json=useRbe" json:"use_rbe,omitempty"`
+ ForceUseGoma *bool `protobuf:"varint,3,opt,name=force_use_goma,json=forceUseGoma" json:"force_use_goma,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BuildConfig) Reset() { *m = BuildConfig{} }
+func (m *BuildConfig) String() string { return proto.CompactTextString(m) }
+func (*BuildConfig) ProtoMessage() {}
+func (*BuildConfig) Descriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{1}
+}
+
+func (m *BuildConfig) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BuildConfig.Unmarshal(m, b)
+}
+func (m *BuildConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BuildConfig.Marshal(b, m, deterministic)
+}
+func (m *BuildConfig) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BuildConfig.Merge(m, src)
+}
+func (m *BuildConfig) XXX_Size() int {
+ return xxx_messageInfo_BuildConfig.Size(m)
+}
+func (m *BuildConfig) XXX_DiscardUnknown() {
+ xxx_messageInfo_BuildConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildConfig proto.InternalMessageInfo
+
+func (m *BuildConfig) GetUseGoma() bool {
+ if m != nil && m.UseGoma != nil {
+ return *m.UseGoma
+ }
+ return false
+}
+
+func (m *BuildConfig) GetUseRbe() bool {
+ if m != nil && m.UseRbe != nil {
+ return *m.UseRbe
+ }
+ return false
+}
+
+func (m *BuildConfig) GetForceUseGoma() bool {
+ if m != nil && m.ForceUseGoma != nil {
+ return *m.ForceUseGoma
+ }
+ return false
+}
+
+type PerfInfo struct {
+ // The description for the phase/action/part while the tool running.
+ Desc *string `protobuf:"bytes,1,opt,name=desc" json:"desc,omitempty"`
+ // The name for the running phase/action/part.
+ Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
+ // The absolute start time.
+ // The number of nanoseconds elapsed since January 1, 1970 UTC.
+ StartTime *uint64 `protobuf:"varint,3,opt,name=start_time,json=startTime" json:"start_time,omitempty"`
+ // The real running time.
+ // The number of nanoseconds elapsed since start_time.
+ RealTime *uint64 `protobuf:"varint,4,opt,name=real_time,json=realTime" json:"real_time,omitempty"`
+ // The number of MB for memory use.
+ MemoryUse *uint64 `protobuf:"varint,5,opt,name=memory_use,json=memoryUse" json:"memory_use,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PerfInfo) Reset() { *m = PerfInfo{} }
+func (m *PerfInfo) String() string { return proto.CompactTextString(m) }
+func (*PerfInfo) ProtoMessage() {}
+func (*PerfInfo) Descriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{2}
+}
+
+func (m *PerfInfo) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PerfInfo.Unmarshal(m, b)
+}
+func (m *PerfInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PerfInfo.Marshal(b, m, deterministic)
+}
+func (m *PerfInfo) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PerfInfo.Merge(m, src)
+}
+func (m *PerfInfo) XXX_Size() int {
+ return xxx_messageInfo_PerfInfo.Size(m)
+}
+func (m *PerfInfo) XXX_DiscardUnknown() {
+ xxx_messageInfo_PerfInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PerfInfo proto.InternalMessageInfo
+
+func (m *PerfInfo) GetDesc() string {
+ if m != nil && m.Desc != nil {
+ return *m.Desc
+ }
+ return ""
+}
+
+func (m *PerfInfo) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *PerfInfo) GetStartTime() uint64 {
+ if m != nil && m.StartTime != nil {
+ return *m.StartTime
+ }
+ return 0
+}
+
+func (m *PerfInfo) GetRealTime() uint64 {
+ if m != nil && m.RealTime != nil {
+ return *m.RealTime
+ }
+ return 0
+}
+
+func (m *PerfInfo) GetMemoryUse() uint64 {
+ if m != nil && m.MemoryUse != nil {
+ return *m.MemoryUse
+ }
+ return 0
+}
+
+type ModuleTypeInfo struct {
+ // The build system, eg. Soong or Make.
+ BuildSystem *ModuleTypeInfo_BUILDSYSTEM `protobuf:"varint,1,opt,name=build_system,json=buildSystem,enum=build_metrics.ModuleTypeInfo_BUILDSYSTEM,def=0" json:"build_system,omitempty"`
+ // The module type, eg. java_library, cc_binary, and etc.
+ ModuleType *string `protobuf:"bytes,2,opt,name=module_type,json=moduleType" json:"module_type,omitempty"`
+ // The number of logical modules.
+ NumOfModules *uint32 `protobuf:"varint,3,opt,name=num_of_modules,json=numOfModules" json:"num_of_modules,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ModuleTypeInfo) Reset() { *m = ModuleTypeInfo{} }
+func (m *ModuleTypeInfo) String() string { return proto.CompactTextString(m) }
+func (*ModuleTypeInfo) ProtoMessage() {}
+func (*ModuleTypeInfo) Descriptor() ([]byte, []int) {
+ return fileDescriptor_6039342a2ba47b72, []int{3}
+}
+
+func (m *ModuleTypeInfo) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ModuleTypeInfo.Unmarshal(m, b)
+}
+func (m *ModuleTypeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ModuleTypeInfo.Marshal(b, m, deterministic)
+}
+func (m *ModuleTypeInfo) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ModuleTypeInfo.Merge(m, src)
+}
+func (m *ModuleTypeInfo) XXX_Size() int {
+ return xxx_messageInfo_ModuleTypeInfo.Size(m)
+}
+func (m *ModuleTypeInfo) XXX_DiscardUnknown() {
+ xxx_messageInfo_ModuleTypeInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ModuleTypeInfo proto.InternalMessageInfo
+
+const Default_ModuleTypeInfo_BuildSystem ModuleTypeInfo_BUILDSYSTEM = ModuleTypeInfo_UNKNOWN
+
+func (m *ModuleTypeInfo) GetBuildSystem() ModuleTypeInfo_BUILDSYSTEM {
+ if m != nil && m.BuildSystem != nil {
+ return *m.BuildSystem
+ }
+ return Default_ModuleTypeInfo_BuildSystem
+}
+
+func (m *ModuleTypeInfo) GetModuleType() string {
+ if m != nil && m.ModuleType != nil {
+ return *m.ModuleType
+ }
+ return ""
+}
+
+func (m *ModuleTypeInfo) GetNumOfModules() uint32 {
+ if m != nil && m.NumOfModules != nil {
+ return *m.NumOfModules
+ }
+ return 0
+}
+
+func init() {
+ proto.RegisterEnum("build_metrics.MetricsBase_BUILDVARIANT", MetricsBase_BUILDVARIANT_name, MetricsBase_BUILDVARIANT_value)
+ proto.RegisterEnum("build_metrics.MetricsBase_ARCH", MetricsBase_ARCH_name, MetricsBase_ARCH_value)
+ proto.RegisterEnum("build_metrics.ModuleTypeInfo_BUILDSYSTEM", ModuleTypeInfo_BUILDSYSTEM_name, ModuleTypeInfo_BUILDSYSTEM_value)
+ proto.RegisterType((*MetricsBase)(nil), "build_metrics.MetricsBase")
+ proto.RegisterType((*BuildConfig)(nil), "build_metrics.BuildConfig")
+ proto.RegisterType((*PerfInfo)(nil), "build_metrics.PerfInfo")
+ proto.RegisterType((*ModuleTypeInfo)(nil), "build_metrics.ModuleTypeInfo")
+}
+
+func init() { proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72) }
+
+var fileDescriptor_6039342a2ba47b72 = []byte{
+ // 887 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x6f, 0x6f, 0xe2, 0x36,
+ 0x18, 0x3f, 0x0a, 0x2d, 0xc9, 0x13, 0xe0, 0x52, 0xb7, 0x52, 0x73, 0x9d, 0x4e, 0x87, 0xd8, 0x3f,
+ 0x26, 0x6d, 0xec, 0x84, 0x2a, 0x54, 0x55, 0xdb, 0x0b, 0xa0, 0xa8, 0x87, 0xee, 0x80, 0x93, 0x81,
+ 0xee, 0xb6, 0x17, 0x8b, 0xd2, 0xc4, 0xd0, 0x6c, 0x24, 0x46, 0xb6, 0x73, 0x5a, 0x3f, 0xc4, 0x3e,
+ 0xe3, 0xbe, 0xc8, 0x5e, 0x4c, 0x7e, 0x4c, 0x28, 0xed, 0x8b, 0x4e, 0x7d, 0x67, 0x3f, 0xbf, 0x3f,
+ 0xfe, 0xd9, 0xb1, 0x9f, 0x40, 0x35, 0x61, 0x4a, 0xc4, 0xa1, 0x6c, 0xad, 0x05, 0x57, 0x9c, 0x54,
+ 0x6f, 0xb2, 0x78, 0x15, 0xf9, 0x9b, 0x62, 0xe3, 0x5f, 0x1b, 0x9c, 0x91, 0x19, 0xf7, 0x02, 0xc9,
+ 0xc8, 0x5b, 0x38, 0x36, 0x84, 0x28, 0x50, 0xcc, 0x57, 0x71, 0xc2, 0xa4, 0x0a, 0x92, 0xb5, 0x57,
+ 0xa8, 0x17, 0x9a, 0x45, 0x4a, 0x10, 0xbb, 0x0c, 0x14, 0x9b, 0xe5, 0x08, 0x79, 0x05, 0x96, 0x51,
+ 0xc4, 0x91, 0xb7, 0x57, 0x2f, 0x34, 0x6d, 0x5a, 0xc6, 0xf9, 0x30, 0x22, 0x17, 0xf0, 0x6a, 0xbd,
+ 0x0a, 0xd4, 0x82, 0x8b, 0xc4, 0xff, 0xcc, 0x84, 0x8c, 0x79, 0xea, 0x87, 0x3c, 0x62, 0x69, 0x90,
+ 0x30, 0xaf, 0x88, 0xdc, 0x93, 0x9c, 0x70, 0x6d, 0xf0, 0xfe, 0x06, 0x26, 0x5f, 0x43, 0x4d, 0x05,
+ 0x62, 0xc9, 0x94, 0xbf, 0x16, 0x3c, 0xca, 0x42, 0xe5, 0x95, 0x50, 0x50, 0x35, 0xd5, 0x8f, 0xa6,
+ 0x48, 0x7e, 0x87, 0xe3, 0x0d, 0xcd, 0x84, 0xf8, 0x1c, 0x88, 0x38, 0x48, 0x95, 0xb7, 0x5f, 0x2f,
+ 0x34, 0x6b, 0xed, 0x6f, 0x5b, 0x0f, 0x76, 0xdb, 0xda, 0xd9, 0x69, 0xab, 0x37, 0x1f, 0x7e, 0xb8,
+ 0xbc, 0xee, 0xd2, 0x61, 0x77, 0x3c, 0xbb, 0x28, 0x0e, 0xc6, 0x57, 0x94, 0x18, 0xa7, 0x9e, 0x96,
+ 0x5c, 0x1b, 0x1f, 0x32, 0x04, 0x67, 0xe3, 0x1f, 0x88, 0xf0, 0xd6, 0x3b, 0x40, 0xdb, 0x37, 0x4f,
+ 0xd8, 0x76, 0x69, 0xff, 0xdd, 0x45, 0x79, 0x3e, 0x7e, 0x3f, 0x9e, 0xfc, 0x32, 0xa6, 0x60, 0xc4,
+ 0x5d, 0x11, 0xde, 0x92, 0x16, 0x1c, 0xed, 0x58, 0x6d, 0x93, 0x96, 0x71, 0x5b, 0x87, 0xf7, 0xc4,
+ 0x7c, 0xe9, 0xef, 0x61, 0x13, 0xc8, 0x0f, 0xd7, 0xd9, 0x96, 0x6e, 0x21, 0xdd, 0x35, 0x48, 0x7f,
+ 0x9d, 0xe5, 0xec, 0x01, 0xd8, 0xb7, 0x5c, 0x6e, 0x62, 0xda, 0xcf, 0x8c, 0x69, 0x69, 0x29, 0x86,
+ 0xfc, 0x00, 0x55, 0xb4, 0x69, 0xa7, 0x91, 0xb1, 0x82, 0x67, 0x5a, 0x39, 0x5a, 0xde, 0x4e, 0x23,
+ 0x74, 0x3b, 0x81, 0x32, 0xba, 0x71, 0xe9, 0x39, 0x98, 0xfb, 0x40, 0x4f, 0x27, 0x92, 0x34, 0x36,
+ 0xcb, 0x70, 0xe9, 0xb3, 0xbf, 0x94, 0x08, 0xbc, 0x0a, 0xc2, 0x8e, 0x81, 0x07, 0xba, 0xb4, 0xe5,
+ 0x84, 0x82, 0x4b, 0xa9, 0x2d, 0xaa, 0xf7, 0x9c, 0xbe, 0xae, 0x4d, 0x24, 0xf9, 0x06, 0x5e, 0xee,
+ 0x70, 0x30, 0x70, 0xcd, 0x5c, 0x93, 0x2d, 0x0b, 0x83, 0xfc, 0x00, 0x47, 0x3b, 0xbc, 0xed, 0xe6,
+ 0x5e, 0x9a, 0xc3, 0xdc, 0x72, 0x77, 0x72, 0xf3, 0x4c, 0xf9, 0x51, 0x2c, 0x3c, 0xd7, 0xe4, 0xe6,
+ 0x99, 0xba, 0x8c, 0x05, 0x39, 0x07, 0x47, 0x32, 0x95, 0xad, 0x7d, 0xc5, 0xf9, 0x4a, 0x7a, 0x87,
+ 0xf5, 0x62, 0xd3, 0x69, 0x9f, 0x3c, 0x3a, 0x9c, 0x8f, 0x4c, 0x2c, 0x86, 0xe9, 0x82, 0x53, 0x40,
+ 0xee, 0x4c, 0x53, 0xc9, 0x19, 0xd8, 0x7f, 0x06, 0x2a, 0xf6, 0x45, 0x96, 0x4a, 0x8f, 0x3c, 0xad,
+ 0xb3, 0x34, 0x93, 0x66, 0xa9, 0x24, 0x1d, 0x00, 0xc9, 0x79, 0xba, 0x34, 0xb2, 0xa3, 0xa7, 0x65,
+ 0x36, 0x52, 0x73, 0x5d, 0x1a, 0xa7, 0x7f, 0x04, 0x46, 0x77, 0xfc, 0x3f, 0x3a, 0xa4, 0xa2, 0xee,
+ 0x67, 0xa8, 0x18, 0x52, 0xc8, 0xd3, 0x45, 0xbc, 0xf4, 0x4e, 0xea, 0x85, 0xa6, 0xd3, 0x3e, 0x7d,
+ 0xa4, 0xc4, 0x17, 0xd2, 0x47, 0x06, 0x75, 0x6e, 0xee, 0x27, 0xe4, 0x14, 0xf0, 0x26, 0xe1, 0xfb,
+ 0xf6, 0xf0, 0xe0, 0xb6, 0x73, 0xf2, 0x25, 0x54, 0x73, 0xeb, 0x24, 0x09, 0xd2, 0xc8, 0x3b, 0x45,
+ 0x42, 0x65, 0xa3, 0xc7, 0x5a, 0xe3, 0x2d, 0x54, 0x76, 0xdf, 0x25, 0xb1, 0xa0, 0x34, 0x9f, 0x0e,
+ 0xa8, 0xfb, 0x82, 0x54, 0xc1, 0xd6, 0xa3, 0xcb, 0x41, 0x6f, 0x7e, 0xe5, 0x16, 0x48, 0x19, 0xf4,
+ 0x93, 0x75, 0xf7, 0x1a, 0x3f, 0x41, 0x49, 0x5f, 0x40, 0xe2, 0x40, 0x7e, 0x05, 0xdd, 0x17, 0x1a,
+ 0xed, 0xd2, 0x91, 0x5b, 0x20, 0x36, 0xec, 0x77, 0xe9, 0xa8, 0x73, 0xe6, 0xee, 0xe9, 0xda, 0xa7,
+ 0xf3, 0x8e, 0x5b, 0x24, 0x00, 0x07, 0x9f, 0xce, 0x3b, 0x7e, 0xe7, 0xcc, 0x2d, 0x35, 0x96, 0xe0,
+ 0xec, 0x6c, 0x46, 0xf7, 0xb2, 0x4c, 0x32, 0x7f, 0xc9, 0x93, 0x00, 0x3b, 0x9e, 0x45, 0xcb, 0x99,
+ 0x64, 0x57, 0x3c, 0x09, 0xf4, 0x95, 0xd0, 0x90, 0xb8, 0x61, 0xd8, 0xe5, 0x2c, 0x7a, 0x90, 0x49,
+ 0x46, 0x6f, 0x18, 0xf9, 0x0a, 0x6a, 0x0b, 0x2e, 0x42, 0xe6, 0x6f, 0x95, 0x45, 0xc4, 0x2b, 0x58,
+ 0x9d, 0x1b, 0x79, 0xe3, 0xef, 0x02, 0x58, 0xf9, 0x81, 0x13, 0x02, 0xa5, 0x88, 0xc9, 0x10, 0x97,
+ 0xb0, 0x29, 0x8e, 0x75, 0x0d, 0x8f, 0xcd, 0xb4, 0x50, 0x1c, 0x93, 0xd7, 0x00, 0x52, 0x05, 0x42,
+ 0x61, 0x1f, 0x46, 0xdb, 0x12, 0xb5, 0xb1, 0xa2, 0xdb, 0x2f, 0xf9, 0x02, 0x6c, 0xc1, 0x82, 0x95,
+ 0x41, 0x4b, 0x88, 0x5a, 0xba, 0x80, 0xe0, 0x6b, 0x80, 0x84, 0x25, 0x5c, 0xdc, 0xe9, 0x5c, 0xd8,
+ 0x0e, 0x4b, 0xd4, 0x36, 0x95, 0xb9, 0x64, 0x8d, 0x7f, 0x0a, 0x50, 0x1b, 0xf1, 0x28, 0x5b, 0xb1,
+ 0xd9, 0xdd, 0x9a, 0x61, 0xaa, 0x79, 0xfe, 0xed, 0xe5, 0x9d, 0x54, 0x2c, 0xc1, 0x74, 0xb5, 0xf6,
+ 0x77, 0x8f, 0x5f, 0xfe, 0x03, 0x91, 0xe9, 0xa2, 0xd3, 0x5f, 0xa7, 0xb3, 0xc1, 0x68, 0xa7, 0x07,
+ 0xa0, 0x64, 0x8a, 0x36, 0xe4, 0x0d, 0x38, 0x09, 0x6a, 0x7c, 0x75, 0xb7, 0xce, 0xf7, 0x07, 0xc9,
+ 0xd6, 0x46, 0x1f, 0x60, 0x9a, 0x25, 0x3e, 0x5f, 0xf8, 0xa6, 0x28, 0x71, 0xa7, 0x55, 0x5a, 0x49,
+ 0xb3, 0x64, 0xb2, 0x30, 0xeb, 0xc9, 0xc6, 0x8f, 0xe0, 0xec, 0xac, 0xf5, 0xf0, 0x73, 0xdb, 0xb0,
+ 0x3f, 0x9d, 0x4c, 0xc6, 0xfa, 0x5e, 0x58, 0x50, 0x1a, 0x75, 0xdf, 0x0f, 0xdc, 0xbd, 0xde, 0xe1,
+ 0xbb, 0xe2, 0x6f, 0xf9, 0xbf, 0xcf, 0xc7, 0x7f, 0xdf, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf1,
+ 0x41, 0x61, 0x5e, 0x0b, 0x07, 0x00, 0x00,
+}
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
new file mode 100644
index 0000000..c44e9f8
--- /dev/null
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -0,0 +1,145 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package build_metrics;
+option go_package = "metrics_proto";
+
+message MetricsBase {
+ // Timestamp generated when the build starts.
+ optional int64 build_date_timestamp = 1;
+
+ // It is usually used to specify the branch name [and release candidate].
+ optional string build_id = 2;
+
+ // The platform version codename, eg. P, Q, REL.
+ optional string platform_version_codename = 3;
+
+ // The target product information, eg. aosp_arm.
+ optional string target_product = 4;
+
+ enum BUILDVARIANT {
+ USER = 0;
+ USERDEBUG = 1;
+ ENG = 2;
+ }
+ // The target build variant information, eg. eng.
+ optional BUILDVARIANT target_build_variant = 5 [default = ENG];
+
+ enum ARCH {
+ UNKNOWN = 0;
+ ARM = 1;
+ ARM64 = 2;
+ X86 = 3;
+ X86_64 = 4;
+ }
+ // The target arch information, eg. arm.
+ optional ARCH target_arch = 6 [default = UNKNOWN];
+
+ // The target arch variant information, eg. armv7-a-neon.
+ optional string target_arch_variant = 7;
+
+ // The target cpu variant information, eg. generic.
+ optional string target_cpu_variant = 8;
+
+ // The host arch information, eg. x86_64.
+ optional ARCH host_arch = 9 [default = UNKNOWN];
+
+ // The host 2nd arch information, eg. x86.
+ optional ARCH host_2nd_arch = 10 [default = UNKNOWN];
+
+ // The host os information, eg. linux.
+ optional string host_os = 11;
+
+ // The host os extra information, eg. Linux-4.17.0-3rodete2-amd64-x86_64-Debian-GNU.
+ optional string host_os_extra = 12;
+
+ // The host cross os information, eg. windows.
+ optional string host_cross_os = 13;
+
+ // The host cross arch information, eg. x86.
+ optional string host_cross_arch = 14;
+
+ // The host cross 2nd arch information, eg. x86_64.
+ optional string host_cross_2nd_arch = 15;
+
+ // The directory for generated built artifacts installation, eg. out.
+ optional string out_dir = 16;
+
+ // The metrics for calling various tools (microfactory) before Soong_UI starts.
+ repeated PerfInfo setup_tools = 17;
+
+ // The metrics for calling Kati by multiple times.
+ repeated PerfInfo kati_runs = 18;
+
+ // The metrics for calling Soong.
+ repeated PerfInfo soong_runs = 19;
+
+ // The metrics for calling Ninja.
+ repeated PerfInfo ninja_runs = 20;
+
+ optional BuildConfig build_config = 23;
+
+ // The hostname of the machine.
+ optional string hostname = 24;
+
+ // The build command that the user entered to the build system.
+ optional string build_command = 26;
+}
+
+message BuildConfig {
+ optional bool use_goma = 1;
+
+ optional bool use_rbe = 2;
+
+ optional bool force_use_goma = 3;
+}
+
+message PerfInfo {
+ // The description for the phase/action/part while the tool running.
+ optional string desc = 1;
+
+ // The name for the running phase/action/part.
+ optional string name = 2;
+
+ // The absolute start time.
+ // The number of nanoseconds elapsed since January 1, 1970 UTC.
+ optional uint64 start_time = 3;
+
+ // The real running time.
+ // The number of nanoseconds elapsed since start_time.
+ optional uint64 real_time = 4;
+
+ // The number of MB for memory use.
+ optional uint64 memory_use = 5;
+}
+
+message ModuleTypeInfo {
+ enum BUILDSYSTEM {
+ UNKNOWN = 0;
+ SOONG = 1;
+ MAKE = 2;
+ }
+ // The build system, eg. Soong or Make.
+ optional BUILDSYSTEM build_system = 1 [default = UNKNOWN];
+
+ // The module type, eg. java_library, cc_binary, and etc.
+ optional string module_type = 2;
+
+ // The number of logical modules.
+ optional uint32 num_of_modules = 3;
+}
diff --git a/ui/metrics/metrics_proto/regen.sh b/ui/metrics/metrics_proto/regen.sh
new file mode 100755
index 0000000..343c638
--- /dev/null
+++ b/ui/metrics/metrics_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. metrics.proto
diff --git a/ui/metrics/time.go b/ui/metrics/time.go
new file mode 100644
index 0000000..c0de357
--- /dev/null
+++ b/ui/metrics/time.go
@@ -0,0 +1,69 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metrics
+
+import (
+ "time"
+
+ "android/soong/ui/metrics/metrics_proto"
+ "android/soong/ui/tracer"
+ "github.com/golang/protobuf/proto"
+)
+
+// for testing purpose only
+var _now = now
+
+type timeEvent struct {
+ desc string
+ name string
+
+ // the time that the event started to occur.
+ start time.Time
+}
+
+type TimeTracer interface {
+ Begin(name, desc string, thread tracer.Thread)
+ End(thread tracer.Thread) metrics_proto.PerfInfo
+}
+
+type timeTracerImpl struct {
+ activeEvents []timeEvent
+}
+
+var _ TimeTracer = &timeTracerImpl{}
+
+func now() time.Time {
+ return time.Now()
+}
+
+func (t *timeTracerImpl) Begin(name, desc string, _ tracer.Thread) {
+ t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, start: _now()})
+}
+
+func (t *timeTracerImpl) End(tracer.Thread) metrics_proto.PerfInfo {
+ if len(t.activeEvents) < 1 {
+ panic("Internal error: No pending events for endAt to end!")
+ }
+ lastEvent := t.activeEvents[len(t.activeEvents)-1]
+ t.activeEvents = t.activeEvents[:len(t.activeEvents)-1]
+ realTime := uint64(_now().Sub(lastEvent.start).Nanoseconds())
+
+ return metrics_proto.PerfInfo{
+ Desc: proto.String(lastEvent.desc),
+ Name: proto.String(lastEvent.name),
+ StartTime: proto.Uint64(uint64(lastEvent.start.UnixNano())),
+ RealTime: proto.Uint64(realTime),
+ }
+}
diff --git a/ui/metrics/time_test.go b/ui/metrics/time_test.go
new file mode 100644
index 0000000..d73080a
--- /dev/null
+++ b/ui/metrics/time_test.go
@@ -0,0 +1,42 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metrics
+
+import (
+ "testing"
+ "time"
+
+ "android/soong/ui/tracer"
+)
+
+func TestEnd(t *testing.T) {
+ startTime := time.Date(2020, time.July, 13, 13, 0, 0, 0, time.UTC)
+ dur := time.Nanosecond * 10
+ initialNow := _now
+ _now = func() time.Time { return startTime.Add(dur) }
+ defer func() { _now = initialNow }()
+
+ timeTracer := &timeTracerImpl{}
+ timeTracer.activeEvents = append(timeTracer.activeEvents, timeEvent{
+ desc: "test",
+ name: "test",
+ start: startTime,
+ })
+
+ perf := timeTracer.End(tracer.Thread(0))
+ if perf.GetRealTime() != uint64(dur.Nanoseconds()) {
+ t.Errorf("got %d, want %d nanoseconds for event duration", perf.GetRealTime(), dur.Nanoseconds())
+ }
+}
diff --git a/ui/metrics/upload_proto/regen.sh b/ui/metrics/upload_proto/regen.sh
new file mode 100755
index 0000000..4521df7
--- /dev/null
+++ b/ui/metrics/upload_proto/regen.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# Generates the golang source file of upload.proto file.
+
+set -e
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+ die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. upload.proto; then
+ die "build failed. ${error_msg}"
+fi
diff --git a/ui/metrics/upload_proto/upload.pb.go b/ui/metrics/upload_proto/upload.pb.go
new file mode 100644
index 0000000..614d4c7
--- /dev/null
+++ b/ui/metrics/upload_proto/upload.pb.go
@@ -0,0 +1,134 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: upload.proto
+
+package soong_metrics_upload_proto
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Upload struct {
+ // The timestamp in milliseconds that the build was created.
+ CreationTimestampMs *uint64 `protobuf:"varint,1,opt,name=creation_timestamp_ms,json=creationTimestampMs" json:"creation_timestamp_ms,omitempty"`
+ // The timestamp in milliseconds when the build was completed.
+ CompletionTimestampMs *uint64 `protobuf:"varint,2,opt,name=completion_timestamp_ms,json=completionTimestampMs" json:"completion_timestamp_ms,omitempty"`
+ // The branch name.
+ BranchName *string `protobuf:"bytes,3,opt,name=branch_name,json=branchName" json:"branch_name,omitempty"`
+ // The target name.
+ TargetName *string `protobuf:"bytes,4,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
+ // A list of metrics filepaths to upload.
+ MetricsFiles []string `protobuf:"bytes,5,rep,name=metrics_files,json=metricsFiles" json:"metrics_files,omitempty"`
+ // A list of directories to delete after the copy of metrics files
+ // is completed for uploading.
+ DirectoriesToDelete []string `protobuf:"bytes,6,rep,name=directories_to_delete,json=directoriesToDelete" json:"directories_to_delete,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Upload) Reset() { *m = Upload{} }
+func (m *Upload) String() string { return proto.CompactTextString(m) }
+func (*Upload) ProtoMessage() {}
+func (*Upload) Descriptor() ([]byte, []int) {
+ return fileDescriptor_91b94b655bd2a7e5, []int{0}
+}
+
+func (m *Upload) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Upload.Unmarshal(m, b)
+}
+func (m *Upload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Upload.Marshal(b, m, deterministic)
+}
+func (m *Upload) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Upload.Merge(m, src)
+}
+func (m *Upload) XXX_Size() int {
+ return xxx_messageInfo_Upload.Size(m)
+}
+func (m *Upload) XXX_DiscardUnknown() {
+ xxx_messageInfo_Upload.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Upload proto.InternalMessageInfo
+
+func (m *Upload) GetCreationTimestampMs() uint64 {
+ if m != nil && m.CreationTimestampMs != nil {
+ return *m.CreationTimestampMs
+ }
+ return 0
+}
+
+func (m *Upload) GetCompletionTimestampMs() uint64 {
+ if m != nil && m.CompletionTimestampMs != nil {
+ return *m.CompletionTimestampMs
+ }
+ return 0
+}
+
+func (m *Upload) GetBranchName() string {
+ if m != nil && m.BranchName != nil {
+ return *m.BranchName
+ }
+ return ""
+}
+
+func (m *Upload) GetTargetName() string {
+ if m != nil && m.TargetName != nil {
+ return *m.TargetName
+ }
+ return ""
+}
+
+func (m *Upload) GetMetricsFiles() []string {
+ if m != nil {
+ return m.MetricsFiles
+ }
+ return nil
+}
+
+func (m *Upload) GetDirectoriesToDelete() []string {
+ if m != nil {
+ return m.DirectoriesToDelete
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterType((*Upload)(nil), "soong_metrics_upload.Upload")
+}
+
+func init() {
+ proto.RegisterFile("upload.proto", fileDescriptor_91b94b655bd2a7e5)
+}
+
+var fileDescriptor_91b94b655bd2a7e5 = []byte{
+ // 230 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xb1, 0x4a, 0x04, 0x31,
+ 0x10, 0x86, 0xd9, 0xbb, 0xf3, 0xe0, 0xe2, 0xd9, 0xec, 0x79, 0x18, 0x44, 0x70, 0xd1, 0x66, 0x2b,
+ 0x0b, 0x0b, 0x1f, 0x40, 0xc4, 0x4e, 0x8b, 0xe5, 0x6c, 0x6c, 0x86, 0x98, 0x1d, 0xd7, 0x40, 0x92,
+ 0x09, 0xc9, 0xf8, 0x1c, 0xbe, 0xb2, 0x6c, 0xe2, 0xe2, 0x82, 0x76, 0xc3, 0xff, 0x7d, 0x7f, 0x31,
+ 0xbf, 0xd8, 0x7e, 0x06, 0x4b, 0xaa, 0xbf, 0x09, 0x91, 0x98, 0xea, 0xd3, 0x44, 0xe4, 0x07, 0x70,
+ 0xc8, 0xd1, 0xe8, 0x04, 0x85, 0x5d, 0x7d, 0x2d, 0xc4, 0xfa, 0x25, 0x9f, 0xf5, 0xad, 0xd8, 0xeb,
+ 0x88, 0x8a, 0x0d, 0x79, 0x60, 0xe3, 0x30, 0xb1, 0x72, 0x01, 0x5c, 0x92, 0x55, 0x53, 0xb5, 0xab,
+ 0x6e, 0x37, 0xc1, 0xc3, 0xc4, 0x9e, 0x52, 0x7d, 0x27, 0xce, 0x34, 0xb9, 0x60, 0xf1, 0x6f, 0x6b,
+ 0x91, 0x5b, 0xfb, 0x5f, 0x3c, 0xef, 0x5d, 0x8a, 0xe3, 0xb7, 0xa8, 0xbc, 0xfe, 0x00, 0xaf, 0x1c,
+ 0xca, 0x65, 0x53, 0xb5, 0x9b, 0x4e, 0x94, 0xe8, 0x59, 0x39, 0x1c, 0x05, 0x56, 0x71, 0x40, 0x2e,
+ 0xc2, 0xaa, 0x08, 0x25, 0xca, 0xc2, 0xb5, 0x38, 0x99, 0x5e, 0x79, 0x37, 0x16, 0x93, 0x3c, 0x6a,
+ 0x96, 0xed, 0xa6, 0xdb, 0xfe, 0x84, 0x8f, 0x63, 0x36, 0xbe, 0xd4, 0x9b, 0x88, 0x9a, 0x29, 0x1a,
+ 0x4c, 0xc0, 0x04, 0x3d, 0x5a, 0x64, 0x94, 0xeb, 0x2c, 0xef, 0x66, 0xf0, 0x40, 0x0f, 0x19, 0xdd,
+ 0x5f, 0xbc, 0x9e, 0xff, 0xb7, 0x14, 0xe4, 0x15, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x64, 0x04,
+ 0xa8, 0xf4, 0x54, 0x01, 0x00, 0x00,
+}
diff --git a/ui/metrics/upload_proto/upload.proto b/ui/metrics/upload_proto/upload.proto
new file mode 100644
index 0000000..bcd0ab2
--- /dev/null
+++ b/ui/metrics/upload_proto/upload.proto
@@ -0,0 +1,39 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+
+package soong_metrics_upload;
+option go_package = "soong_metrics_upload_proto";
+
+message Upload {
+ // The timestamp in milliseconds that the build was created.
+ optional uint64 creation_timestamp_ms = 1;
+
+ // The timestamp in milliseconds when the build was completed.
+ optional uint64 completion_timestamp_ms = 2;
+
+ // The branch name.
+ optional string branch_name = 3;
+
+ // The target name.
+ optional string target_name = 4;
+
+ // A list of metrics filepaths to upload.
+ repeated string metrics_files = 5;
+
+ // A list of directories to delete after the copy of metrics files
+ // is completed for uploading.
+ repeated string directories_to_delete = 6;
+}
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
new file mode 100644
index 0000000..901a713
--- /dev/null
+++ b/ui/status/Android.bp
@@ -0,0 +1,43 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+bootstrap_go_package {
+ name: "soong-ui-status",
+ pkgPath: "android/soong/ui/status",
+ deps: [
+ "golang-protobuf-proto",
+ "soong-ui-logger",
+ "soong-ui-status-ninja_frontend",
+ ],
+ srcs: [
+ "kati.go",
+ "log.go",
+ "ninja.go",
+ "status.go",
+ ],
+ testSrcs: [
+ "kati_test.go",
+ "ninja_test.go",
+ "status_test.go",
+ ],
+}
+
+bootstrap_go_package {
+ name: "soong-ui-status-ninja_frontend",
+ pkgPath: "android/soong/ui/status/ninja_frontend",
+ deps: ["golang-protobuf-proto"],
+ srcs: [
+ "ninja_frontend/frontend.pb.go",
+ ],
+}
diff --git a/ui/status/kati.go b/ui/status/kati.go
new file mode 100644
index 0000000..1485c8d
--- /dev/null
+++ b/ui/status/kati.go
@@ -0,0 +1,139 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package status
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var katiError = regexp.MustCompile(`^(\033\[1m)?[^ ]+:[0-9]+: (\033\[31m)?error:`)
+var katiIncludeRe = regexp.MustCompile(`^(\[(\d+)/(\d+)] )?((including [^ ]+|initializing (build|packaging) system|finishing (build|packaging) rules|writing (build|packaging) rules) ...)$`)
+var katiLogRe = regexp.MustCompile(`^\*kati\*: `)
+var katiNinjaMissing = regexp.MustCompile("^[^ ]+ is missing, regenerating...$")
+
+type katiOutputParser struct {
+ st ToolStatus
+
+ count int
+ total int
+ extra int
+
+ action *Action
+ buf strings.Builder
+ hasError bool
+}
+
+func (k *katiOutputParser) flushAction() {
+ if k.action == nil {
+ return
+ }
+
+ var err error
+ if k.hasError {
+ err = fmt.Errorf("makefile error")
+ }
+
+ k.st.FinishAction(ActionResult{
+ Action: k.action,
+ Output: k.buf.String(),
+ Error: err,
+ })
+
+ k.buf.Reset()
+ k.hasError = false
+}
+
+func (k *katiOutputParser) parseLine(line string) {
+ // Only put kati debug/stat lines in our verbose log
+ if katiLogRe.MatchString(line) {
+ k.st.Verbose(line)
+ return
+ }
+
+ if matches := katiIncludeRe.FindStringSubmatch(line); len(matches) > 0 {
+ k.flushAction()
+ k.count += 1
+
+ matches := katiIncludeRe.FindStringSubmatch(line)
+ if matches[2] != "" {
+ idx, err := strconv.Atoi(matches[2])
+
+ if err == nil && idx+k.extra != k.count {
+ k.extra = k.count - idx
+ k.st.SetTotalActions(k.total + k.extra)
+ }
+ } else {
+ k.extra += 1
+ k.st.SetTotalActions(k.total + k.extra)
+ }
+
+ if matches[3] != "" {
+ tot, err := strconv.Atoi(matches[3])
+
+ if err == nil && tot != k.total {
+ k.total = tot
+ k.st.SetTotalActions(k.total + k.extra)
+ }
+ }
+
+ k.action = &Action{
+ Description: matches[4],
+ }
+ k.st.StartAction(k.action)
+ } else if k.action != nil {
+ if katiError.MatchString(line) {
+ k.hasError = true
+ }
+ k.buf.WriteString(line)
+ k.buf.WriteString("\n")
+ } else {
+ // Before we've started executing actions from Kati
+ if line == "No need to regenerate ninja file" || katiNinjaMissing.MatchString(line) {
+ k.st.Status(line)
+ } else {
+ k.st.Print(line)
+ }
+ }
+}
+
+// KatiReader reads the output from Kati, and turns it into Actions and
+// messages that are passed into the ToolStatus API.
+func KatiReader(st ToolStatus, pipe io.ReadCloser) {
+ parser := &katiOutputParser{
+ st: st,
+ }
+
+ scanner := bufio.NewScanner(pipe)
+ scanner.Buffer(nil, 2*1024*1024)
+ for scanner.Scan() {
+ parser.parseLine(scanner.Text())
+ }
+
+ parser.flushAction()
+
+ if err := scanner.Err(); err != nil {
+ var buf strings.Builder
+ io.Copy(&buf, pipe)
+ st.Print(fmt.Sprintf("Error from kati parser: %s", err))
+ st.Print(buf.String())
+ }
+
+ st.Finish()
+}
diff --git a/ui/status/kati_test.go b/ui/status/kati_test.go
new file mode 100644
index 0000000..f2cb813
--- /dev/null
+++ b/ui/status/kati_test.go
@@ -0,0 +1,175 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package status
+
+import (
+ "testing"
+)
+
+type lastOutput struct {
+ counterOutput
+
+ action *Action
+ result ActionResult
+
+ msgLevel MsgLevel
+ msg string
+}
+
+func (l *lastOutput) StartAction(a *Action, c Counts) {
+ l.action = a
+ l.counterOutput.StartAction(a, c)
+}
+func (l *lastOutput) FinishAction(r ActionResult, c Counts) {
+ l.result = r
+ l.counterOutput.FinishAction(r, c)
+}
+func (l *lastOutput) Message(level MsgLevel, msg string) {
+ l.msgLevel = level
+ l.msg = msg
+}
+func (l *lastOutput) Flush() {}
+
+func TestKatiNormalCase(t *testing.T) {
+ status := &Status{}
+ output := &lastOutput{}
+ status.AddOutput(output)
+
+ parser := &katiOutputParser{
+ st: status.StartTool(),
+ }
+
+ msg := "*kati*: verbose msg"
+ parser.parseLine(msg)
+ output.Expect(t, Counts{})
+
+ if output.msgLevel != VerboseLvl {
+ t.Errorf("Expected verbose message, but got %d", output.msgLevel)
+ }
+ if output.msg != msg {
+ t.Errorf("unexpected message contents:\nwant: %q\n got: %q\n", msg, output.msg)
+ }
+
+ parser.parseLine("out/build-aosp_arm.ninja is missing, regenerating...")
+ output.Expect(t, Counts{})
+
+ parser.parseLine("[1/1] initializing build system ...")
+ output.Expect(t, Counts{
+ TotalActions: 1,
+ RunningActions: 1,
+ StartedActions: 1,
+ FinishedActions: 0,
+ })
+
+ parser.parseLine("[2/5] including out/soong/Android-aosp_arm.mk ...")
+ output.Expect(t, Counts{
+ TotalActions: 5,
+ RunningActions: 1,
+ StartedActions: 2,
+ FinishedActions: 1,
+ })
+
+ parser.parseLine("[3/5] including a ...")
+ msg = "a random message"
+ parser.parseLine(msg)
+
+ // Start the next line to flush the previous result
+ parser.parseLine("[4/5] finishing build rules ...")
+
+ msg += "\n"
+ if output.result.Output != msg {
+ t.Errorf("output for action did not match:\nwant: %q\n got: %q\n", msg, output.result.Output)
+ }
+
+ parser.parseLine("[5/5] writing build rules ...")
+ parser.parseLine("*kati*: verbose msg")
+ parser.flushAction()
+
+ if output.result.Output != "" {
+ t.Errorf("expected no output for last action, but got %q", output.result.Output)
+ }
+
+ output.Expect(t, Counts{
+ TotalActions: 5,
+ RunningActions: 0,
+ StartedActions: 5,
+ FinishedActions: 5,
+ })
+}
+
+func TestKatiExtraIncludes(t *testing.T) {
+ status := &Status{}
+ output := &lastOutput{}
+ status.AddOutput(output)
+
+ parser := &katiOutputParser{
+ st: status.StartTool(),
+ }
+
+ parser.parseLine("[1/1] initializing build system ...")
+ parser.parseLine("[2/5] including out/soong/Android-aosp_arm.mk ...")
+ output.Expect(t, Counts{
+ TotalActions: 5,
+ RunningActions: 1,
+ StartedActions: 2,
+ FinishedActions: 1,
+ })
+
+ parser.parseLine("including a ...")
+
+ output.Expect(t, Counts{
+ TotalActions: 6,
+ RunningActions: 1,
+ StartedActions: 3,
+ FinishedActions: 2,
+ })
+
+ parser.parseLine("including b ...")
+
+ output.Expect(t, Counts{
+ TotalActions: 7,
+ RunningActions: 1,
+ StartedActions: 4,
+ FinishedActions: 3,
+ })
+
+ parser.parseLine("[3/5] finishing build rules ...")
+
+ output.Expect(t, Counts{
+ TotalActions: 7,
+ RunningActions: 1,
+ StartedActions: 5,
+ FinishedActions: 4,
+ })
+}
+
+func TestKatiFailOnError(t *testing.T) {
+ status := &Status{}
+ output := &lastOutput{}
+ status.AddOutput(output)
+
+ parser := &katiOutputParser{
+ st: status.StartTool(),
+ }
+
+ parser.parseLine("[1/1] initializing build system ...")
+ parser.parseLine("[2/5] inclduing out/soong/Android-aosp_arm.mk ...")
+ parser.parseLine("build/make/tools/Android.mk:19: error: testing")
+ parser.flushAction()
+
+ if output.result.Error == nil {
+ t.Errorf("Expected the last action to be marked as an error")
+ }
+}
diff --git a/ui/status/log.go b/ui/status/log.go
new file mode 100644
index 0000000..921aa44
--- /dev/null
+++ b/ui/status/log.go
@@ -0,0 +1,136 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package status
+
+import (
+ "android/soong/ui/logger"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "strings"
+)
+
+type verboseLog struct {
+ w io.WriteCloser
+}
+
+func NewVerboseLog(log logger.Logger, filename string) StatusOutput {
+ if !strings.HasSuffix(filename, ".gz") {
+ filename += ".gz"
+ }
+
+ f, err := logger.CreateFileWithRotation(filename, 5)
+ if err != nil {
+ log.Println("Failed to create verbose log file:", err)
+ return nil
+ }
+
+ w := gzip.NewWriter(f)
+
+ return &verboseLog{
+ w: w,
+ }
+}
+
+func (v *verboseLog) StartAction(action *Action, counts Counts) {}
+
+func (v *verboseLog) FinishAction(result ActionResult, counts Counts) {
+ cmd := result.Command
+ if cmd == "" {
+ cmd = result.Description
+ }
+
+ fmt.Fprintf(v.w, "[%d/%d] %s\n", counts.FinishedActions, counts.TotalActions, cmd)
+
+ if result.Error != nil {
+ fmt.Fprintf(v.w, "FAILED: %s\n", strings.Join(result.Outputs, " "))
+ }
+
+ if result.Output != "" {
+ fmt.Fprintln(v.w, result.Output)
+ }
+}
+
+func (v *verboseLog) Flush() {
+ v.w.Close()
+}
+
+func (v *verboseLog) Message(level MsgLevel, message string) {
+ fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message)
+}
+
+type errorLog struct {
+ w io.WriteCloser
+
+ empty bool
+}
+
+func NewErrorLog(log logger.Logger, filename string) StatusOutput {
+ f, err := logger.CreateFileWithRotation(filename, 5)
+ if err != nil {
+ log.Println("Failed to create error log file:", err)
+ return nil
+ }
+
+ return &errorLog{
+ w: f,
+ empty: true,
+ }
+}
+
+func (e *errorLog) StartAction(action *Action, counts Counts) {}
+
+func (e *errorLog) FinishAction(result ActionResult, counts Counts) {
+ if result.Error == nil {
+ return
+ }
+
+ cmd := result.Command
+ if cmd == "" {
+ cmd = result.Description
+ }
+
+ if !e.empty {
+ fmt.Fprintf(e.w, "\n\n")
+ }
+ e.empty = false
+
+ fmt.Fprintf(e.w, "FAILED: %s\n", result.Description)
+ if len(result.Outputs) > 0 {
+ fmt.Fprintf(e.w, "Outputs: %s\n", strings.Join(result.Outputs, " "))
+ }
+ fmt.Fprintf(e.w, "Error: %s\n", result.Error)
+ if result.Command != "" {
+ fmt.Fprintf(e.w, "Command: %s\n", result.Command)
+ }
+ fmt.Fprintf(e.w, "Output:\n%s\n", result.Output)
+}
+
+func (e *errorLog) Flush() {
+ e.w.Close()
+}
+
+func (e *errorLog) Message(level MsgLevel, message string) {
+ if level < ErrorLvl {
+ return
+ }
+
+ if !e.empty {
+ fmt.Fprintf(e.w, "\n\n")
+ }
+ e.empty = false
+
+ fmt.Fprintf(e.w, "error: %s\n", message)
+}
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
new file mode 100644
index 0000000..ee2a2da
--- /dev/null
+++ b/ui/status/ninja.go
@@ -0,0 +1,207 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package status
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "syscall"
+ "time"
+
+ "github.com/golang/protobuf/proto"
+
+ "android/soong/ui/logger"
+ "android/soong/ui/status/ninja_frontend"
+)
+
+// NewNinjaReader reads the protobuf frontend format from ninja and translates it
+// into calls on the ToolStatus API.
+func NewNinjaReader(ctx logger.Logger, status ToolStatus, fifo string) *NinjaReader {
+ os.Remove(fifo)
+
+ err := syscall.Mkfifo(fifo, 0666)
+ if err != nil {
+ ctx.Fatalf("Failed to mkfifo(%q): %v", fifo, err)
+ }
+
+ n := &NinjaReader{
+ status: status,
+ fifo: fifo,
+ done: make(chan bool),
+ cancel: make(chan bool),
+ }
+
+ go n.run()
+
+ return n
+}
+
+type NinjaReader struct {
+ status ToolStatus
+ fifo string
+ done chan bool
+ cancel chan bool
+}
+
+const NINJA_READER_CLOSE_TIMEOUT = 5 * time.Second
+
+// Close waits for NinjaReader to finish reading from the fifo, or 5 seconds.
+func (n *NinjaReader) Close() {
+ // Signal the goroutine to stop if it is blocking opening the fifo.
+ close(n.cancel)
+
+ timeoutCh := time.After(NINJA_READER_CLOSE_TIMEOUT)
+
+ select {
+ case <-n.done:
+ // Nothing
+ case <-timeoutCh:
+ n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+ }
+
+ return
+}
+
+func (n *NinjaReader) run() {
+ defer close(n.done)
+
+ // Opening the fifo can block forever if ninja never opens the write end, do it in a goroutine so this
+ // method can exit on cancel.
+ fileCh := make(chan *os.File)
+ go func() {
+ f, err := os.Open(n.fifo)
+ if err != nil {
+ n.status.Error(fmt.Sprintf("Failed to open fifo: %v", err))
+ close(fileCh)
+ return
+ }
+ fileCh <- f
+ }()
+
+ var f *os.File
+
+ select {
+ case f = <-fileCh:
+ // Nothing
+ case <-n.cancel:
+ return
+ }
+
+ defer f.Close()
+
+ r := bufio.NewReader(f)
+
+ running := map[uint32]*Action{}
+
+ for {
+ size, err := readVarInt(r)
+ if err != nil {
+ if err != io.EOF {
+ n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+ }
+ return
+ }
+
+ buf := make([]byte, size)
+ _, err = io.ReadFull(r, buf)
+ if err != nil {
+ if err == io.EOF {
+ n.status.Print(fmt.Sprintf("Missing message of size %d from ninja\n", size))
+ } else {
+ n.status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+ }
+ return
+ }
+
+ msg := &ninja_frontend.Status{}
+ err = proto.Unmarshal(buf, msg)
+ if err != nil {
+ n.status.Print(fmt.Sprintf("Error reading message from ninja: %v", err))
+ continue
+ }
+
+ // Ignore msg.BuildStarted
+ if msg.TotalEdges != nil {
+ n.status.SetTotalActions(int(msg.TotalEdges.GetTotalEdges()))
+ }
+ if msg.EdgeStarted != nil {
+ action := &Action{
+ Description: msg.EdgeStarted.GetDesc(),
+ Outputs: msg.EdgeStarted.Outputs,
+ Command: msg.EdgeStarted.GetCommand(),
+ }
+ n.status.StartAction(action)
+ running[msg.EdgeStarted.GetId()] = action
+ }
+ if msg.EdgeFinished != nil {
+ if started, ok := running[msg.EdgeFinished.GetId()]; ok {
+ delete(running, msg.EdgeFinished.GetId())
+
+ var err error
+ exitCode := int(msg.EdgeFinished.GetStatus())
+ if exitCode != 0 {
+ err = fmt.Errorf("exited with code: %d", exitCode)
+ }
+
+ n.status.FinishAction(ActionResult{
+ Action: started,
+ Output: msg.EdgeFinished.GetOutput(),
+ Error: err,
+ })
+ }
+ }
+ if msg.Message != nil {
+ message := "ninja: " + msg.Message.GetMessage()
+ switch msg.Message.GetLevel() {
+ case ninja_frontend.Status_Message_INFO:
+ n.status.Status(message)
+ case ninja_frontend.Status_Message_WARNING:
+ n.status.Print("warning: " + message)
+ case ninja_frontend.Status_Message_ERROR:
+ n.status.Error(message)
+ default:
+ n.status.Print(message)
+ }
+ }
+ if msg.BuildFinished != nil {
+ n.status.Finish()
+ }
+ }
+}
+
+func readVarInt(r *bufio.Reader) (int, error) {
+ ret := 0
+ shift := uint(0)
+
+ for {
+ b, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+
+ ret += int(b&0x7f) << (shift * 7)
+ if b&0x80 == 0 {
+ break
+ }
+ shift += 1
+ if shift > 4 {
+ return 0, fmt.Errorf("Expected varint32 length-delimited message")
+ }
+ }
+
+ return ret, nil
+}
diff --git a/ui/status/ninja_frontend/README b/ui/status/ninja_frontend/README
new file mode 100644
index 0000000..8c4b451
--- /dev/null
+++ b/ui/status/ninja_frontend/README
@@ -0,0 +1,3 @@
+This comes from https://android.googlesource.com/platform/external/ninja/+/master/src/frontend.proto
+
+The only difference is the specification of a go_package. To regenerate frontend.pb.go, run regen.sh.
diff --git a/ui/status/ninja_frontend/frontend.pb.go b/ui/status/ninja_frontend/frontend.pb.go
new file mode 100644
index 0000000..7c05eed
--- /dev/null
+++ b/ui/status/ninja_frontend/frontend.pb.go
@@ -0,0 +1,510 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: frontend.proto
+
+package ninja_frontend
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Status_Message_Level int32
+
+const (
+ Status_Message_INFO Status_Message_Level = 0
+ Status_Message_WARNING Status_Message_Level = 1
+ Status_Message_ERROR Status_Message_Level = 2
+)
+
+var Status_Message_Level_name = map[int32]string{
+ 0: "INFO",
+ 1: "WARNING",
+ 2: "ERROR",
+}
+var Status_Message_Level_value = map[string]int32{
+ "INFO": 0,
+ "WARNING": 1,
+ "ERROR": 2,
+}
+
+func (x Status_Message_Level) Enum() *Status_Message_Level {
+ p := new(Status_Message_Level)
+ *p = x
+ return p
+}
+func (x Status_Message_Level) String() string {
+ return proto.EnumName(Status_Message_Level_name, int32(x))
+}
+func (x *Status_Message_Level) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(Status_Message_Level_value, data, "Status_Message_Level")
+ if err != nil {
+ return err
+ }
+ *x = Status_Message_Level(value)
+ return nil
+}
+func (Status_Message_Level) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5, 0}
+}
+
+type Status struct {
+ TotalEdges *Status_TotalEdges `protobuf:"bytes,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
+ BuildStarted *Status_BuildStarted `protobuf:"bytes,2,opt,name=build_started,json=buildStarted" json:"build_started,omitempty"`
+ BuildFinished *Status_BuildFinished `protobuf:"bytes,3,opt,name=build_finished,json=buildFinished" json:"build_finished,omitempty"`
+ EdgeStarted *Status_EdgeStarted `protobuf:"bytes,4,opt,name=edge_started,json=edgeStarted" json:"edge_started,omitempty"`
+ EdgeFinished *Status_EdgeFinished `protobuf:"bytes,5,opt,name=edge_finished,json=edgeFinished" json:"edge_finished,omitempty"`
+ Message *Status_Message `protobuf:"bytes,6,opt,name=message" json:"message,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Status) Reset() { *m = Status{} }
+func (m *Status) String() string { return proto.CompactTextString(m) }
+func (*Status) ProtoMessage() {}
+func (*Status) Descriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0}
+}
+func (m *Status) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Status.Unmarshal(m, b)
+}
+func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Status.Marshal(b, m, deterministic)
+}
+func (dst *Status) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Status.Merge(dst, src)
+}
+func (m *Status) XXX_Size() int {
+ return xxx_messageInfo_Status.Size(m)
+}
+func (m *Status) XXX_DiscardUnknown() {
+ xxx_messageInfo_Status.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status proto.InternalMessageInfo
+
+func (m *Status) GetTotalEdges() *Status_TotalEdges {
+ if m != nil {
+ return m.TotalEdges
+ }
+ return nil
+}
+
+func (m *Status) GetBuildStarted() *Status_BuildStarted {
+ if m != nil {
+ return m.BuildStarted
+ }
+ return nil
+}
+
+func (m *Status) GetBuildFinished() *Status_BuildFinished {
+ if m != nil {
+ return m.BuildFinished
+ }
+ return nil
+}
+
+func (m *Status) GetEdgeStarted() *Status_EdgeStarted {
+ if m != nil {
+ return m.EdgeStarted
+ }
+ return nil
+}
+
+func (m *Status) GetEdgeFinished() *Status_EdgeFinished {
+ if m != nil {
+ return m.EdgeFinished
+ }
+ return nil
+}
+
+func (m *Status) GetMessage() *Status_Message {
+ if m != nil {
+ return m.Message
+ }
+ return nil
+}
+
+type Status_TotalEdges struct {
+ // New value for total edges in the build.
+ TotalEdges *uint32 `protobuf:"varint,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Status_TotalEdges) Reset() { *m = Status_TotalEdges{} }
+func (m *Status_TotalEdges) String() string { return proto.CompactTextString(m) }
+func (*Status_TotalEdges) ProtoMessage() {}
+func (*Status_TotalEdges) Descriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 0}
+}
+func (m *Status_TotalEdges) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Status_TotalEdges.Unmarshal(m, b)
+}
+func (m *Status_TotalEdges) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Status_TotalEdges.Marshal(b, m, deterministic)
+}
+func (dst *Status_TotalEdges) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Status_TotalEdges.Merge(dst, src)
+}
+func (m *Status_TotalEdges) XXX_Size() int {
+ return xxx_messageInfo_Status_TotalEdges.Size(m)
+}
+func (m *Status_TotalEdges) XXX_DiscardUnknown() {
+ xxx_messageInfo_Status_TotalEdges.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_TotalEdges proto.InternalMessageInfo
+
+func (m *Status_TotalEdges) GetTotalEdges() uint32 {
+ if m != nil && m.TotalEdges != nil {
+ return *m.TotalEdges
+ }
+ return 0
+}
+
+type Status_BuildStarted struct {
+ // Number of jobs Ninja will run in parallel.
+ Parallelism *uint32 `protobuf:"varint,1,opt,name=parallelism" json:"parallelism,omitempty"`
+ // Verbose value passed to ninja.
+ Verbose *bool `protobuf:"varint,2,opt,name=verbose" json:"verbose,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Status_BuildStarted) Reset() { *m = Status_BuildStarted{} }
+func (m *Status_BuildStarted) String() string { return proto.CompactTextString(m) }
+func (*Status_BuildStarted) ProtoMessage() {}
+func (*Status_BuildStarted) Descriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 1}
+}
+func (m *Status_BuildStarted) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Status_BuildStarted.Unmarshal(m, b)
+}
+func (m *Status_BuildStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Status_BuildStarted.Marshal(b, m, deterministic)
+}
+func (dst *Status_BuildStarted) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Status_BuildStarted.Merge(dst, src)
+}
+func (m *Status_BuildStarted) XXX_Size() int {
+ return xxx_messageInfo_Status_BuildStarted.Size(m)
+}
+func (m *Status_BuildStarted) XXX_DiscardUnknown() {
+ xxx_messageInfo_Status_BuildStarted.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_BuildStarted proto.InternalMessageInfo
+
+func (m *Status_BuildStarted) GetParallelism() uint32 {
+ if m != nil && m.Parallelism != nil {
+ return *m.Parallelism
+ }
+ return 0
+}
+
+func (m *Status_BuildStarted) GetVerbose() bool {
+ if m != nil && m.Verbose != nil {
+ return *m.Verbose
+ }
+ return false
+}
+
+type Status_BuildFinished struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Status_BuildFinished) Reset() { *m = Status_BuildFinished{} }
+func (m *Status_BuildFinished) String() string { return proto.CompactTextString(m) }
+func (*Status_BuildFinished) ProtoMessage() {}
+func (*Status_BuildFinished) Descriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 2}
+}
+func (m *Status_BuildFinished) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Status_BuildFinished.Unmarshal(m, b)
+}
+func (m *Status_BuildFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Status_BuildFinished.Marshal(b, m, deterministic)
+}
+func (dst *Status_BuildFinished) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Status_BuildFinished.Merge(dst, src)
+}
+func (m *Status_BuildFinished) XXX_Size() int {
+ return xxx_messageInfo_Status_BuildFinished.Size(m)
+}
+func (m *Status_BuildFinished) XXX_DiscardUnknown() {
+ xxx_messageInfo_Status_BuildFinished.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_BuildFinished proto.InternalMessageInfo
+
+type Status_EdgeStarted struct {
+ // Edge identification number, unique to a Ninja run.
+ Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
+ // Edge start time in milliseconds since Ninja started.
+ StartTime *uint32 `protobuf:"varint,2,opt,name=start_time,json=startTime" json:"start_time,omitempty"`
+ // List of edge inputs.
+ Inputs []string `protobuf:"bytes,3,rep,name=inputs" json:"inputs,omitempty"`
+ // List of edge outputs.
+ Outputs []string `protobuf:"bytes,4,rep,name=outputs" json:"outputs,omitempty"`
+ // Description field from the edge.
+ Desc *string `protobuf:"bytes,5,opt,name=desc" json:"desc,omitempty"`
+ // Command field from the edge.
+ Command *string `protobuf:"bytes,6,opt,name=command" json:"command,omitempty"`
+ // Edge uses console.
+ Console *bool `protobuf:"varint,7,opt,name=console" json:"console,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Status_EdgeStarted) Reset() { *m = Status_EdgeStarted{} }
+func (m *Status_EdgeStarted) String() string { return proto.CompactTextString(m) }
+func (*Status_EdgeStarted) ProtoMessage() {}
+func (*Status_EdgeStarted) Descriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 3}
+}
+func (m *Status_EdgeStarted) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Status_EdgeStarted.Unmarshal(m, b)
+}
+func (m *Status_EdgeStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Status_EdgeStarted.Marshal(b, m, deterministic)
+}
+func (dst *Status_EdgeStarted) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Status_EdgeStarted.Merge(dst, src)
+}
+func (m *Status_EdgeStarted) XXX_Size() int {
+ return xxx_messageInfo_Status_EdgeStarted.Size(m)
+}
+func (m *Status_EdgeStarted) XXX_DiscardUnknown() {
+ xxx_messageInfo_Status_EdgeStarted.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_EdgeStarted proto.InternalMessageInfo
+
+func (m *Status_EdgeStarted) GetId() uint32 {
+ if m != nil && m.Id != nil {
+ return *m.Id
+ }
+ return 0
+}
+
+func (m *Status_EdgeStarted) GetStartTime() uint32 {
+ if m != nil && m.StartTime != nil {
+ return *m.StartTime
+ }
+ return 0
+}
+
+func (m *Status_EdgeStarted) GetInputs() []string {
+ if m != nil {
+ return m.Inputs
+ }
+ return nil
+}
+
+func (m *Status_EdgeStarted) GetOutputs() []string {
+ if m != nil {
+ return m.Outputs
+ }
+ return nil
+}
+
+func (m *Status_EdgeStarted) GetDesc() string {
+ if m != nil && m.Desc != nil {
+ return *m.Desc
+ }
+ return ""
+}
+
+func (m *Status_EdgeStarted) GetCommand() string {
+ if m != nil && m.Command != nil {
+ return *m.Command
+ }
+ return ""
+}
+
+func (m *Status_EdgeStarted) GetConsole() bool {
+ if m != nil && m.Console != nil {
+ return *m.Console
+ }
+ return false
+}
+
+type Status_EdgeFinished struct {
+ // Edge identification number, unique to a Ninja run.
+ Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
+ // Edge end time in milliseconds since Ninja started.
+ EndTime *uint32 `protobuf:"varint,2,opt,name=end_time,json=endTime" json:"end_time,omitempty"`
+ // Exit status (0 for success).
+ Status *int32 `protobuf:"zigzag32,3,opt,name=status" json:"status,omitempty"`
+ // Edge output, may contain ANSI codes.
+ Output *string `protobuf:"bytes,4,opt,name=output" json:"output,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Status_EdgeFinished) Reset() { *m = Status_EdgeFinished{} }
+func (m *Status_EdgeFinished) String() string { return proto.CompactTextString(m) }
+func (*Status_EdgeFinished) ProtoMessage() {}
+func (*Status_EdgeFinished) Descriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 4}
+}
+func (m *Status_EdgeFinished) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Status_EdgeFinished.Unmarshal(m, b)
+}
+func (m *Status_EdgeFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Status_EdgeFinished.Marshal(b, m, deterministic)
+}
+func (dst *Status_EdgeFinished) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Status_EdgeFinished.Merge(dst, src)
+}
+func (m *Status_EdgeFinished) XXX_Size() int {
+ return xxx_messageInfo_Status_EdgeFinished.Size(m)
+}
+func (m *Status_EdgeFinished) XXX_DiscardUnknown() {
+ xxx_messageInfo_Status_EdgeFinished.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_EdgeFinished proto.InternalMessageInfo
+
+func (m *Status_EdgeFinished) GetId() uint32 {
+ if m != nil && m.Id != nil {
+ return *m.Id
+ }
+ return 0
+}
+
+func (m *Status_EdgeFinished) GetEndTime() uint32 {
+ if m != nil && m.EndTime != nil {
+ return *m.EndTime
+ }
+ return 0
+}
+
+func (m *Status_EdgeFinished) GetStatus() int32 {
+ if m != nil && m.Status != nil {
+ return *m.Status
+ }
+ return 0
+}
+
+func (m *Status_EdgeFinished) GetOutput() string {
+ if m != nil && m.Output != nil {
+ return *m.Output
+ }
+ return ""
+}
+
+type Status_Message struct {
+ // Message priority level (INFO, WARNING, or ERROR).
+ Level *Status_Message_Level `protobuf:"varint,1,opt,name=level,enum=ninja.Status_Message_Level,def=0" json:"level,omitempty"`
+ // Info/warning/error message from Ninja.
+ Message *string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Status_Message) Reset() { *m = Status_Message{} }
+func (m *Status_Message) String() string { return proto.CompactTextString(m) }
+func (*Status_Message) ProtoMessage() {}
+func (*Status_Message) Descriptor() ([]byte, []int) {
+ return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5}
+}
+func (m *Status_Message) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Status_Message.Unmarshal(m, b)
+}
+func (m *Status_Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Status_Message.Marshal(b, m, deterministic)
+}
+func (dst *Status_Message) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Status_Message.Merge(dst, src)
+}
+func (m *Status_Message) XXX_Size() int {
+ return xxx_messageInfo_Status_Message.Size(m)
+}
+func (m *Status_Message) XXX_DiscardUnknown() {
+ xxx_messageInfo_Status_Message.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Status_Message proto.InternalMessageInfo
+
+const Default_Status_Message_Level Status_Message_Level = Status_Message_INFO
+
+func (m *Status_Message) GetLevel() Status_Message_Level {
+ if m != nil && m.Level != nil {
+ return *m.Level
+ }
+ return Default_Status_Message_Level
+}
+
+func (m *Status_Message) GetMessage() string {
+ if m != nil && m.Message != nil {
+ return *m.Message
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterType((*Status)(nil), "ninja.Status")
+ proto.RegisterType((*Status_TotalEdges)(nil), "ninja.Status.TotalEdges")
+ proto.RegisterType((*Status_BuildStarted)(nil), "ninja.Status.BuildStarted")
+ proto.RegisterType((*Status_BuildFinished)(nil), "ninja.Status.BuildFinished")
+ proto.RegisterType((*Status_EdgeStarted)(nil), "ninja.Status.EdgeStarted")
+ proto.RegisterType((*Status_EdgeFinished)(nil), "ninja.Status.EdgeFinished")
+ proto.RegisterType((*Status_Message)(nil), "ninja.Status.Message")
+ proto.RegisterEnum("ninja.Status_Message_Level", Status_Message_Level_name, Status_Message_Level_value)
+}
+
+func init() { proto.RegisterFile("frontend.proto", fileDescriptor_frontend_5a49d9b15a642005) }
+
+var fileDescriptor_frontend_5a49d9b15a642005 = []byte{
+ // 496 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0xd1, 0x6e, 0xd3, 0x30,
+ 0x14, 0xa5, 0x69, 0xd3, 0x34, 0x37, 0x6d, 0x28, 0x96, 0x40, 0x59, 0x10, 0xa2, 0xda, 0xd3, 0x78,
+ 0x20, 0x48, 0xbc, 0x20, 0x10, 0x12, 0xa2, 0xd2, 0x06, 0x43, 0xd0, 0x49, 0xde, 0x24, 0x24, 0x5e,
+ 0xaa, 0x74, 0xf6, 0x86, 0x51, 0xe2, 0x54, 0xb1, 0xbb, 0x5f, 0xe0, 0x7f, 0x78, 0xe0, 0xfb, 0x90,
+ 0xaf, 0xed, 0x2c, 0x65, 0x7b, 0xcb, 0xf1, 0x3d, 0xe7, 0xde, 0x73, 0x8f, 0x1d, 0x48, 0xaf, 0xda,
+ 0x46, 0x6a, 0x2e, 0x59, 0xb1, 0x6d, 0x1b, 0xdd, 0x90, 0x50, 0x0a, 0xf9, 0xab, 0x3c, 0xfc, 0x13,
+ 0xc1, 0xf8, 0x5c, 0x97, 0x7a, 0xa7, 0xc8, 0x5b, 0x48, 0x74, 0xa3, 0xcb, 0x6a, 0xcd, 0xd9, 0x35,
+ 0x57, 0xd9, 0x60, 0x31, 0x38, 0x4a, 0x5e, 0x67, 0x05, 0xf2, 0x0a, 0xcb, 0x29, 0x2e, 0x0c, 0xe1,
+ 0xd8, 0xd4, 0x29, 0xe8, 0xee, 0x9b, 0x7c, 0x80, 0xd9, 0x66, 0x27, 0x2a, 0xb6, 0x56, 0xba, 0x6c,
+ 0x35, 0x67, 0x59, 0x80, 0xe2, 0x7c, 0x5f, 0xbc, 0x34, 0x94, 0x73, 0xcb, 0xa0, 0xd3, 0x4d, 0x0f,
+ 0x91, 0x25, 0xa4, 0xb6, 0xc1, 0x95, 0x90, 0x42, 0xfd, 0xe4, 0x2c, 0x1b, 0x62, 0x87, 0xa7, 0xf7,
+ 0x74, 0x38, 0x71, 0x14, 0x6a, 0x67, 0x7a, 0x48, 0xde, 0xc3, 0xd4, 0x38, 0xef, 0x3c, 0x8c, 0xb0,
+ 0xc3, 0xc1, 0x7e, 0x07, 0xe3, 0xd7, 0x5b, 0x48, 0xf8, 0x2d, 0x30, 0x2b, 0xa0, 0xba, 0x33, 0x10,
+ 0xde, 0xb7, 0x82, 0x91, 0x77, 0xf3, 0x71, 0x5c, 0x37, 0xfe, 0x15, 0x44, 0x35, 0x57, 0xaa, 0xbc,
+ 0xe6, 0xd9, 0x18, 0xa5, 0x8f, 0xf7, 0xa5, 0xdf, 0x6c, 0x91, 0x7a, 0x56, 0xfe, 0x12, 0xe0, 0x36,
+ 0x4e, 0xf2, 0xfc, 0x6e, 0xfa, 0xb3, 0x7e, 0xc6, 0xf9, 0x17, 0x98, 0xf6, 0x03, 0x24, 0x0b, 0x48,
+ 0xb6, 0x65, 0x5b, 0x56, 0x15, 0xaf, 0x84, 0xaa, 0x9d, 0xa0, 0x7f, 0x44, 0x32, 0x88, 0x6e, 0x78,
+ 0xbb, 0x69, 0x14, 0xc7, 0xfb, 0x98, 0x50, 0x0f, 0xf3, 0x87, 0x30, 0xdb, 0x8b, 0x32, 0xff, 0x3b,
+ 0x80, 0xa4, 0x17, 0x0d, 0x49, 0x21, 0x10, 0xcc, 0xf5, 0x0c, 0x04, 0x23, 0xcf, 0x00, 0x30, 0xd6,
+ 0xb5, 0x16, 0xb5, 0xed, 0x36, 0xa3, 0x31, 0x9e, 0x5c, 0x88, 0x9a, 0x93, 0x27, 0x30, 0x16, 0x72,
+ 0xbb, 0xd3, 0x2a, 0x1b, 0x2e, 0x86, 0x47, 0x31, 0x75, 0xc8, 0x38, 0x68, 0x76, 0x1a, 0x0b, 0x23,
+ 0x2c, 0x78, 0x48, 0x08, 0x8c, 0x18, 0x57, 0x97, 0x98, 0x72, 0x4c, 0xf1, 0xdb, 0xb0, 0x2f, 0x9b,
+ 0xba, 0x2e, 0x25, 0xc3, 0x04, 0x63, 0xea, 0xa1, 0xad, 0x48, 0xd5, 0x54, 0x3c, 0x8b, 0xec, 0x26,
+ 0x0e, 0xe6, 0x02, 0xa6, 0xfd, 0x3b, 0xb9, 0x63, 0xfc, 0x00, 0x26, 0x5c, 0xb2, 0xbe, 0xed, 0x88,
+ 0x4b, 0xe6, 0x4d, 0x2b, 0xbc, 0x1a, 0x7c, 0x6b, 0x8f, 0xa8, 0x43, 0xe6, 0xdc, 0xba, 0xc4, 0x17,
+ 0x14, 0x53, 0x87, 0xf2, 0xdf, 0x03, 0x88, 0xdc, 0x25, 0x92, 0x37, 0x10, 0x56, 0xfc, 0x86, 0x57,
+ 0x38, 0x29, 0xfd, 0xff, 0x99, 0x3a, 0x56, 0xf1, 0xd5, 0x50, 0xde, 0x8d, 0x4e, 0x57, 0x27, 0x67,
+ 0xd4, 0xf2, 0xcd, 0x26, 0xfe, 0x95, 0x04, 0x76, 0x47, 0x07, 0x0f, 0x5f, 0x40, 0x88, 0x7c, 0x32,
+ 0x01, 0x54, 0xcc, 0x1f, 0x90, 0x04, 0xa2, 0xef, 0x1f, 0xe9, 0xea, 0x74, 0xf5, 0x69, 0x3e, 0x20,
+ 0x31, 0x84, 0xc7, 0x94, 0x9e, 0xd1, 0x79, 0xb0, 0x24, 0x9f, 0x87, 0x3f, 0x52, 0x9c, 0xb8, 0xf6,
+ 0x7f, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x8c, 0xef, 0xcb, 0xe0, 0x03, 0x00, 0x00,
+}
diff --git a/ui/status/ninja_frontend/frontend.proto b/ui/status/ninja_frontend/frontend.proto
new file mode 100644
index 0000000..13fd535
--- /dev/null
+++ b/ui/status/ninja_frontend/frontend.proto
@@ -0,0 +1,84 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package ninja;
+option go_package = "ninja_frontend";
+
+message Status {
+ message TotalEdges {
+ // New value for total edges in the build.
+ optional uint32 total_edges = 1;
+ }
+
+ message BuildStarted {
+ // Number of jobs Ninja will run in parallel.
+ optional uint32 parallelism = 1;
+ // Verbose value passed to ninja.
+ optional bool verbose = 2;
+ }
+
+ message BuildFinished {
+ }
+
+ message EdgeStarted {
+ // Edge identification number, unique to a Ninja run.
+ optional uint32 id = 1;
+ // Edge start time in milliseconds since Ninja started.
+ optional uint32 start_time = 2;
+ // List of edge inputs.
+ repeated string inputs = 3;
+ // List of edge outputs.
+ repeated string outputs = 4;
+ // Description field from the edge.
+ optional string desc = 5;
+ // Command field from the edge.
+ optional string command = 6;
+ // Edge uses console.
+ optional bool console = 7;
+ }
+
+ message EdgeFinished {
+ // Edge identification number, unique to a Ninja run.
+ optional uint32 id = 1;
+ // Edge end time in milliseconds since Ninja started.
+ optional uint32 end_time = 2;
+ // Exit status (0 for success).
+ optional sint32 status = 3;
+ // Edge output, may contain ANSI codes.
+ optional string output = 4;
+ }
+
+ message Message {
+ enum Level {
+ INFO = 0;
+ WARNING = 1;
+ ERROR = 2;
+ }
+ // Message priority level (INFO, WARNING, or ERROR).
+ optional Level level = 1 [default = INFO];
+ // Info/warning/error message from Ninja.
+ optional string message = 2;
+ }
+
+ optional TotalEdges total_edges = 1;
+ optional BuildStarted build_started = 2;
+ optional BuildFinished build_finished = 3;
+ optional EdgeStarted edge_started = 4;
+ optional EdgeFinished edge_finished = 5;
+ optional Message message = 6;
+}
diff --git a/ui/status/ninja_frontend/regen.sh b/ui/status/ninja_frontend/regen.sh
new file mode 100755
index 0000000..d270731
--- /dev/null
+++ b/ui/status/ninja_frontend/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. frontend.proto
diff --git a/ui/status/ninja_test.go b/ui/status/ninja_test.go
new file mode 100644
index 0000000..c400c97
--- /dev/null
+++ b/ui/status/ninja_test.go
@@ -0,0 +1,45 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package status
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "android/soong/ui/logger"
+)
+
+// Tests that closing the ninja reader when nothing has opened the other end of the fifo is fast.
+func TestNinjaReader_Close(t *testing.T) {
+ tempDir, err := ioutil.TempDir("", "ninja_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tempDir)
+
+ stat := &Status{}
+ nr := NewNinjaReader(logger.New(ioutil.Discard), stat.StartTool(), filepath.Join(tempDir, "fifo"))
+
+ start := time.Now()
+
+ nr.Close()
+
+ if g, w := time.Since(start), NINJA_READER_CLOSE_TIMEOUT; g >= w {
+ t.Errorf("nr.Close timed out, %s > %s", g, w)
+ }
+}
diff --git a/ui/status/status.go b/ui/status/status.go
new file mode 100644
index 0000000..46ec72e
--- /dev/null
+++ b/ui/status/status.go
@@ -0,0 +1,344 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package status tracks actions run by various tools, combining the counts
+// (total actions, currently running, started, finished), and giving that to
+// multiple outputs.
+package status
+
+import (
+ "sync"
+)
+
+// Action describes an action taken (or as Ninja calls them, Edges).
+type Action struct {
+ // Description is a shorter, more readable form of the command, meant
+ // for users. It's optional, but one of either Description or Command
+ // should be set.
+ Description string
+
+ // Outputs is the (optional) list of outputs. Usually these are files,
+ // but they can be any string.
+ Outputs []string
+
+ // Command is the actual command line executed to perform the action.
+ // It's optional, but one of either Description or Command should be
+ // set.
+ Command string
+}
+
+// ActionResult describes the result of running an Action.
+type ActionResult struct {
+ // Action is a pointer to the original Action struct.
+ *Action
+
+ // Output is the output produced by the command (usually stdout&stderr
+ // for Actions that run commands)
+ Output string
+
+ // Error is nil if the Action succeeded, or set to an error if it
+ // failed.
+ Error error
+}
+
+// Counts describes the number of actions in each state
+type Counts struct {
+ // TotalActions is the total number of expected changes. This can
+ // generally change up or down during a build, but it should never go
+ // below the number of StartedActions
+ TotalActions int
+
+ // RunningActions are the number of actions that are currently running
+ // -- the number that have called StartAction, but not FinishAction.
+ RunningActions int
+
+ // StartedActions are the number of actions that have been started with
+ // StartAction.
+ StartedActions int
+
+ // FinishedActions are the number of actions that have been finished
+ // with FinishAction.
+ FinishedActions int
+}
+
+// ToolStatus is the interface used by tools to report on their Actions, and to
+// present other information through a set of messaging functions.
+type ToolStatus interface {
+ // SetTotalActions sets the expected total number of actions that will
+ // be started by this tool.
+ //
+ // This call be will ignored if it sets a number that is less than the
+ // current number of started actions.
+ SetTotalActions(total int)
+
+ // StartAction specifies that the associated action has been started by
+ // the tool.
+ //
+ // A specific *Action should not be specified to StartAction more than
+ // once, even if the previous action has already been finished, and the
+ // contents rewritten.
+ //
+ // Do not re-use *Actions between different ToolStatus interfaces
+ // either.
+ StartAction(action *Action)
+
+ // FinishAction specifies the result of a particular Action.
+ //
+ // The *Action embedded in the ActionResult structure must have already
+ // been passed to StartAction (on this interface).
+ //
+ // Do not call FinishAction twice for the same *Action.
+ FinishAction(result ActionResult)
+
+ // Verbose takes a non-important message that is never printed to the
+ // screen, but is in the verbose build log, etc
+ Verbose(msg string)
+ // Status takes a less important message that may be printed to the
+ // screen, but overwritten by another status message. The full message
+ // will still appear in the verbose build log.
+ Status(msg string)
+ // Print takes an message and displays it to the screen and other
+ // output logs, etc.
+ Print(msg string)
+ // Error is similar to Print, but treats it similarly to a failed
+ // action, showing it in the error logs, etc.
+ Error(msg string)
+
+ // Finish marks the end of all Actions being run by this tool.
+ //
+ // SetTotalEdges, StartAction, and FinishAction should not be called
+ // after Finish.
+ Finish()
+}
+
+// MsgLevel specifies the importance of a particular log message. See the
+// descriptions in ToolStatus: Verbose, Status, Print, Error.
+type MsgLevel int
+
+const (
+ VerboseLvl MsgLevel = iota
+ StatusLvl
+ PrintLvl
+ ErrorLvl
+)
+
+func (l MsgLevel) Prefix() string {
+ switch l {
+ case VerboseLvl:
+ return "verbose: "
+ case StatusLvl:
+ return "status: "
+ case PrintLvl:
+ return ""
+ case ErrorLvl:
+ return "error: "
+ default:
+ panic("Unknown message level")
+ }
+}
+
+// StatusOutput is the interface used to get status information as a Status
+// output.
+//
+// All of the functions here are guaranteed to be called by Status while
+// holding it's internal lock, so it's safe to assume a single caller at any
+// time, and that the ordering of calls will be correct. It is not safe to call
+// back into the Status, or one of its ToolStatus interfaces.
+type StatusOutput interface {
+ // StartAction will be called once every time ToolStatus.StartAction is
+ // called. counts will include the current counters across all
+ // ToolStatus instances, including ones that have been finished.
+ StartAction(action *Action, counts Counts)
+
+ // FinishAction will be called once every time ToolStatus.FinishAction
+ // is called. counts will include the current counters across all
+ // ToolStatus instances, including ones that have been finished.
+ FinishAction(result ActionResult, counts Counts)
+
+ // Message is the equivalent of ToolStatus.Verbose/Status/Print/Error,
+ // but the level is specified as an argument.
+ Message(level MsgLevel, msg string)
+
+ // Flush is called when your outputs should be flushed / closed. No
+ // output is expected after this call.
+ Flush()
+}
+
+// Status is the multiplexer / accumulator between ToolStatus instances (via
+// StartTool) and StatusOutputs (via AddOutput). There's generally one of these
+// per build process (though tools like multiproduct_kati may have multiple
+// independent versions).
+type Status struct {
+ counts Counts
+ outputs []StatusOutput
+
+ // Protects counts and outputs, and allows each output to
+ // expect only a single caller at a time.
+ lock sync.Mutex
+}
+
+// AddOutput attaches an output to this object. It's generally expected that an
+// output is attached to a single Status instance.
+func (s *Status) AddOutput(output StatusOutput) {
+ if output == nil {
+ return
+ }
+
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.outputs = append(s.outputs, output)
+}
+
+// StartTool returns a new ToolStatus instance to report the status of a tool.
+func (s *Status) StartTool() ToolStatus {
+ return &toolStatus{
+ status: s,
+ }
+}
+
+// Finish will call Flush on all the outputs, generally flushing or closing all
+// of their outputs. Do not call any other functions on this instance or any
+// associated ToolStatus instances after this has been called.
+func (s *Status) Finish() {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ for _, o := range s.outputs {
+ o.Flush()
+ }
+}
+
+func (s *Status) updateTotalActions(diff int) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.counts.TotalActions += diff
+}
+
+func (s *Status) startAction(action *Action) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.counts.RunningActions += 1
+ s.counts.StartedActions += 1
+
+ for _, o := range s.outputs {
+ o.StartAction(action, s.counts)
+ }
+}
+
+func (s *Status) finishAction(result ActionResult) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.counts.RunningActions -= 1
+ s.counts.FinishedActions += 1
+
+ for _, o := range s.outputs {
+ o.FinishAction(result, s.counts)
+ }
+}
+
+func (s *Status) message(level MsgLevel, msg string) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ for _, o := range s.outputs {
+ o.Message(level, msg)
+ }
+}
+
+func (s *Status) Status(msg string) {
+ s.message(StatusLvl, msg)
+}
+
+type toolStatus struct {
+ status *Status
+
+ counts Counts
+ // Protects counts
+ lock sync.Mutex
+}
+
+var _ ToolStatus = (*toolStatus)(nil)
+
+func (d *toolStatus) SetTotalActions(total int) {
+ diff := 0
+
+ d.lock.Lock()
+ if total >= d.counts.StartedActions && total != d.counts.TotalActions {
+ diff = total - d.counts.TotalActions
+ d.counts.TotalActions = total
+ }
+ d.lock.Unlock()
+
+ if diff != 0 {
+ d.status.updateTotalActions(diff)
+ }
+}
+
+func (d *toolStatus) StartAction(action *Action) {
+ totalDiff := 0
+
+ d.lock.Lock()
+ d.counts.RunningActions += 1
+ d.counts.StartedActions += 1
+
+ if d.counts.StartedActions > d.counts.TotalActions {
+ totalDiff = d.counts.StartedActions - d.counts.TotalActions
+ d.counts.TotalActions = d.counts.StartedActions
+ }
+ d.lock.Unlock()
+
+ if totalDiff != 0 {
+ d.status.updateTotalActions(totalDiff)
+ }
+ d.status.startAction(action)
+}
+
+func (d *toolStatus) FinishAction(result ActionResult) {
+ d.lock.Lock()
+ d.counts.RunningActions -= 1
+ d.counts.FinishedActions += 1
+ d.lock.Unlock()
+
+ d.status.finishAction(result)
+}
+
+func (d *toolStatus) Verbose(msg string) {
+ d.status.message(VerboseLvl, msg)
+}
+func (d *toolStatus) Status(msg string) {
+ d.status.message(StatusLvl, msg)
+}
+func (d *toolStatus) Print(msg string) {
+ d.status.message(PrintLvl, msg)
+}
+func (d *toolStatus) Error(msg string) {
+ d.status.message(ErrorLvl, msg)
+}
+
+func (d *toolStatus) Finish() {
+ d.lock.Lock()
+ defer d.lock.Unlock()
+
+ if d.counts.TotalActions != d.counts.StartedActions {
+ d.status.updateTotalActions(d.counts.StartedActions - d.counts.TotalActions)
+ }
+
+ // TODO: update status to correct running/finished edges?
+ d.counts.RunningActions = 0
+ d.counts.TotalActions = d.counts.StartedActions
+}
diff --git a/ui/status/status_test.go b/ui/status/status_test.go
new file mode 100644
index 0000000..e62785f
--- /dev/null
+++ b/ui/status/status_test.go
@@ -0,0 +1,166 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package status
+
+import "testing"
+
+type counterOutput Counts
+
+func (c *counterOutput) StartAction(action *Action, counts Counts) {
+ *c = counterOutput(counts)
+}
+func (c *counterOutput) FinishAction(result ActionResult, counts Counts) {
+ *c = counterOutput(counts)
+}
+func (c counterOutput) Message(level MsgLevel, msg string) {}
+func (c counterOutput) Flush() {}
+
+func (c counterOutput) Expect(t *testing.T, counts Counts) {
+ if Counts(c) == counts {
+ return
+ }
+ t.Helper()
+
+ if c.TotalActions != counts.TotalActions {
+ t.Errorf("Expected %d total edges, but got %d", counts.TotalActions, c.TotalActions)
+ }
+ if c.RunningActions != counts.RunningActions {
+ t.Errorf("Expected %d running edges, but got %d", counts.RunningActions, c.RunningActions)
+ }
+ if c.StartedActions != counts.StartedActions {
+ t.Errorf("Expected %d started edges, but got %d", counts.StartedActions, c.StartedActions)
+ }
+ if c.FinishedActions != counts.FinishedActions {
+ t.Errorf("Expected %d finished edges, but got %d", counts.FinishedActions, c.FinishedActions)
+ }
+}
+
+func TestBasicUse(t *testing.T) {
+ status := &Status{}
+ counts := &counterOutput{}
+ status.AddOutput(counts)
+ s := status.StartTool()
+
+ s.SetTotalActions(2)
+
+ a := &Action{}
+ s.StartAction(a)
+
+ counts.Expect(t, Counts{
+ TotalActions: 2,
+ RunningActions: 1,
+ StartedActions: 1,
+ FinishedActions: 0,
+ })
+
+ s.FinishAction(ActionResult{Action: a})
+
+ counts.Expect(t, Counts{
+ TotalActions: 2,
+ RunningActions: 0,
+ StartedActions: 1,
+ FinishedActions: 1,
+ })
+
+ a = &Action{}
+ s.StartAction(a)
+
+ counts.Expect(t, Counts{
+ TotalActions: 2,
+ RunningActions: 1,
+ StartedActions: 2,
+ FinishedActions: 1,
+ })
+
+ s.FinishAction(ActionResult{Action: a})
+
+ counts.Expect(t, Counts{
+ TotalActions: 2,
+ RunningActions: 0,
+ StartedActions: 2,
+ FinishedActions: 2,
+ })
+}
+
+// For when a tool claims to have 2 actions, but finishes after one.
+func TestFinishEarly(t *testing.T) {
+ status := &Status{}
+ counts := &counterOutput{}
+ status.AddOutput(counts)
+ s := status.StartTool()
+
+ s.SetTotalActions(2)
+
+ a := &Action{}
+ s.StartAction(a)
+ s.FinishAction(ActionResult{Action: a})
+ s.Finish()
+
+ s = status.StartTool()
+ s.SetTotalActions(2)
+
+ a = &Action{}
+ s.StartAction(a)
+
+ counts.Expect(t, Counts{
+ TotalActions: 3,
+ RunningActions: 1,
+ StartedActions: 2,
+ FinishedActions: 1,
+ })
+}
+
+// For when a tool claims to have 1 action, but starts two.
+func TestExtraActions(t *testing.T) {
+ status := &Status{}
+ counts := &counterOutput{}
+ status.AddOutput(counts)
+ s := status.StartTool()
+
+ s.SetTotalActions(1)
+
+ s.StartAction(&Action{})
+ s.StartAction(&Action{})
+
+ counts.Expect(t, Counts{
+ TotalActions: 2,
+ RunningActions: 2,
+ StartedActions: 2,
+ FinishedActions: 0,
+ })
+}
+
+// When a tool calls Finish() with a running Action
+func TestRunningWhenFinished(t *testing.T) {
+ status := &Status{}
+ counts := &counterOutput{}
+ status.AddOutput(counts)
+
+ s := status.StartTool()
+ s.SetTotalActions(1)
+ s.StartAction(&Action{})
+ s.Finish()
+
+ s = status.StartTool()
+ s.SetTotalActions(1)
+ s.StartAction(&Action{})
+
+ counts.Expect(t, Counts{
+ TotalActions: 2,
+ RunningActions: 2,
+ StartedActions: 2,
+ FinishedActions: 0,
+ })
+}
diff --git a/cmd/symbol_inject/Android.bp b/ui/terminal/Android.bp
similarity index 63%
copy from cmd/symbol_inject/Android.bp
copy to ui/terminal/Android.bp
index a2ea12b..7104a50 100644
--- a/cmd/symbol_inject/Android.bp
+++ b/ui/terminal/Android.bp
@@ -12,21 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-blueprint_go_binary {
- name: "symbol_inject",
+bootstrap_go_package {
+ name: "soong-ui-terminal",
+ pkgPath: "android/soong/ui/terminal",
+ deps: ["soong-ui-status"],
srcs: [
- "symbol_inject.go",
- "elf.go",
- "macho.go",
- "pe.go",
+ "status.go",
+ "writer.go",
+ "util.go",
],
testSrcs: [
- "elf_symboldata_test.go",
- "elf_test.go",
- "macho_symboldata_test.go",
- "macho_test.go",
- "pe_symboldata_test.go",
- "pe_test.go",
- "symbol_inject_test.go",
+ "util_test.go",
],
+ darwin: {
+ srcs: [
+ "util_darwin.go",
+ ],
+ },
+ linux: {
+ srcs: [
+ "util_linux.go",
+ ],
+ },
}
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
new file mode 100644
index 0000000..2445c5b
--- /dev/null
+++ b/ui/terminal/status.go
@@ -0,0 +1,145 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package terminal
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "android/soong/ui/status"
+)
+
+type statusOutput struct {
+ writer Writer
+ format string
+
+ start time.Time
+ quiet bool
+}
+
+// NewStatusOutput returns a StatusOutput that represents the
+// current build status similarly to Ninja's built-in terminal
+// output.
+//
+// statusFormat takes nearly all the same options as NINJA_STATUS.
+// %c is currently unsupported.
+func NewStatusOutput(w Writer, statusFormat string, quietBuild bool) status.StatusOutput {
+ return &statusOutput{
+ writer: w,
+ format: statusFormat,
+
+ start: time.Now(),
+ quiet: quietBuild,
+ }
+}
+
+func (s *statusOutput) Message(level status.MsgLevel, message string) {
+ if level >= status.ErrorLvl {
+ s.writer.Print(fmt.Sprintf("FAILED: %s", message))
+ } else if level > status.StatusLvl {
+ s.writer.Print(fmt.Sprintf("%s%s", level.Prefix(), message))
+ } else if level == status.StatusLvl {
+ s.writer.StatusLine(message)
+ }
+}
+
+func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
+ if !s.writer.isSmartTerminal() {
+ return
+ }
+
+ str := action.Description
+ if str == "" {
+ str = action.Command
+ }
+
+ s.writer.StatusLine(s.progress(counts) + str)
+}
+
+func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+ str := result.Description
+ if str == "" {
+ str = result.Command
+ }
+
+ progress := s.progress(counts) + str
+
+ if result.Error != nil {
+ targets := strings.Join(result.Outputs, " ")
+ if s.quiet || result.Command == "" {
+ s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s", targets, result.Output))
+ } else {
+ s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output))
+ }
+ } else if result.Output != "" {
+ s.writer.StatusAndMessage(progress, result.Output)
+ } else {
+ s.writer.StatusLine(progress)
+ }
+}
+
+func (s *statusOutput) Flush() {}
+
+func (s *statusOutput) progress(counts status.Counts) string {
+ if s.format == "" {
+ return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
+ }
+
+ buf := &strings.Builder{}
+ for i := 0; i < len(s.format); i++ {
+ c := s.format[i]
+ if c != '%' {
+ buf.WriteByte(c)
+ continue
+ }
+
+ i = i + 1
+ if i == len(s.format) {
+ buf.WriteByte(c)
+ break
+ }
+
+ c = s.format[i]
+ switch c {
+ case '%':
+ buf.WriteByte(c)
+ case 's':
+ fmt.Fprintf(buf, "%d", counts.StartedActions)
+ case 't':
+ fmt.Fprintf(buf, "%d", counts.TotalActions)
+ case 'r':
+ fmt.Fprintf(buf, "%d", counts.RunningActions)
+ case 'u':
+ fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
+ case 'f':
+ fmt.Fprintf(buf, "%d", counts.FinishedActions)
+ case 'o':
+ fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
+ case 'c':
+ // TODO: implement?
+ buf.WriteRune('?')
+ case 'p':
+ fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
+ case 'e':
+ fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
+ default:
+ buf.WriteString("unknown placeholder '")
+ buf.WriteByte(c)
+ buf.WriteString("'")
+ }
+ }
+ return buf.String()
+}
diff --git a/ui/terminal/util.go b/ui/terminal/util.go
new file mode 100644
index 0000000..a85a517
--- /dev/null
+++ b/ui/terminal/util.go
@@ -0,0 +1,101 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package terminal
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func isTerminal(w io.Writer) bool {
+ if f, ok := w.(*os.File); ok {
+ var termios syscall.Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+ ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
+ 0, 0, 0)
+ return err == 0
+ }
+ return false
+}
+
+func termWidth(w io.Writer) (int, bool) {
+ if f, ok := w.(*os.File); ok {
+ var winsize struct {
+ ws_row, ws_column uint16
+ ws_xpixel, ws_ypixel uint16
+ }
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
+ syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
+ 0, 0, 0)
+ return int(winsize.ws_column), err == 0
+ }
+ return 0, false
+}
+
+// stripAnsiEscapes strips ANSI control codes from a byte array in place.
+func stripAnsiEscapes(input []byte) []byte {
+ // read represents the remaining part of input that needs to be processed.
+ read := input
+ // write represents where we should be writing in input.
+ // It will share the same backing store as input so that we make our modifications
+ // in place.
+ write := input
+
+ // advance will copy count bytes from read to write and advance those slices
+ advance := func(write, read []byte, count int) ([]byte, []byte) {
+ copy(write, read[:count])
+ return write[count:], read[count:]
+ }
+
+ for {
+ // Find the next escape sequence
+ i := bytes.IndexByte(read, 0x1b)
+ // If it isn't found, or if there isn't room for <ESC>[, finish
+ if i == -1 || i+1 >= len(read) {
+ copy(write, read)
+ break
+ }
+
+ // Not a CSI code, continue searching
+ if read[i+1] != '[' {
+ write, read = advance(write, read, i+1)
+ continue
+ }
+
+ // Found a CSI code, advance up to the <ESC>
+ write, read = advance(write, read, i)
+
+ // Find the end of the CSI code
+ i = bytes.IndexFunc(read, func(r rune) bool {
+ return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
+ })
+ if i == -1 {
+ // We didn't find the end of the code, just remove the rest
+ i = len(read) - 1
+ }
+
+ // Strip off the end marker too
+ i = i + 1
+
+ // Skip the reader forward and reduce final length by that amount
+ read = read[i:]
+ input = input[:len(input)-i]
+ }
+
+ return input
+}
diff --git a/ui/build/util_darwin.go b/ui/terminal/util_darwin.go
similarity index 97%
rename from ui/build/util_darwin.go
rename to ui/terminal/util_darwin.go
index 254a9b8..109a37f 100644
--- a/ui/build/util_darwin.go
+++ b/ui/terminal/util_darwin.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package build
+package terminal
import (
"syscall"
diff --git a/ui/build/util_linux.go b/ui/terminal/util_linux.go
similarity index 97%
rename from ui/build/util_linux.go
rename to ui/terminal/util_linux.go
index 0a4e1d2..0a3d9dd 100644
--- a/ui/build/util_linux.go
+++ b/ui/terminal/util_linux.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package build
+package terminal
import (
"syscall"
diff --git a/ui/terminal/util_test.go b/ui/terminal/util_test.go
new file mode 100644
index 0000000..82bde7c
--- /dev/null
+++ b/ui/terminal/util_test.go
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package terminal
+
+import (
+ "testing"
+)
+
+func TestStripAnsiEscapes(t *testing.T) {
+ testcases := []struct {
+ input string
+ output string
+ }{
+ {
+ "",
+ "",
+ },
+ {
+ "This is a test",
+ "This is a test",
+ },
+ {
+ "interrupted: \x1b[12",
+ "interrupted: ",
+ },
+ {
+ "other \x1bescape \x1b",
+ "other \x1bescape \x1b",
+ },
+ { // from pretty-error macro
+ "\x1b[1mart/Android.mk: \x1b[31merror:\x1b[0m\x1b[1m art: test error \x1b[0m",
+ "art/Android.mk: error: art: test error ",
+ },
+ { // from envsetup.sh make wrapper
+ "\x1b[0;31m#### make failed to build some targets (2 seconds) ####\x1b[00m",
+ "#### make failed to build some targets (2 seconds) ####",
+ },
+ { // from clang (via ninja testcase)
+ "\x1b[1maffixmgr.cxx:286:15: \x1b[0m\x1b[0;1;35mwarning: \x1b[0m\x1b[1musing the result... [-Wparentheses]\x1b[0m",
+ "affixmgr.cxx:286:15: warning: using the result... [-Wparentheses]",
+ },
+ }
+ for _, tc := range testcases {
+ got := string(stripAnsiEscapes([]byte(tc.input)))
+ if got != tc.output {
+ t.Errorf("output strings didn't match\n"+
+ "input: %#v\n"+
+ " want: %#v\n"+
+ " got: %#v", tc.input, tc.output, got)
+ }
+ }
+}
diff --git a/ui/terminal/writer.go b/ui/terminal/writer.go
new file mode 100644
index 0000000..ebe4b2a
--- /dev/null
+++ b/ui/terminal/writer.go
@@ -0,0 +1,229 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package terminal provides a set of interfaces that can be used to interact
+// with the terminal (including falling back when the terminal is detected to
+// be a redirect or other dumb terminal)
+package terminal
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "sync"
+)
+
+// Writer provides an interface to write temporary and permanent messages to
+// the terminal.
+//
+// The terminal is considered to be a dumb terminal if TERM==dumb, or if a
+// terminal isn't detected on stdout/stderr (generally because it's a pipe or
+// file). Dumb terminals will strip out all ANSI escape sequences, including
+// colors.
+type Writer interface {
+ // Print prints the string to the terminal, overwriting any current
+ // status being displayed.
+ //
+ // On a dumb terminal, the status messages will be kept.
+ Print(str string)
+
+ // Status prints the first line of the string to the terminal,
+ // overwriting any previous status line. Strings longer than the width
+ // of the terminal will be cut off.
+ //
+ // On a dumb terminal, previous status messages will remain, and the
+ // entire first line of the string will be printed.
+ StatusLine(str string)
+
+ // StatusAndMessage prints the first line of status to the terminal,
+ // similarly to StatusLine(), then prints the full msg below that. The
+ // status line is retained.
+ //
+ // There is guaranteed to be no other output in between the status and
+ // message.
+ StatusAndMessage(status, msg string)
+
+ // Finish ensures that the output ends with a newline (preserving any
+ // current status line that is current displayed).
+ //
+ // This does nothing on dumb terminals.
+ Finish()
+
+ // Write implements the io.Writer interface. This is primarily so that
+ // the logger can use this interface to print to stderr without
+ // breaking the other semantics of this interface.
+ //
+ // Try to use any of the other functions if possible.
+ Write(p []byte) (n int, err error)
+
+ isSmartTerminal() bool
+}
+
+// NewWriter creates a new Writer based on the stdio and the TERM
+// environment variable.
+func NewWriter(stdio StdioInterface) Writer {
+ w := &writerImpl{
+ stdio: stdio,
+
+ haveBlankLine: true,
+ }
+
+ if term, ok := os.LookupEnv("TERM"); ok && term != "dumb" {
+ w.smartTerminal = isTerminal(stdio.Stdout())
+ }
+ w.stripEscapes = !w.smartTerminal
+
+ return w
+}
+
+type writerImpl struct {
+ stdio StdioInterface
+
+ haveBlankLine bool
+
+ // Protecting the above, we assume that smartTerminal and stripEscapes
+ // does not change after initial setup.
+ lock sync.Mutex
+
+ smartTerminal bool
+ stripEscapes bool
+}
+
+func (w *writerImpl) isSmartTerminal() bool {
+ return w.smartTerminal
+}
+
+func (w *writerImpl) requestLine() {
+ if !w.haveBlankLine {
+ fmt.Fprintln(w.stdio.Stdout())
+ w.haveBlankLine = true
+ }
+}
+
+func (w *writerImpl) Print(str string) {
+ if w.stripEscapes {
+ str = string(stripAnsiEscapes([]byte(str)))
+ }
+
+ w.lock.Lock()
+ defer w.lock.Unlock()
+ w.print(str)
+}
+
+func (w *writerImpl) print(str string) {
+ if !w.haveBlankLine {
+ fmt.Fprint(w.stdio.Stdout(), "\r", "\x1b[K")
+ w.haveBlankLine = true
+ }
+ fmt.Fprint(w.stdio.Stdout(), str)
+ if len(str) == 0 || str[len(str)-1] != '\n' {
+ fmt.Fprint(w.stdio.Stdout(), "\n")
+ }
+}
+
+func (w *writerImpl) StatusLine(str string) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ w.statusLine(str)
+}
+
+func (w *writerImpl) statusLine(str string) {
+ if !w.smartTerminal {
+ fmt.Fprintln(w.stdio.Stdout(), str)
+ return
+ }
+
+ idx := strings.IndexRune(str, '\n')
+ if idx != -1 {
+ str = str[0:idx]
+ }
+
+ // Limit line width to the terminal width, otherwise we'll wrap onto
+ // another line and we won't delete the previous line.
+ //
+ // Run this on every line in case the window has been resized while
+ // we're printing. This could be optimized to only re-run when we get
+ // SIGWINCH if it ever becomes too time consuming.
+ if max, ok := termWidth(w.stdio.Stdout()); ok {
+ if len(str) > max {
+ // TODO: Just do a max. Ninja elides the middle, but that's
+ // more complicated and these lines aren't that important.
+ str = str[:max]
+ }
+ }
+
+ // Move to the beginning on the line, print the output, then clear
+ // the rest of the line.
+ fmt.Fprint(w.stdio.Stdout(), "\r", str, "\x1b[K")
+ w.haveBlankLine = false
+}
+
+func (w *writerImpl) StatusAndMessage(status, msg string) {
+ if w.stripEscapes {
+ msg = string(stripAnsiEscapes([]byte(msg)))
+ }
+
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ w.statusLine(status)
+ w.requestLine()
+ w.print(msg)
+}
+
+func (w *writerImpl) Finish() {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ w.requestLine()
+}
+
+func (w *writerImpl) Write(p []byte) (n int, err error) {
+ w.Print(string(p))
+ return len(p), nil
+}
+
+// StdioInterface represents a set of stdin/stdout/stderr Reader/Writers
+type StdioInterface interface {
+ Stdin() io.Reader
+ Stdout() io.Writer
+ Stderr() io.Writer
+}
+
+// StdioImpl uses the OS stdin/stdout/stderr to implement StdioInterface
+type StdioImpl struct{}
+
+func (StdioImpl) Stdin() io.Reader { return os.Stdin }
+func (StdioImpl) Stdout() io.Writer { return os.Stdout }
+func (StdioImpl) Stderr() io.Writer { return os.Stderr }
+
+var _ StdioInterface = StdioImpl{}
+
+type customStdio struct {
+ stdin io.Reader
+ stdout io.Writer
+ stderr io.Writer
+}
+
+func NewCustomStdio(stdin io.Reader, stdout, stderr io.Writer) StdioInterface {
+ return customStdio{stdin, stdout, stderr}
+}
+
+func (c customStdio) Stdin() io.Reader { return c.stdin }
+func (c customStdio) Stdout() io.Writer { return c.stdout }
+func (c customStdio) Stderr() io.Writer { return c.stderr }
+
+var _ StdioInterface = customStdio{}
diff --git a/ui/tracer/Android.bp b/ui/tracer/Android.bp
index 9729c7e..af588f1 100644
--- a/ui/tracer/Android.bp
+++ b/ui/tracer/Android.bp
@@ -15,10 +15,13 @@
bootstrap_go_package {
name: "soong-ui-tracer",
pkgPath: "android/soong/ui/tracer",
- deps: ["soong-ui-logger"],
+ deps: [
+ "soong-ui-logger",
+ "soong-ui-status",
+ ],
srcs: [
"microfactory.go",
- "ninja.go",
+ "status.go",
"tracer.go",
],
}
diff --git a/ui/tracer/microfactory.go b/ui/tracer/microfactory.go
index acb9be4..c4c37c2 100644
--- a/ui/tracer/microfactory.go
+++ b/ui/tracer/microfactory.go
@@ -17,10 +17,48 @@
import (
"bufio"
"os"
+ "sort"
"strconv"
"strings"
)
+type eventEntry struct {
+ Name string
+ Begin uint64
+ End uint64
+}
+
+func (t *tracerImpl) importEvents(entries []*eventEntry) {
+ sort.Slice(entries, func(i, j int) bool {
+ return entries[i].Begin < entries[j].Begin
+ })
+
+ cpus := []uint64{}
+ for _, entry := range entries {
+ tid := -1
+ for cpu, endTime := range cpus {
+ if endTime <= entry.Begin {
+ tid = cpu
+ cpus[cpu] = entry.End
+ break
+ }
+ }
+ if tid == -1 {
+ tid = len(cpus)
+ cpus = append(cpus, entry.End)
+ }
+
+ t.writeEvent(&viewerEvent{
+ Name: entry.Name,
+ Phase: "X",
+ Time: entry.Begin,
+ Dur: entry.End - entry.Begin,
+ Pid: 1,
+ Tid: uint64(tid),
+ })
+ }
+}
+
func (t *tracerImpl) ImportMicrofactoryLog(filename string) {
if _, err := os.Stat(filename); err != nil {
return
diff --git a/ui/tracer/ninja.go b/ui/tracer/ninja.go
deleted file mode 100644
index 1980559..0000000
--- a/ui/tracer/ninja.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tracer
-
-import (
- "bufio"
- "os"
- "sort"
- "strconv"
- "strings"
- "time"
-)
-
-type eventEntry struct {
- Name string
- Begin uint64
- End uint64
-}
-
-func (t *tracerImpl) importEvents(entries []*eventEntry) {
- sort.Slice(entries, func(i, j int) bool {
- return entries[i].Begin < entries[j].Begin
- })
-
- cpus := []uint64{}
- for _, entry := range entries {
- tid := -1
- for cpu, endTime := range cpus {
- if endTime <= entry.Begin {
- tid = cpu
- cpus[cpu] = entry.End
- break
- }
- }
- if tid == -1 {
- tid = len(cpus)
- cpus = append(cpus, entry.End)
- }
-
- t.writeEvent(&viewerEvent{
- Name: entry.Name,
- Phase: "X",
- Time: entry.Begin,
- Dur: entry.End - entry.Begin,
- Pid: 1,
- Tid: uint64(tid),
- })
- }
-}
-
-// ImportNinjaLog reads a .ninja_log file from ninja and writes the events out
-// to the trace.
-//
-// startOffset is when the ninja process started, and is used to position the
-// relative times from the ninja log into the trace. It's also used to skip
-// reading the ninja log if nothing was run.
-func (t *tracerImpl) ImportNinjaLog(thread Thread, filename string, startOffset time.Time) {
- t.Begin("ninja log import", thread)
- defer t.End(thread)
-
- if stat, err := os.Stat(filename); err != nil {
- t.log.Println("Missing ninja log:", err)
- return
- } else if stat.ModTime().Before(startOffset) {
- t.log.Verboseln("Ninja log not modified, not importing any entries.")
- return
- }
-
- f, err := os.Open(filename)
- if err != nil {
- t.log.Println("Error opening ninja log:", err)
- return
- }
- defer f.Close()
-
- s := bufio.NewScanner(f)
- header := true
- entries := []*eventEntry{}
- prevEnd := 0
- offset := uint64(startOffset.UnixNano()) / 1000
- for s.Scan() {
- if header {
- hdr := s.Text()
- if hdr != "# ninja log v5" {
- t.log.Printf("Unknown ninja log header: %q", hdr)
- return
- }
- header = false
- continue
- }
-
- fields := strings.Split(s.Text(), "\t")
- begin, err := strconv.Atoi(fields[0])
- if err != nil {
- t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
- return
- }
- end, err := strconv.Atoi(fields[1])
- if err != nil {
- t.log.Printf("Unable to parse ninja entry %q: %v", s.Text(), err)
- return
- }
- if end < prevEnd {
- entries = nil
- }
- prevEnd = end
- entries = append(entries, &eventEntry{
- Name: fields[3],
- Begin: offset + uint64(begin)*1000,
- End: offset + uint64(end)*1000,
- })
- }
- if err := s.Err(); err != nil {
- t.log.Println("Unable to parse ninja log:", err)
- return
- }
-
- t.importEvents(entries)
-}
diff --git a/ui/tracer/status.go b/ui/tracer/status.go
new file mode 100644
index 0000000..af50e2d
--- /dev/null
+++ b/ui/tracer/status.go
@@ -0,0 +1,87 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tracer
+
+import (
+ "android/soong/ui/status"
+ "time"
+)
+
+func (t *tracerImpl) StatusTracer() status.StatusOutput {
+ return &statusOutput{
+ tracer: t,
+
+ running: map[*status.Action]actionStatus{},
+ }
+}
+
+type actionStatus struct {
+ cpu int
+ start time.Time
+}
+
+type statusOutput struct {
+ tracer *tracerImpl
+
+ cpus []bool
+ running map[*status.Action]actionStatus
+}
+
+func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
+ cpu := -1
+ for i, busy := range s.cpus {
+ if !busy {
+ cpu = i
+ s.cpus[i] = true
+ break
+ }
+ }
+
+ if cpu == -1 {
+ cpu = len(s.cpus)
+ s.cpus = append(s.cpus, true)
+ }
+
+ s.running[action] = actionStatus{
+ cpu: cpu,
+ start: time.Now(),
+ }
+}
+
+func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+ start, ok := s.running[result.Action]
+ if !ok {
+ return
+ }
+ delete(s.running, result.Action)
+ s.cpus[start.cpu] = false
+
+ str := result.Action.Description
+ if len(result.Action.Outputs) > 0 {
+ str = result.Action.Outputs[0]
+ }
+
+ s.tracer.writeEvent(&viewerEvent{
+ Name: str,
+ Phase: "X",
+ Time: uint64(start.start.UnixNano()) / 1000,
+ Dur: uint64(time.Since(start.start).Nanoseconds()) / 1000,
+ Pid: 1,
+ Tid: uint64(start.cpu),
+ })
+}
+
+func (s *statusOutput) Flush() {}
+func (s *statusOutput) Message(level status.MsgLevel, message string) {}
diff --git a/ui/tracer/tracer.go b/ui/tracer/tracer.go
index 8705040..b8fc87b 100644
--- a/ui/tracer/tracer.go
+++ b/ui/tracer/tracer.go
@@ -31,6 +31,7 @@
"time"
"android/soong/ui/logger"
+ "android/soong/ui/status"
)
type Thread uint64
@@ -46,7 +47,8 @@
Complete(name string, thread Thread, begin, end uint64)
ImportMicrofactoryLog(filename string)
- ImportNinjaLog(thread Thread, filename string, startOffset time.Time)
+
+ StatusTracer() status.StatusOutput
NewThread(name string) Thread
}
diff --git a/xml/xml.go b/xml/xml.go
new file mode 100644
index 0000000..7c670fb
--- /dev/null
+++ b/xml/xml.go
@@ -0,0 +1,133 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package xml
+
+import (
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+// prebuilt_etc_xml installs an xml file under <partition>/etc/<subdir>.
+// It also optionally validates the xml file against the schema.
+
+var (
+ pctx = android.NewPackageContext("android/soong/xml")
+
+ xmllintDtd = pctx.AndroidStaticRule("xmllint-dtd",
+ blueprint.RuleParams{
+ Command: `$XmlLintCmd --dtdvalid $dtd $in > /dev/null && touch -a $out`,
+ CommandDeps: []string{"$XmlLintCmd"},
+ Restat: true,
+ },
+ "dtd")
+
+ xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd",
+ blueprint.RuleParams{
+ Command: `$XmlLintCmd --schema $xsd $in > /dev/null && touch -a $out`,
+ CommandDeps: []string{"$XmlLintCmd"},
+ Restat: true,
+ },
+ "xsd")
+
+ xmllintMinimal = pctx.AndroidStaticRule("xmllint-minimal",
+ blueprint.RuleParams{
+ Command: `$XmlLintCmd $in > /dev/null && touch -a $out`,
+ CommandDeps: []string{"$XmlLintCmd"},
+ Restat: true,
+ })
+)
+
+func init() {
+ android.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
+ pctx.HostBinToolVariable("XmlLintCmd", "xmllint")
+}
+
+type prebuiltEtcXmlProperties struct {
+ // Optional DTD that will be used to validate the xml file.
+ Schema *string `android:"path"`
+}
+
+type prebuiltEtcXml struct {
+ android.PrebuiltEtc
+
+ properties prebuiltEtcXmlProperties
+}
+
+func (p *prebuiltEtcXml) timestampFilePath(ctx android.ModuleContext) android.WritablePath {
+ return android.PathForModuleOut(ctx, p.PrebuiltEtc.SourceFilePath(ctx).Base()+"-timestamp")
+}
+
+func (p *prebuiltEtcXml) DepsMutator(ctx android.BottomUpMutatorContext) {
+ p.PrebuiltEtc.DepsMutator(ctx)
+}
+
+func (p *prebuiltEtcXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.PrebuiltEtc.GenerateAndroidBuildActions(ctx)
+
+ if p.properties.Schema != nil {
+ schema := android.PathForModuleSrc(ctx, proptools.String(p.properties.Schema))
+
+ switch schema.Ext() {
+ case ".dtd":
+ ctx.Build(pctx, android.BuildParams{
+ Rule: xmllintDtd,
+ Description: "xmllint-dtd",
+ Input: p.PrebuiltEtc.SourceFilePath(ctx),
+ Output: p.timestampFilePath(ctx),
+ Implicit: schema,
+ Args: map[string]string{
+ "dtd": schema.String(),
+ },
+ })
+ break
+ case ".xsd":
+ ctx.Build(pctx, android.BuildParams{
+ Rule: xmllintXsd,
+ Description: "xmllint-xsd",
+ Input: p.PrebuiltEtc.SourceFilePath(ctx),
+ Output: p.timestampFilePath(ctx),
+ Implicit: schema,
+ Args: map[string]string{
+ "xsd": schema.String(),
+ },
+ })
+ break
+ default:
+ ctx.PropertyErrorf("schema", "not supported extension: %q", schema.Ext())
+ }
+ } else {
+ // when schema is not specified, just check if the xml is well-formed
+ ctx.Build(pctx, android.BuildParams{
+ Rule: xmllintMinimal,
+ Description: "xmllint-minimal",
+ Input: p.PrebuiltEtc.SourceFilePath(ctx),
+ Output: p.timestampFilePath(ctx),
+ })
+ }
+
+ p.SetAdditionalDependencies([]android.Path{p.timestampFilePath(ctx)})
+}
+
+func PrebuiltEtcXmlFactory() android.Module {
+ module := &prebuiltEtcXml{}
+ module.AddProperties(&module.properties)
+
+ android.InitPrebuiltEtcModule(&module.PrebuiltEtc)
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
diff --git a/xml/xml_test.go b/xml/xml_test.go
new file mode 100644
index 0000000..e8fa49c
--- /dev/null
+++ b/xml/xml_test.go
@@ -0,0 +1,86 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package xml
+
+import (
+ "android/soong/android"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func testXml(t *testing.T, bp string) *android.TestContext {
+ config, buildDir := setup(t)
+ defer teardown(buildDir)
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory))
+ ctx.RegisterModuleType("prebuilt_etc_xml", android.ModuleFactoryAdaptor(PrebuiltEtcXmlFactory))
+ ctx.Register()
+ mockFiles := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "foo.xml": nil,
+ "foo.dtd": nil,
+ "bar.xml": nil,
+ "bar.xsd": nil,
+ }
+ ctx.MockFileSystem(mockFiles)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ return ctx
+}
+
+func setup(t *testing.T) (config android.Config, buildDir string) {
+ buildDir, err := ioutil.TempDir("", "soong_xml_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ config = android.TestArchConfig(buildDir, nil)
+
+ return
+}
+
+func teardown(buildDir string) {
+ os.RemoveAll(buildDir)
+}
+
+// Minimal test
+func TestPrebuiltEtcXml(t *testing.T) {
+ ctx := testXml(t, `
+ prebuilt_etc_xml {
+ name: "foo.xml",
+ src: "foo.xml",
+ schema: "foo.dtd",
+ }
+ prebuilt_etc_xml {
+ name: "bar.xml",
+ src: "bar.xml",
+ schema: "bar.xsd",
+ }
+ `)
+
+ xmllint := ctx.ModuleForTests("foo.xml", "android_common").Rule("xmllint")
+ input := xmllint.Input.String()
+ if input != "foo.xml" {
+ t.Errorf("input expected %q != got %q", "foo.xml", input)
+ }
+ schema := xmllint.Args["dtd"]
+ if schema != "foo.dtd" {
+ t.Errorf("dtd expected %q != got %q", "foo.dtdl", schema)
+ }
+}
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index 60017aa..b4f75f7 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -15,29 +15,19 @@
package main
import (
- "bytes"
"flag"
"fmt"
- "io"
"io/ioutil"
"os"
- "path/filepath"
"runtime"
+ "runtime/pprof"
+ "runtime/trace"
+ "strconv"
"strings"
"android/soong/zip"
)
-type byteReaderCloser struct {
- *bytes.Reader
- io.Closer
-}
-
-type pathMapping struct {
- dest, src string
- zipMethod uint16
-}
-
type uniqueSet map[string]bool
func (u *uniqueSet) String() string {
@@ -56,78 +46,67 @@
type file struct{}
+func (file) String() string { return `""` }
+
+func (file) Set(s string) error {
+ fileArgsBuilder.File(s)
+ return nil
+}
+
type listFiles struct{}
+func (listFiles) String() string { return `""` }
+
+func (listFiles) Set(s string) error {
+ fileArgsBuilder.List(s)
+ return nil
+}
+
type dir struct{}
-func (f *file) String() string {
- return `""`
-}
+func (dir) String() string { return `""` }
-func (f *file) Set(s string) error {
- if *relativeRoot == "" {
- return fmt.Errorf("must pass -C before -f")
- }
-
- fArgs = append(fArgs, zip.FileArg{
- PathPrefixInZip: filepath.Clean(*rootPrefix),
- SourcePrefixToStrip: filepath.Clean(*relativeRoot),
- SourceFiles: []string{s},
- })
-
+func (dir) Set(s string) error {
+ fileArgsBuilder.Dir(s)
return nil
}
-func (l *listFiles) String() string {
- return `""`
-}
+type relativeRoot struct{}
-func (l *listFiles) Set(s string) error {
- if *relativeRoot == "" {
- return fmt.Errorf("must pass -C before -l")
- }
+func (relativeRoot) String() string { return "" }
- list, err := ioutil.ReadFile(s)
- if err != nil {
- return err
- }
-
- fArgs = append(fArgs, zip.FileArg{
- PathPrefixInZip: filepath.Clean(*rootPrefix),
- SourcePrefixToStrip: filepath.Clean(*relativeRoot),
- SourceFiles: strings.Split(string(list), "\n"),
- })
-
+func (relativeRoot) Set(s string) error {
+ fileArgsBuilder.SourcePrefixToStrip(s)
return nil
}
-func (d *dir) String() string {
- return `""`
+type junkPaths struct{}
+
+func (junkPaths) IsBoolFlag() bool { return true }
+func (junkPaths) String() string { return "" }
+
+func (junkPaths) Set(s string) error {
+ v, err := strconv.ParseBool(s)
+ fileArgsBuilder.JunkPaths(v)
+ return err
}
-func (d *dir) Set(s string) error {
- if *relativeRoot == "" {
- return fmt.Errorf("must pass -C before -D")
- }
+type rootPrefix struct{}
- fArgs = append(fArgs, zip.FileArg{
- PathPrefixInZip: filepath.Clean(*rootPrefix),
- SourcePrefixToStrip: filepath.Clean(*relativeRoot),
- GlobDir: filepath.Clean(s),
- })
+func (rootPrefix) String() string { return "" }
+func (rootPrefix) Set(s string) error {
+ fileArgsBuilder.PathPrefixInZip(s)
return nil
}
var (
- rootPrefix, relativeRoot *string
-
- fArgs zip.FileArgs
+ fileArgsBuilder = zip.NewFileArgsBuilder()
nonDeflatedFiles = make(uniqueSet)
)
func usage() {
- fmt.Fprintf(os.Stderr, "usage: zip -o zipfile [-m manifest] -C dir [-f|-l file]...\n")
+ fmt.Fprintf(os.Stderr, "usage: soong_zip -o zipfile [-m manifest] [-C dir] [-f|-l file] [-D dir]...\n")
flag.PrintDefaults()
os.Exit(2)
}
@@ -149,32 +128,70 @@
}
flags := flag.NewFlagSet("flags", flag.ExitOnError)
+ flags.Usage = usage
out := flags.String("o", "", "file to write zip file to")
manifest := flags.String("m", "", "input jar manifest file name")
directories := flags.Bool("d", false, "include directories in zip")
- rootPrefix = flags.String("P", "", "path prefix within the zip at which to place files")
- relativeRoot = flags.String("C", "", "path to use as relative root of files in following -f, -l, or -D arguments")
- parallelJobs := flags.Int("j", runtime.NumCPU(), "number of parallel threads to use")
compLevel := flags.Int("L", 5, "deflate compression level (0-9)")
emulateJar := flags.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
+ ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "continue if a requested file does not exist")
+ symlinks := flags.Bool("symlinks", true, "store symbolic links in zip instead of following them")
+ parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
traceFile := flags.String("trace", "", "write trace to file")
+ flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
flags.Var(&listFiles{}, "l", "file containing list of .class files")
flags.Var(&dir{}, "D", "directory to include in zip")
flags.Var(&file{}, "f", "file to include in zip")
flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
+ flags.Var(&relativeRoot{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
+ flags.Var(&junkPaths{}, "j", "junk paths, zip files without directory names")
flags.Parse(expandedArgs[1:])
- err := zip.Run(zip.ZipArgs{
- FileArgs: fArgs,
+ if flags.NArg() > 0 {
+ fmt.Fprintf(os.Stderr, "unexpected arguments %s\n", strings.Join(flags.Args(), " "))
+ flags.Usage()
+ }
+
+ if *cpuProfile != "" {
+ f, err := os.Create(*cpuProfile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ defer f.Close()
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+
+ if *traceFile != "" {
+ f, err := os.Create(*traceFile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ defer f.Close()
+ err = trace.Start(f)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ defer trace.Stop()
+ }
+
+ if fileArgsBuilder.Error() != nil {
+ fmt.Fprintln(os.Stderr, fileArgsBuilder.Error())
+ os.Exit(1)
+ }
+
+ err := zip.Zip(zip.ZipArgs{
+ FileArgs: fileArgsBuilder.FileArgs(),
OutputFilePath: *out,
- CpuProfileFilePath: *cpuProfile,
- TraceFilePath: *traceFile,
EmulateJar: *emulateJar,
AddDirectoryEntriesToZip: *directories,
CompressionLevel: *compLevel,
@@ -182,9 +199,11 @@
NumParallelJobs: *parallelJobs,
NonDeflatedFiles: nonDeflatedFiles,
WriteIfChanged: *writeIfChanged,
+ StoreSymlinks: *symlinks,
+ IgnoreMissingFiles: *ignoreMissingFiles,
})
if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
+ fmt.Fprintln(os.Stderr, "error:", err.Error())
os.Exit(1)
}
}
diff --git a/zip/zip.go b/zip/zip.go
index b7e3764..1f5fe43 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -22,14 +22,12 @@
"hash/crc32"
"io"
"io/ioutil"
- "log"
"os"
"path/filepath"
- "runtime/pprof"
- "runtime/trace"
"sort"
"strings"
"sync"
+ "syscall"
"time"
"unicode"
@@ -68,29 +66,112 @@
zipMethod uint16
}
-type uniqueSet map[string]bool
-
-func (u *uniqueSet) String() string {
- return `""`
-}
-
-func (u *uniqueSet) Set(s string) error {
- if _, found := (*u)[s]; found {
- return fmt.Errorf("File %q was specified twice as a file to not deflate", s)
- } else {
- (*u)[s] = true
- }
-
- return nil
-}
-
type FileArg struct {
PathPrefixInZip, SourcePrefixToStrip string
SourceFiles []string
+ JunkPaths bool
GlobDir string
}
-type FileArgs []FileArg
+type FileArgsBuilder struct {
+ state FileArg
+ err error
+ fs pathtools.FileSystem
+
+ fileArgs []FileArg
+}
+
+func NewFileArgsBuilder() *FileArgsBuilder {
+ return &FileArgsBuilder{
+ fs: pathtools.OsFs,
+ }
+}
+
+func (b *FileArgsBuilder) JunkPaths(v bool) *FileArgsBuilder {
+ b.state.JunkPaths = v
+ b.state.SourcePrefixToStrip = ""
+ return b
+}
+
+func (b *FileArgsBuilder) SourcePrefixToStrip(prefixToStrip string) *FileArgsBuilder {
+ b.state.JunkPaths = false
+ b.state.SourcePrefixToStrip = prefixToStrip
+ return b
+}
+
+func (b *FileArgsBuilder) PathPrefixInZip(rootPrefix string) *FileArgsBuilder {
+ b.state.PathPrefixInZip = rootPrefix
+ return b
+}
+
+func (b *FileArgsBuilder) File(name string) *FileArgsBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ arg := b.state
+ arg.SourceFiles = []string{name}
+ b.fileArgs = append(b.fileArgs, arg)
+ return b
+}
+
+func (b *FileArgsBuilder) Dir(name string) *FileArgsBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ arg := b.state
+ arg.GlobDir = name
+ b.fileArgs = append(b.fileArgs, arg)
+ return b
+}
+
+func (b *FileArgsBuilder) List(name string) *FileArgsBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ f, err := b.fs.Open(name)
+ if err != nil {
+ b.err = err
+ return b
+ }
+ defer f.Close()
+
+ list, err := ioutil.ReadAll(f)
+ if err != nil {
+ b.err = err
+ return b
+ }
+
+ arg := b.state
+ arg.SourceFiles = strings.Split(string(list), "\n")
+ b.fileArgs = append(b.fileArgs, arg)
+ return b
+}
+
+func (b *FileArgsBuilder) Error() error {
+ if b == nil {
+ return nil
+ }
+ return b.err
+}
+
+func (b *FileArgsBuilder) FileArgs() []FileArg {
+ if b == nil {
+ return nil
+ }
+ return b.fileArgs
+}
+
+type IncorrectRelativeRootError struct {
+ RelativeRoot string
+ Path string
+}
+
+func (x IncorrectRelativeRootError) Error() string {
+ return fmt.Sprintf("path %q is outside relative root %q", x.Path, x.RelativeRoot)
+}
type ZipWriter struct {
time time.Time
@@ -106,6 +187,12 @@
compressorPool sync.Pool
compLevel int
+
+ followSymlinks pathtools.ShouldFollowSymlinks
+ ignoreMissingFiles bool
+
+ stderr io.Writer
+ fs pathtools.FileSystem
}
type zipEntry struct {
@@ -120,10 +207,8 @@
}
type ZipArgs struct {
- FileArgs FileArgs
+ FileArgs []FileArg
OutputFilePath string
- CpuProfileFilePath string
- TraceFilePath string
EmulateJar bool
AddDirectoryEntriesToZip bool
CompressionLevel int
@@ -131,6 +216,11 @@
NumParallelJobs int
NonDeflatedFiles map[string]bool
WriteIfChanged bool
+ StoreSymlinks bool
+ IgnoreMissingFiles bool
+
+ Stderr io.Writer
+ Filesystem pathtools.FileSystem
}
const NOQUOTE = '\x00'
@@ -176,63 +266,112 @@
return args
}
-func Run(args ZipArgs) (err error) {
- if args.CpuProfileFilePath != "" {
- f, err := os.Create(args.CpuProfileFilePath)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- defer f.Close()
- pprof.StartCPUProfile(f)
- defer pprof.StopCPUProfile()
- }
-
- if args.TraceFilePath != "" {
- f, err := os.Create(args.TraceFilePath)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- defer f.Close()
- err = trace.Start(f)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- defer trace.Stop()
- }
-
- if args.OutputFilePath == "" {
- return fmt.Errorf("output file path must be nonempty")
- }
-
+func ZipTo(args ZipArgs, w io.Writer) error {
if args.EmulateJar {
args.AddDirectoryEntriesToZip = true
}
- w := &ZipWriter{
- time: jar.DefaultTime,
- createdDirs: make(map[string]string),
- createdFiles: make(map[string]string),
- directories: args.AddDirectoryEntriesToZip,
- compLevel: args.CompressionLevel,
+ // Have Glob follow symlinks if they are not being stored as symlinks in the zip file.
+ followSymlinks := pathtools.ShouldFollowSymlinks(!args.StoreSymlinks)
+
+ z := &ZipWriter{
+ time: jar.DefaultTime,
+ createdDirs: make(map[string]string),
+ createdFiles: make(map[string]string),
+ directories: args.AddDirectoryEntriesToZip,
+ compLevel: args.CompressionLevel,
+ followSymlinks: followSymlinks,
+ ignoreMissingFiles: args.IgnoreMissingFiles,
+ stderr: args.Stderr,
+ fs: args.Filesystem,
}
+
+ if z.fs == nil {
+ z.fs = pathtools.OsFs
+ }
+
+ if z.stderr == nil {
+ z.stderr = os.Stderr
+ }
+
pathMappings := []pathMapping{}
+ noCompression := args.CompressionLevel == 0
+
for _, fa := range args.FileArgs {
- srcs := fa.SourceFiles
+ var srcs []string
+ for _, s := range fa.SourceFiles {
+ s = strings.TrimSpace(s)
+ if s == "" {
+ continue
+ }
+
+ globbed, _, err := z.fs.Glob(s, nil, followSymlinks)
+ if err != nil {
+ return err
+ }
+ if len(globbed) == 0 {
+ err := &os.PathError{
+ Op: "lstat",
+ Path: s,
+ Err: os.ErrNotExist,
+ }
+ if args.IgnoreMissingFiles {
+ fmt.Fprintln(z.stderr, "warning:", err)
+ } else {
+ return err
+ }
+ }
+ srcs = append(srcs, globbed...)
+ }
if fa.GlobDir != "" {
- srcs = append(srcs, recursiveGlobFiles(fa.GlobDir)...)
+ if exists, isDir, err := z.fs.Exists(fa.GlobDir); err != nil {
+ return err
+ } else if !exists && !args.IgnoreMissingFiles {
+ err := &os.PathError{
+ Op: "lstat",
+ Path: fa.GlobDir,
+ Err: os.ErrNotExist,
+ }
+ if args.IgnoreMissingFiles {
+ fmt.Fprintln(z.stderr, "warning:", err)
+ } else {
+ return err
+ }
+ } else if !isDir && !args.IgnoreMissingFiles {
+ err := &os.PathError{
+ Op: "lstat",
+ Path: fa.GlobDir,
+ Err: syscall.ENOTDIR,
+ }
+ if args.IgnoreMissingFiles {
+ fmt.Fprintln(z.stderr, "warning:", err)
+ } else {
+ return err
+ }
+ }
+ globbed, _, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
+ if err != nil {
+ return err
+ }
+ srcs = append(srcs, globbed...)
}
for _, src := range srcs {
- if err := fillPathPairs(fa.PathPrefixInZip,
- fa.SourcePrefixToStrip, src, &pathMappings, args.NonDeflatedFiles); err != nil {
- log.Fatal(err)
+ err := fillPathPairs(fa, src, &pathMappings, args.NonDeflatedFiles, noCompression)
+ if err != nil {
+ return err
}
}
}
+ return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
+}
+
+func Zip(args ZipArgs) error {
+ if args.OutputFilePath == "" {
+ return fmt.Errorf("output file path must be nonempty")
+ }
+
buf := &bytes.Buffer{}
var out io.Writer = buf
@@ -252,7 +391,7 @@
out = f
}
- err = w.write(out, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
+ err := ZipTo(args, out)
if err != nil {
return err
}
@@ -267,20 +406,31 @@
return nil
}
-func fillPathPairs(prefix, rel, src string, pathMappings *[]pathMapping, nonDeflatedFiles map[string]bool) error {
- src = strings.TrimSpace(src)
- if src == "" {
- return nil
+func fillPathPairs(fa FileArg, src string, pathMappings *[]pathMapping,
+ nonDeflatedFiles map[string]bool, noCompression bool) error {
+
+ var dest string
+
+ if fa.JunkPaths {
+ dest = filepath.Base(src)
+ } else {
+ var err error
+ dest, err = filepath.Rel(fa.SourcePrefixToStrip, src)
+ if err != nil {
+ return err
+ }
+ if strings.HasPrefix(dest, "../") {
+ return IncorrectRelativeRootError{
+ Path: src,
+ RelativeRoot: fa.SourcePrefixToStrip,
+ }
+ }
+
}
- src = filepath.Clean(src)
- dest, err := filepath.Rel(rel, src)
- if err != nil {
- return err
- }
- dest = filepath.Join(prefix, dest)
+ dest = filepath.Join(fa.PathPrefixInZip, dest)
zipMethod := zip.Deflate
- if _, found := nonDeflatedFiles[dest]; found {
+ if _, found := nonDeflatedFiles[dest]; found || noCompression {
zipMethod = zip.Store
}
*pathMappings = append(*pathMappings,
@@ -296,13 +446,6 @@
sort.SliceStable(mappings, less)
}
-type readerSeekerCloser interface {
- io.Reader
- io.ReaderAt
- io.Closer
- io.Seeker
-}
-
func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string, emulateJar bool, parallelJobs int) error {
z.errors = make(chan error)
defer close(z.errors)
@@ -449,7 +592,19 @@
var fileSize int64
var executable bool
- if s, err := os.Lstat(src); err != nil {
+ var s os.FileInfo
+ var err error
+ if z.followSymlinks {
+ s, err = z.fs.Stat(src)
+ } else {
+ s, err = z.fs.Lstat(src)
+ }
+
+ if err != nil {
+ if os.IsNotExist(err) && z.ignoreMissingFiles {
+ fmt.Fprintln(z.stderr, "warning:", err)
+ return nil
+ }
return err
} else if s.IsDir() {
if z.directories {
@@ -480,7 +635,7 @@
executable = s.Mode()&0100 != 0
}
- r, err := os.Open(src)
+ r, err := z.fs.Open(src)
if err != nil {
return err
}
@@ -510,7 +665,21 @@
return err
}
- fh, buf, err := jar.ManifestFileContents(src)
+ var contents []byte
+ if src != "" {
+ f, err := z.fs.Open(src)
+ if err != nil {
+ return err
+ }
+
+ contents, err = ioutil.ReadAll(f)
+ f.Close()
+ if err != nil {
+ return err
+ }
+ }
+
+ fh, buf, err := jar.ManifestFileContents(contents)
if err != nil {
return err
}
@@ -520,7 +689,7 @@
return z.writeFileContents(fh, reader)
}
-func (z *ZipWriter) writeFileContents(header *zip.FileHeader, r readerSeekerCloser) (err error) {
+func (z *ZipWriter) writeFileContents(header *zip.FileHeader, r pathtools.ReaderAtSeekerCloser) (err error) {
header.SetModTime(z.time)
@@ -788,13 +957,16 @@
Name: rel,
}
fileHeader.SetModTime(z.time)
- fileHeader.SetMode(0700 | os.ModeSymlink)
+ fileHeader.SetMode(0777 | os.ModeSymlink)
- dest, err := os.Readlink(file)
+ dest, err := z.fs.Readlink(file)
if err != nil {
return err
}
+ fileHeader.UncompressedSize64 = uint64(len(dest))
+ fileHeader.CRC32 = crc32.ChecksumIEEE([]byte(dest))
+
ze := make(chan *zipEntry, 1)
futureReaders := make(chan chan io.Reader, 1)
futureReader := make(chan io.Reader, 1)
@@ -812,15 +984,3 @@
return nil
}
-
-func recursiveGlobFiles(path string) []string {
- var files []string
- filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
- if !info.IsDir() {
- files = append(files, path)
- }
- return nil
- })
-
- return files
-}
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 03e7958..93c5f3d 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -15,10 +15,479 @@
package zip
import (
+ "bytes"
+ "hash/crc32"
+ "io"
+ "os"
"reflect"
+ "syscall"
"testing"
+
+ "android/soong/third_party/zip"
+
+ "github.com/google/blueprint/pathtools"
)
+var (
+ fileA = []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
+ fileB = []byte("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")
+ fileC = []byte("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")
+ fileEmpty = []byte("")
+ fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
+
+ fileCustomManifest = []byte("Custom manifest: true\n")
+ customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
+)
+
+var mockFs = pathtools.MockFs(map[string][]byte{
+ "a/a/a": fileA,
+ "a/a/b": fileB,
+ "a/a/c -> ../../c": nil,
+ "a/a/d -> b": nil,
+ "c": fileC,
+ "l": []byte("a/a/a\na/a/b\nc\n"),
+ "l2": []byte("missing\n"),
+ "manifest.txt": fileCustomManifest,
+})
+
+func fh(name string, contents []byte, method uint16) zip.FileHeader {
+ return zip.FileHeader{
+ Name: name,
+ Method: method,
+ CRC32: crc32.ChecksumIEEE(contents),
+ UncompressedSize64: uint64(len(contents)),
+ ExternalAttrs: 0,
+ }
+}
+
+func fhManifest(contents []byte) zip.FileHeader {
+ return zip.FileHeader{
+ Name: "META-INF/MANIFEST.MF",
+ Method: zip.Store,
+ CRC32: crc32.ChecksumIEEE(contents),
+ UncompressedSize64: uint64(len(contents)),
+ ExternalAttrs: (syscall.S_IFREG | 0700) << 16,
+ }
+}
+
+func fhLink(name string, to string) zip.FileHeader {
+ return zip.FileHeader{
+ Name: name,
+ Method: zip.Store,
+ CRC32: crc32.ChecksumIEEE([]byte(to)),
+ UncompressedSize64: uint64(len(to)),
+ ExternalAttrs: (syscall.S_IFLNK | 0777) << 16,
+ }
+}
+
+func fhDir(name string) zip.FileHeader {
+ return zip.FileHeader{
+ Name: name,
+ Method: zip.Store,
+ CRC32: crc32.ChecksumIEEE(nil),
+ UncompressedSize64: 0,
+ ExternalAttrs: (syscall.S_IFDIR|0700)<<16 | 0x10,
+ }
+}
+
+func fileArgsBuilder() *FileArgsBuilder {
+ return &FileArgsBuilder{
+ fs: mockFs,
+ }
+}
+
+func TestZip(t *testing.T) {
+ testCases := []struct {
+ name string
+ args *FileArgsBuilder
+ compressionLevel int
+ emulateJar bool
+ nonDeflatedFiles map[string]bool
+ dirEntries bool
+ manifest string
+ storeSymlinks bool
+ ignoreMissingFiles bool
+
+ files []zip.FileHeader
+ err error
+ }{
+ {
+ name: "empty args",
+ args: fileArgsBuilder(),
+
+ files: []zip.FileHeader{},
+ },
+ {
+ name: "files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("c"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fh("c", fileC, zip.Deflate),
+ },
+ },
+ {
+ name: "files glob",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ File("a/**/*"),
+ compressionLevel: 9,
+ storeSymlinks: true,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("a/b", fileB, zip.Deflate),
+ fhLink("a/c", "../../c"),
+ fhLink("a/d", "b"),
+ },
+ },
+ {
+ name: "dir",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ Dir("a"),
+ compressionLevel: 9,
+ storeSymlinks: true,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("a/b", fileB, zip.Deflate),
+ fhLink("a/c", "../../c"),
+ fhLink("a/d", "b"),
+ },
+ },
+ {
+ name: "stored files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("c"),
+ compressionLevel: 0,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Store),
+ fh("a/a/b", fileB, zip.Store),
+ fh("c", fileC, zip.Store),
+ },
+ },
+ {
+ name: "symlinks in zip",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("a/a/c").
+ File("a/a/d"),
+ compressionLevel: 9,
+ storeSymlinks: true,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fhLink("a/a/c", "../../c"),
+ fhLink("a/a/d", "b"),
+ },
+ },
+ {
+ name: "follow symlinks",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("a/a/c").
+ File("a/a/d"),
+ compressionLevel: 9,
+ storeSymlinks: false,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fh("a/a/c", fileC, zip.Deflate),
+ fh("a/a/d", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "list",
+ args: fileArgsBuilder().
+ List("l"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fh("c", fileC, zip.Deflate),
+ },
+ },
+ {
+ name: "prefix in zip",
+ args: fileArgsBuilder().
+ PathPrefixInZip("foo").
+ File("a/a/a").
+ File("a/a/b").
+ File("c"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("foo/a/a/a", fileA, zip.Deflate),
+ fh("foo/a/a/b", fileB, zip.Deflate),
+ fh("foo/c", fileC, zip.Deflate),
+ },
+ },
+ {
+ name: "relative root",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "multiple relative root",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ File("a/a/a").
+ SourcePrefixToStrip("a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "emulate jar",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ emulateJar: true,
+
+ files: []zip.FileHeader{
+ fhDir("META-INF/"),
+ fhManifest(fileManifest),
+ fhDir("a/"),
+ fhDir("a/a/"),
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "emulate jar with manifest",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ emulateJar: true,
+ manifest: "manifest.txt",
+
+ files: []zip.FileHeader{
+ fhDir("META-INF/"),
+ fhManifest(customManifestAfter),
+ fhDir("a/"),
+ fhDir("a/a/"),
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "dir entries",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ dirEntries: true,
+
+ files: []zip.FileHeader{
+ fhDir("a/"),
+ fhDir("a/a/"),
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "junk paths",
+ args: fileArgsBuilder().
+ JunkPaths(true).
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a", fileA, zip.Deflate),
+ fh("b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "non deflated files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ nonDeflatedFiles: map[string]bool{"a/a/a": true},
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Store),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "ignore missing files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("missing"),
+ compressionLevel: 9,
+ ignoreMissingFiles: true,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+
+ // errors
+ {
+ name: "error missing file",
+ args: fileArgsBuilder().
+ File("missing"),
+ err: os.ErrNotExist,
+ },
+ {
+ name: "error missing dir",
+ args: fileArgsBuilder().
+ Dir("missing"),
+ err: os.ErrNotExist,
+ },
+ {
+ name: "error missing file in list",
+ args: fileArgsBuilder().
+ List("l2"),
+ err: os.ErrNotExist,
+ },
+ {
+ name: "error incorrect relative root",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("b").
+ File("a/a/a"),
+ err: IncorrectRelativeRootError{},
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ if test.args.Error() != nil {
+ t.Fatal(test.args.Error())
+ }
+
+ args := ZipArgs{}
+ args.FileArgs = test.args.FileArgs()
+ args.CompressionLevel = test.compressionLevel
+ args.EmulateJar = test.emulateJar
+ args.AddDirectoryEntriesToZip = test.dirEntries
+ args.NonDeflatedFiles = test.nonDeflatedFiles
+ args.ManifestSourcePath = test.manifest
+ args.StoreSymlinks = test.storeSymlinks
+ args.IgnoreMissingFiles = test.ignoreMissingFiles
+ args.Filesystem = mockFs
+ args.Stderr = &bytes.Buffer{}
+
+ buf := &bytes.Buffer{}
+ err := ZipTo(args, buf)
+
+ if (err != nil) != (test.err != nil) {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ } else if test.err != nil {
+ if os.IsNotExist(test.err) {
+ if !os.IsNotExist(test.err) {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ }
+ } else if _, wantRelativeRootErr := test.err.(IncorrectRelativeRootError); wantRelativeRootErr {
+ if _, gotRelativeRootErr := err.(IncorrectRelativeRootError); !gotRelativeRootErr {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ }
+ } else {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ }
+ return
+ }
+
+ br := bytes.NewReader(buf.Bytes())
+ zr, err := zip.NewReader(br, int64(br.Len()))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var files []zip.FileHeader
+ for _, f := range zr.File {
+ r, err := f.Open()
+ if err != nil {
+ t.Fatalf("error when opening %s: %s", f.Name, err)
+ }
+
+ crc := crc32.NewIEEE()
+ len, err := io.Copy(crc, r)
+ r.Close()
+ if err != nil {
+ t.Fatalf("error when reading %s: %s", f.Name, err)
+ }
+
+ if uint64(len) != f.UncompressedSize64 {
+ t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len)
+ }
+
+ if crc.Sum32() != f.CRC32 {
+ t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc)
+ }
+
+ files = append(files, f.FileHeader)
+ }
+
+ if len(files) != len(test.files) {
+ t.Fatalf("want %d files, got %d", len(test.files), len(files))
+ }
+
+ for i := range files {
+ want := test.files[i]
+ got := files[i]
+
+ if want.Name != got.Name {
+ t.Errorf("incorrect file %d want %q got %q", i, want.Name, got.Name)
+ continue
+ }
+
+ if want.UncompressedSize64 != got.UncompressedSize64 {
+ t.Errorf("incorrect file %s length want %v got %v", want.Name,
+ want.UncompressedSize64, got.UncompressedSize64)
+ }
+
+ if want.ExternalAttrs != got.ExternalAttrs {
+ t.Errorf("incorrect file %s attrs want %x got %x", want.Name,
+ want.ExternalAttrs, got.ExternalAttrs)
+ }
+
+ if want.CRC32 != got.CRC32 {
+ t.Errorf("incorrect file %s crc want %v got %v", want.Name,
+ want.CRC32, got.CRC32)
+ }
+
+ if want.Method != got.Method {
+ t.Errorf("incorrect file %s method want %v got %v", want.Name,
+ want.Method, got.Method)
+ }
+ }
+ })
+ }
+}
+
func TestReadRespFile(t *testing.T) {
testCases := []struct {
name, in string