Merge pie-platform-release to aosp-master - DO NOT MERGE
Change-Id: I5f442bcf523e436413b269743b25052bde76c501
diff --git a/Android.bp b/Android.bp
index e9b1ebe..5faefe6 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/makevars.go",
"android/module.go",
@@ -56,6 +58,7 @@
"android/package_ctx.go",
"android/paths.go",
"android/prebuilt.go",
+ "android/prebuilt_etc.go",
"android/proto.go",
"android/register.go",
"android/singleton.go",
@@ -118,6 +121,7 @@
"soong-android",
"soong-cc-config",
"soong-genrule",
+ "soong-tradefed",
],
srcs: [
"cc/androidmk.go",
@@ -131,7 +135,6 @@
"cc/pgo.go",
"cc/prebuilt.go",
"cc/proto.go",
- "cc/relocation_packer.go",
"cc/rs.go",
"cc/sanitize.go",
"cc/sabi.go",
@@ -143,6 +146,7 @@
"cc/vndk_prebuilt.go",
"cc/cmakelists.go",
+ "cc/compdb.go",
"cc/compiler.go",
"cc/installer.go",
"cc/linker.go",
@@ -169,8 +173,10 @@
testSrcs: [
"cc/cc_test.go",
"cc/gen_test.go",
+ "cc/genrule_test.go",
"cc/library_test.go",
"cc/test_data_test.go",
+ "cc/util_test.go",
],
pluginFor: ["soong_build"],
}
@@ -186,9 +192,11 @@
"soong-shared",
],
srcs: [
- "genrule/filegroup.go",
"genrule/genrule.go",
],
+ testSrcs: [
+ "genrule/genrule_test.go",
+ ],
pluginFor: ["soong_build"],
}
@@ -213,12 +221,15 @@
"blueprint-pathtools",
"soong",
"soong-android",
+ "soong-cc",
"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",
@@ -230,14 +241,18 @@
"java/genrule.go",
"java/jacoco.go",
"java/java.go",
+ "java/jdeps.go",
"java/java_resources.go",
+ "java/prebuilt_apis.go",
"java/proto.go",
+ "java/sdk_library.go",
"java/support_libraries.go",
"java/system_modules.go",
],
testSrcs: [
"java/app_test.go",
"java/java_test.go",
+ "java/jdeps_test.go",
],
pluginFor: ["soong_build"],
}
@@ -271,6 +286,7 @@
"python/defaults.go",
"python/installer.go",
"python/library.go",
+ "python/proto.go",
"python/python.go",
"python/test.go",
],
@@ -288,6 +304,56 @@
],
}
+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",
+ ],
+ srcs: [
+ "apex/apex.go",
+ "apex/key.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
//
// Defaults to enable various configurations of host bionic
//
@@ -313,9 +379,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",
},
},
}
@@ -324,9 +401,20 @@
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",
},
},
}
@@ -339,15 +427,31 @@
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",
+ },
},
}
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",
},
},
}
@@ -379,7 +483,7 @@
}
cc_genrule {
- name: "host_bionic_linker_script",
+ name: "host_bionic_linker_flags",
host_supported: true,
device_supported: false,
target: {
@@ -394,7 +498,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..edd2557 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,11 +1,3 @@
-ccross@android.com
-dwillemsen@google.com
-nanzhang@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 * = ccross@android.com,dwillemsen@google.com,nanzhang@google.com
+per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com
+per-file clang.go,global.go,tidy.go = srhines@google.com, chh@google.com
diff --git a/README.md b/README.md
index b234c71..16d3cce 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,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/.bootstrap/docs/soong_build.html](http://go/Android.bp).
+
+### 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 +183,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 +217,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..7030523 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() {
@@ -64,13 +65,13 @@
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.Sort(ModulesByName{androidMkModulesList, ctx})
transMk := PathForOutput(ctx, "Android"+String(ctx.Config().productVariables.Make_suffix)+".mk")
if ctx.Failed() {
@@ -88,7 +89,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 +102,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 +142,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 +196,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,7 +215,7 @@
}
- 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
}
}
@@ -227,6 +254,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,6 +269,9 @@
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)
}
diff --git a/android/apex.go b/android/apex.go
new file mode 100644
index 0000000..dae88ce
--- /dev/null
+++ b/android/apex.go
@@ -0,0 +1,102 @@
+// 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
+
+// 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 the apex mutator 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
+ BuildForApex(apexName string)
+
+ // Tests whether this module will be built for the platform or not (= APEXs)
+ IsForPlatform() bool
+
+ // 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 to multiple APEXs, 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.
+ ApexName() string
+
+ // Tests if this module can have APEX variants. APEX variants are
+ // created only for the modules that returns true here. This is useful
+ // for not creating APEX variants for 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
+}
+
+type ApexProperties struct {
+ 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
+}
+
+func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase {
+ return m
+}
+
+func (m *ApexModuleBase) BuildForApex(apexName string) {
+ m.ApexProperties.ApexName = apexName
+}
+
+func (m *ApexModuleBase) IsForPlatform() bool {
+ return m.ApexProperties.ApexName == ""
+}
+
+func (m *ApexModuleBase) ApexName() string {
+ return m.ApexProperties.ApexName
+}
+
+func (m *ApexModuleBase) CanHaveApexVariants() bool {
+ return m.canHaveApexVariants
+}
+
+func (m *ApexModuleBase) IsInstallableToApex() bool {
+ // should be overriden if needed
+ return false
+}
+
+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..1b56625 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -16,6 +16,7 @@
import (
"encoding/json"
+ "strconv"
)
func init() {
@@ -50,28 +51,48 @@
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
- }
+func getApiLevelsMap(config Config) map[string]int {
+ return config.Once("ApiLevelsMap", 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,
+ }
+ 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..2543fca 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -194,14 +194,14 @@
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)
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},
}
@@ -288,6 +288,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 +319,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,11 +390,37 @@
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 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
+ }
+
+ 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 filterArchStruct(prop reflect.Type) (reflect.Type, bool) {
var fields []reflect.StructField
@@ -785,17 +848,17 @@
}
if (arch.ArchType == X86 && (hasArmAbi(arch) ||
- hasArmAndroidArch(ctx.Config().Targets[Device]))) ||
+ hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
(arch.ArchType == Arm &&
- hasX86AndroidArch(ctx.Config().Targets[Device])) {
+ 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[Device]))) ||
+ hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
(arch.ArchType == Arm &&
- hasX8664AndroidArch(ctx.Config().Targets[Device])) {
+ hasX8664AndroidArch(ctx.Config().Targets[Android])) {
field := "Arm_on_x86_64"
prefix := "target.arm_on_x86_64"
a.appendProperties(ctx, genProps, targetProp, field, prefix)
@@ -820,10 +883,10 @@
}
// 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) {
@@ -837,7 +900,7 @@
return
}
- targets[os.Class] = append(targets[os.Class],
+ targets[os] = append(targets[os],
Target{
Os: os,
Arch: arch,
@@ -854,17 +917,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")
}
@@ -884,7 +947,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 +1018,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", "cortex-a76", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "denver", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
{"arm", "armv7-a-neon", "kryo", []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"}},
{"mips", "mips32-fp", "", []string{"mips"}},
{"mips", "mips32r2-fp", "", []string{"mips"}},
{"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
@@ -1096,18 +1163,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 +1183,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 +1201,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/config.go b/android/config.go
index fafed6b..0aee0e3 100644
--- a/android/config.go
+++ b/android/config.go
@@ -90,8 +90,9 @@
ConfigFileName string
ProductVariablesFileName string
- Targets map[OsClass][]Target
- BuildOsVariant string
+ Targets map[OsType][]Target
+ BuildOsVariant string
+ BuildOsCommonVariant string
deviceConfig *deviceConfig
@@ -108,8 +109,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
@@ -230,17 +230,19 @@
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()
+
return testConfig
}
@@ -302,15 +304,16 @@
}
if archConfig != nil {
- deviceTargets, err := decodeArchSettings(archConfig)
+ androidTargets, err := decodeArchSettings(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 +324,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
@@ -580,10 +574,22 @@
return Bool(c.productVariables.MinimizeJavaDebugInfo) && !Bool(c.productVariables.Eng)
}
+func (c *config) Debuggable() bool {
+ return Bool(c.productVariables.Debuggable)
+}
+
+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()
}
@@ -618,7 +624,7 @@
}
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 +633,12 @@
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) RunErrorProne() bool {
+ return c.IsEnvTrue("RUN_ERROR_PRONE")
}
// Returns true if -source 1.9 -target 1.9 is being passed to javac
@@ -662,8 +663,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:
@@ -710,7 +711,7 @@
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
@@ -768,6 +769,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)
}
@@ -799,6 +807,22 @@
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 *config) IntegerOverflowDisabledForPath(path string) bool {
if c.productVariables.IntegerOverflowExcludePaths == nil {
return false
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/genrule/filegroup.go b/android/filegroup.go
similarity index 75%
rename from genrule/filegroup.go
rename to android/filegroup.go
index 2cff5fe..b284ce0 100644
--- a/genrule/filegroup.go
+++ b/android/filegroup.go
@@ -12,17 +12,16 @@
// 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 {
@@ -43,34 +42,34 @@
}
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 {
+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) DepsMutator(ctx BottomUpMutatorContext) {
+ ExtractSourcesDeps(ctx, fg.properties.Srcs)
+ ExtractSourcesDeps(ctx, fg.properties.Exclude_srcs)
}
-func (fg *fileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (fg *fileGroup) GenerateAndroidBuildActions(ctx 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 +80,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/makevars.go b/android/makevars.go
index 3094a48..3a7ec6e 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -20,6 +20,7 @@
"io/ioutil"
"os"
"strconv"
+ "strings"
"github.com/google/blueprint/proptools"
)
@@ -152,7 +153,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 +201,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
@@ -240,8 +241,16 @@
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.ctx.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) {
diff --git a/android/module.go b/android/module.go
index 1e54470..556c73d 100644
--- a/android/module.go
+++ b/android/module.go
@@ -23,6 +23,7 @@
"github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/proptools"
)
var (
@@ -57,6 +58,7 @@
type androidBaseContext interface {
Target() Target
TargetPrimary() bool
+ MultiTargets() []Target
Arch() Arch
Os() OsType
Host() bool
@@ -69,6 +71,7 @@
DeviceSpecific() bool
SocSpecific() bool
ProductSpecific() bool
+ ProductServicesSpecific() bool
AConfig() Config
DeviceConfig() DeviceConfig
}
@@ -124,6 +127,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
RequiredModuleNames() []string
@@ -143,7 +147,9 @@
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)
@@ -176,6 +182,7 @@
Target() Target
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
SkipInstall()
ExportedToMake() bool
@@ -191,8 +198,6 @@
}
type commonProperties struct {
- Tags []string
-
// emit build rules for this module
Enabled *bool `android:"arch_variant"`
@@ -211,7 +216,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
@@ -239,9 +245,20 @@
// /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
+ // VINTF manifest fragments to be installed if this module is installed
+ Vintf_fragments []string
+
// names of other modules to install if this module is installed
Required []string `android:"arch_variant"`
@@ -249,8 +266,9 @@
Notice *string
// 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"`
@@ -280,11 +298,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
)
@@ -295,6 +327,7 @@
deviceSpecificModule
socSpecificModule
productSpecificModule
+ productServicesSpecificModule
)
func (k moduleKind) String() string {
@@ -307,6 +340,8 @@
return "soc-specific"
case productSpecificModule:
return "product-specific"
+ case productServicesSpecificModule:
+ return "productservices-specific"
default:
panic(fmt.Errorf("unknown module kind %d", k))
}
@@ -320,6 +355,7 @@
&base.nameProperties,
&base.commonProperties,
&base.variableProperties)
+ base.customizableProperties = m.GetProperties()
}
func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
@@ -329,6 +365,7 @@
base.commonProperties.HostOrDeviceSupported = hod
base.commonProperties.Default_multilib = string(defaultMultilib)
base.commonProperties.ArchSpecific = true
+ base.commonProperties.UseTargetVariants = true
switch hod {
case HostAndDeviceSupported, HostAndDeviceDefault:
@@ -338,6 +375,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
@@ -409,6 +451,8 @@
// For tests
buildParams []BuildParams
+
+ prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool
}
func (a *ModuleBase) AddProperties(props ...interface{}) {
@@ -423,6 +467,10 @@
return a.buildParams
}
+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 {
@@ -438,8 +486,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
}
@@ -451,6 +500,10 @@
return a.commonProperties.CompilePrimary
}
+func (a *ModuleBase) MultiTargets() []Target {
+ return a.commonProperties.CompileMultiTargets
+}
+
func (a *ModuleBase) Os() OsType {
return a.Target().Os
}
@@ -475,9 +528,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 ||
@@ -497,6 +552,26 @@
*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 (a *ModuleBase) Enabled() bool {
if a.commonProperties.Enabled == nil {
return !a.Os().DefaultDisabled
@@ -516,6 +591,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)
@@ -542,6 +618,14 @@
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) generateModuleTarget(ctx ModuleContext) {
allInstalledFiles := Paths{}
allCheckbuildFiles := Paths{}
@@ -602,17 +686,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)
}
@@ -624,8 +702,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 {
@@ -639,6 +745,7 @@
return androidBaseContextImpl{
target: a.commonProperties.CompileTarget,
targetPrimary: a.commonProperties.CompilePrimary,
+ multiTargets: a.commonProperties.CompileMultiTargets,
kind: determineModuleKind(a, ctx),
config: ctx.Config().(Config),
}
@@ -693,6 +800,7 @@
type androidBaseContextImpl struct {
target Target
+ multiTargets []Target
targetPrimary bool
debug bool
kind moduleKind
@@ -763,6 +871,13 @@
bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
}
+ bparams.Outputs = proptools.NinjaEscape(bparams.Outputs)
+ bparams.ImplicitOutputs = proptools.NinjaEscape(bparams.ImplicitOutputs)
+ bparams.Inputs = proptools.NinjaEscape(bparams.Inputs)
+ bparams.Implicits = proptools.NinjaEscape(bparams.Implicits)
+ bparams.OrderOnly = proptools.NinjaEscape(bparams.OrderOnly)
+ bparams.Depfile = proptools.NinjaEscape([]string{bparams.Depfile})[0]
+
return bparams
}
@@ -923,6 +1038,10 @@
return a.targetPrimary
}
+func (a *androidBaseContextImpl) MultiTargets() []Target {
+ return a.multiTargets
+}
+
func (a *androidBaseContextImpl) Arch() Arch {
return a.target.Arch
}
@@ -952,10 +1071,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 {
@@ -982,6 +1101,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()
}
@@ -990,11 +1123,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
@@ -1409,16 +1553,16 @@
}
}
-type AndroidModulesByName struct {
- slice []Module
+type ModulesByName struct {
+ slice []blueprint.Module
ctx interface {
ModuleName(blueprint.Module) string
ModuleSubDir(blueprint.Module) string
}
}
-func (s AndroidModulesByName) Len() int { return len(s.slice) }
-func (s AndroidModulesByName) Less(i, j int) bool {
+func (s ModulesByName) Len() int { return len(s.slice) }
+func (s ModulesByName) Less(i, j int) bool {
mi, mj := s.slice[i], s.slice[j]
ni, nj := s.ctx.ModuleName(mi), s.ctx.ModuleName(mj)
@@ -1428,4 +1572,28 @@
return s.ctx.ModuleSubDir(mi) < s.ctx.ModuleSubDir(mj)
}
}
-func (s AndroidModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
+func (s ModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
+
+// Collect information for opening IDE project files in java/jdeps.go.
+type IDEInfo interface {
+ IDEInfo(ideInfo *IdeInfo)
+ BaseModuleName() string
+}
+
+// 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
+}
+
+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 64d9fdd..b9c44e8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -205,7 +205,7 @@
}
func depsMutator(ctx BottomUpMutatorContext) {
- if m, ok := ctx.Module().(Module); ok {
+ if m, ok := ctx.Module().(Module); ok && m.Enabled() {
m.DepsMutator(ctx)
}
}
diff --git a/android/namespace.go b/android/namespace.go
index 0230524..b027ceb 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(".")
diff --git a/android/neverallow.go b/android/neverallow.go
index 8fba4b9..d3a2801 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -56,7 +56,9 @@
without("vendor", "true").
without("owner", "").
because("a VNDK module can never have an owner."),
- neverallow().notIn("libcore").with("no_standard_libs", "true"),
+ neverallow().
+ notIn("libcore", "development", "external/apache-harmony", "external/apache-xml", "external/bouncycastle", "external/conscrypt", "external/icu", "external/okhttp", "external/wycheproof").
+ with("no_standard_libs", "true"),
// TODO(b/67974785): always enforce the manifest
neverallow().
diff --git a/android/package_ctx.go b/android/package_ctx.go
index e228bba..00b99ff 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -124,7 +124,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 +140,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 +157,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())
})
}
diff --git a/android/paths.go b/android/paths.go
index 91dd9a6..b22e3c7 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -47,6 +47,7 @@
InstallInData() bool
InstallInSanitizerDir() bool
+ InstallInRecovery() bool
}
var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -229,6 +230,8 @@
// 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 == "./" {
@@ -244,7 +247,17 @@
reportPathErrorf(ctx, "Path '%s' is not in module source directory '%s'", 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
+ }
+
+ moduleSrcPath := ModuleSrcPath{srcPath}
+ moduleSrcPath.basePath.rel = srcPath.path
+
+ ret = append(ret, moduleSrcPath)
}
return ret
}
@@ -483,29 +496,26 @@
// 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(), ""}}
+ if err != nil {
+ return ret, err
+ }
abs, err := filepath.Abs(ret.String())
if err != nil {
- reportPathError(ctx, err)
- return ret
+ return ret, err
}
buildroot, err := filepath.Abs(ctx.Config().buildDir)
if err != nil {
- reportPathError(ctx, err)
- return ret
+ return ret, err
}
if strings.HasPrefix(abs, buildroot) {
- reportPathErrorf(ctx, "source path %s is in output", abs)
- return ret
+ return ret, fmt.Errorf("source path %s is in output", abs)
}
- return ret
+ return ret, err
}
// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
@@ -528,10 +538,6 @@
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())
- }
-
return ret, nil
}
@@ -548,7 +554,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 +574,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 {
@@ -594,6 +604,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)
@@ -662,6 +677,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
@@ -705,46 +725,6 @@
return PathForOutput(ctx, ".intermediates", path)
}
-// DistPath is a Path representing a file path rooted from the dist directory
-type DistPath struct {
- basePath
-}
-
-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...)
- 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")
- }
- return filepath.Join(*p.config.productVariables.DistDir, p.path)
-}
-
-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
@@ -768,6 +748,10 @@
reportPathError(ctx, err)
}
+ if pathtools.IsGlob(srcPath.String()) {
+ reportPathErrorf(ctx, "path may not contain a glob: %s", srcPath.String())
+ }
+
path := ModuleSrcPath{srcPath}
path.basePath.rel = p
@@ -825,37 +809,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
@@ -948,12 +935,17 @@
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"
}
diff --git a/android/paths_test.go b/android/paths_test.go
index cd9fbfd..fbeccb1 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -201,6 +201,7 @@
inData bool
inSanitizerDir bool
+ inRecovery bool
}
func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
@@ -221,6 +222,10 @@
return m.inSanitizerDir
}
+func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
+ return m.inRecovery
+}
+
func TestPathForModuleInstall(t *testing.T) {
testConfig := TestConfig("", nil)
@@ -287,6 +292,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 +353,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 +414,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 +477,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 {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 9356aab..47c5cf5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -59,7 +59,9 @@
return nil
}
- return PathForModuleSrc(ctx, (*p.srcs)[0])
+ // Return the singleton source after expanding any filegroup in the
+ // sources.
+ return ctx.ExpandSource((*p.srcs)[0], "")
}
func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
@@ -79,7 +81,7 @@
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
@@ -119,10 +121,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()
@@ -133,6 +137,9 @@
} else {
m.SkipInstall()
}
+ if len(*p.srcs) > 0 {
+ ExtractSourceDeps(ctx, &(*p.srcs)[0])
+ }
}
}
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
new file mode 100644
index 0000000..fbc9de2
--- /dev/null
+++ b/android/prebuilt_etc.go
@@ -0,0 +1,189 @@
+// 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"
+)
+
+// prebuilt_etc is for prebuilts that will be installed to
+// <partition>/etc/<subdir>
+
+func init() {
+ RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
+
+ PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("prebuilt_etc", prebuiltEtcMutator).Parallel()
+ })
+}
+
+type prebuiltEtcProperties struct {
+ // Source file of this prebuilt.
+ Src *string `android:"arch_variant"`
+
+ // optional subdirectory under which this file is installed into
+ Sub_dir *string `android:"arch_variant"`
+
+ // Make this module available when building for recovery.
+ Recovery_available *bool
+
+ InRecovery bool `blueprint:"mutated"`
+}
+
+type PrebuiltEtc struct {
+ ModuleBase
+
+ properties prebuiltEtcProperties
+
+ sourceFilePath Path
+ outputFilePath OutputPath
+ 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")
+ }
+
+ // To support ":modulename" in src
+ ExtractSourceDeps(ctx, p.properties.Src)
+}
+
+func (p *PrebuiltEtc) SourceFilePath(ctx ModuleContext) Path {
+ return ctx.ExpandSource(String(p.properties.Src), "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) GenerateAndroidBuildActions(ctx ModuleContext) {
+ p.sourceFilePath = ctx.ExpandSource(String(p.properties.Src), "src")
+ p.outputFilePath = PathForModuleOut(ctx, ctx.ModuleName()).OutputPath
+ p.installDirPath = PathForModuleInstall(ctx, "etc", String(p.properties.Sub_dir))
+
+ // This ensures that outputFilePath has the same name as this module.
+ 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")
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional")
+ 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 :=", name)
+ 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)
+}
+
+func PrebuiltEtcFactory() Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module)
+ // This module is device-only
+ InitAndroidArchModule(module, DeviceSupported, 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 {
+ 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..9244647
--- /dev/null
+++ b/android/prebuilt_etc_test.go
@@ -0,0 +1,93 @@
+// 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 testPrebuiltEtc(t *testing.T, bp string) *TestContext {
+ config, buildDir := setUp(t)
+ defer tearDown(buildDir)
+ ctx := NewTestArchContext()
+ ctx.RegisterModuleType("prebuilt_etc", ModuleFactoryAdaptor(PrebuiltEtcFactory))
+ 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
+}
+
+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)
+ }
+}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 69ce16a..cd1ffae 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -109,6 +109,19 @@
}`,
prebuilt: false,
},
+ {
+ name: "prebuilt file from filegroup preferred",
+ modules: `
+ filegroup {
+ name: "fg",
+ }
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: [":fg"],
+ }`,
+ prebuilt: true,
+ },
}
func TestPrebuilts(t *testing.T) {
@@ -125,6 +138,7 @@
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()
diff --git a/android/singleton.go b/android/singleton.go
index f577b0a..f926435 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -48,9 +48,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))
@@ -136,6 +139,10 @@
}
}
+func (s singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
+ s.SingletonContext.VisitAllModules(visit)
+}
+
func (s singletonContextAdaptor) VisitAllModules(visit func(Module)) {
s.SingletonContext.VisitAllModules(visitAdaptor(visit))
}
diff --git a/android/testing.go b/android/testing.go
index f5d33e1..ca7e7ce 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -92,6 +92,16 @@
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
+}
+
// 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) {
diff --git a/android/variable.go b/android/variable.go
index f4aaec7..476171b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -42,14 +42,9 @@
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"`
+ } `android:"arch_variant"`
Safestack struct {
Cflags []string `android:"arch_variant"`
@@ -67,6 +62,11 @@
Cflags []string
}
+ // Product_is_iot is true for Android Things devices.
+ Product_is_iot struct {
+ Cflags []string
+ }
+
// treble_linker_namespaces is true when the system/vendor linker namespace separation is
// enabled.
Treble_linker_namespaces struct {
@@ -92,6 +92,9 @@
Eng struct {
Cflags []string
Cppflags []string
+ Lto struct {
+ Never *bool
+ }
}
Pdk struct {
@@ -171,7 +174,6 @@
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"`
@@ -181,7 +183,6 @@
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"`
@@ -195,9 +196,10 @@
CFIExcludePaths *[]string `json:",omitempty"`
CFIIncludePaths *[]string `json:",omitempty"`
- VendorPath *string `json:",omitempty"`
- OdmPath *string `json:",omitempty"`
- ProductPath *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"`
@@ -206,6 +208,7 @@
CoveragePaths *[]string `json:",omitempty"`
CoverageExcludePaths *[]string `json:",omitempty"`
+ DevicePrefer32BitApps *bool `json:",omitempty"`
DevicePrefer32BitExecutables *bool `json:",omitempty"`
HostPrefer32BitExecutables *bool `json:",omitempty"`
@@ -220,8 +223,9 @@
Override_rs_driver *string `json:",omitempty"`
+ Product_is_iot *bool `json:",omitempty"`
+
DeviceKernelHeaders []string `json:",omitempty"`
- DistDir *string `json:",omitempty"`
ExtraVndkVersions []string `json:",omitempty"`
@@ -229,6 +233,11 @@
PgoAdditionalProfileDirs []string `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"`
}
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..b3d5aec 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -51,6 +51,7 @@
"LOCAL_MODULE_HOST_OS": hostOs,
"LOCAL_SANITIZE": sanitize(""),
"LOCAL_SANITIZE_DIAG": sanitize("diag."),
+ "LOCAL_STRIP_MODULE": strip(),
"LOCAL_CFLAGS": cflags,
"LOCAL_UNINSTALLABLE_MODULE": invert("installable"),
"LOCAL_PROGUARD_ENABLED": proguardEnabled,
@@ -81,10 +82,10 @@
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",
@@ -125,6 +126,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",
@@ -138,6 +140,7 @@
"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",
@@ -146,12 +149,13 @@
"LOCAL_ANNOTATION_PROCESSOR_CLASSES": "annotation_processor_classes",
"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",
})
addStandardProperties(bpparser.BoolType,
@@ -167,16 +171,20 @@
"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_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",
})
}
@@ -455,6 +463,29 @@
}
}
+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 {
@@ -742,8 +773,8 @@
"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",
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 37e2427..0f6c5ac 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -331,7 +331,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 +340,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 {
}
`,
},
@@ -451,7 +512,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 +535,53 @@
`,
},
{
+ 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)
+ `,
+ 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"],
+ }
+ `,
+ },
+ {
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 {
@@ -570,7 +673,7 @@
],
}
- java_library_static {
+ java_library {
srcs: ["test.java"],
static_libs: [],
}
@@ -589,6 +692,47 @@
}
`,
},
+ {
+ 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,
+ }
+}
+`,
+ },
}
func TestEndToEnd(t *testing.T) {
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..89c1af9 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))
}
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..167e470 100644
--- a/androidmk/parser/scope.go
+++ b/androidmk/parser/scope.go
@@ -71,7 +71,7 @@
func init() {
builtinScope := make(map[string]string)
- builtinScope["__builtin_dollar"] = "$"
+ builtinScope[builtinDollar] = "$"
}
func (v Variable) EvalFunction(scope Scope) (string, bool) {
diff --git a/apex/apex.go b/apex/apex.go
new file mode 100644
index 0000000..619ac33
--- /dev/null
+++ b/apex/apex.go
@@ -0,0 +1,413 @@
+// 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"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/java"
+
+ "github.com/google/blueprint"
+ "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 0644' > ${out} && ` +
+ `echo '/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 " 1000 1000 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} --verbose --force --manifest ${manifest} ` +
+ `--file_contexts ${file_contexts} ` +
+ `--canned_fs_config ${canned_fs_config} ` +
+ `--key ${key} ${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")
+)
+
+var apexSuffix = ".apex"
+
+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"}
+)
+
+func init() {
+ pctx.Import("android/soong/common")
+ pctx.HostBinToolVariable("apexer", "apexer")
+ pctx.HostBinToolVariable("aapt2", "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("zipalign", "zipalign")
+
+ android.RegisterModuleType("apex", apexBundleFactory)
+
+ android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("apex_deps", apexDepsMutator)
+ ctx.BottomUp("apex", apexMutator)
+ })
+}
+
+// maps a module name to set of apex bundle names that the module should be built for
+func apexBundleNamesFor(config android.Config) map[string]map[string]bool {
+ return config.Once("apexBundleNames", func() interface{} {
+ return make(map[string]map[string]bool)
+ }).(map[string]map[string]bool)
+}
+
+// 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 _, ok := mctx.Module().(*apexBundle); ok {
+ apexBundleName := mctx.Module().Name()
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() {
+ moduleName := am.Name()
+ bundleNames, ok := apexBundleNamesFor(mctx.Config())[moduleName]
+ if !ok {
+ bundleNames = make(map[string]bool)
+ apexBundleNamesFor(mctx.Config())[moduleName] = bundleNames
+ }
+ bundleNames[apexBundleName] = true
+ 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() {
+ moduleName := am.Name()
+ if bundleNames, ok := apexBundleNamesFor(mctx.Config())[moduleName]; ok {
+ variations := []string{"platform"}
+ for bn := range bundleNames {
+ variations = append(variations, bn)
+ }
+ modules := mctx.CreateVariations(variations...)
+ for i, m := range modules {
+ if i == 0 {
+ continue // platform
+ }
+ m.(android.ApexModule).BuildForApex(variations[i])
+ }
+ }
+ } 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 apexBundleProperties struct {
+ // Json manifest file describing meta info of this APEX bundle. Default:
+ // "manifest.json"
+ Manifest *string
+
+ // File contexts file for setting security context to each file in this APEX bundle
+ // Default: "file_contexts".
+ 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
+}
+
+type apexBundle struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ properties apexBundleProperties
+
+ outputFile android.WritablePath
+ installDir android.OutputPath
+}
+
+func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
+ for _, arch := range ctx.MultiTargets() {
+ // 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.String()},
+ {Mutator: "image", Variation: "core"},
+ {Mutator: "link", Variation: "shared"},
+ }, sharedLibTag, a.properties.Native_shared_libs...)
+
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: arch.String()},
+ {Mutator: "image", Variation: "core"},
+ }, executableTag, a.properties.Binaries...)
+ }
+
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: "android_common"},
+ }, javaLibTag, a.properties.Java_libs...)
+
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: "android_common"},
+ }, prebuiltTag, a.properties.Prebuilts...)
+
+ if String(a.properties.Key) == "" {
+ ctx.ModuleErrorf("key is missing")
+ return
+ }
+ ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
+}
+
+func getCopyManifestForNativeLibrary(cc *cc.Module) (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"
+ }
+ if !cc.Arch().Native {
+ dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
+ }
+
+ fileToCopy = cc.OutputFile().Path()
+ return
+}
+
+func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = "bin"
+ fileToCopy = cc.OutputFile().Path()
+ return
+}
+
+func getCopyManifestForJavaLibrary(java *java.Library) (fileToCopy android.Path, dirInApex string) {
+ dirInApex = "javalib"
+ fileToCopy = java.Srcs()[0]
+ 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) {
+ // files to copy -> dir in apex
+ copyManifest := make(map[android.Path]string)
+
+ var keyFile android.Path
+
+ ctx.WalkDeps(func(child, parent android.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)
+ copyManifest[fileToCopy] = dirInApex
+ 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 {
+ fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
+ copyManifest[fileToCopy] = dirInApex
+ return true
+ } else {
+ ctx.PropertyErrorf("binaries", "%q is not a cc_binary module", depName)
+ }
+ case javaLibTag:
+ if java, ok := child.(*java.Library); ok {
+ fileToCopy, dirInApex := getCopyManifestForJavaLibrary(java)
+ copyManifest[fileToCopy] = dirInApex
+ 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)
+ copyManifest[fileToCopy] = dirInApex
+ return true
+ } else {
+ ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
+ }
+ case keyTag:
+ if key, ok := child.(*apexKey); ok {
+ keyFile = key.private_key_file
+ return false
+ } else {
+ ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
+ }
+ }
+ } else {
+ // indirect dependencies
+ if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
+ if cc, ok := child.(*cc.Module); ok {
+ fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc)
+ copyManifest[fileToCopy] = dirInApex
+ return true
+ }
+ }
+ }
+ return false
+ })
+
+ // files and dirs that will be created in apex
+ var readOnlyPaths []string
+ var executablePaths []string // this also includes dirs
+ for fileToCopy, dirInApex := range copyManifest {
+ pathInApex := filepath.Join(dirInApex, fileToCopy.Base())
+ if dirInApex == "bin" {
+ executablePaths = append(executablePaths, pathInApex)
+ } else {
+ readOnlyPaths = append(readOnlyPaths, pathInApex)
+ }
+ if !android.InList(dirInApex, executablePaths) {
+ executablePaths = append(executablePaths, dirInApex)
+ }
+ }
+ sort.Strings(readOnlyPaths)
+ sort.Strings(executablePaths)
+ cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
+ ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+ Rule: generateFsConfig,
+ Output: cannedFsConfig,
+ Args: map[string]string{
+ "ro_paths": strings.Join(readOnlyPaths, " "),
+ "exec_paths": strings.Join(executablePaths, " "),
+ },
+ })
+
+ manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "manifest.json"))
+ fileContexts := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.File_contexts, "file_contexts"))
+
+ a.outputFile = android.PathForModuleOut(ctx, a.ModuleBase.Name()+apexSuffix)
+
+ filesToCopy := []android.Path{}
+ for file := range copyManifest {
+ filesToCopy = append(filesToCopy, file)
+ }
+ sort.Slice(filesToCopy, func(i, j int) bool {
+ return filesToCopy[i].String() < filesToCopy[j].String()
+ })
+
+ copyCommands := []string{}
+ for _, src := range filesToCopy {
+ dest := filepath.Join(copyManifest[src], src.Base())
+ dest_path := filepath.Join(android.PathForModuleOut(ctx, "image").String(), dest)
+ copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
+ copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
+ }
+ implicitInputs := append(android.Paths(nil), filesToCopy...)
+ implicitInputs = append(implicitInputs, cannedFsConfig, manifest, fileContexts, keyFile)
+ outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
+ prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
+ ctx.ModuleBuild(pctx, android.ModuleBuildParams{
+ Rule: apexRule,
+ Implicits: implicitInputs,
+ Output: a.outputFile,
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": android.PathForModuleOut(ctx, "image").String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": manifest.String(),
+ "file_contexts": fileContexts.String(),
+ "canned_fs_config": cannedFsConfig.String(),
+ "key": keyFile.String(),
+ },
+ })
+
+ a.installDir = android.PathForModuleInstall(ctx, "apex")
+}
+
+func (a *apexBundle) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ 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)
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
+ fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name+apexSuffix)
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(a.properties.Key))
+ fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+ }}
+}
+
+func apexBundleFactory() android.Module {
+ module := &apexBundle{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
diff --git a/apex/key.go b/apex/key.go
new file mode 100644
index 0000000..ff348a8
--- /dev/null
+++ b/apex/key.go
@@ -0,0 +1,88 @@
+// 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"
+
+ "android/soong/android"
+ "github.com/google/blueprint/proptools"
+)
+
+var String = proptools.String
+
+func init() {
+ android.RegisterModuleType("apex_key", apexKeyFactory)
+}
+
+type apexKey struct {
+ android.ModuleBase
+
+ properties apexKeyProperties
+
+ public_key_file android.Path
+ private_key_file android.Path
+
+ keyName string
+}
+
+type apexKeyProperties struct {
+ // Path 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
+ // Path to the private key file in pem format. Used to sign APEXs.
+ Private_key *string
+}
+
+func apexKeyFactory() android.Module {
+ module := &apexKey{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ return module
+}
+
+func (m *apexKey) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+func (m *apexKey) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ m.public_key_file = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
+ 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 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
+
+ ctx.InstallFile(android.PathForModuleInstall(ctx, "etc/security/apex"), m.keyName, m.public_key_file)
+}
+
+func (m *apexKey) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(m.public_key_file),
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", "$(TARGET_OUT)/etc/security/apex")
+ fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", m.keyName)
+ },
+ },
+ }
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index d5c318c..24a38c9 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -45,10 +45,51 @@
// 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: "rewriteIncorrectAndroidmkAndroidLibraries",
+ fix: rewriteIncorrectAndroidmkAndroidLibraries,
+ },
+ {
+ name: "rewriteTestModuleTypes",
+ fix: rewriteTestModuleTypes,
+ },
+ {
+ name: "rewriteAndroidmkJavaLibs",
+ fix: rewriteAndroidmkJavaLibs,
+ },
+ {
+ name: "rewriteJavaStaticLibs",
+ fix: rewriteJavaStaticLibs,
+ },
+ {
+ name: "mergeMatchingModuleProperties",
+ fix: runPatchListMod(mergeMatchingModuleProperties),
+ },
+ {
+ name: "reorderCommonProperties",
+ fix: runPatchListMod(reorderCommonProperties),
+ },
+ {
+ name: "removeTags",
+ fix: runPatchListMod(removeTags),
+ },
}
func NewFixRequest() FixRequest {
@@ -56,11 +97,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 +141,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 +181,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 +190,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 {
@@ -214,7 +232,7 @@
return nil
}
-func (f *Fixer) rewriteIncorrectAndroidmkAndroidLibraries() error {
+func rewriteIncorrectAndroidmkAndroidLibraries(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
if !ok {
@@ -230,7 +248,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 +267,264 @@
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
+}
+
+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++ {
@@ -359,7 +599,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 +616,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 +657,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 +671,49 @@
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 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..16dfce0 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,308 @@
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)
+ })
})
}
}
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..324b5bc 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -24,7 +24,8 @@
)
var (
- vendorSuffix = ".vendor"
+ vendorSuffix = ".vendor"
+ recoverySuffix = ".recovery"
)
type AndroidMkContext interface {
@@ -50,7 +51,7 @@
}
func (c *Module) AndroidMk() android.AndroidMkData {
- if c.Properties.HideFromMake {
+ if c.Properties.HideFromMake || !c.IsForPlatform() {
return android.AndroidMkData{
Disabled: true,
}
@@ -58,23 +59,18 @@
ret := android.AndroidMkData{
OutputFile: c.outputFile,
+ 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")
- }
+ fmt.Fprintln(w, "LOCAL_SOONG_LINK_TYPE :=", c.getMakeLinkType())
if c.useVndk() {
fmt.Fprintln(w, "LOCAL_USE_VNDK := true")
}
@@ -97,6 +93,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,45 +133,13 @@
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())
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
+ })
} 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.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
@@ -187,9 +153,9 @@
}
}
- 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())
@@ -198,29 +164,29 @@
if library.shared() {
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")
+ })
}
}
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.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, " "))
}
@@ -243,6 +209,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 +230,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 +245,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 +260,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 +273,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 +306,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 +316,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,10 +324,9 @@
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")
})
diff --git a/cc/binary.go b/cc/binary.go
index 9e7b70b..15db2ad 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -15,6 +15,8 @@
package cc
import (
+ "github.com/google/blueprint"
+
"android/soong/android"
)
@@ -31,9 +33,6 @@
// 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
@@ -41,9 +40,6 @@
// 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
@@ -84,6 +80,9 @@
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
@@ -157,7 +156,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 +166,6 @@
"from static libs or set static_executable: true")
}
- android.ExtractSourceDeps(ctx, binary.Properties.Version_script)
-
return deps
}
@@ -178,7 +176,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()
@@ -213,12 +211,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")
- }
}
}
@@ -252,14 +247,23 @@
switch ctx.Os() {
case android.Android:
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 +274,6 @@
"-Wl,--gc-sections",
"-Wl,-z,nocopyreloc",
)
-
}
} else {
if binary.static() {
@@ -287,7 +290,6 @@
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
@@ -297,32 +299,30 @@
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) {
+ // b/80093681, GNU strip/objcopy bug.
+ // Use llvm-{strip,objcopy} when clang lld is used.
+ builderFlags.stripUseLlvmStrip = binary.baseLinker.useClangLld(ctx)
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)
@@ -336,6 +336,17 @@
binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
}
+ 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)
+ }
+
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
linkerDeps = append(linkerDeps, objs.tidyFiles...)
@@ -380,3 +391,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..3d12538 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -67,7 +67,9 @@
partialLd = pctx.AndroidStaticRule("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: "$ldCmd -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
CommandDeps: []string{"$ldCmd"},
},
"ldCmd", "ldFlags")
@@ -109,13 +111,14 @@
"objcopyCmd", "prefix")
_ = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
+ _ = pctx.SourcePathVariable("xzCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/xz")
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"},
},
"args", "crossCompile")
@@ -124,33 +127,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")
@@ -177,7 +169,7 @@
// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
sAbiDump = pctx.AndroidStaticRule("sAbiDump",
blueprint.RuleParams{
- Command: "rm -f $out && $sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem ${config.RSIncludePath}",
+ Command: "rm -f $out && $sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
CommandDeps: []string{"$sAbiDumper"},
},
"cFlags", "exportDirs")
@@ -199,11 +191,8 @@
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 += "|| (echo ' ---- Please update abi references by running $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l ${libName} ----'"
+ commandStr += " && (mkdir -p $$DIST_DIR/abidiffs && cp ${out} $$DIST_DIR/abidiff/)"
commandStr += " && exit 1)"
return blueprint.RuleParams{
Command: commandStr,
@@ -231,29 +220,29 @@
}
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
+ protoFlags string
+ protoOutParams string
+ tidyFlags string
+ sAbiFlags string
+ yasmFlags string
+ aidlFlags string
+ rsFlags string
+ toolchain config.Toolchain
+ tidy bool
+ coverage bool
+ sAbiDump bool
+ protoRoot bool
systemIncludeFlags string
@@ -263,6 +252,7 @@
stripKeepSymbols bool
stripKeepMiniDebugInfo bool
stripAddGnuDebuglink bool
+ stripUseLlvmStrip bool
}
type Objects struct {
@@ -296,7 +286,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 +314,7 @@
toolingCppflags := strings.Join([]string{
commonFlags,
flags.toolingCFlags,
- flags.cppFlags,
+ flags.toolingCppFlags,
}, " ")
cppflags := strings.Join([]string{
@@ -339,19 +329,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 +376,23 @@
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
switch srcFile.Ext() {
case ".S", ".s":
- ccCmd = "gcc"
+ 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 +400,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 {
@@ -617,12 +587,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
@@ -722,11 +687,14 @@
baseName, exportedHeaderFlags string, isVndkExt bool) android.OptionalPath {
outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
-
+ libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
localAbiCheckAllowFlags := append([]string(nil), abiCheckAllowFlags...)
if exportedHeaderFlags == "" {
localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-advice-only")
}
+ if inList(libName, llndkLibraries) {
+ localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-consider-opaque-types-different")
+ }
if isVndkExt {
localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-allow-extensions")
}
@@ -739,7 +707,7 @@
Implicit: referenceDump,
Args: map[string]string{
"referenceDump": referenceDump.String(),
- "libName": baseName[0:(len(baseName) - len(filepath.Ext(baseName)))],
+ "libName": libName,
"arch": ctx.Arch().ArchType.Name,
"allowFlags": strings.Join(localAbiCheckAllowFlags, " "),
},
@@ -751,7 +719,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 +739,7 @@
Input: inputFile,
Args: map[string]string{
"crossCompile": crossCompile,
+ "format": format,
},
})
}
@@ -768,12 +748,7 @@
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++"
ctx.Build(pctx, android.BuildParams{
Rule: partialLd,
@@ -819,6 +794,9 @@
if flags.stripKeepSymbols {
args += " --keep-symbols"
}
+ if flags.stripUseLlvmStrip {
+ args += " --use-llvm-strip"
+ }
ctx.Build(pctx, android.BuildParams{
Rule: strip,
@@ -857,21 +835,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..8b68489 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,25 +34,28 @@
android.RegisterModuleType("cc_defaults", defaultsFactory)
android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("image", vendorMutator).Parallel()
- ctx.BottomUp("link", linkageMutator).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("begin", BeginMutator).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("tsan_deps", sanitizerDepsMutator(tsan))
ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
- ctx.TopDown("minimal_runtime_deps", minimalRuntimeDepsMutator())
+ ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator)
ctx.BottomUp("coverage", coverageLinkingMutator).Parallel()
ctx.TopDown("vndk_deps", sabiDepsMutator)
@@ -68,6 +71,7 @@
SharedLibs, LateSharedLibs []string
StaticLibs, LateStaticLibs, WholeStaticLibs []string
HeaderLibs []string
+ RuntimeLibs []string
ReexportSharedLibHeaders, ReexportStaticLibHeaders, ReexportHeaderLibHeaders []string
@@ -79,7 +83,10 @@
ReexportGeneratedHeaders []string
CrtBegin, CrtEnd string
- LinkerScript string
+
+ // Used for host bionic
+ LinkerFlagsFile string
+ DynamicLinker string
}
type PathDeps struct {
@@ -104,7 +111,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 {
@@ -132,7 +144,6 @@
SystemIncludeFlags []string
Toolchain config.Toolchain
- Clang bool
Tidy bool
Coverage bool
SAbiDump bool
@@ -158,21 +169,27 @@
// 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"`
+ AndroidMkRuntimeLibs []string `blueprint:"mutated"`
+ HideFromMake bool `blueprint:"mutated"`
+ PreventInstall bool `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"`
}
type VendorProperties struct {
@@ -193,16 +210,20 @@
//
// 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
@@ -210,7 +231,8 @@
isVndk() bool
isVndkSp() bool
isVndkExt() bool
- createVndkSourceAbiDump() bool
+ inRecovery() bool
+ shouldCreateVndkSourceAbiDump() bool
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
@@ -292,11 +314,13 @@
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"}
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"}
)
// Module contains the properties and members used by all C/C++ module types, and implements
@@ -305,10 +329,10 @@
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.ApexModuleBase
Properties BaseProperties
VendorProperties VendorProperties
- unused UnusedProperties
// initialize before calling Init
hod android.HostOrDeviceSupported
@@ -347,8 +371,12 @@
staticVariant *Module
}
+func (c *Module) OutputFile() android.OptionalPath {
+ return c.outputFile
+}
+
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()...)
}
@@ -383,10 +411,23 @@
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
}
@@ -446,6 +487,14 @@
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()
+}
+
type baseModuleContext struct {
android.BaseContext
moduleContextImpl
@@ -471,10 +520,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)
}
@@ -493,7 +538,7 @@
}
func (ctx *moduleContextImpl) useSdk() bool {
- if ctx.ctx.Device() && !ctx.useVndk() {
+ if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() {
return String(ctx.mod.Properties.Sdk_version) != ""
}
return false
@@ -537,16 +582,36 @@
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) 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 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 inList(ctx.baseModuleName(), llndkLibraries) {
+ return true
+ }
+ if inList(ctx.baseModuleName(), ndkMigratedLibs) {
+ return true
+ }
+ if ctx.useVndk() && ctx.isVndk() {
+ // Return true if this is VNDK-core, VNDK-SP, or VNDK-Ext and this is not
+ // VNDK-private.
+ return Bool(ctx.mod.VendorProperties.Vendor_available) || ctx.isVndkExt()
+ }
+ return false
}
func (ctx *moduleContextImpl) selectedStl() string {
@@ -658,7 +723,6 @@
}
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
-
ctx := &moduleContext{
ModuleContext: actx,
moduleContextImpl: moduleContextImpl{
@@ -672,9 +736,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)
@@ -739,7 +806,7 @@
c.outputFile = android.OptionalPathForPath(outputFile)
}
- 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
@@ -837,6 +904,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) {
@@ -878,10 +946,6 @@
}
func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
- if !c.Enabled() {
- return
- }
-
ctx := &depsContext{
BottomUpMutatorContext: actx,
moduleContextImpl: moduleContextImpl{
@@ -953,30 +1017,41 @@
actx.AddVariationDependencies(nil, depTag, lib)
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, wholeStaticDepTag,
- deps.WholeStaticLibs...)
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, wholeStaticDepTag, deps.WholeStaticLibs...)
for _, lib := range deps.StaticLibs {
depTag := staticDepTag
if inList(lib, deps.ReexportStaticLibHeaders) {
depTag = staticExportDepTag
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, depTag, lib)
+ 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...)
for _, lib := range deps.SharedLibs {
depTag := sharedDepTag
if inList(lib, deps.ReexportSharedLibHeaders) {
depTag = sharedExportDepTag
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, depTag, lib)
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ }, depTag, lib)
}
- actx.AddVariationDependencies([]blueprint.Variation{{"link", "shared"}}, lateSharedDepTag,
- deps.LateSharedLibs...)
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ }, lateSharedDepTag, deps.LateSharedLibs...)
+
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ }, runtimeDepTag, deps.RuntimeLibs...)
actx.AddDependency(c, genSourceDepTag, deps.GeneratedSources...)
@@ -988,23 +1063,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 +1095,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 +1128,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 {
@@ -1087,31 +1156,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 +1189,45 @@
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 is a member of VNDK and at the same time dependencies of an LLNDK library,
+// it is subject to be double loaded. Such lib should be explicitly marked as double_loaded: true
+// or as vndk-sp (vndk: { enabled: true, support_system_process: true}).
+func checkDoubleLoadableLibries(ctx android.ModuleContext, from *Module, to *Module) {
+ if _, ok := from.linker.(*libraryDecorator); !ok {
+ return
+ }
+
+ if inList(ctx.ModuleName(), llndkLibraries) ||
+ (from.useVndk() && Bool(from.VendorProperties.Double_loadable)) {
+ _, depIsLlndk := to.linker.(*llndkStubDecorator)
+ depIsVndkSp := false
+ if to.vndkdep != nil && to.vndkdep.isVndkSp() {
+ depIsVndkSp = true
+ }
+ depIsVndk := false
+ if to.vndkdep != nil && to.vndkdep.isVndk() {
+ depIsVndk = true
+ }
+ depIsDoubleLoadable := Bool(to.VendorProperties.Double_loadable)
+ if !depIsLlndk && !depIsVndkSp && !depIsDoubleLoadable && depIsVndk {
+ ctx.ModuleErrorf("links VNDK library %q that isn't double_loadable.",
+ ctx.OtherModuleName(to))
+ }
+ }
+}
+
// Convert dependencies to paths. Returns a PathDeps containing paths
func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPaths PathDeps
@@ -1151,8 +1243,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,19 +1269,17 @@
} 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
}
@@ -1234,6 +1322,7 @@
}
checkLinkType(ctx, c, ccDep, t)
+ checkDoubleLoadableLibries(ctx, c, ccDep)
}
var ptr *android.Paths
@@ -1281,6 +1370,8 @@
depPaths.CrtBegin = linkFile
case crtEndDepTag:
depPaths.CrtEnd = linkFile
+ case dynamicLinkerDepTag:
+ depPaths.DynamicLinker = linkFile
}
switch depTag {
@@ -1317,28 +1408,36 @@
*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 {
// 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:
// 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 runtimeDepTag:
+ c.Properties.AndroidMkRuntimeLibs = append(
+ c.Properties.AndroidMkRuntimeLibs, makeLibName(depName))
}
})
@@ -1375,6 +1474,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,12 +1505,47 @@
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 {
+ 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
+}
+
//
// Defaults
//
type Defaults struct {
android.ModuleBase
android.DefaultsModuleBase
+ android.ApexModuleBase
}
func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1429,12 +1567,12 @@
&VendorProperties{},
&BaseCompilerProperties{},
&BaseLinkerProperties{},
+ &MoreBaseLinkerProperties{},
&LibraryProperties{},
&FlagExporterProperties{},
&BinaryLinkerProperties{},
&TestProperties{},
&TestBinaryProperties{},
- &UnusedProperties{},
&StlProperties{},
&SanitizeProperties{},
&StripProperties{},
@@ -1449,6 +1587,7 @@
)
android.InitDefaultsModule(module)
+ android.InitApexModule(module)
return module
}
@@ -1461,6 +1600,8 @@
// vendorMode is the variant used for /vendor code that compiles
// against the VNDK.
vendorMode = "vendor"
+
+ recoveryMode = "recovery"
)
func squashVendorSrcs(m *Module) {
@@ -1473,22 +1614,59 @@
}
}
-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 props, ok := genrule.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 := genrule.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)
+ }
+ mctx.CreateVariations(variants...)
}
}
@@ -1542,43 +1720,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 +1804,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..ead89f6 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"
@@ -57,17 +56,17 @@
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(LibraryFactory))
ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(LibrarySharedFactory))
ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(LibraryHeaderFactory))
- ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(toolchainLibraryFactory))
+ 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("image", imageMutator).Parallel()
+ ctx.BottomUp("link", LinkageMutator).Parallel()
ctx.BottomUp("vndk", vndkMutator).Parallel()
- ctx.BottomUp("begin", beginMutator).Parallel()
+ ctx.BottomUp("begin", BeginMutator).Parallel()
})
ctx.Register()
@@ -76,16 +75,50 @@
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: "",
}
cc_library {
@@ -93,6 +126,7 @@
no_libgcc: true,
nocrt: true,
system_shared_libs: [],
+ recovery_available: true,
}
llndk_library {
name: "libc",
@@ -103,6 +137,7 @@
no_libgcc: true,
nocrt: true,
system_shared_libs: [],
+ recovery_available: true,
}
llndk_library {
name: "libm",
@@ -113,6 +148,7 @@
no_libgcc: true,
nocrt: true,
system_shared_libs: [],
+ recovery_available: true,
}
llndk_library {
name: "libdl",
@@ -125,6 +161,7 @@
system_shared_libs: [],
stl: "none",
vendor_available: true,
+ recovery_available: true,
}
cc_library {
name: "libc++",
@@ -133,6 +170,7 @@
system_shared_libs: [],
stl: "none",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -145,14 +183,19 @@
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: "crtend_so",
+ recovery_available: true,
+ vendor_available: true,
}
cc_library {
@@ -226,8 +269,9 @@
}
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 TestVendorSrc(t *testing.T) {
@@ -1389,6 +1433,120 @@
}
}
+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)
+}
+
var compilerFlagsTestCases = []struct {
in string
out bool
@@ -1538,3 +1696,39 @@
}
}
+
+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")
+ }
+
+}
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..acfc924
--- /dev/null
+++ b/cc/compdb.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 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) []string {
+ var args []string
+ isCpp := false
+ isAsm := false
+ // TODO It would be better to ask soong for the types here.
+ switch src.Ext() {
+ case ".S", ".s", ".asm":
+ isAsm = true
+ isCpp = false
+ case ".c":
+ isAsm = false
+ isCpp = false
+ case ".cpp", ".cc", ".mm":
+ isAsm = false
+ isCpp = true
+ default:
+ log.Print("Unknown file extension " + src.Ext() + " on file " + src.String())
+ isAsm = true
+ isCpp = false
+ }
+ // The executable for the compilation doesn't matter but we need something there.
+ args = append(args, "/bin/false")
+ 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)
+ for _, src := range srcs {
+ if _, ok := builds[src.String()]; !ok {
+ builds[src.String()] = compDbEntry{
+ Directory: rootDir,
+ Arguments: getArguments(src, ctx, ccModule),
+ 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..5ac5d79 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -73,7 +73,7 @@
// 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"`
// list of generated sources to compile. These are the names of gensrcs or
// genrule modules.
@@ -142,6 +142,19 @@
// 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
+
+ // list of source files that should not be used to
+ // build the recovery variant of the C/C++ module.
+ Exclude_srcs []string
+
+ // List of additional cflags that should be used to build the recovery
+ // variant of the C/C++ module.
+ Cflags []string
+ }
}
Proto struct {
@@ -211,6 +224,10 @@
deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
}
+ if compiler.hasSrcExt(".sysprop") {
+ deps.SharedLibs = append(deps.SharedLibs, "libbase")
+ }
+
if Bool(compiler.Properties.Openmp) {
deps.StaticLibs = append(deps.StaticLibs, "libomp")
}
@@ -245,6 +262,8 @@
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
@@ -286,7 +305,7 @@
// 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())
// 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
@@ -317,14 +336,15 @@
"-D__ANDROID_API__="+version, "-D__ANDROID_VNDK__")
}
+ if ctx.inRecovery() {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_RECOVERY__")
+ }
+
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 +354,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 +380,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 +401,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" {
@@ -419,16 +425,6 @@
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 {
cStd = gnuToCReplacer.Replace(cStd)
cppStd = gnuToCReplacer.Replace(cppStd)
@@ -441,6 +437,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,6 +493,11 @@
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) {
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index b2b0764..6a63828 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -43,27 +43,30 @@
"-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",
},
"cortex-a55": []string{
- // The cortex-a55 target is not yet supported,
- // so use cortex-a53.
- "-mcpu=cortex-a53",
+ "-mcpu=cortex-a55",
},
"cortex-a75": []string{
- // Use the cortex-a53 since it is similar to the little
+ // Use the cortex-a55 since it is similar to the little
// core (cortex-a55) and is sensitive to ordering.
- // The cortex-a55 target is not yet supported.
- "-mcpu=cortex-a53",
+ "-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",
},
"exynos-m1": []string{
"-mcpu=exynos-m1",
@@ -72,8 +75,6 @@
"-mcpu=exynos-m2",
},
}
-
- arm64ClangCpuVariantCflags = copyVariantFlags(arm64CpuVariantCflags)
)
const (
@@ -86,71 +87,49 @@
"armv8_2a",
"cortex-a53",
"cortex-a55",
+ "cortex-a72",
"cortex-a73",
"cortex-a75",
+ "cortex-a76",
"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}",
@@ -160,8 +139,10 @@
"": "",
"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}",
"exynos-m1": "${config.Arm64ClangExynosM1Cflags}",
"exynos-m2": "${config.Arm64ClangExynosM2Cflags}",
@@ -172,7 +153,7 @@
toolchain64Bit
ldflags string
- toolchainCflags string
+ lldflags string
toolchainClangCflags string
}
@@ -192,22 +173,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}"
}
@@ -228,11 +193,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"
}
@@ -251,7 +220,7 @@
var extraLdflags string
switch arch.CpuVariant {
- case "cortex-a53", "cortex-a73", "kryo", "exynos-m1", "exynos-m2",
+ case "cortex-a53", "cortex-a72", "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":
@@ -263,7 +232,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/arm_device.go b/cc/config/arm_device.go
index 4e0f6e0..75f5962 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",
@@ -66,7 +68,7 @@
},
}
- armCpuVariantCflags = map[string][]string{
+ armClangCpuVariantCflags = map[string][]string{
"cortex-a7": []string{
"-mcpu=cortex-a7",
"-mfpu=neon-vfpv4",
@@ -98,7 +100,7 @@
"-D__ARM_FEATURE_LPAE=1",
},
"cortex-a55": []string{
- "-mcpu=cortex-a53",
+ "-mcpu=cortex-a55",
"-mfpu=neon-fp-armv8",
// Fake an ARM compiler flag as these processors support LPAE which GCC/clang
// don't advertise.
@@ -107,7 +109,16 @@
"-D__ARM_FEATURE_LPAE=1",
},
"cortex-a75": []string{
- "-mcpu=cortex-a53",
+ "-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",
+ },
+ "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.
@@ -116,7 +127,7 @@
"-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.
@@ -136,9 +147,6 @@
"-D__ARM_FEATURE_LPAE=1",
},
}
-
- armClangCpuVariantCflags = copyVariantFlags(armCpuVariantCflags)
- armClangArchVariantCflags = copyVariantFlags(armArchVariantCflags)
)
const (
@@ -160,8 +168,10 @@
"cortex-a53",
"cortex-a53-a57",
"cortex-a55",
+ "cortex-a72",
"cortex-a73",
"cortex-a75",
+ "cortex-a76",
"krait",
"kryo",
"exynos-m1",
@@ -171,52 +181,20 @@
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
@@ -251,29 +229,6 @@
}
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}",
@@ -288,6 +243,7 @@
"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}",
@@ -300,8 +256,9 @@
type toolchainArm struct {
toolchain32Bit
- ldflags string
- toolchainCflags, toolchainClangCflags string
+ ldflags string
+ lldflags string
+ toolchainClangCflags string
}
func (t *toolchainArm) Name() string {
@@ -320,38 +277,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 +307,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 +322,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))
@@ -419,11 +354,11 @@
}
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..5c82604 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,25 @@
"--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,--icf=safe",
+ "-Wl,--fix-cortex-a8",
+ "-Wl,--no-fix-cortex-a8",
+ "-Wl,-m,aarch64_elf64_le_vec",
+})
+
+var ClangLibToolingUnknownCflags []string = nil
func init() {
pctx.StaticVariable("ClangExtraCflags", strings.Join([]string{
"-D__compiler_offsetof=__builtin_offsetof",
+ // Make implicit fallthrough an error in the future.
+ "-Wimplicit-fallthrough",
+ "-Wno-error=implicit-fallthrough",
+
// Help catch common 32/64-bit errors.
"-Werror=int-conversion",
@@ -117,9 +128,9 @@
// 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-deprecated-register",
+ "-Wno-sign-compare",
}, " "))
pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
@@ -155,6 +166,9 @@
// 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",
// http://b/72331524 Allow null pointer arithmetic until the instances detected by
// this new warning are fixed.
@@ -164,6 +178,27 @@
// warning are fixed.
"-Wno-enum-compare",
"-Wno-enum-compare-switch",
+
+ // Disable c++98-specific warning since Android is not concerned with C++98
+ // compatibility.
+ "-Wno-c++98-compat-extra-semi",
+
+ // Disable this warning until we can fix all instances where it fails.
+ "-Wno-self-assign-overloaded",
+
+ // Disable this warning until we can fix all instances where it fails.
+ "-Wno-constant-logical-operand",
+
+ // Disable this warning because we don't care about behavior with older compilers.
+ "-Wno-return-std-move-in-c++11",
+
+ // Disable this warning until we can fix all instances where it fails.
+ "-Wno-dangling-field",
+ }, " "))
+
+ // Extra cflags for projects under external/ directory
+ pctx.StaticVariable("ClangExtraExternalCflags", strings.Join([]string{
+ // TODO(yikong): Move -Wno flags here
}, " "))
}
@@ -178,6 +213,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..330cb10 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -15,7 +15,6 @@
package config
import (
- "fmt"
"runtime"
"strings"
@@ -87,12 +86,23 @@
"-Wl,--no-undefined-version",
}
+ deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
+ []string{
+ // TODO(b/109657296): needs --no-rosegment until Android
+ // stack unwinder can handle the read-only segment.
+ "-Wl,--no-rosegment",
+ "-Wl,--pack-dyn-relocs=android",
+ "-fuse-ld=lld",
+ }...)
+
hostGlobalCflags = []string{}
hostGlobalCppflags = []string{}
hostGlobalLdflags = []string{}
+ hostGlobalLldflags = []string{"-fuse-ld=lld"}
+
commonGlobalCppflags = []string{
"-Wsign-promo",
}
@@ -108,7 +118,6 @@
CStdVersion = "gnu99"
CppStdVersion = "gnu++14"
- GccCppStdVersion = "gnu++11"
ExperimentalCStdVersion = "gnu11"
ExperimentalCppStdVersion = "gnu++1z"
@@ -116,8 +125,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-4691093"
- ClangDefaultShortVersion = "6.0.2"
+ ClangDefaultVersion = "clang-r339409b"
+ ClangDefaultShortVersion = "8.0.2"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -136,17 +145,13 @@
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}"), " "))
@@ -160,6 +165,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",
@@ -194,6 +201,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 != "" {
@@ -241,10 +249,3 @@
"-isystem bionic/libc/kernel/android/uapi",
}, " ")
}
-
-func replaceFirst(slice []string, from, to string) {
- if slice[0] != from {
- panic(fmt.Errorf("Expected %q, found %q", from, to))
- }
- slice[0] = to
-}
diff --git a/cc/config/mips64_device.go b/cc/config/mips64_device.go
index a6191b6..561d8d6 100644
--- a/cc/config/mips64_device.go
+++ b/cc/config/mips64_device.go
@@ -69,9 +69,6 @@
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 +80,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 +87,8 @@
type toolchainMips64 struct {
toolchain64Bit
- cflags, clangCflags string
- toolchainCflags, toolchainClangCflags string
+ clangCflags string
+ toolchainClangCflags string
}
func (t *toolchainMips64) Name() string {
@@ -111,22 +107,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 +135,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..8cd35b3 100644
--- a/cc/config/mips_device.go
+++ b/cc/config/mips_device.go
@@ -111,9 +111,6 @@
"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 +122,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 +129,8 @@
type toolchainMips struct {
toolchain32Bit
- cflags, clangCflags string
- toolchainCflags, toolchainClangCflags string
+ clangCflags string
+ toolchainClangCflags string
}
func (t *toolchainMips) Name() string {
@@ -153,26 +149,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 +181,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/x86_64_device.go b/cc/config/x86_64_device.go
index 12f3e6f..ff8a6da 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -32,6 +32,8 @@
"-Wl,--hash-style=gnu",
}
+ x86_64Lldflags = ClangFilterUnknownLldflags(x86_64Ldflags)
+
x86_64ArchVariantCflags = map[string][]string{
"": []string{
"-march=x86-64",
@@ -123,14 +125,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 +142,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 +149,7 @@
type toolchainX86_64 struct {
toolchain64Bit
- toolchainCflags, toolchainClangCflags string
+ toolchainClangCflags string
}
func (t *toolchainX86_64) Name() string {
@@ -167,26 +168,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 +196,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_darwin_host.go b/cc/config/x86_darwin_host.go
index cae9757..09632db 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(
@@ -136,25 +117,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 +160,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 +179,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 +223,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..fc0b1d8 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",
@@ -147,14 +149,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 +166,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 +173,7 @@
type toolchainX86 struct {
toolchain32Bit
- toolchainCflags, toolchainClangCflags string
+ toolchainClangCflags string
}
func (t *toolchainX86) Name() string {
@@ -191,26 +192,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 +220,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..5fb88e6 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,6 +117,10 @@
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
@@ -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..8da21b3 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",
@@ -129,27 +122,18 @@
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 +173,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 +186,7 @@
}
func (t *toolchainLinuxX86) ClangCppflags() string {
- return "${config.LinuxClangCppflags} ${config.LinuxX86ClangCppflags}"
+ return ""
}
func (t *toolchainLinuxX8664) ClangTriple() string {
@@ -234,17 +198,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..6300a1b 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -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,20 +75,25 @@
"-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",
@@ -123,24 +123,19 @@
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, " "))
@@ -181,26 +176,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 +188,6 @@
return "-F pe-x86-64"
}
-func (t *toolchainWindows) ClangSupported() bool {
- return false
-}
-
func (t *toolchainWindowsX86) ClangTriple() string {
return "i686-windows-gnu"
}
@@ -245,10 +216,18 @@
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 *toolchainWindows) ShlibSuffix() string {
return ".dll"
}
diff --git a/cc/gen.go b/cc/gen.go
index f22a783..29a2bb2 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -19,17 +19,20 @@
// 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 +51,20 @@
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-output-dir=$headerOutDir --source-output-dir=$srcOutDir --include-name=$includeName $in",
+ CommandDeps: []string{"$syspropCmd"},
+ },
+ "headerOutDir", "srcOutDir", "includeName")
+
windmc = pctx.AndroidStaticRule("windmc",
blueprint.RuleParams{
Command: "$windmcCmd -r$$(dirname $out) -h$$(dirname $out) $in",
@@ -82,7 +92,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 +116,26 @@
})
}
+func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Path) {
+ headerFile := android.PathForModuleGen(ctx, "sysprop", "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()),
+ "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")
@@ -169,6 +198,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..c49d197 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,33 @@
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):
"""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:
+ if 'vndk' in version.tags and not vndk:
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):
+ """Returns True if the symbol should be omitted."""
+ if not vndk and 'vndk' in symbol.tags:
+ 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 +201,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):
@@ -221,9 +242,12 @@
class SymbolFileParser(object):
"""Parses NDK symbol files."""
- def __init__(self, input_file, api_map):
+ def __init__(self, input_file, api_map, arch, api, vndk):
self.input_file = input_file
self.api_map = api_map
+ self.arch = arch
+ self.api = api
+ self.vndk = vndk
self.current_line = None
def parse(self):
@@ -235,8 +259,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):
+ continue
+
+ for symbol in version.symbols:
+ if should_omit_symbol(symbol, self.arch, self.api, self.vndk):
+ 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 +326,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.')
@@ -324,20 +377,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):
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):
continue
if symbol_versioned_in_api(symbol.tags, self.api):
@@ -432,7 +479,11 @@
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).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:
diff --git a/cc/genrule.go b/cc/genrule.go
index 51c0d16..a672992 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -23,13 +23,18 @@
android.RegisterModuleType("cc_genrule", genRuleFactory)
}
+type GenruleExtraProperties struct {
+ Vendor_available *bool
+ Recovery_available *bool
+}
+
// 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/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/library.go b/cc/library.go
index 76f8a8c..920292d 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -21,6 +21,7 @@
"github.com/google/blueprint/pathtools"
"android/soong/android"
+ "android/soong/cc/config"
)
type LibraryProperties struct {
@@ -43,8 +44,6 @@
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"`
// local file name to pass to the linker as -force_symbols_not_weak_list
@@ -64,12 +63,6 @@
// 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"`
- }
- }
Static_ndk_lib *bool
}
@@ -217,7 +210,6 @@
flagExporter
stripper
- relocationPacker
// If we're used as a whole_static_lib, our missing dependencies need
// to be given
@@ -230,8 +222,6 @@
// shlib suffix.
libName string
- sanitize *sanitize
-
sabi *sabi
// Output archive of gcno coverage information files
@@ -247,6 +237,9 @@
// not included in the NDK.
ndkSysrootPath android.Path
+ // Location of the linked, unstripped library for shared libraries
+ unstrippedOutputFile android.Path
+
// Decorated interafaces
*baseCompiler
*baseLinker
@@ -260,8 +253,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 +274,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 +295,7 @@
}
} else {
f = append(f,
- sharedFlag,
+ "-shared",
"-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
}
@@ -361,7 +348,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() {
@@ -431,14 +418,12 @@
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)
-
- library.relocationPacker.packingInit(ctx)
}
func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
@@ -476,12 +461,15 @@
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)
}
+ 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)
+ }
- 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)
return deps
}
@@ -520,23 +508,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 +522,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())
@@ -570,28 +542,25 @@
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) {
+ // b/80093681, GNU strip/objcopy bug.
+ // Use llvm-{strip,objcopy} when clang lld is used.
+ builderFlags.stripUseLlvmStrip = library.baseLinker.useClangLld(ctx)
strippedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
library.stripper.strip(ctx, outputFile, strippedOutputFile, builderFlags)
}
+ library.unstrippedOutputFile = outputFile
+
if Bool(library.baseLinker.Properties.Use_version_lib) && ctx.Host() {
versionedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
@@ -601,25 +570,6 @@
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.SharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
linkerDeps = append(linkerDeps, objs.tidyFiles...)
@@ -640,15 +590,34 @@
return ret
}
+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() {
@@ -659,21 +628,15 @@
}
exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags)
- if refSourceDumpFile.Valid() {
- unzippedRefDump := UnzipRefDump(ctx, refSourceDumpFile.Path(), fileName)
+
+ refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
+ if refAbiDumpFile != nil {
library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
- unzippedRefDump, fileName, exportedHeaderFlags, ctx.isVndkExt())
+ refAbiDumpFile, fileName, exportedHeaderFlags, 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 {
@@ -715,21 +678,27 @@
}
}
+ if library.baseCompiler.hasSrcExt(".sysprop") {
+ flags := []string{
+ "-I" + android.PathForModuleGen(ctx, "sysprop", "include").String(),
+ }
+ library.reexportFlags(flags)
+ library.reuseExportedFlags = append(library.reuseExportedFlags, flags...)
+ }
+
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 {
@@ -765,10 +734,10 @@
}
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() {
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,
@@ -825,9 +794,8 @@
BuildStatic: true,
},
baseCompiler: NewBaseCompiler(),
- baseLinker: NewBaseLinker(),
+ baseLinker: NewBaseLinker(module.sanitize),
baseInstaller: NewBaseInstaller("lib", "lib64", InstallInSystem),
- sanitize: module.sanitize,
sabi: module.sabi,
}
@@ -855,7 +823,7 @@
}
}
-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
diff --git a/cc/linker.go b/cc/linker.go
index bcedc8d..28f4747 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
+ "android/soong/cc/config"
"fmt"
"github.com/google/blueprint"
@@ -58,6 +59,12 @@
// don't link in libgcc.a
No_libgcc *bool
+ // don't link in libclang_rt.builtins-*.a
+ No_libcrt *bool
+
+ // 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,15 +92,41 @@
// 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 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
+ }
+ Recovery struct {
// 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
}
}
@@ -101,16 +134,36 @@
Use_version_lib *bool `android:"arch_variant"`
}
-func NewBaseLinker() *baseLinker {
- return &baseLinker{}
+// TODO(http://b/80437643): BaseLinkerProperties is getting too big,
+// more than 2^16 bytes. New properties are defined in MoreBaseLinkerProperties.
+type MoreBaseLinkerProperties struct {
+ // 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:"arch_variant"`
+
+ Target struct {
+ Vendor struct {
+ // version script for this vendor variant
+ Version_script *string `android:"arch_variant"`
+ }
+ }
+}
+
+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
+ MoreProperties MoreBaseLinkerProperties
dynamicProperties struct {
RunPaths []string `blueprint:"mutated"`
}
+
+ sanitize *sanitize
}
func (linker *baseLinker) appendLdflags(flags []string) {
@@ -126,14 +179,15 @@
}
func (linker *baseLinker) linkerProps() []interface{} {
- return []interface{}{&linker.Properties, &linker.dynamicProperties}
+ return []interface{}{&linker.Properties, &linker.MoreProperties, &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...)
@@ -148,16 +202,33 @@
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 = 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
+ // Allow individual projects to opt out of libcrt,builtins
+ // b/117565638
+ if !Bool(linker.Properties.No_libcrt) {
+ // libclang_rt.builtins, libgcc and libatomic have to be last on the command line
+ // TODO: Also enable for libc and libm
+ if ctx.ModuleName() != "libc" && ctx.ModuleName() != "libm" {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+ }
+ }
+
deps.LateStaticLibs = append(deps.LateStaticLibs, "libatomic")
if !Bool(linker.Properties.No_libgcc) {
deps.LateStaticLibs = append(deps.LateStaticLibs, "libgcc")
@@ -198,9 +269,31 @@
deps.LateStaticLibs = append(deps.LateStaticLibs, "libwinpthread")
}
+ android.ExtractSourceDeps(ctx, linker.MoreProperties.Version_script)
+ android.ExtractSourceDeps(ctx,
+ linker.MoreProperties.Target.Vendor.Version_script)
+
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
+}
+
+// ModuleContext extends BaseModuleContext
+// BaseModuleContext should know if LLD is used?
func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
toolchain := ctx.toolchain()
@@ -209,7 +302,14 @@
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.MoreProperties.Pack_relocations, true) {
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=none")
+ }
+ } 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,10 +319,10 @@
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() {
@@ -269,16 +369,38 @@
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
}
+ versionScript := ctx.ExpandOptionalSource(
+ linker.MoreProperties.Version_script, "version_script")
+
+ if ctx.useVndk() && linker.MoreProperties.Target.Vendor.Version_script != nil {
+ versionScript = ctx.ExpandOptionalSource(
+ linker.MoreProperties.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)
+ }
+ }
+ }
+
return flags
}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 6e64acf..32da059 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -144,17 +144,17 @@
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{}
}
@@ -172,6 +172,7 @@
libraryDecorator: library,
}
stub.Properties.Vendor_available = BoolPtr(true)
+ module.Properties.UseVndk = true
module.compiler = stub
module.linker = stub
module.installer = nil
diff --git a/cc/lto.go b/cc/lto.go
index 2ced124..52b9a34 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,28 +72,44 @@
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"
+
} 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,-plugin-opt,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,-plugin-opt,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.
- if !ctx.isPgoCompile() {
+ if !ctx.isPgoCompile() && !lto.useClangLld(ctx) {
flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-inline-threshold=0")
flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-unroll-threshold=0")
}
diff --git a/cc/makevars.go b/cc/makevars.go
index 3bb00a1..32674a9 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -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, " "))
@@ -123,25 +125,32 @@
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,13 +172,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]
+ crossTargets := ctx.Config().Targets[android.Windows]
if len(crossTargets) > 0 {
makeVarsToolchain(ctx, "", crossTargets[0])
if len(crossTargets) > 1 {
@@ -177,7 +186,7 @@
}
}
- 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])
@@ -211,28 +220,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,70 +229,76 @@
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"))
+ ctx.Strict(makePrefix+"STRIP", gccCmd(toolchain, "strip"))
}
if target.Os == android.Windows {
@@ -315,18 +308,15 @@
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 {
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..cdf63d8 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,6 +34,13 @@
CommandDeps: []string{"$versionerCmd"},
},
"depsPath", "srcDir", "outDir")
+
+ preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader",
+ blueprint.RuleParams{
+ Command: "$preprocessor -o $out $in",
+ CommandDeps: []string{"$preprocessor"},
+ },
+ "preprocessor")
)
func init() {
@@ -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 {
@@ -65,6 +72,9 @@
// List of headers to install. Glob compatible. Common case is "include/**/*.h".
Srcs []string
+ // Source paths that should be excluded from the srcs glob.
+ Exclude_srcs []string
+
// Path to the NOTICE file associated with the headers.
License *string
}
@@ -72,7 +82,7 @@
type headerModule struct {
android.ModuleBase
- properties headerProperies
+ properties headerProperties
installPaths android.Paths
licensePath android.ModuleSrcPath
@@ -128,7 +138,7 @@
return
}
- srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
+ srcFiles := ctx.ExpandSources(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 +164,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: "",
@@ -181,19 +191,19 @@
// 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
}
-func (m *preprocessedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (m *versionedHeaderModule) 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")
}
@@ -248,7 +258,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 +273,92 @@
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
+}
+
+type preprocessedHeadersModule struct {
+ android.ModuleBase
+
+ properties preprocessedHeadersProperties
+
+ installPaths android.Paths
+ licensePath android.ModuleSrcPath
+}
+
+func (m *preprocessedHeadersModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+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 := ctx.ExpandSources(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..63d9f29 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -39,6 +39,7 @@
ndkPrebuiltSharedLibs = []string{
"android",
+ "binder_ndk",
"c",
"dl",
"EGL",
@@ -60,7 +61,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.
@@ -156,7 +157,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 +175,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
}
@@ -318,7 +319,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())
}
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..80b5c6a 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -59,6 +59,7 @@
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)
@@ -107,7 +108,12 @@
licensePaths = append(licensePaths, m.licensePath)
}
- if m, ok := module.(*preprocessedHeaderModule); ok {
+ if m, ok := module.(*versionedHeaderModule); ok {
+ installPaths = append(installPaths, m.installPaths...)
+ licensePaths = append(licensePaths, m.licensePath)
+ }
+
+ if m, ok := module.(*preprocessedHeadersModule); ok {
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
@@ -126,6 +132,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..0010621 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,13 @@
Properties ObjectLinkerProperties
}
-func objectFactory() android.Module {
+func ObjectFactory() android.Module {
module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
module.linker = &objectLinker{
- baseLinker: NewBaseLinker(),
+ baseLinker: NewBaseLinker(nil),
}
module.compiler = NewBaseCompiler()
+ module.stl = &stl{}
return module.Init()
}
@@ -63,11 +64,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
}
diff --git a/cc/pgo.go b/cc/pgo.go
index d39e429..a341ab9 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -19,6 +19,8 @@
"path/filepath"
"strings"
+ "github.com/google/blueprint/proptools"
+
"android/soong/android"
"android/soong/cc/config"
)
@@ -160,13 +162,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 +254,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..47994a8 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -51,6 +51,24 @@
var _ prebuiltLinkerInterface = (*prebuiltLibraryLinker)(nil)
+func (p *prebuiltLibraryLinker) linkerInit(ctx BaseModuleContext) {}
+
+func (p *prebuiltLibraryLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ // export_header_lib_headers needs to be passed along
+ return Deps{
+ HeaderLibs: p.baseLinker.Properties.Header_libs,
+ ReexportHeaderLibHeaders: p.baseLinker.Properties.Export_header_lib_headers,
+ }
+}
+
+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 {
// TODO(ccross): verify shared library dependencies
@@ -58,8 +76,28 @@
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
@@ -118,17 +156,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
diff --git a/cc/proto.go b/cc/proto.go
index 42bb536..6e6f95e 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -19,20 +19,23 @@
"github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
- "github.com/google/blueprint/proptools"
"android/soong/android"
)
func init() {
pctx.HostBinToolVariable("protocCmd", "aprotoc")
+ pctx.HostBinToolVariable("depFixCmd", "dep_fixer")
}
var (
proto = pctx.AndroidStaticRule("protoc",
blueprint.RuleParams{
- Command: "$protocCmd --cpp_out=$protoOutParams:$outDir -I $protoBase $protoFlags $in",
- CommandDeps: []string{"$protocCmd"},
+ Command: "$protocCmd --cpp_out=$protoOutParams:$outDir --dependency_out=$out.d -I $protoBase $protoFlags $in && " +
+ `$depFixCmd $out.d`,
+ CommandDeps: []string{"$protocCmd", "$depFixCmd"},
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
}, "protoFlags", "protoOutParams", "protoBase", "outDir")
)
@@ -54,10 +57,11 @@
}
ctx.Build(pctx, android.BuildParams{
- Rule: proto,
- Description: "protoc " + protoFile.Rel(),
- Outputs: android.WritablePaths{ccFile, headerFile},
- Input: protoFile,
+ Rule: proto,
+ Description: "protoc " + protoFile.Rel(),
+ Output: ccFile,
+ ImplicitOutput: headerFile,
+ Input: protoFile,
Args: map[string]string{
"outDir": android.ProtoDir(ctx).String(),
"protoFlags": protoFlags,
@@ -114,7 +118,7 @@
flags.protoFlags = android.ProtoFlags(ctx, p)
- if proptools.String(p.Proto.Type) == "lite" {
+ if String(p.Proto.Type) == "lite" {
flags.protoOutParams = append(flags.protoOutParams, "lite")
}
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..7c9f5d3 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -49,6 +49,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 +61,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 f5a7c77..4a86499 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -71,12 +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.
- if ctx.Arch().CpuVariant == "exynos-m2" {
- flags.ToolingCFlags = append(flags.ToolingCFlags, "-mcpu=cortex-a53")
- }
+ flags.ToolingCppFlags = filterOutWithPrefix(flags.CppFlags, config.ClangLibToolingUnknownCflags)
return flags
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index de97035..330a5e3 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -21,6 +21,8 @@
"strings"
"sync"
+ "github.com/google/blueprint"
+
"android/soong/android"
"android/soong/cc/config"
)
@@ -34,15 +36,23 @@
asanLdflags = []string{"-Wl,-u,__asan_preinit"}
asanLibs = []string{"libasan"}
+ hwasanCflags = []string{"-mllvm", "-hwasan-with-ifunc=0", "-fno-omit-frame-pointer", "-Wno-frame-larger-than="}
+
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"}
+ minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
+ "-fno-sanitize-recover=integer,undefined"}
+ hwasanGlobalOptions = []string{"heap_history_size=4095"}
)
type sanitizerType int
@@ -57,6 +67,7 @@
const (
asan sanitizerType = iota + 1
+ hwasan
tsan
intOverflow
cfi
@@ -66,6 +77,8 @@
switch t {
case asan:
return "asan"
+ case hwasan:
+ return "hwasan"
case tsan:
return "tsan"
case intOverflow:
@@ -83,8 +96,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 +108,7 @@
Safestack *bool `android:"arch_variant"`
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
+ Scudo *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.
@@ -115,6 +130,7 @@
SanitizerEnabled bool `blueprint:"mutated"`
SanitizeDep bool `blueprint:"mutated"`
MinimalRuntimeDep bool `blueprint:"mutated"`
+ UbsanRuntimeDep bool `blueprint:"mutated"`
InSanitizerDir bool `blueprint:"mutated"`
}
@@ -127,6 +143,7 @@
func init() {
android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
+ android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
}
func (sanitize *sanitize) props() []interface{} {
@@ -149,15 +166,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 +216,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 +271,25 @@
s.Diag.Cfi = nil
}
+ // HWASan requires AArch64 hardware feature (top-byte-ignore).
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Hwaddress = 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 +298,11 @@
s.Diag.Cfi = nil
}
+ // HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
+ if ctx.inRecovery() {
+ s.Hwaddress = nil
+ }
+
if ctx.staticBinary() {
s.Address = nil
s.Coverage = nil
@@ -280,10 +321,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)) {
sanitize.Properties.SanitizerEnabled = true
}
+ // Disable Scudo if ASan or TSan is enabled.
+ if Bool(s.Address) || Bool(s.Thread) || Bool(s.Hwaddress) {
+ 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"`)
@@ -313,14 +365,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
@@ -385,6 +433,11 @@
diagSanitizers = append(diagSanitizers, "address")
}
+ if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ flags.CFlags = append(flags.CFlags, hwasanCflags...)
+ sanitizers = append(sanitizers, "hwaddress")
+ }
+
if Bool(sanitize.Properties.Sanitize.Thread) {
sanitizers = append(sanitizers, "thread")
}
@@ -406,18 +459,13 @@
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")
@@ -430,21 +478,24 @@
}
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")
- }
+ 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")
}
}
+ if Bool(sanitize.Properties.Sanitize.Scudo) {
+ sanitizers = append(sanitizers, "scudo")
+ }
+
if len(sanitizers) > 0 {
sanitizeArg := "-fsanitize=" + strings.Join(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)
@@ -476,17 +527,30 @@
runtimeLibrary := ""
if Bool(sanitize.Properties.Sanitize.Address) {
runtimeLibrary = config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
+ } else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(ctx.toolchain())
} else if Bool(sanitize.Properties.Sanitize.Thread) {
runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(ctx.toolchain())
- } else if len(diagSanitizers) > 0 {
+ } else if Bool(sanitize.Properties.Sanitize.Scudo) {
+ if len(diagSanitizers) == 0 && !sanitize.Properties.UbsanRuntimeDep {
+ runtimeLibrary = config.ScudoMinimalRuntimeLibrary(ctx.toolchain())
+ } else {
+ runtimeLibrary = config.ScudoRuntimeLibrary(ctx.toolchain())
+ }
+ } else if len(diagSanitizers) > 0 || sanitize.Properties.UbsanRuntimeDep {
runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(ctx.toolchain())
}
if runtimeLibrary != "" {
+ runtimeLibraryPath := "${config.ClangAsanLibDir}/" + runtimeLibrary
+ if !ctx.static() {
+ runtimeLibraryPath = runtimeLibraryPath + ctx.toolchain().ShlibSuffix()
+ } else {
+ runtimeLibraryPath = runtimeLibraryPath + ".a"
+ }
+
// ASan runtime library must be the first in the link order.
- flags.libFlags = append([]string{
- "${config.ClangAsanLibDir}/" + runtimeLibrary + ctx.toolchain().ShlibSuffix(),
- }, flags.libFlags...)
+ flags.libFlags = append([]string{runtimeLibraryPath}, flags.libFlags...)
sanitize.runtimeLibrary = runtimeLibrary
// When linking against VNDK, use the vendor variant of the runtime lib
@@ -517,6 +581,9 @@
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"
+ }
}
func (sanitize *sanitize) inSanitizerDir() bool {
@@ -527,6 +594,8 @@
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:
@@ -540,12 +609,14 @@
func (sanitize *sanitize) isUnsanitizedVariant() bool {
return !sanitize.isSanitizerEnabled(asan) &&
+ !sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(cfi)
}
func (sanitize *sanitize) isVariantOnProductionDevice() bool {
return !sanitize.isSanitizerEnabled(asan) &&
+ !sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan)
}
@@ -556,13 +627,14 @@
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)
default:
panic(fmt.Errorf("unknown sanitizerType %d", t))
}
@@ -596,38 +668,58 @@
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 {
+ if d.static() {
+ d.sanitize.Properties.SanitizeDep = true
+ }
+ } else {
d.sanitize.Properties.SanitizeDep = true
}
}
+ return true
})
}
}
}
// 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
+ })
}
}
@@ -688,6 +780,34 @@
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
@@ -701,8 +821,21 @@
}).(*[]string)
}
+func hwasanStaticLibs(config android.Config) *[]string {
+ return config.Once("hwasanStaticLibs", func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
+func hwasanVendorStaticLibs(config android.Config) *[]string {
+ return config.Once("hwasanVendorStaticLibs", 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 +851,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..8eee612 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -19,18 +19,24 @@
"fmt"
)
-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))
}
}
@@ -75,9 +81,9 @@
}
} 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:
@@ -129,10 +135,16 @@
// 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")
+ }
+ 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 +177,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")
@@ -201,10 +227,10 @@
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.Windows: []string{"-Wl,--start-group", "-lmingw32", "-lgcc", "-lgcc_eh",
+ "-lmoldname", "-lmingwex", "-lmsvcr110", "-lmsvcrt", "-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..02397f4 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -21,6 +21,7 @@
type StripProperties struct {
Strip struct {
None *bool
+ All *bool
Keep_symbols *bool
}
}
@@ -30,17 +31,23 @@
}
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 !Bool(stripper.StripProperties.Strip.All) {
+ flags.stripKeepMiniDebugInfo = true
+ }
+ if ctx.Config().Debuggable() && !flags.stripKeepMiniDebugInfo {
+ flags.stripAddGnuDebuglink = true
+ }
TransformStrip(ctx, in, out, flags)
}
}
diff --git a/cc/test.go b/cc/test.go
index b8e9cdb..e6251d3 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -20,11 +20,21 @@
"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 in device.
+ Run_test_as string `android:"arch_variant"`
}
type TestBinaryProperties struct {
@@ -44,6 +54,17 @@
// 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"`
+
+ // 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"`
+
+ // Test options.
+ Test_options *TestOptions
}
func init() {
@@ -128,7 +149,7 @@
}
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 +180,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 +191,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 +221,7 @@
*baseCompiler
Properties TestBinaryProperties
data android.Paths
+ testConfig android.Path
}
func (test *testBinary) linkerProps() []interface{} {
@@ -209,6 +237,8 @@
func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
android.ExtractSourcesDeps(ctx, test.Properties.Data)
+ android.ExtractSourceDeps(ctx, test.Properties.Test_config)
+ android.ExtractSourceDeps(ctx, test.Properties.Test_config_template)
deps = test.testDecorator.linkerDeps(ctx, deps)
deps = test.binaryDecorator.linkerDeps(ctx, deps)
@@ -224,6 +254,16 @@
func (test *testBinary) install(ctx ModuleContext, file android.Path) {
test.data = ctx.ExpandSources(test.Properties.Data, nil)
+ // Append new line in template like below
+ // <option name="run-test-as" value="1234" />
+ optionsMap := map[string]string{}
+ if test.Properties.Test_options != nil {
+ optionsMap["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, optionsMap)
+
test.binaryDecorator.baseInstaller.dir = "nativetest"
test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
@@ -300,13 +340,22 @@
// 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:"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 benchmarkDecorator struct {
*binaryDecorator
Properties BenchmarkProperties
data android.Paths
+ testConfig android.Path
}
func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
@@ -326,6 +375,9 @@
func (benchmark *benchmarkDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
android.ExtractSourcesDeps(ctx, benchmark.Properties.Data)
+ android.ExtractSourceDeps(ctx, benchmark.Properties.Test_config)
+ android.ExtractSourceDeps(ctx, benchmark.Properties.Test_config_template)
+
deps = benchmark.binaryDecorator.linkerDeps(ctx, deps)
deps.StaticLibs = append(deps.StaticLibs, "libgoogle-benchmark")
return deps
@@ -333,6 +385,9 @@
func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
benchmark.data = ctx.ExpandSources(benchmark.Properties.Data, nil)
+ benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
+ benchmark.Properties.Test_config_template)
+
benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
benchmark.binaryDecorator.baseInstaller.install(ctx, file)
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 4a7b0f7..eead25b 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())
}
diff --git a/cc/test_gen_stub_libs.py b/cc/test_gen_stub_libs.py
index b20a5c7..3b5585a 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,106 @@
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))
self.assertTrue(
- gsl.should_omit_version('foo', ['introduced=14'], 'arm', 9, False))
+ gsl.should_omit_version(
+ gsl.Version('foo_PRIVATE', None, [], []), 'arm', 9, False))
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo_PLATFORM', None, [], []), 'arm', 9, False))
+
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['platform-only'], []), 'arm', 9,
+ False))
+
+ def test_omit_vndk(self):
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['vndk'], []), 'arm', 9, False))
+
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, True))
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['vndk'], []), 'arm', 9, True))
+
+ def test_omit_arch(self):
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, False))
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['arm'], []), 'arm', 9, False))
+
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['x86'], []), 'arm', 9, False))
+
+ def test_omit_api(self):
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, [], []), 'arm', 9, False))
+ self.assertFalse(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['introduced=9'], []), 'arm', 9,
+ False))
+
+ self.assertTrue(
+ gsl.should_omit_version(
+ gsl.Version('foo', None, ['introduced=14'], []), 'arm', 9,
+ False))
+
+
+class OmitSymbolTest(unittest.TestCase):
+ def test_omit_vndk(self):
+ self.assertTrue(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['vndk']), 'arm', 9, False))
+
+ self.assertFalse(
+ gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, True))
+ self.assertFalse(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['vndk']), 'arm', 9, True))
+
+ def test_omit_arch(self):
+ self.assertFalse(
+ gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False))
+ self.assertFalse(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['arm']), 'arm', 9, False))
+
+ self.assertTrue(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['x86']), 'arm', 9, False))
+
+ def test_omit_api(self):
+ self.assertFalse(
+ gsl.should_omit_symbol(gsl.Symbol('foo', []), 'arm', 9, False))
+ self.assertFalse(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['introduced=9']), 'arm', 9, False))
+
+ self.assertTrue(
+ gsl.should_omit_symbol(
+ gsl.Symbol('foo', ['introduced=14']), 'arm', 9, 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)
self.assertIsNone(parser.current_line)
self.assertEqual('foo', parser.next_line().strip())
@@ -223,7 +278,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 +287,7 @@
VERSION_2 {
} VERSION_1; # asdf
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False)
parser.next_line()
version = parser.parse_version()
@@ -253,31 +308,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)
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)
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)
parser.next_line()
symbol = parser.parse_symbol()
@@ -290,47 +345,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)
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)
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)
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)
parser.parse()
def test_parse(self):
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
local:
hidden1;
@@ -347,7 +402,7 @@
qwerty;
} VERSION_1;
"""))
- parser = gsl.SymbolFileParser(input_file, {})
+ parser = gsl.SymbolFileParser(input_file, {}, 'arm', 16, False)
versions = parser.parse()
expected = [
@@ -368,8 +423,8 @@
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()
+ src_file = io.StringIO()
+ version_file = io.StringIO()
generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
version = gsl.Version('VERSION_PRIVATE', None, [], [
@@ -396,8 +451,8 @@
def test_omit_symbol(self):
# Thorough testing of the cases involved here is handled by
# SymbolPresenceTest.
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
+ src_file = io.StringIO()
+ version_file = io.StringIO()
generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
version = gsl.Version('VERSION_1', None, [], [
@@ -422,8 +477,8 @@
self.assertEqual('', version_file.getvalue())
def test_write(self):
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
+ src_file = io.StringIO()
+ version_file = io.StringIO()
generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
versions = [
@@ -475,7 +530,7 @@
'P': 9001,
}
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
global:
foo; # var
@@ -505,11 +560,11 @@
wobble;
} VERSION_4;
"""))
- parser = gsl.SymbolFileParser(input_file, api_map)
+ parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9, False)
versions = parser.parse()
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
+ src_file = io.StringIO()
+ version_file = io.StringIO()
generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
generator.write(versions)
@@ -545,7 +600,7 @@
'Q': 9002,
}
- input_file = cStringIO.StringIO(textwrap.dedent("""\
+ input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
global:
foo; # introduced=O
@@ -555,11 +610,11 @@
*;
};
"""))
- parser = gsl.SymbolFileParser(input_file, api_map)
+ parser = gsl.SymbolFileParser(input_file, api_map, 'arm', 9001, False)
versions = parser.parse()
- src_file = cStringIO.StringIO()
- version_file = cStringIO.StringIO()
+ src_file = io.StringIO()
+ version_file = io.StringIO()
generator = gsl.Generator(src_file, version_file, 'arm', 9001, False)
generator.write(versions)
@@ -578,6 +633,38 @@
""")
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)
+
+ with self.assertRaises(gsl.MultiplyDefinedSymbolError) as cm:
+ parser.parse()
+ self.assertEquals(['bar', 'foo'],
+ cm.exception.multiply_defined_symbols)
+
def main():
suite = unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/cc/tidy.go b/cc/tidy.go
index 8ca94ef..ddb445a 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -62,16 +62,16 @@
return flags
}
- // Clang-tidy requires clang
- if !flags.Clang {
- return flags
- }
-
flags.Tidy = true
+ // 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.NinjaAndShellEscape
-
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 +83,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 {
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index 2bb4018..5811b01 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -23,11 +23,18 @@
//
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
+
+ Properties toolchainLibraryProperties
}
func (*toolchainLibraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
@@ -35,7 +42,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)
+}
+
+func ToolchainLibraryFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
toolchainLibrary := &toolchainLibraryDecorator{
@@ -43,7 +56,6 @@
}
module.compiler = toolchainLibrary
module.linker = toolchainLibrary
- module.Properties.Clang = BoolPtr(false)
module.stl = nil
module.sanitize = nil
module.installer = nil
@@ -58,16 +70,10 @@
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)
-
- ctx.CheckbuildFile(outputFile)
-
- return outputFile
+ return android.PathForSource(ctx, *library.Properties.Src)
}
diff --git a/cc/util.go b/cc/util.go
index 1e4a0c0..1412d54 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path/filepath"
"regexp"
"strings"
@@ -58,29 +59,29 @@
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, " "),
+ 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,
+ coverage: in.Coverage,
+ tidy: in.Tidy,
+ sAbiDump: in.SAbiDump,
+ protoRoot: in.ProtoRoot,
systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
@@ -102,3 +103,29 @@
}
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
+}
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..1a9b77a 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -15,6 +15,7 @@
package cc
import (
+ "errors"
"sort"
"strings"
"sync"
@@ -151,38 +152,42 @@
}
// 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 (
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 9c9545d..2d7274d 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,6 +54,10 @@
// 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"`
}
@@ -67,10 +72,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 +90,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 +130,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,6 +149,7 @@
module.stl = nil
module.sanitize = nil
library.StripProperties.Strip.None = BoolPtr(true)
+ module.Properties.UseVndk = true
prebuilt := &vndkPrebuiltLibraryDecorator{
libraryDecorator: library,
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..0647fb2
--- /dev/null
+++ b/cmd/dep_fixer/main.go
@@ -0,0 +1,67 @@
+// 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 <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 a single file as an argument")
+ }
+
+ old, err := ioutil.ReadFile(flag.Arg(0))
+ if err != nil {
+ log.Fatalf("Error opening %q: %v", flag.Arg(0), err)
+ }
+
+ deps, err := Parse(flag.Arg(0), bytes.NewBuffer(append([]byte(nil), old...)))
+ if err != nil {
+ log.Fatalf("Failed to parse: %v", err)
+ }
+
+ new := deps.Print()
+
+ if *output == "" || *output == flag.Arg(0) {
+ if !bytes.Equal(old, 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/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 74%
copy from ui/build/util_linux.go
copy to cmd/host_bionic_inject/Android.bp
index 0a4e1d2..acce683 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,8 @@
// 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"],
+}
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..0dabbba
--- /dev/null
+++ b/cmd/host_bionic_inject/host_bionic_inject.go
@@ -0,0 +1,174 @@
+// 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
+ }
+
+ found := false
+ for j, prog := range file.Progs {
+ if prog.Type != elf.PT_LOAD {
+ continue
+ }
+
+ if lprog.Vaddr+dlwrap_linker_offset.Value != prog.Vaddr {
+ continue
+ }
+ found = true
+
+ if lprog.Memsz != prog.Memsz {
+ return fmt.Errorf("Linker prog %d (0x%x) memsz (0x%x) does not match (0x%x)",
+ i, lprog.Vaddr, lprog.Memsz, prog.Memsz)
+ }
+
+ // The linker shouldn't be using BSS, since only one
+ // BSS section is supported per ELF file.
+ if prog.Memsz != prog.Filesz {
+ return fmt.Errorf("Embedded prog %d (0x%x) memsz (0x%x) does not match filesz (0x%x)",
+ j, prog.Vaddr, prog.Memsz, prog.Filesz)
+ }
+
+ 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 !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/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..8e71a97 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -24,7 +24,8 @@
"os"
"path/filepath"
"sort"
- "strings"
+
+ "github.com/google/blueprint/pathtools"
"android/soong/jar"
"android/soong/third_party/zip"
@@ -63,19 +64,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")
+ pyMain = flag.String("pm", "", "__main__.py file to insert in par")
entrypoint = flag.String("e", "", "par entrypoint file to insert in par")
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] [-e entrypoint] [-pm __main__.py] output [inputs...]")
flag.PrintDefaults()
}
@@ -113,7 +115,7 @@
log.Fatal(err)
}
defer reader.Close()
- namedReader := namedZipReader{path: input, reader: reader}
+ namedReader := namedZipReader{path: input, reader: &reader.Reader}
readers = append(readers, namedReader)
}
@@ -125,9 +127,13 @@
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, *entrypoint, *pyMain, *sortEntries, *emulateJar, *emulatePar,
+ *stripDirEntries, *ignoreDuplicates, []string(stripFiles), []string(stripDirs), map[string]bool(zipsToNotStrip))
if err != nil {
log.Fatal(err)
}
@@ -136,7 +142,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 +173,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 +199,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 +223,7 @@
String() string
IsDir() bool
CRC32() uint32
+ Size() uint64
WriteToZip(dest string, zw *zip.Writer) error
}
@@ -218,8 +233,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, entrypoint, 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 +259,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
}
@@ -268,6 +289,22 @@
addMapping("entry_point.txt", fileSource)
}
+ if pyMain != "" {
+ buf, err := ioutil.ReadFile(pyMain)
+ if err != nil {
+ return err
+ }
+ fh := &zip.FileHeader{
+ Name: "__main__.py",
+ Method: zip.Store,
+ UncompressedSize64: uint64(len(buf)),
+ }
+ fh.SetMode(0700)
+ fh.SetModTime(jar.DefaultTime)
+ fileSource := bufferEntry{fh, buf}
+ addMapping("__main__.py", fileSource)
+ }
+
if emulatePar {
// the runfiles packages needs to be populated with "__init__.py".
newPyPkgs := []string{}
@@ -317,8 +354,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 +378,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 +441,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..19fa5ed
--- /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..9cb75fa 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,33 @@
trace := tracer.New(log)
defer trace.Close()
+ stat := &status.Status{}
+ defer stat.Finish()
+ stat.AddOutput(terminal.NewStatusOutput(writer, ""))
+
+ 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 +230,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 +258,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 +270,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 +328,141 @@
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, ""))
+
+ config := build.NewConfig(ctx, flag.Args()...)
+ config.Environment().Set("OUT_DIR", outDir)
+ 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 {
+ // Save space, Kati doesn't notice
+ if f := config.KatiBuildNinjaFile(); f != "" {
+ os.Truncate(f, 0)
+ }
+ } else {
+ 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..a79c84f 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,6 +592,13 @@
}
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)
}
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..b603571 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -26,6 +26,8 @@
"android/soong/ui/build"
"android/soong/ui/logger"
+ "android/soong/ui/status"
+ "android/soong/ui/terminal"
"android/soong/ui/tracer"
)
@@ -44,7 +46,18 @@
}
func main() {
- log := logger.New(os.Stderr)
+ var stdio terminal.StdioInterface
+ stdio = terminal.StdioImpl{}
+
+ // dumpvar uses stdout, everything else should be in stderr
+ if os.Args[1] == "--dumpvar-mode" || os.Args[1] == "--dumpvars-mode" {
+ 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 +73,23 @@
trace := tracer.New(log)
defer trace.Close()
+ stat := &status.Status{}
+ defer stat.Finish()
+ stat.AddOutput(terminal.NewStatusOutput(writer, os.Getenv("NINJA_STATUS")))
+ 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,
+ Tracer: trace,
+ Writer: writer,
+ Status: stat,
}}
var config build.Config
if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" {
@@ -78,19 +98,19 @@
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")
}
+ 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")))
+
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 {
@@ -114,6 +134,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
diff --git a/cmd/zip2zip/zip2zip.go b/cmd/zip2zip/zip2zip.go
index e8ea9b9..c4fb3d6 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
@@ -149,7 +155,7 @@
newName = output
}
}
- includeMatches = append(includeMatches, pair{file, newName})
+ includeMatches = append(includeMatches, pair{file, newName, false})
}
}
@@ -157,10 +163,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 +175,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 +196,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 +212,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 +253,27 @@
}
}
-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
+}
diff --git a/cmd/zip2zip/zip2zip_test.go b/cmd/zip2zip/zip2zip_test.go
index 212ab28..ae16494 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,79 @@
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",
+ },
+ },
}
func errorString(e error) string {
@@ -282,7 +383,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 +396,22 @@
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)
}
})
}
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/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..e6c6efd 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -52,10 +52,9 @@
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 +62,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
@@ -88,7 +87,7 @@
Export_include_dirs []string
// list of input files
- Srcs []string
+ Srcs []string `android:"arch_variant"`
}
type Module struct {
@@ -102,8 +101,9 @@
taskGenerator taskFunc
- deps android.Paths
- rule blueprint.Rule
+ deps android.Paths
+ rule blueprint.Rule
+ rawCommand string
exportedIncludeDirs android.Paths
@@ -140,10 +140,14 @@
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)
}
}
}
@@ -158,14 +162,25 @@
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 {
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
@@ -193,16 +208,10 @@
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()})
} else {
ctx.ModuleErrorf("host tool %q missing output file", tool)
}
- default:
- ctx.ModuleErrorf("unknown dependency on %q", ctx.OtherModuleName(module))
}
})
}
@@ -211,33 +220,48 @@
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
- } else {
- ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool.Rel()], tool.Rel())
- }
+ for _, toolFile := range g.properties.Tool_files {
+ paths := ctx.ExpandSources([]string{toolFile}, nil)
+ g.deps = append(g.deps, paths...)
+ addLocationLabel(toolFile, paths.Strings())
+ }
+
+ var srcFiles android.Paths
+ for _, in := range g.properties.Srcs {
+ paths := ctx.ExpandSources([]string{in}, nil)
+ 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 +269,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 +277,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 +327,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 +385,17 @@
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 generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module {
module := &Module{
taskGenerator: taskGenerator,
@@ -451,7 +504,7 @@
type genRuleProperties struct {
// names of the output files that will be generated
- Out []string
+ Out []string `android:"arch_variant"`
}
var Bool = proptools.Bool
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
new file mode 100644
index 0000000..a99fa18
--- /dev/null
+++ b/genrule/genrule_test.go
@@ -0,0 +1,489 @@
+// 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"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+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("tool", android.ModuleFactoryAdaptor(toolFactory))
+ 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
+
+ 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",
+ },
+ }
+
+ 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"
+
+ ctx := testContext(config, bp, nil)
+
+ _, 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 gen.rawCommand != "'"+test.expect+"'" {
+ t.Errorf("want %q, got %q", test.expect, gen.rawCommand)
+ }
+ })
+ }
+
+}
+
+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) DepsMutator(ctx android.BottomUpMutatorContext) {}
+
+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 _ 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/aapt2.go b/java/aapt2.go
index 70c7507..5553bfd 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -111,7 +111,8 @@
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 '`,
diff --git a/java/aar.go b/java/aar.go
index 66f1cab..a06d191 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -27,6 +27,7 @@
ExportPackage() android.Path
ExportedProguardFlagFiles() android.Paths
ExportedStaticPackages() android.Paths
+ ExportedManifest() android.Path
}
func init() {
@@ -66,6 +67,7 @@
rroDirs android.Paths
rTxt android.Path
extraAaptPackagesFile android.Path
+ isLibrary bool
aaptProperties aaptProperties
}
@@ -74,8 +76,8 @@
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) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (flags []string,
+ deps android.Paths, resDirs, overlayDirs []globbedResourceDir, rroDirs android.Paths) {
hasVersionCode := false
hasVersionName := false
@@ -116,29 +118,17 @@
assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
}
- // App manifest file
- manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
- manifestPath = android.PathForModuleSrc(ctx, manifestFile)
linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
linkDeps = append(linkDeps, manifestPath)
linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-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 {
@@ -159,22 +149,35 @@
linkFlags = append(linkFlags, "--version-name ", versionName)
}
- return linkFlags, linkDeps, resDirs, overlayDirs, overlayFiles, rroDirs, manifestPath
+ return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs
}
-func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkVersion string) {
+func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkContext sdkContext) {
if !ctx.Config().UnbundledBuild() {
- sdkDep := decodeSdkDep(ctx, sdkVersion)
+ sdkDep := decodeSdkDep(ctx, sdkContext)
if sdkDep.frameworkResModule != "" {
- ctx.AddDependency(ctx.Module(), frameworkResTag, 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, 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)
+
+ linkFlags, linkDeps, resDirs, overlayDirs, rroDirs := a.aapt2Flags(ctx, sdkContext, manifestPath)
+
+ 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,16 +186,39 @@
// 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())
}
+
+ var compiledRes, compiledOverlay android.Paths
+
+ compiledOverlay = append(compiledOverlay, transitiveStaticLibs...)
+
+ if a.isLibrary {
+ // For a static library we treat all the resources equally with no overlay.
+ for _, compiledResDir := range compiledResDirs {
+ compiledRes = append(compiledRes, compiledResDir...)
+ }
+ } else 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 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...)
-
aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, extraPackages,
linkFlags, linkDeps, compiledRes, compiledOverlay)
@@ -206,14 +232,14 @@
}
// 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,
+ 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,6 +250,8 @@
}
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)
@@ -232,6 +260,7 @@
if exportPackage != nil {
transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
+ staticLibManifests = append(staticLibManifests, aarDep.ExportedManifest())
}
}
})
@@ -249,7 +278,7 @@
transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs)
- return transitiveStaticLibs, deps, flags
+ return transitiveStaticLibs, staticLibManifests, deps, flags
}
type AndroidLibrary struct {
@@ -272,17 +301,22 @@
return a.exportedStaticPackages
}
+func (a *AndroidLibrary) ExportedManifest() android.Path {
+ return a.manifestPath
+}
+
var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
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.isLibrary = true
+ a.aapt.buildActions(ctx, sdkContext(a))
ctx.CheckbuildFile(a.proguardOptionsFile)
ctx.CheckbuildFile(a.exportPackage)
@@ -327,7 +361,7 @@
module.androidLibraryProperties.BuildAAR = true
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ InitJavaModule(module, android.DeviceSupported)
return module
}
@@ -338,14 +372,19 @@
type AARImportProperties struct {
Aars []string
- 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_enabled *bool
}
type AARImport struct {
android.ModuleBase
+ android.DefaultableModuleBase
prebuilt android.Prebuilt
properties AARImportProperties
@@ -354,10 +393,22 @@
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()
+}
+
var _ AndroidLibraryDependency = (*AARImport)(nil)
func (a *AARImport) ExportPackage() android.Path {
@@ -372,6 +423,10 @@
return a.exportedStaticPackages
}
+func (a *AARImport) ExportedManifest() android.Path {
+ return a.manifest
+}
+
func (a *AARImport) Prebuilt() *android.Prebuilt {
return &a.prebuilt
}
@@ -382,14 +437,14 @@
func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) {
if !ctx.Config().UnbundledBuild() {
- sdkDep := decodeSdkDep(ctx, String(a.properties.Sdk_version))
+ 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 +462,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_enabled) {
+ 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 +508,12 @@
"--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, libDeps, libFlags := aaptLibs(ctx, sdkContext(a))
+
+ _ = staticLibManifests
linkDeps = append(linkDeps, libDeps...)
linkFlags = append(linkFlags, libFlags...)
@@ -470,10 +534,22 @@
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) AidlIncludeDirs() android.Paths {
return nil
}
+func (a *AARImport) ExportedSdkLibs() []string {
+ return nil
+}
+
var _ android.PrebuiltInterface = (*Import)(nil)
func AARImportFactory() android.Module {
@@ -482,6 +558,6 @@
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..168a22d
--- /dev/null
+++ b/java/android_manifest.go
@@ -0,0 +1,83 @@
+// 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/java/config"
+ "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.JavaCmd} -classpath ${config.ManifestMergerClasspath} com.android.manifmerger.Merger ` +
+ `--main $in $libs --out $out`,
+ CommandDeps: config.ManifestMergerClasspath,
+ },
+ "libs")
+
+func manifestMerger(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
+ staticLibManifests android.Paths, isLibrary bool) android.Path {
+
+ var args []string
+ if isLibrary {
+ args = append(args, "--library")
+ }
+
+ // Inject minSdkVersion into the manifest
+ fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: manifestFixerRule,
+ Input: manifest,
+ Output: fixedManifest,
+ Args: map[string]string{
+ "minSdkVersion": sdkVersionOrDefault(ctx, sdkContext.minSdkVersion()),
+ "targetSdkVersion": sdkVersionOrDefault(ctx, sdkContext.sdkVersion()),
+ "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/androidmk.go b/java/androidmk.go
index b85ecb4..359594c 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -19,15 +19,13 @@
"io"
"strings"
- "github.com/google/blueprint/proptools"
-
"android/soong/android"
)
func (library *Library) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
- OutputFile: android.OptionalPathForPath(library.implementationJarFile),
+ OutputFile: android.OptionalPathForPath(library.implementationAndResourcesJar),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
Extra: []android.AndroidMkExtraFunc{
func(w io.Writer, outputFile android.Path) {
@@ -39,7 +37,7 @@
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 {
@@ -58,17 +56,21 @@
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))
+ fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", library.sdkVersion())
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, " "))
+ }
+
// 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(), " "))
}
@@ -77,19 +79,19 @@
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
- if proptools.Bool(library.deviceProperties.Hostdex) && !library.Host() {
+ 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")
- fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationJarFile.String())
- if library.properties.Installable != nil && *library.properties.Installable == false {
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationAndResourcesJar.String())
+ 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())
}
- fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.implementationJarFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", library.headerJarFile.String())
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(data.Required, " "))
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
}
@@ -97,6 +99,26 @@
}
}
+func (j *Test) AndroidMk() android.AndroidMkData {
+ data := j.Library.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(j.testProperties.Test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(j.testProperties.Test_suites, " "))
+ } else {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+ }
+ if j.testConfig != nil {
+ fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", j.testConfig.String())
+ }
+ })
+
+ androidMkWriteTestData(j.data, &data)
+
+ return data
+}
+
func (prebuilt *Import) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
@@ -104,9 +126,9 @@
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_SDK_VERSION :=", prebuilt.sdkVersion())
},
},
}
@@ -125,7 +147,8 @@
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 +159,13 @@
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())
+ },
+ },
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
@@ -175,8 +203,8 @@
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())
@@ -212,11 +240,52 @@
}
fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.pem.String())
+ if len(app.appProperties.Overrides) > 0 {
+ fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES := "+strings.Join(app.appProperties.Overrides, " "))
+ }
+
+ for _, jniLib := range app.installJniLibs {
+ fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), "+=", jniLib.name)
+ }
},
},
}
}
+func (a *AndroidTest) AndroidMk() android.AndroidMkData {
+ data := a.AndroidApp.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(a.testProperties.Test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(a.testProperties.Test_suites, " "))
+ } else {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+ }
+ 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) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(a.appTestHelperAppProperties.Test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(a.appTestHelperAppProperties.Test_suites, " "))
+ } else {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+ }
+ })
+
+ return data
+}
+
func (a *AndroidLibrary) AndroidMk() android.AndroidMkData {
data := a.Library.AndroidMk()
@@ -247,15 +316,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,17 +334,158 @@
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())
+ }
+ 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.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())
+ }
+ 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, " "))
+ })
+ }
+}
diff --git a/java/app.go b/java/app.go
index ae0592a..5d25dcf 100644
--- a/java/app.go
+++ b/java/app.go
@@ -19,24 +19,30 @@
import (
"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)
}
// 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
+ // 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
- // 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,7 +57,17 @@
// 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"`
+
+ EmbedJNI bool `blueprint:"mutated"`
}
type AndroidApp struct {
@@ -61,6 +77,10 @@
certificate certificate
appProperties appProperties
+
+ extraLinkFlags []string
+
+ installJniLibs []jniLib
}
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -71,6 +91,10 @@
return nil
}
+func (a *AndroidApp) ExportedManifest() android.Path {
+ return a.manifestPath
+}
+
var _ AndroidLibraryDependency = (*AndroidApp)(nil)
type certificate struct {
@@ -79,20 +103,44 @@
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(String(a.appProperties.Certificate))
+ 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.generateAndroidBuildActions(ctx)
+}
+
+func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
+ linkFlags := append([]string(nil), a.extraLinkFlags...)
hasProduct := false
for _, f := range a.aaptProperties.Aaptflags {
@@ -119,7 +167,7 @@
// 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), linkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
@@ -140,35 +188,45 @@
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"),
- }
- default:
- defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
- a.certificate = certificate{
- defaultDir.Join(ctx, c+".x509.pem"),
- defaultDir.Join(ctx, c+".pk8"),
- }
- }
-
- 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"),
- })
- }
-
packageFile := android.PathForModuleOut(ctx, "package.apk")
- CreateAppPackage(ctx, packageFile, a.exportPackage, a.outputFile, certificates)
+ var certificates []certificate
+
+ var jniJarFile android.WritablePath
+ jniLibs, certificateDeps := a.collectAppDeps(ctx)
+ if len(jniLibs) > 0 {
+ embedJni := ctx.Config().UnbundledBuild() || a.appProperties.EmbedJNI
+ if embedJni {
+ jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
+ TransformJniLibsToJar(ctx, jniJarFile, jniLibs)
+ } else {
+ a.installJniLibs = jniLibs
+ }
+ }
+
+ if ctx.Failed() {
+ return
+ }
+
+ cert := String(a.appProperties.Certificate)
+ 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, cert+".x509.pem"),
+ defaultDir.Join(ctx, cert+".pk8"),
+ }
+ } else {
+ pem, key := ctx.Config().DefaultAppCertificate(ctx)
+ a.certificate = certificate{pem, key}
+ }
+
+ certificates = append([]certificate{a.certificate}, certificateDeps...)
+
+ CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, a.outputFile, certificates)
a.outputFile = packageFile
@@ -182,12 +240,51 @@
}
}
+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 AndroidAppFactory() android.Module {
module := &AndroidApp{}
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(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,
@@ -195,6 +292,132 @@
&module.aaptProperties,
&module.appProperties)
- 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)
+
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) {
+ a.generateAndroidBuildActions(ctx)
+
+ a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath)
+ a.data = ctx.ExpandSources(a.testProperties.Data, nil)
+}
+
+func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+ android.ExtractSourceDeps(ctx, a.testProperties.Test_config)
+ android.ExtractSourceDeps(ctx, a.testProperties.Test_config_template)
+ android.ExtractSourcesDeps(ctx, a.testProperties.Data)
+ 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))
+ }
+}
+
+func AndroidTestFactory() android.Module {
+ module := &AndroidTest{}
+
+ module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
+
+ module.Module.properties.Instrument = true
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+ module.appProperties.EmbedJNI = true
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.protoProperties,
+ &module.aaptProperties,
+ &module.appProperties,
+ &module.appTestProperties,
+ &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
+}
+
+func AndroidTestHelperAppFactory() android.Module {
+ module := &AndroidTestHelperApp{}
+
+ module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
+
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+ module.appProperties.EmbedJNI = true
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.protoProperties,
+ &module.aaptProperties,
+ &module.appProperties,
+ &module.appTestHelperAppProperties)
+
+ 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
+}
+
+func AndroidAppCertificateFactory() android.Module {
+ module := &AndroidAppCertificate{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ return module
+}
+
+func (c *AndroidAppCertificate) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+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"),
+ }
+}
diff --git a/java/app_builder.go b/java/app_builder.go
index 954ca44..b9b5f43 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -19,9 +19,11 @@
// functions.
import (
+ "path/filepath"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
)
@@ -61,16 +63,18 @@
})
func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
- resJarFile, dexJarFile android.Path, certificates []certificate) {
-
- // TODO(ccross): JNI libs
+ resJarFile, jniJarFile, dexJarFile android.Path, certificates []certificate) {
unsignedApk := android.PathForModuleOut(ctx, "unsigned.apk")
- inputs := android.Paths{resJarFile}
+ var inputs android.Paths
if dexJarFile != nil {
inputs = append(inputs, dexJarFile)
}
+ inputs = append(inputs, resJarFile)
+ if jniJarFile != nil {
+ inputs = append(inputs, jniJarFile)
+ }
ctx.Build(pctx, android.BuildParams{
Rule: combineApk,
@@ -103,10 +107,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) {
@@ -132,3 +136,37 @@
},
})
}
+
+func TransformJniLibsToJar(ctx android.ModuleContext, outputFile android.WritablePath,
+ jniLibs []jniLib) {
+
+ var deps android.Paths
+ jarArgs := []string{
+ "-j", // junk paths, they will be added back with -P arguments
+ }
+
+ if !ctx.Config().UnbundledBuild() {
+ 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.NinjaAndShellEscape(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..f6476dc 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"fmt"
+ "path/filepath"
"reflect"
"sort"
"strings"
@@ -71,7 +72,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,
@@ -335,3 +339,118 @@
}
}
}
+
+func TestJNI(t *testing.T) {
+ ctx := testJava(t, `
+ toolchain_library {
+ name: "libcompiler_rt-extras",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libatomic",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libgcc",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-aarch64-android",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-arm-android",
+ src: "",
+ }
+
+ cc_object {
+ name: "crtbegin_so",
+ stl: "none",
+ }
+
+ cc_object {
+ name: "crtend_so",
+ stl: "none",
+ }
+
+ 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"],
+ }
+ `)
+
+ // check the existence of the internal modules
+ ctx.ModuleForTests("test", "android_common")
+ ctx.ModuleForTests("test_first", "android_common")
+ ctx.ModuleForTests("test_both", "android_common")
+ ctx.ModuleForTests("test_32", "android_common")
+ ctx.ModuleForTests("test_64", "android_common")
+
+ 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)
+ }
+ })
+ }
+}
diff --git a/java/builder.go b/java/builder.go
index bc95599..cefb916 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -24,9 +24,9 @@
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/java/config"
)
var (
@@ -42,10 +42,11 @@
blueprint.RuleParams{
Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+ `(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
`${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
- `$javacFlags $bootClasspath $classpath ` +
+ `$processorpath $javacFlags $bootClasspath $classpath ` +
`-source $javaVersion -target $javaVersion ` +
- `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list && ` +
+ `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
CommandDeps: []string{
"${config.JavacCmd}",
@@ -56,17 +57,17 @@
Rspfile: "$out.rsp",
RspfileContent: "$in",
},
- "javacFlags", "bootClasspath", "classpath", "srcJars", "srcJarDir",
+ "javacFlags", "bootClasspath", "classpath", "processorpath", "srcJars", "srcJarDir",
"outDir", "annoDir", "javaVersion")
kotlinc = pctx.AndroidGomaStaticRule("kotlinc",
blueprint.RuleParams{
- Command: `rm -rf "$outDir" "$srcJarDir" && mkdir -p "$outDir" "$srcJarDir" && ` +
+ Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" && mkdir -p "$classesDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.GenKotlinBuildFileCmd} $classpath $outDir $out.rsp $srcJarDir/list > $outDir/kotlinc-build.xml &&` +
+ `${config.GenKotlinBuildFileCmd} $classpath $classesDir $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
`${config.KotlincCmd} $kotlincFlags ` +
- `-jvm-target $kotlinJvmTarget -Xbuild-file=$outDir/kotlinc-build.xml && ` +
- `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir`,
+ `-jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir`,
CommandDeps: []string{
"${config.KotlincCmd}",
"${config.KotlinCompilerJar}",
@@ -77,30 +78,7 @@
Rspfile: "$out.rsp",
RspfileContent: `$in`,
},
- "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")
+ "kotlincFlags", "classpath", "srcJars", "srcJarDir", "classesDir", "kotlinJvmTarget", "kotlinBuildFile")
turbine = pctx.AndroidStaticRule("turbine",
blueprint.RuleParams{
@@ -108,7 +86,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 +109,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,9 +131,17 @@
CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
},
"rulesFile")
+
+ jetifier = pctx.AndroidStaticRule("jetifier",
+ blueprint.RuleParams{
+ Command: "${config.JavaCmd} -jar ${config.JetifierJar} -l error -o $out -i $in",
+ CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"},
+ },
+ )
)
func init() {
+ pctx.Import("android/soong/common")
pctx.Import("android/soong/java/config")
}
@@ -154,11 +149,13 @@
javacFlags string
bootClasspath classpath
classpath classpath
+ processorPath classpath
systemModules classpath
aidlFlags string
javaVersion string
errorProneExtraJavacFlags string
+ errorProneProcessorPath classpath
kotlincFlags string
kotlincClasspath classpath
@@ -186,11 +183,12 @@
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(),
+ "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",
},
@@ -206,26 +204,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 +238,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 +251,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 +270,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 +290,7 @@
}
deps = append(deps, flags.classpath...)
+ deps = append(deps, flags.processorPath...)
srcJarDir := "srcjars"
outDir := "classes"
@@ -305,7 +302,7 @@
annoDir = filepath.Join(shardDir, annoDir)
}
ctx.Build(pctx, android.BuildParams{
- Rule: rule,
+ Rule: javac,
Description: desc,
Output: outputFile,
Inputs: srcFiles,
@@ -314,6 +311,7 @@
"javacFlags": flags.javacFlags,
"bootClasspath": bootClasspath,
"classpath": flags.classpath.FormJavaClassPath("-classpath"),
+ "processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
"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.NinjaAndShellEscape(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,35 @@
})
}
+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",
+ },
+ })
+}
+
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,7 +438,7 @@
}
}
-func (x *classpath) FormDesugarClasspath(optName string) []string {
+func (x *classpath) FormTurbineClasspath(optName string) []string {
if x == nil || *x == nil {
return nil
}
diff --git a/java/config/config.go b/java/config/config.go
index 980db3b..d2a8c46 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -27,9 +27,11 @@
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"}
+ DefaultLambdaStubsLibrary = "core-lambda-stubs"
+ SdkLambdaStubsPath = "prebuilts/sdk/tools/core-lambda-stubs.jar"
DefaultJacocoExcludeFilter = []string{"org.junit.*", "org.jacoco.*", "org.mockito.*"}
@@ -41,6 +43,17 @@
"android.car7",
"core-oj",
"core-libart",
+ "core-simple",
+ }
+
+ ManifestMergerClasspath = []string{
+ "prebuilts/gradle-plugin/com/android/tools/build/manifest-merger/26.1.0/manifest-merger-26.1.0.jar",
+ "prebuilts/gradle-plugin/com/android/tools/common/26.1.0/common-26.1.0.jar",
+ "prebuilts/gradle-plugin/com/android/tools/sdk-common/26.1.0/sdk-common-26.1.0.jar",
+ "prebuilts/gradle-plugin/com/android/tools/sdklib/26.1.0/sdklib-26.1.0.jar",
+ "prebuilts/gradle-plugin/org/jetbrains/kotlin/kotlin-runtime/1.0.5/kotlin-runtime-1.0.5.jar",
+ "prebuilts/gradle-plugin/org/jetbrains/kotlin/kotlin-stdlib/1.1.3/kotlin-stdlib-1.1.3.jar",
+ "prebuilts/misc/common/guava/guava-21.0.jar",
}
)
@@ -92,18 +105,7 @@
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")
@@ -117,9 +119,11 @@
})
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.HostBinToolVariable("SoongJavacWrapper", "soong_javac_wrapper")
@@ -143,4 +147,9 @@
}
hostBinToolVariableWithPrebuilt("Aapt2Cmd", "prebuilts/sdk/tools", "aapt2")
+
+ pctx.SourcePathVariable("ManifestFixerCmd", "build/soong/scripts/manifest_fixer.py")
+
+ pctx.SourcePathsVariable("ManifestMergerJars", " ", ManifestMergerClasspath...)
+ pctx.SourcePathsVariable("ManifestMergerClasspath", ":", ManifestMergerClasspath...)
}
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/makevars.go b/java/config/makevars.go
index 27c7daa..275f496 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -46,31 +46,23 @@
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("ZIPSYNC", "${ZipSyncCmd}")
@@ -79,4 +71,10 @@
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_DEPS", "${ManifestMergerJars}")
+ ctx.Strict("ANDROID_MANIFEST_MERGER",
+ "${JavaCmd} -classpath ${ManifestMergerClasspath} com.android.manifmerger.Merger")
}
diff --git a/java/dex.go b/java/dex.go
index 66e71b5..625fb83 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -22,151 +22,85 @@
"android/soong/android"
)
-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",
- 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`,
+ `${config.D8Cmd} --output $outDir $d8Flags $in && ` +
+ `${config.SoongZipCmd} -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")
+ "outDir", "d8Flags")
var r8 = pctx.AndroidStaticRule("r8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
+ `rm -f "$outDict" && ` +
`${config.R8Cmd} -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} -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")
+ "outDir", "outDict", "r8Flags")
-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 +110,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 +133,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 +157,52 @@
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.
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)
// Compile classes.jar into classes.dex and then javalib.jar
javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
outDir := android.PathForModuleOut(ctx, "dex")
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)
ctx.Build(pctx, android.BuildParams{
- Rule: r8,
- Description: "r8",
- Output: javalibJar,
- Input: classesJar,
- Implicits: r8Deps,
+ Rule: r8,
+ Description: "r8",
+ Output: javalibJar,
+ ImplicitOutput: proguardDictionary,
+ Input: classesJar,
+ Implicits: r8Deps,
Args: map[string]string{
- "dxFlags": strings.Join(dxFlags, " "),
"r8Flags": strings.Join(r8Flags, " "),
"outDict": j.proguardDictionary.String(),
"outDir": outDir.String(),
},
})
} else {
- rule := dx
- desc := "dx"
- if fullD8 {
- rule = d8
- desc = "d8"
- }
+ d8Flags, d8Deps := j.d8Flags(ctx, flags)
ctx.Build(pctx, android.BuildParams{
- Rule: rule,
- Description: desc,
+ Rule: d8,
+ Description: "d8",
Output: javalibJar,
Input: classesJar,
+ Implicits: d8Deps,
Args: map[string]string{
- "dxFlags": strings.Join(dxFlags, " "),
+ "d8Flags": strings.Join(d8Flags, " "),
"outDir": outDir.String(),
},
})
}
- j.dexJarFile = javalibJar
return javalibJar
}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 7171c91..fca5fc4 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,34 +30,120 @@
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`,
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 ` +
+ `$opts && ` +
+ `${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir`,
+ 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")
+
+ 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 ` +
+ `$opts && touch $out ) || ` +
+ `( 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")
+
+ 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`,
+ 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.
@@ -63,58 +151,290 @@
// 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"`
- // 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, legacy-test, core-junit,
+ // 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)
+ 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
// 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
+
+ // 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
+
+ // 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
+
+ // 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
+
+ // 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
+
+ // source.properties under current module will be copied to docs out dir, if not null.
+ Static_doc_properties *string
// 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
+
+ // 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
+ }
+
+ // 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
+ }
+
+ // user can specify the version of previous released API file in order to do compatibility check.
+ Previous_api *string
+
+ // 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
+
+ // 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
+
+ doclavaStubsFlags string
+ doclavaDocsFlags string
+ postDoclavaCmds string
+
+ metalavaStubsFlags string
+ metalavaAnnotationsFlags 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
+}
+
+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 +444,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 +474,324 @@
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) 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)
+ }
+
+ android.ExtractSourcesDeps(ctx, j.properties.Srcs)
+
+ // exclude_srcs may contain filegroup or genrule.
+ android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
+
+ // arg_files may contains filegroup or genrule.
+ android.ExtractSourcesDeps(ctx, j.properties.Arg_files)
+}
+
+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
+
+ // aidl flags.
+ aidlFlags := j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
+ if len(aidlFlags) > 0 {
+ // optimization.
+ ctx.Variable(pctx, "aidlFlags", strings.Join(aidlFlags, " "))
+ flags.aidlFlags = "$aidlFlags"
+ }
+
+ return flags
+}
+
+func (j *Javadoc) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
+ aidlIncludeDirs android.Paths) []string {
+
+ aidlIncludes := android.PathsForModuleSrc(ctx, j.properties.Aidl.Local_include_dirs)
+ aidlIncludes = append(aidlIncludes, android.PathsForSource(ctx, j.properties.Aidl.Include_dirs)...)
+
+ var flags []string
+ if aidlPreprocess.Valid() {
+ flags = append(flags, "-p"+aidlPreprocess.String())
+ } 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 flags
+}
+
+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)
+ 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 Dependency:
+ deps.classpath = append(deps.classpath, dep.ImplementationJars()...)
+ case SdkLibraryDependency:
+ sdkVersion := j.sdkVersion()
+ linkType := javaSdk
+ if strings.HasPrefix(sdkVersion, "system_") || strings.HasPrefix(sdkVersion, "test_") {
+ linkType = javaSystem
+ } else if sdkVersion == "" {
+ linkType = javaPlatform
+ }
+ deps.classpath = append(deps.classpath, dep.ImplementationJars(linkType)...)
+ 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 := ctx.ExpandSources(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 = ctx.ExpandSources(j.properties.Arg_files, nil)
+ argFilesMap := map[string]android.Path{}
+
+ for _, f := range j.argFiles {
+ 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())
+ }
+ }
+
+ 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 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("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,230 +812,116 @@
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 String(d.properties.Custom_template) == "" {
- // TODO: This is almost always droiddoc-templates-sdk
- ctx.PropertyErrorf("custom_template", "must specify a template")
- } else {
+ if String(d.properties.Custom_template) != "" {
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)
+
+ if String(d.properties.Static_doc_index_redirect) != "" {
+ android.ExtractSourceDeps(ctx, d.properties.Static_doc_index_redirect)
+ }
+
+ if String(d.properties.Static_doc_properties) != "" {
+ android.ExtractSourceDeps(ctx, d.properties.Static_doc_properties)
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Current, "current") {
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Api_file)
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Removed_api_file)
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") {
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Api_file)
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Removed_api_file)
+ }
}
-func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- deps := d.Javadoc.collectDeps(ctx)
+func (d *Droiddoc) initBuilderFlags(ctx android.ModuleContext, implicits *android.Paths,
+ deps deps) (droiddocBuilderFlags, error) {
+ var flags droiddocBuilderFlags
- var implicits android.Paths
- implicits = append(implicits, deps.bootClasspath...)
- implicits = append(implicits, deps.classpath...)
+ *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(), ":")
+ // For OpenJDK 8 we can use -bootclasspath to define the core libraries code.
+ flags.bootClasspathArgs = deps.bootClasspath.FormJavaClassPath("-bootclasspath")
}
- if len(deps.classpath.Strings()) > 0 {
- classpathArgs = "-classpath " + strings.Join(deps.classpath.Strings(), ":")
+ 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()
}
- var templateDir 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")
+ }
+
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()
+ *implicits = append(*implicits, ctx.Glob(htmlDir.Join(ctx, "**/*").String(), nil)...)
+ args = args + " -htmldir " + htmlDir.String()
}
- 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()
+ *implicits = append(*implicits, ctx.Glob(htmlDir2.Join(ctx, "**/*").String(), nil)...)
+ args = args + " -htmldir2 " + htmlDir2.String()
}
if len(d.properties.Html_dirs) > 2 {
@@ -409,11 +929,12 @@
}
knownTags := ctx.ExpandSources(d.properties.Knowntags, nil)
- implicits = append(implicits, 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 +943,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 +951,827 @@
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, "out").String(),
+ "srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
+ "stubsDir": android.PathForModuleOut(ctx, "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
+
+ checkCurrentApiTimestamp android.WritablePath
+ updateCurrentApiTimestamp android.WritablePath
+ checkLastReleasedApiTimestamp android.WritablePath
+
+ annotationsZip android.WritablePath
+ apiVersionsXml android.WritablePath
+
+ apiFilePath android.Path
+
+ jdiffDocZip android.WritablePath
+ jdiffStubsSrcJar 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 apiCheckEnabled(d.properties.Check_api.Current, "current") {
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Api_file)
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Current.Removed_api_file)
+ }
+
+ if apiCheckEnabled(d.properties.Check_api.Last_released, "last_released") {
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Api_file)
+ android.ExtractSourceDeps(ctx, d.properties.Check_api.Last_released.Removed_api_file)
+ }
+
+ if String(d.properties.Previous_api) != "" {
+ android.ExtractSourceDeps(ctx, d.properties.Previous_api)
+ }
+
+ 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) {
+ metalavaFlags = metalavaFlags + " --sdk-values " + android.PathForModuleOut(ctx, "out").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 {
+ var flags string
+ if Bool(d.properties.Annotations_enabled) {
+ if String(d.properties.Previous_api) == "" {
+ ctx.PropertyErrorf("metalava_previous_api",
+ "has to be non-empty if annotations was enabled!")
+ }
+ previousApi := ctx.ExpandSource(String(d.properties.Previous_api),
+ "metalava_previous_api")
+ *implicits = append(*implicits, previousApi)
+
+ flags += " --include-annotations --migrate-nullness " + previousApi.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...)
+ flags += " --merge-qualifier-annotations " + t.dir.String()
+ } else {
+ ctx.PropertyErrorf("merge_annotations_dirs",
+ "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+ // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
+ flags += " --hide HiddenTypedefConstant --hide SuperfluousPrefix --hide AnnotationExtraction"
+ }
+
+ return flags
+}
+
+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() + "/%/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) {
+
+ 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,
+ },
+ })
+}
+
+func (d *Droidstubs) transformCheckApi(ctx android.ModuleContext,
+ apiFile, removedApiFile android.Path, implicits android.Paths,
+ javaVersion, bootclasspathArgs, classpathArgs, sourcepathArgs, opts, msg string,
+ output android.WritablePath) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: metalavaApiCheck,
+ Description: "Metalava Check API",
+ Output: output,
+ Inputs: d.Javadoc.srcFiles,
+ Implicits: append(android.Paths{apiFile, removedApiFile, d.apiFile, d.removedApiFile},
+ implicits...),
+ Args: map[string]string{
+ "srcJarDir": android.PathForModuleOut(ctx, "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 = 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")
+
+ 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
+
+ d.transformCheckApi(ctx, apiFile, removedApiFile, metalavaCheckApiImplicits,
+ javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts,
+ 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")
+
+ 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() + " "
+
+ d.transformCheckApi(ctx, apiFile, removedApiFile, metalavaCheckApiImplicits,
+ javaVersion, flags.bootClasspathArgs, flags.classpathArgs, flags.sourcepathArgs, opts,
+ `\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 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) {
+func (d *ExportedDroiddocDir) GenerateAndroidBuildActions(ctx android.ModuleContext) {
path := android.PathForModuleSrc(ctx, String(d.properties.Path))
d.dir = path
d.deps = ctx.Glob(path.Join(ctx, "**/*").String(), nil)
}
+
+//
+// Defaults
+//
+type DocDefaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func (*DocDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+func (d *DocDefaults) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+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..993e6d1 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -26,6 +26,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,6 +50,17 @@
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 {
@@ -82,6 +94,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 {
@@ -99,6 +124,9 @@
case ".proto":
srcJarFile := genProto(ctx, srcFile, flags)
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..8f29482 100644
--- a/java/genrule.go
+++ b/java/genrule.go
@@ -21,6 +21,7 @@
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.
@@ -33,3 +34,14 @@
return module
}
+
+// java_genrule_host 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.
+func genRuleFactoryHost() android.Module {
+ module := genrule.NewGenRule()
+
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+
+ return module
+}
diff --git a/java/java.go b/java/java.go
index ddfb09a..f651884 100644
--- a/java/java.go
+++ b/java/java.go
@@ -29,16 +29,19 @@
"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", LibraryFactory)
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_host", TestHostFactory)
android.RegisterModuleType("java_import", ImportFactory)
android.RegisterModuleType("java_import_host", ImportFactoryHost)
@@ -72,7 +75,7 @@
// list of files to use as Java resources
Java_resources []string `android:"arch_variant"`
- // list of files that should be excluded from java_resources
+ // list of files that should be excluded from java_resources and java_resource_dirs
Exclude_java_resources []string `android:"arch_variant"`
// don't build against the default libraries (bootclasspath, legacy-test, core-junit,
@@ -101,7 +104,8 @@
// 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
@@ -127,6 +131,15 @@
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
@@ -162,9 +175,17 @@
// 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 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,11 +199,17 @@
// 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
+ // If set to true, compile dex regardless of installable. Defaults to false.
+ Compile_dex *bool
+
Dex_preopt struct {
// If false, prevent dexpreopting and stripping the dex file from the final jar. Defaults to
// true.
@@ -203,8 +230,8 @@
}
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
// If true, optimize for size by removing unused code. Defaults to true for apps,
@@ -241,13 +268,22 @@
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 containing uninstrumented classes that will be instrumented by jacoco
@@ -256,7 +292,7 @@
// output file containing mapping of obfuscated names
proguardDictionary android.Path
- // output file suitable for installing or running
+ // output file of the module, which may be a classes jar or a dex jar
outputFile android.Path
exportAidlIncludeDirs android.Paths
@@ -272,10 +308,20 @@
// 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
}
func (j *Module) Srcs() android.Paths {
- return android.Paths{j.implementationJarFile}
+ return android.Paths{j.outputFile}
}
var _ android.SourceFileProducer = (*Module)(nil)
@@ -283,9 +329,32 @@
type Dependency interface {
HeaderJars() android.Paths
ImplementationJars() android.Paths
+ ResourceJars() android.Paths
+ ImplementationAndResourcesJars() android.Paths
AidlIncludeDirs() android.Paths
+ ExportedSdkLibs() []string
}
+type SdkLibraryDependency interface {
+ HeaderJars(linkType linkType) android.Paths
+ ImplementationJars(linkType linkType) 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 +365,41 @@
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"}
+ annoTag = dependencyTag{name: "annotation processor"}
+ bootClasspathTag = dependencyTag{name: "bootclasspath"}
+ systemModulesTag = dependencyTag{name: "system modules"}
+ frameworkResTag = dependencyTag{name: "framework-res"}
+ frameworkApkTag = dependencyTag{name: "framework-apk"}
+ kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
+ 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
+ jars android.Paths
aidl android.Path
}
-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,10 +412,62 @@
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.
+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()
+}
+
+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
+}
+
+func sdkVersionOrDefault(ctx android.BaseContext, v string) string {
+ switch v {
+ case "", "current", "system_current", "test_current", "core_current", "core_platform_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", "core_platform_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()
+ i, err := sdkVersionToNumber(ctx, v)
+ if err != nil {
+ ctx.PropertyErrorf("sdk_version", "%s", err)
return sdkDep{}
}
@@ -365,24 +487,30 @@
}
}
- toFile := func(v string) sdkDep {
- isCore := strings.HasPrefix(v, "core_")
- if isCore {
- v = strings.TrimPrefix(v, "core_")
+ 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)
+ dir := filepath.Join("prebuilts", "sdk", v, api)
jar := filepath.Join(dir, "android.jar")
- if isCore {
- jar = filepath.Join(dir, "core.jar")
- }
- aidl := filepath.Join(dir, "framework.aidl")
+ // 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,
- module: "sdk_v" + v,
+ modules: []string{fmt.Sprintf("sdk_%s_%s_android", api, v)},
}
}
@@ -398,22 +526,28 @@
return sdkDep{
useFiles: true,
- jar: jarPath.Path(),
+ jars: android.Paths{jarPath.Path(), lambdaStubsPath},
aidl: aidlPath.Path(),
}
}
- //toModule := func(m string) sdkDep {
- // return sdkDep{
- // useModule: true,
- // module: m,
- // systemModules: m + "_system_modules",
- // frameworkResModule: r,
- // }
- //}
+ toModule := func(m, r string) sdkDep {
+ ret := sdkDep{
+ useModule: true,
+ modules: []string{m, config.DefaultLambdaStubsLibrary},
+ systemModules: m + "_system_modules",
+ frameworkResModule: r,
+ }
+ 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
+ }
if ctx.Config().UnbundledBuild() && v != "" {
- return toFile(v)
+ return toPrebuilt(v)
}
switch v {
@@ -422,55 +556,60 @@
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")
+ 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")
+ case "core_current":
+ return toModule("core.current.stubs", "")
+ case "core_platform_current":
+ return toModule("core.platform.api.stubs", "")
default:
- return toFile(v)
+ return toPrebuilt(v)
}
}
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)
+ ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+ ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.modules...)
if Bool(j.deviceProperties.Optimize.Enabled) {
- ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultBootclasspathLibraries...)
- ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultLibraries...)
+ 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...)
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant},
+ }, annoTag, j.properties.Annotation_processors...)
android.ExtractSourcesDeps(ctx, j.properties.Srcs)
android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
@@ -484,11 +623,11 @@
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 j.shouldInstrumentStatic(ctx) {
- ctx.AddDependency(ctx.Module(), staticLibTag, "jacocoagent")
+ ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
}
}
@@ -527,7 +666,8 @@
aidlIncludes = append(aidlIncludes,
android.PathsForSource(ctx, j.deviceProperties.Aidl.Include_dirs)...)
- var flags []string
+ flags := []string{"-b"}
+
if aidlPreprocess.Valid() {
flags = append(flags, "-p"+aidlPreprocess.String())
} else {
@@ -545,16 +685,22 @@
flags = append(flags, "-t")
}
+ if Bool(j.deviceProperties.Aidl.Generate_get_transaction_name) {
+ flags = append(flags, "--transaction_names")
+ }
+
return flags
}
type deps struct {
classpath classpath
bootClasspath classpath
+ processorPath classpath
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
@@ -570,31 +716,110 @@
}
}
+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" || ver == "core_platform_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.aidlIncludeDirs = append(deps.aidlIncludeDirs, 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:
@@ -606,25 +831,52 @@
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()...)
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()...)
+ case annoTag:
+ deps.processorPath = append(deps.processorPath, dep.ImplementationAndResourcesJars()...)
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()))
}
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
+ case SdkLibraryDependency:
+ switch tag {
+ case libTag:
+ linkType, _ := getLinkType(j, ctx.ModuleName())
+ deps.classpath = append(deps.classpath, dep.HeaderJars(linkType)...)
+ // 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 android.SourceFileProducer:
switch tag {
case libTag:
@@ -659,16 +911,43 @@
}
})
+ j.exportedSdkLibs = android.FirstUniqueStrings(j.exportedSdkLibs)
+
return deps
}
+func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) string {
+ var ret string
+ sdk, err := sdkVersionToNumber(ctx, sdkContext.sdkVersion())
+ 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 +955,29 @@
// 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() &&
+ 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,6 +1003,11 @@
}
}
+ if j.properties.Patch_module != nil && flags.javaVersion == "1.9" {
+ patchClasspath := ".:" + flags.classpath.FormJavaClassPath("")
+ javacFlags = append(javacFlags, "--patch-module="+String(j.properties.Patch_module)+"="+patchClasspath)
+ }
+
// systemModules
if deps.systemModules != nil {
flags.systemModules = append(flags.systemModules, deps.systemModules)
@@ -744,6 +1021,12 @@
flags.aidlFlags = "$aidlFlags"
}
+ if len(javacFlags) > 0 {
+ // optimization.
+ ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
+ flags.javacFlags = "$javacFlags"
+ }
+
return flags
}
@@ -754,7 +1037,7 @@
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)
@@ -768,7 +1051,9 @@
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()...)
jarName := ctx.ModuleName() + ".jar"
@@ -782,6 +1067,8 @@
}
}
+ var kotlinJars android.Paths
+
if srcFiles.HasExt(".kt") {
// 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
@@ -796,6 +1083,7 @@
kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
+ flags.kotlincClasspath = append(flags.kotlincClasspath, deps.bootClasspath...)
flags.kotlincClasspath = append(flags.kotlincClasspath, deps.kotlinStdlib...)
flags.kotlincClasspath = append(flags.kotlincClasspath, deps.classpath...)
@@ -808,11 +1096,14 @@
// 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
@@ -828,18 +1119,14 @@
j.properties.Javac_shard_size)
}
}
- // 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 +1164,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 +1177,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,43 +1186,75 @@
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
- jars = append(jars, deps.staticJars...)
+ if len(deps.staticResourceJars) > 0 {
+ var jars android.Paths
+ if j.resourceJar != nil {
+ jars = append(jars, j.resourceJar)
+ }
+ jars = append(jars, deps.staticResourceJars...)
- var manifest android.OptionalPath
- if j.properties.Manifest != nil {
+ 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...)
+ jars = append(jars, deps.staticResourceJars...)
+
+ manifest := j.overrideManifest
+ if !manifest.Valid() && j.properties.Manifest != nil {
manifest = android.OptionalPathForPath(ctx.ExpandSource(*j.properties.Manifest, "manifest"))
}
// 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
}
+ // jarjar implementation jar if necessary
if j.properties.Jarjar_rules != nil {
jarjar_rules := android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
// Transform classes.jar into classes-jarjar.jar
jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName)
TransformJarJar(ctx, jarjarFile, outputFile, jarjar_rules)
outputFile = jarjarFile
+
+ // jarjar resource jar if necessary
+ if j.resourceJar != nil {
+ resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName)
+ TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, jarjar_rules)
+ j.resourceJar = resourceJarJarFile
+ }
+
if ctx.Failed() {
return
}
@@ -954,18 +1274,49 @@
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)) {
+ var dexOutputFile android.ModuleOutPath
+ dexOutputFile = j.compileDex(ctx, flags, outputFile, jarName)
if ctx.Failed() {
return
}
+
+ // 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)
+ dexOutputFile = combinedJar
+ }
+
+ j.dexJarFile = dexOutputFile
+
+ outputFile = dexOutputFile
+ } 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()
}
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 +1329,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,7 +1339,8 @@
// 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 {
@@ -1004,7 +1358,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,21 +1372,6 @@
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)
func (j *Module) HeaderJars() android.Paths {
@@ -1043,16 +1382,48 @@
return android.Paths{j.implementationJarFile}
}
+func (j *Module) ResourceJars() android.Paths {
+ if j.resourceJar == nil {
+ return nil
+ }
+ return android.Paths{j.resourceJar}
+}
+
+func (j *Module) ImplementationAndResourcesJars() android.Paths {
+ return android.Paths{j.implementationAndResourcesJar}
+}
+
func (j *Module) AidlIncludeDirs() android.Paths {
return j.exportAidlIncludeDirs
}
+func (j *Module) ExportedSdkLibs() []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.properties.Jarjar_rules != nil {
+ dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, *j.properties.Jarjar_rules)
+ }
+}
+
+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)
//
@@ -1064,7 +1435,7 @@
func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.compile(ctx)
- if j.installable() {
+ if Bool(j.properties.Installable) || ctx.Host() {
j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
ctx.ModuleName()+".jar", j.outputFile)
}
@@ -1074,22 +1445,16 @@
j.deps(ctx)
}
-func LibraryFactory(installable bool) func() android.Module {
- return func() android.Module {
- module := &Library{}
+func LibraryFactory() android.Module {
+ module := &Library{}
- if !installable {
- module.properties.Installable = proptools.BoolPtr(false)
- }
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &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
}
func LibraryHostFactory() android.Module {
@@ -1099,6 +1464,82 @@
&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:"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"`
+
+ // list of files or filegroup modules that provide data that should be installed alongside
+ // the test
+ Data []string
+}
+
+type Test struct {
+ Library
+
+ testProperties testProperties
+
+ testConfig android.Path
+ data android.Paths
+}
+
+func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template)
+ j.data = ctx.ExpandSources(j.testProperties.Data, nil)
+
+ j.Library.GenerateAndroidBuildActions(ctx)
+}
+
+func (j *Test) DepsMutator(ctx android.BottomUpMutatorContext) {
+ j.deps(ctx)
+ android.ExtractSourceDeps(ctx, j.testProperties.Test_config)
+ android.ExtractSourceDeps(ctx, j.testProperties.Test_config_template)
+ android.ExtractSourcesDeps(ctx, j.testProperties.Data)
+}
+
+func TestFactory() android.Module {
+ module := &Test{}
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.protoProperties,
+ &module.testProperties)
+
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+
+ InitJavaModule(module, android.HostAndDeviceSupported)
+ return module
+}
+
+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
}
@@ -1110,6 +1551,9 @@
type binaryProperties struct {
// installable script to execute the resulting jar
Wrapper *string
+
+ // Name of the class containing main to be inserted into the manifest as Main-Class.
+ Main_class *string
}
type Binary struct {
@@ -1130,6 +1574,15 @@
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
@@ -1167,6 +1620,8 @@
&module.Module.protoProperties,
&module.binaryProperties)
+ module.Module.properties.Installable = proptools.BoolPtr(true)
+
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
android.InitDefaultableModule(module)
return module
@@ -1177,10 +1632,11 @@
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
@@ -1196,16 +1652,37 @@
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_enabled *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,30 +1698,97 @@
}
func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
+ android.ExtractSourcesDeps(ctx, j.properties.Jars)
+ ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
}
func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- j.classpathFiles = android.PathsForModuleSrc(ctx, j.properties.Jars)
+ jars := ctx.ExpandSources(j.properties.Jars, nil)
- 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_enabled) {
+ 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
+ return android.Paths{j.combinedClasspathFile}
}
func (j *Import) ImplementationJars() android.Paths {
- return j.classpathFiles
+ return android.Paths{j.combinedClasspathFile}
+}
+
+func (j *Import) ResourceJars() android.Paths {
+ return nil
+}
+
+func (j *Import) ImplementationAndResourcesJars() android.Paths {
+ return android.Paths{j.combinedClasspathFile}
}
func (j *Import) AidlIncludeDirs() android.Paths {
return nil
}
+func (j *Import) ExportedSdkLibs() []string {
+ return j.exportedSdkLibs
+}
+
+// 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)
func ImportFactory() android.Module {
@@ -1253,7 +1797,7 @@
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Jars)
- android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ InitJavaModule(module, android.HostAndDeviceSupported)
return module
}
@@ -1263,7 +1807,7 @@
module.AddProperties(&module.properties)
android.InitPrebuiltModule(module, &module.properties.Jars)
- android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+ InitJavaModule(module, android.HostSupported)
return module
}
@@ -1293,6 +1837,13 @@
&CompilerProperties{},
&CompilerDeviceProperties{},
&android.ProtoProperties{},
+ &aaptProperties{},
+ &androidLibraryProperties{},
+ &appProperties{},
+ &appTestProperties{},
+ &ImportProperties{},
+ &AARImportProperties{},
+ &sdkLibraryProperties{},
)
android.InitDefaultsModule(module)
@@ -1301,5 +1852,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..6c1fd39 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.PathForModuleSrc(ctx).Join(ctx, exclude).String(), nil)
+ for _, dir := range dirs {
+ excludeDirs = append(excludeDirs, dir.String())
+ excludeFiles = append(excludeFiles, dir.(android.ModuleSrcPath).Join(ctx, "**/*").String())
+ }
}
- excludes = append(excludes, resourceExcludes...)
+ excludeFiles = append(excludeFiles, ctx.ExpandSources(excludeResourceFiles, nil).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.PathForModuleSrc(ctx).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)
}
}
}
@@ -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..86349fe 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -15,8 +15,6 @@
package java
import (
- "android/soong/android"
- "android/soong/genrule"
"fmt"
"io/ioutil"
"os"
@@ -25,6 +23,12 @@
"strconv"
"strings"
"testing"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/genrule"
)
var buildDir string
@@ -71,33 +75,50 @@
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory))
+ ctx.RegisterModuleType("android_test", android.ModuleFactoryAdaptor(AndroidTestFactory))
ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
- ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory(true)))
+ ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory))
ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
ctx.RegisterModuleType("java_import", android.ModuleFactoryAdaptor(ImportFactory))
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("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("prebuilt_apis", android.ModuleFactoryAdaptor(prebuiltApisFactory))
ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ 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))
+
+ // 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.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("link", cc.LinkageMutator).Parallel()
+ ctx.BottomUp("begin", cc.BeginMutator).Parallel()
+ })
+
ctx.Register()
extraModules := []string{
- "core-oj",
- "core-libart",
+ "core-lambda-stubs",
"framework",
"ext",
- "okhttp",
"android_stubs_current",
"android_system_stubs_current",
"android_test_stubs_current",
+ "core.current.stubs",
+ "core.platform.api.stubs",
"kotlin-stdlib",
}
@@ -107,7 +128,8 @@
name: "%s",
srcs: ["a.java"],
no_standard_libs: true,
- system_modules: "core-system-modules",
+ sdk_version: "core_current",
+ system_modules: "core-platform-api-stubs-system-modules",
}
`, extra)
}
@@ -121,6 +143,7 @@
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",
@@ -135,31 +158,49 @@
}
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,
+ "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,
- "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/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"],}`),
// For framework-res, which is an implicit dependency for framework
"AndroidManifest.xml": nil,
@@ -172,9 +213,11 @@
"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,
}
@@ -190,7 +233,7 @@
func run(t *testing.T, ctx *android.TestContext, config android.Config) {
t.Helper()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
@@ -211,9 +254,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")
}
@@ -313,6 +353,7 @@
var classpathTestcases = []struct {
name string
+ unbundled bool
moduleType string
host android.OsClass
properties string
@@ -322,16 +363,16 @@
}{
{
name: "default",
- bootclasspath: []string{"core-oj", "core-libart"},
- system: "core-system-modules",
- classpath: []string{"ext", "framework", "okhttp"},
+ bootclasspath: []string{"core.platform.api.stubs", "core-lambda-stubs"},
+ system: "core-platform-api-stubs-system-modules",
+ classpath: []string{"ext", "framework"},
},
{
name: "blank sdk version",
properties: `sdk_version: "",`,
- bootclasspath: []string{"core-oj", "core-libart"},
- system: "core-system-modules",
- classpath: []string{"ext", "framework", "okhttp"},
+ bootclasspath: []string{"core.platform.api.stubs", "core-lambda-stubs"},
+ system: "core-platform-api-stubs-system-modules",
+ classpath: []string{"ext", "framework"},
},
{
@@ -339,23 +380,21 @@
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"},
+ classpath: []string{"prebuilts/sdk/14/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
{
name: "current",
properties: `sdk_version: "current",`,
- bootclasspath: []string{`""`},
+ bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"},
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{`""`},
+ bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/system_current/android.jar"},
},
{
@@ -363,23 +402,21 @@
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"},
+ classpath: []string{"prebuilts/sdk/14/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
{
name: "test_current",
properties: `sdk_version: "test_current",`,
- bootclasspath: []string{`""`},
+ bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"},
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{`""`},
+ bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/current/core.jar"},
},
{
@@ -392,8 +429,8 @@
{
name: "nostdlib system_modules",
- properties: `no_standard_libs: true, system_modules: "core-system-modules"`,
- system: "core-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{},
},
@@ -427,6 +464,24 @@
properties: `host_supported: true, no_standard_libs: true, system_modules: "none"`,
classpath: []string{},
},
+ {
+
+ name: "unbundled sdk v14",
+ unbundled: true,
+ properties: `sdk_version: "14",`,
+ bootclasspath: []string{`""`},
+ system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
+ classpath: []string{"prebuilts/sdk/14/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ },
+ {
+
+ 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"},
+ },
}
func TestClasspath(t *testing.T) {
@@ -477,7 +532,12 @@
t.Run("1.8", func(t *testing.T) {
// Test default javac 1.8
- ctx := testJava(t, bp)
+ config := testConfig(nil)
+ if testcase.unbundled {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+ }
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
javac := ctx.ModuleForTests("foo", variant).Rule("javac")
@@ -505,6 +565,9 @@
// 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)
+ }
ctx := testContext(config, bp, nil)
run(t, ctx, config)
@@ -545,14 +608,15 @@
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())
}
}
@@ -610,13 +674,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 +691,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 +701,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 +753,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 +769,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 {
@@ -805,12 +869,14 @@
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 +907,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 +941,7 @@
name: "bar-doc",
srcs: [
"bar-doc/*.java",
+ "bar-doc/IFoo.aidl",
],
exclude_srcs: [
"bar-doc/b.java"
@@ -897,6 +964,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 +1053,74 @@
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"+sdkImplLibrarySuffix, "android_common")
+ ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
+ 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.impl.jar") {
+ t.Errorf("baz javac classpath %v should not contain %q", bazJavac.Args["classpath"],
+ "foo.impl.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)
+ }
+ }
+}
diff --git a/java/jdeps.go b/java/jdeps.go
new file mode 100644
index 0000000..c7fa42a
--- /dev/null
+++ b/java/jdeps.go
@@ -0,0 +1,109 @@
+// 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) {
+ 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)
+ }
+ out := data.OutputFile.String()
+ if out != "" {
+ dpInfo.Installed_paths = append(dpInfo.Installed_paths, out)
+ }
+ 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..ca8a3cd
--- /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.properties.Jarjar_rules = &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/prebuilt_apis.go b/java/prebuilt_apis.go
new file mode 100644
index 0000000..59b2092
--- /dev/null
+++ b/java/prebuilt_apis.go
@@ -0,0 +1,200 @@
+// 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"
+ "strconv"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// 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.
+// Specificaly, 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 lastest <ver>.
+//
+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) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // no need to implement
+}
+
+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 int, scope string) {
+ elements := strings.Split(path, "/")
+ ver, err := strconv.Atoi(elements[0])
+ if err != nil {
+ ctx.ModuleErrorf("invalid version %q found in path: %q", elements[0], path)
+ return
+ }
+ apiver = ver
+
+ 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 prebuiltSdkStubs(mctx android.TopDownMutatorContext) {
+ mydir := mctx.ModuleDir() + "/"
+ // <apiver>/<scope>/<module>.jar
+ 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+"*/*.jar", nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob jar files under %q: %s", mydir+apiver+"/"+scope, err)
+ }
+ files = append(files, vfiles...)
+ }
+ }
+
+ 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, err := mctx.GlobWithDeps(mydir+"*/*/api/*.txt", nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob api txt files under %q: %s", mydir, err)
+ }
+ 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 int
+ 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, strconv.Itoa(apiver), localPath)
+
+ // find the latest apiver
+ key := module + "." + scope
+ info, ok := m[key]
+ if !ok {
+ m[key] = latestApiInfo{module, scope, apiver, localPath}
+ } else if apiver > info.apiver {
+ info.apiver = apiver
+ info.path = localPath
+ }
+ }
+ // 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)
+ }
+}
+
+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..8028039 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -18,25 +18,29 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/proptools"
"android/soong/android"
)
func init() {
pctx.HostBinToolVariable("protocCmd", "aprotoc")
+ pctx.HostBinToolVariable("depFixCmd", "dep_fixer")
}
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 && ` +
+ `$protocCmd $protoOut=$protoOutParams:$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` +
+ `$depFixCmd $out.d && ` +
`${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
CommandDeps: []string{
"$protocCmd",
+ "$depFixCmd",
"${config.SoongZipCmd}",
},
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
}, "protoBase", "protoFlags", "protoOut", "protoOutParams")
)
@@ -67,29 +71,29 @@
}
func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
- switch proptools.String(p.Proto.Type) {
+ switch String(p.Proto.Type) {
case "micro":
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-micro")
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
case "nano":
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-nano")
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
case "lite", "":
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-lite")
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
case "full":
if ctx.Host() {
- ctx.AddDependency(ctx.Module(), staticLibTag, "libprotobuf-java-full")
+ 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",
- proptools.String(p.Proto.Type))
+ String(p.Proto.Type))
}
}
func protoFlags(ctx android.ModuleContext, j *CompilerProperties, p *android.ProtoProperties,
flags javaBuilderFlags) javaBuilderFlags {
- switch proptools.String(p.Proto.Type) {
+ switch String(p.Proto.Type) {
case "micro":
flags.protoOutTypeFlag = "--javamicro_out"
case "nano":
@@ -101,7 +105,7 @@
flags.protoOutTypeFlag = "--java_out"
default:
ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- proptools.String(p.Proto.Type))
+ String(p.Proto.Type))
}
if len(j.Proto.Output_params) > 0 {
diff --git a/java/sdk_library.go b/java/sdk_library.go
new file mode 100644
index 0000000..e513a59
--- /dev/null
+++ b/java/sdk_library.go
@@ -0,0 +1,726 @@
+// 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"
+ "sort"
+ "strings"
+ "sync"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ sdkStubsLibrarySuffix = ".stubs"
+ sdkSystemApiSuffix = ".system"
+ sdkTestApiSuffix = ".test"
+ sdkDocsSuffix = ".docs"
+ sdkImplLibrarySuffix = ".impl"
+ sdkXmlFileSuffix = ".xml"
+)
+
+type stubsLibraryDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+var (
+ publicApiStubsTag = dependencyTag{name: "public"}
+ systemApiStubsTag = dependencyTag{name: "system"}
+ testApiStubsTag = dependencyTag{name: "test"}
+ implLibTag = dependencyTag{name: "platform"}
+ 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 source files used to compile the Java module. May be .java, .logtags, .proto,
+ // or .aidl files.
+ Srcs []string `android:"arch_variant"`
+
+ // list of optional source files that are part of API but not part of runtime library.
+ Api_srcs []string `android:"arch_variant"`
+
+ // list of of java libraries that will be in the classpath
+ Libs []string `android:"arch_variant"`
+
+ // list of java libraries that will be compiled into the resulting runtime jar.
+ // These libraries are not compiled into the stubs jar.
+ Static_libs []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
+
+ Errorprone struct {
+ // List of javac flags that should only be used when running errorprone.
+ Javacflags []string
+ }
+
+ // Additional droiddoc options
+ Droiddoc_options []string
+
+ // If set to true, compile dex regardless of installable. Defaults to false.
+ // This applies to the stubs lib.
+ Compile_dex *bool
+
+ // the sub dirs under srcs_lib_whitelist_dirs will be scanned for java srcs.
+ // Defaults to "android.annotation".
+ Srcs_lib_whitelist_pkgs []string
+
+ // if set to true, create stubs through Metalava instead of Doclava. Javadoc/Doclava is
+ // currently still used for documentation generation, and will be replaced by Dokka soon.
+ // Defaults to true.
+ Metalava_enabled *bool
+
+ // TODO: determines whether to create HTML doc or not
+ //Html_doc *bool
+}
+
+type sdkLibrary struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ properties sdkLibraryProperties
+ deviceProperties CompilerDeviceProperties
+
+ publicApiStubsPath android.Paths
+ systemApiStubsPath android.Paths
+ testApiStubsPath android.Paths
+ implLibPath android.Paths
+
+ publicApiStubsImplPath android.Paths
+ systemApiStubsImplPath android.Paths
+ testApiStubsImplPath android.Paths
+ implLibImplPath android.Paths
+
+ publicApiFilePath android.Path
+ systemApiFilePath android.Path
+ testApiFilePath android.Path
+}
+
+func (module *sdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // Add dependencies to the stubs library
+ ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+ ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+ ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
+ ctx.AddVariationDependencies(nil, implLibTag, module.implName())
+
+ ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
+ ctx.AddVariationDependencies(nil, systemApiFileTag, module.docsName(apiScopeSystem))
+ ctx.AddVariationDependencies(nil, testApiFileTag, module.docsName(apiScopeTest))
+}
+
+func (module *sdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // 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()
+ case implLibTag:
+ module.implLibPath = lib.HeaderJars()
+ module.implLibImplPath = lib.ImplementationJars()
+ default:
+ ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
+ }
+ }
+ 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 {
+ return android.AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ // Create a phony module that installs the impl library, for the case when this lib is
+ // in PRODUCT_PACKAGES.
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+ fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+module.implName())
+ fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+ owner := module.ModuleBase.Owner()
+ if owner == "" {
+ 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.publicApiStubsPath.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.systemApiStubsPath.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.testApiStubsPath.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")+")")
+ }
+ },
+ }
+}
+
+// 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() + sdkImplLibrarySuffix
+}
+
+// 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
+ Product_variables struct {
+ Unbundled_build struct {
+ Enabled *bool
+ }
+ Pdk struct {
+ Enabled *bool
+ }
+ }
+ }{}
+
+ 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.properties.Stub_only_libs
+ // Unbundled apps will use the prebult one from /prebuilts/sdk
+ props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
+ props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
+ if module.properties.Compile_dex != nil {
+ props.Compile_dex = module.properties.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
+ Args *string
+ Api_tag_name *string
+ Api_filename *string
+ Removed_api_filename *string
+ Check_api struct {
+ Current ApiToCheck
+ Last_released ApiToCheck
+ }
+ Aidl struct {
+ Include_dirs []string
+ Local_include_dirs []string
+ }
+ }{}
+ droiddocProps := struct {
+ Custom_template *string
+ }{}
+
+ props.Name = proptools.StringPtr(module.docsName(apiScope))
+ props.Srcs = append(props.Srcs, module.properties.Srcs...)
+ props.Srcs = append(props.Srcs, module.properties.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.properties.Libs
+ props.Libs = append(props.Libs, module.properties.Static_libs...)
+ props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
+ props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
+
+ if module.properties.Metalava_enabled == nil {
+ module.properties.Metalava_enabled = proptools.BoolPtr(true)
+ }
+
+ droiddocArgs := ""
+ if Bool(module.properties.Metalava_enabled) == true {
+ droiddocArgs = " --stub-packages " + strings.Join(module.properties.Api_packages, ":") +
+ " " + android.JoinWithPrefix(module.properties.Hidden_api_packages, " --hide-package ") +
+ " " + android.JoinWithPrefix(module.properties.Droiddoc_options, " ") +
+ " --hide MissingPermission --hide BroadcastBehavior " +
+ "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
+ "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
+ } else {
+ droiddocProps.Custom_template = proptools.StringPtr("droiddoc-templates-sdk")
+ droiddocArgs = " -stubpackages " + strings.Join(module.properties.Api_packages, ":") +
+ " " + android.JoinWithPrefix(module.properties.Hidden_api_packages, " -hidePackage ") +
+ " " + android.JoinWithPrefix(module.properties.Droiddoc_options, " ") +
+ " -hide 110 -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 -nodocs"
+ }
+
+ switch apiScope {
+ case apiScopeSystem:
+ droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi"
+ case apiScopeTest:
+ droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.TestApi"
+ }
+ 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))
+ if Bool(module.properties.Metalava_enabled) == false {
+ // any change is reported as error
+ props.Check_api.Current.Args = proptools.StringPtr("-error 2 -error 3 -error 4 -error 5 " +
+ "-error 6 -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 " +
+ "-error 14 -error 15 -error 16 -error 17 -error 18 -error 19 -error 20 " +
+ "-error 21 -error 23 -error 24 -error 25 -error 26 -error 27")
+
+ // backward incompatible changes are reported as error
+ props.Check_api.Last_released.Args = proptools.StringPtr("-hide 2 -hide 3 -hide 4 -hide 5 " +
+ "-hide 6 -hide 24 -hide 25 -hide 26 -hide 27 " +
+ "-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 " +
+ "-error 15 -error 16 -error 17 -error 18")
+
+ // Include the part of the framework source. This is required for the case when
+ // API class is extending from the framework class. In that case, doclava needs
+ // to know whether the base class is hidden or not. Since that information is
+ // encoded as @hide string in the comment, we need source files for the classes,
+ // not the compiled ones.
+ props.Srcs_lib = proptools.StringPtr("framework")
+ props.Srcs_lib_whitelist_dirs = []string{"core/java"}
+
+ // Add android.annotation package to give access to the framework-defined
+ // annotations such as SystemApi, NonNull, etc.
+ if module.properties.Srcs_lib_whitelist_pkgs != nil {
+ props.Srcs_lib_whitelist_pkgs = module.properties.Srcs_lib_whitelist_pkgs
+ } else {
+ props.Srcs_lib_whitelist_pkgs = []string{"android.annotation"}
+ }
+ }
+ // These libs are required by doclava to parse the framework sources add via
+ // Src_lib and Src_lib_whitelist_* properties just above.
+ // If we don't add them to the classpath, errors messages are generated by doclava,
+ // though they don't break the build.
+ props.Libs = append(props.Libs, "framework")
+
+ if Bool(module.properties.Metalava_enabled) == true {
+ mctx.CreateModule(android.ModuleFactoryAdaptor(DroidstubsFactory), &props)
+ } else {
+ mctx.CreateModule(android.ModuleFactoryAdaptor(DroiddocFactory), &props, &droiddocProps)
+ }
+}
+
+// Creates the runtime library. This is not directly linkable from other modules.
+func (module *sdkLibrary) createImplLibrary(mctx android.TopDownMutatorContext) {
+ props := struct {
+ Name *string
+ Srcs []string
+ Libs []string
+ Static_libs []string
+ Soc_specific *bool
+ Device_specific *bool
+ Product_specific *bool
+ Installable *bool
+ Required []string
+ Errorprone struct {
+ Javacflags []string
+ }
+ }{}
+
+ props.Name = proptools.StringPtr(module.implName())
+ props.Srcs = module.properties.Srcs
+ props.Libs = module.properties.Libs
+ props.Static_libs = module.properties.Static_libs
+ props.Installable = proptools.BoolPtr(true)
+ // XML file is installed along with the impl lib
+ props.Required = []string{module.xmlFileName()}
+ props.Errorprone.Javacflags = module.properties.Errorprone.Javacflags
+
+ 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, &module.deviceProperties)
+}
+
+// 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)
+}
+
+// to satisfy SdkLibraryDependency interface
+func (module *sdkLibrary) HeaderJars(linkType linkType) android.Paths {
+ // This module is just a wrapper for the stubs.
+ if linkType == javaSystem {
+ return module.systemApiStubsPath
+ } else if linkType == javaPlatform {
+ return module.implLibPath
+ } else {
+ return module.publicApiStubsPath
+ }
+}
+
+// to satisfy SdkLibraryDependency interface
+func (module *sdkLibrary) ImplementationJars(linkType linkType) android.Paths {
+ // This module is just a wrapper for the stubs.
+ if linkType == javaSystem {
+ return module.systemApiStubsImplPath
+ } else if linkType == javaPlatform {
+ return module.implLibImplPath
+ } else {
+ return module.publicApiStubsImplPath
+ }
+}
+
+func javaSdkLibraries(config android.Config) *[]string {
+ return config.Once("javaSdkLibraries", 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 {
+ if module.properties.Srcs == nil {
+ mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
+ }
+ if module.properties.Api_packages == nil {
+ mctx.PropertyErrorf("api_packages", "java_sdk_library must specify api_packages")
+ }
+
+ // for public API stubs
+ module.createStubsLibrary(mctx, apiScopePublic)
+ module.createDocs(mctx, apiScopePublic)
+
+ // 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)
+ module.createImplLibrary(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 sdkLibraryFactory() android.Module {
+ module := &sdkLibrary{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.deviceProperties)
+ InitJavaModule(module, android.DeviceSupported)
+ return 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/phony/phony.go b/phony/phony.go
index a39b5d5..0c62e8a 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,7 +31,7 @@
requiredModuleNames []string
}
-func phonyFactory() android.Module {
+func PhonyFactory() android.Module {
module := &phony{}
android.InitAndroidModule(module)
diff --git a/python/androidmk.go b/python/androidmk.go
index 5fa01ab..20861cb 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -66,6 +66,11 @@
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)
+ }
})
base.subAndroidMk(ret, p.binaryDecorator.pythonInstaller)
}
@@ -77,6 +82,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)
diff --git a/python/binary.go b/python/binary.go
index 0314edb..bf9acb4 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,8 +18,6 @@
import (
"fmt"
- "path/filepath"
- "strings"
"android/soong/android"
)
@@ -80,89 +78,44 @@
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{}
- }
-
- // 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)
- }
+func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
+ embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
+ depsSrcsZips android.Paths) android.OptionalPath {
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 +149,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..11a792a 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,25 +28,37 @@
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 && ` +
+ `$mergeParCmd -p -pm $stub $mergedZip $srcsZips && echo '#!/usr/bin/env python' | cat - $mergedZip > $out && ` +
+ `chmod +x $out && (rm -f $stub; rm -f $mergedZip)`,
+ CommandDeps: []string{"$mergeParCmd"},
+ },
+ "interp", "main", "template", "stub", "mergedZip", "srcsZips")
+
+ embeddedPar = pctx.AndroidStaticRule("embeddedPar",
+ blueprint.RuleParams{
+ Command: `echo '$main' > $entryPoint &&` +
+ `$mergeParCmd -p -e $entryPoint $mergedZip $srcsZips && cat $launcher | cat - $mergedZip > $out && ` +
+ `chmod +x $out && (rm -f $entryPoint; rm -f $mergedZip)`,
+ CommandDeps: []string{"$mergeParCmd"},
+ },
+ "main", "entryPoint", "mergedZip", "srcsZips", "launcher")
)
func init() {
@@ -55,132 +66,62 @@
pctx.Import("android/soong/common")
pctx.HostBinToolVariable("parCmd", "soong_zip")
+ pctx.HostBinToolVariable("mergeParCmd", "merge_zips")
}
-type fileListSpec struct {
- fileList android.Path
- relativeRoot string
-}
+func registerBuildActionForParFile(ctx android.ModuleContext, embeddedLauncher bool,
+ launcherPath android.OptionalPath, interpreter, main, binName string,
+ srcsZips android.Paths) android.Path {
-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)
+ // .intermediate output path for merged zip file.
+ mergedZip := android.PathForModuleOut(ctx, binName+".mergedzip")
// .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),
- "template": template.String(),
- "stub": stub,
- "parFile": parFile.String(),
- "parArgs": strings.Join(parArgs, " "),
+ "interp": strings.Replace(interpreter, "/", `\/`, -1),
+ "main": strings.Replace(main, "/", `\/`, -1),
+ "template": template.String(),
+ "stub": stub,
+ "mergedZip": mergedZip.String(),
+ "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,
+ Rule: embeddedPar,
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(),
+ "main": main,
+ "entryPoint": entryPoint,
+ "mergedZip": mergedZip.String(),
+ "srcsZips": strings.Join(srcsZips.Strings(), " "),
+ "launcher": launcherPath.String(),
},
})
}
diff --git a/python/proto.go b/python/proto.go
new file mode 100644
index 0000000..2370cd2
--- /dev/null
+++ b/python/proto.go
@@ -0,0 +1,73 @@
+// 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"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+func init() {
+ pctx.HostBinToolVariable("protocCmd", "aprotoc")
+}
+
+var (
+ proto = pctx.AndroidStaticRule("protoc",
+ blueprint.RuleParams{
+ Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
+ `$protocCmd --python_out=$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` +
+ `$parCmd -o $out $pkgPathArgs -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
+ CommandDeps: []string{
+ "$protocCmd",
+ "$parCmd",
+ },
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
+ }, "protoBase", "protoFlags", "pkgPathArgs")
+)
+
+func genProto(ctx android.ModuleContext, p *android.ProtoProperties,
+ protoFile android.Path, protoFlags []string, pkgPath string) android.Path {
+ srcJarFile := android.PathForModuleGen(ctx, protoFile.Base()+".srcszip")
+
+ protoRoot := android.ProtoCanonicalPathFromRoot(ctx, p)
+
+ var protoBase string
+ if protoRoot {
+ protoBase = "."
+ } else {
+ protoBase = strings.TrimSuffix(protoFile.String(), protoFile.Rel())
+ }
+
+ var pkgPathArgs string
+ if pkgPath != "" {
+ pkgPathArgs = "-P " + pkgPath
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: proto,
+ Description: "protoc " + protoFile.Rel(),
+ Output: srcJarFile,
+ Input: protoFile,
+ Args: map[string]string{
+ "protoBase": protoBase,
+ "protoFlags": strings.Join(protoFlags, " "),
+ "pkgPathArgs": pkgPathArgs,
+ },
+ })
+
+ return srcJarFile
+}
diff --git a/python/python.go b/python/python.go
index ed879eb..e8b4713 100644
--- a/python/python.go
+++ b/python/python.go
@@ -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.
@@ -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,9 +153,9 @@
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
}
type installer interface {
@@ -168,7 +165,7 @@
type PythonDependency interface {
GetSrcsPathMappings() []pathMapping
GetDataPathMappings() []pathMapping
- GetParSpec() parSpec
+ GetSrcsZip() android.Path
}
func (p *Module) GetSrcsPathMappings() []pathMapping {
@@ -179,8 +176,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 +186,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()...)
}
@@ -208,15 +205,15 @@
var (
pythonLibTag = dependencyTag{name: "pythonLib"}
launcherTag = dependencyTag{name: "launcher"}
- pyIdentifierRegexp = regexp.MustCompile(`^([a-z]|[A-Z]|_)([a-z]|[A-Z]|[0-9]|_)*$`)
+ 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"
- runFiles = "runfiles"
internal = "internal"
)
@@ -253,14 +250,39 @@
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 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:
+ 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) {
// deps from "data".
android.ExtractSourcesDeps(ctx, p.properties.Data)
@@ -268,6 +290,9 @@
android.ExtractSourcesDeps(ctx, p.properties.Srcs)
android.ExtractSourcesDeps(ctx, p.properties.Exclude_srcs)
+ if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
+ ctx.AddVariationDependencies(nil, pythonLibTag, "libprotobuf-python")
+ }
switch p.properties.Actual_version {
case pyVersion2:
// deps from "version.py2.srcs" property.
@@ -281,7 +306,7 @@
if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion2) {
ctx.AddVariationDependencies(nil, pythonLibTag, "py2-stdlib")
ctx.AddFarVariationDependencies([]blueprint.Variation{
- {"arch", ctx.Target().String()},
+ {Mutator: "arch", Variation: ctx.Target().String()},
}, launcherTag, "py2-launcher")
}
@@ -336,16 +361,17 @@
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() {
@@ -378,56 +404,47 @@
expandedData := ctx.ExpandSources(p.properties.Data, nil)
// 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 +458,94 @@
}
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 {
+ for _, srcFile := range protoSrcs {
+ zip := genProto(ctx, &p.protoProperties, srcFile,
+ android.ProtoFlags(ctx, &p.protoProperties), 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 +559,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 +574,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)
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/stub_template_host.txt b/python/scripts/stub_template_host.txt
index b90a28b..213401d 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -11,15 +11,12 @@
PYTHON_BINARY = '%interpreter%'
MAIN_FILE = '%main%'
PYTHON_PATH = 'PYTHONPATH'
-ZIP_RUNFILES_DIRECTORY_NAME = 'runfiles'
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 +35,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()
@@ -85,7 +83,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..39326f0 100644
--- a/python/test.go
+++ b/python/test.go
@@ -25,8 +25,20 @@
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"`
+}
+
type testDecorator struct {
*binaryDecorator
+
+ testProperties TestProperties
+}
+
+func (test *testDecorator) bootstrapperProps() []interface{} {
+ return append(test.binaryDecorator.bootstrapperProps(), &test.testProperties)
}
func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index d150451..7f6e2c9 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -48,7 +48,7 @@
"Platform_sdk_version": ${PLATFORM_SDK_VERSION},
"Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
- "DeviceName": "flounder",
+ "DeviceName": "generic_arm64",
"DeviceArch": "arm64",
"DeviceArchVariant": "armv8-a",
"DeviceCpuVariant": "denver64",
@@ -62,7 +62,6 @@
"armeabi-v7a"
],
"HostArch": "x86_64",
- "HostSecondaryArch": "x86",
"Malloc_not_svelte": false,
"Safestack": false
}
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/copygcclib.sh
deleted file mode 100755
index 28359fc..0000000
--- a/scripts/copygcclib.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash -e
-
-# 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.
-
-OUT=$1
-shift
-LIBPATH=$($@ | sed -e "s|^$PWD/||")
-cp -f $LIBPATH $OUT
-echo "$OUT: $LIBPATH" > ${OUT}.d
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..07925df
--- /dev/null
+++ b/scripts/manifest_fixer.py
@@ -0,0 +1,336 @@
+#!/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('--prefer-integrity', type=bool, dest='prefer_integrity',
+ help=('specify if the app prefers strict integrity. Should not be 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_prefer_integrity(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, 'preferIntegrity')
+ if attr is None:
+ attr = doc.createAttributeNS(android_ns, 'android:preferIntegrity')
+ attr.value = 'true'
+ application.setAttributeNode(attr)
+ elif attr.value != 'true':
+ raise RuntimeError('existing attribute mismatches the option of --prefer-integrity')
+
+
+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.prefer_integrity:
+ add_prefer_integrity(doc)
+
+ 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..a621445
--- /dev/null
+++ b/scripts/manifest_fixer_test.py
@@ -0,0 +1,385 @@
+#!/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 PreferIntegrityTest(unittest.TestCase):
+ """Unit tests for add_prefer_integrity function."""
+
+ def run_test(self, input_manifest):
+ doc = minidom.parseString(input_manifest)
+ manifest_fixer.add_prefer_integrity(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 prefer_integrity(self, value):
+ return ' android:preferIntegrity="%s"' % value
+
+ def test_manifest_with_undeclared_preference(self):
+ manifest_input = self.manifest_tmpl % ''
+ expected = self.manifest_tmpl % self.prefer_integrity('true')
+ output = self.run_test(manifest_input)
+ self.assertEqual(output, expected)
+
+ def test_manifest_with_prefer_integrity(self):
+ manifest_input = self.manifest_tmpl % self.prefer_integrity('true')
+ expected = manifest_input
+ output = self.run_test(manifest_input)
+ self.assertEqual(output, expected)
+
+ def test_manifest_with_not_prefer_integrity(self):
+ manifest_input = self.manifest_tmpl % self.prefer_integrity('false')
+ self.assertRaises(RuntimeError, self.run_test, manifest_input)
+
+if __name__ == '__main__':
+ unittest.main()
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/strip.sh b/scripts/strip.sh
index 75e6994..4634c18 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -17,14 +17,19 @@
# 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
# --add-gnu-debuglink
+# --keep-mini-debug-info
+# --keep-symbols
+# --use-llvm-strip
+
+set -o pipefail
OPTSTRING=d:i:o:-:
@@ -32,42 +37,79 @@
cat <<EOF
Usage: strip.sh [options] -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-llvm-strip Use llvm-{strip,objcopy} instead of strip/objcopy
EOF
exit 1
}
+# With --use-llvm-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_llvm_strip}" ]; then
+ "${CLANG_BIN}/llvm-strip" --strip-all -keep=.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`
+ # Maybe we should replace this objcopy with llvm-objcopy, but
+ # we have not found a use case that is broken by objcopy yet.
+ REMOVE_SECTIONS=`"${CROSS_COMPILE}readelf" -S "${infile}" | awk '/.debug_/ {print "--remove-section " $2}' | xargs`
+ if [ ! -z "${use_llvm_strip}" ]; then
+ "${CLANG_BIN}/llvm-objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
+ else
+ "${CROSS_COMPILE}objcopy" "${infile}" "${outfile}.tmp" ${REMOVE_SECTIONS}
+ fi
}
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_llvm_strip}" ]; then
+ "${CLANG_BIN}/llvm-strip" --strip-all -keep=.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_llvm_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_llvm_strip}" ]; then
+ "${CLANG_BIN}/llvm-objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+ else
+ "${CROSS_COMPILE}objcopy" --add-gnu-debuglink="${infile}" "${outfile}.tmp"
+ fi
}
while getopts $OPTSTRING opt; do
@@ -77,9 +119,10 @@
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 ;;
+ keep-mini-debug-info) keep_mini_debug_info=true ;;
+ keep-symbols) keep_symbols=true ;;
+ use-llvm-strip) use_llvm_strip=true ;;
*) echo "Unknown option --${OPTARG}"; usage ;;
esac;;
?) usage ;;
@@ -129,12 +172,18 @@
rm -f "${outfile}"
mv "${outfile}.tmp" "${outfile}"
+if [ ! -z "${use_llvm_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/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/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/tradefed/autogen.go b/tradefed/autogen.go
new file mode 100644
index 0000000..5fb85c3
--- /dev/null
+++ b/tradefed/autogen.go
@@ -0,0 +1,167 @@
+// 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"
+
+ "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' $template > $out &&" +
+ "${optionCmd} $out",
+ CommandDeps: []string{"$template"},
+}, "name", "template", "optionCmd")
+
+func testConfigPath(ctx android.ModuleContext, prop *string) (path android.Path, autogenPath android.WritablePath) {
+ if p := getTestConfig(ctx, prop); p != nil {
+ return p, nil
+ } else if !strings.HasPrefix(ctx.ModuleDir(), "cts/") {
+ 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
+ }
+}
+
+func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, optionsMap map[string]string) {
+ // If no test option found, delete {UID_OPTION} line.
+ // If found, replace it with corresponding options format.
+ optionCmd := "sed -i '/{UID_OPTION}/d'"
+ if optionsMap != nil {
+ //Append options
+ var options []string
+ for optionName, value := range optionsMap {
+ if value != "" {
+ options = append(options, fmt.Sprintf("<option name=\"%s\" value=\"%s\" />", optionName, value))
+ }
+ }
+ optionCmd = fmt.Sprintf("sed -i 's&{UID_OPTION}&%s&g'", strings.Join(options, "\\n "))
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: autogenTestConfig,
+ Description: "test config",
+ Output: output,
+ Args: map[string]string{
+ "name": ctx.ModuleName(),
+ "template": template,
+ "optionCmd": optionCmd,
+ },
+ })
+}
+
+func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
+ testConfigTemplateProp *string, optionsMap map[string]string) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp)
+ if autogenPath != nil {
+ templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if templatePath.Valid() {
+ autogenTemplate(ctx, autogenPath, templatePath.String(), optionsMap)
+ } else {
+ if ctx.Device() {
+ autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}", optionsMap)
+ } else {
+ autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}", optionsMap)
+ }
+ }
+ return autogenPath
+ }
+ return path
+}
+
+func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
+ testConfigTemplateProp *string) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp)
+ if autogenPath != nil {
+ templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+ if templatePath.Valid() {
+ autogenTemplate(ctx, autogenPath, templatePath.String(), nil)
+ } else {
+ autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", nil)
+ }
+ return autogenPath
+ }
+ return path
+}
+
+func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp)
+ 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
+}
+
+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) android.Path {
+ path, autogenPath := testConfigPath(ctx, testConfigProp)
+ 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..2c432d0
--- /dev/null
+++ b/tradefed/config.go
@@ -0,0 +1,35 @@
+// 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("EmptyTestConfig", "build/make/core/empty_test_config.xml")
+}
diff --git a/tradefed/makevars.go b/tradefed/makevars.go
new file mode 100644
index 0000000..3ec7997
--- /dev/null
+++ b/tradefed/makevars.go
@@ -0,0 +1,35 @@
+// 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("EMPTY_TEST_CONFIG", "${EmptyTestConfig}")
+}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 5809894..a48a314 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -13,10 +13,25 @@
// 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-status",
+ "soong-ui-terminal",
"soong-ui-tracer",
"soong-shared",
"soong-finder",
@@ -33,6 +48,7 @@
"finder.go",
"kati.go",
"ninja.go",
+ "path.go",
"proc_sync.go",
"signal.go",
"soong.go",
@@ -48,13 +64,11 @@
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..c902a0f 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,16 @@
buildLock := BecomeSingletonOrFail(ctx, config)
defer buildLock.Unlock()
+ checkProblematicFiles(ctx)
+
SetupOutDir(ctx, config)
checkCaseSensitivity(ctx, config)
ensureEmptyDirectoriesExist(ctx, config.TempDir())
+ SetupPath(ctx, config)
+
if what&BuildProductConfig != 0 {
// Run make for product config
runMakeProductConfig(ctx, config)
@@ -162,7 +178,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..4dee638 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -102,12 +102,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"),
diff --git a/ui/build/config.go b/ui/build/config.go
index 27ed8e9..840f505 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -34,6 +34,7 @@
arguments []string
goma bool
environ *Environment
+ distDir string
// From the arguments
parallel int
@@ -44,10 +45,18 @@
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
+
+ pathReplaced bool
}
const srcDirFileCheck = "build/soong/root.bp"
@@ -78,6 +87,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 +114,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 +131,25 @@
// 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",
)
// Tell python not to spam the source tree with .pyc files.
ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
+ ret.environ.Set("TMPDIR", absPath(ctx, ret.TempDir()))
+
// Precondition: the current directory is the top of the source tree
if _, err := os.Stat(srcDirFileCheck); err != nil {
if os.IsNotExist(err) {
@@ -154,19 +189,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)
@@ -235,10 +258,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 +305,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 +379,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 {
@@ -485,8 +508,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 +543,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 +585,35 @@
}
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) 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
+}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index e4eab94..242e3af 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -22,13 +22,14 @@
"testing"
"android/soong/ui/logger"
+ "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{})),
}}
}
diff --git a/ui/build/context.go b/ui/build/context.go
index 0636631..c8b00c3 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -16,45 +16,14 @@
import (
"context"
- "io"
- "os"
- "time"
"android/soong/ui/logger"
+ "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,7 +31,8 @@
context.Context
logger.Logger
- StdioInterface
+ Writer terminal.Writer
+ Status *status.Status
Thread tracer.Thread
Tracer tracer.Tracer
@@ -88,28 +58,3 @@
c.Tracer.Complete(name, 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)
- }
-}
-
-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..ad57d02 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -18,6 +18,8 @@
"bytes"
"fmt"
"strings"
+
+ "android/soong/ui/status"
)
// DumpMakeVars can be used to extract the values of Make variables after the
@@ -30,8 +32,40 @@
//
// 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) {
@@ -46,7 +80,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 +93,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))
@@ -156,6 +189,19 @@
// 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",
+
+ // Not used, but useful to be in the soong.log
+ "BUILD_BROKEN_ANDROIDMK_EXPORTS",
+ "BUILD_BROKEN_DUP_COPY_HEADERS",
}, exportEnvVars...), BannerVars...)
make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true)
@@ -164,7 +210,7 @@
}
// Print the banner like make does
- fmt.Fprintln(ctx.Stdout(), Banner(make_vars))
+ ctx.Writer.Print(Banner(make_vars))
// Populate the environment
env := config.Environment()
@@ -179,4 +225,9 @@
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")
}
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..3130f74 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -56,7 +56,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 +89,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/kati.go b/ui/build/kati.go
index e4715bb..56e9a88 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -15,19 +15,21 @@
package build
import (
- "bufio"
"crypto/md5"
"fmt"
- "io"
"io/ioutil"
"path/filepath"
- "regexp"
- "strconv"
"strings"
+
+ "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 +52,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,43 +60,26 @@
}
}
-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",
"--kati_stats",
- "-f", "build/make/core/main.mk",
- }
-
- 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()))
- }
+ }, args...)
cmd := Command(ctx, config, "ckati", executable, args...)
cmd.Sandbox = katiSandbox
@@ -104,99 +89,99 @@
}
cmd.Stderr = cmd.Stdout
+ envFunc(cmd.Environment)
+
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("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("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")
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_implicit_rules",
+ "--werror_overriding_commands",
"-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..c8f19d1 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -21,15 +21,21 @@
"strconv"
"strings"
"time"
+
+ "android/soong/ui/status"
)
func runNinja(ctx Context, config Config) {
ctx.BeginTrace("ninja")
defer ctx.EndTrace()
+ fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
+ status.NinjaReader(ctx, ctx.Status.StartTool(), fifo)
+
executable := config.PrebuiltBuildTool("ninja")
args := []string{
"-d", "keepdepfile",
+ "--frontend_file", fifo,
}
args = append(args, config.NinjaArgs()...)
@@ -47,9 +53,6 @@
args = append(args, "-f", config.CombinedNinjaFile())
- if config.IsVerbose() {
- args = append(args, "-v")
- }
args = append(args, "-w", "dupbuild=err")
cmd := Command(ctx, config, "ninja", executable, args...)
@@ -57,6 +60,8 @@
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 +71,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 +97,7 @@
}
}()
- startTime := time.Now()
- defer ctx.ImportNinjaLog(logPath, startTime)
-
- cmd.RunOrFatal()
+ cmd.RunAndPrintOrFatal()
}
type statusChecker struct {
diff --git a/ui/build/path.go b/ui/build/path.go
new file mode 100644
index 0000000..52658ef
--- /dev/null
+++ b/ui/build/path.go
@@ -0,0 +1,149 @@
+// 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"
+ "strings"
+
+ "github.com/google/blueprint/microfactory"
+
+ "android/soong/ui/build/paths"
+)
+
+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("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)
+ 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..c4fcc20
--- /dev/null
+++ b/ui/build/paths/config.go
@@ -0,0 +1,181 @@
+// 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
+}
+
+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,
+}
+
+func GetConfig(name string) PathConfig {
+ if config, ok := Configuration[name]; ok {
+ return config
+ }
+ return Missing
+}
+
+var Configuration = map[string]PathConfig{
+ "awk": Allowed,
+ "basename": Allowed,
+ "bash": Allowed,
+ "bc": Allowed,
+ "bzip2": Allowed,
+ "cat": Allowed,
+ "chmod": Allowed,
+ "cmp": Allowed,
+ "comm": Allowed,
+ "cp": Allowed,
+ "cut": Allowed,
+ "date": Allowed,
+ "dd": Allowed,
+ "diff": Allowed,
+ "dirname": Allowed,
+ "du": Allowed,
+ "echo": Allowed,
+ "egrep": Allowed,
+ "env": Allowed,
+ "expr": Allowed,
+ "find": Allowed,
+ "fuser": Allowed,
+ "getconf": Allowed,
+ "getopt": Allowed,
+ "git": Allowed,
+ "grep": Allowed,
+ "gzip": Allowed,
+ "head": Allowed,
+ "hexdump": Allowed,
+ "hostname": Allowed,
+ "id": Allowed,
+ "jar": Allowed,
+ "java": Allowed,
+ "javap": Allowed,
+ "ln": Allowed,
+ "ls": Allowed,
+ "lsof": Allowed,
+ "m4": Allowed,
+ "md5sum": Allowed,
+ "mkdir": Allowed,
+ "mktemp": Allowed,
+ "mv": Allowed,
+ "od": Allowed,
+ "openssl": Allowed,
+ "paste": Allowed,
+ "patch": Allowed,
+ "pgrep": Allowed,
+ "pkill": Allowed,
+ "ps": Allowed,
+ "pstree": Allowed,
+ "pwd": Allowed,
+ "python": Allowed,
+ "python2.7": Allowed,
+ "python3": Allowed,
+ "readlink": Allowed,
+ "realpath": Allowed,
+ "rm": Allowed,
+ "rmdir": Allowed,
+ "rsync": Allowed,
+ "runalarm": Allowed,
+ "sed": Allowed,
+ "setsid": Allowed,
+ "sh": Allowed,
+ "sha1sum": Allowed,
+ "sha256sum": Allowed,
+ "sha512sum": Allowed,
+ "sleep": Allowed,
+ "sort": Allowed,
+ "stat": Allowed,
+ "tar": Allowed,
+ "tail": Allowed,
+ "tee": Allowed,
+ "todos": Allowed,
+ "touch": Allowed,
+ "tr": Allowed,
+ "true": Allowed,
+ "uname": Allowed,
+ "uniq": Allowed,
+ "unix2dos": Allowed,
+ "unzip": Allowed,
+ "wc": Allowed,
+ "which": Allowed,
+ "whoami": Allowed,
+ "xargs": Allowed,
+ "xxd": 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,
+
+ // We've got prebuilts of these
+ //"dtc": Forbidden,
+ //"lz4": Forbidden,
+ //"lz4c": Forbidden,
+}
+
+func init() {
+ if runtime.GOOS == "darwin" {
+ Configuration["md5"] = Allowed
+ Configuration["sw_vers"] = Allowed
+ Configuration["xcrun"] = 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/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/soong.go b/ui/build/soong.go
index cbb75c7..478c02c 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -18,9 +18,11 @@
"os"
"path/filepath"
"strconv"
- "time"
+ "strings"
"github.com/google/blueprint/microfactory"
+
+ "android/soong/ui/status"
)
func runSoong(ctx Context, config Config) {
@@ -41,9 +43,8 @@
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() {
@@ -56,12 +57,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 +78,47 @@
}
}()
+ var cfg microfactory.Config
+ cfg.Map("github.com/google/blueprint", "build/blueprint")
+
+ cfg.TrimPath = absPath(ctx, ".")
+
func() {
ctx.BeginTrace("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)
}
}()
+ func() {
+ ctx.BeginTrace("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(name)
defer ctx.EndTrace()
+ fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
+ status.NinjaReader(ctx, ctx.Status.StartTool(), fifo)
+
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..348d41f 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -16,9 +16,13 @@
import (
"bufio"
+ "fmt"
"path/filepath"
"runtime"
+ "sort"
"strings"
+
+ "android/soong/ui/status"
)
// Checks for files in the out directory that have a rule that depends on them but no rule to
@@ -36,6 +40,12 @@
ctx.BeginTrace("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 +66,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 +80,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/util.go b/ui/build/util.go
index f698ccd..0676a86 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -15,13 +15,9 @@
package build
import (
- "bytes"
- "io"
"os"
"path/filepath"
"strings"
- "syscall"
- "unsafe"
)
func absPath(ctx Context, p string) string {
@@ -62,9 +58,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
@@ -102,81 +113,3 @@
}
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
- }
- 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_test.go b/ui/build/util_test.go
index e85eada..89bfc77 100644
--- a/ui/build/util_test.go
+++ b/ui/build/util_test.go
@@ -14,49 +14,38 @@
package build
-import "testing"
+import (
+ "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"))
}
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
new file mode 100644
index 0000000..76caaef
--- /dev/null
+++ b/ui/status/Android.bp
@@ -0,0 +1,42 @@
+// 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",
+ "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..7c26d42
--- /dev/null
+++ b/ui/status/kati.go
@@ -0,0 +1,138 @@
+// 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)
+ 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..4ceb5ef
--- /dev/null
+++ b/ui/status/ninja.go
@@ -0,0 +1,151 @@
+// 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"
+
+ "github.com/golang/protobuf/proto"
+
+ "android/soong/ui/logger"
+ "android/soong/ui/status/ninja_frontend"
+)
+
+// NinjaReader reads the protobuf frontend format from ninja and translates it
+// into calls on the ToolStatus API.
+func NinjaReader(ctx logger.Logger, status ToolStatus, fifo string) {
+ os.Remove(fifo)
+
+ err := syscall.Mkfifo(fifo, 0666)
+ if err != nil {
+ ctx.Fatalf("Failed to mkfifo(%q): %v", fifo, err)
+ }
+
+ go ninjaReader(status, fifo)
+}
+
+func ninjaReader(status ToolStatus, fifo string) {
+ f, err := os.Open(fifo)
+ if err != nil {
+ status.Error(fmt.Sprintf("Failed to open fifo: %v", err))
+ }
+ defer f.Close()
+
+ r := bufio.NewReader(f)
+
+ running := map[uint32]*Action{}
+
+ for {
+ size, err := readVarInt(r)
+ if err != nil {
+ if err != io.EOF {
+ 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 {
+ status.Print(fmt.Sprintf("Missing message of size %d from ninja\n", size))
+ } else {
+ status.Error(fmt.Sprintf("Got error reading from ninja: %s", err))
+ }
+ return
+ }
+
+ msg := &ninja_frontend.Status{}
+ err = proto.Unmarshal(buf, msg)
+ if err != nil {
+ status.Print(fmt.Sprintf("Error reading message from ninja: %v", err))
+ continue
+ }
+
+ // Ignore msg.BuildStarted
+ if msg.TotalEdges != nil {
+ status.SetTotalActions(int(msg.TotalEdges.GetTotalEdges()))
+ }
+ if msg.EdgeStarted != nil {
+ action := &Action{
+ Description: msg.EdgeStarted.GetDesc(),
+ Outputs: msg.EdgeStarted.Outputs,
+ Command: msg.EdgeStarted.GetCommand(),
+ }
+ 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)
+ }
+
+ 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:
+ status.Status(message)
+ case ninja_frontend.Status_Message_WARNING:
+ status.Print("warning: " + message)
+ case ninja_frontend.Status_Message_ERROR:
+ status.Error(message)
+ default:
+ status.Print(message)
+ }
+ }
+ if msg.BuildFinished != nil {
+ 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/status.go b/ui/status/status.go
new file mode 100644
index 0000000..c851d7f
--- /dev/null
+++ b/ui/status/status.go
@@ -0,0 +1,340 @@
+// 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)
+ }
+}
+
+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..c8eb382
--- /dev/null
+++ b/ui/terminal/status.go
@@ -0,0 +1,144 @@
+// 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
+}
+
+// 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) status.StatusOutput {
+ return &statusOutput{
+ writer: w,
+ format: statusFormat,
+
+ start: time.Now(),
+ }
+}
+
+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 {
+ hasCommand := ""
+ if result.Command != "" {
+ hasCommand = "\n"
+ }
+
+ s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s%s%s",
+ strings.Join(result.Outputs, " "), result.Command, hasCommand, 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..218d73c
--- /dev/null
+++ b/xml/xml.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 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
+}
+
+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)
+
+ // To support ":modulename" in schema
+ android.ExtractSourceDeps(ctx, p.properties.Schema)
+}
+
+func (p *prebuiltEtcXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.PrebuiltEtc.GenerateAndroidBuildActions(ctx)
+
+ if p.properties.Schema != nil {
+ schema := ctx.ExpandSource(proptools.String(p.properties.Schema), "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..774966a 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(args.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(args.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(args.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