Merge "Add BoardKernelModuleInterfaceVersions."
diff --git a/android/Android.bp b/android/Android.bp
index 50faa44..ff14a70 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -15,6 +15,7 @@
"apex.go",
"api_levels.go",
"arch.go",
+ "bazel_overlay.go",
"config.go",
"csuite_config.go",
"defaults.go",
diff --git a/android/apex.go b/android/apex.go
index cd84f8a..8c06b63 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -296,7 +296,10 @@
for i, mod := range modules {
platformVariation := i == 0
if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
- mod.SkipInstall()
+ // Do not install the module for platform, but still allow it to output
+ // uninstallable AndroidMk entries in certain cases when they have
+ // side effects.
+ mod.MakeUninstallable()
}
if !platformVariation {
mod.(ApexModule).apexModuleBase().ApexProperties.Info = m.apexVariations[i-1]
diff --git a/android/bazel_overlay.go b/android/bazel_overlay.go
new file mode 100644
index 0000000..a034282
--- /dev/null
+++ b/android/bazel_overlay.go
@@ -0,0 +1,76 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+// The Bazel Overlay singleton is responsible for generating the Ninja actions
+// for calling the soong_build primary builder in the main build.ninja file.
+func init() {
+ RegisterSingletonType("bazel_overlay", BazelOverlaySingleton)
+}
+
+func BazelOverlaySingleton() Singleton {
+ return &bazelOverlaySingleton{}
+}
+
+type bazelOverlaySingleton struct{}
+
+func (c *bazelOverlaySingleton) GenerateBuildActions(ctx SingletonContext) {
+ // Create a build and rule statement, using the Bazel overlay's WORKSPACE
+ // file as the output file marker.
+ var deps Paths
+ moduleListFilePath := pathForBuildToolDep(ctx, ctx.Config().moduleListFile)
+ deps = append(deps, moduleListFilePath)
+ deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
+
+ bazelOverlayDirectory := PathForOutput(ctx, "bazel_overlay")
+ bazelOverlayWorkspaceFile := bazelOverlayDirectory.Join(ctx, "WORKSPACE")
+ primaryBuilder := primaryBuilderPath(ctx)
+ bazelOverlay := ctx.Rule(pctx, "bazelOverlay",
+ blueprint.RuleParams{
+ Command: fmt.Sprintf(
+ "rm -rf ${outDir}/* && %s --bazel_overlay_dir ${outDir} %s && echo WORKSPACE: `cat %s` > ${outDir}/.overlay-depfile.d",
+ primaryBuilder.String(),
+ strings.Join(os.Args[1:], " "),
+ moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
+ ),
+ CommandDeps: []string{primaryBuilder.String()},
+ Description: fmt.Sprintf(
+ "Creating the Bazel overlay workspace with %s at $outDir",
+ primaryBuilder.Base()),
+ Deps: blueprint.DepsGCC,
+ Depfile: "${outDir}/.overlay-depfile.d",
+ },
+ "outDir")
+
+ ctx.Build(pctx, BuildParams{
+ Rule: bazelOverlay,
+ Output: bazelOverlayWorkspaceFile,
+ Inputs: deps,
+ Args: map[string]string{
+ "outDir": bazelOverlayDirectory.String(),
+ },
+ })
+
+ // Add a phony target for building the bazel overlay
+ ctx.Phony("bazel_overlay", bazelOverlayWorkspaceFile)
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index ec522fc..68311e3 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,9 +15,7 @@
package android
import (
- "io"
"strings"
- "text/template"
)
func init() {
@@ -71,23 +69,8 @@
return append(Paths{}, fg.srcs...)
}
-var androidMkTemplate = template.Must(template.New("filegroup").Parse(`
-ifdef {{.makeVar}}
- $(error variable {{.makeVar}} set by soong module is already set in make)
-endif
-{{.makeVar}} := {{.value}}
-.KATI_READONLY := {{.makeVar}}
-`))
-
-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,
- "value": strings.Join(fg.srcs.Strings(), " "),
- })
- }
- },
+func (fg *fileGroup) MakeVars(ctx MakeVarsModuleContext) {
+ if makeVar := String(fg.properties.Export_to_make_var); makeVar != "" {
+ ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " "))
}
}
diff --git a/android/makevars.go b/android/makevars.go
index ff7c8e4..86f4b42 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -17,6 +17,7 @@
import (
"bytes"
"fmt"
+ "sort"
"strconv"
"strings"
@@ -34,43 +35,16 @@
}
///////////////////////////////////////////////////////////////////////////////
-// Interface for other packages to use to declare make variables
-type MakeVarsContext interface {
+
+// BaseMakeVarsContext contains the common functions for other packages to use
+// to declare make variables
+type BaseMakeVarsContext interface {
Config() Config
DeviceConfig() DeviceConfig
AddNinjaFileDeps(deps ...string)
- ModuleName(module blueprint.Module) string
- ModuleDir(module blueprint.Module) string
- ModuleSubDir(module blueprint.Module) string
- ModuleType(module blueprint.Module) string
- BlueprintFile(module blueprint.Module) string
-
- ModuleErrorf(module blueprint.Module, format string, args ...interface{})
- Errorf(format string, args ...interface{})
Failed() bool
- VisitAllModules(visit func(Module))
- VisitAllModulesIf(pred func(Module) bool, visit func(Module))
-
- // Verify the make variable matches the Soong version, fail the build
- // if it does not. If the make variable is empty, just set it.
- Strict(name, ninjaStr string)
- // Check to see if the make variable matches the Soong version, warn if
- // it does not. If the make variable is empty, just set it.
- Check(name, ninjaStr string)
-
- // These are equivalent to the above, but sort the make and soong
- // variables before comparing them. They also show the unique entries
- // in each list when displaying the difference, instead of the entire
- // string.
- StrictSorted(name, ninjaStr string)
- CheckSorted(name, ninjaStr string)
-
- // Evaluates a ninja string and returns the result. Used if more
- // complicated modification needs to happen before giving it to Make.
- Eval(ninjaStr string) (string, error)
-
// These are equivalent to Strict and Check, but do not attempt to
// evaluate the values before writing them to the Makefile. They can
// be used when all ninja variables have already been evaluated through
@@ -108,6 +82,48 @@
DistForGoalsWithFilename(goals []string, path Path, filename string)
}
+// MakeVarsContext contains the set of functions available for MakeVarsProvider
+// and SingletonMakeVarsProvider implementations.
+type MakeVarsContext interface {
+ BaseMakeVarsContext
+
+ ModuleName(module blueprint.Module) string
+ ModuleDir(module blueprint.Module) string
+ ModuleSubDir(module blueprint.Module) string
+ ModuleType(module blueprint.Module) string
+ BlueprintFile(module blueprint.Module) string
+
+ ModuleErrorf(module blueprint.Module, format string, args ...interface{})
+ Errorf(format string, args ...interface{})
+
+ VisitAllModules(visit func(Module))
+ VisitAllModulesIf(pred func(Module) bool, visit func(Module))
+
+ // Verify the make variable matches the Soong version, fail the build
+ // if it does not. If the make variable is empty, just set it.
+ Strict(name, ninjaStr string)
+ // Check to see if the make variable matches the Soong version, warn if
+ // it does not. If the make variable is empty, just set it.
+ Check(name, ninjaStr string)
+
+ // These are equivalent to the above, but sort the make and soong
+ // variables before comparing them. They also show the unique entries
+ // in each list when displaying the difference, instead of the entire
+ // string.
+ StrictSorted(name, ninjaStr string)
+ CheckSorted(name, ninjaStr string)
+
+ // Evaluates a ninja string and returns the result. Used if more
+ // complicated modification needs to happen before giving it to Make.
+ Eval(ninjaStr string) (string, error)
+}
+
+// MakeVarsModuleContext contains the set of functions available for modules
+// implementing the ModuleMakeVarsProvider interface.
+type MakeVarsModuleContext interface {
+ BaseMakeVarsContext
+}
+
var _ PathContext = MakeVarsContext(nil)
type MakeVarsProvider func(ctx MakeVarsContext)
@@ -135,6 +151,14 @@
return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
}
+// ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make.
+type ModuleMakeVarsProvider interface {
+ Module
+
+ // MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make.
+ MakeVars(ctx MakeVarsModuleContext)
+}
+
///////////////////////////////////////////////////////////////////////////////
func makeVarsSingletonFunc() Singleton {
@@ -209,10 +233,45 @@
dists = append(dists, mctx.dists...)
}
+ ctx.VisitAllModules(func(m Module) {
+ if provider, ok := m.(ModuleMakeVarsProvider); ok {
+ mctx := &makeVarsContext{
+ SingletonContext: ctx,
+ }
+
+ provider.MakeVars(mctx)
+
+ vars = append(vars, mctx.vars...)
+ phonies = append(phonies, mctx.phonies...)
+ dists = append(dists, mctx.dists...)
+ }
+ })
+
if ctx.Failed() {
return
}
+ sort.Slice(vars, func(i, j int) bool {
+ return vars[i].name < vars[j].name
+ })
+ sort.Slice(phonies, func(i, j int) bool {
+ return phonies[i].name < phonies[j].name
+ })
+ lessArr := func(a, b []string) bool {
+ if len(a) == len(b) {
+ for i := range a {
+ if a[i] < b[i] {
+ return true
+ }
+ }
+ return false
+ }
+ return len(a) < len(b)
+ }
+ sort.Slice(dists, func(i, j int) bool {
+ return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths)
+ })
+
outBytes := s.writeVars(vars)
if err := pathtools.WriteFileIfChanged(outFile, outBytes, 0666); err != nil {
diff --git a/android/module.go b/android/module.go
index 8605954..2dc2ac7 100644
--- a/android/module.go
+++ b/android/module.go
@@ -256,6 +256,7 @@
InstallForceOS() *OsType
SkipInstall()
IsSkipInstall() bool
+ MakeUninstallable()
ExportedToMake() bool
InitRc() Paths
VintfFragments() Paths
@@ -1046,6 +1047,15 @@
return m.commonProperties.SkipInstall == true
}
+// Similar to SkipInstall, but if the AndroidMk entry would set
+// LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
+// rather than leaving it out altogether. That happens in cases where it would
+// have other side effects, in particular when it adds a NOTICE file target,
+// which other install targets might depend on.
+func (m *ModuleBase) MakeUninstallable() {
+ m.SkipInstall()
+}
+
func (m *ModuleBase) ExportedToMake() bool {
return m.commonProperties.NamespaceExportedToMake
}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index e91b40a..7c747c8 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -286,10 +286,8 @@
entries.SubName = "." + library.stubsVersion()
}
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- // Note library.skipInstall() has a special case to get here for static
- // libraries that otherwise would have skipped installation and hence not
- // have executed AndroidMkEntries at all. The reason is to ensure they get
- // a NOTICE file make target which other libraries might depend on.
+ // library.makeUninstallable() depends on this to bypass SkipInstall() for
+ // static libraries.
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
if library.buildStubs() {
entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
@@ -518,10 +516,14 @@
entries.Class = "HEADER_LIBRARIES"
}
+ entries.SubName = ""
+
+ if c.sanitizerProperties.CfiEnabled {
+ entries.SubName += ".cfi"
+ }
+
if c.androidMkVendorSuffix {
- entries.SubName = vendorSuffix
- } else {
- entries.SubName = ""
+ entries.SubName += vendorSuffix
}
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
diff --git a/cc/cc.go b/cc/cc.go
index 4b36218..6f1a06d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -418,7 +418,7 @@
inSanitizerDir() bool
hostToolPath() android.OptionalPath
relativeInstallPath() string
- skipInstall(mod *Module)
+ makeUninstallable(mod *Module)
}
type xref interface {
@@ -2730,12 +2730,12 @@
return c.InRecovery()
}
-func (c *Module) SkipInstall() {
+func (c *Module) MakeUninstallable() {
if c.installer == nil {
- c.ModuleBase.SkipInstall()
+ c.ModuleBase.MakeUninstallable()
return
}
- c.installer.skipInstall(c)
+ c.installer.makeUninstallable(c)
}
func (c *Module) HostToolPath() android.OptionalPath {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 38a5c2d..77b5c52 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1013,17 +1013,25 @@
filepath.Join(sharedDir, "libvendor_available.so.json"))
// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+ // Also cfi variants are captured, except for prebuilts like toolchain_library
staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
+ staticCfiVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static_cfi", archType, archVariant)
staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
jsonFiles = append(jsonFiles,
filepath.Join(staticDir, "libb.a.json"),
filepath.Join(staticDir, "libvndk.a.json"),
+ filepath.Join(staticDir, "libvndk.cfi.a.json"),
filepath.Join(staticDir, "libvendor.a.json"),
- filepath.Join(staticDir, "libvendor_available.a.json"))
+ filepath.Join(staticDir, "libvendor.cfi.a.json"),
+ filepath.Join(staticDir, "libvendor_available.a.json"),
+ filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
// For binary executables, all vendor:true and vendor_available modules are captured.
if archType == "arm64" {
@@ -1055,6 +1063,39 @@
}
}
+func TestVendorSnapshotSanitizer(t *testing.T) {
+ bp := `
+ vendor_snapshot_static {
+ name: "libsnapshot",
+ vendor: true,
+ target_arch: "arm64",
+ version: "BOARD",
+ arch: {
+ arm64: {
+ src: "libsnapshot.a",
+ cfi: {
+ src: "libsnapshot.cfi.a",
+ }
+ },
+ },
+ }
+`
+ config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ ctx := testCcWithConfig(t, config)
+
+ // Check non-cfi and cfi variant.
+ staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
+ staticCfiVariant := "android_vendor.BOARD_arm64_armv8-a_static_cfi"
+
+ staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticVariant).Module().(*Module)
+ assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
+
+ staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticCfiVariant).Module().(*Module)
+ assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+}
+
func TestDoubleLoadableDepError(t *testing.T) {
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
diff --git a/cc/installer.go b/cc/installer.go
index 0b4a68c..e551c63 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -107,6 +107,6 @@
return String(installer.Properties.Relative_install_path)
}
-func (installer *baseInstaller) skipInstall(mod *Module) {
- mod.ModuleBase.SkipInstall()
+func (installer *baseInstaller) makeUninstallable(mod *Module) {
+ mod.ModuleBase.MakeUninstallable()
}
diff --git a/cc/library.go b/cc/library.go
index 2a329ac..441c7b8 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1365,16 +1365,15 @@
return android.CheckAvailableForApex(what, list)
}
-func (library *libraryDecorator) skipInstall(mod *Module) {
+func (library *libraryDecorator) makeUninstallable(mod *Module) {
if library.static() && library.buildStatic() && !library.buildStubs() {
- // If we're asked to skip installation of a static library (in particular
- // when it's not //apex_available:platform) we still want an AndroidMk entry
- // for it to ensure we get the relevant NOTICE file targets (cf.
- // notice_files.mk) that other libraries might depend on. AndroidMkEntries
- // always sets LOCAL_UNINSTALLABLE_MODULE for these entries.
+ // If we're asked to make a static library uninstallable we don't do
+ // anything since AndroidMkEntries always sets LOCAL_UNINSTALLABLE_MODULE
+ // for these entries. This is done to still get the make targets for NOTICE
+ // files from notice_files.mk, which other libraries might depend on.
return
}
- mod.ModuleBase.SkipInstall()
+ mod.ModuleBase.MakeUninstallable()
}
var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 653b43e..baf43ce 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -199,10 +199,6 @@
p.properties.Srcs = nil
}
-func (p *prebuiltLibraryLinker) skipInstall(mod *Module) {
- mod.ModuleBase.SkipInstall()
-}
-
func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewLibrary(hod)
module.compiler = nil
@@ -211,7 +207,6 @@
libraryDecorator: library,
}
module.linker = prebuilt
- module.installer = prebuilt
module.AddProperties(&prebuilt.properties)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 3433dd3..6db6348 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -321,14 +321,14 @@
// Is CFI actually enabled?
if !ctx.Config().EnableCFI() {
- s.Cfi = nil
- s.Diag.Cfi = nil
+ s.Cfi = boolPtr(false)
+ s.Diag.Cfi = boolPtr(false)
}
// Also disable CFI for arm32 until b/35157333 is fixed.
if ctx.Arch().ArchType == android.Arm {
- s.Cfi = nil
- s.Diag.Cfi = nil
+ s.Cfi = boolPtr(false)
+ s.Diag.Cfi = boolPtr(false)
}
// HWASan requires AArch64 hardware feature (top-byte-ignore).
@@ -343,14 +343,14 @@
// Also disable CFI if ASAN is enabled.
if Bool(s.Address) || Bool(s.Hwaddress) {
- s.Cfi = nil
- s.Diag.Cfi = nil
+ s.Cfi = boolPtr(false)
+ s.Diag.Cfi = boolPtr(false)
}
// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
if !ctx.Os().Linux() {
- s.Cfi = nil
- s.Diag.Cfi = nil
+ s.Cfi = boolPtr(false)
+ s.Diag.Cfi = boolPtr(false)
s.Misc_undefined = nil
s.Undefined = nil
s.All_undefined = nil
@@ -359,14 +359,15 @@
// Also disable CFI for VNDK variants of components
if ctx.isVndk() && ctx.useVndk() {
- s.Cfi = nil
- s.Diag.Cfi = nil
- }
-
- // Also disable CFI if building against snapshot.
- vndkVersion := ctx.DeviceConfig().VndkVersion()
- if ctx.useVndk() && vndkVersion != "current" && vndkVersion != "" {
- s.Cfi = nil
+ if ctx.static() {
+ // Cfi variant for static vndk should be captured as vendor snapshot,
+ // so don't strictly disable Cfi.
+ s.Cfi = nil
+ s.Diag.Cfi = nil
+ } else {
+ s.Cfi = boolPtr(false)
+ s.Diag.Cfi = boolPtr(false)
+ }
}
// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
@@ -411,7 +412,7 @@
// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
// mutually incompatible.
if Bool(s.Fuzzer) {
- s.Cfi = nil
+ s.Cfi = boolPtr(false)
}
}
@@ -741,27 +742,64 @@
}
}
+// Determines if the current module is a static library going to be captured
+// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
+// except for ones which explicitly disable cfi.
+func needsCfiForVendorSnapshot(mctx android.TopDownMutatorContext) bool {
+ if isVendorProprietaryPath(mctx.ModuleDir()) {
+ return false
+ }
+
+ c := mctx.Module().(*Module)
+
+ if !c.inVendor() {
+ return false
+ }
+
+ if !c.static() {
+ return false
+ }
+
+ if c.Prebuilt() != nil {
+ return false
+ }
+
+ return c.sanitize != nil &&
+ !Bool(c.sanitize.Properties.Sanitize.Never) &&
+ !c.sanitize.isSanitizerExplicitlyDisabled(cfi)
+}
+
// 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.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 || t == hwasan || t == scs {
- if d.static() {
+ if c, ok := mctx.Module().(*Module); ok {
+ enabled := c.sanitize.isSanitizerEnabled(t)
+ if t == cfi && needsCfiForVendorSnapshot(mctx) {
+ // We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
+ // determine defaultVariation in sanitizerMutator below.
+ // Instead, just mark SanitizeDep to forcefully create cfi variant.
+ enabled = true
+ c.sanitize.Properties.SanitizeDep = true
+ }
+ if enabled {
+ 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 || t == hwasan || t == scs {
+ if d.static() {
+ d.sanitize.Properties.SanitizeDep = true
+ }
+ } else {
d.sanitize.Properties.SanitizeDep = true
}
- } else {
- d.sanitize.Properties.SanitizeDep = true
}
- }
- return true
- })
+ return true
+ })
+ }
} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
// If an APEX module includes a lib which is enabled for a sanitizer T, then
// the APEX module is also enabled for the same sanitizer type.
@@ -1095,6 +1133,24 @@
// APEX modules fall here
sanitizeable.AddSanitizerDependencies(mctx, t.name())
mctx.CreateVariations(t.variationName())
+ } else if c, ok := mctx.Module().(*Module); ok {
+ // Check if it's a snapshot module supporting sanitizer
+ if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
+ // Set default variation as above.
+ defaultVariation := t.variationName()
+ mctx.SetDefaultDependencyVariation(&defaultVariation)
+ modules := mctx.CreateVariations("", t.variationName())
+ modules[0].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, false)
+ modules[1].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, true)
+
+ // Export the static lib name to make
+ if c.static() && c.ExportedToMake() {
+ if t == cfi {
+ // use BaseModuleName which is the name for Make.
+ cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+ }
+ }
+ }
}
}
}
diff --git a/cc/testing.go b/cc/testing.go
index c2353d8..4113fd2 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -567,6 +567,7 @@
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
+ ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
android.RegisterPrebuiltMutators(ctx)
RegisterRequiredBuildComponentsForTest(ctx)
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 6df940c..e17a6d0 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -80,23 +80,75 @@
}).(*snapshotMap)
}
-type vendorSnapshotLibraryProperties struct {
+type vendorSnapshotBaseProperties struct {
// snapshot version.
Version string
// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
Target_arch string
+}
+// vendorSnapshotModuleBase provides common basic functions for all snapshot modules.
+type vendorSnapshotModuleBase struct {
+ baseProperties vendorSnapshotBaseProperties
+ moduleSuffix string
+}
+
+func (p *vendorSnapshotModuleBase) Name(name string) string {
+ return name + p.NameSuffix()
+}
+
+func (p *vendorSnapshotModuleBase) NameSuffix() string {
+ versionSuffix := p.version()
+ if p.arch() != "" {
+ versionSuffix += "." + p.arch()
+ }
+
+ return p.moduleSuffix + versionSuffix
+}
+
+func (p *vendorSnapshotModuleBase) version() string {
+ return p.baseProperties.Version
+}
+
+func (p *vendorSnapshotModuleBase) arch() string {
+ return p.baseProperties.Target_arch
+}
+
+func (p *vendorSnapshotModuleBase) isSnapshotPrebuilt() bool {
+ return true
+}
+
+// Call this after creating a snapshot module with module suffix
+// such as vendorSnapshotSharedSuffix
+func (p *vendorSnapshotModuleBase) init(m *Module, suffix string) {
+ p.moduleSuffix = suffix
+ m.AddProperties(&p.baseProperties)
+ android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+ vendorSnapshotLoadHook(ctx, p)
+ })
+}
+
+func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *vendorSnapshotModuleBase) {
+ if p.version() != ctx.DeviceConfig().VndkVersion() {
+ ctx.Module().Disable()
+ return
+ }
+}
+
+type vendorSnapshotLibraryProperties struct {
// Prebuilt file for each arch.
Src *string `android:"arch_variant"`
+ // list of directories that will be added to the include path (using -I).
+ Export_include_dirs []string `android:"arch_variant"`
+
+ // list of directories that will be added to the system path (using -isystem).
+ Export_system_include_dirs []string `android:"arch_variant"`
+
// list of flags that will be used for any module that links against this module.
Export_flags []string `android:"arch_variant"`
- // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols,
- // etc).
- Check_elf_files *bool
-
// Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
Sanitize_ubsan_dep *bool `android:"arch_variant"`
@@ -104,42 +156,24 @@
Sanitize_minimal_dep *bool `android:"arch_variant"`
}
+type snapshotSanitizer interface {
+ isSanitizerEnabled(t sanitizerType) bool
+ setSanitizerVariation(t sanitizerType, enabled bool)
+}
+
type vendorSnapshotLibraryDecorator struct {
+ vendorSnapshotModuleBase
*libraryDecorator
- properties vendorSnapshotLibraryProperties
+ properties vendorSnapshotLibraryProperties
+ sanitizerProperties struct {
+ CfiEnabled bool `blueprint:"mutated"`
+
+ // Library flags for cfi variant.
+ Cfi vendorSnapshotLibraryProperties `android:"arch_variant"`
+ }
androidMkVendorSuffix bool
}
-func (p *vendorSnapshotLibraryDecorator) Name(name string) string {
- return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotLibraryDecorator) NameSuffix() string {
- versionSuffix := p.version()
- if p.arch() != "" {
- versionSuffix += "." + p.arch()
- }
-
- var linkageSuffix string
- if p.buildShared() {
- linkageSuffix = vendorSnapshotSharedSuffix
- } else if p.buildStatic() {
- linkageSuffix = vendorSnapshotStaticSuffix
- } else {
- linkageSuffix = vendorSnapshotHeaderSuffix
- }
-
- return linkageSuffix + versionSuffix
-}
-
-func (p *vendorSnapshotLibraryDecorator) version() string {
- return p.properties.Version
-}
-
-func (p *vendorSnapshotLibraryDecorator) arch() string {
- return p.properties.Target_arch
-}
-
func (p *vendorSnapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
return p.libraryDecorator.linkerFlags(ctx, flags)
@@ -165,11 +199,16 @@
return p.libraryDecorator.link(ctx, flags, deps, objs)
}
+ if p.sanitizerProperties.CfiEnabled {
+ p.properties = p.sanitizerProperties.Cfi
+ }
+
if !p.matchesWithDevice(ctx.DeviceConfig()) {
return nil
}
- p.libraryDecorator.exportIncludes(ctx)
+ p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
+ p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
in := android.PathForModuleSrc(ctx, *p.properties.Src)
@@ -189,32 +228,38 @@
return in
}
-func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
- return false
-}
-
-func (p *vendorSnapshotLibraryDecorator) isSnapshotPrebuilt() bool {
- return true
-}
-
func (p *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
p.baseInstaller.install(ctx, file)
}
}
-type vendorSnapshotInterface interface {
- version() string
+func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
+ return false
}
-func vendorSnapshotLoadHook(ctx android.LoadHookContext, p vendorSnapshotInterface) {
- if p.version() != ctx.DeviceConfig().VndkVersion() {
- ctx.Module().Disable()
+func (p *vendorSnapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
+ switch t {
+ case cfi:
+ return p.sanitizerProperties.Cfi.Src != nil
+ default:
+ return false
+ }
+}
+
+func (p *vendorSnapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
+ if !enabled {
+ return
+ }
+ switch t {
+ case cfi:
+ p.sanitizerProperties.CfiEnabled = true
+ default:
return
}
}
-func vendorSnapshotLibrary() (*Module, *vendorSnapshotLibraryDecorator) {
+func vendorSnapshotLibrary(suffix string) (*Module, *vendorSnapshotLibraryDecorator) {
module, library := NewLibrary(android.DeviceSupported)
module.stl = nil
@@ -237,77 +282,47 @@
module.linker = prebuilt
module.installer = prebuilt
+ prebuilt.init(module, suffix)
module.AddProperties(
&prebuilt.properties,
+ &prebuilt.sanitizerProperties,
)
return module, prebuilt
}
func VendorSnapshotSharedFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary()
+ module, prebuilt := vendorSnapshotLibrary(vendorSnapshotSharedSuffix)
prebuilt.libraryDecorator.BuildOnlyShared()
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- vendorSnapshotLoadHook(ctx, prebuilt)
- })
return module.Init()
}
func VendorSnapshotStaticFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary()
+ module, prebuilt := vendorSnapshotLibrary(vendorSnapshotStaticSuffix)
prebuilt.libraryDecorator.BuildOnlyStatic()
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- vendorSnapshotLoadHook(ctx, prebuilt)
- })
return module.Init()
}
func VendorSnapshotHeaderFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary()
+ module, prebuilt := vendorSnapshotLibrary(vendorSnapshotHeaderSuffix)
prebuilt.libraryDecorator.HeaderOnly()
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- vendorSnapshotLoadHook(ctx, prebuilt)
- })
return module.Init()
}
+var _ snapshotSanitizer = (*vendorSnapshotLibraryDecorator)(nil)
+
type vendorSnapshotBinaryProperties struct {
- // snapshot version.
- Version string
-
- // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
- Target_arch string
-
// Prebuilt file for each arch.
Src *string `android:"arch_variant"`
}
type vendorSnapshotBinaryDecorator struct {
+ vendorSnapshotModuleBase
*binaryDecorator
properties vendorSnapshotBinaryProperties
androidMkVendorSuffix bool
}
-func (p *vendorSnapshotBinaryDecorator) Name(name string) string {
- return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotBinaryDecorator) NameSuffix() string {
- versionSuffix := p.version()
- if p.arch() != "" {
- versionSuffix += "." + p.arch()
- }
- return vendorSnapshotBinarySuffix + versionSuffix
-}
-
-func (p *vendorSnapshotBinaryDecorator) version() string {
- return p.properties.Version
-}
-
-func (p *vendorSnapshotBinaryDecorator) arch() string {
- return p.properties.Target_arch
-}
-
func (p *vendorSnapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
if config.DeviceArch() != p.arch() {
return false
@@ -349,8 +364,8 @@
return outputFile
}
-func (p *vendorSnapshotBinaryDecorator) isSnapshotPrebuilt() bool {
- return true
+func (p *vendorSnapshotBinaryDecorator) nativeCoverage() bool {
+ return false
}
func VendorSnapshotBinaryFactory() android.Module {
@@ -372,51 +387,23 @@
module.stl = nil
module.linker = prebuilt
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- vendorSnapshotLoadHook(ctx, prebuilt)
- })
-
+ prebuilt.init(module, vendorSnapshotBinarySuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
type vendorSnapshotObjectProperties struct {
- // snapshot version.
- Version string
-
- // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
- Target_arch string
-
// Prebuilt file for each arch.
Src *string `android:"arch_variant"`
}
type vendorSnapshotObjectLinker struct {
+ vendorSnapshotModuleBase
objectLinker
properties vendorSnapshotObjectProperties
androidMkVendorSuffix bool
}
-func (p *vendorSnapshotObjectLinker) Name(name string) string {
- return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotObjectLinker) NameSuffix() string {
- versionSuffix := p.version()
- if p.arch() != "" {
- versionSuffix += "." + p.arch()
- }
- return vendorSnapshotObjectSuffix + versionSuffix
-}
-
-func (p *vendorSnapshotObjectLinker) version() string {
- return p.properties.Version
-}
-
-func (p *vendorSnapshotObjectLinker) arch() string {
- return p.properties.Target_arch
-}
-
func (p *vendorSnapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
if config.DeviceArch() != p.arch() {
return false
@@ -443,10 +430,6 @@
return false
}
-func (p *vendorSnapshotObjectLinker) isSnapshotPrebuilt() bool {
- return true
-}
-
func VendorSnapshotObjectFactory() android.Module {
module := newObject()
@@ -457,10 +440,7 @@
}
module.linker = prebuilt
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- vendorSnapshotLoadHook(ctx, prebuilt)
- })
-
+ prebuilt.init(module, vendorSnapshotObjectSuffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -568,13 +548,17 @@
if l, ok := m.linker.(snapshotLibraryInterface); ok {
// TODO(b/65377115): add full support for sanitizer
if m.sanitize != nil {
- // cfi, scs and hwasan export both sanitized and unsanitized variants for static and header
+ // scs and hwasan export both sanitized and unsanitized variants for static and header
// Always use unsanitized variants of them.
- for _, t := range []sanitizerType{cfi, scs, hwasan} {
+ for _, t := range []sanitizerType{scs, hwasan} {
if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
return false
}
}
+ // cfi also exports both variants. But for static, we capture both.
+ if !l.static() && !l.shared() && m.sanitize.isSanitizerEnabled(cfi) {
+ return false
+ }
}
if l.static() {
return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
@@ -668,6 +652,7 @@
ExportedDirs []string `json:",omitempty"`
ExportedSystemDirs []string `json:",omitempty"`
ExportedFlags []string `json:",omitempty"`
+ Sanitize string `json:",omitempty"`
SanitizeMinimalDep bool `json:",omitempty"`
SanitizeUbsanDep bool `json:",omitempty"`
@@ -717,6 +702,7 @@
var propOut string
if l, ok := m.linker.(snapshotLibraryInterface); ok {
+
// library flags
prop.ExportedFlags = l.exportedFlags()
for _, dir := range l.exportedDirs() {
@@ -749,6 +735,15 @@
if libType != "header" {
libPath := m.outputFile.Path()
stem = libPath.Base()
+ if l.static() && m.sanitize != nil && m.sanitize.isSanitizerEnabled(cfi) {
+ // both cfi and non-cfi variant for static libraries can exist.
+ // attach .cfi to distinguish between cfi and non-cfi.
+ // e.g. libbase.a -> libbase.cfi.a
+ ext := filepath.Ext(stem)
+ stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
+ prop.Sanitize = "cfi"
+ prop.ModuleName += ".cfi"
+ }
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
} else {
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index b559bac..7b8352b 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -26,6 +26,7 @@
srcs: [
"main.go",
"writedocs.go",
+ "bazel_overlay.go",
],
primaryBuilder: true,
}
diff --git a/cmd/soong_build/bazel_overlay.go b/cmd/soong_build/bazel_overlay.go
new file mode 100644
index 0000000..e37c163
--- /dev/null
+++ b/cmd/soong_build/bazel_overlay.go
@@ -0,0 +1,173 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "android/soong/android"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+const (
+ soongModuleLoad = `package(default_visibility = ["//visibility:public"])
+load("//:soong_module.bzl", "soong_module")
+`
+
+ // A BUILD file target snippet representing a Soong module
+ soongModuleTarget = `soong_module(
+ name = "%s",
+ module_name = "%s",
+ module_type = "%s",
+ module_variant = "%s",
+ deps = [
+ %s
+ ],
+)
+`
+
+ // The soong_module rule implementation in a .bzl file
+ soongModuleBzl = `
+SoongModuleInfo = provider(
+ fields = {
+ "name": "Name of module",
+ "type": "Type of module",
+ "variant": "Variant of module",
+ },
+)
+
+def _soong_module_impl(ctx):
+ return [
+ SoongModuleInfo(
+ name = ctx.attr.module_name,
+ type = ctx.attr.module_type,
+ variant = ctx.attr.module_variant,
+ ),
+ ]
+
+soong_module = rule(
+ implementation = _soong_module_impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_type": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "deps": attr.label_list(providers = [SoongModuleInfo]),
+ },
+)
+`
+)
+
+func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
+ name := ""
+ if c.ModuleSubDir(logicModule) != "" {
+ name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
+ } else {
+ name = c.ModuleName(logicModule)
+ }
+
+ return strings.Replace(name, "//", "", 1)
+}
+
+func qualifiedTargetLabel(c *blueprint.Context, logicModule blueprint.Module) string {
+ return "//" +
+ packagePath(c, logicModule) +
+ ":" +
+ targetNameWithVariant(c, logicModule)
+}
+
+func packagePath(c *blueprint.Context, logicModule blueprint.Module) string {
+ return filepath.Dir(c.BlueprintFile(logicModule))
+}
+
+func createBazelOverlay(ctx *android.Context, bazelOverlayDir string) error {
+ blueprintCtx := ctx.Context
+ blueprintCtx.VisitAllModules(func(module blueprint.Module) {
+ buildFile, err := buildFileForModule(blueprintCtx, module)
+ if err != nil {
+ panic(err)
+ }
+
+ // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
+ // items, if the modules are added using different DependencyTag. Figure
+ // out the implications of that.
+ depLabels := map[string]bool{}
+ blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
+ depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
+ })
+
+ var depLabelList string
+ for depLabel, _ := range depLabels {
+ depLabelList += "\"" + depLabel + "\",\n "
+ }
+ buildFile.Write([]byte(
+ fmt.Sprintf(
+ soongModuleTarget,
+ targetNameWithVariant(blueprintCtx, module),
+ blueprintCtx.ModuleName(module),
+ blueprintCtx.ModuleType(module),
+ // misleading name, this actually returns the variant.
+ blueprintCtx.ModuleSubDir(module),
+ depLabelList)))
+ buildFile.Close()
+ })
+
+ if err := writeReadOnlyFile(bazelOverlayDir, "WORKSPACE", ""); err != nil {
+ return err
+ }
+
+ if err := writeReadOnlyFile(bazelOverlayDir, "BUILD", ""); err != nil {
+ return err
+ }
+
+ return writeReadOnlyFile(bazelOverlayDir, "soong_module.bzl", soongModuleBzl)
+}
+
+func buildFileForModule(ctx *blueprint.Context, module blueprint.Module) (*os.File, error) {
+ // Create nested directories for the BUILD file
+ dirPath := filepath.Join(bazelOverlayDir, packagePath(ctx, module))
+ if _, err := os.Stat(dirPath); os.IsNotExist(err) {
+ os.MkdirAll(dirPath, os.ModePerm)
+ }
+ // Open the file for appending, and create it if it doesn't exist
+ f, err := os.OpenFile(
+ filepath.Join(dirPath, "BUILD.bazel"),
+ os.O_APPEND|os.O_CREATE|os.O_WRONLY,
+ 0644)
+ if err != nil {
+ return nil, err
+ }
+
+ // If the file is empty, add the load statement for the `soong_module` rule
+ fi, err := f.Stat()
+ if err != nil {
+ return nil, err
+ }
+ if fi.Size() == 0 {
+ f.Write([]byte(soongModuleLoad + "\n"))
+ }
+
+ return f, nil
+}
+
+// The overlay directory should be read-only, sufficient for bazel query.
+func writeReadOnlyFile(dir string, baseName string, content string) error {
+ workspaceFile := filepath.Join(bazelOverlayDir, baseName)
+ // 0444 is read-only
+ return ioutil.WriteFile(workspaceFile, []byte(content), 0444)
+}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 532d9e4..01a39a2 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -26,11 +26,13 @@
)
var (
- docFile string
+ docFile string
+ bazelOverlayDir string
)
func init() {
flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
+ flag.StringVar(&bazelOverlayDir, "bazel_overlay_dir", "", "path to the bazel overlay directory")
}
func newNameResolver(config android.Config) *android.NameResolver {
@@ -65,7 +67,7 @@
os.Exit(1)
}
- if docFile != "" {
+ if !shouldPrepareBuildActions() {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
@@ -85,6 +87,13 @@
bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
+ if bazelOverlayDir != "" {
+ if err := createBazelOverlay(ctx, bazelOverlayDir); err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err)
+ os.Exit(1)
+ }
+ }
+
if docFile != "" {
if err := writeDocs(ctx, docFile); err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
@@ -94,7 +103,7 @@
// TODO(ccross): make this a command line argument. Requires plumbing through blueprint
// to affect the command line of the primary builder.
- if docFile == "" {
+ if shouldPrepareBuildActions() {
metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb")
err = android.WriteMetrics(configuration, metricsFile)
if err != nil {
@@ -103,3 +112,9 @@
}
}
}
+
+func shouldPrepareBuildActions() bool {
+ // If we're writing soong_docs or bazel_overlay, don't write build.ninja or
+ // collect metrics.
+ return docFile == "" && bazelOverlayDir == ""
+}
diff --git a/rust/project_json.go b/rust/project_json.go
index 41dd194..8310479 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -75,17 +75,16 @@
knownCrates map[string]crateInfo, module android.Module,
crate *rustProjectCrate, deps map[string]int) {
- //TODO(tweek): The stdlib dependencies do not appear here. We need to manually add them.
ctx.VisitDirectDeps(module, func(child android.Module) {
- childId, childName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
+ childId, childCrateName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child)
if !ok {
return
}
- if _, ok = deps[childName]; ok {
+ if _, ok = deps[ctx.ModuleName(child)]; ok {
return
}
- crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childName})
- deps[childName] = childId
+ crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childCrateName})
+ deps[ctx.ModuleName(child)] = childId
})
}
@@ -106,8 +105,9 @@
if !ok {
return 0, "", false
}
+ moduleName := ctx.ModuleName(module)
crateName := rModule.CrateName()
- if cInfo, ok := knownCrates[crateName]; ok {
+ if cInfo, ok := knownCrates[moduleName]; ok {
// We have seen this crate already; merge any new dependencies.
crate := project.Crates[cInfo.ID]
mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps)
@@ -115,15 +115,18 @@
return cInfo.ID, crateName, true
}
crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)}
- src := rustLib.baseCompiler.Properties.Srcs[0]
- crate.RootModule = path.Join(ctx.ModuleDir(rModule), src)
+ srcs := rustLib.baseCompiler.Properties.Srcs
+ if len(srcs) == 0 {
+ return 0, "", false
+ }
+ crate.RootModule = path.Join(ctx.ModuleDir(rModule), srcs[0])
crate.Edition = rustLib.baseCompiler.edition()
deps := make(map[string]int)
mergeDependencies(ctx, project, knownCrates, module, &crate, deps)
id := len(project.Crates)
- knownCrates[crateName] = crateInfo{ID: id, Deps: deps}
+ knownCrates[moduleName] = crateInfo{ID: id, Deps: deps}
project.Crates = append(project.Crates, crate)
// rust-analyzer requires that all crates belong to at least one root:
// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 6786e72..8521940 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -15,6 +15,7 @@
package rust
import (
+ "encoding/json"
"io/ioutil"
"path/filepath"
"testing"
@@ -23,20 +24,12 @@
"android/soong/cc"
)
-func TestProjectJson(t *testing.T) {
- bp := `rust_library {
- name: "liba",
- srcs: ["src/lib.rs"],
- crate_name: "a"
- }` + GatherRequiredDepsForTest()
- env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
- fs := map[string][]byte{
- "foo.rs": nil,
- "src/lib.rs": nil,
- }
-
+// testProjectJson run the generation of rust-project.json. It returns the raw
+// content of the generated file.
+func testProjectJson(t *testing.T, bp string, fs map[string][]byte) []byte {
cc.GatherRequiredFilesForTest(fs)
+ env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
config := android.TestArchConfig(buildDir, env, bp, fs)
ctx := CreateTestContext()
ctx.Register(config)
@@ -48,8 +41,131 @@
// The JSON file is generated via WriteFileToOutputDir. Therefore, it
// won't appear in the Output of the TestingSingleton. Manually verify
// it exists.
- _, err := ioutil.ReadFile(filepath.Join(buildDir, "rust-project.json"))
+ content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
if err != nil {
t.Errorf("rust-project.json has not been generated")
}
+ return content
+}
+
+// validateJsonCrates validates that content follows the basic structure of
+// rust-project.json. It returns the crates attribute if the validation
+// succeeded.
+// It uses an empty interface instead of relying on a defined structure to
+// avoid a strong dependency on our implementation.
+func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} {
+ var content interface{}
+ err := json.Unmarshal(rawContent, &content)
+ if err != nil {
+ t.Errorf("Unable to parse the rust-project.json as JSON: %v", err)
+ }
+ root, ok := content.(map[string]interface{})
+ if !ok {
+ t.Errorf("Unexpected JSON format: %v", content)
+ }
+ if _, ok = root["crates"]; !ok {
+ t.Errorf("No crates attribute in rust-project.json: %v", root)
+ }
+ crates, ok := root["crates"].([]interface{})
+ if !ok {
+ t.Errorf("Unexpected crates format: %v", root["crates"])
+ }
+ return crates
+}
+
+func TestProjectJsonDep(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba",
+ srcs: ["a/src/lib.rs"],
+ crate_name: "a"
+ }
+ rust_library {
+ name: "libb",
+ srcs: ["b/src/lib.rs"],
+ crate_name: "b",
+ rlibs: ["liba"],
+ }
+ ` + GatherRequiredDepsForTest()
+ fs := map[string][]byte{
+ "a/src/lib.rs": nil,
+ "b/src/lib.rs": nil,
+ }
+ jsonContent := testProjectJson(t, bp, fs)
+ validateJsonCrates(t, jsonContent)
+}
+
+func TestProjectJsonBindGen(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba",
+ srcs: ["src/lib.rs"],
+ rlibs: ["libbindings"],
+ crate_name: "a"
+ }
+ rust_bindgen {
+ name: "libbindings",
+ crate_name: "bindings",
+ source_stem: "bindings",
+ host_supported: true,
+ wrapper_src: "src/any.h",
+ }
+ ` + GatherRequiredDepsForTest()
+ fs := map[string][]byte{
+ "src/lib.rs": nil,
+ }
+ jsonContent := testProjectJson(t, bp, fs)
+ validateJsonCrates(t, jsonContent)
+}
+
+func TestProjectJsonMultiVersion(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba1",
+ srcs: ["a1/src/lib.rs"],
+ crate_name: "a"
+ }
+ rust_library {
+ name: "liba2",
+ srcs: ["a2/src/lib.rs"],
+ crate_name: "a",
+ }
+ rust_library {
+ name: "libb",
+ srcs: ["b/src/lib.rs"],
+ crate_name: "b",
+ rustlibs: ["liba1", "liba2"],
+ }
+ ` + GatherRequiredDepsForTest()
+ fs := map[string][]byte{
+ "a1/src/lib.rs": nil,
+ "a2/src/lib.rs": nil,
+ "b/src/lib.rs": nil,
+ }
+ jsonContent := testProjectJson(t, bp, fs)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, crate := range crates {
+ c := crate.(map[string]interface{})
+ if c["root_module"] == "b/src/lib.rs" {
+ deps, ok := c["deps"].([]interface{})
+ if !ok {
+ t.Errorf("Unexpected format for deps: %v", c["deps"])
+ }
+ aCount := 0
+ for _, dep := range deps {
+ d, ok := dep.(map[string]interface{})
+ if !ok {
+ t.Errorf("Unexpected format for dep: %v", dep)
+ }
+ if d["name"] == "a" {
+ aCount++
+ }
+ }
+ if aCount != 2 {
+ t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
+ }
+ return
+ }
+ }
+ t.Errorf("libb crate has not been found: %v", crates)
}
diff --git a/rust/rust.go b/rust/rust.go
index edfa5d8..113b0b2 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -807,8 +807,8 @@
directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
}
- //Append the dependencies exportedDirs
- if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok {
+ //Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
+ if lib, ok := rustDep.compiler.(exportedFlagsProducer); ok && depTag != procMacroDepTag {
depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedLinkDirs()...)
depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
}
diff --git a/rust/testing.go b/rust/testing.go
index 83b2828..925b02c 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -44,7 +44,28 @@
},
host_supported: true,
}
-
+ rust_prebuilt_library {
+ name: "libstd_x86_64-apple-darwin",
+ crate_name: "std",
+ rlib: {
+ srcs: ["libstd.rlib"],
+ },
+ dylib: {
+ srcs: ["libstd.so"],
+ },
+ host_supported: true,
+ }
+ rust_prebuilt_library {
+ name: "libtest_x86_64-apple-darwin",
+ crate_name: "test",
+ rlib: {
+ srcs: ["libtest.rlib"],
+ },
+ dylib: {
+ srcs: ["libtest.so"],
+ },
+ host_supported: true,
+ }
//////////////////////////////
// Device module requirements
diff --git a/third_party/zip/android.go b/third_party/zip/android.go
index 8d387cc..f8e45c5 100644
--- a/third_party/zip/android.go
+++ b/third_party/zip/android.go
@@ -43,6 +43,15 @@
offset: uint64(w.cw.count),
}
w.dir = append(w.dir, h)
+ if !fh.isZip64() {
+ // Some writers will generate 64 bit sizes and set 32 bit fields to
+ // uint32max even if the actual size fits in 32 bit. So we should
+ // make sure CompressedSize contains the correct value in such
+ // cases. With out the two lines below we would be writing invalid(-1)
+ // sizes in such case.
+ fh.CompressedSize = uint32(fh.CompressedSize64)
+ fh.UncompressedSize = uint32(fh.UncompressedSize64)
+ }
if err := writeHeader(w.cw, fh); err != nil {
return err