Merge "Revert "Reland "Target Java 17"""
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 87554a2..0647f53 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -143,6 +143,7 @@
"external/javassist": Bp2BuildDefaultTrueRecursively,
"external/jemalloc_new": Bp2BuildDefaultTrueRecursively,
"external/jsoncpp": Bp2BuildDefaultTrueRecursively,
+ "external/jsr305": Bp2BuildDefaultTrueRecursively,
"external/junit": Bp2BuildDefaultTrueRecursively,
"external/libaom": Bp2BuildDefaultTrueRecursively,
"external/libavc": Bp2BuildDefaultTrueRecursively,
@@ -369,7 +370,6 @@
"external/bazelbuild-kotlin-rules":/* recursive = */ true,
"external/bazel-skylib":/* recursive = */ true,
"external/guava":/* recursive = */ true,
- "external/jsr305":/* recursive = */ true,
"external/protobuf":/* recursive = */ false,
"external/python/absl-py":/* recursive = */ true,
@@ -379,8 +379,8 @@
"frameworks/base/tools/codegen":/* recursive = */ true,
"frameworks/ex/common":/* recursive = */ true,
+ // Building manually due to b/179889880: resource files cross package boundary
"packages/apps/Music":/* recursive = */ true,
- "packages/apps/QuickSearchBox":/* recursive = */ true,
"prebuilts/abi-dumps/platform":/* recursive = */ true,
"prebuilts/abi-dumps/ndk":/* recursive = */ true,
@@ -400,6 +400,9 @@
// not recursive due to conflicting workspace paths in tools/atest/bazel/rules
"tools/asuite/atest":/* recursive = */ false,
"tools/asuite/atest/bazel/reporter":/* recursive = */ true,
+
+ // TODO(b/266459895): remove this and the placeholder BUILD file after re-enabling libunwindstack
+ "external/rust/crates/rustc-demangle-capi":/* recursive = */ false,
}
Bp2buildModuleAlwaysConvertList = []string{
@@ -669,6 +672,16 @@
// kotlin srcs in java libs
"CtsPkgInstallerConstants",
"kotlinx_atomicfu",
+
+ // kotlin srcs in java binary
+ "AnalyzerKt",
+ "trebuchet-core",
+
+ // kotlin srcs in android_library
+ "renderscript_toolkit",
+
+ //kotlin srcs in android_binary
+ "MusicKotlin",
}
Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -1316,9 +1329,10 @@
// uses glob in $(locations)
"libc_musl_sysroot",
- }
- Bp2buildCcLibraryStaticOnlyList = []string{}
+ // TODO(b/266459895): depends on libunwindstack
+ "libutils_test",
+ }
MixedBuildsDisabledList = []string{
"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
@@ -1362,6 +1376,30 @@
"prebuilt_kotlin-test",
// TODO(b/217750501) exclude_files property not supported
"prebuilt_currysrc_org.eclipse",
+
+ // TODO(b/266459895): re-enable libunwindstack
+ "libunwindstack",
+ "libunwindstack_stdout_log",
+ "libunwindstack_no_dex",
+ "libunwindstack_utils",
+ "unwind_reg_info",
+ "libunwindstack_local",
+ "unwind_for_offline",
+ "unwind",
+ "unwind_info",
+ "unwind_symbols",
+ "libc_malloc_debug",
+ "libfdtrack",
+ "mediaswcodec",
+ "libcodec2_hidl@1.0",
+ "libEGL",
+ "libstagefright_bufferqueue_helper_novndk",
+ "libGLESv2",
+ "libcodec2_hidl@1.1",
+ "libmedia_codecserviceregistrant",
+ "libcodec2_hidl@1.2",
+ "libutils_test",
+ "libutilscallstack",
}
// Bazel prod-mode allowlist. Modules in this list are built by Bazel
@@ -1379,5 +1417,9 @@
StagingMixedBuildsEnabledList = []string{
"com.android.adbd",
"adbd_test",
+ "adb_crypto_test",
+ "adb_pairing_auth_test",
+ "adb_pairing_connection_test",
+ "adb_tls_connection_test",
}
)
diff --git a/android/api_domain.go b/android/api_domain.go
index bdd4e6f..587ceae 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -32,17 +32,18 @@
// TODO(b/246656800): Reconcile with android.SdkKind
const (
- PublicApi ApiSurface = iota
- SystemApi
- VendorApi
+ // API surface provided by platform and mainline modules to other mainline modules
+ ModuleLibApi ApiSurface = iota
+ PublicApi // Aka NDK
+ VendorApi // Aka LLNDK
)
func (a ApiSurface) String() string {
switch a {
+ case ModuleLibApi:
+ return "module-libapi"
case PublicApi:
return "publicapi"
- case SystemApi:
- return "systemapi"
case VendorApi:
return "vendorapi"
default:
diff --git a/android/bazel.go b/android/bazel.go
index 3731dfe..52f50c5 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -66,8 +66,8 @@
//
// This is a bool pointer to support tristates: true, false, not set.
//
- // To opt-in a module, set bazel_module: { bp2build_available: true }
- // To opt-out a module, set bazel_module: { bp2build_available: false }
+ // To opt in a module, set bazel_module: { bp2build_available: true }
+ // To opt out a module, set bazel_module: { bp2build_available: false }
// To defer the default setting for the directory, do not set the value.
Bp2build_available *bool
@@ -126,7 +126,7 @@
// one with the single member called Soong_config_variables, which itself is
// a struct containing fields for each supported feature in that namespace.
//
- // The reason for using an slice of interface{} is to support defaults
+ // The reason for using a slice of interface{} is to support defaults
// propagation of the struct pointers.
namespacedVariableProps() namespacedVariableProperties
setNamespacedVariableProps(props namespacedVariableProperties)
@@ -237,16 +237,6 @@
// Per-module denylist to always opt modules out of bp2build conversion.
moduleDoNotConvert map[string]bool
-
- // Per-module denylist of cc_library modules to only generate the static
- // variant if their shared variant isn't ready or buildable by Bazel.
- ccLibraryStaticOnly map[string]bool
-}
-
-// GenerateCcLibraryStaticOnly returns whether a cc_library module should only
-// generate a static version of itself based on the current global configuration.
-func (a Bp2BuildConversionAllowlist) GenerateCcLibraryStaticOnly(moduleName string) bool {
- return a.ccLibraryStaticOnly[moduleName]
}
// NewBp2BuildAllowlist creates a new, empty Bp2BuildConversionAllowlist
@@ -258,7 +248,6 @@
map[string]bool{},
map[string]bool{},
map[string]bool{},
- map[string]bool{},
}
}
@@ -322,18 +311,6 @@
return a
}
-// SetCcLibraryStaticOnlyList copies the entries from ccLibraryStaticOnly into the allowlist
-func (a Bp2BuildConversionAllowlist) SetCcLibraryStaticOnlyList(ccLibraryStaticOnly []string) Bp2BuildConversionAllowlist {
- if a.ccLibraryStaticOnly == nil {
- a.ccLibraryStaticOnly = map[string]bool{}
- }
- for _, m := range ccLibraryStaticOnly {
- a.ccLibraryStaticOnly[m] = true
- }
-
- return a
-}
-
// ShouldKeepExistingBuildFileForDir returns whether an existing BUILD file should be
// added to the build symlink forest based on the current global configuration.
func (a Bp2BuildConversionAllowlist) ShouldKeepExistingBuildFileForDir(dir string) bool {
@@ -365,8 +342,7 @@
SetKeepExistingBuildFile(allowlists.Bp2buildKeepExistingBuildFile).
SetModuleAlwaysConvertList(allowlists.Bp2buildModuleAlwaysConvertList).
SetModuleTypeAlwaysConvertList(allowlists.Bp2buildModuleTypeAlwaysConvertList).
- SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList).
- SetCcLibraryStaticOnlyList(allowlists.Bp2buildCcLibraryStaticOnlyList)
+ SetModuleDoNotConvertList(allowlists.Bp2buildModuleDoNotConvertList)
}).(Bp2BuildConversionAllowlist)
}
@@ -375,30 +351,16 @@
// method will also log whether this module is mixed build enabled for
// metrics reporting.
func MixedBuildsEnabled(ctx BaseModuleContext) bool {
- mixedBuildEnabled := mixedBuildPossible(ctx)
+ module := ctx.Module()
+ mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
+ ctx.Os() != Windows && // Windows toolchains are not currently supported.
+ module.Enabled() &&
+ convertedToBazel(ctx, module) &&
+ ctx.Config().BazelContext.IsModuleNameAllowed(module.Name())
ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
return mixedBuildEnabled
}
-// mixedBuildPossible returns true if a module is ready to be replaced by a
-// converted or handcrafted Bazel target.
-func mixedBuildPossible(ctx BaseModuleContext) bool {
- if !ctx.Config().IsMixedBuildsEnabled() {
- return false
- }
- if ctx.Os() == Windows {
- // Windows toolchains are not currently supported.
- return false
- }
- if !ctx.Module().Enabled() {
- return false
- }
- if !convertedToBazel(ctx, ctx.Module()) {
- return false
- }
- return ctx.Config().BazelContext.BazelAllowlisted(ctx.Module().Name())
-}
-
// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool {
b, ok := module.(Bazelable)
@@ -564,3 +526,15 @@
return "", errors.New("Main-Class is not found.")
}
+
+func AttachValidationActions(ctx ModuleContext, outputFilePath Path, validations Paths) ModuleOutPath {
+ validatedOutputFilePath := PathForModuleOut(ctx, "validated", outputFilePath.Base())
+ ctx.Build(pctx, BuildParams{
+ Rule: CpNoPreserveSymlink,
+ Description: "run validations " + outputFilePath.Base(),
+ Output: validatedOutputFilePath,
+ Input: outputFilePath,
+ Validations: validations,
+ })
+ return validatedOutputFilePath
+}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 40cc6a2..8c34c92 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -29,8 +29,10 @@
"android/soong/android/allowlists"
"android/soong/bazel/cquery"
"android/soong/shared"
+ "android/soong/starlark_fmt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/metrics"
"android/soong/bazel"
)
@@ -43,6 +45,30 @@
Description: "",
CommandDeps: []string{"${bazelBuildRunfilesTool}"},
}, "outDir")
+ allowedBazelEnvironmentVars = []string{
+ // clang-tidy
+ "ALLOW_LOCAL_TIDY_TRUE",
+ "DEFAULT_TIDY_HEADER_DIRS",
+ "TIDY_TIMEOUT",
+ "WITH_TIDY",
+ "WITH_TIDY_FLAGS",
+ "TIDY_EXTERNAL_VENDOR",
+
+ "SKIP_ABI_CHECKS",
+ "UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK",
+ "AUTO_ZERO_INITIALIZE",
+ "AUTO_PATTERN_INITIALIZE",
+ "AUTO_UNINITIALIZE",
+ "USE_CCACHE",
+ "LLVM_NEXT",
+ "ALLOW_UNKNOWN_WARNING_OPTION",
+
+ // Overrides the version in the apex_manifest.json. The version is unique for
+ // each branch (internal, aosp, mainline releases, dessert releases). This
+ // enables modules built on an older branch to be installed against a newer
+ // device for development purposes.
+ "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION",
+ }
)
func init() {
@@ -110,6 +136,10 @@
return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
}
+type invokeBazelContext interface {
+ GetEventHandler() *metrics.EventHandler
+}
+
// BazelContext is a context object useful for interacting with Bazel during
// the course of a build. Use of Bazel to evaluate part of the build graph
// is referred to as a "mixed build". (Some modules are managed by Soong,
@@ -146,13 +176,13 @@
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext. The ctx argument is optional and is only
// used for performance data collection
- InvokeBazel(config Config, ctx *Context) error
+ InvokeBazel(config Config, ctx invokeBazelContext) error
// Returns true if Bazel handling is enabled for the module with the given name.
// Note that this only implies "bazel mixed build" allowlisting. The caller
// should independently verify the module is eligible for Bazel handling
// (for example, that it is MixedBuildBuildable).
- BazelAllowlisted(moduleName string) bool
+ IsModuleNameAllowed(moduleName string) bool
// Returns the bazel output base (the root directory for all bazel intermediate outputs).
OutputBase() string
@@ -165,8 +195,8 @@
}
type bazelRunner interface {
- createBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
- issueBazelCommand(bazelCmd *exec.Cmd) (output string, errorMessage string, error error)
+ createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
+ issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
}
type bazelPaths struct {
@@ -181,11 +211,13 @@
// A context object which tracks queued requests that need to be made to Bazel,
// and their results after the requests have been made.
-type bazelContext struct {
+type mixedBuildBazelContext struct {
bazelRunner
- paths *bazelPaths
- requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
- requestMutex sync.Mutex // requests can be written in parallel
+ paths *bazelPaths
+ // cquery requests that have not yet been issued to Bazel. This list is maintained
+ // in a sorted state, and is guaranteed to have no duplicates.
+ requests []cqueryKey
+ requestMutex sync.Mutex // requests can be written in parallel
results map[cqueryKey]string // Results of cquery requests after Bazel invocations
@@ -210,7 +242,7 @@
targetBuildVariant string
}
-var _ BazelContext = &bazelContext{}
+var _ BazelContext = &mixedBuildBazelContext{}
// A bazel context to use when Bazel is disabled.
type noopBazelContext struct{}
@@ -233,35 +265,50 @@
}
func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
- result, _ := m.LabelToOutputFiles[label]
+ result, ok := m.LabelToOutputFiles[label]
+ if !ok {
+ return []string{}, fmt.Errorf("no target with label %q in LabelToOutputFiles", label)
+ }
return result, nil
}
func (m MockBazelContext) GetCcInfo(label string, _ configKey) (cquery.CcInfo, error) {
- result, _ := m.LabelToCcInfo[label]
+ result, ok := m.LabelToCcInfo[label]
+ if !ok {
+ return cquery.CcInfo{}, fmt.Errorf("no target with label %q in LabelToCcInfo", label)
+ }
return result, nil
}
func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
- result, _ := m.LabelToPythonBinary[label]
+ result, ok := m.LabelToPythonBinary[label]
+ if !ok {
+ return "", fmt.Errorf("no target with label %q in LabelToPythonBinary", label)
+ }
return result, nil
}
func (m MockBazelContext) GetApexInfo(label string, _ configKey) (cquery.ApexInfo, error) {
- result, _ := m.LabelToApexInfo[label]
+ result, ok := m.LabelToApexInfo[label]
+ if !ok {
+ return cquery.ApexInfo{}, fmt.Errorf("no target with label %q in LabelToApexInfo", label)
+ }
return result, nil
}
func (m MockBazelContext) GetCcUnstrippedInfo(label string, _ configKey) (cquery.CcUnstrippedInfo, error) {
- result, _ := m.LabelToCcBinary[label]
+ result, ok := m.LabelToCcBinary[label]
+ if !ok {
+ return cquery.CcUnstrippedInfo{}, fmt.Errorf("no target with label %q in LabelToCcBinary", label)
+ }
return result, nil
}
-func (m MockBazelContext) InvokeBazel(_ Config, _ *Context) error {
+func (m MockBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
panic("unimplemented")
}
-func (m MockBazelContext) BazelAllowlisted(_ string) bool {
+func (m MockBazelContext) IsModuleNameAllowed(_ string) bool {
return true
}
@@ -277,14 +324,36 @@
var _ BazelContext = MockBazelContext{}
-func (bazelCtx *bazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+func (bazelCtx *mixedBuildBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
key := makeCqueryKey(label, requestType, cfgKey)
bazelCtx.requestMutex.Lock()
defer bazelCtx.requestMutex.Unlock()
- bazelCtx.requests[key] = true
+
+ // Insert key into requests, maintaining the sort, and only if it's not duplicate.
+ keyString := key.String()
+ foundEqual := false
+ notLessThanKeyString := func(i int) bool {
+ s := bazelCtx.requests[i].String()
+ v := strings.Compare(s, keyString)
+ if v == 0 {
+ foundEqual = true
+ }
+ return v >= 0
+ }
+ targetIndex := sort.Search(len(bazelCtx.requests), notLessThanKeyString)
+ if foundEqual {
+ return
+ }
+
+ if targetIndex == len(bazelCtx.requests) {
+ bazelCtx.requests = append(bazelCtx.requests, key)
+ } else {
+ bazelCtx.requests = append(bazelCtx.requests[:targetIndex+1], bazelCtx.requests[targetIndex:]...)
+ bazelCtx.requests[targetIndex] = key
+ }
}
-func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+func (bazelCtx *mixedBuildBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
key := makeCqueryKey(label, cquery.GetOutputFiles, cfgKey)
if rawString, ok := bazelCtx.results[key]; ok {
bazelOutput := strings.TrimSpace(rawString)
@@ -294,7 +363,7 @@
return nil, fmt.Errorf("no bazel response found for %v", key)
}
-func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+func (bazelCtx *mixedBuildBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
key := makeCqueryKey(label, cquery.GetCcInfo, cfgKey)
if rawString, ok := bazelCtx.results[key]; ok {
bazelOutput := strings.TrimSpace(rawString)
@@ -303,7 +372,7 @@
return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key)
}
-func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+func (bazelCtx *mixedBuildBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
key := makeCqueryKey(label, cquery.GetPythonBinary, cfgKey)
if rawString, ok := bazelCtx.results[key]; ok {
bazelOutput := strings.TrimSpace(rawString)
@@ -312,7 +381,7 @@
return "", fmt.Errorf("no bazel response found for %v", key)
}
-func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
+func (bazelCtx *mixedBuildBazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey)
if rawString, ok := bazelCtx.results[key]; ok {
return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString))
@@ -320,7 +389,7 @@
return cquery.ApexInfo{}, fmt.Errorf("no bazel response found for %v", key)
}
-func (bazelCtx *bazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) {
+func (bazelCtx *mixedBuildBazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) {
key := makeCqueryKey(label, cquery.GetCcUnstrippedInfo, cfgKey)
if rawString, ok := bazelCtx.results[key]; ok {
return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString))
@@ -353,7 +422,7 @@
panic("implement me")
}
-func (n noopBazelContext) InvokeBazel(_ Config, _ *Context) error {
+func (n noopBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
panic("unimplemented")
}
@@ -361,7 +430,7 @@
return ""
}
-func (n noopBazelContext) BazelAllowlisted(_ string) bool {
+func (n noopBazelContext) IsModuleNameAllowed(_ string) bool {
return false
}
@@ -396,12 +465,6 @@
enabledModules[enabledAdHocModule] = true
}
case BazelDevMode:
- // Don't use partially-converted cc_library targets in mixed builds,
- // since mixed builds would generally rely on both static and shared
- // variants of a cc_library.
- for staticOnlyModule := range GetBp2BuildAllowList().ccLibraryStaticOnly {
- disabledModules[staticOnlyModule] = true
- }
addToStringSet(disabledModules, allowlists.MixedBuildsDisabledList)
default:
panic("Expected BazelProdMode, BazelStagingMode, or BazelDevMode")
@@ -475,10 +538,9 @@
targetProduct = c.DeviceProduct()
}
- return &bazelContext{
+ return &mixedBuildBazelContext{
bazelRunner: &builtinBazelRunner{},
paths: &paths,
- requests: make(map[cqueryKey]bool),
modulesDefaultToBazel: c.BuildMode == BazelDevMode,
bazelEnabledModules: enabledModules,
bazelDisabledModules: disabledModules,
@@ -491,7 +553,7 @@
return p.metricsDir
}
-func (context *bazelContext) BazelAllowlisted(moduleName string) bool {
+func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string) bool {
if context.bazelDisabledModules[moduleName] {
return false
}
@@ -526,7 +588,7 @@
extraFlags []string
}
-func (r *mockBazelRunner) createBazelCommand(_ *bazelPaths, _ bazel.RunName,
+func (r *mockBazelRunner) createBazelCommand(_ Config, _ *bazelPaths, _ bazel.RunName,
command bazelCommand, extraFlags ...string) *exec.Cmd {
r.commands = append(r.commands, command)
r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
@@ -538,7 +600,7 @@
return cmd
}
-func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
+func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.EventHandler) (string, string, error) {
if command, ok := r.tokens[bazelCmd]; ok {
return r.bazelCommandResults[command], "", nil
}
@@ -551,7 +613,9 @@
// Returns (stdout, stderr, error). The first and second return values are strings
// containing the stdout and stderr of the run command, and an error is returned if
// the invocation returned an error code.
-func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
+func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) {
+ eventHandler.Begin("bazel command")
+ defer eventHandler.End("bazel command")
stderr := &bytes.Buffer{}
bazelCmd.Stderr = stderr
if output, err := bazelCmd.Output(); err != nil {
@@ -563,7 +627,7 @@
}
}
-func (r *builtinBazelRunner) createBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
+func (r *builtinBazelRunner) createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand,
extraFlags ...string) *exec.Cmd {
cmdFlags := []string{
"--output_base=" + absolutePath(paths.outputBase),
@@ -607,6 +671,13 @@
// explicitly in BUILD files.
"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
}
+ for _, envvar := range allowedBazelEnvironmentVars {
+ val := config.Getenv(envvar)
+ if val == "" {
+ continue
+ }
+ extraEnv = append(extraEnv, fmt.Sprintf("%s=%s", envvar, val))
+ }
bazelCmd.Env = append(os.Environ(), extraEnv...)
return bazelCmd
@@ -618,7 +689,7 @@
}
-func (context *bazelContext) mainBzlFileContents() []byte {
+func (context *mixedBuildBazelContext) mainBzlFileContents() []byte {
// TODO(cparsons): Define configuration transitions programmatically based
// on available archs.
contents := `
@@ -687,7 +758,7 @@
return []byte(productReplacer.Replace(contents))
}
-func (context *bazelContext) mainBuildFileContents() []byte {
+func (context *mixedBuildBazelContext) mainBuildFileContents() []byte {
// TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
// architecture mapping.
formatString := `
@@ -718,14 +789,23 @@
configNodesSection := ""
labelsByConfig := map[string][]string{}
- for val := range context.requests {
+
+ for _, val := range context.requests {
labelString := fmt.Sprintf("\"@%s\"", val.label)
configString := getConfigString(val)
labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
}
+ // Configs need to be sorted to maintain determinism of the BUILD file.
+ sortedConfigs := make([]string, 0, len(labelsByConfig))
+ for val := range labelsByConfig {
+ sortedConfigs = append(sortedConfigs, val)
+ }
+ sort.Slice(sortedConfigs, func(i, j int) bool { return sortedConfigs[i] < sortedConfigs[j] })
+
allLabels := []string{}
- for configString, labels := range labelsByConfig {
+ for _, configString := range sortedConfigs {
+ labels := labelsByConfig[configString]
configTokens := strings.Split(configString, "|")
if len(configTokens) != 2 {
panic(fmt.Errorf("Unexpected config string format: %s", configString))
@@ -751,12 +831,12 @@
// Returns the file contents of the buildroot.cquery file that should be used for the cquery
// expression in order to obtain information about buildroot and its dependencies.
-// The contents of this file depend on the bazelContext's requests; requests are enumerated
+// The contents of this file depend on the mixedBuildBazelContext's requests; requests are enumerated
// and grouped by their request type. The data retrieved for each label depends on its
// request type.
-func (context *bazelContext) cqueryStarlarkFileContents() []byte {
+func (context *mixedBuildBazelContext) cqueryStarlarkFileContents() []byte {
requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
- for val := range context.requests {
+ for _, val := range context.requests {
cqueryId := getCqueryId(val)
mapEntryString := fmt.Sprintf("%q : True", cqueryId)
requestTypeToCqueryIdEntries[val.requestType] =
@@ -912,11 +992,10 @@
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext.
-func (context *bazelContext) InvokeBazel(config Config, ctx *Context) error {
- if ctx != nil {
- ctx.EventHandler.Begin("bazel")
- defer ctx.EventHandler.End("bazel")
- }
+func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("bazel")
+ defer eventHandler.End("bazel")
if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
if err := os.MkdirAll(metricsDir, 0777); err != nil {
@@ -924,26 +1003,25 @@
}
}
context.results = make(map[cqueryKey]string)
- if err := context.runCquery(ctx); err != nil {
+ if err := context.runCquery(config, ctx); err != nil {
return err
}
if err := context.runAquery(config, ctx); err != nil {
return err
}
- if err := context.generateBazelSymlinks(ctx); err != nil {
+ if err := context.generateBazelSymlinks(config, ctx); err != nil {
return err
}
// Clear requests.
- context.requests = map[cqueryKey]bool{}
+ context.requests = []cqueryKey{}
return nil
}
-func (context *bazelContext) runCquery(ctx *Context) error {
- if ctx != nil {
- ctx.EventHandler.Begin("cquery")
- defer ctx.EventHandler.End("cquery")
- }
+func (context *mixedBuildBazelContext) runCquery(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("cquery")
+ defer eventHandler.End("cquery")
soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
@@ -952,23 +1030,23 @@
return err
}
}
- if err := os.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
return err
}
- if err := os.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
return err
}
- if err := os.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
+ if err := writeFileBytesIfChanged(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
return err
}
cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
- if err := os.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
+ if err := writeFileBytesIfChanged(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
return err
}
- cqueryCommandWithFlag := context.createBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
+ cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
- cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag)
+ cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler)
if cqueryErr != nil {
return cqueryErr
}
@@ -983,7 +1061,7 @@
cqueryResults[splitLine[0]] = splitLine[1]
}
}
- for val := range context.requests {
+ for _, val := range context.requests {
if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
context.results[val] = cqueryResult
} else {
@@ -994,11 +1072,18 @@
return nil
}
-func (context *bazelContext) runAquery(config Config, ctx *Context) error {
- if ctx != nil {
- ctx.EventHandler.Begin("aquery")
- defer ctx.EventHandler.End("aquery")
+func writeFileBytesIfChanged(path string, contents []byte, perm os.FileMode) error {
+ oldContents, err := os.ReadFile(path)
+ if err != nil || !bytes.Equal(contents, oldContents) {
+ err = os.WriteFile(path, contents, perm)
}
+ return nil
+}
+
+func (context *mixedBuildBazelContext) runAquery(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("aquery")
+ defer eventHandler.End("aquery")
// Issue an aquery command to retrieve action information about the bazel build tree.
//
// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
@@ -1023,36 +1108,35 @@
extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
}
}
- aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
- extraFlags...))
+ aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
+ extraFlags...), eventHandler)
if err != nil {
return err
}
- context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+ context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput), eventHandler)
return err
}
-func (context *bazelContext) generateBazelSymlinks(ctx *Context) error {
- if ctx != nil {
- ctx.EventHandler.Begin("symlinks")
- defer ctx.EventHandler.End("symlinks")
- }
+func (context *mixedBuildBazelContext) generateBazelSymlinks(config Config, ctx invokeBazelContext) error {
+ eventHandler := ctx.GetEventHandler()
+ eventHandler.Begin("symlinks")
+ defer eventHandler.End("symlinks")
// Issue a build command of the phony root to generate symlink forests for dependencies of the
// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
// but some of symlinks may be required to resolve source dependencies of the build.
- _, _, err := context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd))
+ _, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd), eventHandler)
return err
}
-func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+func (context *mixedBuildBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
return context.buildStatements
}
-func (context *bazelContext) AqueryDepsets() []bazel.AqueryDepset {
+func (context *mixedBuildBazelContext) AqueryDepsets() []bazel.AqueryDepset {
return context.depsets
}
-func (context *bazelContext) OutputBase() string {
+func (context *mixedBuildBazelContext) OutputBase() string {
return context.paths.outputBase
}
@@ -1126,10 +1210,11 @@
// the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
// because this would cause circular dependency. So, until we move aquery processing
// to the 'android' package, we need to handle special cases here.
- if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
+ switch buildStatement.Mnemonic {
+ case "FileWrite", "SourceSymlinkManifest":
out := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
WriteFileRuleVerbatim(ctx, out, buildStatement.FileContents)
- } else if buildStatement.Mnemonic == "SymlinkTree" {
+ case "SymlinkTree":
// build-runfiles arguments are the manifest file and the target directory
// where it creates the symlink tree according to this manifest (and then
// writes the MANIFEST file to it).
@@ -1148,7 +1233,7 @@
"outDir": outDir,
},
})
- } else {
+ default:
panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
}
}
@@ -1249,3 +1334,13 @@
func bazelDepsetName(contentHash string) string {
return fmt.Sprintf("bazel_depset_%s", contentHash)
}
+
+func EnvironmentVarsFile(config Config) string {
+ return fmt.Sprintf(bazel.GeneratedBazelFileWarning+`
+_env = %s
+
+env = _env
+`,
+ starlark_fmt.PrintStringList(allowedBazelEnvironmentVars, 0),
+ )
+}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 10bbf31..4a4ecb5 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -11,11 +11,18 @@
"android/soong/bazel/cquery"
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
+ "github.com/google/blueprint/metrics"
"google.golang.org/protobuf/proto"
)
var testConfig = TestConfig("out", nil, "", nil)
+type testInvokeBazelContext struct{}
+
+func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
+ return &metrics.EventHandler{}
+}
+
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
label := "@//foo:bar"
cfg := configKey{"arm64_armv8-a", Android}
@@ -23,7 +30,7 @@
bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
})
bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
- err := bazelContext.InvokeBazel(testConfig, nil)
+ err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
@@ -37,7 +44,7 @@
func TestInvokeBazelWritesBazelFiles(t *testing.T) {
bazelContext, baseDir := testBazelContext(t, map[bazelCommand]string{})
- err := bazelContext.InvokeBazel(testConfig, nil)
+ err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
@@ -118,7 +125,7 @@
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)})
- err = bazelContext.InvokeBazel(testConfig, nil)
+ err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
if err != nil {
t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
}
@@ -168,10 +175,36 @@
}
}
+func TestBazelRequestsSorted(t *testing.T) {
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+ bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Linux})
+ bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
+ bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, configKey{"otherarch", Android})
+ bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, configKey{"otherarch", Android})
+
+ if len(bazelContext.requests) != 7 {
+ t.Error("Expected 7 request elements, but got", len(bazelContext.requests))
+ }
+
+ lastString := ""
+ for _, val := range bazelContext.requests {
+ thisString := val.String()
+ if thisString <= lastString {
+ t.Errorf("Requests are not ordered correctly. '%s' came before '%s'", lastString, thisString)
+ }
+ lastString = thisString
+ }
+}
+
func verifyExtraFlags(t *testing.T, config Config, expected string) string {
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
- err := bazelContext.InvokeBazel(config, nil)
+ err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
if err != nil {
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
@@ -189,7 +222,7 @@
return actual
}
-func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
+func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) {
t.Helper()
p := bazelPaths{
soongOutDir: t.TempDir(),
@@ -201,10 +234,9 @@
bazelCommandResults[aqueryCommand] = ""
}
runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
- return &bazelContext{
+ return &mixedBuildBazelContext{
bazelRunner: runner,
paths: &p,
- requests: map[cqueryKey]bool{},
}, p.soongOutDir
}
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index e151521..bad7baf 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -461,11 +461,6 @@
return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
}
-// ModuleFromBazelLabel reverses the logic in bp2buildModuleLabel
-func ModuleFromBazelLabel(label string) string {
- return strings.Split(label, ":")[1]
-}
-
// BazelOutPath is a Bazel output path compatible to be used for mixed builds within Soong/Ninja.
type BazelOutPath struct {
OutputPath
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 7b38b6a..87b2c8f 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -417,11 +417,6 @@
t.Errorf("bp2build module do not convert of %s: expected: true, got: %v", k, allowlist.moduleDoNotConvert[k])
}
}
- for _, k := range allowlists.Bp2buildCcLibraryStaticOnlyList {
- if !allowlist.ccLibraryStaticOnly[k] {
- t.Errorf("bp2build cc library static only of %s: expected: true, got: %v", k, allowlist.ccLibraryStaticOnly[k])
- }
- }
}
func TestShouldKeepExistingBuildFileForDir(t *testing.T) {
diff --git a/android/config.go b/android/config.go
index d5ed883..c0f84c8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -397,11 +397,13 @@
arch_variant_product_var_constraints = _arch_variant_product_var_constraints
`,
}
- err = os.WriteFile(filepath.Join(dir, "product_variables.bzl"), []byte(strings.Join(bzl, "\n")), 0644)
+ err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variables.bzl"),
+ []byte(strings.Join(bzl, "\n")), 0644)
if err != nil {
return fmt.Errorf("Could not write .bzl config file %s", err)
}
- err = os.WriteFile(filepath.Join(dir, "BUILD"), []byte(bazel.GeneratedBazelFileWarning), 0644)
+ err = pathtools.WriteFileIfChanged(filepath.Join(dir, "BUILD"),
+ []byte(bazel.GeneratedBazelFileWarning), 0644)
if err != nil {
return fmt.Errorf("Could not write BUILD config file %s", err)
}
@@ -521,27 +523,33 @@
config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
}
- if cmdArgs.SymlinkForestMarker != "" {
- config.BuildMode = SymlinkForest
- } else if cmdArgs.Bp2buildMarker != "" {
- config.BuildMode = Bp2build
- } else if cmdArgs.BazelQueryViewDir != "" {
- config.BuildMode = GenerateQueryView
- } else if cmdArgs.BazelApiBp2buildDir != "" {
- config.BuildMode = ApiBp2build
- } else if cmdArgs.ModuleGraphFile != "" {
- config.BuildMode = GenerateModuleGraph
- } else if cmdArgs.DocFile != "" {
- config.BuildMode = GenerateDocFile
- } else if cmdArgs.BazelModeDev {
- config.BuildMode = BazelDevMode
- } else if cmdArgs.BazelMode {
- config.BuildMode = BazelProdMode
- } else if cmdArgs.BazelModeStaging {
- config.BuildMode = BazelStagingMode
- } else {
- config.BuildMode = AnalysisNoBazel
+ setBuildMode := func(arg string, mode SoongBuildMode) {
+ if arg != "" {
+ if config.BuildMode != AnalysisNoBazel {
+ fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg)
+ os.Exit(1)
+ }
+ config.BuildMode = mode
+ }
}
+ setBazelMode := func(arg bool, argName string, mode SoongBuildMode) {
+ if arg {
+ if config.BuildMode != AnalysisNoBazel {
+ fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", argName)
+ os.Exit(1)
+ }
+ config.BuildMode = mode
+ }
+ }
+ setBuildMode(cmdArgs.SymlinkForestMarker, SymlinkForest)
+ setBuildMode(cmdArgs.Bp2buildMarker, Bp2build)
+ setBuildMode(cmdArgs.BazelQueryViewDir, GenerateQueryView)
+ setBuildMode(cmdArgs.BazelApiBp2buildDir, ApiBp2build)
+ setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
+ setBuildMode(cmdArgs.DocFile, GenerateDocFile)
+ setBazelMode(cmdArgs.BazelModeDev, "--bazel-mode-dev", BazelDevMode)
+ setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
+ setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
config.BazelContext, err = NewBazelContext(config)
config.Bp2buildPackageConfig = GetBp2BuildAllowList()
@@ -583,32 +591,28 @@
c.mockBpList = blueprint.MockModuleListFile
}
+// TODO(b/265062549): Add a field to our collected (and uploaded) metrics which
+// describes a reason that we fell back to non-mixed builds.
// Returns true if "Bazel builds" is enabled. In this mode, part of build
// analysis is handled by Bazel.
func (c *config) IsMixedBuildsEnabled() bool {
globalMixedBuildsSupport := c.Once(OnceKey{"globalMixedBuildsSupport"}, func() interface{} {
if c.productVariables.DeviceArch != nil && *c.productVariables.DeviceArch == "riscv64" {
- fmt.Fprintln(os.Stderr, "unsupported device arch 'riscv64' for Bazel: falling back to non-mixed build")
return false
}
if c.IsEnvTrue("GLOBAL_THINLTO") {
- fmt.Fprintln(os.Stderr, "unsupported env var GLOBAL_THINLTO for Bazel: falling back to non-mixed build")
return false
}
if len(c.productVariables.SanitizeHost) > 0 {
- fmt.Fprintln(os.Stderr, "unsupported product var SanitizeHost for Bazel: falling back to non-mixed build")
return false
}
if len(c.productVariables.SanitizeDevice) > 0 {
- fmt.Fprintln(os.Stderr, "unsupported product var SanitizeDevice for Bazel: falling back to non-mixed build")
return false
}
if len(c.productVariables.SanitizeDeviceDiag) > 0 {
- fmt.Fprintln(os.Stderr, "unsupported product var SanitizeDeviceDiag for Bazel: falling back to non-mixed build")
return false
}
if len(c.productVariables.SanitizeDeviceArch) > 0 {
- fmt.Fprintln(os.Stderr, "unsupported product var SanitizeDeviceArch for Bazel: falling back to non-mixed build")
return false
}
return true
@@ -1498,6 +1502,10 @@
return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
}
+func (c *config) ApexTrimEnabled() bool {
+ return Bool(c.productVariables.TrimmedApex)
+}
+
func (c *config) EnforceSystemCertificate() bool {
return Bool(c.productVariables.EnforceSystemCertificate)
}
diff --git a/android/defaults.go b/android/defaults.go
index 7906e94..925eafc 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -55,7 +55,7 @@
d.hook = hook
}
-func (d *DefaultableModuleBase) callHookIfAvailable(ctx DefaultableHookContext) {
+func (d *DefaultableModuleBase) CallHookIfAvailable(ctx DefaultableHookContext) {
if d.hook != nil {
d.hook(ctx)
}
@@ -82,7 +82,7 @@
SetDefaultableHook(hook DefaultableHook)
// Call the hook if specified.
- callHookIfAvailable(context DefaultableHookContext)
+ CallHookIfAvailable(context DefaultableHookContext)
}
type DefaultableModule interface {
@@ -630,6 +630,6 @@
defaultable.applyDefaults(ctx, defaultsList)
}
- defaultable.callHookIfAvailable(ctx)
+ defaultable.CallHookIfAvailable(ctx)
}
}
diff --git a/android/defs.go b/android/defs.go
index 6e5bb05..18eed2d 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -58,6 +58,14 @@
},
"cpFlags", "extraCmds")
+ // A copy rule that doesn't preserve symlinks.
+ CpNoPreserveSymlink = pctx.AndroidStaticRule("CpNoPreserveSymlink",
+ blueprint.RuleParams{
+ Command: "rm -f $out && cp $cpFlags $in $out$extraCmds",
+ Description: "cp $out",
+ },
+ "cpFlags", "extraCmds")
+
// A copy rule that only updates the output if it changed.
CpIfChanged = pctx.AndroidStaticRule("CpIfChanged",
blueprint.RuleParams{
diff --git a/android/mutator.go b/android/mutator.go
index 4e55609..4dacb8d 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -709,24 +709,29 @@
// module and returns it as a list of keyed tags.
func ApexAvailableTags(mod Module) bazel.StringListAttribute {
attr := bazel.StringListAttribute{}
- tags := []string{}
// Transform specific attributes into tags.
if am, ok := mod.(ApexModule); ok {
// TODO(b/218841706): hidl_interface has the apex_available prop, but it's
// defined directly as a prop and not via ApexModule, so this doesn't
// pick those props up.
- // TODO(b/260694842): This does not pick up aidl_interface.backend.ndk.apex_available.
- for _, a := range am.apexModuleBase().ApexAvailable() {
- tags = append(tags, "apex_available="+a)
- }
- }
- if len(tags) > 0 {
- // This avoids creating a tags attr with an empty list if there are no tags.
- attr.Value = tags
+ attr.Value = ConvertApexAvailableToTags(am.apexModuleBase().ApexAvailable())
}
return attr
}
+func ConvertApexAvailableToTags(apexAvailable []string) []string {
+ if len(apexAvailable) == 0 {
+ // We need nil specifically to make bp2build not add the tags property at all,
+ // instead of adding it with an empty list
+ return nil
+ }
+ result := make([]string, 0, len(apexAvailable))
+ for _, a := range apexAvailable {
+ result = append(result, "apex_available="+a)
+ }
+ return result
+}
+
func (t *topDownMutatorContext) createBazelTargetModule(
bazelProps bazel.BazelTargetModuleProperties,
commonAttrs CommonAttributes,
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 21eebd2..dbdfa33 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "reflect"
"strings"
"testing"
@@ -267,3 +268,22 @@
FixtureWithRootAndroidBp(`test {name: "foo"}`),
).RunTest(t)
}
+
+func TestConvertApexAvailableToTags(t *testing.T) {
+ input := []string{
+ "com.android.adbd",
+ "//apex_available:platform",
+ }
+ actual := ConvertApexAvailableToTags(input)
+ expected := []string{
+ "apex_available=com.android.adbd",
+ "apex_available=//apex_available:platform",
+ }
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("Expected: %v, actual: %v", expected, actual)
+ }
+
+ if ConvertApexAvailableToTags(nil) != nil {
+ t.Errorf("Expected providing nil to return nil")
+ }
+}
diff --git a/android/paths.go b/android/paths.go
index 0fc39df..6c3009f 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -1868,10 +1867,14 @@
return ret
}
-// validateSafePath validates a path that we trust (may contain ninja variables).
-// Ensures that each path component does not attempt to leave its component.
-func validateSafePath(pathComponents ...string) (string, error) {
+// validatePathInternal ensures that a path does not leave its component, and
+// optionally doesn't contain Ninja variables.
+func validatePathInternal(allowNinjaVariables bool, pathComponents ...string) (string, error) {
for _, path := range pathComponents {
+ if !allowNinjaVariables && strings.Contains(path, "$") {
+ return "", fmt.Errorf("Path contains invalid character($): %s", path)
+ }
+
path := filepath.Clean(path)
if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
return "", fmt.Errorf("Path is outside directory: %s", path)
@@ -1883,16 +1886,18 @@
return filepath.Join(pathComponents...), nil
}
+// validateSafePath validates a path that we trust (may contain ninja
+// variables). Ensures that each path component does not attempt to leave its
+// component. Returns a joined version of each path component.
+func validateSafePath(pathComponents ...string) (string, error) {
+ return validatePathInternal(true, pathComponents...)
+}
+
// validatePath validates that a path does not include ninja variables, and that
// each path component does not attempt to leave its component. Returns a joined
// version of each path component.
func validatePath(pathComponents ...string) (string, error) {
- for _, path := range pathComponents {
- if strings.Contains(path, "$") {
- return "", fmt.Errorf("Path contains invalid character($): %s", path)
- }
- }
- return validateSafePath(pathComponents...)
+ return validatePathInternal(false, pathComponents...)
}
func PathForPhony(ctx PathContext, phony string) WritablePath {
@@ -2093,13 +2098,16 @@
// Writes a file to the output directory. Attempting to write directly to the output directory
// will fail due to the sandbox of the soong_build process.
+// Only writes the file if the file doesn't exist or if it has different contents, to prevent
+// updating the timestamp if no changes would be made. (This is better for incremental
+// performance.)
func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
absPath := absolutePath(path.String())
err := os.MkdirAll(filepath.Dir(absPath), 0777)
if err != nil {
return err
}
- return ioutil.WriteFile(absPath, data, perm)
+ return pathtools.WriteFileIfChanged(absPath, data, perm)
}
func RemoveAllOutputDir(path WritablePath) error {
diff --git a/android/testing.go b/android/testing.go
index 29af71f..fc39a9c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1125,6 +1125,11 @@
config.katiEnabled = true
}
+func SetTrimmedApexEnabledForTests(config Config) {
+ config.productVariables.TrimmedApex = new(bool)
+ *config.productVariables.TrimmedApex = true
+}
+
func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
t.Helper()
var p AndroidMkEntriesProvider
@@ -1145,7 +1150,7 @@
var p AndroidMkDataProvider
var ok bool
if p, ok = mod.(AndroidMkDataProvider); !ok {
- t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
+ t.Fatalf("module does not implement AndroidMkDataProvider: " + mod.Name())
}
data := p.AndroidMk()
data.fillInData(ctx, mod)
diff --git a/android/util.go b/android/util.go
index a0f7160..947af69 100644
--- a/android/util.go
+++ b/android/util.go
@@ -136,6 +136,38 @@
return IndexList(s, list) != -1
}
+func setFromList[T comparable](l []T) map[T]bool {
+ m := make(map[T]bool, len(l))
+ for _, t := range l {
+ m[t] = true
+ }
+ return m
+}
+
+// ListSetDifference checks if the two lists contain the same elements. It returns
+// a boolean which is true if there is a difference, and then returns lists of elements
+// that are in l1 but not l2, and l2 but not l1.
+func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
+ listsDiffer := false
+ diff1 := []T{}
+ diff2 := []T{}
+ m1 := setFromList(l1)
+ m2 := setFromList(l2)
+ for t := range m1 {
+ if _, ok := m2[t]; !ok {
+ diff1 = append(diff1, t)
+ listsDiffer = true
+ }
+ }
+ for t := range m2 {
+ if _, ok := m1[t]; !ok {
+ diff2 = append(diff2, t)
+ listsDiffer = true
+ }
+ }
+ return listsDiffer, diff1, diff2
+}
+
// Returns true if the given string s is prefixed with any string in the given prefix list.
func HasAnyPrefix(s string, prefixList []string) bool {
for _, prefix := range prefixList {
diff --git a/android/variable.go b/android/variable.go
index 9725895..e714fc4 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -380,6 +380,7 @@
Ndk_abis *bool `json:",omitempty"`
+ TrimmedApex *bool `json:",omitempty"`
Flatten_apex *bool `json:",omitempty"`
ForceApexSymlinkOptimization *bool `json:",omitempty"`
CompressedApex *bool `json:",omitempty"`
@@ -502,6 +503,7 @@
Malloc_zero_contents: boolPtr(true),
Malloc_pattern_fill_contents: boolPtr(false),
Safestack: boolPtr(false),
+ TrimmedApex: boolPtr(false),
BootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
@@ -702,124 +704,9 @@
}
}
- productConfigProperties.zeroValuesForNamespacedVariables()
-
return productConfigProperties
}
-// zeroValuesForNamespacedVariables ensures that selects that contain __only__
-// conditions default values have zero values set for the other non-default
-// values for that select statement.
-//
-// If the ProductConfigProperties map contains these items, as parsed from the .bp file:
-//
-// library_linking_strategy: {
-// prefer_static: {
-// static_libs: [
-// "lib_a",
-// "lib_b",
-// ],
-// },
-// conditions_default: {
-// shared_libs: [
-// "lib_a",
-// "lib_b",
-// ],
-// },
-// },
-//
-// Static_libs {Library_linking_strategy ANDROID prefer_static} [lib_a lib_b]
-// Shared_libs {Library_linking_strategy ANDROID conditions_default} [lib_a lib_b]
-//
-// We need to add this:
-//
-// Shared_libs {Library_linking_strategy ANDROID prefer_static} []
-//
-// so that the following gets generated for the "dynamic_deps" attribute,
-// instead of putting lib_a and lib_b directly into dynamic_deps without a
-// select:
-//
-// dynamic_deps = select({
-// "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
-// "//conditions:default": [
-// "//foo/bar:lib_a",
-// "//foo/bar:lib_b",
-// ],
-// }),
-func (props *ProductConfigProperties) zeroValuesForNamespacedVariables() {
- // A map of product config properties to the zero values of their respective
- // property value.
- zeroValues := make(map[ProductConfigProperty]interface{})
-
- // A map of prop names (e.g. cflags) to product config properties where the
- // (prop name, ProductConfigProperty) tuple contains a non-conditions_default key.
- //
- // e.g.
- //
- // prefer_static: {
- // static_libs: [
- // "lib_a",
- // "lib_b",
- // ],
- // },
- // conditions_default: {
- // shared_libs: [
- // "lib_a",
- // "lib_b",
- // ],
- // },
- //
- // The tuple of ("static_libs", prefer_static) would be in this map.
- hasNonDefaultValue := make(map[string]map[ProductConfigProperty]bool)
-
- // Iterate over all added soong config variables.
- for propName, v := range *props {
- for p, intf := range v {
- if p.Namespace == "" {
- // If there's no namespace, this isn't a soong config variable,
- // i.e. this is a product variable. product variables have no
- // conditions_defaults, so skip them.
- continue
- }
- if p.FullConfig == bazel.ConditionsDefaultConfigKey {
- // Skip conditions_defaults.
- continue
- }
- if hasNonDefaultValue[propName] == nil {
- hasNonDefaultValue[propName] = make(map[ProductConfigProperty]bool)
- hasNonDefaultValue[propName][p] = false
- }
- // Create the zero value of the variable.
- if _, exists := zeroValues[p]; !exists {
- zeroValue := reflect.Zero(reflect.ValueOf(intf).Type()).Interface()
- if zeroValue == nil {
- panic(fmt.Errorf("Expected non-nil zero value for product/config variable %+v\n", intf))
- }
- zeroValues[p] = zeroValue
- }
- hasNonDefaultValue[propName][p] = true
- }
- }
-
- for propName := range *props {
- for p, zeroValue := range zeroValues {
- // Ignore variables that already have a non-default value for that axis
- if exists, _ := hasNonDefaultValue[propName][p]; !exists {
- // fmt.Println(propName, p.Namespace, p.Name, p.FullConfig, zeroValue)
- // Insert the zero value for this propname + product config value.
- props.AddProductConfigProperty(
- propName,
- p.Namespace,
- p.Name,
- p.FullConfig,
- zeroValue,
- bazel.NoConfigAxis,
- )
- }
- }
- }
-}
-
func (p *ProductConfigProperties) AddProductConfigProperty(
propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) {
if (*p)[propertyName] == nil {
@@ -945,14 +832,16 @@
for j := 0; j < variableValue.NumField(); j++ {
property := variableValue.Field(j)
+ // e.g. Asflags, Cflags, Enabled, etc.
+ propertyName := variableValue.Type().Field(j).Name
+ // config can also be "conditions_default".
+ config := proptools.PropertyNameForField(propertyName)
+
// If the property wasn't set, no need to pass it along
if property.IsZero() {
continue
}
- // e.g. Asflags, Cflags, Enabled, etc.
- propertyName := variableValue.Type().Field(j).Name
-
if v, ok := maybeExtractConfigVarProp(property); ok {
// The field is a struct, which is used by:
// 1) soong_config_string_variables
@@ -972,13 +861,14 @@
// static_libs: ...
// }
field := v
+ // Iterate over fields of this struct prop.
for k := 0; k < field.NumField(); k++ {
- // Iterate over fields of this struct prop.
- if field.Field(k).IsZero() {
+ // For product variables, zero values are irrelevant; however, for soong config variables,
+ // empty values are relevant because there can also be a conditions default which is not
+ // applied for empty variables.
+ if field.Field(k).IsZero() && namespace == "" {
continue
}
- // config can also be "conditions_default".
- config := proptools.PropertyNameForField(propertyName)
actualPropertyName := field.Type().Field(k).Name
productConfigProperties.AddProductConfigProperty(
diff --git a/android/visibility.go b/android/visibility.go
index b209599..5955133 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -155,7 +155,11 @@
}
func isAncestor(p1 string, p2 string) bool {
- return strings.HasPrefix(p2+"/", p1+"/")
+ // Equivalent to strings.HasPrefix(p2+"/", p1+"/"), but without the string copies
+ // The check for a trailing slash is so that we don't consider sibling
+ // directories with common prefixes to be ancestors, e.g. "fooo/bar" should not be
+ // a descendant of "foo".
+ return strings.HasPrefix(p2, p1) && (len(p2) == len(p1) || p2[len(p1)] == '/')
}
func (r subpackagesRule) String() string {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index b76f6bd..7f03621 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -23,8 +23,7 @@
"android/soong/android"
"android/soong/cc"
"android/soong/java"
-
- "github.com/google/blueprint/proptools"
+ "android/soong/rust"
)
func (a *apexBundle) AndroidMk() android.AndroidMkData {
@@ -73,12 +72,15 @@
return fi.androidMkModuleName + "." + apexBundleName + a.suffix
}
-func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
+func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir string,
apexAndroidMkData android.AndroidMkData) []string {
- // apexBundleName comes from the 'name' property; apexName comes from 'apex_name' property.
+ // apexBundleName comes from the 'name' property or soong module.
+ // apexName comes from 'name' property of apex_manifest.
// An apex is installed to /system/apex/<apexBundleName> and is activated at /apex/<apexName>
// In many cases, the two names are the same, but could be different in general.
+ // However, symbol files for apex files are installed under /apex/<apexBundleName> to avoid
+ // conflicts between two apexes with the same apexName.
moduleNames := []string{}
apexType := a.properties.ApexType
@@ -89,25 +91,6 @@
return moduleNames
}
- // b/162366062. Prevent GKI APEXes to emit make rules to avoid conflicts.
- if strings.HasPrefix(apexName, "com.android.gki.") && apexType != flattenedApex {
- return moduleNames
- }
-
- // b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
- // APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
- // as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
- // for the overriding VNDK APEXes.
- symbolFilesNotNeeded := a.vndkApex && len(a.overridableProperties.Overrides) > 0
- if symbolFilesNotNeeded && apexType != flattenedApex {
- return moduleNames
- }
-
- // Avoid creating duplicate build rules for multi-installed APEXes.
- if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
- return moduleNames
- }
-
seenDataOutPaths := make(map[string]bool)
for _, fi := range a.filesInfo {
@@ -144,15 +127,15 @@
if fi.module != nil && fi.module.Owner() != "" {
fmt.Fprintln(w, "LOCAL_MODULE_OWNER :=", fi.module.Owner())
}
- // /apex/<apex_name>/{lib|framework|...}
- pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
+ // /apex/<apexBundleName>/{lib|framework|...}
+ pathForSymbol := filepath.Join("$(PRODUCT_OUT)", "apex", apexBundleName, fi.installDir)
var modulePath string
if apexType == flattenedApex {
- // /system/apex/<name>/{lib|framework|...}
+ // /system/apex/<apexBundleName>/{lib|framework|...}
modulePath = filepath.Join(a.installDir.String(), apexBundleName, fi.installDir)
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
- if a.primaryApexType && !symbolFilesNotNeeded {
- fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
+ if a.primaryApexType {
+ fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathForSymbol)
}
android.AndroidMkEmitAssignList(w, "LOCAL_MODULE_SYMLINKS", fi.symlinks)
newDataPaths := []android.DataPath{}
@@ -165,8 +148,8 @@
}
android.AndroidMkEmitAssignList(w, "LOCAL_TEST_DATA", android.AndroidMkDataPaths(newDataPaths))
} else {
- modulePath = pathWhenActivated
- fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
+ modulePath = pathForSymbol
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
// We don't need to have notice file for the individual modules in it. Otherwise,
@@ -256,6 +239,10 @@
if ccMod.CoverageOutputFile().Valid() {
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", ccMod.CoverageOutputFile().String())
}
+ } else if rustMod, ok := fi.module.(*rust.Module); ok {
+ if rustMod.UnstrippedOutputFile() != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", rustMod.UnstrippedOutputFile().String())
+ }
}
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk")
default:
@@ -309,7 +296,7 @@
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
}
- android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.requiredDeps, required)
+ android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
}
@@ -320,8 +307,7 @@
moduleNames := []string{}
apexType := a.properties.ApexType
if a.installable() {
- apexName := proptools.StringDefault(a.properties.Apex_name, name)
- moduleNames = a.androidMkForFiles(w, name, apexName, moduleDir, data)
+ moduleNames = a.androidMkForFiles(w, name, moduleDir, data)
}
if apexType == flattenedApex {
diff --git a/apex/apex.go b/apex/apex.go
index e99823b..ff38773 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -80,6 +80,7 @@
ctx.BottomUp("apex", apexMutator).Parallel()
ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+ ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
// Register after apex_info mutator so that it can use ApexVariationName
ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
}
@@ -93,10 +94,6 @@
// a default one is automatically generated.
AndroidManifest *string `android:"path"`
- // Canonical name of this APEX bundle. Used to determine the path to the activated APEX on
- // device (/apex/<apex_name>). If unspecified, follows the name property.
- Apex_name *string
-
// Determines the file contexts file for setting the security contexts to files in this APEX
// bundle. For platform APEXes, this should points to a file under /system/sepolicy Default:
// /system/sepolicy/apex/<module_name>_file_contexts.
@@ -123,10 +120,6 @@
// List of filesystem images that are embedded inside this APEX bundle.
Filesystems []string
- // The minimum SDK version that this APEX must support at minimum. This is usually set to
- // the SDK version that the APEX was first introduced.
- Min_sdk_version *string
-
// Whether this APEX is considered updatable or not. When set to true, this will enforce
// additional rules for making sure that the APEX is truly updatable. To be updatable,
// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -152,16 +145,6 @@
// Should be only used in non-system apexes (e.g. vendor: true). Default is false.
Use_vndk_as_stable *bool
- // Whether this is multi-installed APEX should skip installing symbol files.
- // Multi-installed APEXes share the same apex_name and are installed at the same time.
- // Default is false.
- //
- // Should be set to true for all multi-installed APEXes except the singular
- // default version within the multi-installed group.
- // Only the default version can install symbol files in $(PRODUCT_OUT}/apex,
- // or else conflicting build rules may be created.
- Multi_install_skip_symbol_files *bool
-
// The type of APEX to build. Controls what the APEX payload is. Either 'image', 'zip' or
// 'both'. When set to image, contents are stored in a filesystem image inside a zip
// container. When set to zip, contents are stored in a zip container directly. This type is
@@ -389,6 +372,13 @@
// conditions, e.g., target device needs to support APEX compression, are also fulfilled.
// Default: false.
Compressible *bool
+
+ // Trim against a specific Dynamic Common Lib APEX
+ Trim_against *string
+
+ // The minimum SDK version that this APEX must support at minimum. This is usually set to
+ // the SDK version that the APEX was first introduced.
+ Min_sdk_version *string
}
type apexBundle struct {
@@ -439,8 +429,8 @@
// GenerateAndroidBuildActions.
filesInfo []apexFile
- // List of other module names that should be installed when this APEX gets installed.
- requiredDeps []string
+ // List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
+ makeModulesToInstall []string
///////////////////////////////////////////////////////////////////////////////////////////
// Outputs (final and intermediates)
@@ -488,8 +478,6 @@
// Optional list of lint report zip files for apexes that contain java or app modules
lintReports android.Paths
- prebuiltFileToDelete string
-
isCompressed bool
// Path of API coverage generate file
@@ -675,6 +663,7 @@
androidAppTag = &dependencyTag{name: "androidApp", payload: true}
bpfTag = &dependencyTag{name: "bpf", payload: true}
certificateTag = &dependencyTag{name: "certificate"}
+ dclaTag = &dependencyTag{name: "dcla"}
executableTag = &dependencyTag{name: "executable", payload: true}
fsTag = &dependencyTag{name: "filesystem", payload: true}
bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
@@ -908,6 +897,33 @@
}
}
+func apexDCLADepsMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Config().ApexTrimEnabled() {
+ return
+ }
+ if a, ok := mctx.Module().(*apexBundle); ok && a.overridableProperties.Trim_against != nil {
+ commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+ mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(a.overridableProperties.Trim_against))
+ } else if o, ok := mctx.Module().(*OverrideApex); ok {
+ for _, p := range o.GetProperties() {
+ properties, ok := p.(*overridableProperties)
+ if !ok {
+ continue
+ }
+ if properties.Trim_against != nil {
+ commonVariation := mctx.Config().AndroidCommonTarget.Variations()
+ mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(properties.Trim_against))
+ }
+ }
+ }
+}
+
+type DCLAInfo struct {
+ ProvidedLibs []string
+}
+
+var DCLAInfoProvider = blueprint.NewMutatorProvider(DCLAInfo{}, "apex_info")
+
type ApexBundleInfo struct {
Contents *android.ApexContents
}
@@ -1017,7 +1033,7 @@
// This is the main part of this mutator. Mark the collected dependencies that they need to
// be built for this apexBundle.
- apexVariationName := proptools.StringDefault(a.properties.Apex_name, mctx.ModuleName()) // could be com.android.foo
+ apexVariationName := mctx.ModuleName() // could be com.android.foo
a.properties.ApexVariationName = apexVariationName
apexInfo := android.ApexInfo{
ApexVariationName: apexVariationName,
@@ -1035,6 +1051,12 @@
child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
return true
})
+
+ if a.dynamic_common_lib_apex() {
+ mctx.SetProvider(DCLAInfoProvider, DCLAInfo{
+ ProvidedLibs: a.properties.Native_shared_libs,
+ })
+ }
}
type ApexInfoMutator interface {
@@ -1531,6 +1553,19 @@
return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
}
+// See the list of libs to trim
+func (a *apexBundle) libs_to_trim(ctx android.ModuleContext) []string {
+ dclaModules := ctx.GetDirectDepsWithTag(dclaTag)
+ if len(dclaModules) > 1 {
+ panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
+ }
+ if len(dclaModules) > 0 {
+ DCLAInfo := ctx.OtherModuleProvider(dclaModules[0], DCLAInfoProvider).(DCLAInfo)
+ return DCLAInfo.ProvidedLibs
+ }
+ return []string{}
+}
+
// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
// members) can be sanitized, either forcibly, or by the global configuration. For some of the
// sanitizers, extra dependencies can be forcibly added as well.
@@ -1657,7 +1692,7 @@
return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
}
-func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.PythonBinaryModule) apexFile {
dirInApex := "bin"
fileToCopy := py.HostToolPath().Path()
return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
@@ -1732,6 +1767,18 @@
return af
}
+func apexFileForJavaModuleProfile(ctx android.BaseModuleContext, module javaModule) *apexFile {
+ if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+ if profilePathOnHost := dexpreopter.ProfilePathOnHost(); profilePathOnHost != nil {
+ dirInApex := "javalib"
+ af := newApexFile(ctx, profilePathOnHost, module.BaseModuleName()+"-profile", dirInApex, etc, nil)
+ af.customStem = module.Stem() + ".jar.prof"
+ return &af
+ }
+ }
+ return nil
+}
+
// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
// the same way.
type androidApp interface {
@@ -1915,6 +1962,11 @@
}
a.outputFile = a.outputApexFile
+ if len(outputs.TidyFiles) > 0 {
+ tidyFiles := android.PathsForBazelOut(ctx, outputs.TidyFiles)
+ a.outputFile = android.AttachValidationActions(ctx, a.outputFile, tidyFiles)
+ }
+
// TODO(b/257829940): These are used by the apex_keys_text singleton; would probably be a clearer
// interface if these were set in a provider rather than the module itself
a.publicKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyInfo[0])
@@ -1922,11 +1974,9 @@
a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[0])
a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyInfo[1])
- // Ensure ApexInfo.RequiresLibs are installed as part of a bundle build
- for _, bazelLabel := range outputs.RequiresLibs {
- // convert Bazel label back to Soong module name
- a.requiredDeps = append(a.requiredDeps, android.ModuleFromBazelLabel(bazelLabel))
- }
+ // Ensure ApexMkInfo.install_to_system make module names are installed as
+ // part of a bundled build.
+ a.makeModulesToInstall = append(a.makeModulesToInstall, outputs.MakeModulesToInstall...)
apexType := a.properties.ApexType
switch apexType {
@@ -1944,21 +1994,17 @@
a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
a.compatSymlinks.Paths()...)
default:
- panic(fmt.Errorf("unexpected apex_type for the ProcessBazelQuery: %v", a.properties.ApexType))
+ panic(fmt.Errorf("internal error: unexpected apex_type for the ProcessBazelQueryResponse: %v", a.properties.ApexType))
}
- /*
- TODO(asmundak): compared to building an APEX with Soong, building it with Bazel does not
- return filesInfo and requiredDeps fields (in the Soong build the latter is updated).
- Fix this, as these fields are subsequently used in apex/androidmk.go and in apex/builder/go
- To find out what Soong build puts there, run:
- vctx := visitorContext{handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case)}
- ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
- return a.depVisitor(&vctx, ctx, child, parent)
- })
- vctx.normalizeFileInfo()
- */
-
+ // filesInfo is not set in mixed mode, because all information about the
+ // apex's contents should completely come from the Starlark providers.
+ //
+ // Prevent accidental writes to filesInfo in the earlier parts Soong by
+ // asserting it to be nil.
+ if a.filesInfo != nil {
+ panic(fmt.Errorf("internal error: filesInfo must be nil for an apex handled by Bazel."))
+ }
}
func (a *apexBundle) setCompression(ctx android.ModuleContext) {
@@ -2025,7 +2071,7 @@
a.primaryApexType = true
if ctx.Config().InstallExtraFlattenedApexes() {
- a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+ a.makeModulesToInstall = append(a.makeModulesToInstall, a.Name()+flattenedSuffix)
}
}
case zipApex:
@@ -2147,7 +2193,7 @@
case *cc.Module:
vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
return true // track transitive dependencies
- case *python.Module:
+ case *python.PythonBinaryModule:
if ch.HostToolPath().Valid() {
vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
}
@@ -2177,7 +2223,7 @@
vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
- a.requiredDeps = append(a.requiredDeps, makeModuleName)
+ a.makeModulesToInstall = append(a.makeModulesToInstall, makeModuleName)
}
return true
case sscpfTag:
@@ -2295,12 +2341,6 @@
} else {
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
}
- case android.PrebuiltDepTag:
- // If the prebuilt is force disabled, remember to delete the prebuilt file
- // that might have been installed in the previous builds
- if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
- a.prebuiltFileToDelete = prebuilt.InstallFilename()
- }
}
return false
}
@@ -2340,11 +2380,14 @@
//
// Always include if we are a host-apex however since those won't have any
// system libraries.
- if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() {
+ //
+ // Skip the dependency in unbundled builds where the device image is not
+ // being built.
+ if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
// we need a module name for Make
name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
- if !android.InList(name, a.requiredDeps) {
- a.requiredDeps = append(a.requiredDeps, name)
+ if !android.InList(name, a.makeModulesToInstall) {
+ a.makeModulesToInstall = append(a.makeModulesToInstall, name)
}
}
vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
@@ -2431,6 +2474,9 @@
case *java.Library, *java.SdkLibrary:
af := apexFileForJavaModule(ctx, child.(javaModule))
vctx.filesInfo = append(vctx.filesInfo, af)
+ if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
+ vctx.filesInfo = append(vctx.filesInfo, *profileAf)
+ }
return true // track transitive dependencies
default:
ctx.PropertyErrorf("systemserverclasspath_fragments",
@@ -2479,7 +2525,6 @@
}
////////////////////////////////////////////////////////////////////////////////////////////
// 2) traverse the dependency tree to collect apexFile structs from them.
-
// Collect the module directory for IDE info in java/jdeps.go.
a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
@@ -2837,7 +2882,7 @@
// Only override the minSdkVersion value on Apexes which already specify
// a min_sdk_version (it's optional for non-updatable apexes), and that its
// min_sdk_version value is lower than the one to override with.
- minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+ minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version))
if minApiLevel.IsNone() {
return ""
}
@@ -3426,7 +3471,7 @@
Key bazel.LabelAttribute
Certificate bazel.LabelAttribute // used when the certificate prop is a module
Certificate_name bazel.StringAttribute // used when the certificate prop is a string
- Min_sdk_version *string
+ Min_sdk_version bazel.StringAttribute
Updatable bazel.BoolAttribute
Installable bazel.BoolAttribute
Binaries bazel.LabelListAttribute
@@ -3445,6 +3490,10 @@
Native_shared_libs_64 bazel.LabelListAttribute
}
+const (
+ minSdkVersionPropName = "Min_sdk_version"
+)
+
// ConvertWithBp2build performs bp2build conversion of an apex
func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
// We only convert apex and apex_test modules at this time
@@ -3483,11 +3532,19 @@
fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.File_contexts))
}
+ productVariableProps := android.ProductVariableProperties(ctx)
// TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but
// given it's coming via config, we probably don't want to put it in here.
- var minSdkVersion *string
- if a.properties.Min_sdk_version != nil {
- minSdkVersion = a.properties.Min_sdk_version
+ var minSdkVersion bazel.StringAttribute
+ if a.overridableProperties.Min_sdk_version != nil {
+ minSdkVersion.SetValue(*a.overridableProperties.Min_sdk_version)
+ }
+ if props, ok := productVariableProps[minSdkVersionPropName]; ok {
+ for c, p := range props {
+ if val, ok := p.(*string); ok {
+ minSdkVersion.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
+ }
+ }
}
var keyLabelAttribute bazel.LabelAttribute
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 6faed70..1581949 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -55,17 +55,17 @@
touch ${out};
else
echo -e "\n******************************";
- echo "ERROR: go/apex-allowed-deps-error";
+ echo "ERROR: go/apex-allowed-deps-error contains more information";
echo "******************************";
echo "Detected changes to allowed dependencies in updatable modules.";
echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
echo -e "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)\n";
echo "When submitting the generated CL, you must include the following information";
echo "in the commit message if you are adding a new dependency:";
- echo "Apex-Size-Increase:";
- echo "Previous-Platform-Support:";
- echo "Aosp-First:";
- echo "Test-Info:";
+ echo "Apex-Size-Increase: Expected binary size increase for affected APEXes (or the size of the .jar / .so file of the new library)";
+ echo "Previous-Platform-Support: Are the maintainers of the new dependency committed to supporting previous platform releases?";
+ echo "Aosp-First: Is the new dependency being developed AOSP-first or internal?";
+ echo "Test-Info: What’s the testing strategy for the new dependency? Does it have its own tests, and are you adding integration tests? How/when are the tests run?";
echo "You do not need OWNERS approval to submit the change, but mainline-modularization@";
echo "will periodically review additions and may require changes.";
echo -e "******************************\n";
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 33fce7c..eec24b0 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3329,17 +3329,14 @@
// non-APEX variant does not have __ANDROID_APEX__ defined
mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
- // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+ // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=10000")
- // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
+ // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=29")
// When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and
// each variant defines additional macros to distinguish which apex variant it is built for
@@ -3348,19 +3345,17 @@
mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- // recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+ // recovery variant does not set __ANDROID_APEX__
mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
// non-APEX variant does not have __ANDROID_APEX__ defined
mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- // recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
+ // recovery variant does not set __ANDROID_APEX__
mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
}
func TestHeaderLibsDependency(t *testing.T) {
@@ -3489,14 +3484,14 @@
return ret
}
-func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+func assertFileListEquals(t *testing.T, expectedFiles []string, actualFiles []fileInApex) {
t.Helper()
var failed bool
var surplus []string
filesMatched := make(map[string]bool)
- for _, file := range getFiles(t, ctx, moduleName, variant) {
+ for _, file := range actualFiles {
matchFound := false
- for _, expected := range files {
+ for _, expected := range expectedFiles {
if file.match(expected) {
matchFound = true
filesMatched[expected] = true
@@ -3514,9 +3509,9 @@
failed = true
}
- if len(files) > len(filesMatched) {
+ if len(expectedFiles) > len(filesMatched) {
var missing []string
- for _, expected := range files {
+ for _, expected := range expectedFiles {
if !filesMatched[expected] {
missing = append(missing, expected)
}
@@ -3530,6 +3525,32 @@
}
}
+func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) {
+ assertFileListEquals(t, files, getFiles(t, ctx, moduleName, variant))
+}
+
+func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
+ deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Rule("deapexer")
+ outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
+ if deapexer.Output != nil {
+ outputs = append(outputs, deapexer.Output.String())
+ }
+ for _, output := range deapexer.ImplicitOutputs {
+ outputs = append(outputs, output.String())
+ }
+ actualFiles := make([]fileInApex, 0, len(outputs))
+ for _, output := range outputs {
+ dir := "/deapexer/"
+ pos := strings.LastIndex(output, dir)
+ if pos == -1 {
+ t.Fatal("Unknown deapexer output ", output)
+ }
+ path := output[pos+len(dir):]
+ actualFiles = append(actualFiles, fileInApex{path: path, src: "", isLink: false})
+ }
+ assertFileListEquals(t, files, actualFiles)
+}
+
func TestVndkApexCurrent(t *testing.T) {
commonFiles := []string{
"lib/libc++.so",
@@ -3811,11 +3832,9 @@
}`+vndkLibrariesTxtFiles("28", "current"))
assertApexName := func(expected, moduleName string) {
- bundle := ctx.ModuleForTests(moduleName, "android_common_image").Module().(*apexBundle)
- actual := proptools.String(bundle.properties.Apex_name)
- if !reflect.DeepEqual(actual, expected) {
- t.Errorf("Got '%v', expected '%v'", actual, expected)
- }
+ module := ctx.ModuleForTests(moduleName, "android_common_image")
+ apexManifestRule := module.Rule("apexManifestRule")
+ ensureContains(t, apexManifestRule.Args["opt"], "-v name "+expected)
}
assertApexName("com.android.vndk.v29", "com.android.vndk.current")
@@ -4112,57 +4131,11 @@
ensureListEmpty(t, requireNativeLibs)
}
-func TestApexName(t *testing.T) {
- ctx := testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- apex_name: "com.android.myapex",
- native_shared_libs: ["mylib"],
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "mylib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- apex_available: [
- "//apex_available:platform",
- "myapex",
- ],
- }
- `)
-
- module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
- apexManifestRule := module.Rule("apexManifestRule")
- ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex")
- apexRule := module.Rule("apexRule")
- ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname")
-
- apexBundle := module.Module().(*apexBundle)
- data := android.AndroidMkDataForTest(t, ctx, apexBundle)
- name := apexBundle.BaseModuleName()
- prefix := "TARGET_"
- var builder strings.Builder
- data.Custom(&builder, name, prefix, "", data)
- androidMk := builder.String()
- ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n")
- ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n")
-}
-
func TestOverrideApexManifestDefaultVersion(t *testing.T) {
ctx := testApex(t, `
apex {
name: "myapex",
key: "myapex.key",
- apex_name: "com.android.myapex",
native_shared_libs: ["mylib"],
updatable: false,
}
@@ -4187,7 +4160,7 @@
"OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
}))
- module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image")
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
apexManifestRule := module.Rule("apexManifestRule")
ensureContains(t, apexManifestRule.Args["default_version"], "1234")
}
@@ -5721,7 +5694,7 @@
}),
)
ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- ensureListContains(t, ab.requiredDeps, "myapex.flattened")
+ ensureListContains(t, ab.makeModulesToInstall, "myapex.flattened")
mk := android.AndroidMkDataForTest(t, ctx, ab)
var builder strings.Builder
mk.Custom(&builder, ab.Name(), "TARGET_", "", mk)
@@ -9302,7 +9275,7 @@
`)
bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+ bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "foo")
data := android.AndroidMkDataForTest(t, ctx, bundle)
var builder strings.Builder
data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
@@ -9310,7 +9283,7 @@
ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := apex_manifest.pb.myapex apex_pubkey.myapex foo\n")
flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
- flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+ flattenedBundle.makeModulesToInstall = append(flattenedBundle.makeModulesToInstall, "foo")
flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
var flattenedBuilder strings.Builder
flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
@@ -9554,7 +9527,7 @@
func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
- android.AssertStringListContains(t, "", a.requiredDeps, dep)
+ android.AssertStringListContains(t, "", a.makeModulesToInstall, dep)
}
}
@@ -9562,7 +9535,7 @@
func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
- android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
+ android.AssertStringListDoesNotContain(t, "", a.makeModulesToInstall, dep)
}
}
@@ -9867,3 +9840,70 @@
libcCoreVariant := result.ModuleForTests("libc.apiimport", "android_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "core variant should link against source libc", true, hasDep(libfooCoreVariant, libcCoreVariant))
}
+
+func TestTrimmedApex(t *testing.T) {
+ bp := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo","libbaz"],
+ min_sdk_version: "29",
+ trim_against: "mydcla",
+ }
+ apex {
+ name: "mydcla",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo","libbar"],
+ min_sdk_version: "29",
+ file_contexts: ":myapex-file_contexts",
+ dynamic_common_lib_apex: true,
+ }
+ apex_key {
+ name: "myapex.key",
+ }
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libbar",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libbaz",
+ shared_libs: ["libc"],
+ apex_available: ["myapex","mydcla"],
+ min_sdk_version: "29",
+ }
+ cc_api_library {
+ name: "libc",
+ src: "libc.so",
+ min_sdk_version: "29",
+ recovery_available: true,
+ }
+ api_imports {
+ name: "api_imports",
+ shared_libs: [
+ "libc",
+ ],
+ header_libs: [],
+ }
+ `
+ ctx := testApex(t, bp)
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ apexRule := module.MaybeRule("apexRule")
+ if apexRule.Rule == nil {
+ t.Errorf("Expecting regular apex rule but a non regular apex rule found")
+ }
+
+ ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests))
+ trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("TrimmedApexRule")
+ libs_to_trim := trimmedApexRule.Args["libs_to_trim"]
+ android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo")
+ android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar")
+ android.AssertStringDoesNotContain(t, "unexpected libs in the libs to trim", libs_to_trim, "libbaz")
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index af4fd9f..2ddfd03 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -530,9 +530,8 @@
java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).RunTest(t)
- ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
"etc/boot-image.prof",
- "etc/classpaths/bootclasspath.pb",
"javalib/arm/boot.art",
"javalib/arm/boot.oat",
"javalib/arm/boot.vdex",
@@ -592,9 +591,8 @@
java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
).RunTest(t)
- ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
"etc/boot-image.prof",
- "etc/classpaths/bootclasspath.pb",
"javalib/bar.jar",
"javalib/foo.jar",
})
diff --git a/apex/bp2build.go b/apex/bp2build.go
index d28f512..a3dda83 100644
--- a/apex/bp2build.go
+++ b/apex/bp2build.go
@@ -15,16 +15,22 @@
import (
"android/soong/android"
+ "encoding/json"
"strings"
)
// This file contains the bp2build integration for the apex package.
// Export constants as Starlark using bp2build to Bazel.
-func BazelApexToolchainVars() string {
+func BazelApexToolchainVars() (string, error) {
+ marshalled, err := json.Marshal(apexAvailBaseline)
+ if err != nil {
+ return "", err
+ }
content := []string{
"# GENERATED BY SOONG. DO NOT EDIT.",
"default_manifest_version = " + android.DefaultUpdatableModuleVersion, // constants.go is different in every branch.
+ "apex_available_baseline = json.decode('''" + string(marshalled) + "''')",
}
- return strings.Join(content, "\n")
+ return strings.Join(content, "\n"), nil
}
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
index 01afa52..2f2b61e 100644
--- a/apex/bp2build_test.go
+++ b/apex/bp2build_test.go
@@ -42,6 +42,7 @@
OutputBaseDir: outputBaseDir,
LabelToApexInfo: map[string]cquery.ApexInfo{
"//:foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider.
SignedOutput: "signed_out.apex",
SignedCompressedOutput: "signed_out.capex",
UnsignedOutput: "unsigned_out.apex",
@@ -56,6 +57,9 @@
// unused
PackageName: "pkg_name",
ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
},
},
}
@@ -111,7 +115,12 @@
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
}
- if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+ // make modules to be installed to system
+ if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+ t.Errorf("Expected makeModulesToInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+ }
+ if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
}
}
@@ -212,6 +221,7 @@
OutputBaseDir: outputBaseDir,
LabelToApexInfo: map[string]cquery.ApexInfo{
"//:foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider
SignedOutput: "signed_out.apex",
UnsignedOutput: "unsigned_out.apex",
BundleKeyInfo: []string{"public_key", "private_key"},
@@ -225,8 +235,12 @@
// unused
PackageName: "pkg_name",
ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
},
"//:override_foo": cquery.ApexInfo{
+ // ApexInfo Starlark provider
SignedOutput: "override_signed_out.apex",
UnsignedOutput: "override_unsigned_out.apex",
BundleKeyInfo: []string{"override_public_key", "override_private_key"},
@@ -240,6 +254,9 @@
// unused
PackageName: "override_pkg_name",
ProvidesLibs: []string{"a", "b"},
+
+ // ApexMkInfo Starlark provider
+ MakeModulesToInstall: []string{"c"}, // d deliberately omitted
},
},
}
@@ -295,7 +312,12 @@
if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
}
- if w := "LOCAL_REQUIRED_MODULES := c d"; !strings.Contains(data, w) {
+
+ // make modules to be installed to system
+ if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
+ t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+ }
+ if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
}
}
diff --git a/apex/builder.go b/apex/builder.go
index 3b9cac0..93ff80d 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -40,6 +40,8 @@
pctx.Import("android/soong/java")
pctx.HostBinToolVariable("apexer", "apexer")
pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
+ pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing")
+
// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
@@ -146,6 +148,34 @@
}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
"opt_flags", "manifest", "is_DCLA")
+ TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{
+ Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+ `(. ${out}.copy_commands) && ` +
+ `APEXER_TOOL_PATH=${tool_path} ` +
+ `${apexer_with_trim_preprocessing} ` +
+ `--apexer ${apexer} ` +
+ `--canned_fs_config ${canned_fs_config} ` +
+ `--manifest ${manifest} ` +
+ `--libs_to_trim ${libs_to_trim} ` +
+ `${image_dir} ` +
+ `${out} ` +
+ `-- ` +
+ `--include_build_info ` +
+ `--force ` +
+ `--payload_type image ` +
+ `--key ${key} ` +
+ `--file_contexts ${file_contexts} ` +
+ `${opt_flags} `,
+ CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
+ "${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
+ "${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
+ "prebuilts/sdk/current/public/android.jar"},
+ Rspfile: "${out}.copy_commands",
+ RspfileContent: "${copy_commands}",
+ Description: "APEX ${image_dir} => ${out}",
+ }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
+ "opt_flags", "manifest", "libs_to_trim")
+
zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
`(. ${out}.copy_commands) && ` +
@@ -211,10 +241,11 @@
provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
- // APEX name can be overridden
+ // VNDK APEX name is determined at runtime, so update "name" in apex_manifest
optCommands := []string{}
- if a.properties.Apex_name != nil {
- optCommands = append(optCommands, "-v name "+*a.properties.Apex_name)
+ if a.vndkApex {
+ apexName := vndkApexNamePrefix + a.vndkVersion(ctx.DeviceConfig())
+ optCommands = append(optCommands, "-v name "+apexName)
}
// Collect jniLibs. Notice that a.filesInfo is already sorted
@@ -415,7 +446,7 @@
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
apexType := a.properties.ApexType
suffix := apexType.suffix()
- apexName := proptools.StringDefault(a.properties.Apex_name, a.BaseModuleName())
+ apexName := a.BaseModuleName()
////////////////////////////////////////////////////////////////////////////////////////////
// Step 1: copy built files to appropriate directories under the image directory
@@ -424,26 +455,13 @@
installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable()
- // b/140136207. When there are overriding APEXes for a VNDK APEX, the symbols file for the overridden
- // APEX and the overriding APEX will have the same installation paths at /apex/com.android.vndk.v<ver>
- // as their apexName will be the same. To avoid the path conflicts, skip installing the symbol files
- // for the overriding VNDK APEXes.
- if a.vndkApex && len(a.overridableProperties.Overrides) > 0 {
- installSymbolFiles = false
- }
-
- // Avoid creating duplicate build rules for multi-installed APEXes.
- if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
- installSymbolFiles = false
-
- }
// set of dependency module:location mappings
installMapSet := make(map[string]bool)
// TODO(jiyong): use the RuleBuilder
var copyCommands []string
var implicitInputs []android.Path
- pathWhenActivated := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
+ apexDir := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
for _, fi := range a.filesInfo {
destPath := imageDir.Join(ctx, fi.path()).String()
// Prepare the destination path
@@ -473,12 +491,12 @@
fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String()))
if installSymbolFiles {
- installedPath = ctx.InstallFileWithExtraFilesZip(pathWhenActivated.Join(ctx, fi.installDir),
+ installedPath = ctx.InstallFileWithExtraFilesZip(apexDir.Join(ctx, fi.installDir),
fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs())
}
} else {
if installSymbolFiles {
- installedPath = ctx.InstallFile(pathWhenActivated.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
+ installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
}
}
implicitInputs = append(implicitInputs, fi.builtFile)
@@ -492,7 +510,7 @@
symlinkDest := imageDir.Join(ctx, symlinkPath).String()
copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
if installSymbolFiles {
- installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
+ installedSymlink := ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
implicitInputs = append(implicitInputs, installedSymlink)
}
}
@@ -519,8 +537,8 @@
}
implicitInputs = append(implicitInputs, a.manifestPbOut)
if installSymbolFiles {
- installedManifest := ctx.InstallFile(pathWhenActivated, "apex_manifest.pb", a.manifestPbOut)
- installedKey := ctx.InstallFile(pathWhenActivated, "apex_pubkey", a.publicKeyFile)
+ installedManifest := ctx.InstallFile(apexDir, "apex_manifest.pb", a.manifestPbOut)
+ installedKey := ctx.InstallFile(apexDir, "apex_pubkey", a.publicKeyFile)
implicitInputs = append(implicitInputs, installedManifest, installedKey)
}
@@ -676,12 +694,6 @@
optFlags = append(optFlags, "--unsigned_payload")
}
- if a.properties.Apex_name != nil {
- // If apex_name is set, apexer can skip checking if key name matches with
- // apex name. Note that apex_manifest is also mended.
- optFlags = append(optFlags, "--do_not_check_keyname")
- }
-
if moduleMinSdkVersion == android.SdkVersion_Android10 {
implicitInputs = append(implicitInputs, a.manifestJsonOut)
optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
@@ -706,6 +718,24 @@
"opt_flags": strings.Join(optFlags, " "),
},
})
+ } else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: TrimmedApexRule,
+ Implicits: implicitInputs,
+ Output: unsignedOutputFile,
+ Description: "apex (" + apexType.name() + ")",
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": imageDir.String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": a.manifestPbOut.String(),
+ "file_contexts": fileContexts.String(),
+ "canned_fs_config": cannedFsConfig.String(),
+ "key": a.privateKeyFile.String(),
+ "opt_flags": strings.Join(optFlags, " "),
+ "libs_to_trim": strings.Join(a.libs_to_trim(ctx), ","),
+ },
+ })
} else {
ctx.Build(pctx, android.BuildParams{
Rule: apexRule,
@@ -970,7 +1000,7 @@
if a.vndkApex {
overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName)
if overridden {
- return strings.Replace(*a.properties.Apex_name, vndkApexName, overrideName, 1)
+ return overrideName + ".v" + a.vndkVersion(ctx.DeviceConfig())
}
return ""
}
@@ -1133,7 +1163,7 @@
if a.properties.Canned_fs_config != nil {
cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config))
}
- cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
+ cmd.Text(") | LC_ALL=C sort ").FlagWithOutput("> ", cannedFsConfig)
builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))
return cannedFsConfig.OutputPath
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index d037664..c404a2e 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -31,7 +31,7 @@
result := android.GroupFixturePreparers(
prepareForTestWithSystemserverclasspathFragment,
prepareForTestWithMyapex,
- dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+ dexpreopt.FixtureSetApexSystemServerJars("myapex:foo", "myapex:bar"),
).RunTestWithBp(t, `
apex {
name: "myapex",
@@ -57,10 +57,23 @@
],
}
+ java_library {
+ name: "bar",
+ srcs: ["c.java"],
+ installable: true,
+ dex_preopt: {
+ profile: "bar-art-profile",
+ },
+ apex_available: [
+ "myapex",
+ ],
+ }
+
systemserverclasspath_fragment {
name: "mysystemserverclasspathfragment",
contents: [
"foo",
+ "bar",
],
apex_available: [
"myapex",
@@ -71,6 +84,8 @@
ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
"etc/classpaths/systemserverclasspath.pb",
"javalib/foo.jar",
+ "javalib/bar.jar",
+ "javalib/bar.jar.prof",
})
java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
@@ -236,7 +251,7 @@
result := android.GroupFixturePreparers(
prepareForTestWithSystemserverclasspathFragment,
prepareForTestWithMyapex,
- dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo"),
+ dexpreopt.FixtureSetApexStandaloneSystemServerJars("myapex:foo", "myapex:bar"),
).RunTestWithBp(t, `
apex {
name: "myapex",
@@ -262,10 +277,23 @@
],
}
+ java_library {
+ name: "bar",
+ srcs: ["c.java"],
+ dex_preopt: {
+ profile: "bar-art-profile",
+ },
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
systemserverclasspath_fragment {
name: "mysystemserverclasspathfragment",
standalone_contents: [
"foo",
+ "bar",
],
apex_available: [
"myapex",
@@ -276,6 +304,8 @@
ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
"etc/classpaths/systemserverclasspath.pb",
"javalib/foo.jar",
+ "javalib/bar.jar",
+ "javalib/bar.jar.prof",
})
}
diff --git a/apex/vndk.go b/apex/vndk.go
index ef3e5e1..c0be753 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -65,8 +65,19 @@
}
vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
- // Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
- ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
+ apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+ if err != nil {
+ mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+ return
+ }
+
+ targets := mctx.MultiTargets()
+ if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) {
+ // Disable VNDK apexes for VNDK versions less than the minimum supported API level for the primary
+ // architecture.
+ ab.Disable()
+ }
+
}
}
diff --git a/bazel/aquery.go b/bazel/aquery.go
index bc823b3..6af472a 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -23,9 +23,11 @@
"sort"
"strings"
+ analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
+
+ "github.com/google/blueprint/metrics"
"github.com/google/blueprint/proptools"
"google.golang.org/protobuf/proto"
- analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
)
type artifactId int
@@ -118,12 +120,11 @@
// A helper type for aquery processing which facilitates retrieval of path IDs from their
// less readable Bazel structures (depset and path fragment).
type aqueryArtifactHandler struct {
- // Switches to true if any depset contains only `bazelToolsDependencySentinel`
- bazelToolsDependencySentinelNeeded bool
// Maps depset id to AqueryDepset, a representation of depset which is
// post-processed for middleman artifact handling, unhandled artifact
// dropping, content hashing, etc.
depsetIdToAqueryDepset map[depsetId]AqueryDepset
+ emptyDepsetIds map[depsetId]struct{}
// Maps content hash to AqueryDepset.
depsetHashToAqueryDepset map[string]AqueryDepset
@@ -145,9 +146,6 @@
// The file name of py3wrapper.sh, which is used by py_binary targets.
const py3wrapperFileName = "/py3wrapper.sh"
-// A file to be put into depsets that are otherwise empty
-const bazelToolsDependencySentinel = "BAZEL_TOOLS_DEPENDENCY_SENTINEL"
-
func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
m := map[K]V{}
for _, v := range values {
@@ -192,6 +190,7 @@
depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
depsetHashToAqueryDepset: map[string]AqueryDepset{},
depsetHashToArtifactPathsCache: map[string][]string{},
+ emptyDepsetIds: make(map[depsetId]struct{}, 0),
artifactIdToPath: artifactIdToPath,
}
@@ -208,16 +207,16 @@
// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
// depset.
-func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (AqueryDepset, error) {
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (*AqueryDepset, error) {
if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
- return aqueryDepset, nil
+ return &aqueryDepset, nil
}
transitiveDepsetIds := depset.TransitiveDepSetIds
var directArtifactPaths []string
for _, artifactId := range depset.DirectArtifactIds {
path, pathExists := a.artifactIdToPath[artifactId]
if !pathExists {
- return AqueryDepset{}, fmt.Errorf("undefined input artifactId %d", artifactId)
+ return nil, fmt.Errorf("undefined input artifactId %d", artifactId)
}
// Filter out any inputs which are universally dropped, and swap middleman
// artifacts with their corresponding depsets.
@@ -226,6 +225,7 @@
transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
} else if strings.HasSuffix(path, py3wrapperFileName) ||
strings.HasPrefix(path, "../bazel_tools") {
+ continue
// Drop these artifacts.
// See go/python-binary-host-mixed-build for more details.
// 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
@@ -241,20 +241,23 @@
for _, childDepsetId := range transitiveDepsetIds {
childDepset, exists := depsetIdToDepset[childDepsetId]
if !exists {
- return AqueryDepset{}, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+ if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
+ continue
+ } else {
+ return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
+ }
}
- childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset)
- if err != nil {
- return AqueryDepset{}, err
+ if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
+ return nil, err
+ } else if childAqueryDepset == nil {
+ continue
+ } else {
+ childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
}
- childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
}
if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
- // We could omit this depset altogether but that requires cleanup on
- // transitive dependents.
- // As a simpler alternative, we use this sentinel file as a dependency.
- directArtifactPaths = append(directArtifactPaths, bazelToolsDependencySentinel)
- a.bazelToolsDependencySentinelNeeded = true
+ a.emptyDepsetIds[depset.Id] = struct{}{}
+ return nil, nil
}
aqueryDepset := AqueryDepset{
ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
@@ -263,7 +266,7 @@
}
a.depsetIdToAqueryDepset[depset.Id] = aqueryDepset
a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
- return aqueryDepset, nil
+ return &aqueryDepset, nil
}
// getInputPaths flattens the depsets of the given IDs and returns all transitive
@@ -312,7 +315,7 @@
// action graph, as described by the given action graph json proto.
// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
// are one-to-one with Bazel's depSetOfFiles objects.
-func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, []AqueryDepset, error) {
+func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]BuildStatement, []AqueryDepset, error) {
aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
if err != nil {
@@ -386,82 +389,92 @@
ParentId: pathFragmentId(protoPathFragments.ParentId)})
}
- aqueryHandler, err := newAqueryHandler(aqueryResult)
- if err != nil {
- return nil, nil, err
- }
- var buildStatements []BuildStatement
- if aqueryHandler.bazelToolsDependencySentinelNeeded {
- buildStatements = append(buildStatements, BuildStatement{
- Command: fmt.Sprintf("touch '%s'", bazelToolsDependencySentinel),
- OutputPaths: []string{bazelToolsDependencySentinel},
- Mnemonic: bazelToolsDependencySentinel,
- })
- }
-
- for _, actionEntry := range aqueryResult.Actions {
- if shouldSkipAction(actionEntry) {
- continue
- }
-
- var buildStatement BuildStatement
- if actionEntry.isSymlinkAction() {
- buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
- } else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
- buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
- } else if actionEntry.isFileWriteAction() {
- buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
- } else if actionEntry.isSymlinkTreeAction() {
- buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
- } else if len(actionEntry.Arguments) < 1 {
- return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
- } else {
- buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
- }
-
+ var aqueryHandler *aqueryArtifactHandler
+ {
+ eventHandler.Begin("init_handler")
+ defer eventHandler.End("init_handler")
+ aqueryHandler, err = newAqueryHandler(aqueryResult)
if err != nil {
return nil, nil, err
}
- buildStatements = append(buildStatements, buildStatement)
+ }
+
+ var buildStatements []BuildStatement
+ {
+ eventHandler.Begin("build_statements")
+ defer eventHandler.End("build_statements")
+ for _, actionEntry := range aqueryResult.Actions {
+ if shouldSkipAction(actionEntry) {
+ continue
+ }
+
+ var buildStatement BuildStatement
+ if actionEntry.isSymlinkAction() {
+ buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
+ } else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
+ buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
+ } else if actionEntry.isFileWriteAction() {
+ buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
+ } else if actionEntry.isSymlinkTreeAction() {
+ buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
+ } else if len(actionEntry.Arguments) < 1 {
+ err = fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
+ } else {
+ buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
+ }
+
+ if err != nil {
+ return nil, nil, err
+ }
+ buildStatements = append(buildStatements, buildStatement)
+ }
}
depsetsByHash := map[string]AqueryDepset{}
var depsets []AqueryDepset
- for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
- if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
- // Two depsets collide on hash. Ensure that their contents are identical.
- if !reflect.DeepEqual(aqueryDepset, prevEntry) {
- return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
+ {
+ eventHandler.Begin("depsets")
+ defer eventHandler.End("depsets")
+ for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
+ if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
+ // Two depsets collide on hash. Ensure that their contents are identical.
+ if !reflect.DeepEqual(aqueryDepset, prevEntry) {
+ return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
+ }
+ } else {
+ depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
+ depsets = append(depsets, aqueryDepset)
}
- } else {
- depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
- depsets = append(depsets, aqueryDepset)
}
}
- // Build Statements and depsets must be sorted by their content hash to
- // preserve determinism between builds (this will result in consistent ninja file
- // output). Note they are not sorted by their original IDs nor their Bazel ordering,
- // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
- sort.Slice(buildStatements, func(i, j int) bool {
- // For build statements, compare output lists. In Bazel, each output file
- // may only have one action which generates it, so this will provide
- // a deterministic ordering.
- outputs_i := buildStatements[i].OutputPaths
- outputs_j := buildStatements[j].OutputPaths
- if len(outputs_i) != len(outputs_j) {
- return len(outputs_i) < len(outputs_j)
- }
- if len(outputs_i) == 0 {
- // No outputs for these actions, so compare commands.
- return buildStatements[i].Command < buildStatements[j].Command
- }
- // There may be multiple outputs, but the output ordering is deterministic.
- return outputs_i[0] < outputs_j[0]
+ eventHandler.Do("build_statement_sort", func() {
+ // Build Statements and depsets must be sorted by their content hash to
+ // preserve determinism between builds (this will result in consistent ninja file
+ // output). Note they are not sorted by their original IDs nor their Bazel ordering,
+ // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
+ sort.Slice(buildStatements, func(i, j int) bool {
+ // For build statements, compare output lists. In Bazel, each output file
+ // may only have one action which generates it, so this will provide
+ // a deterministic ordering.
+ outputs_i := buildStatements[i].OutputPaths
+ outputs_j := buildStatements[j].OutputPaths
+ if len(outputs_i) != len(outputs_j) {
+ return len(outputs_i) < len(outputs_j)
+ }
+ if len(outputs_i) == 0 {
+ // No outputs for these actions, so compare commands.
+ return buildStatements[i].Command < buildStatements[j].Command
+ }
+ // There may be multiple outputs, but the output ordering is deterministic.
+ return outputs_i[0] < outputs_j[0]
+ })
})
- sort.Slice(depsets, func(i, j int) bool {
- return depsets[i].ContentHash < depsets[j].ContentHash
+ eventHandler.Do("depset_sort", func() {
+ sort.Slice(depsets, func(i, j int) bool {
+ return depsets[i].ContentHash < depsets[j].ContentHash
+ })
})
return buildStatements, depsets, nil
}
@@ -484,7 +497,9 @@
var hashes []string
for _, depsetId := range inputDepsetIds {
if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
- return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+ if _, empty := a.emptyDepsetIds[depsetId]; !empty {
+ return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", depsetId)
+ }
} else {
hashes = append(hashes, aqueryDepset.ContentHash)
}
@@ -656,17 +671,18 @@
return replacer.Replace(actionEntry.TemplateContent)
}
+// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
+var commandLineArgumentReplacer = strings.NewReplacer(
+ `\`, `\\`,
+ `$`, `\$`,
+ "`", "\\`",
+ `"`, `\"`,
+ "\n", "\\n",
+ `'`, `'"'"'`,
+)
+
func escapeCommandlineArgument(str string) string {
- // \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
- replacer := strings.NewReplacer(
- `\`, `\\`,
- `$`, `\$`,
- "`", "\\`",
- `"`, `\"`,
- "\n", "\\n",
- `'`, `'"'"'`,
- )
- return replacer.Replace(str)
+ return commandLineArgumentReplacer.Replace(str)
}
func (a action) isSymlinkAction() bool {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 2eacafa..c6b139e 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -21,8 +21,10 @@
"sort"
"testing"
- "google.golang.org/protobuf/proto"
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
+
+ "github.com/google/blueprint/metrics"
+ "google.golang.org/protobuf/proto"
)
func TestAqueryMultiArchGenrule(t *testing.T) {
@@ -136,7 +138,7 @@
t.Error(err)
return
}
- actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
+ actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
var expectedBuildStatements []BuildStatement
for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
expectedBuildStatements = append(expectedBuildStatements,
@@ -195,7 +197,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined outputId 3")
}
@@ -226,8 +228,8 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
- assertError(t, err, "undefined input depsetId 2")
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
+ assertError(t, err, "undefined (not even empty) input depsetId 2")
}
func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
@@ -257,7 +259,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
}
@@ -288,7 +290,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined input artifactId 3")
}
@@ -319,7 +321,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined path fragment id 3")
}
@@ -352,7 +354,7 @@
t.Error(err)
return
}
- actual, _, err := AqueryBuildStatements(data)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -402,7 +404,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
}
@@ -483,7 +485,7 @@
t.Error(err)
return
}
- actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
+ actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
expectedBuildStatements := []BuildStatement{
{
@@ -538,7 +540,7 @@
t.Error(err)
return
}
- actual, _, err := AqueryBuildStatements(data)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -584,13 +586,18 @@
{ "id": 60, "label": ".."}
]
}`
+ /* depsets
+ 1111 2222
+ / \ |
+ ../dep2 ../bazel_tools/dep1
+ */
data, err := JsonToActionGraphContainer(inputString)
if err != nil {
t.Error(err)
return
}
- actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
- if len(actualDepsets) != 2 {
+ actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+ if len(actualDepsets) != 1 {
t.Errorf("expected 1 depset but found %#v", actualDepsets)
return
}
@@ -624,6 +631,82 @@
}
}
+func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
+ const inputString = `{
+ "artifacts": [
+ { "id": 1, "path_fragment_id": 10 },
+ { "id": 2, "path_fragment_id": 20 },
+ { "id": 3, "path_fragment_id": 30 }],
+ "dep_set_of_files": [{
+ "id": 1111,
+ "transitive_dep_set_ids": [2222]
+ }, {
+ "id": 2222,
+ "direct_artifact_ids": [3]
+ }, {
+ "id": 3333,
+ "direct_artifact_ids": [3]
+ }, {
+ "id": 4444,
+ "transitive_dep_set_ids": [3333]
+ }],
+ "actions": [{
+ "target_id": 100,
+ "action_key": "x",
+ "input_dep_set_ids": [1111, 4444],
+ "mnemonic": "x",
+ "arguments": ["bogus", "command"],
+ "output_ids": [2],
+ "primary_output_id": 1
+ }],
+ "path_fragments": [
+ { "id": 10, "label": "input" },
+ { "id": 20, "label": "output" },
+ { "id": 30, "label": "dep", "parent_id": 50 },
+ { "id": 50, "label": "bazel_tools", "parent_id": 60 },
+ { "id": 60, "label": ".."}
+ ]
+}`
+ /* depsets
+ 1111 4444
+ || ||
+ 2222 3333
+ | |
+ ../bazel_tools/dep
+ Note: in dep_set_of_files:
+ 1111 appears BEFORE its dependency,2222 while
+ 4444 appears AFTER its dependency 3333
+ and this test shows that that order doesn't affect empty depset pruning
+ */
+ data, err := JsonToActionGraphContainer(inputString)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
+ if len(actualDepsets) != 0 {
+ t.Errorf("expected 0 depsets but found %#v", actualDepsets)
+ return
+ }
+
+ expectedBuildStatement := BuildStatement{
+ Command: "bogus command",
+ OutputPaths: []string{"output"},
+ Mnemonic: "x",
+ }
+ buildStatementFound := false
+ for _, actualBuildStatement := range actualBuildStatements {
+ if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+ buildStatementFound = true
+ break
+ }
+ }
+ if !buildStatementFound {
+ t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+ return
+ }
+}
+
func TestMiddlemenAction(t *testing.T) {
const inputString = `
{
@@ -667,7 +750,7 @@
t.Error(err)
return
}
- actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data)
+ actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -764,7 +847,7 @@
t.Error(err)
return
}
- actual, _, err := AqueryBuildStatements(data)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
@@ -813,7 +896,7 @@
t.Error(err)
return
}
- actual, _, err := AqueryBuildStatements(data)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -859,7 +942,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
}
@@ -890,7 +973,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, "undefined outputId 2")
}
@@ -923,7 +1006,7 @@
t.Error(err)
return
}
- actual, _, err := AqueryBuildStatements(data)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -965,7 +1048,7 @@
t.Error(err)
return
}
- _, _, err = AqueryBuildStatements(data)
+ _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
assertError(t, err, `Expect 1 output to template expand action, got: output []`)
}
@@ -993,7 +1076,7 @@
t.Error(err)
return
}
- actual, _, err := AqueryBuildStatements(data)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
@@ -1030,7 +1113,7 @@
t.Error(err)
return
}
- actual, _, err := AqueryBuildStatements(data)
+ actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
if err != nil {
t.Errorf("Unexpected error %q", err)
}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 2b8753b..4680256 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -343,8 +343,8 @@
}
func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
- if ca.configurationType < other.configurationType {
- return true
+ if ca.configurationType == other.configurationType {
+ return ca.subType < other.subType
}
- return ca.subType < other.subType
+ return ca.configurationType < other.configurationType
}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 81c60d9..0c8247a 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -14,7 +14,14 @@
GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
)
+type CcAndroidMkInfo struct {
+ LocalStaticLibs []string
+ LocalWholeStaticLibs []string
+ LocalSharedLibs []string
+}
+
type CcInfo struct {
+ CcAndroidMkInfo
OutputFiles []string
CcObjectFiles []string
CcSharedLibraryFiles []string
@@ -173,27 +180,40 @@
tidy_files = []
clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
if clang_tidy_info:
- tidy_files = [v.path for v in clang_tidy_info.tidy_files.to_list()]
+ tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
abi_diff_files = []
abi_diff_info = p.get("//build/bazel/rules/abi:abi_dump.bzl%AbiDiffInfo")
if abi_diff_info:
abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()]
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+ androidmk_info = p[androidmk_tag]
+ local_static_libs = androidmk_info.local_static_libs
+ local_whole_static_libs = androidmk_info.local_whole_static_libs
+ local_shared_libs = androidmk_info.local_shared_libs
+
return json_encode({
- "OutputFiles": outputFiles,
- "CcObjectFiles": ccObjectFiles,
- "CcSharedLibraryFiles": sharedLibraries,
- "CcStaticLibraryFiles": staticLibraries,
- "Includes": includes,
- "SystemIncludes": system_includes,
- "Headers": headers,
- "RootStaticArchives": rootStaticArchives,
- "RootDynamicLibraries": rootSharedLibraries,
- "TidyFiles": tidy_files,
- "TocFile": toc_file,
- "UnstrippedOutput": unstripped,
- "AbiDiffFiles": abi_diff_files,
+ "OutputFiles": outputFiles,
+ "CcObjectFiles": ccObjectFiles,
+ "CcSharedLibraryFiles": sharedLibraries,
+ "CcStaticLibraryFiles": staticLibraries,
+ "Includes": includes,
+ "SystemIncludes": system_includes,
+ "Headers": headers,
+ "RootStaticArchives": rootStaticArchives,
+ "RootDynamicLibraries": rootSharedLibraries,
+ "TidyFiles": [t for t in tidy_files],
+ "TocFile": toc_file,
+ "UnstrippedOutput": unstripped,
+ "AbiDiffFiles": abi_diff_files,
+ "LocalStaticLibs": [l for l in local_static_libs],
+ "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+ "LocalSharedLibs": [l for l in local_shared_libs],
})`
}
@@ -237,6 +257,15 @@
if info.signed_compressed_output:
signed_compressed_output = info.signed_compressed_output.path
+mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo")
+if not mk_info:
+ fail("%s did not provide ApexMkInfo" % id_string)
+
+tidy_files = []
+clang_tidy_info = providers(target).get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+ tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
+
return json_encode({
"signed_output": info.signed_output.path,
"signed_compressed_output": signed_compressed_output,
@@ -251,10 +280,13 @@
"backing_libs": info.backing_libs.path,
"bundle_file": info.base_with_config_zip.path,
"installed_files": info.installed_files.path,
+ "make_modules_to_install": mk_info.make_modules_to_install,
+ "tidy_files": [t for t in tidy_files],
})`
}
type ApexInfo struct {
+ // From the ApexInfo provider
SignedOutput string `json:"signed_output"`
SignedCompressedOutput string `json:"signed_compressed_output"`
UnsignedOutput string `json:"unsigned_output"`
@@ -268,6 +300,10 @@
BackingLibs string `json:"backing_libs"`
BundleFile string `json:"bundle_file"`
InstalledFiles string `json:"installed_files"`
+ TidyFiles []string `json:"tidy_files"`
+
+ // From the ApexMkInfo provider
+ MakeModulesToInstall []string `json:"make_modules_to_install"`
}
// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
@@ -289,15 +325,38 @@
}
func (g getCcUnstrippedInfoType) StarlarkFunctionBody() string {
- return `unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
+ return `
p = providers(target)
output_path = target.files.to_list()[0].path
+
unstripped = output_path
+unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
if unstripped_tag in p:
- unstripped = p[unstripped_tag].unstripped.files.to_list()[0].path
+ unstripped_info = p[unstripped_tag]
+ unstripped = unstripped_info.unstripped.files.to_list()[0].path
+
+local_static_libs = []
+local_whole_static_libs = []
+local_shared_libs = []
+androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
+if androidmk_tag in p:
+ androidmk_info = p[androidmk_tag]
+ local_static_libs = androidmk_info.local_static_libs
+ local_whole_static_libs = androidmk_info.local_whole_static_libs
+ local_shared_libs = androidmk_info.local_shared_libs
+
+tidy_files = []
+clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+ tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
+
return json_encode({
"OutputFile": output_path,
"UnstrippedOutput": unstripped,
+ "LocalStaticLibs": [l for l in local_static_libs],
+ "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
+ "LocalSharedLibs": [l for l in local_shared_libs],
+ "TidyFiles": [t for t in tidy_files],
})
`
}
@@ -312,8 +371,10 @@
}
type CcUnstrippedInfo struct {
+ CcAndroidMkInfo
OutputFile string
UnstrippedOutput string
+ TidyFiles []string
}
// splitOrEmpty is a modification of strings.Split() that returns an empty list
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 1d30535..7003ce1 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -177,9 +177,11 @@
"backing_libs":"path/to/backing.txt",
"bundle_file": "dir/bundlefile.zip",
"installed_files":"path/to/installed-files.txt",
- "provides_native_libs":[]
+ "provides_native_libs":[],
+ "make_modules_to_install": ["foo","bar"]
}`,
expectedOutput: ApexInfo{
+ // ApexInfo
SignedOutput: "my.apex",
UnsignedOutput: "my.apex.unsigned",
RequiresLibs: []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
@@ -191,6 +193,9 @@
BackingLibs: "path/to/backing.txt",
BundleFile: "dir/bundlefile.zip",
InstalledFiles: "path/to/installed-files.txt",
+
+ // ApexMkInfo
+ MakeModulesToInstall: []string{"foo", "bar"},
},
},
}
diff --git a/bazel/properties.go b/bazel/properties.go
index 76450dc..f4acd26 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -73,6 +73,16 @@
}
}
+func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
+ keys := make([]ConfigurationAxis, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+
+ sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+ return keys
+}
+
// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
func MakeLabelListFromTargetNames(targetNames []string) LabelList {
@@ -152,7 +162,7 @@
ll.Includes = append(ll.Includes, other.Includes...)
}
if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
- ll.Excludes = append(other.Excludes, other.Excludes...)
+ ll.Excludes = append(ll.Excludes, other.Excludes...)
}
}
@@ -412,13 +422,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
- for k := range la.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(la.ConfigurableValues)
}
// MakeLabelAttribute turns a string into a LabelAttribute
@@ -608,13 +612,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
- for k := range ba.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(ba.ConfigurableValues)
}
// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
@@ -761,13 +759,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
- for k := range lla.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(lla.ConfigurableValues)
}
// Append all values, including os and arch specific ones, from another
@@ -814,6 +806,16 @@
return false
}
+// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
+func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
+ for _, values := range lla.ConfigurableValues[axis] {
+ if !values.IsNil() {
+ return true
+ }
+ }
+ return false
+}
+
// IsEmpty returns true if the attribute has no values under any configuration.
func (lla LabelListAttribute) IsEmpty() bool {
if len(lla.Value.Includes) > 0 {
@@ -878,7 +880,7 @@
// then remove all config-specific excludes
allLabels := baseLabels.deepCopy()
allLabels.Append(val)
- lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
+ lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
}
// After going through all configs, delete the duplicates in the config
@@ -1080,14 +1082,10 @@
if cs[axis] == nil {
cs[axis] = make(stringSelectValues)
}
- var v = ""
- if str != nil {
- v = *str
- }
- cs[axis][config] = v
+ cs[axis][config] = str
}
-type stringSelectValues map[string]string
+type stringSelectValues map[string]*string
// HasConfigurableValues returns true if the attribute contains axis-specific string values.
func (sa StringAttribute) HasConfigurableValues() bool {
@@ -1099,6 +1097,11 @@
return false
}
+// SetValue sets the base, non-configured value for the Label
+func (sa *StringAttribute) SetValue(value string) {
+ sa.SetSelectValue(NoConfigAxis, "", &value)
+}
+
// SetSelectValue set a value for a bazel select for the given axis, config and value.
func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
axis.validateConfig(config)
@@ -1123,7 +1126,7 @@
return sa.Value
case arch, os, osArch, productVariables:
if v, ok := sa.ConfigurableValues[axis][config]; ok {
- return &v
+ return v
} else {
return nil
}
@@ -1134,13 +1137,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(sa.ConfigurableValues))
- for k := range sa.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(sa.ConfigurableValues)
}
// Collapse reduces the configurable axes of the string attribute to a single axis.
@@ -1154,7 +1151,7 @@
_, containsProductVariables := axisTypes[productVariables]
if containsProductVariables {
if containsOs || containsArch || containsOsArch {
- return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
+ return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
}
}
if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
@@ -1181,7 +1178,7 @@
}
}
}
- // All os_arch values are now set. Clear os and arch axes.
+ /// All os_arch values are now set. Clear os and arch axes.
delete(sa.ConfigurableValues, ArchConfigurationAxis)
delete(sa.ConfigurableValues, OsConfigurationAxis)
// Verify post-condition; this should never fail, provided no additional
@@ -1189,6 +1186,21 @@
if len(sa.ConfigurableValues) > 1 {
panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
}
+ } else if containsProductVariables {
+ usedBaseValue := false
+ for a, configToProp := range sa.ConfigurableValues {
+ if a.configurationType == productVariables {
+ for c, p := range configToProp {
+ if p == nil {
+ sa.SetSelectValue(a, c, sa.Value)
+ usedBaseValue = true
+ }
+ }
+ }
+ }
+ if usedBaseValue {
+ sa.Value = nil
+ }
}
return nil
}
@@ -1327,13 +1339,7 @@
// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
- keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
- for k := range sla.ConfigurableValues {
- keys = append(keys, k)
- }
-
- sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
- return keys
+ return SortedConfigurationAxes(sla.ConfigurableValues)
}
// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
@@ -1367,6 +1373,9 @@
// TryVariableSubstitution, replace string substitution formatting within each string in slice with
// Starlark string.format compatible tag for productVariable.
func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
+ if len(slice) == 0 {
+ return slice, false
+ }
ret := make([]string, 0, len(slice))
changesMade := false
for _, s := range slice {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 8729381..cf03eb5 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -231,6 +231,7 @@
"all_include",
"arm_exclude",
"android_exclude",
+ "product_config_exclude",
},
[]string{"all_exclude"},
),
@@ -251,10 +252,10 @@
"a": makeLabelList([]string{}, []string{"not_in_value"}),
"b": makeLabelList([]string{"b_val"}, []string{}),
"c": makeLabelList([]string{"c_val"}, []string{}),
- ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
+ ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
},
ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
- "a": makeLabelList([]string{}, []string{"not_in_value"}),
+ "a": makeLabelList([]string{}, []string{"product_config_exclude"}),
},
},
}
@@ -287,6 +288,10 @@
"c": makeLabels("c_val"),
ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
},
+ ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): {
+ "a": nilLabels,
+ ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
+ },
}
for _, axis := range attr.SortedConfigurationAxes() {
if _, ok := expectedConfiguredIncludes[axis]; !ok {
diff --git a/bp2build/aar_conversion_test.go b/bp2build/aar_conversion_test.go
index df7cced..0cda5dd 100644
--- a/bp2build/aar_conversion_test.go
+++ b/bp2build/aar_conversion_test.go
@@ -133,7 +133,41 @@
"exports": `[":static_import_dep"]`,
},
),
+ MakeNeverlinkDuplicateTarget("android_library", "TestImport"),
},
},
)
}
+
+func TestConvertAndroidLibraryKotlin(t *testing.T) {
+ t.Helper()
+ RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, Bp2buildTestCase{
+ Description: "Android Library with .kt srcs and common_srcs attribute",
+ ModuleTypeUnderTest: "android_library",
+ ModuleTypeUnderTestFactory: java.AndroidLibraryFactory,
+ Filesystem: map[string]string{
+ "AndroidManifest.xml": "",
+ },
+ Blueprint: `
+android_library {
+ name: "TestLib",
+ srcs: ["a.java", "b.kt"],
+ common_srcs: ["c.kt"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget(
+ "android_library",
+ "TestLib",
+ AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "common_srcs": `["c.kt"]`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ }),
+ MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+ }})
+}
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 2b35521..4d18f83 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -28,6 +28,7 @@
func registerAndroidAppModuleTypes(ctx android.RegistrationContext) {
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
}
func TestMinimalAndroidApp(t *testing.T) {
@@ -200,3 +201,108 @@
}),
}})
}
+
+func TestAndroidAppLibs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with libs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{},
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ libs: ["barLib"]
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `[]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ }})
+}
+
+func TestAndroidAppKotlinSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with kotlin sources and common_srcs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ srcs: ["a.java", "b.kt"],
+ certificate: ":foocert",
+ manifest: "fooManifest.xml",
+ libs: ["barLib"]
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "manifest": `"fooManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "deps": `[":foo_kt"]`,
+ "certificate": `":foocert"`,
+ "manifest": `"fooManifest.xml"`,
+ }),
+ }})
+}
+
+func TestAndroidAppCommonSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, Bp2buildTestCase{
+ Description: "Android app with common_srcs",
+ ModuleTypeUnderTest: "android_app",
+ ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+ Filesystem: map[string]string{
+ "res/res.png": "",
+ },
+ Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ common_srcs: ["b.kt"],
+ certificate: "foocert",
+ manifest: "fooManifest.xml",
+ libs: ["barLib"],
+}
+java_library{
+ name: "barLib",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "barLib", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "barLib"),
+ MakeBazelTarget("android_library", "foo_kt", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "common_srcs": `["b.kt"]`,
+ "manifest": `"fooManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `[":barLib-neverlink"]`,
+ }),
+ MakeBazelTarget("android_binary", "foo", AttrNameToString{
+ "deps": `[":foo_kt"]`,
+ "certificate_name": `"foocert"`,
+ "manifest": `"fooManifest.xml"`,
+ }),
+ }})
+}
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 5dc9612..062eba8 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -45,19 +45,36 @@
bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
writeFiles(ctx, bp2buildDir, bp2buildFiles)
- productConfigFiles, err := CreateProductConfigFiles(ctx)
+ injectionFiles, err := CreateSoongInjectionDirFiles(ctx, res.metrics)
if err != nil {
- fmt.Printf("ERROR: %s", err.Error())
+ fmt.Printf("%s\n", err.Error())
os.Exit(1)
}
-
- soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
- writeFiles(ctx, soongInjectionDir, productConfigFiles)
- writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics))
+ writeFiles(ctx, android.PathForOutput(ctx, bazel.SoongInjectionDirName), injectionFiles)
return &res.metrics
}
+// Wrapper function that will be responsible for all files in soong_injection directory
+// This includes
+// 1. config value(s) that are hardcoded in Soong
+// 2. product_config variables
+func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, error) {
+ var ret []BazelFile
+
+ productConfigFiles, err := CreateProductConfigFiles(ctx)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, productConfigFiles...)
+ injectionFiles, err := soongInjectionFiles(ctx.Config(), metrics)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, injectionFiles...)
+ return ret, nil
+}
+
// Get the output directory and create it if it doesn't exist.
func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
dirPath := outputDir.Join(ctx, dir)
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index e343a05..3eec439 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -97,6 +97,8 @@
build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
+build:linux_musl_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86
+build:linux_musl_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64
`)),
newFile(
"product_config_platforms",
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index f924d00..c11a50d 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1806,6 +1806,11 @@
ModuleTypeUnderTestFactory: cc.LibraryFactory,
Blueprint: soongCcLibraryPreamble + `
cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
name: "target_linux_bionic_empty",
target: {
linux_bionic: {
@@ -1816,7 +1821,10 @@
}
`,
ExpectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", AttrNameToString{
- "system_dynamic_deps": `[]`,
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
}),
},
)
@@ -1829,6 +1837,11 @@
ModuleTypeUnderTestFactory: cc.LibraryFactory,
Blueprint: soongCcLibraryPreamble + `
cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
name: "target_bionic_empty",
target: {
bionic: {
@@ -1839,12 +1852,68 @@
}
`,
ExpectedBazelTargets: makeCcLibraryTargets("target_bionic_empty", AttrNameToString{
- "system_dynamic_deps": `[]`,
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
}),
},
)
}
+func TestCcLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_lib empty for musl variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "target_musl_empty",
+ target: {
+ musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("target_musl_empty", AttrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ })
+}
+
+func TestCcLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library system_shared_lib empty for linux_musl variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: soongCcLibraryPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "target_linux_musl_empty",
+ target: {
+ linux_musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: makeCcLibraryTargets("target_linux_musl_empty", AttrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ })
+}
func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) {
runCcLibraryTestCase(t, Bp2buildTestCase{
Description: "cc_library system_shared_libs set for shared and root",
@@ -2756,7 +2825,7 @@
expectedBazelTargets := []string{
MakeBazelTarget(
"cc_api_library_headers",
- "libfoo.systemapi.headers",
+ "libfoo.module-libapi.headers",
AttrNameToString{
"export_includes": `["dir1"]`,
}),
@@ -2773,18 +2842,18 @@
"api": `"libfoo.map.txt"`,
"library_name": `"libfoo"`,
"api_surfaces": `[
- "systemapi",
+ "module-libapi",
"vendorapi",
]`,
"hdrs": `[
- ":libfoo.systemapi.headers",
+ ":libfoo.module-libapi.headers",
":libfoo.vendorapi.headers",
]`,
}),
}
RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
Blueprint: bp,
- Description: "cc API contributions to systemapi and vendorapi",
+ Description: "cc API contributions to module-libapi and vendorapi",
ExpectedBazelTargets: expectedBazelTargets,
})
}
@@ -2803,8 +2872,8 @@
stubs: {symbol_file: "a.map.txt"},
}`,
expectedApi: `"a.map.txt"`,
- expectedApiSurfaces: `["systemapi"]`,
- description: "Library that contributes to systemapi",
+ expectedApiSurfaces: `["module-libapi"]`,
+ description: "Library that contributes to module-libapi",
},
{
bp: `
@@ -2825,10 +2894,10 @@
}`,
expectedApi: `"a.map.txt"`,
expectedApiSurfaces: `[
- "systemapi",
+ "module-libapi",
"vendorapi",
]`,
- description: "Library that contributes to systemapi and vendorapi",
+ description: "Library that contributes to module-libapi and vendorapi",
},
}
for _, testCase := range testCases {
@@ -3368,23 +3437,23 @@
ExpectedBazelTargets: []string{
MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
})`,
"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
})`,
"local_includes": `["."]`,
}),
MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
"implementation_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":buh__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":buh__BP2BUILD__MISSING__DEP"],
})`,
"implementation_dynamic_deps": `[":baz__BP2BUILD__MISSING__DEP"] + select({
- "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
})`,
"local_includes": `["."]`,
}),
@@ -3414,16 +3483,16 @@
ExpectedBazelTargets: []string{
MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
"implementation_dynamic_deps": `select({
- "//build/bazel/rules/apex:non_apex": [":bar__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":bar__BP2BUILD__MISSING__DEP"],
})`,
"dynamic_deps": `select({
- "//build/bazel/rules/apex:non_apex": [":baz__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":baz__BP2BUILD__MISSING__DEP"],
})`,
"deps": `select({
- "//build/bazel/rules/apex:non_apex": [":abc__BP2BUILD__MISSING__DEP"],
- "//conditions:default": [],
+ "//build/bazel/rules/apex:in_apex": [],
+ "//conditions:default": [":abc__BP2BUILD__MISSING__DEP"],
})`,
"local_includes": `["."]`,
}),
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 767f4ad..d5256f6 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1310,6 +1310,11 @@
runCcLibraryStaticTestCase(t, Bp2buildTestCase{
Description: "cc_library_static system_shared_lib empty for bionic variant",
Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_bionic_empty",
target: {
@@ -1322,7 +1327,10 @@
`,
ExpectedBazelTargets: []string{
MakeBazelTarget("cc_library_static", "target_bionic_empty", AttrNameToString{
- "system_dynamic_deps": `[]`,
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
}),
},
})
@@ -1336,6 +1344,11 @@
runCcLibraryStaticTestCase(t, Bp2buildTestCase{
Description: "cc_library_static system_shared_lib empty for linux_bionic variant",
Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_linux_bionic_empty",
target: {
@@ -1348,6 +1361,63 @@
`,
ExpectedBazelTargets: []string{
MakeBazelTarget("cc_library_static", "target_linux_bionic_empty", AttrNameToString{
+ "system_dynamic_deps": `select({
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestStaticLibrary_SystemSharedLibsMuslEmpty(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty for musl variant",
+ Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+ name: "target_musl_empty",
+ target: {
+ musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_musl_empty", AttrNameToString{
+ "system_dynamic_deps": `[]`,
+ }),
+ },
+ })
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxMuslEmpty(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static system_shared_lib empty for linux_musl variant",
+ Blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+ name: "target_linux_musl_empty",
+ target: {
+ linux_musl: {
+ system_shared_libs: [],
+ },
+ },
+ include_build_directory: false,
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "target_linux_musl_empty", AttrNameToString{
"system_dynamic_deps": `[]`,
}),
},
@@ -1359,6 +1429,11 @@
Description: "cc_library_static system_shared_libs set for bionic variant",
Blueprint: soongCcLibraryStaticPreamble +
simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_bionic",
target: {
@@ -1374,6 +1449,7 @@
"system_dynamic_deps": `select({
"//build/bazel/platforms/os:android": [":libc"],
"//build/bazel/platforms/os:linux_bionic": [":libc"],
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
"//conditions:default": [],
})`,
}),
@@ -1387,6 +1463,11 @@
Blueprint: soongCcLibraryStaticPreamble +
simpleModuleDoNotConvertBp2build("cc_library", "libc") +
simpleModuleDoNotConvertBp2build("cc_library", "libm") + `
+cc_library {
+ name: "libc_musl",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library_static {
name: "target_linux_bionic",
system_shared_libs: ["libc"],
@@ -1402,6 +1483,7 @@
MakeBazelTarget("cc_library_static", "target_linux_bionic", AttrNameToString{
"system_dynamic_deps": `[":libc"] + select({
"//build/bazel/platforms/os:linux_bionic": [":libm"],
+ "//build/bazel/platforms/os:linux_musl": [":libc_musl"],
"//conditions:default": [],
})`,
}),
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1377c6b..eab84e1 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -58,6 +58,7 @@
exclude_srcs: ["a/b/exclude.c"],
sdk_version: "current",
min_sdk_version: "29",
+ crt: true,
}
`,
ExpectedBazelTargets: []string{
@@ -76,6 +77,7 @@
"system_dynamic_deps": `[]`,
"sdk_version": `"current"`,
"min_sdk_version": `"29"`,
+ "crt": "True",
}),
},
})
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 987c903..2a0a78e 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -28,10 +28,13 @@
ret[selectKey] = reflect.ValueOf(strs)
}
}
+
// if there is a select, use the base value as the conditions default value
if len(ret) > 0 {
- ret[bazel.ConditionsDefaultSelectKey] = value
- value = reflect.Zero(value.Type())
+ if _, ok := ret[bazel.ConditionsDefaultSelectKey]; !ok {
+ ret[bazel.ConditionsDefaultSelectKey] = value
+ value = reflect.Zero(value.Type())
+ }
}
return value, []selects{ret}
@@ -157,6 +160,12 @@
// select statements.
func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
var value reflect.Value
+ // configurableAttrs is the list of individual select statements to be
+ // concatenated together. These select statements should be along different
+ // axes. For example, one element may be
+ // `select({"//color:red": "one", "//color:green": "two"})`, and the second
+ // element may be `select({"//animal:cat": "three", "//animal:dog": "four"}).
+ // These selects should be sorted by axis identifier.
var configurableAttrs []selects
var prepend bool
var defaultSelectValue *string
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index e15dd59..73df675 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -6,6 +6,7 @@
"strings"
"android/soong/android"
+ "android/soong/cc"
cc_config "android/soong/cc/config"
java_config "android/soong/java/config"
@@ -20,20 +21,26 @@
Contents string
}
-func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
+// PRIVATE: Use CreateSoongInjectionDirFiles instead
+func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) ([]BazelFile, error) {
var files []BazelFile
files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
- files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+ files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
+ files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(cfg)))
files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
- files = append(files, newFile("apex_toolchain", "constants.bzl", apex.BazelApexToolchainVars()))
+ apexToolchainVars, err := apex.BazelApexToolchainVars()
+ if err != nil {
+ return nil, err
+ }
+ files = append(files, newFile("apex_toolchain", "constants.bzl", apexToolchainVars))
files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.Serialize().ConvertedModules, "\n")))
@@ -49,17 +56,19 @@
apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
if err != nil {
- panic(err)
+ return nil, err
}
files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
+ files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
+ files = append(files, newFile("allowlists", "env.bzl", android.EnvironmentVarsFile(cfg)))
// TODO(b/262781701): Create an alternate soong_build entrypoint for writing out these files only when requested
files = append(files, newFile("allowlists", "mixed_build_prod_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelProdMode), "\n")+"\n"))
files = append(files, newFile("allowlists", "mixed_build_staging_allowlist.txt", strings.Join(android.GetBazelEnabledModules(android.BazelStagingMode), "\n")+"\n"))
- return files
+ return files, nil
}
func CreateBazelFiles(
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index dfc7f0b..8c1d2ae 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -84,8 +84,10 @@
func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
- files := CreateSoongInjectionFiles(testConfig, CreateCodegenMetrics())
-
+ files, err := soongInjectionFiles(testConfig, CreateCodegenMetrics())
+ if err != nil {
+ t.Error(err)
+ }
expectedFilePaths := []bazelFilepath{
{
dir: "android",
@@ -101,7 +103,11 @@
},
{
dir: "cc_toolchain",
- basename: "constants.bzl",
+ basename: "config_constants.bzl",
+ },
+ {
+ dir: "cc_toolchain",
+ basename: "sanitizer_constants.bzl",
},
{
dir: "java_toolchain",
@@ -149,6 +155,14 @@
},
{
dir: "allowlists",
+ basename: GeneratedBuildFileName,
+ },
+ {
+ dir: "allowlists",
+ basename: "env.bzl",
+ },
+ {
+ dir: "allowlists",
basename: "mixed_build_prod_allowlist.txt",
},
{
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index e8551e5..46105c7 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -134,3 +134,153 @@
},
})
}
+
+func TestJavaBinaryHostKotlinSrcs(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, libs.",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java", "b.kt"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_kt"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinCommonSrcs(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with common_srcs",
+ Filesystem: testFs,
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java"],
+ common_srcs: ["b.kt"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "common_srcs": `["b.kt"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_kt"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinWithResourceDir(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, libs, resource dir .",
+ Filesystem: map[string]string{
+ "test.mf": "Main-Class: com.android.test.MainClass",
+ "res/a.res": "",
+ "res/dir1/b.res": "",
+ },
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java", "b.kt"],
+ java_resource_dirs: ["res"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_kt"]`,
+ "resources": `[
+ "res/a.res",
+ "res/dir1/b.res",
+ ]`,
+ "resource_strip_prefix": `"res"`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
+
+func TestJavaBinaryHostKotlinWithResources(t *testing.T) {
+ runJavaBinaryHostTestCase(t, Bp2buildTestCase{
+ Description: "java_binary_host with srcs, libs, resources.",
+ Filesystem: map[string]string{
+ "test.mf": "Main-Class: com.android.test.MainClass",
+ "res/a.res": "",
+ "res/b.res": "",
+ },
+ Blueprint: `java_binary_host {
+ name: "java-binary-host",
+ manifest: "test.mf",
+ srcs: ["a.java", "b.kt"],
+ java_resources: ["res/a.res", "res/b.res"],
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("kt_jvm_library", "java-binary-host_kt", AttrNameToString{
+ "srcs": `[
+ "a.java",
+ "b.kt",
+ ]`,
+ "resources": `[
+ "res/a.res",
+ "res/b.res",
+ ]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeBazelTarget("java_binary", "java-binary-host", AttrNameToString{
+ "main_class": `"com.android.test.MainClass"`,
+ "runtime_deps": `[":java-binary-host_kt"]`,
+ "target_compatible_with": `select({
+ "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ })`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 93a6174..0784f4b 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -715,3 +715,43 @@
},
})
}
+
+func TestJavaLibraryArchVariantLibs(t *testing.T) {
+ runJavaLibraryTestCase(t, Bp2buildTestCase{
+ Description: "java_library with arch variant libs",
+ Blueprint: `java_library {
+ name: "java-lib-1",
+ srcs: ["a.java"],
+ libs: ["java-lib-2"],
+ target: {
+ android: {
+ libs: ["java-lib-3"],
+ },
+ },
+ bazel_module: { bp2build_available: true },
+}
+
+ java_library{
+ name: "java-lib-2",
+}
+
+ java_library{
+ name: "java-lib-3",
+}
+`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+ "srcs": `["a.java"]`,
+ "deps": `[":java-lib-2-neverlink"] + select({
+ "//build/bazel/platforms/os:android": [":java-lib-3-neverlink"],
+ "//conditions:default": [],
+ })`,
+ }),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+ MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"),
+ MakeBazelTarget("java_library", "java-lib-3", AttrNameToString{}),
+ MakeNeverlinkDuplicateTarget("java_library", "java-lib-3"),
+ },
+ })
+}
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
index d9049d4..8c6337b 100644
--- a/bp2build/java_plugin_conversion_test.go
+++ b/bp2build/java_plugin_conversion_test.go
@@ -60,7 +60,7 @@
"//conditions:default": [],
})`,
"deps": `[
- ":java-lib-1",
+ ":java-lib-1-neverlink",
":java-lib-2",
]`,
"srcs": `[
@@ -101,7 +101,7 @@
"//conditions:default": [],
})`,
"deps": `[
- ":java-lib-1",
+ ":java-lib-1-neverlink",
":java-lib-2",
]`,
}),
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index bd21629..7e29fac 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -148,11 +148,26 @@
func (metrics *CodegenMetrics) AddEvent(event *bp2build_metrics_proto.Event) {
metrics.serialized.Events = append(metrics.serialized.Events, event)
}
+
func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) {
metrics.serialized.UnconvertedModuleCount += 1
metrics.serialized.TotalModuleTypeCount[moduleType] += 1
}
+func (metrics *CodegenMetrics) SetSymlinkCount(n uint64) {
+ if m := metrics.serialized.WorkspaceSymlinkCount; m != 0 {
+ fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceSymlinkCount of %d", m)
+ }
+ metrics.serialized.WorkspaceSymlinkCount = n
+}
+
+func (metrics *CodegenMetrics) SetMkDirCount(n uint64) {
+ if m := metrics.serialized.WorkspaceMkDirCount; m != 0 {
+ fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceDirCount of %d", m)
+ }
+ metrics.serialized.WorkspaceMkDirCount = n
+}
+
func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
return metrics.serialized.HandCraftedModuleCount +
metrics.serialized.GeneratedModuleCount +
@@ -173,6 +188,10 @@
)
func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) {
+ //a package module has empty name
+ if moduleType == "package" {
+ return
+ }
// Undo prebuilt_ module name prefix modifications
moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName)
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index dcd1f85..ba42f34 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -32,6 +32,7 @@
android.RegisterSoongConfigModuleBuildComponents(ctx)
ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+ ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
}
func TestErrorInBpFileDoesNotPanic(t *testing.T) {
@@ -192,6 +193,7 @@
copts = select({
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["-DSOC_DEFAULT"],
}),
local_includes = ["."],
@@ -210,7 +212,7 @@
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b", "soc_c"],
+ values: ["soc_a", "soc_b", "soc_c", "soc_d"],
}
soong_config_module_type {
@@ -263,6 +265,7 @@
copts = select({
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["-DSOC_DEFAULT"],
}) + select({
"//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
@@ -279,7 +282,7 @@
bp := `
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b", "soc_c"],
+ values: ["soc_a", "soc_b", "soc_c", "soc_d"],
}
soong_config_module_type {
@@ -332,11 +335,13 @@
copts = select({
"//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
"//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["-DSOC_DEFAULT"],
}),
implementation_deps = select({
"//build/bazel/product_variables:acme__board__soc_a": ["//foo/bar:soc_a_dep"],
"//build/bazel/product_variables:acme__board__soc_b": ["//foo/bar:soc_b_dep"],
+ "//build/bazel/product_variables:acme__board__soc_c": [],
"//conditions:default": ["//foo/bar:soc_default_static_dep"],
}),
local_includes = ["."],
@@ -604,6 +609,234 @@
)`}})
}
+func TestSoongConfigModuleType_Defaults_UseBaselineValueForStringProp(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_custom",
+ module_type: "custom",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: [
+ "string_literal_prop",
+ ],
+}
+
+library_linking_strategy_custom {
+ name: "foo",
+ string_literal_prop: "29",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ string_literal_prop: "30",
+ },
+ },
+ },
+}`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{},
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("custom", "foo", AttrNameToString{
+ "string_literal_prop": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "29",
+ "//conditions:default": "30",
+ })`,
+ }),
+ },
+ })
+}
+
+func TestSoongConfigModuleType_UnsetConditions(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: [
+ "shared_libs",
+ "static_libs",
+ ],
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_lib_a_defaults",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_a",
+ ],
+ },
+ },
+ },
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_merged_defaults",
+ defaults: ["library_linking_strategy_lib_a_defaults"],
+ host_supported: true,
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_b",
+ ],
+ },
+ },
+ },
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary",
+ srcs: ["library_linking_strategy.cc"],
+ defaults: ["library_linking_strategy_merged_defaults"],
+ include_build_directory: false,
+}`
+
+ otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_default", bazel_module: { bp2build_available: false } }
+`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
+ "foo/bar/Android.bp": otherDeps,
+ },
+ ExpectedBazelTargets: []string{`cc_binary(
+ name = "library_linking_strategy_sample_binary",
+ dynamic_deps = select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": [
+ "//foo/bar:lib_b",
+ "//foo/bar:lib_a",
+ ],
+ }),
+ srcs = ["library_linking_strategy.cc"],
+)`}})
+}
+
+func TestSoongConfigModuleType_UnsetConditionsExcludeLibs(t *testing.T) {
+ bp := `
+soong_config_string_variable {
+ name: "library_linking_strategy",
+ values: [
+ "prefer_static",
+ ],
+}
+
+soong_config_module_type {
+ name: "library_linking_strategy_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ variables: ["library_linking_strategy"],
+ properties: ["shared_libs"],
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_lib_a_defaults",
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_a",
+ ],
+ },
+ },
+ },
+}
+
+library_linking_strategy_cc_defaults {
+ name: "library_linking_strategy_merged_defaults",
+ defaults: ["library_linking_strategy_lib_a_defaults"],
+ host_supported: true,
+ soong_config_variables: {
+ library_linking_strategy: {
+ prefer_static: {},
+ conditions_default: {
+ shared_libs: [
+ "lib_b",
+ "lib_c",
+ ],
+ },
+ },
+ },
+ exclude_shared_libs: ["lib_a"],
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary",
+ defaults: ["library_linking_strategy_merged_defaults"],
+ include_build_directory: false,
+}
+
+cc_binary {
+ name: "library_linking_strategy_sample_binary_with_excludes",
+ defaults: ["library_linking_strategy_merged_defaults"],
+ exclude_shared_libs: ["lib_c"],
+ include_build_directory: false,
+}`
+
+ otherDeps := `
+cc_library { name: "lib_a", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_b", bazel_module: { bp2build_available: false } }
+cc_library { name: "lib_c", bazel_module: { bp2build_available: false } }
+`
+
+ runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+ Description: "soong config variables - generates selects for library_linking_strategy",
+ ModuleTypeUnderTest: "cc_binary",
+ ModuleTypeUnderTestFactory: cc.BinaryFactory,
+ Blueprint: bp,
+ Filesystem: map[string]string{
+ "foo/bar/Android.bp": otherDeps,
+ },
+ ExpectedBazelTargets: []string{
+ MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary", AttrNameToString{
+ "dynamic_deps": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": [
+ "//foo/bar:lib_b",
+ "//foo/bar:lib_c",
+ ],
+ })`,
+ }),
+ MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary_with_excludes", AttrNameToString{
+ "dynamic_deps": `select({
+ "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+ "//conditions:default": ["//foo/bar:lib_b"],
+ })`,
+ }),
+ }})
+}
+
func TestSoongConfigModuleType_Defaults(t *testing.T) {
bp := `
soong_config_string_variable {
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 81ec7ee..aac5e7d 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -15,17 +15,17 @@
package bp2build
import (
- "errors"
"fmt"
- "io/fs"
"io/ioutil"
"os"
"path/filepath"
"regexp"
+ "sort"
"sync"
"sync/atomic"
"android/soong/shared"
+ "github.com/google/blueprint/pathtools"
)
// A tree structure that describes what to do at each directory in the created
@@ -46,62 +46,10 @@
topdir string // $TOPDIR
// State
- wg sync.WaitGroup
- depCh chan string
- okay atomic.Bool // Whether the forest was successfully constructed
-}
-
-// A simple thread pool to limit concurrency on system calls.
-// Necessary because Go spawns a new OS-level thread for each blocking system
-// call. This means that if syscalls are too slow and there are too many of
-// them, the hard limit on OS-level threads can be exhausted.
-type syscallPool struct {
- shutdownCh []chan<- struct{}
- workCh chan syscall
-}
-
-type syscall struct {
- work func()
- done chan<- struct{}
-}
-
-func createSyscallPool(count int) *syscallPool {
- result := &syscallPool{
- shutdownCh: make([]chan<- struct{}, count),
- workCh: make(chan syscall),
- }
-
- for i := 0; i < count; i++ {
- shutdownCh := make(chan struct{})
- result.shutdownCh[i] = shutdownCh
- go result.worker(shutdownCh)
- }
-
- return result
-}
-
-func (p *syscallPool) do(work func()) {
- doneCh := make(chan struct{})
- p.workCh <- syscall{work, doneCh}
- <-doneCh
-}
-
-func (p *syscallPool) shutdown() {
- for _, ch := range p.shutdownCh {
- ch <- struct{}{} // Blocks until the value is received
- }
-}
-
-func (p *syscallPool) worker(shutdownCh <-chan struct{}) {
- for {
- select {
- case <-shutdownCh:
- return
- case work := <-p.workCh:
- work.work()
- work.done <- struct{}{}
- }
- }
+ wg sync.WaitGroup
+ depCh chan string
+ mkdirCount atomic.Uint64
+ symlinkCount atomic.Uint64
}
// Ensures that the node for the given path exists in the tree and returns it.
@@ -169,25 +117,13 @@
generatedBuildFileContent = packageDefaultVisibilityRegex.ReplaceAll(generatedBuildFileContent, []byte{})
}
- outFile, err := os.Create(output)
- if err != nil {
- return err
+ newContents := generatedBuildFileContent
+ if newContents[len(newContents)-1] != '\n' {
+ newContents = append(newContents, '\n')
}
+ newContents = append(newContents, srcBuildFileContent...)
- _, err = outFile.Write(generatedBuildFileContent)
- if err != nil {
- return err
- }
-
- if generatedBuildFileContent[len(generatedBuildFileContent)-1] != '\n' {
- _, err = outFile.WriteString("\n")
- if err != nil {
- return err
- }
- }
-
- _, err = outFile.Write(srcBuildFileContent)
- return err
+ return pathtools.WriteFileIfChanged(output, newContents, 0666)
}
// Calls readdir() and returns it as a map from the basename of the files in dir
@@ -215,12 +151,35 @@
}
// Creates a symbolic link at dst pointing to src
-func symlinkIntoForest(topdir, dst, src string) {
- err := os.Symlink(shared.JoinPath(topdir, src), shared.JoinPath(topdir, dst))
- if err != nil {
+func symlinkIntoForest(topdir, dst, src string) uint64 {
+ srcPath := shared.JoinPath(topdir, src)
+ dstPath := shared.JoinPath(topdir, dst)
+
+ // Check if a symlink already exists.
+ if dstInfo, err := os.Lstat(dstPath); err != nil {
+ if !os.IsNotExist(err) {
+ fmt.Fprintf(os.Stderr, "Failed to lstat '%s': %s", dst, err)
+ os.Exit(1)
+ }
+ } else {
+ if dstInfo.Mode()&os.ModeSymlink != 0 {
+ // Assume that the link's target is correct, i.e. no manual tampering.
+ // E.g. OUT_DIR could have been previously used with a different source tree check-out!
+ return 0
+ } else {
+ if err := os.RemoveAll(dstPath); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to remove '%s': %s", dst, err)
+ os.Exit(1)
+ }
+ }
+ }
+
+ // Create symlink.
+ if err := os.Symlink(srcPath, dstPath); err != nil {
fmt.Fprintf(os.Stderr, "Cannot create symlink at '%s' pointing to '%s': %s", dst, src, err)
os.Exit(1)
}
+ return 1
}
func isDir(path string, fi os.FileInfo) bool {
@@ -251,8 +210,9 @@
defer context.wg.Done()
if instructions != nil && instructions.excluded {
- // This directory is not needed, bail out
- return
+ // Excluded paths are skipped at the level of the non-excluded parent.
+ fmt.Fprintf(os.Stderr, "may not specify a root-level exclude directory '%s'", srcDir)
+ os.Exit(1)
}
// We don't add buildFilesDir here because the bp2build files marker files is
@@ -270,29 +230,63 @@
renamingBuildFile = true
srcDirMap["BUILD.bazel"] = srcDirMap["BUILD"]
delete(srcDirMap, "BUILD")
+ if instructions != nil {
+ if _, ok := instructions.children["BUILD"]; ok {
+ instructions.children["BUILD.bazel"] = instructions.children["BUILD"]
+ delete(instructions.children, "BUILD")
+ }
+ }
}
}
}
- allEntries := make(map[string]struct{})
+ allEntries := make([]string, 0, len(srcDirMap)+len(buildFilesMap))
for n := range srcDirMap {
- allEntries[n] = struct{}{}
+ allEntries = append(allEntries, n)
}
-
for n := range buildFilesMap {
- allEntries[n] = struct{}{}
+ if _, ok := srcDirMap[n]; !ok {
+ allEntries = append(allEntries, n)
+ }
+ }
+ // Tests read the error messages generated, so ensure their order is deterministic
+ sort.Strings(allEntries)
+
+ fullForestPath := shared.JoinPath(context.topdir, forestDir)
+ createForestDir := false
+ if fi, err := os.Lstat(fullForestPath); err != nil {
+ if os.IsNotExist(err) {
+ createForestDir = true
+ } else {
+ fmt.Fprintf(os.Stderr, "Could not read info for '%s': %s\n", forestDir, err)
+ }
+ } else if fi.Mode()&os.ModeDir == 0 {
+ if err := os.RemoveAll(fullForestPath); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to remove '%s': %s", forestDir, err)
+ os.Exit(1)
+ }
+ createForestDir = true
+ }
+ if createForestDir {
+ if err := os.MkdirAll(fullForestPath, 0777); err != nil {
+ fmt.Fprintf(os.Stderr, "Could not mkdir '%s': %s\n", forestDir, err)
+ os.Exit(1)
+ }
+ context.mkdirCount.Add(1)
}
- err := os.MkdirAll(shared.JoinPath(context.topdir, forestDir), 0777)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Cannot mkdir '%s': %s\n", forestDir, err)
- os.Exit(1)
- }
+ // Start with a list of items that already exist in the forest, and remove
+ // each element as it is processed in allEntries. Any remaining items in
+ // forestMapForDeletion must be removed. (This handles files which were
+ // removed since the previous forest generation).
+ forestMapForDeletion := readdirToMap(shared.JoinPath(context.topdir, forestDir))
- for f := range allEntries {
+ for _, f := range allEntries {
if f[0] == '.' {
continue // Ignore dotfiles
}
+ delete(forestMapForDeletion, f)
+ // todo add deletionCount metric
// The full paths of children in the input trees and in the output tree
forestChild := shared.JoinPath(forestDir, f)
@@ -303,13 +297,9 @@
buildFilesChild := shared.JoinPath(buildFilesDir, f)
// Descend in the instruction tree if it exists
- var instructionsChild *instructionsNode = nil
+ var instructionsChild *instructionsNode
if instructions != nil {
- if f == "BUILD.bazel" && renamingBuildFile {
- instructionsChild = instructions.children["BUILD"]
- } else {
- instructionsChild = instructions.children[f]
- }
+ instructionsChild = instructions.children[f]
}
srcChildEntry, sExists := srcDirMap[f]
@@ -317,7 +307,7 @@
if instructionsChild != nil && instructionsChild.excluded {
if bExists {
- symlinkIntoForest(context.topdir, forestChild, buildFilesChild)
+ context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, buildFilesChild))
}
continue
}
@@ -333,7 +323,7 @@
go plantSymlinkForestRecursive(context, instructionsChild, forestChild, buildFilesChild, srcChild)
} else {
// Not in the source tree, symlink BUILD file
- symlinkIntoForest(context.topdir, forestChild, buildFilesChild)
+ context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, buildFilesChild))
}
} else if !bExists {
if sDir && instructionsChild != nil {
@@ -343,7 +333,7 @@
go plantSymlinkForestRecursive(context, instructionsChild, forestChild, buildFilesChild, srcChild)
} else {
// Not in the build file tree, symlink source tree, carry on
- symlinkIntoForest(context.topdir, forestChild, srcChild)
+ context.symlinkCount.Add(symlinkIntoForest(context.topdir, forestChild, srcChild))
}
} else if sDir && bDir {
// Both are directories. Descend.
@@ -356,82 +346,55 @@
// The Android.bp file that codegen used to produce `buildFilesChild` is
// already a dependency, we can ignore `buildFilesChild`.
context.depCh <- srcChild
- err = mergeBuildFiles(shared.JoinPath(context.topdir, forestChild), srcBuildFile, generatedBuildFile, context.verbose)
- if err != nil {
+ if err := mergeBuildFiles(shared.JoinPath(context.topdir, forestChild), srcBuildFile, generatedBuildFile, context.verbose); err != nil {
fmt.Fprintf(os.Stderr, "Error merging %s and %s: %s",
srcBuildFile, generatedBuildFile, err)
- context.okay.Store(false)
+ os.Exit(1)
}
} else {
// Both exist and one is a file. This is an error.
fmt.Fprintf(os.Stderr,
"Conflict in workspace symlink tree creation: both '%s' and '%s' exist and exactly one is a directory\n",
srcChild, buildFilesChild)
- context.okay.Store(false)
- }
- }
-}
-
-func removeParallelRecursive(pool *syscallPool, path string, fi os.FileInfo, wg *sync.WaitGroup) {
- defer wg.Done()
-
- if fi.IsDir() {
- children := readdirToMap(path)
- childrenWg := &sync.WaitGroup{}
- childrenWg.Add(len(children))
-
- for child, childFi := range children {
- go removeParallelRecursive(pool, shared.JoinPath(path, child), childFi, childrenWg)
- }
-
- childrenWg.Wait()
- }
-
- pool.do(func() {
- if err := os.Remove(path); err != nil {
- fmt.Fprintf(os.Stderr, "Cannot unlink '%s': %s\n", path, err)
os.Exit(1)
}
- })
-}
-
-func removeParallel(path string) {
- fi, err := os.Lstat(path)
- if err != nil {
- if errors.Is(err, fs.ErrNotExist) {
- return
- }
-
- fmt.Fprintf(os.Stderr, "Cannot lstat '%s': %s\n", path, err)
- os.Exit(1)
}
- wg := &sync.WaitGroup{}
- wg.Add(1)
+ // Remove all files in the forest that exist in neither the source
+ // tree nor the build files tree. (This handles files which were removed
+ // since the previous forest generation).
+ for f := range forestMapForDeletion {
+ var instructionsChild *instructionsNode
+ if instructions != nil {
+ instructionsChild = instructions.children[f]
+ }
- // Random guess as to the best number of syscalls to run in parallel
- pool := createSyscallPool(100)
- removeParallelRecursive(pool, path, fi, wg)
- pool.shutdown()
-
- wg.Wait()
+ if instructionsChild != nil && instructionsChild.excluded {
+ // This directory may be excluded because bazel writes to it under the
+ // forest root. Thus this path is intentionally left alone.
+ continue
+ }
+ forestChild := shared.JoinPath(context.topdir, forestDir, f)
+ if err := os.RemoveAll(forestChild); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to remove '%s/%s': %s", forestDir, f, err)
+ os.Exit(1)
+ }
+ }
}
-// Creates a symlink forest by merging the directory tree at "buildFiles" and
+// PlantSymlinkForest Creates a symlink forest by merging the directory tree at "buildFiles" and
// "srcDir" while excluding paths listed in "exclude". Returns the set of paths
// under srcDir on which readdir() had to be called to produce the symlink
// forest.
-func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles string, exclude []string) []string {
+func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles string, exclude []string) (deps []string, mkdirCount, symlinkCount uint64) {
context := &symlinkForestContext{
- verbose: verbose,
- topdir: topdir,
- depCh: make(chan string),
+ verbose: verbose,
+ topdir: topdir,
+ depCh: make(chan string),
+ mkdirCount: atomic.Uint64{},
+ symlinkCount: atomic.Uint64{},
}
- context.okay.Store(true)
-
- removeParallel(shared.JoinPath(topdir, forest))
-
instructions := instructionsFromExcludePathList(exclude)
go func() {
context.wg.Add(1)
@@ -440,14 +403,9 @@
close(context.depCh)
}()
- deps := make([]string, 0)
for dep := range context.depCh {
deps = append(deps, dep)
}
- if !context.okay.Load() {
- os.Exit(1)
- }
-
- return deps
+ return deps, context.mkdirCount.Load(), context.symlinkCount.Load()
}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 1f9874c..92a9bf1 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -258,6 +258,7 @@
}
func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) {
+ t.Helper()
if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount {
t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
description, expectedCount, expectedContents, actualCount, actualTargets)
@@ -453,6 +454,14 @@
}
}
}
+ productVariableProps := android.ProductVariableProperties(ctx)
+ if props, ok := productVariableProps["String_literal_prop"]; ok {
+ for c, p := range props {
+ if val, ok := p.(*string); ok {
+ strAttr.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
+ }
+ }
+ }
paths.ResolveExcludes()
diff --git a/cc/Android.bp b/cc/Android.bp
index 8860f78..5fd9afe 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -52,7 +52,6 @@
"vndk.go",
"vndk_prebuilt.go",
- "cflag_artifacts.go",
"cmakelists.go",
"compdb.go",
"compiler.go",
diff --git a/cc/api_level.go b/cc/api_level.go
index fdff5cb..a5571f3 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -20,7 +20,9 @@
"android/soong/android"
)
-func minApiForArch(ctx android.EarlyModuleContext,
+// MinApiLevelForArch returns the ApiLevel for the Android version that
+// first supported the architecture.
+func MinApiForArch(ctx android.EarlyModuleContext,
arch android.ArchType) android.ApiLevel {
switch arch {
@@ -38,7 +40,7 @@
func nativeApiLevelFromUser(ctx android.BaseModuleContext,
raw string) (android.ApiLevel, error) {
- min := minApiForArch(ctx, ctx.Arch().ArchType)
+ min := MinApiForArch(ctx, ctx.Arch().ArchType)
if raw == "minimum" {
return min, nil
}
diff --git a/cc/binary.go b/cc/binary.go
index 54c1abc..a04b174 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -588,9 +588,15 @@
return
}
- outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, info.OutputFile)
+ if len(info.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, info.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
handler.module.linker.(*binaryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+ handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
}
func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
diff --git a/cc/binary_test.go b/cc/binary_test.go
index 43aff5c..e0b5b5d 100644
--- a/cc/binary_test.go
+++ b/cc/binary_test.go
@@ -55,6 +55,47 @@
android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
}
+func TestCcBinaryWithBazelValidations(t *testing.T) {
+ t.Parallel()
+ bp := `
+cc_binary {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+ "//foo/bar:bar": cquery.CcUnstrippedInfo{
+ OutputFile: "foo",
+ UnstrippedOutput: "foo.unstripped",
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ ).RunTestWithConfig(t, config).TestContext
+
+ binMod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module()
+ producer := binMod.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_binary outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm64_armv8-a/validated/foo"}
+ android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+ unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+ expectedUnStrippedFile := "outputbase/execroot/__main__/foo.unstripped"
+ android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+}
+
func TestBinaryLinkerScripts(t *testing.T) {
t.Parallel()
result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
diff --git a/cc/bp2build.go b/cc/bp2build.go
index d331d89..6c5505a 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -91,12 +91,12 @@
}
archVariantProps := m.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
for axis, configToProps := range archVariantProps {
- for config, _props := range configToProps {
+ for cfg, _props := range configToProps {
if archProps, ok := _props.(*BaseCompilerProperties); ok {
archDisabledSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_disabled_srcs)
- moduleAttrs.Tidy_disabled_srcs.SetSelectValue(axis, config, archDisabledSrcs)
+ moduleAttrs.Tidy_disabled_srcs.SetSelectValue(axis, cfg, archDisabledSrcs)
archTimeoutSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_timeout_srcs)
- moduleAttrs.Tidy_timeout_srcs.SetSelectValue(axis, config, archTimeoutSrcs)
+ moduleAttrs.Tidy_timeout_srcs.SetSelectValue(axis, cfg, archTimeoutSrcs)
}
}
}
@@ -205,14 +205,14 @@
func bp2BuildPropParseHelper(ctx android.ArchVariantContext, module *Module, propsType interface{}, parseFunc func(axis bazel.ConfigurationAxis, config string, props interface{})) {
for axis, configToProps := range module.GetArchVariantProperties(ctx, propsType) {
- for config, props := range configToProps {
- parseFunc(axis, config, props)
+ for cfg, props := range configToProps {
+ parseFunc(axis, cfg, props)
}
}
}
// Parses properties common to static and shared libraries. Also used for prebuilt libraries.
-func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
+func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, _ *libraryDecorator, isStatic bool) staticOrSharedAttributes {
attrs := staticOrSharedAttributes{}
setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
@@ -669,8 +669,8 @@
ret := &bazel.LabelAttribute{}
for _, axis := range ca.asmSrcs.SortedConfigurationAxes() {
- for config := range ca.asmSrcs.ConfigurableValues[axis] {
- ret.SetSelectValue(axis, config, bazel.Label{Label: ":" + m.Name() + "_yasm"})
+ for cfg := range ca.asmSrcs.ConfigurableValues[axis] {
+ ret.SetSelectValue(axis, cfg, bazel.Label{Label: ":" + m.Name() + "_yasm"})
}
}
return ret
@@ -690,8 +690,8 @@
if _, ok := axisToConfigs[axis]; !ok {
axisToConfigs[axis] = map[string]bool{}
}
- for config, _ := range configMap {
- axisToConfigs[axis][config] = true
+ for cfg := range configMap {
+ axisToConfigs[axis][cfg] = true
}
}
}
@@ -702,50 +702,56 @@
compilerAttrs := compilerAttributes{}
linkerAttrs := linkerAttributes{}
- for axis, configs := range axisToConfigs {
- for config, _ := range configs {
+ // Iterate through these axes in a deterministic order. This is required
+ // because processing certain dependencies may result in concatenating
+ // elements along other axes. (For example, processing NoConfig may result
+ // in elements being added to InApex). This is thus the only way to ensure
+ // that the order of entries in each list is in a predictable order.
+ for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
+ configs := axisToConfigs[axis]
+ for cfg := range configs {
var allHdrs []string
- if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
+ if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
allHdrs = baseCompilerProps.Generated_headers
if baseCompilerProps.Lex != nil {
- compilerAttrs.lexopts.SetSelectValue(axis, config, baseCompilerProps.Lex.Flags)
+ compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
}
- (&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
+ (&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, cfg, baseCompilerProps)
}
var exportHdrs []string
- if baseLinkerProps, ok := archVariantLinkerProps[axis][config].(*BaseLinkerProperties); ok {
+ if baseLinkerProps, ok := archVariantLinkerProps[axis][cfg].(*BaseLinkerProperties); ok {
exportHdrs = baseLinkerProps.Export_generated_headers
- (&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, config, baseLinkerProps)
+ (&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, cfg, baseLinkerProps)
}
headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
- implementationHdrs.SetSelectValue(axis, config, headers.implementation)
- compilerAttrs.hdrs.SetSelectValue(axis, config, headers.export)
+ implementationHdrs.SetSelectValue(axis, cfg, headers.implementation)
+ compilerAttrs.hdrs.SetSelectValue(axis, cfg, headers.export)
exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
- compilerAttrs.includes.Includes.SetSelectValue(axis, config, exportIncludes)
- compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, config, exportAbsoluteIncludes)
+ compilerAttrs.includes.Includes.SetSelectValue(axis, cfg, exportIncludes)
+ compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, cfg, exportAbsoluteIncludes)
includes, absoluteIncludes := includesFromLabelList(headers.implementation)
- currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, config)
+ currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, cfg)
currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
- compilerAttrs.absoluteIncludes.SetSelectValue(axis, config, currAbsoluteIncludes)
+ compilerAttrs.absoluteIncludes.SetSelectValue(axis, cfg, currAbsoluteIncludes)
- currIncludes := compilerAttrs.localIncludes.SelectValue(axis, config)
+ currIncludes := compilerAttrs.localIncludes.SelectValue(axis, cfg)
currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
- compilerAttrs.localIncludes.SetSelectValue(axis, config, currIncludes)
+ compilerAttrs.localIncludes.SetSelectValue(axis, cfg, currIncludes)
- if libraryProps, ok := archVariantLibraryProperties[axis][config].(*LibraryProperties); ok {
+ if libraryProps, ok := archVariantLibraryProperties[axis][cfg].(*LibraryProperties); ok {
if axis == bazel.NoConfigAxis {
compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
- compilerAttrs.stubsVersions.SetSelectValue(axis, config, libraryProps.Stubs.Versions)
+ compilerAttrs.stubsVersions.SetSelectValue(axis, cfg, libraryProps.Stubs.Versions)
}
if suffix := libraryProps.Suffix; suffix != nil {
- compilerAttrs.suffix.SetSelectValue(axis, config, suffix)
+ compilerAttrs.suffix.SetSelectValue(axis, cfg, suffix)
}
}
}
@@ -813,6 +819,8 @@
features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
features.DeduplicateAxesFromBase()
+ addMuslSystemDynamicDeps(ctx, linkerAttrs)
+
return baseAttributes{
compilerAttrs,
linkerAttrs,
@@ -823,6 +831,16 @@
}
}
+// As a workaround for b/261657184, we are manually adding the default value
+// of system_dynamic_deps for the linux_musl os.
+// TODO: Solve this properly
+func addMuslSystemDynamicDeps(ctx android.Bp2buildMutatorContext, attrs linkerAttributes) {
+ systemDynamicDeps := attrs.systemDynamicDeps.SelectValue(bazel.OsConfigurationAxis, "linux_musl")
+ if attrs.systemDynamicDeps.HasAxisSpecificValues(bazel.OsConfigurationAxis) && systemDynamicDeps.IsNil() {
+ attrs.systemDynamicDeps.SetSelectValue(bazel.OsConfigurationAxis, "linux_musl", android.BazelLabelForModuleDeps(ctx, config.MuslDefaultSharedLibraries))
+ }
+}
+
type fdoProfileAttributes struct {
Absolute_path_profile string
}
@@ -967,34 +985,22 @@
// resolveTargetApex re-adds the shared and static libs in target.apex.exclude_shared|static_libs props to non-apex variant
// since all libs are already excluded by default
-func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, isBinary bool, props *BaseLinkerProperties) {
- sharedLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
- ctx,
- true,
- props.Target.Apex.Exclude_shared_libs,
- props.Export_shared_lib_headers,
- bazelLabelForSharedDeps,
- )
- dynamicDeps := la.dynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- implDynamicDeps := la.implementationDynamicDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- (&dynamicDeps).Append(sharedLibsForNonApex.export)
- (&implDynamicDeps).Append(sharedLibsForNonApex.implementation)
- la.dynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, dynamicDeps)
- la.implementationDynamicDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDynamicDeps)
+func (la *linkerAttributes) resolveTargetApexProp(ctx android.BazelConversionPathContext, props *BaseLinkerProperties) {
+ excludeSharedLibs := bazelLabelForSharedDeps(ctx, props.Target.Apex.Exclude_shared_libs)
+ sharedExcludes := bazel.LabelList{Excludes: excludeSharedLibs.Includes}
+ sharedExcludesLabelList := bazel.LabelListAttribute{}
+ sharedExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, sharedExcludes)
- staticLibsForNonApex := maybePartitionExportedAndImplementationsDeps(
- ctx,
- !isBinary,
- props.Target.Apex.Exclude_static_libs,
- props.Export_static_lib_headers,
- bazelLabelForSharedDeps,
- )
- deps := la.deps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- implDeps := la.implementationDeps.SelectValue(bazel.InApexAxis, bazel.NonApex)
- (&deps).Append(staticLibsForNonApex.export)
- (&implDeps).Append(staticLibsForNonApex.implementation)
- la.deps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, deps)
- la.implementationDeps.SetSelectValue(bazel.InApexAxis, bazel.NonApex, implDeps)
+ la.dynamicDeps.Append(sharedExcludesLabelList)
+ la.implementationDynamicDeps.Append(sharedExcludesLabelList)
+
+ excludeStaticLibs := bazelLabelForStaticDeps(ctx, props.Target.Apex.Exclude_static_libs)
+ staticExcludes := bazel.LabelList{Excludes: excludeStaticLibs.Includes}
+ staticExcludesLabelList := bazel.LabelListAttribute{}
+ staticExcludesLabelList.SetSelectValue(bazel.InApexAxis, bazel.InApex, staticExcludes)
+
+ la.deps.Append(staticExcludesLabelList)
+ la.implementationDeps.Append(staticExcludesLabelList)
}
func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
@@ -1028,8 +1034,7 @@
ctx,
!isBinary,
staticLibs,
- // Exclude static libs in Exclude_static_libs and Target.Apex.Exclude_static_libs props
- append(props.Exclude_static_libs, props.Target.Apex.Exclude_static_libs...),
+ props.Exclude_static_libs,
props.Export_static_lib_headers,
bazelLabelForStaticDepsExcludes,
)
@@ -1068,14 +1073,13 @@
ctx,
!isBinary,
sharedLibs,
- // Exclude shared libs in Exclude_shared_libs and Target.Apex.Exclude_shared_libs props
- append(props.Exclude_shared_libs, props.Target.Apex.Exclude_shared_libs...),
+ props.Exclude_shared_libs,
props.Export_shared_lib_headers,
bazelLabelForSharedDepsExcludes,
)
la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
- la.resolveTargetApexProp(ctx, isBinary, props)
+ la.resolveTargetApexProp(ctx, props)
if axis == bazel.NoConfigAxis || (axis == bazel.OsConfigurationAxis && config == bazel.OsAndroid) {
// If a dependency in la.implementationDynamicDeps has stubs, its stub variant should be
@@ -1213,7 +1217,7 @@
for name, dep := range productVarToDepFields {
props, exists := productVariableProps[name]
excludeProps, excludesExists := productVariableProps[dep.excludesField]
- // if neither an include or excludes property exists, then skip it
+ // if neither an include nor excludes property exists, then skip it
if !exists && !excludesExists {
continue
}
@@ -1352,7 +1356,7 @@
func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
label := android.BazelModuleLabel(ctx, m)
- if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(m.Name()) {
+ if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary {
return BazelLabelNameForStaticModule(label)
}
return label
diff --git a/cc/builder.go b/cc/builder.go
index 0629406..fef00d4 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -519,6 +519,13 @@
cppflags += " ${config.NoOverrideGlobalCflags}"
toolingCppflags += " ${config.NoOverrideGlobalCflags}"
+ if flags.toolchain.Is64Bit() {
+ cflags += " ${config.NoOverride64GlobalCflags}"
+ toolingCflags += " ${config.NoOverride64GlobalCflags}"
+ cppflags += " ${config.NoOverride64GlobalCflags}"
+ toolingCppflags += " ${config.NoOverride64GlobalCflags}"
+ }
+
modulePath := android.PathForModuleSrc(ctx).String()
if android.IsThirdPartyPath(modulePath) {
cflags += " ${config.NoOverrideExternalGlobalCflags}"
diff --git a/cc/cc.go b/cc/cc.go
index cb425c3..c81160d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -28,6 +28,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel/cquery"
"android/soong/cc/config"
"android/soong/fuzz"
"android/soong/genrule"
@@ -1065,6 +1066,31 @@
return false
}
+func (c *Module) IsFuzzModule() bool {
+ if _, ok := c.compiler.(*fuzzBinary); ok {
+ return true
+ }
+ return false
+}
+
+func (c *Module) FuzzModuleStruct() fuzz.FuzzModule {
+ return c.FuzzModule
+}
+
+func (c *Module) FuzzPackagedModule() fuzz.FuzzPackagedModule {
+ if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
+ return fuzzer.fuzzPackagedModule
+ }
+ panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) FuzzSharedLibraries() android.Paths {
+ if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
+ return fuzzer.sharedLibraries
+ }
+ panic(fmt.Errorf("FuzzSharedLibraries called on non-fuzz module: %q", c.BaseModuleName()))
+}
+
func (c *Module) NonCcVariants() bool {
return false
}
@@ -1445,6 +1471,8 @@
}
func InstallToBootstrap(name string, config android.Config) bool {
+ // NOTE: also update //build/bazel/rules/apex/cc.bzl#_installed_to_bootstrap
+ // if this list is updated.
if name == "libclang_rt.hwasan" {
return true
}
@@ -1859,6 +1887,10 @@
var (
mixedBuildSupportedCcTest = []string{
"adbd_test",
+ "adb_crypto_test",
+ "adb_pairing_auth_test",
+ "adb_pairing_connection_test",
+ "adb_tls_connection_test",
}
)
@@ -1866,7 +1898,8 @@
// in any of the --bazel-mode(s). This filters at the module level and takes
// precedence over the allowlists in allowlists/allowlists.go.
func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
- if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) {
+ _, isForTesting := ctx.Config().BazelContext.(android.MockBazelContext)
+ if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) && !isForTesting {
// Per-module rollout of mixed-builds for cc_test modules.
return false
}
@@ -1880,12 +1913,6 @@
func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
bazelModuleLabel := c.getBazelModuleLabel(ctx)
-
- bazelCtx := ctx.Config().BazelContext
- if ccInfo, err := bazelCtx.GetCcInfo(bazelModuleLabel, android.GetConfigKey(ctx)); err == nil {
- c.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
- }
-
c.bazelHandler.ProcessBazelQueryResponse(ctx, bazelModuleLabel)
c.Properties.SubName = GetSubnameProperty(ctx, c)
@@ -2081,6 +2108,12 @@
}
}
+func (c *Module) setAndroidMkVariablesFromCquery(info cquery.CcAndroidMkInfo) {
+ c.Properties.AndroidMkSharedLibs = info.LocalSharedLibs
+ c.Properties.AndroidMkStaticLibs = info.LocalStaticLibs
+ c.Properties.AndroidMkWholeStaticLibs = info.LocalWholeStaticLibs
+}
+
func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
if c.cachedToolchain == nil {
c.cachedToolchain = config.FindToolchainWithContext(ctx)
@@ -2212,6 +2245,13 @@
if err != nil {
ctx.PropertyErrorf("min_sdk_version", err.Error())
}
+
+ // Raise the minSdkVersion to the minimum supported for the architecture.
+ minApiForArch := MinApiForArch(ctx, m.Target().Arch.ArchType)
+ if apiLevel.LessThan(minApiForArch) {
+ apiLevel = minApiForArch
+ }
+
return []blueprint.Variation{
{Mutator: "sdk", Variation: "sdk"},
{Mutator: "version", Variation: apiLevel.String()},
@@ -3693,7 +3733,7 @@
// This allows introducing new architectures in the platform that
// need to be included in apexes that normally require an older
// min_sdk_version.
- minApiForArch := minApiForArch(ctx, c.Target().Arch.ArchType)
+ minApiForArch := MinApiForArch(ctx, c.Target().Arch.ArchType)
if sdkVersion.LessThan(minApiForArch) {
sdkVersion = minApiForArch
}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 6dfd395..62adfd3 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -25,6 +25,7 @@
"testing"
"android/soong/android"
+ "android/soong/bazel/cquery"
)
func TestMain(m *testing.M) {
@@ -3054,6 +3055,253 @@
checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
}
+func TestLibDepAndroidMkExportInMixedBuilds(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "static_dep",
+ }
+ cc_library {
+ name: "whole_static_dep",
+ }
+ cc_library {
+ name: "shared_dep",
+ }
+ cc_library {
+ name: "lib",
+ bazel_module: { label: "//:lib" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_test {
+ name: "test",
+ bazel_module: { label: "//:test" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ gtest: false,
+ }
+ cc_binary {
+ name: "binary",
+ bazel_module: { label: "//:binary" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_library_headers {
+ name: "lib_headers",
+ bazel_module: { label: "//:lib_headers" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ cc_prebuilt_library {
+ name: "lib_prebuilt",
+ bazel_module: { label: "//:lib_prebuilt" },
+ static_libs: ["static_dep"],
+ whole_static_libs: ["whole_static_dep"],
+ shared_libs: ["shared_dep"],
+ }
+ `
+
+ testCases := []struct {
+ name string
+ moduleName string
+ variant string
+ androidMkInfo cquery.CcAndroidMkInfo
+ }{
+ {
+ name: "shared lib",
+ moduleName: "lib",
+ variant: "android_arm64_armv8-a_shared",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "static lib",
+ moduleName: "lib",
+ variant: "android_arm64_armv8-a_static",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_test arm64",
+ moduleName: "test",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_test arm",
+ moduleName: "test",
+ variant: "android_arm_armv7-a-neon",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_binary",
+ moduleName: "binary",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "cc_library_headers",
+ moduleName: "lib_headers",
+ variant: "android_arm64_armv8-a",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "prebuilt lib static",
+ moduleName: "lib_prebuilt",
+ variant: "android_arm64_armv8-a_static",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ {
+ name: "prebuilt lib shared",
+ moduleName: "lib_prebuilt",
+ variant: "android_arm64_armv8-a_shared",
+ androidMkInfo: cquery.CcAndroidMkInfo{
+ LocalStaticLibs: []string{"static_dep"},
+ LocalWholeStaticLibs: []string{"whole_static_dep"},
+ LocalSharedLibs: []string{"shared_dep"},
+ },
+ },
+ }
+
+ outputBaseDir := "out/bazel"
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outputBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//:lib": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ RootDynamicLibraries: []string{""},
+ },
+ "//:lib_bp2build_cc_library_static": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ RootStaticArchives: []string{""},
+ },
+ "//:lib_headers": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ OutputFiles: []string{""},
+ },
+ "//:lib_prebuilt": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ "//:lib_prebuilt_bp2build_cc_library_static": cquery.CcInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ },
+ LabelToCcBinary: map[string]cquery.CcUnstrippedInfo{
+ "//:test": cquery.CcUnstrippedInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ "//:binary": cquery.CcUnstrippedInfo{
+ CcAndroidMkInfo: tc.androidMkInfo,
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ ctx := result.TestContext
+
+ module := ctx.ModuleForTests(tc.moduleName, tc.variant).Module().(*Module)
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ if !reflect.DeepEqual(module.Properties.AndroidMkStaticLibs, tc.androidMkInfo.LocalStaticLibs) {
+ t.Errorf("incorrect static_libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ module.Properties.AndroidMkStaticLibs,
+ tc.androidMkInfo.LocalStaticLibs,
+ )
+ }
+ staticDepsDiffer, missingStaticDeps, additionalStaticDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+ tc.androidMkInfo.LocalStaticLibs,
+ )
+ if staticDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+ tc.androidMkInfo.LocalStaticLibs,
+ entries.EntryMap["LOCAL_STATIC_LIBRARIES"],
+ missingStaticDeps,
+ additionalStaticDeps,
+ )
+ }
+
+ if !reflect.DeepEqual(module.Properties.AndroidMkWholeStaticLibs, tc.androidMkInfo.LocalWholeStaticLibs) {
+ t.Errorf("expected module.Properties.AndroidMkWholeStaticLibs to be %q, but was %q",
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ module.Properties.AndroidMkWholeStaticLibs,
+ )
+ }
+ wholeStaticDepsDiffer, missingWholeStaticDeps, additionalWholeStaticDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ )
+ if wholeStaticDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_WHOLE_STATIC_LIBRARIES to be %q but was %q; missing: %q; extra %q",
+ tc.androidMkInfo.LocalWholeStaticLibs,
+ entries.EntryMap["LOCAL_WHOLE_STATIC_LIBRARIES"],
+ missingWholeStaticDeps,
+ additionalWholeStaticDeps,
+ )
+ }
+
+ if !reflect.DeepEqual(module.Properties.AndroidMkSharedLibs, tc.androidMkInfo.LocalSharedLibs) {
+ t.Errorf("incorrect shared_libs"+
+ "\nactual: %v"+
+ "\nexpected: %v",
+ module.Properties.AndroidMkSharedLibs,
+ tc.androidMkInfo.LocalSharedLibs,
+ )
+ }
+ sharedDepsDiffer, missingSharedDeps, additionalSharedDeps := android.ListSetDifference(
+ entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+ tc.androidMkInfo.LocalSharedLibs,
+ )
+ if sharedDepsDiffer {
+ t.Errorf(
+ "expected LOCAL_SHARED_LIBRARIES to be %q but was %q; missing %q; extra %q",
+ tc.androidMkInfo.LocalSharedLibs,
+ entries.EntryMap["LOCAL_SHARED_LIBRARIES"],
+ missingSharedDeps,
+ additionalSharedDeps,
+ )
+ }
+ })
+ }
+}
+
var compilerFlagsTestCases = []struct {
in string
out bool
@@ -4483,6 +4731,39 @@
}
+func TestAddnoOverride64GlobalCflags(t *testing.T) {
+ t.Parallel()
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libclient",
+ srcs: ["foo.c"],
+ shared_libs: ["libfoo#1"],
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ shared_libs: ["libbar"],
+ export_shared_lib_headers: ["libbar"],
+ stubs: {
+ symbol_file: "foo.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ cc_library_shared {
+ name: "libbar",
+ export_include_dirs: ["include/libbar"],
+ srcs: ["foo.c"],
+ }`)
+
+ cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+
+ if !strings.Contains(cFlags, "${config.NoOverride64GlobalCflags}") {
+ t.Errorf("expected %q in cflags, got %q", "${config.NoOverride64GlobalCflags}", cFlags)
+ }
+}
+
func TestCcBuildBrokenClangProperty(t *testing.T) {
t.Parallel()
tests := []struct {
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
deleted file mode 100644
index be46fc0..0000000
--- a/cc/cflag_artifacts.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package cc
-
-import (
- "fmt"
- "sort"
- "strings"
-
- "github.com/google/blueprint/proptools"
-
- "android/soong/android"
-)
-
-func init() {
- android.RegisterSingletonType("cflag_artifacts_text", cflagArtifactsTextFactory)
-}
-
-var (
- TrackedCFlags = []string{
- "-Wall",
- "-Werror",
- "-Wextra",
- "-Wthread-safety",
- "-O3",
- }
-
- TrackedCFlagsDir = []string{
- "device/google/",
- "vendor/google/",
- }
-)
-
-const FileBP = 50
-
-// Stores output files.
-type cflagArtifactsText struct {
- interOutputs map[string]android.WritablePaths
- outputs android.WritablePaths
-}
-
-// allowedDir verifies if the directory/project is part of the TrackedCFlagsDir
-// filter.
-func allowedDir(subdir string) bool {
- subdir += "/"
- return android.HasAnyPrefix(subdir, TrackedCFlagsDir)
-}
-
-func (s *cflagArtifactsText) genFlagFilename(flag string) string {
- return fmt.Sprintf("module_cflags%s.txt", flag)
-}
-
-// incrementFile is used to generate an output path object with the passed in flag
-// and part number.
-// e.g. FLAG + part # -> out/soong/cflags/module_cflags-FLAG.txt.0
-func (s *cflagArtifactsText) incrementFile(ctx android.SingletonContext,
- flag string, part int) (string, android.OutputPath) {
-
- filename := fmt.Sprintf("%s.%d", s.genFlagFilename(flag), part)
- filepath := android.PathForOutput(ctx, "cflags", filename)
- s.interOutputs[flag] = append(s.interOutputs[flag], filepath)
- return filename, filepath
-}
-
-// GenCFlagArtifactParts is used to generate the build rules which produce the
-// intermediary files for each desired C Flag artifact
-// e.g. module_cflags-FLAG.txt.0, module_cflags-FLAG.txt.1, ...
-func (s *cflagArtifactsText) GenCFlagArtifactParts(ctx android.SingletonContext,
- flag string, using bool, modules []string, part int) int {
-
- cleanedName := strings.Replace(flag, "=", "_", -1)
- filename, filepath := s.incrementFile(ctx, cleanedName, part)
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().Textf("rm -f %s", filepath.String())
-
- if using {
- rule.Command().
- Textf("echo '# Modules using %s'", flag).
- FlagWithOutput(">> ", filepath)
- } else {
- rule.Command().
- Textf("echo '# Modules not using %s'", flag).
- FlagWithOutput(">> ", filepath)
- }
-
- length := len(modules)
-
- if length == 0 {
- rule.Build(filename, "gen "+filename)
- part++
- }
-
- // Following loop splits the module list for each tracked C Flag into
- // chunks of length FileBP (file breakpoint) and generates a partial artifact
- // (intermediary file) build rule for each split.
- moduleShards := android.ShardStrings(modules, FileBP)
- for index, shard := range moduleShards {
- rule.Command().
- Textf("for m in %s; do echo $m",
- strings.Join(proptools.ShellEscapeList(shard), " ")).
- FlagWithOutput(">> ", filepath).
- Text("; done")
- rule.Build(filename, "gen "+filename)
-
- if index+1 != len(moduleShards) {
- filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
- rule = android.NewRuleBuilder(pctx, ctx)
- rule.Command().Textf("rm -f %s", filepath.String())
- }
- }
-
- return part + len(moduleShards)
-}
-
-// GenCFlagArtifacts is used to generate build rules which combine the
-// intermediary files of a specific tracked flag into a single C Flag artifact
-// for each tracked flag.
-// e.g. module_cflags-FLAG.txt.0 + module_cflags-FLAG.txt.1 = module_cflags-FLAG.txt
-func (s *cflagArtifactsText) GenCFlagArtifacts(ctx android.SingletonContext) {
- // Scans through s.interOutputs and creates a build rule for each tracked C
- // Flag that concatenates the associated intermediary file into a single
- // artifact.
- for _, flag := range TrackedCFlags {
- // Generate build rule to combine related intermediary files into a
- // C Flag artifact
- rule := android.NewRuleBuilder(pctx, ctx)
- filename := s.genFlagFilename(flag)
- outputpath := android.PathForOutput(ctx, "cflags", filename)
- rule.Command().
- Text("cat").
- Inputs(s.interOutputs[flag].Paths()).
- FlagWithOutput("> ", outputpath)
- rule.Build(filename, "gen "+filename)
- s.outputs = append(s.outputs, outputpath)
- }
-}
-
-func (s *cflagArtifactsText) GenerateBuildActions(ctx android.SingletonContext) {
- modulesWithCFlag := make(map[string][]string)
-
- // Scan through all modules, selecting the ones that are part of the filter,
- // and then storing into a map which tracks whether or not tracked C flag is
- // used or not.
- ctx.VisitAllModules(func(module android.Module) {
- if ccModule, ok := module.(*Module); ok {
- if allowedDir(ctx.ModuleDir(ccModule)) {
- cflags := ccModule.flags.Local.CFlags
- cppflags := ccModule.flags.Local.CppFlags
- module := fmt.Sprintf("%s:%s (%s)",
- ctx.BlueprintFile(ccModule),
- ctx.ModuleName(ccModule),
- ctx.ModuleSubDir(ccModule))
- for _, flag := range TrackedCFlags {
- if inList(flag, cflags) || inList(flag, cppflags) {
- modulesWithCFlag[flag] = append(modulesWithCFlag[flag], module)
- } else {
- modulesWithCFlag["!"+flag] = append(modulesWithCFlag["!"+flag], module)
- }
- }
- }
- }
- })
-
- // Traversing map and setting up rules to produce intermediary files which
- // contain parts of each expected C Flag artifact.
- for _, flag := range TrackedCFlags {
- sort.Strings(modulesWithCFlag[flag])
- part := s.GenCFlagArtifactParts(ctx, flag, true, modulesWithCFlag[flag], 0)
- sort.Strings(modulesWithCFlag["!"+flag])
- s.GenCFlagArtifactParts(ctx, flag, false, modulesWithCFlag["!"+flag], part)
- }
-
- // Combine intermediary files into a single C Flag artifact.
- s.GenCFlagArtifacts(ctx)
-}
-
-func cflagArtifactsTextFactory() android.Singleton {
- return &cflagArtifactsText{
- interOutputs: make(map[string]android.WritablePaths),
- }
-}
-
-func (s *cflagArtifactsText) MakeVars(ctx android.MakeVarsContext) {
- ctx.Strict("SOONG_MODULES_CFLAG_ARTIFACTS", strings.Join(s.outputs.Strings(), " "))
-}
diff --git a/cc/compiler.go b/cc/compiler.go
index a751754..88985b6 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -416,11 +416,6 @@
if ctx.apexVariationName() != "" {
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
- if ctx.Device() {
- flags.Global.CommonFlags = append(flags.Global.CommonFlags,
- fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
- ctx.apexSdkVersion().FinalOrFutureInt()))
- }
}
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
diff --git a/cc/config/global.go b/cc/config/global.go
index e78839b..454a4db 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -207,10 +207,7 @@
"-Werror=fortify-source",
"-Werror=address-of-temporary",
- // Bug: http://b/29823425 Disable -Wnull-dereference until the
- // new cases detected by this warning in Clang r271374 are
- // fixed.
- //"-Werror=null-dereference",
+ "-Werror=null-dereference",
"-Werror=return-type",
// http://b/72331526 Disable -Wtautological-* until the instances detected by these
@@ -247,6 +244,8 @@
"-Wno-error=enum-constexpr-conversion", // http://b/243964282
}
+ noOverride64GlobalCflags = []string{}
+
noOverrideExternalGlobalCflags = []string{
// http://b/148815709
"-Wno-sizeof-array-div",
@@ -277,7 +276,6 @@
// http://b/145211477
"-Wno-pointer-compare",
- // http://b/145211022
"-Wno-final-dtor-non-final-class",
// http://b/165945989
@@ -291,6 +289,9 @@
// http://b/239661264
"-Wno-deprecated-non-prototype",
+
+ // http://b/191699019
+ "-Wno-format-insufficient-args",
}
llvmNextExtraCommonGlobalCflags = []string{
@@ -369,10 +370,6 @@
flags = append(flags, "-Wno-unused-command-line-argument")
}
- if ctx.Config().IsEnvTrue("LLVM_NEXT") {
- flags = append(flags, llvmNextExtraCommonGlobalCflags...)
- }
-
if ctx.Config().IsEnvTrue("ALLOW_UNKNOWN_WARNING_OPTION") {
flags = append(flags, "-Wno-error=unknown-warning-option")
}
@@ -388,8 +385,18 @@
return strings.Join(deviceGlobalCflags, " ")
})
+ // Export the static default NoOverrideGlobalCflags to Bazel.
+ exportedVars.ExportStringList("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+ pctx.VariableFunc("NoOverrideGlobalCflags", func(ctx android.PackageVarContext) string {
+ flags := noOverrideGlobalCflags
+ if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+ flags = append(noOverrideGlobalCflags, llvmNextExtraCommonGlobalCflags...)
+ }
+ return strings.Join(flags, " ")
+ })
+
+ exportedVars.ExportStringListStaticVariable("NoOverride64GlobalCflags", noOverride64GlobalCflags)
exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
- exportedVars.ExportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags)
@@ -424,7 +431,7 @@
pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
pctx.StaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
- pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib64/clang/${ClangShortVersion}/lib/linux")
+ pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib/clang/${ClangShortVersion}/lib/linux")
// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
// being used for the rest of the build process.
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 67208b2..b3619c8 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -35,7 +35,9 @@
}
riscv64Lldflags = append(riscv64Ldflags,
- "-Wl,-z,max-page-size=4096")
+ "-Wl,-z,max-page-size=4096",
+ "-Wl,-plugin-opt,-emulated-tls=0",
+ )
riscv64Cppflags = []string{}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 1180da4..d55a13d 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -40,6 +40,8 @@
"-cert-err33-c",
// http://b/241125373
"-bugprone-unchecked-optional-access",
+ // http://b/265438407
+ "-misc-use-anonymous-namespace",
}
// Some clang-tidy checks are included in some tidy_checks_as_errors lists,
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 740405e..93aa82e 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -112,7 +112,7 @@
muslCrtBeginSharedBinary, muslCrtEndSharedBinary = []string{"libc_musl_crtbegin_dynamic"}, []string{"libc_musl_crtend"}
muslCrtBeginSharedLibrary, muslCrtEndSharedLibrary = []string{"libc_musl_crtbegin_so"}, []string{"libc_musl_crtend_so"}
- muslDefaultSharedLibraries = []string{"libc_musl"}
+ MuslDefaultSharedLibraries = []string{"libc_musl"}
)
const (
@@ -331,7 +331,7 @@
func (toolchainMusl) CrtEndSharedBinary() []string { return muslCrtEndSharedBinary }
func (toolchainMusl) CrtEndSharedLibrary() []string { return muslCrtEndSharedLibrary }
-func (toolchainMusl) DefaultSharedLibraries() []string { return muslDefaultSharedLibraries }
+func (toolchainMusl) DefaultSharedLibraries() []string { return MuslDefaultSharedLibraries }
func (toolchainMusl) Cflags() string {
return "${config.LinuxMuslCflags}"
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 7113d87..7aa8b91 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -212,7 +212,7 @@
return true
}
-func sharedLibraryInstallLocation(
+func SharedLibraryInstallLocation(
libraryPath android.Path, isHost bool, fuzzDir string, archString string) string {
installLocation := "$(PRODUCT_OUT)/data"
if isHost {
@@ -224,7 +224,7 @@
}
// Get the device-only shared library symbols install directory.
-func sharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
+func SharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base())
}
@@ -237,59 +237,64 @@
installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
- fuzzBin.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzBin.fuzzPackagedModule.FuzzProperties.Corpus)
- builder := android.NewRuleBuilder(pctx, ctx)
- intermediateDir := android.PathForModuleOut(ctx, "corpus")
- for _, entry := range fuzzBin.fuzzPackagedModule.Corpus {
- builder.Command().Text("cp").
- Input(entry).
- Output(intermediateDir.Join(ctx, entry.Base()))
- }
- builder.Build("copy_corpus", "copy corpus")
- fuzzBin.fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
-
- fuzzBin.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzBin.fuzzPackagedModule.FuzzProperties.Data)
- builder = android.NewRuleBuilder(pctx, ctx)
- intermediateDir = android.PathForModuleOut(ctx, "data")
- for _, entry := range fuzzBin.fuzzPackagedModule.Data {
- builder.Command().Text("cp").
- Input(entry).
- Output(intermediateDir.Join(ctx, entry.Rel()))
- }
- builder.Build("copy_data", "copy data")
- fuzzBin.fuzzPackagedModule.DataIntermediateDir = intermediateDir
-
- if fuzzBin.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
- fuzzBin.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzBin.fuzzPackagedModule.FuzzProperties.Dictionary)
- if fuzzBin.fuzzPackagedModule.Dictionary.Ext() != ".dict" {
- ctx.PropertyErrorf("dictionary",
- "Fuzzer dictionary %q does not have '.dict' extension",
- fuzzBin.fuzzPackagedModule.Dictionary.String())
- }
- }
-
- if fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
- configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
- android.WriteFileRule(ctx, configPath, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
- fuzzBin.fuzzPackagedModule.Config = configPath
- }
+ fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule, pctx)
// Grab the list of required shared libraries.
fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
for _, lib := range fuzzBin.sharedLibraries {
fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
- sharedLibraryInstallLocation(
+ SharedLibraryInstallLocation(
lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
// Also add the dependency on the shared library symbols dir.
if !ctx.Host() {
fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
- sharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+ SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
}
}
}
+func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule {
+ fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus)
+ builder := android.NewRuleBuilder(pctx, ctx)
+ intermediateDir := android.PathForModuleOut(ctx, "corpus")
+ for _, entry := range fuzzPackagedModule.Corpus {
+ builder.Command().Text("cp").
+ Input(entry).
+ Output(intermediateDir.Join(ctx, entry.Base()))
+ }
+ builder.Build("copy_corpus", "copy corpus")
+ fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
+
+ fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
+ builder = android.NewRuleBuilder(pctx, ctx)
+ intermediateDir = android.PathForModuleOut(ctx, "data")
+ for _, entry := range fuzzPackagedModule.Data {
+ builder.Command().Text("cp").
+ Input(entry).
+ Output(intermediateDir.Join(ctx, entry.Rel()))
+ }
+ builder.Build("copy_data", "copy data")
+ fuzzPackagedModule.DataIntermediateDir = intermediateDir
+
+ if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+ fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary)
+ if fuzzPackagedModule.Dictionary.Ext() != ".dict" {
+ ctx.PropertyErrorf("dictionary",
+ "Fuzzer dictionary %q does not have '.dict' extension",
+ fuzzPackagedModule.Dictionary.String())
+ }
+ }
+
+ if fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
+ configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
+ android.WriteFileRule(ctx, configPath, fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+ fuzzPackagedModule.Config = configPath
+ }
+ return fuzzPackagedModule
+}
+
func NewFuzzer(hod android.HostOrDeviceSupported) *Module {
module, binary := newBinary(hod, false)
baseInstallerPath := "fuzz"
@@ -344,7 +349,7 @@
// Responsible for generating GNU Make rules that package fuzz targets into
// their architecture & target/host specific zip file.
-type ccFuzzPackager struct {
+type ccRustFuzzPackager struct {
fuzz.FuzzPackager
fuzzPackagingArchModules string
fuzzTargetSharedDepsInstallPairs string
@@ -353,7 +358,7 @@
func fuzzPackagingFactory() android.Singleton {
- fuzzPackager := &ccFuzzPackager{
+ fuzzPackager := &ccRustFuzzPackager{
fuzzPackagingArchModules: "SOONG_FUZZ_PACKAGING_ARCH_MODULES",
fuzzTargetSharedDepsInstallPairs: "FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
allFuzzTargetsName: "ALL_FUZZ_TARGETS",
@@ -361,7 +366,7 @@
return fuzzPackager
}
-func (s *ccFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+func (s *ccRustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
// Map between each architecture + host/device combination, and the files that
// need to be packaged (in the tuple of {source file, destination folder in
// archive}).
@@ -376,19 +381,18 @@
sharedLibraryInstalled := make(map[string]bool)
ctx.VisitAllModules(func(module android.Module) {
- ccModule, ok := module.(*Module)
- if !ok || ccModule.Properties.PreventInstall {
+ ccModule, ok := module.(LinkableInterface)
+ if !ok || ccModule.PreventInstall() {
return
}
// Discard non-fuzz targets.
- if ok := fuzz.IsValid(ccModule.FuzzModule); !ok {
+ if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok {
return
}
sharedLibsInstallDirPrefix := "lib"
- fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
- if !ok {
+ if !ccModule.IsFuzzModule() {
return
}
@@ -399,12 +403,12 @@
fpm := fuzz.FuzzPackagedModule{}
if ok {
- fpm = fuzzModule.fuzzPackagedModule
+ fpm = ccModule.FuzzPackagedModule()
}
intermediatePath := "fuzz"
- archString := ccModule.Arch().ArchType.String()
+ archString := ccModule.Target().Arch.ArchType.String()
archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
@@ -415,7 +419,7 @@
files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
// Package shared libraries
- files = append(files, GetSharedLibsToZip(fuzzModule.sharedLibraries, ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
+ files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
// The executable.
files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
@@ -429,7 +433,7 @@
s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
}
-func (s *ccFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+func (s *ccRustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
packages := s.Packages.Strings()
sort.Strings(packages)
sort.Strings(s.FuzzPackager.SharedLibInstallStrings)
@@ -460,7 +464,7 @@
// For each architecture-specific shared library dependency, we need to
// install it to the output directory. Setup the install destination here,
// which will be used by $(copy-many-files) in the Make backend.
- installDestination := sharedLibraryInstallLocation(
+ installDestination := SharedLibraryInstallLocation(
library, module.Host(), fuzzDir, archString)
if (*sharedLibraryInstalled)[installDestination] {
continue
@@ -479,7 +483,7 @@
// we want symbolization tools (like `stack`) to be able to find the symbols
// in $ANDROID_PRODUCT_OUT/symbols automagically.
if !module.Host() {
- symbolsInstallDestination := sharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
+ symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
library.String()+":"+symbolsInstallDestination)
diff --git a/cc/library.go b/cc/library.go
index 787de44..9421007 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -292,14 +292,6 @@
}
func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
- // For some cc_library modules, their static variants are ready to be
- // converted, but not their shared variants. For these modules, delegate to
- // the cc_library_static bp2build converter temporarily instead.
- if android.GetBp2BuildAllowList().GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
- sharedOrStaticLibraryBp2Build(ctx, m, true)
- return
- }
-
sharedAttrs := bp2BuildParseSharedProps(ctx, m)
staticAttrs := bp2BuildParseStaticProps(ctx, m)
baseAttributes := bp2BuildParseBaseProps(ctx, m)
@@ -413,12 +405,12 @@
sharedTargetAttrs.Suffix = compilerAttrs.suffix
for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
- for config, props := range configToProps {
+ for cfg, props := range configToProps {
if props, ok := props.(*LibraryProperties); ok {
if props.Inject_bssl_hash != nil {
// This is an edge case applies only to libcrypto
if m.Name() == "libcrypto" || m.Name() == "libcrypto_for_testing" {
- sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, config, props.Inject_bssl_hash)
+ sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, cfg, props.Inject_bssl_hash)
} else {
ctx.PropertyErrorf("inject_bssl_hash", "only applies to libcrypto")
}
@@ -483,10 +475,10 @@
func apiContributionBp2Build(ctx android.TopDownMutatorContext, module *Module) {
apiSurfaces := make([]string, 0)
apiHeaders := make([]string, 0)
- // systemapi (non-null `stubs` property)
+ // module-libapi for apexes (non-null `stubs` property)
if module.HasStubsVariants() {
- apiSurfaces = append(apiSurfaces, android.SystemApi.String())
- apiIncludes := getSystemApiIncludes(ctx, module)
+ apiSurfaces = append(apiSurfaces, android.ModuleLibApi.String())
+ apiIncludes := getModuleLibApiIncludes(ctx, module)
if !apiIncludes.isEmpty() {
createApiHeaderTarget(ctx, apiIncludes)
apiHeaders = append(apiHeaders, apiIncludes.name)
@@ -502,8 +494,8 @@
}
}
// create a target only if this module contributes to an api surface
- // TODO: Currently this does not distinguish systemapi-only headers and vendrorapi-only headers
- // TODO: Update so that systemapi-only headers do not get exported to vendorapi (and vice-versa)
+ // TODO: Currently this does not distinguish modulelibapi-only headers and vendrorapi-only headers
+ // TODO: Update so that modulelibapi-only headers do not get exported to vendorapi (and vice-versa)
if len(apiSurfaces) > 0 {
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_api_contribution",
@@ -535,8 +527,8 @@
linker := module.linker.(*libraryDecorator)
if llndkApi := linker.Properties.Llndk.Symbol_file; llndkApi != nil {
apiFile = llndkApi
- } else if systemApi := linker.Properties.Stubs.Symbol_file; systemApi != nil {
- apiFile = systemApi
+ } else if moduleLibApi := linker.Properties.Stubs.Symbol_file; moduleLibApi != nil {
+ apiFile = moduleLibApi
} else {
ctx.ModuleErrorf("API surface library does not have any API file")
}
@@ -574,7 +566,8 @@
includes.attrs.Deps.Append(lla)
}
-func getSystemApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+// includes provided to the module-lib API surface. This API surface is used by apexes.
+func getModuleLibApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
flagProps := c.library.(*libraryDecorator).flagExporter.Properties
linkProps := c.library.(*libraryDecorator).baseLinker.Properties
includes := android.FirstUniqueStrings(flagProps.Export_include_dirs)
@@ -587,7 +580,7 @@
}
return apiIncludes{
- name: c.Name() + ".systemapi.headers",
+ name: c.Name() + ".module-libapi.headers",
attrs: bazelCcApiLibraryHeadersAttributes{
bazelCcLibraryHeadersAttributes: attrs,
},
@@ -852,7 +845,11 @@
ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
return
}
- outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, rootStaticArchives[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
objPaths := ccInfo.CcObjectFiles
@@ -888,9 +885,13 @@
ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries)
return
}
- outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
- handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, rootDynamicLibraries[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
handler.module.linker.(*libraryDecorator).unstrippedOutputFile = android.PathForBazelOut(ctx, ccInfo.UnstrippedOutput)
var tocFile android.OptionalPath
@@ -944,6 +945,8 @@
// implementation.
i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
}
+
+ handler.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
}
func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
@@ -1034,7 +1037,7 @@
return ret
}
-func GlobGeneratedHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
+func GlobGeneratedHeadersForSnapshot(_ android.ModuleContext, paths android.Paths) android.Paths {
ret := android.Paths{}
for _, header := range paths {
// TODO(b/148123511): remove exportedDeps after cleaning up genrule
@@ -1247,6 +1250,10 @@
// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
// systemapi, respectively. The former is for symbols defined in platform libraries
// and the latter is for symbols defined in APEXes.
+ // A single library can contain either # apex or # systemapi, but not both.
+ // The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
+ // However, having this distinction helps guard accidental
+ // promotion or demotion of API and also helps the API review process b/191371676
var flag string
if ctx.Module().(android.ApexModule).NotInPlatform() {
flag = "--apex"
@@ -1900,7 +1907,7 @@
errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/master/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
library.sourceAbiDiff(ctx, referenceDump, baseName, prevVersion,
- isLlndkOrNdk, /* allowExtensions */ true, sourceVersion, errorMessage)
+ isLlndkOrNdk, true /* allowExtensions */, sourceVersion, errorMessage)
}
func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
@@ -1909,7 +1916,7 @@
libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
- library.sourceAbiDiff(ctx, referenceDump, baseName, /* nameExt */ "",
+ library.sourceAbiDiff(ctx, referenceDump, baseName, "",
isLlndkOrNdk, allowExtensions, "current", errorMessage)
}
@@ -1920,7 +1927,7 @@
errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
- isLlndkOrNdk, /* allowExtensions */ false, "current", errorMessage)
+ isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
}
func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 4d38068..32ea1d4 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -76,7 +76,11 @@
return
}
- outputPath := android.PathForBazelOut(ctx, outputPaths[0])
+ var outputPath android.Path = android.PathForBazelOut(ctx, outputPaths[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+ }
h.module.outputFile = android.OptionalPathForPath(outputPath)
// HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library
@@ -88,6 +92,8 @@
// validation will fail. For now, set this to an empty list.
// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
h.library.collectedSnapshotHeaders = android.Paths{}
+
+ h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
}
// cc_library_headers contains a set of c/c++ headers which are imported by
@@ -169,7 +175,7 @@
// For API export, create a top-level arch-agnostic target and list the arch-specific targets as its deps
// arch-agnostic includes
- apiIncludes := getSystemApiIncludes(ctx, module)
+ apiIncludes := getModuleLibApiIncludes(ctx, module)
// arch and os specific includes
archApiIncludes, androidOsIncludes := archOsSpecificApiIncludes(ctx, module)
for _, arch := range allArches { // sorted iteration
@@ -186,7 +192,7 @@
}
if !apiIncludes.isEmpty() {
- // override the name from <mod>.systemapi.headers --> <mod>.contribution
+ // override the name from <mod>.module-libapi.headers --> <mod>.contribution
apiIncludes.name = android.ApiContributionTargetName(module.Name())
createApiHeaderTarget(ctx, apiIncludes)
}
diff --git a/cc/library_stub.go b/cc/library_stub.go
index d21df51..08a5eb6 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -205,6 +205,14 @@
d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
variantMod.exportProperties.Export_include_dirs...)
+
+ // Export headers as system include dirs if specified. Mostly for libc
+ if Bool(variantMod.exportProperties.Export_headers_as_system) {
+ d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
+ d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
+ d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+ d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
+ }
}
}
}
diff --git a/cc/library_test.go b/cc/library_test.go
index dab5bb8..de3db99 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -308,6 +308,75 @@
android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
}
+func TestCcLibraryWithBazelValidations(t *testing.T) {
+ t.Parallel()
+ bp := `
+cc_library {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ Headers: []string{"foo.h"},
+ RootDynamicLibraries: []string{"foo.so"},
+ UnstrippedOutput: "foo_unstripped.so",
+ },
+ "//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ Headers: []string{"foo.h"},
+ RootStaticArchives: []string{"foo.a"},
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ ).RunTestWithConfig(t, config).TestContext
+
+ staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+
+ expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm_armv7-a-neon_static/validated/foo.a"}
+ android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+ flagExporter := ctx.ModuleProvider(staticFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+ android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+ android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
+
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_library outputfiles %s", err)
+ }
+ expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ android.AssertStringEquals(t, "unstripped shared library", "outputbase/execroot/__main__/foo_unstripped.so", sharedFoo.(*Module).linker.unstrippedOutputFilePath().String())
+ flagExporter = ctx.ModuleProvider(sharedFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+ android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+ android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+ android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
+}
+
func TestLibraryVersionScript(t *testing.T) {
t.Parallel()
result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
@@ -344,6 +413,59 @@
}
+func TestCcLibrarySharedWithBazelValidations(t *testing.T) {
+ t.Parallel()
+ bp := `
+cc_library_shared {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ RootDynamicLibraries: []string{"foo.so"},
+ TocFile: "foo.so.toc",
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ ).RunTestWithConfig(t, config).TestContext
+
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ producer := sharedFoo.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"out/soong/.intermediates/foo/android_arm_armv7-a-neon_shared/validated/foo.so"}
+ android.AssertPathsRelativeToTopEquals(t, "output files", expectedOutputFiles, outputFiles)
+
+ tocFilePath := sharedFoo.(*Module).Toc()
+ if !tocFilePath.Valid() {
+ t.Errorf("Invalid tocFilePath: %s", tocFilePath)
+ }
+ tocFile := tocFilePath.Path()
+ expectedToc := "outputbase/execroot/__main__/foo.so.toc"
+ android.AssertStringEquals(t, "toc file", expectedToc, tocFile.String())
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+ expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+ gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+}
+
func TestCcLibrarySharedWithBazel(t *testing.T) {
t.Parallel()
bp := `
diff --git a/cc/linkable.go b/cc/linkable.go
index 0522fc6..9578807 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -3,6 +3,7 @@
import (
"android/soong/android"
"android/soong/bazel/cquery"
+ "android/soong/fuzz"
"android/soong/snapshot"
"github.com/google/blueprint"
@@ -120,6 +121,17 @@
IsPrebuilt() bool
Toc() android.OptionalPath
+ // IsFuzzModule returns true if this a *_fuzz module.
+ IsFuzzModule() bool
+
+ // FuzzPackagedModule returns the fuzz.FuzzPackagedModule for this module.
+ // Expects that IsFuzzModule returns true.
+ FuzzPackagedModule() fuzz.FuzzPackagedModule
+
+ // FuzzSharedLibraries returns the shared library dependencies for this module.
+ // Expects that IsFuzzModule returns true.
+ FuzzSharedLibraries() android.Paths
+
Device() bool
Host() bool
@@ -256,6 +268,9 @@
// Partition returns the partition string for this module.
Partition() string
+
+ // FuzzModule returns the fuzz.FuzzModule associated with the module.
+ FuzzModuleStruct() fuzz.FuzzModule
}
var (
diff --git a/cc/linker.go b/cc/linker.go
index 625d89c..371d78d 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -15,10 +15,10 @@
package cc
import (
- "fmt"
-
"android/soong/android"
"android/soong/cc/config"
+ "fmt"
+ "path/filepath"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -270,8 +270,7 @@
type baseLinker struct {
Properties BaseLinkerProperties
dynamicProperties struct {
- RunPaths []string `blueprint:"mutated"`
- BuildStubs bool `blueprint:"mutated"`
+ BuildStubs bool `blueprint:"mutated"`
}
sanitize *sanitize
@@ -281,13 +280,8 @@
linker.Properties.Ldflags = append(linker.Properties.Ldflags, flags...)
}
-// linkerInit initializes dynamic properties of the linker (such as runpath).
+// linkerInit initializes dynamic properties of the linker.
func (linker *baseLinker) linkerInit(ctx BaseModuleContext) {
- if ctx.toolchain().Is64Bit() {
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib64", "lib64")
- } else {
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib", "lib")
- }
}
func (linker *baseLinker) linkerProps() []interface{} {
@@ -529,17 +523,8 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
- if ctx.Host() && !ctx.Windows() {
- rpathPrefix := `\$$ORIGIN/`
- if ctx.Darwin() {
- rpathPrefix = "@loader_path/"
- }
-
- if !ctx.static() {
- for _, rpath := range linker.dynamicProperties.RunPaths {
- flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
- }
- }
+ if ctx.Host() && !ctx.Windows() && !ctx.static() {
+ flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...)
}
if ctx.useSdk() {
@@ -610,6 +595,45 @@
return flags
}
+// RpathFlags returns the rpath linker flags for current target to search the following directories relative
+// to the binary:
+//
+// - "." to find libraries alongside tests
+// - "lib[64]" to find libraries in a subdirectory of the binaries' directory
+// - "../lib[64]" to find libraries when the binaries are in a bin directory
+// - "../../lib[64]" to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+// out/host/linux-x86/nativetest/<test dir>/<test>
+// - "../../../lib[[64] to find libraries in out/host/linux-x86/lib64 when the test or binary is in
+// out/host/linux-x86/testcases/<test dir>/<CPU>/<test>
+func RpathFlags(ctx android.ModuleContext) []string {
+ key := struct {
+ os android.OsType
+ arch android.ArchType
+ }{ctx.Target().Os, ctx.Target().Arch.ArchType}
+
+ return ctx.Config().OnceStringSlice(android.NewCustomOnceKey(key), func() []string {
+ rpathPrefix := `\$$ORIGIN/`
+ if key.os == android.Darwin {
+ rpathPrefix = "@loader_path/"
+ }
+
+ var libDir string
+ if key.arch.Multilib == "lib64" {
+ libDir = "lib64"
+ } else {
+ libDir = "lib"
+ }
+
+ return []string{
+ "-Wl,-rpath," + rpathPrefix,
+ "-Wl,-rpath," + rpathPrefix + libDir,
+ "-Wl,-rpath," + rpathPrefix + filepath.Join("..", libDir),
+ "-Wl,-rpath," + rpathPrefix + filepath.Join("../..", libDir),
+ "-Wl,-rpath," + rpathPrefix + filepath.Join("../../..", libDir),
+ }
+ })
+}
+
func (linker *baseLinker) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
panic(fmt.Errorf("baseLinker doesn't know how to link"))
diff --git a/cc/lto.go b/cc/lto.go
index 1afa1dd..581856b 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -91,11 +91,6 @@
return flags
}
- // TODO(b/254713216): LTO doesn't work on riscv64 yet.
- if ctx.Arch().ArchType == android.Riscv64 {
- return flags
- }
-
if lto.LTO(ctx) {
var ltoCFlag string
var ltoLdFlag string
diff --git a/cc/lto_test.go b/cc/lto_test.go
index fbd91be..4220f32 100644
--- a/cc/lto_test.go
+++ b/cc/lto_test.go
@@ -15,10 +15,11 @@
package cc
import (
- "android/soong/android"
"strings"
"testing"
+ "android/soong/android"
+
"github.com/google/blueprint"
)
@@ -177,3 +178,64 @@
t.Errorf("'baz' expected to have flags %q, but got %q", w, libFooCFlags)
}
}
+
+func TestLtoDisabledButEnabledForArch(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ lto: {
+ never: true,
+ },
+ target: {
+ android_arm: {
+ lto: {
+ never: false,
+ thin: true,
+ },
+ },
+ },
+ }`
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ libFooWithLto := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+ libFooWithoutLto := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+
+ android.AssertStringDoesContain(t, "missing flag for LTO in variant that expects it",
+ libFooWithLto.Args["ldFlags"], "-flto=thin")
+ android.AssertStringDoesNotContain(t, "got flag for LTO in variant that doesn't expect it",
+ libFooWithoutLto.Args["ldFlags"], "-flto=thin")
+}
+
+func TestLtoDoesNotPropagateToRuntimeLibs(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library {
+ name: "runtime_libbar",
+ srcs: ["bar.c"],
+ }
+
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ runtime_libs: ["runtime_libbar"],
+ lto: {
+ thin: true,
+ },
+ }`
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, bp)
+
+ libFoo := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+ libBar := result.ModuleForTests("runtime_libbar", "android_arm_armv7-a-neon_shared").Rule("ld")
+
+ android.AssertStringDoesContain(t, "missing flag for LTO in LTO enabled library",
+ libFoo.Args["ldFlags"], "-flto=thin")
+ android.AssertStringDoesNotContain(t, "got flag for LTO in runtime_lib",
+ libBar.Args["ldFlags"], "-flto=thin")
+}
diff --git a/cc/makevars.go b/cc/makevars.go
index c70d4a6..6c3f551 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -93,6 +93,7 @@
ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ExternalCflags}")
ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
+ ctx.Strict("GLOBAL_CLANG_CFLAGS_64_NO_OVERRIDE", "${config.NoOverride64GlobalCflags}")
ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
diff --git a/cc/object.go b/cc/object.go
index 6cb1a30..11ce793 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -142,6 +142,7 @@
Absolute_includes bazel.StringListAttribute
Stl *string
Linker_script bazel.LabelAttribute
+ Crt *bool
sdkAttributes
}
@@ -208,6 +209,7 @@
Absolute_includes: compilerAttrs.absoluteIncludes,
Stl: compilerAttrs.stl,
Linker_script: linkerScript,
+ Crt: m.linker.(*objectLinker).Properties.Crt,
sdkAttributes: bp2BuildParseSdkAttributes(m),
}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index af83278..03a600a 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -462,6 +462,8 @@
}
h.module.maybeUnhideFromMake()
+
+ h.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
}
func (h *prebuiltLibraryBazelHandler) processStaticBazelQueryResponse(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
@@ -486,13 +488,17 @@
return true
}
- out := android.PathForBazelOut(ctx, staticLibs[0])
- h.module.outputFile = android.OptionalPathForPath(out)
+ var outputPath android.Path = android.PathForBazelOut(ctx, staticLibs[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+ }
- depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(out).Build()
+ h.module.outputFile = android.OptionalPathForPath(outputPath)
+
+ depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(outputPath).Build()
ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: out,
-
+ StaticLibrary: outputPath,
TransitiveStaticLibrariesForOrdering: depSet,
})
@@ -516,21 +522,26 @@
return true
}
- out := android.PathForBazelOut(ctx, sharedLibs[0])
- h.module.outputFile = android.OptionalPathForPath(out)
+ var outputPath android.Path = android.PathForBazelOut(ctx, sharedLibs[0])
+ if len(ccInfo.TidyFiles) > 0 {
+ h.module.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+ outputPath = android.AttachValidationActions(ctx, outputPath, h.module.tidyFiles)
+ }
+
+ h.module.outputFile = android.OptionalPathForPath(outputPath)
// FIXME(b/214600441): We don't yet strip prebuilt shared libraries
- h.library.unstrippedOutputFile = out
+ h.library.unstrippedOutputFile = outputPath
var toc android.Path
if len(ccInfo.TocFile) > 0 {
toc = android.PathForBazelOut(ctx, ccInfo.TocFile)
} else {
- toc = out // Just reuse `out` so ninja still gets an input but won't matter
+ toc = outputPath // Just reuse `out` so ninja still gets an input but won't matter
}
info := SharedLibraryInfo{
- SharedLibrary: out,
+ SharedLibrary: outputPath,
TableOfContents: android.OptionalPathForPath(toc),
Target: ctx.Target(),
}
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 95fa99e..405680c 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -443,6 +443,75 @@
expectedStaticOutputFiles, staticOutputFiles.Strings())
}
+func TestPrebuiltLibraryWithBazelValidations(t *testing.T) {
+ const bp = `
+cc_prebuilt_library {
+ name: "foo",
+ shared: {
+ srcs: ["foo.so"],
+ },
+ static: {
+ srcs: ["foo.a"],
+ },
+ bazel_module: { label: "//foo/bar:bar" },
+ tidy: true,
+}`
+ outBaseDir := "outputbase"
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ android.FixtureMergeEnv(map[string]string{
+ "ALLOW_LOCAL_TIDY_TRUE": "1",
+ }),
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: outBaseDir,
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcSharedLibraryFiles: []string{"foo.so"},
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ "//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+ CcStaticLibraryFiles: []string{"foo.a"},
+ TidyFiles: []string{"foo.c.tidy"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ sharedFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+
+ expectedOutputFile := "out/soong/.intermediates/foo/android_arm_armv7-a-neon_shared/validated/foo.so"
+ sharedInfo := result.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library shared target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ expectedOutputFile, sharedInfo.SharedLibrary)
+
+ outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{expectedOutputFile}
+ android.AssertPathsRelativeToTopEquals(t,
+ "prebuilt library shared target output files did not match expected.",
+ expectedOutputFiles, outputFiles)
+
+ staticFoo := result.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ staticInfo := result.ModuleProvider(staticFoo, StaticLibraryInfoProvider).(StaticLibraryInfo)
+ expectedStaticOutputFile := "out/soong/.intermediates/foo/android_arm_armv7-a-neon_static/validated/foo.a"
+ android.AssertPathRelativeToTopEquals(t,
+ "prebuilt library static target path did not exist or did not match expected. If the base path is what does not match, it is likely that Soong built this module instead of Bazel.",
+ expectedStaticOutputFile, staticInfo.StaticLibrary)
+
+ staticOutputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object staticOutputFiles %s", err)
+ }
+ expectedStaticOutputFiles := []string{expectedStaticOutputFile}
+ android.AssertPathsRelativeToTopEquals(t,
+ "prebuilt library static target output files did not match expected.",
+ expectedStaticOutputFiles, staticOutputFiles)
+}
+
func TestPrebuiltLibraryWithBazelStaticDisabled(t *testing.T) {
const bp = `
cc_prebuilt_library {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 8e2d161..66f459a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -76,8 +76,11 @@
minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
"-fno-sanitize-recover=integer,undefined"}
hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
- "export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
+ "export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"}
memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
+
+ hostOnlySanitizeFlags = []string{"-fno-sanitize-recover=all"}
+ deviceOnlySanitizeFlags = []string{"-fsanitize-trap=all", "-ftrap-function=abort"}
)
type SanitizerType int
@@ -379,7 +382,12 @@
var _ android.SkipApexAllowedDependenciesCheck = (*libraryDependencyTag)(nil)
+var exportedVars = android.NewExportedVariables(pctx)
+
func init() {
+ exportedVars.ExportStringListStaticVariable("HostOnlySanitizeFlags", hostOnlySanitizeFlags)
+ exportedVars.ExportStringList("DeviceOnlySanitizeFlags", deviceOnlySanitizeFlags)
+
android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
}
@@ -869,9 +877,9 @@
// When fuzzing, we wish to crash with diagnostics on any bug.
flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
} else if ctx.Host() {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover=all")
+ flags.Local.CFlags = append(flags.Local.CFlags, hostOnlySanitizeFlags...)
} else {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
+ flags.Local.CFlags = append(flags.Local.CFlags, deviceOnlySanitizeFlags...)
}
if enableMinimalRuntime(s) {
@@ -1787,3 +1795,7 @@
func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
}
+
+func BazelCcSanitizerToolchainVars(config android.Config) string {
+ return android.BazelToolchainVars(config, exportedVars)
+}
diff --git a/cc/stl.go b/cc/stl.go
index 6353a4a..f1433ef 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -38,9 +38,9 @@
func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
stl := m.SelectedStl()
switch stl {
- case "ndk_libc++_shared":
+ case "ndk_libc++_shared", "libc++":
return "libc++", "shared"
- case "ndk_libc++_static":
+ case "ndk_libc++_static", "libc++_static":
return "libc++", "static"
case "ndk_system":
return "system", "shared"
@@ -80,7 +80,8 @@
return ""
}
s = deduplicateStlInput(s)
- if ctx.useSdk() && ctx.Device() {
+ archHasNDKStl := ctx.Arch().ArchType != android.Riscv64
+ if ctx.useSdk() && ctx.Device() && archHasNDKStl {
switch s {
case "", "system":
return "ndk_system"
diff --git a/cc/test.go b/cc/test.go
index 16ef0ef..27de06b 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -311,23 +311,6 @@
return deps
}
-func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
- // 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
- // 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)
- }
-
- // add "" to rpath so that test binaries can find libraries in their own test directory
- linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
-}
-
func (test *testDecorator) linkerProps() []interface{} {
return []interface{}{&test.LinkerProperties}
}
@@ -356,11 +339,6 @@
return props
}
-func (test *testBinary) linkerInit(ctx BaseModuleContext) {
- test.testDecorator.linkerInit(ctx, test.binaryDecorator.baseLinker)
- test.binaryDecorator.linkerInit(ctx)
-}
-
func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps = test.testDecorator.linkerDeps(ctx, deps)
deps = test.binaryDecorator.linkerDeps(ctx, deps)
@@ -535,11 +513,6 @@
return append(props, test.libraryDecorator.linkerProps()...)
}
-func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
- test.testDecorator.linkerInit(ctx, test.libraryDecorator.baseLinker)
- test.libraryDecorator.linkerInit(ctx)
-}
-
func (test *testLibrary) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps = test.testDecorator.linkerDeps(ctx, deps)
deps = test.libraryDecorator.linkerDeps(ctx, deps)
@@ -610,15 +583,6 @@
return true
}
-func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
- runpath := "../../lib"
- if ctx.toolchain().Is64Bit() {
- runpath += "64"
- }
- benchmark.baseLinker.dynamicProperties.RunPaths = append(benchmark.baseLinker.dynamicProperties.RunPaths, runpath)
- benchmark.binaryDecorator.linkerInit(ctx)
-}
-
func (benchmark *benchmarkDecorator) linkerProps() []interface{} {
props := benchmark.binaryDecorator.linkerProps()
props = append(props, &benchmark.Properties)
@@ -685,9 +649,15 @@
return
}
- outputFilePath := android.PathForBazelOut(ctx, info.OutputFile)
+ var outputFilePath android.Path = android.PathForBazelOut(ctx, info.OutputFile)
+ if len(info.TidyFiles) > 0 {
+ handler.module.tidyFiles = android.PathsForBazelOut(ctx, info.TidyFiles)
+ outputFilePath = android.AttachValidationActions(ctx, outputFilePath, handler.module.tidyFiles)
+ }
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
handler.module.linker.(*testBinary).unstrippedOutputFile = android.PathForBazelOut(ctx, info.UnstrippedOutput)
+
+ handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
}
// binaryAttributes contains Bazel attributes corresponding to a cc test
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 712c7fc..e3d1179 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -359,7 +359,8 @@
}
for _, file := range inputZip.Entries() {
pyPkg := getPackage(file.Name)
- if filepath.Base(file.Name) == "__init__.py" {
+ baseName := filepath.Base(file.Name)
+ if baseName == "__init__.py" || baseName == "__init__.pyc" {
if _, found := initedPackages[pyPkg]; found {
panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index d8d5e5d..0115f4a 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -321,7 +321,7 @@
jobs = runtime.NumCPU() / 4
ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
- if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
+ if ramJobs := ramGb / 40; ramGb > 0 && jobs > ramJobs {
jobs = ramJobs
}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index baa7380..5f27fa7 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -145,8 +145,10 @@
func runApiBp2build(ctx *android.Context, extraNinjaDeps []string) string {
ctx.EventHandler.Begin("api_bp2build")
defer ctx.EventHandler.End("api_bp2build")
- // Do not allow missing dependencies.
- ctx.SetAllowMissingDependencies(false)
+ // api_bp2build does not run the typical pipeline of soong mutators.
+ // Hoevever, it still runs the defaults mutator which can create dependencies.
+ // These dependencies might not always exist (e.g. in tests)
+ ctx.SetAllowMissingDependencies(ctx.Config().AllowMissingDependencies())
ctx.RegisterForApiBazelConversion()
// Register the Android.bp files in the tree
@@ -176,7 +178,8 @@
ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
// Create soong_injection repository
- soongInjectionFiles := bp2build.CreateSoongInjectionFiles(ctx.Config(), bp2build.CreateCodegenMetrics())
+ soongInjectionFiles, err := bp2build.CreateSoongInjectionDirFiles(codegenContext, bp2build.CreateCodegenMetrics())
+ maybeQuit(err, "")
absoluteSoongInjectionDir := shared.JoinPath(topDir, ctx.Config().SoongOutDir(), bazel.SoongInjectionDirName)
for _, file := range soongInjectionFiles {
// The API targets in api_bp2build workspace do not have any dependency on api_bp2build.
@@ -189,7 +192,7 @@
workspace := shared.JoinPath(ctx.Config().SoongOutDir(), "api_bp2build")
// Create the symlink forest
- symlinkDeps := bp2build.PlantSymlinkForest(
+ symlinkDeps, _, _ := bp2build.PlantSymlinkForest(
ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE"),
topDir,
workspace,
@@ -445,6 +448,7 @@
"bazel-genfiles",
"bazel-out",
"bazel-testlogs",
+ "bazel-workspace",
"bazel-" + filepath.Base(topDir),
}
}
@@ -459,8 +463,10 @@
// symlink tree creation binary. Then the latter would not need to depend on
// the very heavy-weight machinery of soong_build .
func runSymlinkForestCreation(ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
+ var ninjaDeps []string
+ var mkdirCount, symlinkCount uint64
+
ctx.EventHandler.Do("symlink_forest", func() {
- var ninjaDeps []string
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
verbose := ctx.Config().IsEnvTrue("BP2BUILD_VERBOSE")
@@ -469,15 +475,16 @@
// or file created/deleted under it would trigger an update of the symlink forest.
generatedRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "bp2build")
workspaceRoot := shared.JoinPath(ctx.Config().SoongOutDir(), "workspace")
+ var symlinkForestDeps []string
ctx.EventHandler.Do("plant", func() {
- symlinkForestDeps := bp2build.PlantSymlinkForest(
+ symlinkForestDeps, mkdirCount, symlinkCount = bp2build.PlantSymlinkForest(
verbose, topDir, workspaceRoot, generatedRoot, excludedFromSymlinkForest(ctx, verbose))
- ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
})
-
- writeDepFile(cmdlineArgs.SymlinkForestMarker, ctx.EventHandler, ninjaDeps)
- touch(shared.JoinPath(topDir, cmdlineArgs.SymlinkForestMarker))
+ ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
})
+
+ writeDepFile(cmdlineArgs.SymlinkForestMarker, ctx.EventHandler, ninjaDeps)
+ touch(shared.JoinPath(topDir, cmdlineArgs.SymlinkForestMarker))
codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
if codegenMetrics == nil {
m := bp2build.CreateCodegenMetrics()
@@ -486,6 +493,8 @@
//TODO (usta) we cannot determine if we loaded a stale file, i.e. from an unrelated prior
//invocation of codegen. We should simply use a separate .pb file
}
+ codegenMetrics.SetSymlinkCount(symlinkCount)
+ codegenMetrics.SetMkDirCount(mkdirCount)
writeBp2BuildMetrics(codegenMetrics, ctx.EventHandler, metricsDir)
return cmdlineArgs.SymlinkForestMarker
}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 8c99988..fd718c2 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -199,11 +199,16 @@
soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
+ bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
+
+ //the profile file generated by Bazel"
+ bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
metricsFiles := []string{
buildErrorFile, // build error strings
rbeMetricsFile, // high level metrics related to remote build execution.
bp2buildMetricsFile, // high level metrics related to bp2build.
soongMetricsFile, // high level metrics related to this build system.
+ bazelMetricsFile, // high level metrics related to bazel execution
config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
}
@@ -213,12 +218,12 @@
trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
- c.run(buildCtx, config, args)
-
- defer met.Dump(soongMetricsFile)
if !config.SkipMetricsUpload() {
- defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
+ defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
}
+ defer met.Dump(soongMetricsFile)
+
+ c.run(buildCtx, config, args)
}
diff --git a/compliance/OWNERS b/compliance/OWNERS
new file mode 100644
index 0000000..f52e201
--- /dev/null
+++ b/compliance/OWNERS
@@ -0,0 +1,8 @@
+# OSEP Build
+bbadour@google.com
+kanouche@google.com
+napier@google.com
+
+# Open Source Compliance Tools
+rtp@google.com
+austinyuan@google.com
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index e3404a5..a590c72 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -101,6 +101,10 @@
}
func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
+ if ctx.Config().UnbundledBuild() {
+ return true
+ }
+
if contains(global.DisablePreoptModules, module.Name) {
return true
}
diff --git a/docs/map_files.md b/docs/map_files.md
index 1388059..35e8cbb 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -148,9 +148,24 @@
### systemapi
-This is a synonym of the `apex` tag. It should be used to clarify that the API
-is an API exposed by the system for an APEX, whereas `apex` should be used for
-APIs exposed by an APEX to the platform or another APEX.
+Indicates that the symbol is exposed by the platform for an apex. Whereas `apex`
+should be used for APIs exposed by an APEX to the platform or another APEX.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
+
+Since a single library can be installed ether in platform or an apex, but not
+both, a single map.txt file should not contain _both_ # apex and # systemapi symbols.
+
+The granularity between # apex and # systemapi exists to help the API review
+process (b/191371676). These two symbols have very similar lifetime "in
+practice". A #systemapi symbol can be dropped from the next release if we are
+confident that no one is using it. Similarily, #apex can be dropped if we are
+sure that the old platform which used the symbol has reached EOL and thus is no
+longer accepting new APEX updates. Unlike the APIs for apps where we have zero
+control over how APIs are used, we are in a much more controllable environment
+when talking about #systemapi and #apex symbols. So, we have some flexibility
+here when determining the lifetime of a symbol.
### var
diff --git a/go.mod b/go.mod
index 7239f6d..a5d9dd5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,21 +1,9 @@
module android/soong
+go 1.19
+
require (
- google.golang.org/protobuf v0.0.0
- github.com/google/blueprint v0.0.0
- prebuilts/bazel/common/proto/analysis_v2 v0.0.0
- prebuilts/bazel/common/proto/build v0.0.0 // indirect
+ github.com/google/blueprint v0.0.0
+ google.golang.org/protobuf v0.0.0
+ prebuilts/bazel/common/proto/analysis_v2 v0.0.0
)
-
-replace (
- google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
- github.com/google/blueprint v0.0.0 => ../blueprint
- github.com/google/go-cmp v0.5.5 => ../../external/go-cmp
- prebuilts/bazel/common/proto/analysis_v2 => ../../prebuilts/bazel/common/proto/analysis_v2
- prebuilts/bazel/common/proto/build => ../../prebuilts/bazel/common/proto/build
-)
-
-// Indirect deps from golang-protobuf
-exclude github.com/golang/protobuf v1.5.0
-
-go 2.0
diff --git a/go.work b/go.work
new file mode 100644
index 0000000..737a9df
--- /dev/null
+++ b/go.work
@@ -0,0 +1,19 @@
+go 1.19
+
+use (
+ .
+ ../../external/go-cmp
+ ../../external/golang-protobuf
+ ../../prebuilts/bazel/common/proto/analysis_v2
+ ../../prebuilts/bazel/common/proto/build
+ ../blueprint
+)
+
+replace (
+ github.com/golang/protobuf v0.0.0 => ../../external/golang-protobuf
+ github.com/google/blueprint v0.0.0 => ../blueprint
+ github.com/google/go-cmp v0.0.0 => ../../external/go-cmp
+ google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
+ prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../prebuilts/bazel/common/proto/analysis_v2
+ prebuilts/bazel/common/proto/build v0.0.0 => ../../prebuilts/bazel/common/proto/build
+)
diff --git a/java/aar.go b/java/aar.go
index f4a2ff2..a483e13 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -651,6 +651,8 @@
// Functionality common to Module and Import.
embeddableInModuleAndImport
+ providesTransitiveHeaderJars
+
properties AARImportProperties
classpathFile android.WritablePath
@@ -897,8 +899,11 @@
a.assetsPackage = mergedAssets
}
+ a.collectTransitiveHeaderJars(ctx)
ctx.SetProvider(JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(a.classpathFile),
+ TransitiveLibsHeaderJars: a.transitiveLibsHeaderJars,
+ TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
ImplementationJars: android.PathsIfNonNil(a.classpathFile),
})
@@ -1031,7 +1036,7 @@
ctx.CreateBazelTargetModule(
bazel.BazelTargetModuleProperties{
Rule_class: "aar_import",
- Bzl_load_location: "@rules_android//rules:rules.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
},
android.CommonAttributes{Name: name},
&bazelAndroidLibraryImport{
@@ -1041,6 +1046,21 @@
},
)
+ neverlink := true
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "android_library",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+ },
+ android.CommonAttributes{Name: name + "-neverlink"},
+ &bazelAndroidLibrary{
+ javaLibraryAttributes: &javaLibraryAttributes{
+ Neverlink: bazel.BoolAttribute{Value: &neverlink},
+ Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+ },
+ },
+ )
+
}
func (a *AndroidLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
@@ -1054,10 +1074,14 @@
ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
}
+ if len(a.properties.Common_srcs) != 0 {
+ commonAttrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, a.properties.Common_srcs))
+ }
+
name := a.Name()
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_library",
- Bzl_load_location: "@rules_android//rules:rules.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
}
ctx.CreateBazelTargetModule(
diff --git a/java/android_manifest.go b/java/android_manifest.go
index c785310..f6457a0 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -149,13 +149,14 @@
if params.SdkContext != nil {
targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params)
- args = append(args, "--targetSdkVersion ", targetSdkVersion)
if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
deps = append(deps, ApiFingerprintPath(ctx))
}
+ args = append(args, "--targetSdkVersion ", targetSdkVersion)
+
minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
if err != nil {
ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
diff --git a/java/app.go b/java/app.go
index 3c5760b..1731970 100755
--- a/java/app.go
+++ b/java/app.go
@@ -801,6 +801,8 @@
unstrippedFile: dep.UnstrippedOutputFile(),
partition: dep.Partition(),
})
+ } else if ctx.Config().AllowMissingDependencies() {
+ ctx.AddMissingDependencies([]string{otherName})
} else {
ctx.ModuleErrorf("dependency %q missing output file", otherName)
}
@@ -982,8 +984,11 @@
// The name of the android_app module that the tests will run against.
Instrumentation_for *string
- // if specified, the instrumentation target package name in the manifest is overwritten by it.
+ // If specified, the instrumentation target package name in the manifest is overwritten by it.
Instrumentation_target_package *string
+
+ // If specified, the mainline module package name in the test config is overwritten by it.
+ Mainline_package_name *string
}
type AndroidTest struct {
@@ -1061,6 +1066,11 @@
FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
}
+ if a.appTestProperties.Mainline_package_name != nil {
+ fixNeeded = true
+ command.FlagWithArg("--mainline-package-name ", *a.appTestProperties.Mainline_package_name)
+ }
+
if fixNeeded {
rule.Build("fix_test_config", "fix test config")
return fixedConfig
@@ -1310,6 +1320,9 @@
ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
}
+ } else {
+ ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...)
+ ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...)
}
}
@@ -1476,7 +1489,7 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_app_certificate",
- Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -1503,20 +1516,48 @@
certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableAppProperties.Certificate)
- attrs := &bazelAndroidAppAttributes{
- commonAttrs,
- aapt,
- deps,
+ appAttrs := &bazelAndroidAppAttributes{
// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
- a.overridableAppProperties.Package_name,
- certificate,
- certificateName,
+ Custom_package: a.overridableAppProperties.Package_name,
+ Certificate: certificate,
+ Certificate_name: certificateName,
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_binary",
- Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
}
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+
+ if !bp2BuildInfo.hasKotlinSrcs && len(a.properties.Common_srcs) == 0 {
+ appAttrs.javaCommonAttributes = commonAttrs
+ appAttrs.bazelAapt = aapt
+ appAttrs.Deps = deps
+ } else {
+ ktName := a.Name() + "_kt"
+ commonAttrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, a.properties.Common_srcs))
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "android_library",
+ Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+ },
+ android.CommonAttributes{Name: ktName},
+ &bazelAndroidLibrary{
+ javaLibraryAttributes: &javaLibraryAttributes{
+ javaCommonAttributes: commonAttrs,
+ Deps: deps,
+ },
+ bazelAapt: aapt,
+ },
+ )
+
+ appAttrs.bazelAapt = &bazelAapt{Manifest: aapt.Manifest}
+ appAttrs.Deps = bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + ktName})
+ }
+
+ ctx.CreateBazelTargetModule(
+ props,
+ android.CommonAttributes{Name: a.Name()},
+ appAttrs,
+ )
}
diff --git a/java/app_import.go b/java/app_import.go
index 8c1e19c..e24e780 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -17,6 +17,7 @@
// This file contains the module implementations for android_app_import and android_test_import.
import (
+ "github.com/google/blueprint"
"reflect"
"github.com/google/blueprint/proptools"
@@ -31,6 +32,24 @@
initAndroidAppImportVariantGroupTypes()
}
+var (
+ uncompressEmbeddedJniLibsRule = pctx.AndroidStaticRule("uncompress-embedded-jni-libs", blueprint.RuleParams{
+ Command: `if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+ `${config.Zip2ZipCmd} -i $in -o $out -0 'lib/**/*.so'` +
+ `; else cp -f $in $out; fi`,
+ CommandDeps: []string{"${config.Zip2ZipCmd}"},
+ Description: "Uncompress embedded JNI libs",
+ })
+
+ uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
+ Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
+ `${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
+ `; else cp -f $in $out; fi`,
+ CommandDeps: []string{"${config.Zip2ZipCmd}"},
+ Description: "Uncompress dex files",
+ })
+)
+
func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
@@ -193,15 +212,12 @@
})
return
}
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'lib/**/*.so'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: uncompressEmbeddedJniLibsRule,
+ Input: inputPath,
+ Output: outputPath,
+ })
}
// Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -218,19 +234,6 @@
return shouldUncompressDex(ctx, &a.dexpreopter)
}
-func (a *AndroidAppImport) uncompressDex(
- ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'classes*.dex'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-dex", "Uncompress dex files")
-}
-
func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.generateAndroidBuildActions(ctx)
}
@@ -306,7 +309,11 @@
a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
if a.dexpreopter.uncompressedDex {
dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
- a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: uncompressDexRule,
+ Input: jnisUncompressed,
+ Output: dexUncompressed,
+ })
jnisUncompressed = dexUncompressed
}
diff --git a/java/app_import_test.go b/java/app_import_test.go
index ad27e3a..a29606f 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -17,7 +17,6 @@
import (
"fmt"
"reflect"
- "regexp"
"strings"
"testing"
@@ -294,7 +293,6 @@
},
}
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
for _, test := range testCases {
result := android.GroupFixturePreparers(
PrepareForTestWithJavaDefaultModules,
@@ -305,13 +303,9 @@
).RunTestWithBp(t, bp)
variant := result.ModuleForTests("foo", "android_common")
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if strings.HasSuffix(matches[1], test.expected) {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+ if strings.HasSuffix(input, test.expected) {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
}
provenanceMetaDataRule := variant.Rule("genProvenanceMetaData")
@@ -456,7 +450,6 @@
},
}
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
for _, test := range testCases {
ctx, _ := testJava(t, test.bp)
@@ -469,13 +462,9 @@
android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
continue
}
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if strings.HasSuffix(matches[1], test.expected) {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+ if strings.HasSuffix(input, test.expected) {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
@@ -686,8 +675,8 @@
`)
variant := ctx.ModuleForTests("foo", "android_common")
- jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- if !strings.HasPrefix(jniRule, "if (zipinfo") {
+ jniRule := variant.Output("jnis-uncompressed/foo.apk").BuildParams.Rule.String()
+ if jniRule == android.Cp.String() {
t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
}
diff --git a/java/app_test.go b/java/app_test.go
index cd88864..c77f29d 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2330,12 +2330,14 @@
srcs: ["b.java"],
package_name: "com.android.bar.test",
instrumentation_for: "foo",
+ mainline_package_name: "com.android.bar",
}
override_android_test {
name: "baz_test",
base: "foo_test",
package_name: "com.android.baz.test",
+ mainline_package_name: "com.android.baz",
}
`)
@@ -2354,6 +2356,7 @@
expectedFlags: []string{
"--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.bar.test",
+ "--mainline-package-name com.android.bar",
},
},
{
@@ -2363,6 +2366,8 @@
"--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.baz.test",
"--test-file-name baz_test.apk",
+ "out/soong/.intermediates/foo_test/android_common_baz_test/test_config_fixer/AndroidTest.xml",
+ "--mainline-package-name com.android.baz",
},
},
}
@@ -3163,6 +3168,7 @@
variables.Platform_sdk_version = &platform_sdk_version
variables.Platform_sdk_codename = &platform_sdk_codename
variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+ variables.Unbundled_build = proptools.BoolPtr(true)
variables.Unbundled_build_apps = []string{"sampleModule"}
}),
)
@@ -3241,6 +3247,7 @@
variables.Platform_sdk_final = &testCase.platform_sdk_final
variables.Platform_sdk_version = &platform_sdk_version
variables.Platform_sdk_codename = &platform_sdk_codename
+ variables.Unbundled_build = proptools.BoolPtr(true)
variables.Unbundled_build_apps = []string{"sampleModule"}
}),
)
@@ -3311,6 +3318,7 @@
variables.Platform_sdk_final = &testCase.platform_sdk_final
variables.Platform_sdk_version = &platform_sdk_version
variables.Platform_sdk_codename = &platform_sdk_codename
+ variables.Unbundled_build = proptools.BoolPtr(true)
variables.Unbundled_build_apps = []string{"sampleModule"}
}),
)
diff --git a/java/base.go b/java/base.go
index 84fda37..cce06a4 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1583,6 +1583,8 @@
ctx.SetProvider(JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(j.headerJarFile),
+ TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars,
+ TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar),
ImplementationJars: android.PathsIfNonNil(j.implementationJarFile),
ResourceJars: android.PathsIfNonNil(j.resourceJar),
@@ -1719,6 +1721,52 @@
return instrumentedJar
}
+type providesTransitiveHeaderJars struct {
+ // set of header jars for all transitive libs deps
+ transitiveLibsHeaderJars *android.DepSet
+ // set of header jars for all transitive static libs deps
+ transitiveStaticLibsHeaderJars *android.DepSet
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet {
+ return j.transitiveLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet {
+ return j.transitiveStaticLibsHeaderJars
+}
+
+func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.ModuleContext) {
+ directLibs := android.Paths{}
+ directStaticLibs := android.Paths{}
+ transitiveLibs := []*android.DepSet{}
+ transitiveStaticLibs := []*android.DepSet{}
+ ctx.VisitDirectDeps(func(module android.Module) {
+ // don't add deps of the prebuilt version of the same library
+ if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
+ return
+ }
+
+ dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+ if dep.TransitiveLibsHeaderJars != nil {
+ transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars)
+ }
+ if dep.TransitiveStaticLibsHeaderJars != nil {
+ transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars)
+ }
+
+ tag := ctx.OtherModuleDependencyTag(module)
+ _, isUsesLibDep := tag.(usesLibraryDependencyTag)
+ if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
+ directLibs = append(directLibs, dep.HeaderJars...)
+ } else if tag == staticLibTag {
+ directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
+ }
+ })
+ j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
+ j.transitiveStaticLibsHeaderJars = android.NewDepSet(android.POSTORDER, directStaticLibs, transitiveStaticLibs)
+}
+
func (j *Module) HeaderJars() android.Paths {
if j.headerJarFile == nil {
return nil
@@ -1947,6 +1995,7 @@
sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
+ j.collectTransitiveHeaderJars(ctx)
ctx.VisitDirectDeps(func(module android.Module) {
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
diff --git a/java/config/config.go b/java/config/config.go
index 49d88c4..7c22076 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -96,8 +96,6 @@
}, dexerJavaVmFlagsList...))
exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
"-JXmx2048M",
- // Disable this optimization as it can impact weak reference semantics. See b/233432839.
- "-JDcom.android.tools.r8.disableEnqueuerDeferredTracing=true",
}, dexerJavaVmFlagsList...))
exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 4fb1d76..b9332dd 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -63,11 +63,6 @@
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
sdk_version: "none",
system_modules: "none",
@@ -148,11 +143,6 @@
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
sdk_version: "none",
system_modules: "none",
@@ -278,11 +268,6 @@
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
}
@@ -294,11 +279,6 @@
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
],
}
@@ -322,11 +302,6 @@
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
- // This one is not on device but it's needed when javac compiles code
- // containing @Generated annotations produced by some code generation
- // tools.
- // See http://b/123891440.
- "core-generated-annotation-stubs",
// Ensure that core libraries that depend on the public API can access
// the UnsupportedAppUsage, CorePlatformApi and IntraCoreApi
diff --git a/java/dex.go b/java/dex.go
index 40ee99d..a8dd375 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -89,7 +89,10 @@
// list of extra proguard flag files
extraProguardFlagFiles android.Paths
proguardDictionary android.OptionalPath
+ proguardConfiguration android.OptionalPath
proguardUsageZip android.OptionalPath
+
+ providesTransitiveHeaderJars
}
func (d *dexer) effectiveOptimizeEnabled() bool {
@@ -130,17 +133,18 @@
var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
+ `rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
`mkdir -p $$(dirname ${outUsage}) && ` +
`mkdir -p $$(dirname $tmpJar) && ` +
`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
`$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
`--no-data-resources ` +
`-printmapping ${outDict} ` +
+ `--pg-conf-output ${outConfig} ` +
`-printusage ${outUsage} ` +
`--deps-file ${out}.d ` +
`$r8Flags && ` +
- `touch "${outDict}" "${outUsage}" && ` +
+ `touch "${outDict}" "${outConfig}" "${outUsage}" && ` +
`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
`rm -rf ${outUsageDir} && ` +
`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
@@ -176,7 +180,7 @@
ExecStrategy: "${config.RER8ExecStrategy}",
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
},
- }, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
+ }, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
@@ -249,13 +253,32 @@
})
r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
- r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
- r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
-
r8Deps = append(r8Deps, proguardRaiseDeps...)
+ r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
r8Deps = append(r8Deps, flags.bootClasspath...)
+ r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
r8Deps = append(r8Deps, flags.dexClasspath...)
+ transitiveStaticLibsLookupMap := map[android.Path]bool{}
+ if d.transitiveStaticLibsHeaderJars != nil {
+ for _, jar := range d.transitiveStaticLibsHeaderJars.ToList() {
+ transitiveStaticLibsLookupMap[jar] = true
+ }
+ }
+ transitiveHeaderJars := android.Paths{}
+ if d.transitiveLibsHeaderJars != nil {
+ for _, jar := range d.transitiveLibsHeaderJars.ToList() {
+ if _, ok := transitiveStaticLibsLookupMap[jar]; ok {
+ // don't include a lib if it is already packaged in the current JAR as a static lib
+ continue
+ }
+ transitiveHeaderJars = append(transitiveHeaderJars, jar)
+ }
+ }
+ transitiveClasspath := classpath(transitiveHeaderJars)
+ r8Flags = append(r8Flags, transitiveClasspath.FormJavaClassPath("-libraryjars"))
+ r8Deps = append(r8Deps, transitiveClasspath...)
+
flagFiles := android.Paths{
android.PathForSource(ctx, "build/make/core/proguard.flags"),
}
@@ -342,6 +365,8 @@
if useR8 {
proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+ proguardConfiguration := android.PathForModuleOut(ctx, "proguard_configuration")
+ d.proguardConfiguration = android.OptionalPathForPath(proguardConfiguration)
proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
@@ -354,6 +379,7 @@
"r8Flags": strings.Join(append(commonFlags, r8Flags...), " "),
"zipFlags": zipFlags,
"outDict": proguardDictionary.String(),
+ "outConfig": proguardConfiguration.String(),
"outUsageDir": proguardUsageDir.String(),
"outUsage": proguardUsage.String(),
"outUsageZip": proguardUsageZip.String(),
diff --git a/java/dex_test.go b/java/dex_test.go
index fc6cd0f..dc85f9e 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -18,6 +18,8 @@
"testing"
"android/soong/android"
+
+ "github.com/google/blueprint/proptools"
)
func TestR8(t *testing.T) {
@@ -74,7 +76,7 @@
android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
appR8.Args["r8Flags"], libHeader.String())
- android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app javac classpath",
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
appR8.Args["r8Flags"], staticLibHeader.String())
android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
appR8.Args["r8Flags"], "-ignorewarnings")
@@ -86,6 +88,174 @@
corePlatformAppR8.Args["r8Flags"], "--android-platform-build")
}
+func TestR8TransitiveDeps(t *testing.T) {
+ bp := `
+ override_android_app {
+ name: "override_app",
+ base: "app",
+ }
+
+ android_app {
+ name: "app",
+ srcs: ["foo.java"],
+ libs: [
+ "lib",
+ "uses_libs_dep_import",
+ ],
+ static_libs: [
+ "static_lib",
+ "repeated_dep",
+ ],
+ platform_apis: true,
+ }
+
+ java_library {
+ name: "static_lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "lib",
+ libs: [
+ "transitive_lib",
+ "repeated_dep",
+ "prebuilt_lib",
+ ],
+ static_libs: ["transitive_static_lib"],
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "repeated_dep",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "transitive_static_lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "transitive_lib",
+ srcs: ["foo.java"],
+ libs: ["transitive_lib_2"],
+ }
+
+ java_library {
+ name: "transitive_lib_2",
+ srcs: ["foo.java"],
+ }
+
+ java_import {
+ name: "lib",
+ jars: ["lib.jar"],
+ }
+
+ java_library {
+ name: "uses_lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "optional_uses_lib",
+ srcs: ["foo.java"],
+ }
+
+ android_library {
+ name: "uses_libs_dep",
+ uses_libs: ["uses_lib"],
+ optional_uses_libs: ["optional_uses_lib"],
+ }
+
+ android_library_import {
+ name: "uses_libs_dep_import",
+ aars: ["aar.aar"],
+ static_libs: ["uses_libs_dep"],
+ }
+ `
+
+ testcases := []struct {
+ name string
+ unbundled bool
+ }{
+ {
+ name: "non-unbundled build",
+ unbundled: false,
+ },
+ {
+ name: "unbundled build",
+ unbundled: true,
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.name, func(t *testing.T) {
+ fixturePreparer := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd
+ if tc.unbundled {
+ fixturePreparer = android.GroupFixturePreparers(
+ fixturePreparer,
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.Unbundled_build = proptools.BoolPtr(true)
+ },
+ ),
+ )
+ }
+ result := fixturePreparer.RunTestWithBp(t, bp)
+
+ getHeaderJar := func(name string) android.Path {
+ mod := result.ModuleForTests(name, "android_common")
+ return mod.Output("turbine-combined/" + name + ".jar").Output
+ }
+
+ appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+ overrideAppR8 := result.ModuleForTests("app", "android_common_override_app").Rule("r8")
+ appHeader := getHeaderJar("app")
+ overrideAppHeader := result.ModuleForTests("app", "android_common_override_app").Output("turbine-combined/app.jar").Output
+ libHeader := getHeaderJar("lib")
+ transitiveLibHeader := getHeaderJar("transitive_lib")
+ transitiveLib2Header := getHeaderJar("transitive_lib_2")
+ staticLibHeader := getHeaderJar("static_lib")
+ transitiveStaticLibHeader := getHeaderJar("transitive_static_lib")
+ repeatedDepHeader := getHeaderJar("repeated_dep")
+ usesLibHeader := getHeaderJar("uses_lib")
+ optionalUsesLibHeader := getHeaderJar("optional_uses_lib")
+ prebuiltLibHeader := result.ModuleForTests("prebuilt_lib", "android_common").Output("combined/lib.jar").Output
+
+ for _, rule := range []android.TestingBuildParams{appR8, overrideAppR8} {
+ android.AssertStringDoesNotContain(t, "expected no app header jar in app r8 classpath",
+ rule.Args["r8Flags"], appHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no override_app header jar in app r8 classpath",
+ rule.Args["r8Flags"], overrideAppHeader.String())
+ android.AssertStringDoesContain(t, "expected transitive lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], transitiveLibHeader.String())
+ android.AssertStringDoesContain(t, "expected transitive lib ^2 header jar in app r8 classpath",
+ rule.Args["r8Flags"], transitiveLib2Header.String())
+ android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], libHeader.String())
+ android.AssertStringDoesContain(t, "expected uses_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], usesLibHeader.String())
+ android.AssertStringDoesContain(t, "expected optional_uses_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], optionalUsesLibHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], staticLibHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no transitive static_lib header jar in app r8 classpath",
+ rule.Args["r8Flags"], transitiveStaticLibHeader.String())
+ // we shouldn't list this dep because it is already included as static_libs in the app
+ android.AssertStringDoesNotContain(t, "expected no repeated_dep header jar in app r8 classpath",
+ rule.Args["r8Flags"], repeatedDepHeader.String())
+ // skip a prebuilt transitive dep if the source is also a transitive dep
+ android.AssertStringDoesNotContain(t, "expected no prebuilt header jar in app r8 classpath",
+ rule.Args["r8Flags"], prebuiltLibHeader.String())
+ android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
+ rule.Args["r8Flags"], "-ignorewarnings")
+ android.AssertStringDoesContain(t, "expected --android-platform-build in app r8 flags",
+ rule.Args["r8Flags"], "--android-platform-build")
+ }
+ })
+ }
+}
+
func TestR8Flags(t *testing.T) {
result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
android_app {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 77cbe9c..c4b0af4 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -27,6 +27,7 @@
dexpreoptDisabled(ctx android.BaseModuleContext) bool
DexpreoptBuiltInstalledForApex() []dexpreopterInstall
AndroidMkEntriesForApex() []android.AndroidMkEntries
+ ProfilePathOnHost() android.Path
}
type dexpreopterInstall struct {
@@ -103,6 +104,9 @@
// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
// dexpreopt another partition).
configPath android.WritablePath
+
+ // The path to the profile on host.
+ profilePathOnHost android.Path
}
type DexpreoptProperties struct {
@@ -180,9 +184,8 @@
isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
if isApexVariant(ctx) {
- // Don't preopt APEX variant module unless the module is an APEX system server jar and we are
- // building the entire system image.
- if !isApexSystemServerJar || ctx.Config().UnbundledBuild() {
+ // Don't preopt APEX variant module unless the module is an APEX system server jar.
+ if !isApexSystemServerJar {
return true
}
} else {
@@ -368,21 +371,29 @@
installBase := filepath.Base(install.To)
arch := filepath.Base(installDir)
installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+ isProfile := strings.HasSuffix(installBase, ".prof")
+
+ if isProfile {
+ d.profilePathOnHost = install.From
+ }
if isApexSystemServerJar {
- // APEX variants of java libraries are hidden from Make, so their dexpreopt
- // outputs need special handling. Currently, for APEX variants of java
- // libraries, only those in the system server classpath are handled here.
- // Preopting of boot classpath jars in the ART APEX are handled in
- // java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
- // The installs will be handled by Make as sub-modules of the java library.
- d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
- name: arch + "-" + installBase,
- moduleName: moduleName(ctx),
- outputPathOnHost: install.From,
- installDirOnDevice: installPath,
- installFileOnDevice: installBase,
- })
+ // Profiles are handled separately because they are installed into the APEX.
+ if !isProfile {
+ // APEX variants of java libraries are hidden from Make, so their dexpreopt
+ // outputs need special handling. Currently, for APEX variants of java
+ // libraries, only those in the system server classpath are handled here.
+ // Preopting of boot classpath jars in the ART APEX are handled in
+ // java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
+ // The installs will be handled by Make as sub-modules of the java library.
+ d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
+ name: arch + "-" + installBase,
+ moduleName: moduleName(ctx),
+ outputPathOnHost: install.From,
+ installDirOnDevice: installPath,
+ installFileOnDevice: installBase,
+ })
+ }
} else if !d.preventInstall {
ctx.InstallFile(installPath, installBase, install.From)
}
@@ -404,3 +415,7 @@
}
return entries
}
+
+func (d *dexpreopter) ProfilePathOnHost() android.Path {
+ return d.profilePathOnHost
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index aa55f37..01a2c14 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -602,8 +602,15 @@
Flag("-J-Xmx1600m").
Flag("-J-XX:-OmitStackTraceInFastThrow").
Flag("-XDignore.symbol.file").
- FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+ Flag("--ignore-source-errors").
+ // b/240421555: use a stub doclet until Doclava works with JDK 17
+ //FlagWithArg("-doclet ", "com.google.doclava.Doclava").
+ FlagWithArg("-doclet ", "com.google.stubdoclet.StubDoclet").
FlagWithInputList("-docletpath ", docletPath.Paths(), ":").
+ FlagWithArg("-Xmaxerrs ", "1").
+ FlagWithArg("-Xmaxwarns ", "1").
+ Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED").
+ Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED").
FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
@@ -687,7 +694,7 @@
outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
cmd := rule.Command().
- BuiltTool("soong_javac_wrapper").Tool(android.PathForSource(ctx, "prebuilts/jdk/jdk11/linux-x86/bin/javadoc")).
+ BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
Flag(config.JavacVmFlags).
FlagWithArg("-encoding ", "UTF-8").
FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs).
@@ -773,6 +780,8 @@
jsilver := ctx.Config().HostJavaToolPath(ctx, "jsilver.jar")
doclava := ctx.Config().HostJavaToolPath(ctx, "doclava.jar")
+ // b/240421555: use a stub doclet until Doclava works with JDK 17
+ stubdoclet := ctx.Config().HostJavaToolPath(ctx, "stubdoclet.jar")
outDir := android.PathForModuleOut(ctx, "out")
srcJarDir := android.PathForModuleOut(ctx, "srcjars")
@@ -800,7 +809,8 @@
if Bool(d.properties.Dokka_enabled) {
desc = "dokka"
} else {
- d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava})
+ // b/240421555: use a stub doclet until Doclava works with JDK 17
+ d.doclavaDocsFlags(ctx, cmd, classpath{jsilver, doclava, stubdoclet})
for _, o := range d.Javadoc.properties.Out {
cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
@@ -818,9 +828,9 @@
FlagWithArg("-C ", outDir.String()).
FlagWithArg("-D ", outDir.String())
- rule.Restat()
+ // rule.Restat()
- zipSyncCleanupCmd(rule, srcJarDir)
+ // zipSyncCleanupCmd(rule, srcJarDir)
rule.Build("javadoc", desc)
}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 4bbe70a..8a521aa 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -148,6 +148,10 @@
// path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
// what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
Extensions_info_file *string `android:"path"`
+
+ // API surface of this module. If set, the module contributes to an API surface.
+ // For the full list of available API surfaces, refer to soong/android/sdk_version.go
+ Api_surface *string
}
// Used by xsd_config
@@ -178,6 +182,10 @@
&module.Javadoc.properties)
InitDroiddocModule(module, android.HostAndDeviceSupported)
+
+ module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+ module.createApiContribution(ctx)
+ })
return module
}
@@ -862,6 +870,25 @@
}, attrs)
}
+func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
+ api_file := d.properties.Check_api.Current.Api_file
+ api_surface := d.properties.Api_surface
+
+ props := struct {
+ Name *string
+ Api_surface *string
+ Api_file *string
+ Visibility []string
+ }{}
+
+ props.Name = proptools.StringPtr(d.Name() + ".api.contribution")
+ props.Api_surface = api_surface
+ props.Api_file = api_file
+ props.Visibility = []string{"//visibility:override", "//visibility:public"}
+
+ ctx.CreateModule(ApiContributionFactory, &props)
+}
+
// TODO (b/262014796): Export the API contributions of CorePlatformApi
// A map to populate the api surface of a droidstub from a substring appearing in its name
// This map assumes that droidstubs (either checked-in or created by java_sdk_library)
@@ -876,6 +903,7 @@
"system": android.SdkSystem,
"module_lib": android.SdkModule,
"module-lib": android.SdkModule,
+ "platform.api": android.SdkCorePlatform,
"test": android.SdkTest,
"toolchain": android.SdkToolchain,
}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index ef2e6dc..7a04d73 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -346,3 +346,60 @@
android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name))
}
}
+
+func TestDroidStubsApiContributionGeneration(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droidstubs {
+ name: "foo",
+ srcs: ["A/a.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "A/current.txt",
+ removed_api_file: "A/removed.txt",
+ }
+ }
+ }
+ `,
+ map[string][]byte{
+ "A/a.java": nil,
+ "A/current.txt": nil,
+ "A/removed.txt": nil,
+ },
+ )
+
+ ctx.ModuleForTests("foo.api.contribution", "")
+}
+
+func TestGeneratedApiContributionVisibilityTest(t *testing.T) {
+ library_bp := `
+ java_api_library {
+ name: "bar",
+ api_surface: "public",
+ api_contributions: ["foo.api.contribution"],
+ }
+ `
+ ctx, _ := testJavaWithFS(t, `
+ droidstubs {
+ name: "foo",
+ srcs: ["A/a.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "A/current.txt",
+ removed_api_file: "A/removed.txt",
+ }
+ },
+ visibility: ["//a"],
+ }
+ `,
+ map[string][]byte{
+ "a/a.java": nil,
+ "a/current.txt": nil,
+ "a/removed.txt": nil,
+ "b/Android.bp": []byte(library_bp),
+ },
+ )
+
+ ctx.ModuleForTests("bar", "android_common")
+}
diff --git a/java/java.go b/java/java.go
index 3b0ad8d..659f98a 100644
--- a/java/java.go
+++ b/java/java.go
@@ -230,6 +230,12 @@
// against this module. If empty, ImplementationJars should be used instead.
HeaderJars android.Paths
+ // set of header jars for all transitive libs deps
+ TransitiveLibsHeaderJars *android.DepSet
+
+ // set of header jars for all transitive static libs deps
+ TransitiveStaticLibsHeaderJars *android.DepSet
+
// ImplementationAndResourceJars is a list of jars that contain the implementations of classes
// in the module as well as any resources included in the module.
ImplementationAndResourcesJars android.Paths
@@ -380,6 +386,7 @@
instrumentationForTag = dependencyTag{name: "instrumentation_for"}
extraLintCheckTag = dependencyTag{name: "extra-lint-check", toolchain: true}
jniLibTag = dependencyTag{name: "jnilib", runtimeLinked: true}
+ r8LibraryJarTag = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
jniInstallTag = installDependencyTag{name: "jni install"}
binaryInstallTag = installDependencyTag{name: "binary install"}
@@ -1587,7 +1594,11 @@
var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{})
func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- apiFile := android.PathForModuleSrc(ctx, String(ap.properties.Api_file))
+ var apiFile android.Path = nil
+ if apiFileString := ap.properties.Api_file; apiFileString != nil {
+ apiFile = android.PathForModuleSrc(ctx, String(apiFileString))
+ }
+
ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{
ApiFile: apiFile,
})
@@ -1618,13 +1629,17 @@
// List of flags to be passed to the javac compiler to generate jar file
Javacflags []string
+
+ // List of shared java libs that this module has dependencies to and
+ // should be passed as classpath in javac invocation
+ Libs []string
}
func ApiLibraryFactory() android.Module {
module := &ApiLibrary{}
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
module.AddProperties(&module.properties)
+ android.InitDefaultableModule(module)
return module
}
@@ -1689,6 +1704,7 @@
for _, apiContributionName := range apiContributions {
ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName)
}
+ ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...)
}
func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1706,10 +1722,22 @@
homeDir := android.PathForModuleOut(ctx, "metalava", "home")
- var srcFiles []android.Path
- ctx.VisitDirectDepsWithTag(javaApiContributionTag, func(dep android.Module) {
- provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
- srcFiles = append(srcFiles, android.PathForSource(ctx, provider.ApiFile.String()))
+ var srcFiles android.Paths
+ var classPaths android.Paths
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ tag := ctx.OtherModuleDependencyTag(dep)
+ switch tag {
+ case javaApiContributionTag:
+ provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
+ providerApiFile := provider.ApiFile
+ if providerApiFile == nil {
+ ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
+ }
+ srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String()))
+ case libTag:
+ provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+ classPaths = append(classPaths, provider.HeaderJars...)
+ }
})
// Add the api_files inputs
@@ -1739,11 +1767,16 @@
var flags javaBuilderFlags
flags.javaVersion = getStubsJavaVersion()
flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
+ flags.classpath = classpath(classPaths)
TransformJavaToClasses(ctx, al.stubsJar, 0, android.Paths{},
android.Paths{al.stubsSrcJar}, flags, android.Paths{})
ctx.Phony(ctx.ModuleName(), al.stubsJar)
+
+ ctx.SetProvider(JavaInfoProvider, JavaInfo{
+ HeaderJars: android.PathsIfNonNil(al.stubsJar),
+ })
}
//
@@ -1926,9 +1959,9 @@
var flags javaBuilderFlags
+ j.collectTransitiveHeaderJars(ctx)
ctx.VisitDirectDeps(func(module android.Module) {
tag := ctx.OtherModuleDependencyTag(module)
-
if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
switch tag {
@@ -2018,6 +2051,8 @@
ctx.SetProvider(JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(j.combinedClasspathFile),
+ TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars,
+ TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
ImplementationJars: android.PathsIfNonNil(j.combinedClasspathFile),
AidlIncludeDirs: j.exportAidlIncludeDirs,
@@ -2402,6 +2437,7 @@
&RuntimeResourceOverlayProperties{},
&LintProperties{},
&appTestHelperAppProperties{},
+ &JavaApiLibraryProperties{},
)
android.InitDefaultsModule(module)
@@ -2526,9 +2562,10 @@
type javaCommonAttributes struct {
*javaResourcesAttributes
- Srcs bazel.LabelListAttribute
- Plugins bazel.LabelListAttribute
- Javacopts bazel.StringListAttribute
+ Srcs bazel.LabelListAttribute
+ Plugins bazel.LabelListAttribute
+ Javacopts bazel.StringListAttribute
+ Common_srcs bazel.LabelListAttribute
}
type javaDependencyLabels struct {
@@ -2566,7 +2603,7 @@
// to be returned to the calling function.
func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
var srcs bazel.LabelListAttribute
- var deps bazel.LabelList
+ var deps bazel.LabelListAttribute
var staticDeps bazel.LabelList
archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
@@ -2672,18 +2709,17 @@
Javacopts: bazel.MakeStringListAttribute(javacopts),
}
- if m.properties.Libs != nil {
-
- // TODO 244210934 ALIX Check if this else statement breaks presubmits get rid of it if it doesn't
- if strings.HasPrefix(ctx.ModuleType(), "java_binary") || strings.HasPrefix(ctx.ModuleType(), "java_library") || ctx.ModuleType() == "android_library" {
- for _, d := range m.properties.Libs {
- neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
- neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
- deps.Add(&neverlinkLabel)
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*CommonProperties); ok {
+ var libLabels []bazel.Label
+ for _, d := range archProps.Libs {
+ neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d)
+ neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink"
+ libLabels = append(libLabels, neverlinkLabel)
+ }
+ deps.SetSelectValue(axis, config, bazel.MakeLabelList(libLabels))
}
-
- } else {
- deps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Libs))))
}
}
@@ -2701,7 +2737,7 @@
staticDeps.Add(protoDepLabel)
depLabels := &javaDependencyLabels{}
- depLabels.Deps = bazel.MakeLabelListAttribute(deps)
+ depLabels.Deps = deps
depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
bp2BuildInfo := &bp2BuildJavaInfo{
@@ -2714,10 +2750,9 @@
type javaLibraryAttributes struct {
*javaCommonAttributes
- Deps bazel.LabelListAttribute
- Exports bazel.LabelListAttribute
- Neverlink bazel.BoolAttribute
- Common_srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Exports bazel.LabelListAttribute
+ Neverlink bazel.BoolAttribute
}
func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
@@ -2751,7 +2786,7 @@
Bzl_load_location: "//build/bazel/rules/java:library.bzl",
}
} else {
- attrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
+ attrs.javaCommonAttributes.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
props = bazel.BazelTargetModuleProperties{
Rule_class: "kt_jvm_library",
@@ -2807,14 +2842,8 @@
mainClass = mainClassInManifest
}
- attrs := &javaBinaryHostAttributes{
- javaCommonAttributes: commonAttrs,
- Deps: deps,
- Runtime_deps: runtimeDeps,
- Main_class: mainClass,
- }
-
// Attribute jvm_flags
+ var jvmFlags bazel.StringListAttribute
if m.binaryProperties.Jni_libs != nil {
jniLibPackages := map[string]bool{}
for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes {
@@ -2837,12 +2866,56 @@
// See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH
jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage)
}
- attrs.Jvm_flags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
+ jvmFlags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "java_binary",
}
+ attrs := &javaBinaryHostAttributes{
+ Runtime_deps: runtimeDeps,
+ Main_class: mainClass,
+ Jvm_flags: jvmFlags,
+ }
+
+ if !bp2BuildInfo.hasKotlinSrcs && len(m.properties.Common_srcs) == 0 {
+ attrs.javaCommonAttributes = commonAttrs
+ attrs.Deps = deps
+ } else {
+ ktName := m.Name() + "_kt"
+ ktProps := bazel.BazelTargetModuleProperties{
+ Rule_class: "kt_jvm_library",
+ Bzl_load_location: "@rules_kotlin//kotlin:jvm_library.bzl",
+ }
+ ktAttrs := &javaLibraryAttributes{
+ Deps: deps,
+ javaCommonAttributes: &javaCommonAttributes{
+ Srcs: commonAttrs.Srcs,
+ Plugins: commonAttrs.Plugins,
+ Javacopts: commonAttrs.Javacopts,
+ },
+ }
+
+ if len(m.properties.Common_srcs) != 0 {
+ ktAttrs.javaCommonAttributes.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
+ }
+
+ // kt_jvm_library does not support resource_strip_prefix, if this attribute
+ // is set, than javaResourcesAttributes needs to be set in the
+ // javaCommonAttributes of the java_binary target
+ if commonAttrs.javaResourcesAttributes != nil {
+ if commonAttrs.javaResourcesAttributes.Resource_strip_prefix != nil {
+ attrs.javaCommonAttributes = &javaCommonAttributes{
+ javaResourcesAttributes: commonAttrs.javaResourcesAttributes,
+ }
+ } else {
+ ktAttrs.javaCommonAttributes.javaResourcesAttributes = commonAttrs.javaResourcesAttributes
+ }
+ }
+
+ ctx.CreateBazelTargetModule(ktProps, android.CommonAttributes{Name: ktName}, ktAttrs)
+ attrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + ktName}})
+ }
// Create the BazelTargetModule.
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/java/java_test.go b/java/java_test.go
index 085f627..21993ec 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -30,7 +30,6 @@
"android/soong/cc"
"android/soong/dexpreopt"
"android/soong/genrule"
- "android/soong/python"
)
// Legacy preparer used for running tests within the java package.
@@ -49,7 +48,6 @@
// Include all the default java modules.
PrepareForTestWithJavaDefaultModules,
PrepareForTestWithOverlayBuildComponents,
- python.PrepareForTestWithPythonBuildComponents,
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
}),
@@ -1440,24 +1438,26 @@
}
func TestDataNativeBinaries(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
java_test_host {
name: "foo",
srcs: ["a.java"],
data_native_bins: ["bin"]
}
- python_binary_host {
+ cc_binary_host {
name: "bin",
- srcs: ["bin.py"],
+ srcs: ["bin.cpp"],
}
- `)
+ `).TestContext
buildOS := ctx.Config().BuildOS.String()
test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
- expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+ expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64/bin:bin"}
actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
}
@@ -1840,6 +1840,20 @@
}`)
}
+func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+ testJavaError(t,
+ "Error: foo has an empty api file.",
+ `java_api_contribution {
+ name: "foo",
+ }
+ java_api_library {
+ name: "bar",
+ api_surface: "public",
+ api_contributions: ["foo"],
+ }
+ `)
+}
+
func TestJavaApiLibraryAndProviderLink(t *testing.T) {
provider_bp_a := `
java_api_contribution {
@@ -1894,6 +1908,98 @@
}
}
+func TestJavaApiLibraryAndDefaultsLink(t *testing.T) {
+ provider_bp_a := `
+ java_api_contribution {
+ name: "foo1",
+ api_file: "foo1.txt",
+ }
+ `
+ provider_bp_b := `
+ java_api_contribution {
+ name: "foo2",
+ api_file: "foo2.txt",
+ }
+ `
+ provider_bp_c := `
+ java_api_contribution {
+ name: "foo3",
+ api_file: "foo3.txt",
+ }
+ `
+ provider_bp_d := `
+ java_api_contribution {
+ name: "foo4",
+ api_file: "foo4.txt",
+ }
+ `
+ ctx, _ := testJavaWithFS(t, `
+ java_defaults {
+ name: "baz1",
+ api_surface: "public",
+ api_contributions: ["foo1", "foo2"],
+ }
+
+ java_defaults {
+ name: "baz2",
+ api_surface: "system",
+ api_contributions: ["foo3"],
+ }
+
+ java_api_library {
+ name: "bar1",
+ api_surface: "public",
+ api_contributions: ["foo1"],
+ }
+
+ java_api_library {
+ name: "bar2",
+ api_surface: "public",
+ defaults:["baz1"],
+ }
+
+ java_api_library {
+ name: "bar3",
+ api_surface: "system",
+ defaults:["baz1", "baz2"],
+ api_contributions: ["foo4"],
+ api_files: ["api1/current.txt", "api2/current.txt"]
+ }
+ `,
+ map[string][]byte{
+ "a/Android.bp": []byte(provider_bp_a),
+ "b/Android.bp": []byte(provider_bp_b),
+ "c/Android.bp": []byte(provider_bp_c),
+ "d/Android.bp": []byte(provider_bp_d),
+ })
+
+ testcases := []struct {
+ moduleName string
+ sourceTextFileDirs []string
+ }{
+ {
+ moduleName: "bar1",
+ sourceTextFileDirs: []string{"a/foo1.txt"},
+ },
+ {
+ moduleName: "bar2",
+ sourceTextFileDirs: []string{"a/foo1.txt", "b/foo2.txt"},
+ },
+ {
+ moduleName: "bar3",
+ sourceTextFileDirs: []string{"c/foo3.txt", "a/foo1.txt", "b/foo2.txt", "d/foo4.txt", "api1/current.txt", "api2/current.txt"},
+ },
+ }
+ for _, c := range testcases {
+ m := ctx.ModuleForTests(c.moduleName, "android_common")
+ manifest := m.Output("metalava.sbox.textproto")
+ sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+ manifestCommand := sboxProto.Commands[0].GetCommand()
+ sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ")
+ android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
+ }
+}
+
func TestJavaApiLibraryJarGeneration(t *testing.T) {
provider_bp_a := `
java_api_contribution {
@@ -1901,7 +2007,8 @@
api_file: "foo1.txt",
}
`
- provider_bp_b := `java_api_contribution {
+ provider_bp_b := `
+ java_api_contribution {
name: "foo2",
api_file: "foo2.txt",
}
@@ -1946,6 +2053,81 @@
}
}
+func TestJavaApiLibraryLibsLink(t *testing.T) {
+ provider_bp_a := `
+ java_api_contribution {
+ name: "foo1",
+ api_file: "foo1.txt",
+ }
+ `
+ provider_bp_b := `
+ java_api_contribution {
+ name: "foo2",
+ api_file: "foo2.txt",
+ }
+ `
+ lib_bp_a := `
+ java_library {
+ name: "lib1",
+ srcs: ["Lib.java"],
+ }
+ `
+ lib_bp_b := `
+ java_library {
+ name: "lib2",
+ srcs: ["Lib.java"],
+ }
+ `
+
+ ctx, _ := testJavaWithFS(t, `
+ java_api_library {
+ name: "bar1",
+ api_surface: "public",
+ api_contributions: ["foo1"],
+ libs: ["lib1"],
+ }
+
+ java_api_library {
+ name: "bar2",
+ api_surface: "system",
+ api_contributions: ["foo1", "foo2"],
+ libs: ["lib1", "lib2", "bar1"],
+ }
+ `,
+ map[string][]byte{
+ "a/Android.bp": []byte(provider_bp_a),
+ "b/Android.bp": []byte(provider_bp_b),
+ "c/Android.bp": []byte(lib_bp_a),
+ "c/Lib.java": {},
+ "d/Android.bp": []byte(lib_bp_b),
+ "d/Lib.java": {},
+ })
+
+ testcases := []struct {
+ moduleName string
+ classPathJarNames []string
+ }{
+ {
+ moduleName: "bar1",
+ classPathJarNames: []string{"lib1.jar"},
+ },
+ {
+ moduleName: "bar2",
+ classPathJarNames: []string{"lib1.jar", "lib2.jar", "bar1/android.jar"},
+ },
+ }
+ for _, c := range testcases {
+ m := ctx.ModuleForTests(c.moduleName, "android_common")
+ javacRules := m.Rule("javac")
+ classPathArgs := javacRules.Args["classpath"]
+ for _, jarName := range c.classPathJarNames {
+ if !strings.Contains(classPathArgs, jarName) {
+ t.Errorf("Module output does not contain expected jar %s", jarName)
+ }
+ }
+ }
+}
+
func TestTradefedOptions(t *testing.T) {
result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
java_test_host {
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 491ce29..933fc51 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -44,6 +44,10 @@
kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
Output("turbine-combined/kotlin-stdlib.jar").Output
+ kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
+ Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
+ kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
+ Output("turbine-combined/kotlin-stdlib-jdk8.jar").Output
kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
Output("turbine-combined/kotlin-annotations.jar").Output
@@ -79,6 +83,16 @@
fooJar.Inputs.Strings(), kotlinStdlib.String())
}
+ if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %v",
+ fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
+ }
+
+ if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
+ t.Errorf("foo jar inputs %v does not contain %v",
+ fooJar.Inputs.Strings(), kotlinStdlibJdk8.String())
+ }
+
if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
t.Errorf("foo jar inputs %v does not contain %v",
fooJar.Inputs.Strings(), kotlinAnnotations.String())
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 3b64bf7..a2295f4 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1599,6 +1599,7 @@
Srcs []string
Installable *bool
Sdk_version *string
+ Api_surface *string
System_modules *string
Libs []string
Output_javadoc_comments *bool
@@ -1638,6 +1639,7 @@
props.Srcs = append(props.Srcs, module.properties.Srcs...)
props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
props.Sdk_version = module.deviceProperties.Sdk_version
+ props.Api_surface = &apiScope.name
props.System_modules = module.deviceProperties.System_modules
props.Installable = proptools.BoolPtr(false)
// A droiddoc module has only one Libs property and doesn't distinguish between
@@ -1747,7 +1749,7 @@
}
}
- mctx.CreateModule(DroidstubsFactory, &props)
+ mctx.CreateModule(DroidstubsFactory, &props).(*Droidstubs).CallHookIfAvailable(mctx)
}
func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 210bfc3..1d0c13d 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -120,6 +120,7 @@
result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
+ result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
result.ModuleForTests("foo.api.public.28", "")
result.ModuleForTests("foo.api.system.28", "")
diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go
index f8c930a..5f55f6a 100644
--- a/mk2rbc/android_products_test.go
+++ b/mk2rbc/android_products_test.go
@@ -29,7 +29,6 @@
}
expectedProducts := map[string]string{
"aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"),
- "aosp_tv_arm": abspath("aosp_tv_arm.mk"),
"aosp_tv_arm64": abspath("aosp_tv_arm64.mk"),
}
if !reflect.DeepEqual(actualProducts, expectedProducts) {
diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test
index a2220ed..400ec35 100644
--- a/mk2rbc/test/android_products.mk.test
+++ b/mk2rbc/test/android_products.mk.test
@@ -1,4 +1,3 @@
PRODUCT_MAKEFILES := \
- $(LOCAL_DIR)/aosp_tv_arm.mk \
$(LOCAL_DIR)/aosp_tv_arm64.mk \
aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file
diff --git a/python/Android.bp b/python/Android.bp
index e49fa6a..7578673 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -9,13 +9,13 @@
"blueprint",
"soong-android",
"soong-tradefed",
+ "soong-cc",
],
srcs: [
- "androidmk.go",
"binary.go",
+ "bp2build.go",
"builder.go",
"defaults.go",
- "installer.go",
"library.go",
"proto.go",
"python.go",
diff --git a/python/androidmk.go b/python/androidmk.go
deleted file mode 100644
index 7dc4713..0000000
--- a/python/androidmk.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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 (
- "path/filepath"
- "strings"
-
- "android/soong/android"
-)
-
-type subAndroidMkProvider interface {
- AndroidMk(*Module, *android.AndroidMkEntries)
-}
-
-func (p *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
- if p.subAndroidMkOnce == nil {
- p.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
- }
- if androidmk, ok := obj.(subAndroidMkProvider); ok {
- if !p.subAndroidMkOnce[androidmk] {
- p.subAndroidMkOnce[androidmk] = true
- androidmk.AndroidMk(p, entries)
- }
- }
-}
-
-func (p *Module) AndroidMkEntries() []android.AndroidMkEntries {
- entries := android.AndroidMkEntries{OutputFile: p.installSource}
-
- p.subAndroidMk(&entries, p.installer)
-
- return []android.AndroidMkEntries{entries}
-}
-
-func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Class = "EXECUTABLES"
-
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
- })
- base.subAndroidMk(entries, p.pythonInstaller)
-}
-
-func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Class = "NATIVE_TESTS"
-
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
- if p.testConfig != nil {
- entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
- }
-
- entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
-
- entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-
- p.testProperties.Test_options.SetAndroidMkEntries(entries)
- })
- base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
-}
-
-func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
- entries.Required = append(entries.Required, "libc++")
- entries.ExtraEntries = append(entries.ExtraEntries,
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- path, file := filepath.Split(installer.path.String())
- stem := strings.TrimSuffix(file, filepath.Ext(file))
-
- entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
- entries.SetString("LOCAL_MODULE_PATH", path)
- entries.SetString("LOCAL_MODULE_STEM", stem)
- entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
- entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
- })
-}
diff --git a/python/binary.go b/python/binary.go
index 670e0d3..75135f3 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -18,11 +18,10 @@
import (
"fmt"
+ "path/filepath"
+ "strings"
"android/soong/android"
- "android/soong/bazel"
-
- "github.com/google/blueprint/proptools"
)
func init() {
@@ -33,63 +32,6 @@
ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
}
-type bazelPythonBinaryAttributes struct {
- Main *bazel.Label
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Python_version *string
- Imports bazel.StringListAttribute
-}
-
-func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
- // TODO(b/182306917): this doesn't fully handle all nested props versioned
- // by the python version, which would have been handled by the version split
- // mutator. This is sufficient for very simple python_binary_host modules
- // under Bionic.
- py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
- py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
- var python_version *string
- if py3Enabled && py2Enabled {
- panic(fmt.Errorf(
- "error for '%s' module: bp2build's python_binary_host converter does not support "+
- "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
- } else if py2Enabled {
- python_version = &pyVersion2
- } else {
- // do nothing, since python_version defaults to PY3.
- }
-
- baseAttrs := m.makeArchVariantBaseAttributes(ctx)
- attrs := &bazelPythonBinaryAttributes{
- Main: nil,
- Srcs: baseAttrs.Srcs,
- Deps: baseAttrs.Deps,
- Python_version: python_version,
- Imports: baseAttrs.Imports,
- }
-
- for _, propIntf := range m.GetProperties() {
- if props, ok := propIntf.(*BinaryProperties); ok {
- // main is optional.
- if props.Main != nil {
- main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
- attrs.Main = &main
- break
- }
- }
- }
-
- props := bazel.BazelTargetModuleProperties{
- // Use the native py_binary rule.
- Rule_class: "py_binary",
- }
-
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{
- Name: m.Name(),
- Data: baseAttrs.Data,
- }, attrs)
-}
-
type BinaryProperties struct {
// the name of the source file that is the main entry point of the program.
// this file must also be listed in srcs.
@@ -118,49 +60,58 @@
Auto_gen_config *bool
}
-type binaryDecorator struct {
+type PythonBinaryModule struct {
+ PythonLibraryModule
binaryProperties BinaryProperties
- *pythonInstaller
+ // (.intermediate) module output path as installation source.
+ installSource android.Path
+
+ // Final installation path.
+ installedDest android.Path
+
+ androidMkSharedLibs []string
}
+var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
+var _ android.Module = (*PythonBinaryModule)(nil)
+
type IntermPathProvider interface {
IntermPathForModuleOut() android.OptionalPath
}
-func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
- module := newModule(hod, android.MultilibFirst)
- decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}
-
- module.bootstrapper = decorator
- module.installer = decorator
-
- return module, decorator
+func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule {
+ return &PythonBinaryModule{
+ PythonLibraryModule: *newModule(hod, android.MultilibFirst),
+ }
}
func PythonBinaryHostFactory() android.Module {
- module, _ := NewBinary(android.HostSupported)
-
- android.InitBazelModule(module)
-
- return module.init()
+ return NewBinary(android.HostSupported).init()
}
-func (binary *binaryDecorator) autorun() bool {
- return BoolDefault(binary.binaryProperties.Autorun, true)
+func (p *PythonBinaryModule) init() android.Module {
+ p.AddProperties(&p.properties, &p.protoProperties)
+ p.AddProperties(&p.binaryProperties)
+ android.InitAndroidArchModule(p, p.hod, p.multilib)
+ android.InitDefaultableModule(p)
+ android.InitBazelModule(p)
+ return p
}
-func (binary *binaryDecorator) bootstrapperProps() []interface{} {
- return []interface{}{&binary.binaryProperties}
+func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+ p.buildBinary(ctx)
+ p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
+ p.installSource.Base(), p.installSource)
}
-func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
- embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
- depsSrcsZips android.Paths) android.OptionalPath {
-
+func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
+ embeddedLauncher := p.isEmbeddedLauncherEnabled()
+ depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher)
main := ""
- if binary.autorun() {
- main = binary.getPyMainFile(ctx, srcsPathMappings)
+ if p.autorun() {
+ main = p.getPyMainFile(ctx, p.srcsPathMappings)
}
var launcherPath android.OptionalPath
@@ -175,15 +126,88 @@
}
})
}
- binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
- binary.getHostInterpreterName(ctx, actualVersion),
- main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
+ srcsZips := make(android.Paths, 0, len(depsSrcsZips)+1)
+ if embeddedLauncher {
+ srcsZips = append(srcsZips, p.precompiledSrcsZip)
+ } else {
+ srcsZips = append(srcsZips, p.srcsZip)
+ }
+ srcsZips = append(srcsZips, depsSrcsZips...)
+ p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
+ p.getHostInterpreterName(ctx, p.properties.Actual_version),
+ main, p.getStem(ctx), srcsZips)
- return android.OptionalPathForPath(binFile)
+ var sharedLibs []string
+ // if embedded launcher is enabled, we need to collect the shared library dependencies of the
+ // launcher
+ for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
+ sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+ }
+ p.androidMkSharedLibs = sharedLibs
+}
+
+func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
+ entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)}
+
+ entries.Class = "EXECUTABLES"
+
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+ })
+
+ entries.Required = append(entries.Required, "libc++")
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ path, file := filepath.Split(p.installedDest.String())
+ stem := strings.TrimSuffix(file, filepath.Ext(file))
+
+ entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
+ entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+ })
+
+ return []android.AndroidMkEntries{entries}
+}
+
+func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ p.PythonLibraryModule.DepsMutator(ctx)
+
+ if p.isEmbeddedLauncherEnabled() {
+ p.AddDepsOnPythonLauncherAndStdlib(ctx, pythonLibTag, launcherTag, launcherSharedLibTag, p.autorun(), ctx.Target())
+ }
+}
+
+// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
+// fulfilling the android.HostToolProvider interface.
+func (p *PythonBinaryModule) HostToolPath() android.OptionalPath {
+ // TODO: This should only be set when building host binaries -- tests built for device would be
+ // setting this incorrectly.
+ return android.OptionalPathForPath(p.installedDest)
+}
+
+// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
+func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{p.installSource}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
+ return Bool(p.properties.Embedded_launcher)
+}
+
+func (b *PythonBinaryModule) autorun() bool {
+ return BoolDefault(b.binaryProperties.Autorun, true)
}
// get host interpreter name.
-func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext,
actualVersion string) string {
var interp string
switch actualVersion {
@@ -200,13 +224,13 @@
}
// find main program path within runfiles tree.
-func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
+func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
srcsPathMappings []pathMapping) string {
var main string
- if String(binary.binaryProperties.Main) == "" {
+ if String(p.binaryProperties.Main) == "" {
main = ctx.ModuleName() + pyExt
} else {
- main = String(binary.binaryProperties.Main)
+ main = String(p.binaryProperties.Main)
}
for _, path := range srcsPathMappings {
@@ -219,11 +243,21 @@
return ""
}
-func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
+func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string {
stem := ctx.ModuleName()
- if String(binary.binaryProperties.Stem) != "" {
- stem = String(binary.binaryProperties.Stem)
+ if String(p.binaryProperties.Stem) != "" {
+ stem = String(p.binaryProperties.Stem)
}
- return stem + String(binary.binaryProperties.Suffix)
+ return stem + String(p.binaryProperties.Suffix)
+}
+
+func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath {
+ if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" {
+ dir = dir64
+ }
+ if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
+ dir = filepath.Join(dir, ctx.Arch().ArchType.String())
+ }
+ return android.PathForModuleInstall(ctx, dir, relative)
}
diff --git a/python/bp2build.go b/python/bp2build.go
new file mode 100644
index 0000000..bdac2dc
--- /dev/null
+++ b/python/bp2build.go
@@ -0,0 +1,226 @@
+// Copyright 2023 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 (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/bazel"
+)
+
+type bazelPythonLibraryAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Imports bazel.StringListAttribute
+ Srcs_version *string
+}
+
+type bazelPythonProtoLibraryAttributes struct {
+ Deps bazel.LabelListAttribute
+}
+
+type baseAttributes struct {
+ // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
+ //Pkg_path bazel.StringAttribute
+ // TODO: Related to Pkg_bath and similarLy gated
+ //Is_internal bazel.BoolAttribute
+ // Combines Srcs and Exclude_srcs
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ // Combines Data and Java_data (invariant)
+ Data bazel.LabelListAttribute
+ Imports bazel.StringListAttribute
+}
+
+func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
+ var attrs baseAttributes
+ archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
+ for axis, configToProps := range archVariantBaseProps {
+ for config, props := range configToProps {
+ if baseProps, ok := props.(*BaseProperties); ok {
+ attrs.Srcs.SetSelectValue(axis, config,
+ android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
+ attrs.Deps.SetSelectValue(axis, config,
+ android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
+ data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
+ data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
+ attrs.Data.SetSelectValue(axis, config, data)
+ }
+ }
+ }
+
+ partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
+ "proto": android.ProtoSrcLabelPartition,
+ "py": bazel.LabelPartition{Keep_remainder: true},
+ })
+ attrs.Srcs = partitionedSrcs["py"]
+
+ if !partitionedSrcs["proto"].IsEmpty() {
+ protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
+ protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
+
+ pyProtoLibraryName := m.Name() + "_py_proto"
+ ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+ Rule_class: "py_proto_library",
+ Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
+ }, android.CommonAttributes{
+ Name: pyProtoLibraryName,
+ }, &bazelPythonProtoLibraryAttributes{
+ Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
+ })
+
+ attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
+ }
+
+ // Bazel normally requires `import path.from.top.of.tree` statements in
+ // python code, but with soong you can directly import modules from libraries.
+ // Add "imports" attributes to the bazel library so it matches soong's behavior.
+ imports := "."
+ if m.properties.Pkg_path != nil {
+ // TODO(b/215119317) This is a hack to handle the fact that we don't convert
+ // pkg_path properly right now. If the folder structure that contains this
+ // Android.bp file matches pkg_path, we can set imports to an appropriate
+ // number of ../..s to emulate moving the files under a pkg_path folder.
+ pkg_path := filepath.Clean(*m.properties.Pkg_path)
+ if strings.HasPrefix(pkg_path, "/") {
+ ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
+ }
+
+ if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
+ ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
+ }
+ numFolders := strings.Count(pkg_path, "/") + 1
+ dots := make([]string, numFolders)
+ for i := 0; i < numFolders; i++ {
+ dots[i] = ".."
+ }
+ imports = strings.Join(dots, "/")
+ }
+ attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
+
+ return attrs
+}
+
+func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *PythonLibraryModule) {
+ // TODO(b/182306917): this doesn't fully handle all nested props versioned
+ // by the python version, which would have been handled by the version split
+ // mutator. This is sufficient for very simple python_library modules under
+ // Bionic.
+ py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
+ py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+ var python_version *string
+ if py2Enabled && !py3Enabled {
+ python_version = &pyVersion2
+ } else if !py2Enabled && py3Enabled {
+ python_version = &pyVersion3
+ } else if !py2Enabled && !py3Enabled {
+ ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
+ } else {
+ // do nothing, since python_version defaults to PY2ANDPY3
+ }
+
+ baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+
+ attrs := &bazelPythonLibraryAttributes{
+ Srcs: baseAttrs.Srcs,
+ Deps: baseAttrs.Deps,
+ Srcs_version: python_version,
+ Imports: baseAttrs.Imports,
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_library rule.
+ Rule_class: "py_library",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: m.Name(),
+ Data: baseAttrs.Data,
+ }, attrs)
+}
+
+type bazelPythonBinaryAttributes struct {
+ Main *bazel.Label
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Python_version *string
+ Imports bazel.StringListAttribute
+}
+
+func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *PythonBinaryModule) {
+ // TODO(b/182306917): this doesn't fully handle all nested props versioned
+ // by the python version, which would have been handled by the version split
+ // mutator. This is sufficient for very simple python_binary_host modules
+ // under Bionic.
+ py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
+ py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+ var python_version *string
+ if py3Enabled && py2Enabled {
+ panic(fmt.Errorf(
+ "error for '%s' module: bp2build's python_binary_host converter does not support "+
+ "converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
+ } else if py2Enabled {
+ python_version = &pyVersion2
+ } else {
+ // do nothing, since python_version defaults to PY3.
+ }
+
+ baseAttrs := m.makeArchVariantBaseAttributes(ctx)
+ attrs := &bazelPythonBinaryAttributes{
+ Main: nil,
+ Srcs: baseAttrs.Srcs,
+ Deps: baseAttrs.Deps,
+ Python_version: python_version,
+ Imports: baseAttrs.Imports,
+ }
+
+ for _, propIntf := range m.GetProperties() {
+ if props, ok := propIntf.(*BinaryProperties); ok {
+ // main is optional.
+ if props.Main != nil {
+ main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
+ attrs.Main = &main
+ break
+ }
+ }
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ // Use the native py_binary rule.
+ Rule_class: "py_binary",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: m.Name(),
+ Data: baseAttrs.Data,
+ }, attrs)
+}
+
+func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ pythonLibBp2Build(ctx, p)
+}
+
+func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ pythonBinaryBp2Build(ctx, p)
+}
+
+func (p *PythonTestModule) ConvertWithBp2build(_ android.TopDownMutatorContext) {
+ // Tests are currently unsupported
+}
diff --git a/python/builder.go b/python/builder.go
index b4ab206..1066493 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -70,6 +70,17 @@
CommandDeps: []string{"$mergeParCmd"},
},
"srcsZips", "launcher")
+
+ precompile = pctx.AndroidStaticRule("precompilePython", blueprint.RuleParams{
+ Command: `LD_LIBRARY_PATH="$ldLibraryPath" ` +
+ `PYTHONPATH=$stdlibZip/internal/stdlib ` +
+ `$launcher build/soong/python/scripts/precompile_python.py $in $out`,
+ CommandDeps: []string{
+ "$stdlibZip",
+ "$launcher",
+ "build/soong/python/scripts/precompile_python.py",
+ },
+ }, "stdlibZip", "launcher", "ldLibraryPath")
)
func init() {
diff --git a/python/installer.go b/python/installer.go
deleted file mode 100644
index 396f036..0000000
--- a/python/installer.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 (
- "path/filepath"
-
- "android/soong/android"
-)
-
-// This file handles installing python executables into their final location
-
-type installLocation int
-
-const (
- InstallInData installLocation = iota
-)
-
-type pythonInstaller struct {
- dir string
- dir64 string
- relative string
-
- path android.InstallPath
-
- androidMkSharedLibs []string
-}
-
-func NewPythonInstaller(dir, dir64 string) *pythonInstaller {
- return &pythonInstaller{
- dir: dir,
- dir64: dir64,
- }
-}
-
-var _ installer = (*pythonInstaller)(nil)
-
-func (installer *pythonInstaller) installDir(ctx android.ModuleContext) android.InstallPath {
- dir := installer.dir
- if ctx.Arch().ArchType.Multilib == "lib64" && installer.dir64 != "" {
- dir = installer.dir64
- }
- if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
- dir = filepath.Join(dir, ctx.Arch().ArchType.String())
- }
- return android.PathForModuleInstall(ctx, dir, installer.relative)
-}
-
-func (installer *pythonInstaller) install(ctx android.ModuleContext, file android.Path) {
- installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
-}
-
-func (installer *pythonInstaller) setAndroidMkSharedLibs(sharedLibs []string) {
- installer.androidMkSharedLibs = sharedLibs
-}
diff --git a/python/library.go b/python/library.go
index df92df4..7cdb80b 100644
--- a/python/library.go
+++ b/python/library.go
@@ -18,9 +18,6 @@
import (
"android/soong/android"
- "android/soong/bazel"
-
- "github.com/google/blueprint/proptools"
)
func init() {
@@ -33,66 +30,9 @@
}
func PythonLibraryHostFactory() android.Module {
- module := newModule(android.HostSupported, android.MultilibFirst)
-
- android.InitBazelModule(module)
-
- return module.init()
-}
-
-type bazelPythonLibraryAttributes struct {
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- Imports bazel.StringListAttribute
- Srcs_version *string
-}
-
-type bazelPythonProtoLibraryAttributes struct {
- Deps bazel.LabelListAttribute
-}
-
-func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *Module) {
- // TODO(b/182306917): this doesn't fully handle all nested props versioned
- // by the python version, which would have been handled by the version split
- // mutator. This is sufficient for very simple python_library modules under
- // Bionic.
- py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
- py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
- var python_version *string
- if py2Enabled && !py3Enabled {
- python_version = &pyVersion2
- } else if !py2Enabled && py3Enabled {
- python_version = &pyVersion3
- } else if !py2Enabled && !py3Enabled {
- ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
- } else {
- // do nothing, since python_version defaults to PY2ANDPY3
- }
-
- baseAttrs := m.makeArchVariantBaseAttributes(ctx)
-
- attrs := &bazelPythonLibraryAttributes{
- Srcs: baseAttrs.Srcs,
- Deps: baseAttrs.Deps,
- Srcs_version: python_version,
- Imports: baseAttrs.Imports,
- }
-
- props := bazel.BazelTargetModuleProperties{
- // Use the native py_library rule.
- Rule_class: "py_library",
- }
-
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{
- Name: m.Name(),
- Data: baseAttrs.Data,
- }, attrs)
+ return newModule(android.HostSupported, android.MultilibFirst).init()
}
func PythonLibraryFactory() android.Module {
- module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
-
- android.InitBazelModule(module)
-
- return module.init()
+ return newModule(android.HostAndDeviceSupported, android.MultilibBoth).init()
}
diff --git a/python/python.go b/python/python.go
index 24e1bb2..18e5b68 100644
--- a/python/python.go
+++ b/python/python.go
@@ -22,8 +22,6 @@
"regexp"
"strings"
- "android/soong/bazel"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -122,26 +120,13 @@
Embedded_launcher *bool `blueprint:"mutated"`
}
-type baseAttributes struct {
- // TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
- //Pkg_path bazel.StringAttribute
- // TODO: Related to Pkg_bath and similarLy gated
- //Is_internal bazel.BoolAttribute
- // Combines Srcs and Exclude_srcs
- Srcs bazel.LabelListAttribute
- Deps bazel.LabelListAttribute
- // Combines Data and Java_data (invariant)
- Data bazel.LabelListAttribute
- Imports bazel.StringListAttribute
-}
-
// Used to store files of current module after expanding dependencies
type pathMapping struct {
dest string
src android.Path
}
-type Module struct {
+type PythonLibraryModule struct {
android.ModuleBase
android.DefaultableModuleBase
android.BazelModuleBase
@@ -153,16 +138,6 @@
hod android.HostOrDeviceSupported
multilib android.Multilib
- // interface used to bootstrap .par executable when embedded_launcher is true
- // this should be set by Python modules which are runnable, e.g. binaries and tests
- // bootstrapper might be nil (e.g. Python library module).
- bootstrapper bootstrapper
-
- // interface that implements functions required for installation
- // this should be set by Python modules which are runnable, e.g. binaries and tests
- // installer might be nil (e.g. Python library module).
- installer installer
-
// the Python files of current module after expanding source dependencies.
// pathMapping: <dest: runfile_path, src: source_path>
srcsPathMappings []pathMapping
@@ -171,152 +146,62 @@
// pathMapping: <dest: runfile_path, src: source_path>
dataPathMappings []pathMapping
- // the zip filepath for zipping current module source/data files.
+ // The zip file containing the current module's source/data files.
srcsZip android.Path
- // dependency modules' zip filepath for zipping current module source/data files.
- depsSrcsZips android.Paths
-
- // (.intermediate) module output path as installation source.
- installSource android.OptionalPath
-
- // Map to ensure sub-part of the AndroidMk for this module is only added once
- subAndroidMkOnce map[subAndroidMkProvider]bool
+ // The zip file containing the current module's source/data files, with the
+ // source files precompiled.
+ precompiledSrcsZip android.Path
}
// newModule generates new Python base module
-func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
- return &Module{
+func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *PythonLibraryModule {
+ return &PythonLibraryModule{
hod: hod,
multilib: multilib,
}
}
-func (m *Module) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
- var attrs baseAttributes
- archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
- for axis, configToProps := range archVariantBaseProps {
- for config, props := range configToProps {
- if baseProps, ok := props.(*BaseProperties); ok {
- attrs.Srcs.SetSelectValue(axis, config,
- android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
- attrs.Deps.SetSelectValue(axis, config,
- android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
- data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
- data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
- attrs.Data.SetSelectValue(axis, config, data)
- }
- }
- }
-
- partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
- "proto": android.ProtoSrcLabelPartition,
- "py": bazel.LabelPartition{Keep_remainder: true},
- })
- attrs.Srcs = partitionedSrcs["py"]
-
- if !partitionedSrcs["proto"].IsEmpty() {
- protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
- protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
-
- pyProtoLibraryName := m.Name() + "_py_proto"
- ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
- Rule_class: "py_proto_library",
- Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
- }, android.CommonAttributes{
- Name: pyProtoLibraryName,
- }, &bazelPythonProtoLibraryAttributes{
- Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
- })
-
- attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
- }
-
- // Bazel normally requires `import path.from.top.of.tree` statements in
- // python code, but with soong you can directly import modules from libraries.
- // Add "imports" attributes to the bazel library so it matches soong's behavior.
- imports := "."
- if m.properties.Pkg_path != nil {
- // TODO(b/215119317) This is a hack to handle the fact that we don't convert
- // pkg_path properly right now. If the folder structure that contains this
- // Android.bp file matches pkg_path, we can set imports to an appropriate
- // number of ../..s to emulate moving the files under a pkg_path folder.
- pkg_path := filepath.Clean(*m.properties.Pkg_path)
- if strings.HasPrefix(pkg_path, "/") {
- ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
- }
-
- if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
- ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
- }
- numFolders := strings.Count(pkg_path, "/") + 1
- dots := make([]string, numFolders)
- for i := 0; i < numFolders; i++ {
- dots[i] = ".."
- }
- imports = strings.Join(dots, "/")
- }
- attrs.Imports = bazel.MakeStringListAttribute([]string{imports})
-
- return attrs
-}
-
-// bootstrapper interface should be implemented for runnable modules, e.g. binary and test
-type bootstrapper interface {
- bootstrapperProps() []interface{}
- bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
- srcsPathMappings []pathMapping, srcsZip android.Path,
- depsSrcsZips android.Paths) android.OptionalPath
-
- autorun() bool
-}
-
-// installer interface should be implemented for installable modules, e.g. binary and test
-type installer interface {
- install(ctx android.ModuleContext, path android.Path)
- setAndroidMkSharedLibs(sharedLibs []string)
-}
-
// interface implemented by Python modules to provide source and data mappings and zip to python
// modules that depend on it
type pythonDependency interface {
getSrcsPathMappings() []pathMapping
getDataPathMappings() []pathMapping
getSrcsZip() android.Path
+ getPrecompiledSrcsZip() android.Path
}
// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
-func (p *Module) getSrcsPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getSrcsPathMappings() []pathMapping {
return p.srcsPathMappings
}
// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination
-func (p *Module) getDataPathMappings() []pathMapping {
+func (p *PythonLibraryModule) getDataPathMappings() []pathMapping {
return p.dataPathMappings
}
// getSrcsZip returns the filepath where the current module's source/data files are zipped.
-func (p *Module) getSrcsZip() android.Path {
+func (p *PythonLibraryModule) getSrcsZip() android.Path {
return p.srcsZip
}
-var _ pythonDependency = (*Module)(nil)
+// getSrcsZip returns the filepath where the current module's source/data files are zipped.
+func (p *PythonLibraryModule) getPrecompiledSrcsZip() android.Path {
+ return p.precompiledSrcsZip
+}
-var _ android.AndroidMkEntriesProvider = (*Module)(nil)
+func (p *PythonLibraryModule) getBaseProperties() *BaseProperties {
+ return &p.properties
+}
-func (p *Module) init(additionalProps ...interface{}) android.Module {
+var _ pythonDependency = (*PythonLibraryModule)(nil)
+
+func (p *PythonLibraryModule) init() android.Module {
p.AddProperties(&p.properties, &p.protoProperties)
-
- // Add additional properties for bootstrapping/installation
- // This is currently tied to the bootstrapper interface;
- // however, these are a combination of properties for the installation and bootstrapping of a module
- if p.bootstrapper != nil {
- p.AddProperties(p.bootstrapper.bootstrapperProps()...)
- }
-
android.InitAndroidArchModule(p, p.hod, p.multilib)
android.InitDefaultableModule(p)
-
+ android.InitBazelModule(p)
return p
}
@@ -338,36 +223,48 @@
}
var (
- pythonLibTag = dependencyTag{name: "pythonLib"}
- javaDataTag = dependencyTag{name: "javaData"}
+ pythonLibTag = dependencyTag{name: "pythonLib"}
+ javaDataTag = dependencyTag{name: "javaData"}
+ // The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun".
launcherTag = dependencyTag{name: "launcher"}
launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
- pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
- pyExt = ".py"
- protoExt = ".proto"
- pyVersion2 = "PY2"
- pyVersion3 = "PY3"
- internalPath = "internal"
+ // The python interpreter built for host so that we can precompile python sources.
+ // This only works because the precompiled sources don't vary by architecture.
+ // The soong module name is "py3-launcher".
+ hostLauncherTag = dependencyTag{name: "hostLauncher"}
+ hostlauncherSharedLibTag = dependencyTag{name: "hostlauncherSharedLib"}
+ hostStdLibTag = dependencyTag{name: "hostStdLib"}
+ pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+ pyExt = ".py"
+ protoExt = ".proto"
+ pyVersion2 = "PY2"
+ pyVersion3 = "PY3"
+ internalPath = "internal"
)
+type basePropertiesProvider interface {
+ getBaseProperties() *BaseProperties
+}
+
// versionSplitMutator creates version variants for modules and appends the version-specific
// properties for a given variant to the properties in the variant module
func versionSplitMutator() func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
- if base, ok := mctx.Module().(*Module); ok {
- versionNames := []string{}
+ if base, ok := mctx.Module().(basePropertiesProvider); ok {
+ props := base.getBaseProperties()
+ var versionNames []string
// collect version specific properties, so that we can merge version-specific properties
// into the module's overall properties
- versionProps := []VersionProperties{}
+ var versionProps []VersionProperties
// PY3 is first so that we alias the PY3 variant rather than PY2 if both
// are available
- if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) {
+ if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
versionNames = append(versionNames, pyVersion3)
- versionProps = append(versionProps, base.properties.Version.Py3)
+ versionProps = append(versionProps, props.Version.Py3)
}
- if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) {
+ if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
versionNames = append(versionNames, pyVersion2)
- versionProps = append(versionProps, base.properties.Version.Py2)
+ versionProps = append(versionProps, props.Version.Py2)
}
modules := mctx.CreateLocalVariations(versionNames...)
// Alias module to the first variant
@@ -376,9 +273,10 @@
}
for i, v := range versionNames {
// set the actual version for Python module.
- modules[i].(*Module).properties.Actual_version = v
+ newProps := modules[i].(basePropertiesProvider).getBaseProperties()
+ newProps.Actual_version = v
// append versioned properties for the Python module to the overall properties
- err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil)
+ err := proptools.AppendMatchingProperties([]interface{}{newProps}, &versionProps[i], nil)
if err != nil {
panic(err)
}
@@ -387,38 +285,6 @@
}
}
-// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
-// fulfilling HostToolProvider interface.
-func (p *Module) HostToolPath() android.OptionalPath {
- if p.installer != nil {
- if bin, ok := p.installer.(*binaryDecorator); ok {
- // TODO: This should only be set when building host binaries -- tests built for device would be
- // setting this incorrectly.
- return android.OptionalPathForPath(bin.path)
- }
- }
-
- return android.OptionalPath{}
-
-}
-
-// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
-func (p *Module) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- if outputFile := p.installSource; outputFile.Valid() {
- return android.Paths{outputFile.Path()}, nil
- }
- return android.Paths{}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (p *Module) isEmbeddedLauncherEnabled() bool {
- return p.installer != nil && Bool(p.properties.Embedded_launcher)
-}
-
func anyHasExt(paths []string, ext string) bool {
for _, p := range paths {
if filepath.Ext(p) == ext {
@@ -429,7 +295,7 @@
return false
}
-func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
+func (p *PythonLibraryModule) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
return anyHasExt(p.properties.Srcs, ext)
}
@@ -437,7 +303,7 @@
// - handles proto dependencies,
// - if required, specifies launcher and adds launcher dependencies,
// - applies python version mutations to Python dependencies
-func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
android.ProtoDeps(ctx, &p.protoProperties)
versionVariation := []blueprint.Variation{
@@ -452,111 +318,85 @@
// Add python library dependencies for this python version variation
ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
- // If this module will be installed and has an embedded launcher, we need to add dependencies for:
- // * standard library
- // * launcher
- // * shared dependencies of the launcher
- if p.installer != nil && p.isEmbeddedLauncherEnabled() {
- var stdLib string
- var launcherModule string
- // Add launcher shared lib dependencies. Ideally, these should be
- // derived from the `shared_libs` property of the launcher. However, we
- // cannot read the property at this stage and it will be too late to add
- // dependencies later.
- launcherSharedLibDeps := []string{
- "libsqlite",
- }
- // Add launcher-specific dependencies for bionic
- if ctx.Target().Os.Bionic() {
- launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
- }
- if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
- launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
- }
-
- switch p.properties.Actual_version {
- case pyVersion2:
- stdLib = "py2-stdlib"
-
- launcherModule = "py2-launcher"
- if p.bootstrapper.autorun() {
- launcherModule = "py2-launcher-autorun"
- }
-
- launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
-
- case pyVersion3:
- stdLib = "py3-stdlib"
-
- launcherModule = "py3-launcher"
- if p.bootstrapper.autorun() {
- launcherModule = "py3-launcher-autorun"
- }
- if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl {
- launcherModule += "-static"
- }
-
- if ctx.Device() {
- launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
- }
- default:
- panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
- p.properties.Actual_version, ctx.ModuleName()))
- }
- ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
- }
-
// Emulate the data property for java_data but with the arch variation overridden to "common"
// so that it can point to java modules.
javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
+
+ p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget)
}
-func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.generatePythonBuildActions(ctx)
-
- // Only Python binary and test modules have non-empty bootstrapper.
- if p.bootstrapper != nil {
- // if the module is being installed, we need to collect all transitive dependencies to embed in
- // the final par
- p.collectPathsFromTransitiveDeps(ctx)
- // bootstrap the module, including resolving main file, getting launcher path, and
- // registering actions to build the par file
- // bootstrap returns the binary output path
- p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
- p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
+// AddDepsOnPythonLauncherAndStdlib will make the current module depend on the python stdlib,
+// launcher (interpreter), and the launcher's shared libraries. If autorun is true, it will use
+// the autorun launcher instead of the regular one. This function acceps a targetForDeps argument
+// as the target to use for these dependencies. For embedded launcher python binaries, the launcher
+// that will be embedded will be under the same target as the python module itself. But when
+// precompiling python code, we need to get the python launcher built for host, even if we're
+// compiling the python module for device, so we pass a different target to this function.
+func (p *PythonLibraryModule) AddDepsOnPythonLauncherAndStdlib(ctx android.BottomUpMutatorContext,
+ stdLibTag, launcherTag, launcherSharedLibTag blueprint.DependencyTag,
+ autorun bool, targetForDeps android.Target) {
+ var stdLib string
+ var launcherModule string
+ // Add launcher shared lib dependencies. Ideally, these should be
+ // derived from the `shared_libs` property of the launcher. TODO: read these from
+ // the python launcher itself using ctx.OtherModuleProvider() or similar on the result
+ // of ctx.AddFarVariationDependencies()
+ launcherSharedLibDeps := []string{
+ "libsqlite",
+ }
+ // Add launcher-specific dependencies for bionic
+ if targetForDeps.Os.Bionic() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
+ }
+ if targetForDeps.Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
}
- // Only Python binary and test modules have non-empty installer.
- if p.installer != nil {
- var sharedLibs []string
- // if embedded launcher is enabled, we need to collect the shared library depenendencies of the
- // launcher
- for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
- sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
+ switch p.properties.Actual_version {
+ case pyVersion2:
+ stdLib = "py2-stdlib"
+
+ launcherModule = "py2-launcher"
+ if autorun {
+ launcherModule = "py2-launcher-autorun"
}
- p.installer.setAndroidMkSharedLibs(sharedLibs)
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
+ case pyVersion3:
+ stdLib = "py3-stdlib"
- // Install the par file from installSource
- if p.installSource.Valid() {
- p.installer.install(ctx, p.installSource.Path())
+ launcherModule = "py3-launcher"
+ if autorun {
+ launcherModule = "py3-launcher-autorun"
}
+ if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl {
+ launcherModule += "-static"
+ }
+ if ctx.Device() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
+ }
+ default:
+ panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
+ p.properties.Actual_version, ctx.ModuleName()))
}
+ targetVariations := targetForDeps.Variations()
+ if ctx.ModuleName() != stdLib {
+ stdLibVariations := make([]blueprint.Variation, 0, len(targetVariations)+1)
+ stdLibVariations = append(stdLibVariations, blueprint.Variation{Mutator: "python_version", Variation: p.properties.Actual_version})
+ stdLibVariations = append(stdLibVariations, targetVariations...)
+ // Using AddFarVariationDependencies for all of these because they can be for a different
+ // platform, like if the python module itself was being compiled for device, we may want
+ // the python interpreter built for host so that we can precompile python sources.
+ ctx.AddFarVariationDependencies(stdLibVariations, stdLibTag, stdLib)
+ }
+ ctx.AddFarVariationDependencies(targetVariations, launcherTag, launcherModule)
+ ctx.AddFarVariationDependencies(targetVariations, launcherSharedLibTag, launcherSharedLibDeps...)
}
-// generatePythonBuildActions performs build actions common to all Python modules
-func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) {
+// GenerateAndroidBuildActions performs build actions common to all Python modules
+func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
- requiresSrcs := true
- if p.bootstrapper != nil && !p.bootstrapper.autorun() {
- requiresSrcs = false
- }
- if len(expandedSrcs) == 0 && requiresSrcs {
- ctx.ModuleErrorf("doesn't have any source files!")
- }
// expand data files from "data" property.
expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
@@ -589,6 +429,7 @@
// generate the zipfile of all source and data files
p.srcsZip = p.createSrcsZip(ctx, pkgPath)
+ p.precompiledSrcsZip = p.precompileSrcs(ctx)
}
func isValidPythonPath(path string) error {
@@ -607,7 +448,7 @@
// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path>
// for python/data files expanded from properties.
-func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
+func (p *PythonLibraryModule) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
expandedSrcs, expandedData android.Paths) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check current module duplicates.
@@ -642,27 +483,28 @@
}
// createSrcsZip registers build actions to zip current module's sources and data.
-func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
+func (p *PythonLibraryModule) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
relativeRootMap := make(map[string]android.Paths)
- pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
-
var protoSrcs android.Paths
- // "srcs" or "data" properties may contain filegroup so it might happen that
- // the root directory for each source path is different.
- for _, path := range pathMappings {
+ addPathMapping := func(path pathMapping) {
// handle proto sources separately
if path.src.Ext() == protoExt {
protoSrcs = append(protoSrcs, path.src)
} else {
- 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}
- }
+ relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+ relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
}
}
+
+ // "srcs" or "data" properties may contain filegroups so it might happen that
+ // the root directory for each source path is different.
+ for _, path := range p.srcsPathMappings {
+ addPathMapping(path)
+ }
+ for _, path := range p.dataPathMappings {
+ addPathMapping(path)
+ }
+
var zips android.Paths
if len(protoSrcs) > 0 {
protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
@@ -736,30 +578,79 @@
}
}
-// isPythonLibModule returns whether the given module is a Python library Module or not
+func (p *PythonLibraryModule) precompileSrcs(ctx android.ModuleContext) android.Path {
+ // To precompile the python sources, we need a python interpreter and stdlib built
+ // for host. We then use those to compile the python sources, which may be used on either
+ // host of device. Python bytecode is architecture agnostic, so we're essentially
+ // "cross compiling" for device here purely by virtue of host and device python bytecode
+ // being the same.
+ var stdLib android.Path
+ var launcher android.Path
+ if ctx.ModuleName() == "py3-stdlib" || ctx.ModuleName() == "py2-stdlib" {
+ stdLib = p.srcsZip
+ } else {
+ ctx.VisitDirectDepsWithTag(hostStdLibTag, func(module android.Module) {
+ if dep, ok := module.(pythonDependency); ok {
+ stdLib = dep.getPrecompiledSrcsZip()
+ }
+ })
+ }
+ ctx.VisitDirectDepsWithTag(hostLauncherTag, func(module android.Module) {
+ if dep, ok := module.(IntermPathProvider); ok {
+ optionalLauncher := dep.IntermPathForModuleOut()
+ if optionalLauncher.Valid() {
+ launcher = optionalLauncher.Path()
+ }
+ }
+ })
+ var launcherSharedLibs android.Paths
+ var ldLibraryPath []string
+ ctx.VisitDirectDepsWithTag(hostlauncherSharedLibTag, func(module android.Module) {
+ if dep, ok := module.(IntermPathProvider); ok {
+ optionalPath := dep.IntermPathForModuleOut()
+ if optionalPath.Valid() {
+ launcherSharedLibs = append(launcherSharedLibs, optionalPath.Path())
+ ldLibraryPath = append(ldLibraryPath, filepath.Dir(optionalPath.Path().String()))
+ }
+ }
+ })
+
+ out := android.PathForModuleOut(ctx, ctx.ModuleName()+".srcszipprecompiled")
+ if stdLib == nil || launcher == nil {
+ // This shouldn't happen in a real build because we'll error out when adding dependencies
+ // on the stdlib and launcher if they don't exist. But some tests set
+ // AllowMissingDependencies.
+ return out
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: precompile,
+ Input: p.srcsZip,
+ Output: out,
+ Implicits: launcherSharedLibs,
+ Description: "Precompile the python sources of " + ctx.ModuleName(),
+ Args: map[string]string{
+ "stdlibZip": stdLib.String(),
+ "launcher": launcher.String(),
+ "ldLibraryPath": strings.Join(ldLibraryPath, ":"),
+ },
+ })
+ return out
+}
+
+// isPythonLibModule returns whether the given module is a Python library PythonLibraryModule or not
func isPythonLibModule(module blueprint.Module) bool {
- if m, ok := module.(*Module); ok {
- return m.isLibrary()
+ if _, ok := module.(*PythonLibraryModule); ok {
+ if _, ok := module.(*PythonBinaryModule); !ok {
+ return true
+ }
}
return false
}
-// This is distinguished by the fact that Python libraries are not installable, while other Python
-// modules are.
-func (p *Module) isLibrary() bool {
- // Python library has no bootstrapper or installer
- return p.bootstrapper == nil && p.installer == nil
-}
-
-func (p *Module) isBinary() bool {
- _, ok := p.bootstrapper.(*binaryDecorator)
- return ok
-}
-
// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
// for module and its transitive dependencies and collects list of data/source file
// zips for transitive dependencies.
-func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) {
+func (p *PythonLibraryModule) collectPathsFromTransitiveDeps(ctx android.ModuleContext, precompiled bool) android.Paths {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check duplicates.
destToPySrcs := make(map[string]string)
@@ -773,6 +664,8 @@
seen := make(map[android.Module]bool)
+ var result android.Paths
+
// visit all its dependencies in depth first.
ctx.WalkDeps(func(child, parent android.Module) bool {
// we only collect dependencies tagged as python library deps
@@ -801,10 +694,15 @@
checkForDuplicateOutputPath(ctx, destToPyData,
path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
}
- p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip())
+ if precompiled {
+ result = append(result, dep.getPrecompiledSrcsZip())
+ } else {
+ result = append(result, dep.getSrcsZip())
+ }
}
return true
})
+ return result
}
// chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
@@ -825,18 +723,10 @@
}
// InstallInData returns true as Python is not supported in the system partition
-func (p *Module) InstallInData() bool {
+func (p *PythonLibraryModule) InstallInData() bool {
return true
}
-func (p *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
- if p.isLibrary() {
- pythonLibBp2Build(ctx, p)
- } else if p.isBinary() {
- pythonBinaryBp2Build(ctx, p)
- }
-}
-
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/python/python_test.go b/python/python_test.go
index 42a1ffb..75a6a89 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -18,10 +18,10 @@
"fmt"
"os"
"path/filepath"
- "regexp"
"testing"
"android/soong/android"
+ "android/soong/cc"
)
type pyModule struct {
@@ -33,8 +33,10 @@
}
var (
- buildNamePrefix = "soong_python_test"
- moduleVariantErrTemplate = "%s: module %q variant %q: "
+ buildNamePrefix = "soong_python_test"
+ // We allow maching almost anything before the actual variant so that the os/arch variant
+ // is matched.
+ moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
pkgPathErrTemplate = moduleVariantErrTemplate +
"pkg_path: %q must be a relative path contained in par file."
badIdentifierErrTemplate = moduleVariantErrTemplate +
@@ -312,10 +314,6 @@
"e/file4.py",
},
srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
- depsSrcsZips: []string{
- "out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
- "out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
- },
},
},
},
@@ -327,17 +325,26 @@
if d.desc != "module with duplicate runfile path" {
continue
}
- errorPatterns := make([]string, len(d.errors))
- for i, s := range d.errors {
- errorPatterns[i] = regexp.QuoteMeta(s)
- }
+ d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
+python_library {
+ name: "py3-stdlib",
+ host_supported: true,
+}
+cc_binary {
+ name: "py3-launcher",
+ host_supported: true,
+}
+`)
t.Run(d.desc, func(t *testing.T) {
result := android.GroupFixturePreparers(
android.PrepareForTestWithDefaults,
+ android.PrepareForTestWithArchMutator,
+ android.PrepareForTestWithAllowMissingDependencies,
+ cc.PrepareForTestWithCcDefaultModules,
PrepareForTestWithPythonBuildComponents,
d.mockFiles.AddToFixture(),
- ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
RunTest(t)
if len(result.Errs) > 0 {
@@ -346,17 +353,17 @@
for _, e := range d.expectedBinaries {
t.Run(e.name, func(t *testing.T) {
- expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips)
+ expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
})
}
})
}
}
-func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) {
+func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
module := ctx.ModuleForTests(name, variant)
- base, baseOk := module.Module().(*Module)
+ base, baseOk := module.Module().(*PythonLibraryModule)
if !baseOk {
t.Fatalf("%s is not Python module!", name)
}
@@ -369,8 +376,6 @@
android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
-
- android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
}
func TestMain(m *testing.M) {
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
new file mode 100644
index 0000000..e12e7d2
--- /dev/null
+++ b/python/scripts/precompile_python.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import py_compile
+import os
+import shutil
+import tempfile
+import zipfile
+
+# This file needs to support both python 2 and 3.
+
+
+def process_one_file(name, inf, outzip):
+ if not name.endswith('.py'):
+ outzip.writestr(name, inf.read())
+ return
+
+ # Unfortunately py_compile requires the input/output files to be written
+ # out to disk.
+ with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+ shutil.copyfileobj(inf, tmp)
+ in_name = tmp.name
+ with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
+ out_name = tmp.name
+ try:
+ py_compile.compile(in_name, out_name, name, doraise=True)
+ with open(out_name, 'rb') as f:
+ outzip.writestr(name + 'c', f.read())
+ finally:
+ os.remove(in_name)
+ os.remove(out_name)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('src_zip')
+ parser.add_argument('dst_zip')
+ args = parser.parse_args()
+
+ with open(args.dst_zip, 'wb') as outf, open(args.src_zip, 'rb') as inf:
+ with zipfile.ZipFile(outf, mode='w') as outzip, zipfile.ZipFile(inf, mode='r') as inzip:
+ for name in inzip.namelist():
+ with inzip.open(name, mode='r') as inzipf:
+ process_one_file(name, inzipf, outzip)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/python/test.go b/python/test.go
index fc5c211..fb8e918 100644
--- a/python/test.go
+++ b/python/test.go
@@ -32,6 +32,20 @@
ctx.RegisterModuleType("python_test", PythonTestFactory)
}
+func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule {
+ return &PythonTestModule{PythonBinaryModule: *NewBinary(hod)}
+}
+
+func PythonTestHostFactory() android.Module {
+ return NewTest(android.HostSupportedNoCross).init()
+}
+
+func PythonTestFactory() android.Module {
+ module := NewTest(android.HostAndDeviceSupported)
+ module.multilib = android.MultilibBoth
+ return module.init()
+}
+
type TestProperties struct {
// the name of the test configuration (for example "AndroidTest.xml") that should be
// installed with the module.
@@ -52,76 +66,79 @@
Test_options android.CommonTestOptions
}
-type testDecorator struct {
- *binaryDecorator
+type PythonTestModule struct {
+ PythonBinaryModule
testProperties TestProperties
-
- testConfig android.Path
-
- data []android.DataPath
+ testConfig android.Path
+ data []android.DataPath
}
-func (test *testDecorator) bootstrapperProps() []interface{} {
- return append(test.binaryDecorator.bootstrapperProps(), &test.testProperties)
+func (p *PythonTestModule) init() android.Module {
+ p.AddProperties(&p.properties, &p.protoProperties)
+ p.AddProperties(&p.binaryProperties)
+ p.AddProperties(&p.testProperties)
+ android.InitAndroidArchModule(p, p.hod, p.multilib)
+ android.InitDefaultableModule(p)
+ android.InitBazelModule(p)
+ if p.hod == android.HostSupportedNoCross && p.testProperties.Test_options.Unit_test == nil {
+ p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+ }
+ return p
}
-func (test *testDecorator) install(ctx android.ModuleContext, file android.Path) {
- test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
- TestConfigProp: test.testProperties.Test_config,
- TestConfigTemplateProp: test.testProperties.Test_config_template,
- TestSuites: test.binaryDecorator.binaryProperties.Test_suites,
- AutoGenConfig: test.binaryDecorator.binaryProperties.Auto_gen_config,
+func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // We inherit from only the library's GenerateAndroidBuildActions, and then
+ // just use buildBinary() so that the binary is not installed into the location
+ // it would be for regular binaries.
+ p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+ p.buildBinary(ctx)
+
+ p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: p.testProperties.Test_config,
+ TestConfigTemplateProp: p.testProperties.Test_config_template,
+ TestSuites: p.binaryProperties.Test_suites,
+ AutoGenConfig: p.binaryProperties.Auto_gen_config,
DeviceTemplate: "${PythonBinaryHostTestConfigTemplate}",
HostTemplate: "${PythonBinaryHostTestConfigTemplate}",
})
- test.binaryDecorator.pythonInstaller.dir = "nativetest"
- test.binaryDecorator.pythonInstaller.dir64 = "nativetest64"
+ p.installedDest = ctx.InstallFile(installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()), p.installSource.Base(), p.installSource)
- test.binaryDecorator.pythonInstaller.relative = ctx.ModuleName()
-
- test.binaryDecorator.pythonInstaller.install(ctx, file)
-
- dataSrcPaths := android.PathsForModuleSrc(ctx, test.testProperties.Data)
-
- for _, dataSrcPath := range dataSrcPaths {
- test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
+ for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
+ p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
}
// Emulate the data property for java_data dependencies.
for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
- test.data = append(test.data, android.DataPath{SrcPath: javaDataSrcPath})
+ p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath})
}
}
}
-func NewTest(hod android.HostOrDeviceSupported) *Module {
- module, binary := NewBinary(hod)
-
- binary.pythonInstaller = NewPythonInstaller("nativetest", "nativetest64")
-
- test := &testDecorator{binaryDecorator: binary}
- if hod == android.HostSupportedNoCross && test.testProperties.Test_options.Unit_test == nil {
- test.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
+ entriesList := p.PythonBinaryModule.AndroidMkEntries()
+ if len(entriesList) != 1 {
+ panic("Expected 1 entry")
}
+ entries := &entriesList[0]
- module.bootstrapper = test
- module.installer = test
+ entries.Class = "NATIVE_TESTS"
- return module
-}
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ //entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
+ if p.testConfig != nil {
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
+ }
-func PythonTestHostFactory() android.Module {
- module := NewTest(android.HostSupportedNoCross)
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
- return module.init()
-}
+ entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-func PythonTestFactory() android.Module {
- module := NewTest(android.HostAndDeviceSupported)
- module.multilib = android.MultilibBoth
+ p.testProperties.Test_options.SetAndroidMkEntries(entries)
+ })
- return module.init()
+ return entriesList
}
diff --git a/python/tests/par_test.py b/python/tests/par_test.py
index 56a5063..1e03f16 100644
--- a/python/tests/par_test.py
+++ b/python/tests/par_test.py
@@ -27,7 +27,10 @@
failed = True
assert_equal("__name__", __name__, "__main__")
-assert_equal("os.path.basename(__file__)", os.path.basename(__file__), "par_test.py")
+fileName = os.path.basename(__file__)
+if fileName.endswith('.pyc'):
+ fileName = fileName[:-1]
+assert_equal("os.path.basename(__file__)", fileName, "par_test.py")
archive = os.path.dirname(__file__)
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index ffad430..b513409 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -28,7 +28,10 @@
archive = sys.modules["__main__"].__loader__.archive
assert_equal("__name__", __name__, "testpkg.par_test")
-assert_equal("__file__", __file__, os.path.join(archive, "testpkg/par_test.py"))
+fileName = __file__
+if fileName.endswith('.pyc'):
+ fileName = fileName[:-1]
+assert_equal("__file__", fileName, os.path.join(archive, "testpkg/par_test.py"))
# Python3 is returning None here for me, and I haven't found any problems caused by this.
if sys.version_info[0] == 2:
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 32c746e..20e9919 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -205,8 +205,8 @@
})
}
-func (fuzz *fuzzDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
- ctx.SubAndroidMk(entries, fuzz.binaryDecorator)
+func (fuzz *fuzzDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+ ctx.SubAndroidMk(ret, fuzz.binaryDecorator)
var fuzzFiles []string
for _, d := range fuzz.fuzzPackagedModule.Corpus {
@@ -229,11 +229,14 @@
filepath.Dir(fuzz.fuzzPackagedModule.Config.String())+":config.json")
}
- entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
+ ret.ExtraEntries = append(ret.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
if len(fuzzFiles) > 0 {
entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
}
+ if fuzz.installedSharedDeps != nil {
+ entries.AddStrings("LOCAL_FUZZ_INSTALLED_SHARED_DEPS", fuzz.installedSharedDeps...)
+ }
})
}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 17d80dd..878f896 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -15,7 +15,6 @@
package rust
import (
- "fmt"
"strings"
"github.com/google/blueprint"
@@ -30,7 +29,7 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r468909b"
+ bindgenClangVersion = "clang-r475365b"
_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -53,7 +52,7 @@
if ctx.Config().UseHostMusl() {
return "musl/lib/"
} else {
- return "lib64/"
+ return "lib/"
}
})
_ = pctx.SourcePathVariable("bindgenClang",
@@ -178,10 +177,6 @@
if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" {
cflags = append(cflags, "-D__ANDROID_APEX__")
- if ctx.Device() {
- cflags = append(cflags, fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
- ctx.RustModule().apexSdkVersion.FinalOrFutureInt()))
- }
}
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
diff --git a/rust/compiler.go b/rust/compiler.go
index 6055158..31acd49 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -15,6 +15,7 @@
package rust
import (
+ "android/soong/cc"
"fmt"
"path/filepath"
"strings"
@@ -207,6 +208,10 @@
panic("baseCompiler does not implement SetDisabled()")
}
+func (compiler *baseCompiler) noStdlibs() bool {
+ return Bool(compiler.Properties.No_stdlibs)
+}
+
func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath {
panic("baseCompiler does not implement coverageOutputZipPath()")
}
@@ -307,19 +312,7 @@
flags.EmitXrefs = ctx.Config().EmitXrefRules()
if ctx.Host() && !ctx.Windows() {
- rpathPrefix := `\$$ORIGIN/`
- if ctx.Darwin() {
- rpathPrefix = "@loader_path/"
- }
-
- var rpath string
- if ctx.toolchain().Is64Bit() {
- rpath = "lib64"
- } else {
- rpath = "lib"
- }
- flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
- flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath)
+ flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
}
return flags
diff --git a/rust/config/global.go b/rust/config/global.go
index 81aec7e..ef428b8 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.64.0"
+ RustDefaultVersion = "1.66.1"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2021"
Stdlibs = []string{
@@ -51,6 +51,9 @@
// Use v0 mangling to distinguish from C++ symbols
"-C symbol-mangling-version=v0",
"--color always",
+ // TODO (b/267698452): Temporary workaround until the "no unstable
+ // features" policy is enforced.
+ "-A stable-features",
}
deviceGlobalRustFlags = []string{
diff --git a/rust/config/x86_linux_bionic_host.go b/rust/config/x86_linux_bionic_host.go
index b1a2c17..79c40ce 100644
--- a/rust/config/x86_linux_bionic_host.go
+++ b/rust/config/x86_linux_bionic_host.go
@@ -21,7 +21,9 @@
)
var (
- LinuxBionicRustFlags = []string{}
+ LinuxBionicRustFlags = []string{
+ "-C panic=abort",
+ }
LinuxBionicRustLinkFlags = []string{
"-B${cc_config.ClangBin}",
"-fuse-ld=lld",
diff --git a/rust/coverage.go b/rust/coverage.go
index 5ea481f..bc6504d 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -21,6 +21,7 @@
)
var CovLibraryName = "libprofile-clang-extras"
+var ProfilerBuiltins = "libprofiler_builtins.rust_sysroot"
// Add '%c' to default specifier after we resolve http://b/210012154
const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
@@ -41,6 +42,11 @@
ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, cc.CoverageDepTag, CovLibraryName)
+
+ // no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
+ if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
+ ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins)
+ }
}
return deps
@@ -60,6 +66,13 @@
flags.LinkFlags = append(flags.LinkFlags,
profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+
+ // no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
+ if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
+ profiler_builtins := ctx.GetDirectDepWithTag(ProfilerBuiltins, rlibDepTag).(*Module)
+ deps.RLibs = append(deps.RLibs, RustLibrary{Path: profiler_builtins.OutputFile().Path(), CrateName: profiler_builtins.CrateName()})
+ }
+
if cc.EnableContinuousCoverage(ctx) {
flags.RustFlags = append(flags.RustFlags, "-C llvm-args=--runtime-counter-relocation")
flags.LinkFlags = append(flags.LinkFlags, "-Wl,-mllvm,-runtime-counter-relocation")
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 6faf55c..d7e7ddf 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -16,8 +16,6 @@
import (
"path/filepath"
- "sort"
- "strings"
"android/soong/android"
"android/soong/cc"
@@ -27,14 +25,14 @@
func init() {
android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
- android.RegisterSingletonType("rust_fuzz_packaging", rustFuzzPackagingFactory)
}
type fuzzDecorator struct {
*binaryDecorator
- fuzzPackagedModule fuzz.FuzzPackagedModule
- sharedLibraries android.Paths
+ fuzzPackagedModule fuzz.FuzzPackagedModule
+ sharedLibraries android.Paths
+ installedSharedDeps []string
}
var _ compiler = (*fuzzDecorator)(nil)
@@ -64,9 +62,14 @@
flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags)
// `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages.
- flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+ if ctx.InstallInVendor() {
+ flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`)
+ } else {
+ flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+
+ }
return flags
}
@@ -88,10 +91,8 @@
}
func (fuzzer *fuzzDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
- out := fuzzer.binaryDecorator.compile(ctx, flags, deps)
- // Grab the list of required shared libraries.
- fuzzer.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
+ out := fuzzer.binaryDecorator.compile(ctx, flags, deps)
return out
}
@@ -104,83 +105,6 @@
return rlibAutoDep
}
-// Responsible for generating GNU Make rules that package fuzz targets into
-// their architecture & target/host specific zip file.
-type rustFuzzPackager struct {
- fuzz.FuzzPackager
-}
-
-func rustFuzzPackagingFactory() android.Singleton {
- return &rustFuzzPackager{}
-}
-
-func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
- // Map between each architecture + host/device combination.
- archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
-
- // List of individual fuzz targets.
- s.FuzzTargets = make(map[string]bool)
-
- // Map tracking whether each shared library has an install rule to avoid duplicate install rules from
- // multiple fuzzers that depend on the same shared library.
- sharedLibraryInstalled := make(map[string]bool)
-
- ctx.VisitAllModules(func(module android.Module) {
- // Discard non-fuzz targets.
- rustModule, ok := module.(*Module)
- if !ok {
- return
- }
-
- if ok := fuzz.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
- return
- }
-
- fuzzModule, ok := rustModule.compiler.(*fuzzDecorator)
- if !ok {
- return
- }
-
- hostOrTargetString := "target"
- if rustModule.Host() {
- hostOrTargetString = "host"
- }
-
- archString := rustModule.Arch().ArchType.String()
- archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
- archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
-
- var files []fuzz.FileToZip
- builder := android.NewRuleBuilder(pctx, ctx)
-
- // Package the artifacts (data, corpus, config and dictionary into a zipfile.
- files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
-
- // The executable.
- files = append(files, fuzz.FileToZip{rustModule.UnstrippedOutputFile(), ""})
-
- // Package shared libraries
- files = append(files, cc.GetSharedLibsToZip(fuzzModule.sharedLibraries, rustModule, &s.FuzzPackager, archString, "lib", &sharedLibraryInstalled)...)
-
- archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
- if !ok {
- return
- }
-
- })
- s.CreateFuzzPackage(ctx, archDirs, fuzz.Rust, pctx)
-}
-
-func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
- packages := s.Packages.Strings()
- sort.Strings(packages)
-
- ctx.Strict("SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
- // Preallocate the slice of fuzz targets to minimize memory allocations.
- s.PreallocateSlice(ctx, "ALL_RUST_FUZZ_TARGETS")
-}
-
func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
fuzz.binaryDecorator.baseCompiler.dir = filepath.Join(
"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
@@ -188,13 +112,22 @@
"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
fuzz.binaryDecorator.baseCompiler.install(ctx)
- if fuzz.fuzzPackagedModule.FuzzProperties.Corpus != nil {
- fuzz.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Corpus)
- }
- if fuzz.fuzzPackagedModule.FuzzProperties.Data != nil {
- fuzz.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Data)
- }
- if fuzz.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
- fuzz.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzz.fuzzPackagedModule.FuzzProperties.Dictionary)
+ fuzz.fuzzPackagedModule = cc.PackageFuzzModule(ctx, fuzz.fuzzPackagedModule, pctx)
+
+ installBase := "fuzz"
+
+ // Grab the list of required shared libraries.
+ fuzz.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
+
+ for _, lib := range fuzz.sharedLibraries {
+ fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+ cc.SharedLibraryInstallLocation(
+ lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+
+ // Also add the dependency on the shared library symbols dir.
+ if !ctx.Host() {
+ fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+ cc.SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+ }
}
}
diff --git a/rust/image.go b/rust/image.go
index dfc7f74..50bf02a 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -129,6 +129,10 @@
return ctx.ModuleContext.DeviceSpecific() || (ctx.RustModule().InVendor() && ctx.RustModule().VendorVariantToOdm())
}
+func (ctx *moduleContext) SystemExtSpecific() bool {
+ return ctx.ModuleContext.SystemExtSpecific()
+}
+
// Returns true when this module creates a vendor variant and wants to install the vendor variant
// to the odm partition.
func (c *Module) VendorVariantToOdm() bool {
@@ -158,22 +162,15 @@
}
func (mod *Module) OnlyInRamdisk() bool {
- // TODO(b/165791368)
- return false
+ return mod.ModuleBase.InstallInRamdisk()
}
func (mod *Module) OnlyInRecovery() bool {
- // TODO(b/165791368)
- return false
+ return mod.ModuleBase.InstallInRecovery()
}
func (mod *Module) OnlyInVendorRamdisk() bool {
- return false
-}
-
-func (mod *Module) OnlyInProduct() bool {
- //TODO(b/165791368)
- return false
+ return mod.ModuleBase.InstallInVendorRamdisk()
}
// Returns true when this module is configured to have core and vendor variants.
@@ -226,10 +223,7 @@
// Rust does not support installing to the product image yet.
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
- if mctx.ProductSpecific() {
- mctx.PropertyErrorf("product_specific",
- "Rust modules do not yet support installing to the product image.")
- } else if Bool(mod.VendorProperties.Double_loadable) {
+ if Bool(mod.VendorProperties.Double_loadable) {
mctx.PropertyErrorf("double_loadable",
"Rust modules do not yet support double loading")
}
@@ -243,6 +237,11 @@
mctx.PropertyErrorf("vendor", "Vendor-only dylibs are not yet supported, use rust_library_rlib.")
}
}
+ if mctx.ProductSpecific() {
+ if lib, ok := mod.compiler.(libraryInterface); ok && lib.buildDylib() {
+ mctx.PropertyErrorf("product", "Product-only dylibs are not yet supported, use rust_library_rlib.")
+ }
+ }
cc.MutateImage(mctx, mod)
diff --git a/rust/image_test.go b/rust/image_test.go
index 95e788f..8185872 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -103,3 +103,93 @@
}
`)
}
+
+func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
+ mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+ partitionDefined := false
+ checkPartition := func(specific bool, partition string) {
+ if specific {
+ if expected != partition && !partitionDefined {
+ // The variant is installed to the 'partition'
+ t.Errorf("%s variant of %q must not be installed to %s partition", variant, name, partition)
+ }
+ partitionDefined = true
+ } else {
+ // The variant is not installed to the 'partition'
+ if expected == partition {
+ t.Errorf("%s variant of %q must be installed to %s partition", variant, name, partition)
+ }
+ }
+ }
+ socSpecific := func(m *Module) bool {
+ return m.SocSpecific()
+ }
+ deviceSpecific := func(m *Module) bool {
+ return m.DeviceSpecific()
+ }
+ productSpecific := func(m *Module) bool {
+ return m.ProductSpecific() || m.productSpecificModuleContext()
+ }
+ systemExtSpecific := func(m *Module) bool {
+ return m.SystemExtSpecific()
+ }
+ checkPartition(socSpecific(mod), "vendor")
+ checkPartition(deviceSpecific(mod), "odm")
+ checkPartition(productSpecific(mod), "product")
+ checkPartition(systemExtSpecific(mod), "system_ext")
+ if !partitionDefined && expected != "system" {
+ t.Errorf("%s variant of %q is expected to be installed to %s partition,"+
+ " but installed to system partition", variant, name, expected)
+ }
+}
+
+func TestInstallPartition(t *testing.T) {
+ t.Parallel()
+ t.Helper()
+ ctx := testRust(t, `
+ rust_binary {
+ name: "sample_system",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ }
+ rust_binary {
+ name: "sample_system_ext",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ system_ext_specific: true,
+ }
+ rust_binary {
+ name: "sample_product",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ product_specific: true,
+ }
+ rust_binary {
+ name: "sample_vendor",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ vendor: true,
+ }
+ rust_binary {
+ name: "sample_odm",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ device_specific: true,
+ }
+ rust_binary {
+ name: "sample_all_available",
+ crate_name: "sample",
+ srcs: ["foo.rs"],
+ vendor_available: true,
+ product_available: true,
+ }
+ `)
+
+ checkInstallPartition(t, ctx, "sample_system", binaryCoreVariant, "system")
+ checkInstallPartition(t, ctx, "sample_system_ext", binaryCoreVariant, "system_ext")
+ checkInstallPartition(t, ctx, "sample_product", binaryProductVariant, "product")
+ checkInstallPartition(t, ctx, "sample_vendor", binaryVendorVariant, "vendor")
+ checkInstallPartition(t, ctx, "sample_odm", binaryVendorVariant, "odm")
+
+ checkInstallPartition(t, ctx, "sample_all_available", binaryCoreVariant, "system")
+}
diff --git a/rust/rust.go b/rust/rust.go
index 28a300b..e4cf671 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -208,6 +208,11 @@
}
return android.Paths{}, nil
}
+ case "unstripped":
+ if mod.compiler != nil {
+ return android.PathsIfNonNil(mod.compiler.unstrippedOutputFilePath()), nil
+ }
+ return nil, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
@@ -485,6 +490,7 @@
SetDisabled()
stdLinkage(ctx *depsContext) RustLinkage
+ noStdlibs() bool
unstrippedOutputFilePath() android.Path
strippedOutputFilePath() android.OptionalPath
@@ -619,6 +625,31 @@
return false
}
+func (mod *Module) IsFuzzModule() bool {
+ if _, ok := mod.compiler.(*fuzzDecorator); ok {
+ return true
+ }
+ return false
+}
+
+func (mod *Module) FuzzModuleStruct() fuzz.FuzzModule {
+ return mod.FuzzModule
+}
+
+func (mod *Module) FuzzPackagedModule() fuzz.FuzzPackagedModule {
+ if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
+ return fuzzer.fuzzPackagedModule
+ }
+ panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) FuzzSharedLibraries() android.Paths {
+ if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
+ return fuzzer.sharedLibraries
+ }
+ panic(fmt.Errorf("FuzzSharedLibraries called on non-fuzz module: %q", mod.BaseModuleName()))
+}
+
func (mod *Module) UnstrippedOutputFile() android.Path {
if mod.compiler != nil {
return mod.compiler.unstrippedOutputFilePath()
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 97bd541..3bcd58c 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -83,6 +83,10 @@
rlibVendorVariant = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
sharedRecoveryVariant = "android_recovery_arm64_armv8-a_shared"
rlibRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std"
+ binaryCoreVariant = "android_arm64_armv8-a"
+ binaryVendorVariant = "android_vendor.29_arm64_armv8-a"
+ binaryProductVariant = "android_product.29_arm64_armv8-a"
+ binaryRecoveryVariant = "android_recovery_arm64_armv8-a"
)
func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 4f8d22b..3236bce 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -31,9 +31,8 @@
Properties SourceProviderProperties
// The first file in OutputFiles must be the library entry point.
- OutputFiles android.Paths
- subAndroidMkOnce map[SubAndroidMkProvider]bool
- subName string
+ OutputFiles android.Paths
+ subName string
}
var _ SourceProvider = (*BaseSourceProvider)(nil)
diff --git a/rust/test.go b/rust/test.go
index 4f922b4..4b5296e 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -200,6 +200,7 @@
if ctx.Device() {
flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
}
+
return flags
}
diff --git a/rust/testing.go b/rust/testing.go
index 4796f69..24ca3d6 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -104,6 +104,7 @@
crate_name: "std",
srcs: ["foo.rs"],
no_stdlibs: true,
+ product_available: true,
host_supported: true,
vendor_available: true,
vendor_ramdisk_available: true,
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index a02c195..08bd80c 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -75,6 +75,7 @@
jdk\.internal\.ref
jdk\.internal\.reflect
jdk\.internal\.util
+jdk\.internal\.util\.jar
jdk\.internal\.vm\.annotation
jdk\.net
org\.w3c\.dom
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index e46efe4..3d7c0fa 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -27,13 +27,15 @@
def Proto(args):
- json_content = ''
- with open(args.source) as f:
- for line in f:
- if not line.lstrip().startswith('//'):
- json_content += line
- obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
- pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
+ pb = linker_config_pb2.LinkerConfig()
+ for input in args.source.split(':'):
+ json_content = ''
+ with open(input) as f:
+ for line in f:
+ if not line.lstrip().startswith('//'):
+ json_content += line
+ obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
+ ParseDict(obj, pb)
with open(args.output, 'wb') as f:
f.write(pb.SerializeToString())
@@ -104,7 +106,7 @@
'--source',
required=True,
type=str,
- help='Source linker configuration file in JSON.')
+ help='Colon-separated list of linker configuration files in JSON.')
parser_proto.add_argument(
'-o',
'--output',
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
index 3650fa1..2ff0c5f 100644
--- a/scripts/hiddenapi/signature_trie.py
+++ b/scripts/hiddenapi/signature_trie.py
@@ -150,10 +150,6 @@
f"wildcard '{last_element}' and "
f"member signature '{member[0]}'")
wildcard = [last_element]
- elif last_element.islower():
- raise Exception(f"Invalid signature '{signature}': last element "
- f"'{last_element}' is lower case but should be an "
- f"upper case class name or wildcard")
else:
packages = elements[0:-1]
# Split the class name into outer / inner classes
diff --git a/scripts/hiddenapi/signature_trie_test.py b/scripts/hiddenapi/signature_trie_test.py
index 6d4e660..bd4a9a8 100755
--- a/scripts/hiddenapi/signature_trie_test.py
+++ b/scripts/hiddenapi/signature_trie_test.py
@@ -117,14 +117,6 @@
self.assertEqual(elements, self.signature_to_elements(signature))
self.assertEqual(signature, self.elements_to_signature(elements))
- def test_invalid_no_class_or_wildcard(self):
- signature = "java/lang"
- with self.assertRaises(Exception) as context:
- self.signature_to_elements(signature)
- self.assertIn(
- "last element 'lang' is lower case but should be an "
- "upper case class name or wildcard", str(context.exception))
-
def test_non_standard_class_name(self):
elements = [
("package", "javax"),
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
index 3dbc22e..07e01a1 100644
--- a/scripts/test_config_fixer.py
+++ b/scripts/test_config_fixer.py
@@ -31,6 +31,8 @@
KNOWN_PREPARERS = ['com.android.tradefed.targetprep.TestAppInstallSetup',
'com.android.tradefed.targetprep.suite.SuiteApkInstaller']
+MAINLINE_CONTROLLER = 'com.android.tradefed.testtype.suite.module.MainlineTestModuleController'
+
def parse_args():
"""Parse commandline arguments."""
@@ -41,6 +43,8 @@
help=('overwrite package fields in the test config'))
parser.add_argument('--test-file-name', default='', dest='test_file_name',
help=('overwrite test file name in the test config'))
+ parser.add_argument('--mainline-package-name', default='', dest='mainline_package_name',
+ help=('overwrite mainline module package name in the test config'))
parser.add_argument('input', help='input test config file')
parser.add_argument('output', help='output test config file')
return parser.parse_args()
@@ -72,6 +76,16 @@
if option.getAttribute('name') == "test-file-name":
option.setAttribute('value', test_file_name)
+def overwrite_mainline_module_package_name(test_config_doc, mainline_package_name):
+
+ test_config = parse_test_config(test_config_doc)
+
+ for obj in get_children_with_tag(test_config, 'object'):
+ if obj.getAttribute('class') == MAINLINE_CONTROLLER:
+ for option in get_children_with_tag(obj, 'option'):
+ if option.getAttribute('name') == "mainline-module-package-name":
+ option.setAttribute('value', mainline_package_name)
+
def main():
"""Program entry point."""
try:
@@ -88,6 +102,9 @@
if args.test_file_name:
overwrite_test_file_name(doc, args.test_file_name)
+ if args.mainline_package_name:
+ overwrite_mainline_module_package_name(doc, args.mainline_package_name)
+
with open(args.output, 'w') as f:
write_xml(f, doc)
diff --git a/scripts/test_config_fixer_test.py b/scripts/test_config_fixer_test.py
index 39ce5b3..699f91e 100644
--- a/scripts/test_config_fixer_test.py
+++ b/scripts/test_config_fixer_test.py
@@ -23,6 +23,8 @@
import test_config_fixer
+from manifest import write_xml
+
sys.dont_write_bytecode = True
@@ -117,5 +119,39 @@
self.assertEqual(expected, output.getvalue())
+class OverwriteMainlineModulePackageNameTest(unittest.TestCase):
+ """ Unit tests for overwrite_mainline_module_package_name function """
+
+ test_config = (
+ '<?xml version="1.0" encoding="utf-8"?>\n'
+ '<configuration description="Runs some tests.">\n'
+ ' <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">\n'
+ ' <option name="test-file-name" value="foo.apk"/>\n'
+ ' </target_preparer>\n'
+ ' <test class="com.android.tradefed.testtype.AndroidJUnitTest">\n'
+ ' <option name="package" value="com.android.foo"/>\n'
+ ' <option name="runtime-hint" value="20s"/>\n'
+ ' </test>\n'
+ ' <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">\n'
+ ' <option name="enable" value="true"/>\n'
+ ' <option name="mainline-module-package-name" value="%s"/>\n'
+ ' </object>\n'
+ '</configuration>\n')
+
+ def test_testappinstallsetup(self):
+ doc = minidom.parseString(self.test_config % ("com.android.old.package.name"))
+
+ test_config_fixer.overwrite_mainline_module_package_name(doc, "com.android.new.package.name")
+ output = io.StringIO()
+ test_config_fixer.write_xml(output, doc)
+
+ # Only the mainline module package name should be updated. Format the xml
+ # with minidom first to avoid mismatches due to trivial reformatting.
+ expected = io.StringIO()
+ write_xml(expected, minidom.parseString(self.test_config % ("com.android.new.package.name")))
+ self.maxDiff = None
+ self.assertEqual(expected.getvalue(), output.getvalue())
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/tests/apex_cc_module_arch_variant_tests.sh b/tests/apex_cc_module_arch_variant_tests.sh
index 97e6576..b0cade1 100755
--- a/tests/apex_cc_module_arch_variant_tests.sh
+++ b/tests/apex_cc_module_arch_variant_tests.sh
@@ -56,7 +56,7 @@
# Number of CppCompile actions with arch variant flag
actions_with_arch_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
- 'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c "\-march=$ARCH_VARIANT_CFLAG")
+ 'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex))' | grep -c \'-march=$ARCH_VARIANT_CFLAG\')
# Number of all CppCompile actions
all_cppcompile_actions_num=0
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
index 412f84a..5007078 100755
--- a/tests/apex_comparison_tests.sh
+++ b/tests/apex_comparison_tests.sh
@@ -74,7 +74,7 @@
# # Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg.
call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs //system/apex/tools:deapexer
DEBUGFS_PATH="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //external/e2fsprogs/debugfs))"
-DEAPEXER="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //system/apex/tools:deapexer))"
+DEAPEXER="bazel-bin/system/apex/tools/deapexer"
DEAPEXER="$DEAPEXER --debugfs_path=$DEBUGFS_PATH"
#######
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 17b4419..fda5ca0 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -75,7 +75,6 @@
grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
}
-
function test_add_android_bp() {
setup
run_soong
@@ -313,7 +312,6 @@
}
-
function test_add_file_to_soong_build() {
setup
run_soong
@@ -736,7 +734,6 @@
}
-
function test_bp2build_bazel_workspace_structure {
setup
@@ -802,11 +799,10 @@
|| fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
}
-function test_bp2build_reports_multiple_errors {
+function test_bp2build_fails_fast {
setup
mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
- touch a/a.txt
cat > a/Android.bp <<EOF
filegroup {
name: "a",
@@ -816,7 +812,6 @@
EOF
mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
- touch b/b.txt
cat > b/Android.bp <<EOF
filegroup {
name: "b",
@@ -829,8 +824,8 @@
fail "Build should have failed"
fi
- grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
- grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
+ # we should expect at least one error
+ grep -q -E "(a|b)/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for ${GENERATED_BUILD_FILE_NAME} not found"
}
function test_bp2build_back_and_forth_null_build {
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 8c2ce48..878b4a1 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -140,7 +140,7 @@
# NOTE: We don't actually use the extra BUILD file for anything here
run_bazel build --config=android --config=bp2build --config=ci //foo/...
- local the_answer_file="$(find -L bazel-out -name the_answer.txt)"
+ local -r the_answer_file="$(find -L bazel-out -name the_answer.txt)"
if [[ ! -f "${the_answer_file}" ]]; then
fail "Expected the_answer.txt to be generated, but was missing"
fi
@@ -156,6 +156,49 @@
eval "${_save_trap}"
}
+function test_bp2build_symlinks_files {
+ setup
+ mkdir -p foo
+ touch foo/BLANK1
+ touch foo/BLANK2
+ touch foo/F2D
+ touch foo/BUILD
+
+ run_soong bp2build
+
+ if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
+ fail "./out/soong/workspace/foo/BUILD should be omitted"
+ fi
+ for file in BLANK1 BLANK2 F2D
+ do
+ if [[ ! -L "./out/soong/workspace/foo/$file" ]]; then
+ fail "./out/soong/workspace/foo/$file should exist"
+ fi
+ done
+ local -r BLANK1_BEFORE=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
+
+ rm foo/BLANK2
+ rm foo/F2D
+ mkdir foo/F2D
+ touch foo/F2D/BUILD
+
+ run_soong bp2build
+
+ if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
+ fail "./out/soong/workspace/foo/BUILD should be omitted"
+ fi
+ local -r BLANK1_AFTER=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
+ if [[ "$BLANK1_AFTER" != "$BLANK1_BEFORE" ]]; then
+ fail "./out/soong/workspace/foo/BLANK1 should be untouched"
+ fi
+ if [[ -e "./out/soong/workspace/foo/BLANK2" ]]; then
+ fail "./out/soong/workspace/foo/BLANK2 should be removed"
+ fi
+ if [[ -L "./out/soong/workspace/foo/F2D" ]] || [[ ! -d "./out/soong/workspace/foo/F2D" ]]; then
+ fail "./out/soong/workspace/foo/F2D should be a dir"
+ fi
+}
+
function test_cc_correctness {
setup
@@ -231,4 +274,11 @@
fi
}
+# Smoke test to verify api_bp2build worksapce does not contain any errors
+function test_api_bp2build_empty_build() {
+ setup
+ run_soong api_bp2build
+ run_bazel build --config=android --config=api_bp2build //:empty
+}
+
scan_and_run_tests
diff --git a/tests/lib.sh b/tests/lib.sh
index 26bdc97..0973beb 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -94,6 +94,8 @@
symlink_directory external/go-cmp
symlink_directory external/golang-protobuf
symlink_directory external/starlark-go
+ symlink_directory external/python
+ symlink_directory external/sqlite
touch "$MOCK_TOP/Android.bp"
}
diff --git a/ui/build/config.go b/ui/build/config.go
index b928faa..cb7fe1e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -147,8 +147,10 @@
}
}
-// fetchEnvConfig optionally fetches environment config from an
-// experiments system to control Soong features dynamically.
+// fetchEnvConfig optionally fetches a configuration file that can then subsequently be
+// loaded into Soong environment to control certain aspects of build behavior (e.g., enabling RBE).
+// If a configuration file already exists on disk, the fetch is run in the background
+// so as to NOT block the rest of the build execution.
func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
configName := envConfigName + "." + jsonSuffix
expConfigFetcher := &smpb.ExpConfigFetcher{Filename: &configName}
@@ -174,8 +176,13 @@
return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
}
+ configExists := false
+ outConfigFilePath := filepath.Join(config.OutDir(), configName)
+ if _, err := os.Stat(outConfigFilePath); err == nil {
+ configExists = true
+ }
+
tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
- defer cancel()
fetchStart := time.Now()
cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(),
"-output_config_name", configName)
@@ -185,22 +192,39 @@
return err
}
- if err := cmd.Wait(); err != nil {
- status := smpb.ExpConfigFetcher_ERROR
- expConfigFetcher.Status = &status
- return err
- }
- fetchEnd := time.Now()
- expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
- outConfigFilePath := filepath.Join(config.OutDir(), configName)
- expConfigFetcher.Filename = proto.String(outConfigFilePath)
- if _, err := os.Stat(outConfigFilePath); err == nil {
+ fetchCfg := func() error {
+ if err := cmd.Wait(); err != nil {
+ status := smpb.ExpConfigFetcher_ERROR
+ expConfigFetcher.Status = &status
+ return err
+ }
+ fetchEnd := time.Now()
+ expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
+ expConfigFetcher.Filename = proto.String(outConfigFilePath)
+
+ if _, err := os.Stat(outConfigFilePath); err != nil {
+ status := smpb.ExpConfigFetcher_NO_CONFIG
+ expConfigFetcher.Status = &status
+ return err
+ }
status := smpb.ExpConfigFetcher_CONFIG
expConfigFetcher.Status = &status
- } else {
- status := smpb.ExpConfigFetcher_NO_CONFIG
- expConfigFetcher.Status = &status
+ return nil
}
+
+ // If a config file does not exist, wait for the config file to be fetched. Otherwise
+ // fetch the config file in the background and return immediately.
+ if !configExists {
+ defer cancel()
+ return fetchCfg()
+ }
+
+ go func() {
+ defer cancel()
+ if err := fetchCfg(); err != nil {
+ ctx.Verbosef("Failed to fetch config file %v: %v\n", configName, err)
+ }
+ }()
return nil
}
@@ -300,8 +324,8 @@
if bc != "" {
if err := fetchEnvConfig(ctx, ret, bc); err != nil {
ctx.Verbosef("Failed to fetch config file: %v\n", err)
-
- } else if err := loadEnvConfig(ctx, ret, bc); err != nil {
+ }
+ if err := loadEnvConfig(ctx, ret, bc); err != nil {
ctx.Fatalln("Failed to parse env config files: %v", err)
}
}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 9f14bdd..9959e6f 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,16 +18,21 @@
// another.
import (
+ "bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
+ "strconv"
+ "strings"
"time"
+ "android/soong/shared"
"android/soong/ui/metrics"
"google.golang.org/protobuf/proto"
+ bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
upload_proto "android/soong/ui/metrics/upload_proto"
)
@@ -73,12 +78,113 @@
return metricsFiles
}
+func parseTimingToNanos(str string) int64 {
+ millisString := removeDecimalPoint(str)
+ timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+ return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+ percentageString := removeDecimalPoint(str)
+ //remove the % at the end of the string
+ percentage := strings.ReplaceAll(percentageString, "%", "")
+ percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+ return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+ // The format is always 0.425 or 10.425
+ return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+ words := strings.Fields(line)
+ timing := words[3]
+ return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+ words := strings.Fields(line)
+ getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+ // Sample lines include:
+ // Total launch phase time 0.011 s 2.59%
+ // Total target pattern evaluation phase time 0.011 s 2.59%
+ var beginning int
+ var end int
+ for ind, word := range words {
+ if word == "Total" {
+ beginning = ind + 1
+ } else if beginning > 0 && word == "phase" {
+ end = ind
+ break
+ }
+ }
+ phaseName := strings.Join(words[beginning:end], " ")
+
+ // end is now "phase" - advance by 2 for timing and 4 for percentage
+ percentageString := words[end+4]
+ timingString := words[end+2]
+ timing := parseTimingToNanos(timingString)
+ percentagePortion := parsePercentageToTenThousandths(percentageString)
+ return phaseName, timing, percentagePortion
+ }
+
+ phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+ phaseTiming := bazel_metrics_proto.PhaseTiming{}
+ phaseTiming.DurationNanos = &timing
+ phaseTiming.PortionOfBuildTime = &portion
+
+ phaseTiming.PhaseName = &phaseName
+ return phaseTiming
+}
+
+func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
+ if bazelProfileFile == "" {
+ return
+ }
+
+ readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+ //serialize the proto, write it
+ bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+ file, err := os.ReadFile(filepath)
+ if err != nil {
+ ctx.Fatalln("Error reading metrics file\n", err)
+ }
+
+ scanner := bufio.NewScanner(strings.NewReader(string(file)))
+ scanner.Split(bufio.ScanLines)
+
+ var phaseTimings []*bazel_metrics_proto.PhaseTiming
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "Total run time") {
+ total := parseTotal(line)
+ bazelMetrics.Total = &total
+ } else if strings.HasPrefix(line, "Total") {
+ phaseTiming := parsePhaseTiming(line)
+ phaseTimings = append(phaseTimings, &phaseTiming)
+ }
+ }
+ bazelMetrics.PhaseTimings = phaseTimings
+
+ return bazelMetrics
+ }
+
+ if _, err := os.Stat(bazelProfileFile); err != nil {
+ // We can assume bazel didn't run if the profile doesn't exist
+ return
+ }
+ bazelProto := readBazelProto(bazelProfileFile)
+ shared.Save(&bazelProto, bazelMetricsFile)
+}
+
// UploadMetrics uploads a set of metrics files to a server for analysis.
// The metrics files are first copied to a temporary directory
// and the uploader is then executed in the background to allow the user/system
// to continue working. Soong communicates to the uploader through the
// upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
@@ -88,6 +194,7 @@
return
}
+ processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
// Several of the files might be directories.
metricsFiles := pruneMetricsFiles(paths)
if len(metricsFiles) == 0 {
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 764a1e1..58d9237 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -29,6 +29,30 @@
"android/soong/ui/logger"
)
+func writeBazelProfileFile(dir string) error {
+ contents := `
+
+=== PHASE SUMMARY INFORMATION ===
+
+Total launch phase time 1.193 s 15.77%
+Total init phase time 1.092 s 14.44%
+Total target pattern evaluation phase time 0.580 s 7.67%
+Total interleaved loading-and-analysis phase time 3.646 s 48.21%
+Total preparation phase time 0.022 s 0.30%
+Total execution phase time 0.993 s 13.13%
+Total finish phase time 0.036 s 0.48%
+---------------------------------------------------------------------
+Total run time 7.563 s 100.00%
+
+Critical path (178 ms):
+ Time Percentage Description
+ 178 ms 100.00% action 'BazelWorkspaceStatusAction stable-status.txt'
+
+`
+ file := filepath.Join(dir, "bazel_metrics.txt")
+ return os.WriteFile(file, []byte(contents), 0666)
+}
+
func TestPruneMetricsFiles(t *testing.T) {
rootDir := t.TempDir()
@@ -84,12 +108,12 @@
}, {
description: "non-existent metrics files no upload",
uploader: "echo",
- files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3"},
+ files: []string{"metrics_file_1", "metrics_file_2", "metrics_file_3, bazel_metrics.pb"},
}, {
description: "trigger upload",
uploader: "echo",
createFiles: true,
- files: []string{"metrics_file_1", "metrics_file_2"},
+ files: []string{"metrics_file_1", "metrics_file_2, bazel_metrics.pb"},
}}
for _, tt := range tests {
@@ -130,6 +154,9 @@
}
}
}
+ if err := writeBazelProfileFile(outDir); err != nil {
+ t.Fatalf("failed to create bazel profile file in dir: %v", outDir)
+ }
config := Config{&configImpl{
environ: &Environment{
@@ -139,7 +166,7 @@
metricsUploader: tt.uploader,
}}
- UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
+ UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
})
}
}
@@ -194,8 +221,79 @@
metricsUploader: "echo",
}}
- UploadMetrics(ctx, config, true, time.Now(), metricsFile)
+ UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
})
}
}
+
+func TestParsePercentageToTenThousandths(t *testing.T) {
+ // 2.59% should be returned as 259 - representing 259/10000 of the build
+ percentage := parsePercentageToTenThousandths("2.59%")
+ if percentage != 259 {
+ t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 259, have %d\n", percentage)
+ }
+
+ // Test without a leading digit
+ percentage = parsePercentageToTenThousandths(".52%")
+ if percentage != 52 {
+ t.Errorf("Expected percentage to be returned as ten-thousandths. Expected 52, have %d\n", percentage)
+ }
+}
+
+func TestParseTimingToNanos(t *testing.T) {
+ // This parses from seconds (with millis precision) and returns nanos
+ timingNanos := parseTimingToNanos("0.111")
+ if timingNanos != 111000000 {
+ t.Errorf("Error parsing timing. Expected 111000, have %d\n", timingNanos)
+ }
+
+ // Test without a leading digit
+ timingNanos = parseTimingToNanos(".112")
+ if timingNanos != 112000000 {
+ t.Errorf("Error parsing timing. Expected 112000, have %d\n", timingNanos)
+ }
+}
+
+func TestParsePhaseTiming(t *testing.T) {
+ // Sample lines include:
+ // Total launch phase time 0.011 s 2.59%
+ // Total target pattern evaluation phase time 0.012 s 4.59%
+
+ line1 := "Total launch phase time 0.011 s 2.59%"
+ timing := parsePhaseTiming(line1)
+
+ if timing.GetPhaseName() != "launch" {
+ t.Errorf("Failed to parse phase name. Expected launch, have %s\n", timing.GetPhaseName())
+ } else if timing.GetDurationNanos() != 11000000 {
+ t.Errorf("Failed to parse duration nanos. Expected 11000000, have %d\n", timing.GetDurationNanos())
+ } else if timing.GetPortionOfBuildTime() != 259 {
+ t.Errorf("Failed to parse portion of build time. Expected 259, have %d\n", timing.GetPortionOfBuildTime())
+ }
+
+ // Test with a multiword phase name
+ line2 := "Total target pattern evaluation phase time 0.012 s 4.59%"
+
+ timing = parsePhaseTiming(line2)
+ if timing.GetPhaseName() != "target pattern evaluation" {
+ t.Errorf("Failed to parse phase name. Expected target pattern evaluation, have %s\n", timing.GetPhaseName())
+ } else if timing.GetDurationNanos() != 12000000 {
+ t.Errorf("Failed to parse duration nanos. Expected 12000000, have %d\n", timing.GetDurationNanos())
+ } else if timing.GetPortionOfBuildTime() != 459 {
+ t.Errorf("Failed to parse portion of build time. Expected 459, have %d\n", timing.GetPortionOfBuildTime())
+ }
+}
+
+func TestParseTotal(t *testing.T) {
+ // Total line is in the form of:
+ // Total run time 7.563 s 100.00%
+
+ line := "Total run time 7.563 s 100.00%"
+
+ total := parseTotal(line)
+
+ // Only the seconds field is parsed, as nanos
+ if total != 7563000000 {
+ t.Errorf("Failed to parse total build time. Expected 7563000000, have %d\n", total)
+ }
+}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 2301c56..bd1517c 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -22,6 +22,7 @@
deps: [
"golang-protobuf-proto",
"soong-ui-bp2build_metrics_proto",
+ "soong-ui-bazel_metrics_proto",
"soong-ui-metrics_upload_proto",
"soong-ui-metrics_proto",
"soong-ui-mk_metrics_proto",
@@ -73,6 +74,18 @@
}
bootstrap_go_package {
+ name: "soong-ui-bazel_metrics_proto",
+ pkgPath: "android/soong/ui/metrics/bazel_metrics_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "bazel_metrics_proto/bazel_metrics.pb.go",
+ ],
+}
+
+bootstrap_go_package {
name: "soong-ui-mk_metrics_proto",
pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
deps: [
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
index 93f3471..4821043 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -14,8 +14,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.27.1
-// protoc v3.9.1
+// protoc-gen-go v1.28.0
+// protoc v3.21.7
// source: bp2build_metrics.proto
package bp2build_metrics_proto
@@ -45,6 +45,10 @@
HandCraftedModuleCount uint64 `protobuf:"varint,2,opt,name=handCraftedModuleCount,proto3" json:"handCraftedModuleCount,omitempty"`
// Total number of unconverted Soong modules
UnconvertedModuleCount uint64 `protobuf:"varint,3,opt,name=unconvertedModuleCount,proto3" json:"unconvertedModuleCount,omitempty"`
+ // Counts of symlinks in synthetic bazel workspace
+ WorkspaceSymlinkCount uint64 `protobuf:"varint,9,opt,name=workspaceSymlinkCount,proto3" json:"workspaceSymlinkCount,omitempty"`
+ // Counts of mkdir calls during creation of synthetic bazel workspace
+ WorkspaceMkDirCount uint64 `protobuf:"varint,10,opt,name=workspaceMkDirCount,proto3" json:"workspaceMkDirCount,omitempty"`
// Counts of generated Bazel targets per Bazel rule class
RuleClassCount map[string]uint64 `protobuf:"bytes,4,rep,name=ruleClassCount,proto3" json:"ruleClassCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
// List of converted modules
@@ -111,6 +115,20 @@
return 0
}
+func (x *Bp2BuildMetrics) GetWorkspaceSymlinkCount() uint64 {
+ if x != nil {
+ return x.WorkspaceSymlinkCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetWorkspaceMkDirCount() uint64 {
+ if x != nil {
+ return x.WorkspaceMkDirCount
+ }
+ return 0
+}
+
func (x *Bp2BuildMetrics) GetRuleClassCount() map[string]uint64 {
if x != nil {
return x.RuleClassCount
@@ -221,7 +239,7 @@
0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
- 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xe9, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xd1, 0x07, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
@@ -232,60 +250,66 @@
0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76,
0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
- 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69,
- 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,
- 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
- 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
- 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65,
- 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43,
- 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43,
- 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e,
- 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
- 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
- 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
- 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75,
- 0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
- 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
- 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64,
- 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
- 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
- 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
- 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
- 0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
- 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e,
- 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62,
- 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32,
- 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74,
- 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
- 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64,
- 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06,
- 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73,
- 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75,
- 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e,
- 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c,
- 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
- 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
- 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d,
+ 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34,
+ 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69,
+ 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x77,
+ 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x43,
+ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x6b, 0x44, 0x69,
+ 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69, 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41,
+ 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32,
+ 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70,
+ 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75,
+ 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e,
+ 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01,
+ 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62,
+ 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+ 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54,
- 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
- 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
- 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
- 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74,
- 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
- 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
- 0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
- 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
- 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b,
- 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61,
- 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f,
- 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64,
- 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63,
+ 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
+ 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+ 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+ 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+ 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14,
+ 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43,
+ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+ 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+ 0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74,
+ 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f,
+ 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
+ 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
+ 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
+ 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76,
+ 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61,
+ 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74,
+ 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54,
+ 0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
+ 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
+ 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+ 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
index 19a7827..9ff4ae2 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -27,6 +27,12 @@
// Total number of unconverted Soong modules
uint64 unconvertedModuleCount = 3;
+ // Counts of symlinks in synthetic bazel workspace
+ uint64 workspaceSymlinkCount= 9;
+
+ // Counts of mkdir calls during creation of synthetic bazel workspace
+ uint64 workspaceMkDirCount= 10;
+
// Counts of generated Bazel targets per Bazel rule class
map<string, uint64> ruleClassCount = 4;
diff --git a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
index 356b188..f6c6df8 100644
--- a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
+++ b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
@@ -22,6 +22,6 @@
py_proto_library(
name = "bp2build_py_proto",
+ visibility = ["//build/bazel/scripts/bp2build_progress:__pkg__"],
deps = [":bp2build_proto"],
- visibility = ["//build/bazel/scripts/bp2build-progress:__pkg__"],
)
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index cbc73ed..def76aa 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -163,6 +163,7 @@
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")
+ sha256Checksum := flags.Bool("sha256", false, "add a zip header to each file containing its SHA256 digest")
flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
flags.Var(&listFiles{}, "l", "file containing list of files to zip")
@@ -224,6 +225,7 @@
WriteIfChanged: *writeIfChanged,
StoreSymlinks: *symlinks,
IgnoreMissingFiles: *ignoreMissingFiles,
+ Sha256Checksum: *sha256Checksum,
})
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err.Error())
diff --git a/zip/zip.go b/zip/zip.go
index 955fe68..6f1a8ad 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -17,8 +17,11 @@
import (
"bytes"
"compress/flate"
+ "crypto/sha256"
+ "encoding/binary"
"errors"
"fmt"
+ "hash"
"hash/crc32"
"io"
"io/ioutil"
@@ -38,6 +41,14 @@
"android/soong/third_party/zip"
)
+// Sha256HeaderID is a custom Header ID for the `extra` field in
+// the file header to store the SHA checksum.
+const Sha256HeaderID = 0x4967
+
+// Sha256HeaderSignature is the signature to verify that the extra
+// data block is used to store the SHA checksum.
+const Sha256HeaderSignature = 0x9514
+
// Block size used during parallel compression of a single file.
const parallelBlockSize = 1 * 1024 * 1024 // 1MB
@@ -231,6 +242,8 @@
stderr io.Writer
fs pathtools.FileSystem
+
+ sha256Checksum bool
}
type zipEntry struct {
@@ -257,6 +270,7 @@
WriteIfChanged bool
StoreSymlinks bool
IgnoreMissingFiles bool
+ Sha256Checksum bool
Stderr io.Writer
Filesystem pathtools.FileSystem
@@ -280,6 +294,7 @@
ignoreMissingFiles: args.IgnoreMissingFiles,
stderr: args.Stderr,
fs: args.Filesystem,
+ sha256Checksum: args.Sha256Checksum,
}
if z.fs == nil {
@@ -782,15 +797,17 @@
// this based on actual buffer sizes in RateLimit.
ze.futureReaders = make(chan chan io.Reader, (fileSize/parallelBlockSize)+1)
- // Calculate the CRC in the background, since reading the entire
- // file could take a while.
+ // Calculate the CRC and SHA256 in the background, since reading
+ // the entire file could take a while.
//
// We could split this up into chunks as well, but it's faster
// than the compression. Due to the Go Zip API, we also need to
// know the result before we can begin writing the compressed
// data out to the zipfile.
+ //
+ // We calculate SHA256 only if `-sha256` is set.
wg.Add(1)
- go z.crcFile(r, ze, compressChan, wg)
+ go z.checksumFileAsync(r, ze, compressChan, wg)
for start := int64(0); start < fileSize; start += parallelBlockSize {
sr := io.NewSectionReader(r, start, parallelBlockSize)
@@ -829,20 +846,53 @@
return nil
}
-func (z *ZipWriter) crcFile(r io.Reader, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
+func (z *ZipWriter) checksumFileAsync(r io.ReadSeeker, ze *zipEntry, resultChan chan *zipEntry, wg *sync.WaitGroup) {
defer wg.Done()
defer z.cpuRateLimiter.Finish()
+ z.checksumFile(r, ze)
+
+ resultChan <- ze
+ close(resultChan)
+}
+
+func (z *ZipWriter) checksumFile(r io.ReadSeeker, ze *zipEntry) {
crc := crc32.NewIEEE()
- _, err := io.Copy(crc, r)
+ writers := []io.Writer{crc}
+
+ var shaHasher hash.Hash
+ if z.sha256Checksum && !ze.fh.Mode().IsDir() {
+ shaHasher = sha256.New()
+ writers = append(writers, shaHasher)
+ }
+
+ w := io.MultiWriter(writers...)
+
+ _, err := io.Copy(w, r)
if err != nil {
z.errors <- err
return
}
ze.fh.CRC32 = crc.Sum32()
- resultChan <- ze
- close(resultChan)
+ if shaHasher != nil {
+ z.appendSHAToExtra(ze, shaHasher.Sum(nil))
+ }
+}
+
+func (z *ZipWriter) appendSHAToExtra(ze *zipEntry, checksum []byte) {
+ // The block of SHA256 checksum consist of:
+ // - Header ID, equals to Sha256HeaderID (2 bytes)
+ // - Data size (2 bytes)
+ // - Data block:
+ // - Signature, equals to Sha256HeaderSignature (2 bytes)
+ // - Data, SHA checksum value
+ var buf []byte
+ buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderID)
+ buf = binary.LittleEndian.AppendUint16(buf, uint16(len(checksum)+2))
+ buf = binary.LittleEndian.AppendUint16(buf, Sha256HeaderSignature)
+ buf = append(buf, checksum...)
+ ze.fh.Extra = append(ze.fh.Extra, buf...)
}
func (z *ZipWriter) compressPartialFile(r io.Reader, dict []byte, last bool, resultChan chan io.Reader, wg *sync.WaitGroup) {
@@ -894,17 +944,9 @@
}
func (z *ZipWriter) compressWholeFile(ze *zipEntry, r io.ReadSeeker, compressChan chan *zipEntry) {
+ z.checksumFile(r, ze)
- crc := crc32.NewIEEE()
- _, err := io.Copy(crc, r)
- if err != nil {
- z.errors <- err
- return
- }
-
- ze.fh.CRC32 = crc.Sum32()
-
- _, err = r.Seek(0, 0)
+ _, err := r.Seek(0, 0)
if err != nil {
z.errors <- err
return
diff --git a/zip/zip_test.go b/zip/zip_test.go
index c4832dc..e7fdea8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -16,6 +16,7 @@
import (
"bytes"
+ "encoding/hex"
"hash/crc32"
"io"
"os"
@@ -35,6 +36,10 @@
fileEmpty = []byte("")
fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
+ sha256FileA = "d53eda7a637c99cc7fb566d96e9fa109bf15c478410a3f5eb4d4c4e26cd081f6"
+ sha256FileB = "430c56c5818e62bcb6d478901ef86284e97714c138f3c86aa14fd6a84b7ce5d3"
+ sha256FileC = "31c5ab6111f1d6aa13c2c4e92bb3c0f7c76b61b42d141af1e846eb7f6586a51c"
+
fileCustomManifest = []byte("Custom manifest: true\n")
customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
)
@@ -67,6 +72,20 @@
}
}
+func fhWithSHA256(name string, contents []byte, method uint16, sha256 string) zip.FileHeader {
+ h := fh(name, contents, method)
+ // The extra field contains 38 bytes, including 2 bytes of header ID, 2 bytes
+ // of size, 2 bytes of signature, and 32 bytes of checksum data block.
+ var extra [38]byte
+ // The first 6 bytes contains Sha256HeaderID (0x4967), size (unit(34)) and
+ // Sha256HeaderSignature (0x9514)
+ copy(extra[0:], []byte{103, 73, 34, 0, 20, 149})
+ sha256Bytes, _ := hex.DecodeString(sha256)
+ copy(extra[6:], sha256Bytes)
+ h.Extra = append(h.Extra, extra[:]...)
+ return h
+}
+
func fhManifest(contents []byte) zip.FileHeader {
return zip.FileHeader{
Name: "META-INF/MANIFEST.MF",
@@ -87,13 +106,18 @@
}
}
-func fhDir(name string) zip.FileHeader {
+type fhDirOptions struct {
+ extra []byte
+}
+
+func fhDir(name string, opts fhDirOptions) zip.FileHeader {
return zip.FileHeader{
Name: name,
Method: zip.Store,
CRC32: crc32.ChecksumIEEE(nil),
UncompressedSize64: 0,
ExternalAttrs: (syscall.S_IFDIR|0755)<<16 | 0x10,
+ Extra: opts.extra,
}
}
@@ -114,6 +138,7 @@
manifest string
storeSymlinks bool
ignoreMissingFiles bool
+ sha256Checksum bool
files []zip.FileHeader
err error
@@ -320,10 +345,10 @@
emulateJar: true,
files: []zip.FileHeader{
- fhDir("META-INF/"),
+ fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
fhManifest(fileManifest),
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -338,10 +363,10 @@
manifest: "manifest.txt",
files: []zip.FileHeader{
- fhDir("META-INF/"),
+ fhDir("META-INF/", fhDirOptions{extra: []byte{254, 202, 0, 0}}),
fhManifest(customManifestAfter),
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -355,8 +380,8 @@
dirEntries: true,
files: []zip.FileHeader{
- fhDir("a/"),
- fhDir("a/a/"),
+ fhDir("a/", fhDirOptions{}),
+ fhDir("a/a/", fhDirOptions{}),
fh("a/a/a", fileA, zip.Deflate),
fh("a/a/b", fileB, zip.Deflate),
},
@@ -412,6 +437,23 @@
fh("a/a/a", fileA, zip.Deflate),
},
},
+ {
+ name: "generate SHA256 checksum",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("a/a/c").
+ File("c"),
+ compressionLevel: 9,
+ sha256Checksum: true,
+
+ files: []zip.FileHeader{
+ fhWithSHA256("a/a/a", fileA, zip.Deflate, sha256FileA),
+ fhWithSHA256("a/a/b", fileB, zip.Deflate, sha256FileB),
+ fhWithSHA256("a/a/c", fileC, zip.Deflate, sha256FileC),
+ fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
+ },
+ },
// errors
{
@@ -465,6 +507,7 @@
args.ManifestSourcePath = test.manifest
args.StoreSymlinks = test.storeSymlinks
args.IgnoreMissingFiles = test.ignoreMissingFiles
+ args.Sha256Checksum = test.sha256Checksum
args.Filesystem = mockFs
args.Stderr = &bytes.Buffer{}
@@ -555,6 +598,11 @@
t.Errorf("incorrect file %s method want %v got %v", want.Name,
want.Method, got.Method)
}
+
+ if !bytes.Equal(want.Extra, got.Extra) {
+ t.Errorf("incorrect file %s extra want %v got %v", want.Name,
+ want.Extra, got.Extra)
+ }
}
})
}