From 47535c51fabab67b2c4b36a4038fa65418310781 Mon Sep 17 00:00:00 2001 From: Liz Kammer Date: Wed, 2 Jun 2021 16:02:22 -0400 Subject: [PATCH] Handle excludes_{shared,static}_libs Bug: 188497994 Test: bp2build.sh Change-Id: I4a5ea40cbd804e8542fe33143e4926abc0c6164f --- android/bazel_paths.go | 16 +++ android/variable.go | 17 +-- bp2build/cc_library_conversion_test.go | 114 ++++++++++++++++++++ cc/bp2build.go | 141 +++++++++++++++++++------ 4 files changed, 252 insertions(+), 36 deletions(-) diff --git a/android/bazel_paths.go b/android/bazel_paths.go index f4b2a7caf..f93fe2b5e 100644 --- a/android/bazel_paths.go +++ b/android/bazel_paths.go @@ -100,6 +100,22 @@ func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) b return labels } +// BazelLabelForModuleDeps expects two lists: modules (containing modules to include in the list), +// and excludes (modules to exclude from the list). Both of these should contain references to other +// modules, ("" or ":"). It returns a Bazel-compatible label list which corresponds +// to dependencies on the module within the given ctx, and the excluded dependencies. +func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList { + moduleLabels := BazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes)) + if len(excludes) == 0 { + return moduleLabels + } + excludeLabels := BazelLabelForModuleDeps(ctx, excludes) + return bazel.LabelList{ + Includes: moduleLabels.Includes, + Excludes: excludeLabels.Includes, + } +} + func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label { return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0] } diff --git a/android/variable.go b/android/variable.go index 6d235d610..c76612099 100644 --- a/android/variable.go +++ b/android/variable.go @@ -458,12 +458,13 @@ type ProductConfigContext interface { // with the appropriate ProductConfigVariable. type ProductConfigProperty struct { ProductConfigVariable string + FullConfig string Property interface{} } // ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that // all it all product variable-specific versions of a property are easily accessed together -type ProductConfigProperties map[string][]ProductConfigProperty +type ProductConfigProperties map[string]map[string]ProductConfigProperty // ProductVariableProperties returns a ProductConfigProperties containing only the properties which // have been set for the module in the given context. @@ -512,11 +513,15 @@ func productVariableValues(variableProps interface{}, suffix string, productConf // e.g. Asflags, Cflags, Enabled, etc. propertyName := variableValue.Type().Field(j).Name - (*productConfigProperties)[propertyName] = append((*productConfigProperties)[propertyName], - ProductConfigProperty{ - ProductConfigVariable: productVariableName + suffix, - Property: property.Interface(), - }) + if (*productConfigProperties)[propertyName] == nil { + (*productConfigProperties)[propertyName] = make(map[string]ProductConfigProperty) + } + config := productVariableName + suffix + (*productConfigProperties)[propertyName][config] = ProductConfigProperty{ + ProductConfigVariable: productVariableName, + FullConfig: config, + Property: property.Interface(), + } } } } diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index c8ae031a2..d84a7bbe5 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -985,3 +985,117 @@ func TestCcLibraryLabelAttributeGetTargetProperties(t *testing.T) { )`}, }) } + +func TestCcLibraryExcludeLibs(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryStaticPreamble + ` +cc_library { + name: "foo_static", + srcs: ["common.c"], + whole_static_libs: [ + "arm_whole_static_lib_excludes", + "malloc_not_svelte_whole_static_lib_excludes" + ], + static_libs: [ + "arm_static_lib_excludes", + "malloc_not_svelte_static_lib_excludes" + ], + shared_libs: [ + "arm_shared_lib_excludes", + ], + arch: { + arm: { + exclude_shared_libs: [ + "arm_shared_lib_excludes", + ], + exclude_static_libs: [ + "arm_static_lib_excludes", + "arm_whole_static_lib_excludes", + ], + }, + }, + product_variables: { + malloc_not_svelte: { + shared_libs: ["malloc_not_svelte_shared_lib"], + whole_static_libs: ["malloc_not_svelte_whole_static_lib"], + exclude_static_libs: [ + "malloc_not_svelte_static_lib_excludes", + "malloc_not_svelte_whole_static_lib_excludes", + ], + }, + }, +} + +cc_library { + name: "arm_whole_static_lib_excludes", + bazel_module: { bp2build_available: false }, +} + +cc_library { + name: "malloc_not_svelte_whole_static_lib", + bazel_module: { bp2build_available: false }, +} + +cc_library { + name: "malloc_not_svelte_whole_static_lib_excludes", + bazel_module: { bp2build_available: false }, +} + +cc_library { + name: "arm_static_lib_excludes", + bazel_module: { bp2build_available: false }, +} + +cc_library { + name: "malloc_not_svelte_static_lib_excludes", + bazel_module: { bp2build_available: false }, +} + +cc_library { + name: "arm_shared_lib_excludes", + bazel_module: { bp2build_available: false }, +} + +cc_library { + name: "malloc_not_svelte_shared_lib", + bazel_module: { bp2build_available: false }, +} +`, + expectedBazelTargets: []string{ + `cc_library( + name = "foo_static", + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + dynamic_deps = select({ + "//build/bazel/platforms/arch:arm": [], + "//conditions:default": [":arm_shared_lib_excludes"], + }) + select({ + "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"], + "//conditions:default": [], + }), + implementation_deps = select({ + "//build/bazel/platforms/arch:arm": [], + "//conditions:default": [":arm_static_lib_excludes"], + }) + select({ + "//build/bazel/product_variables:malloc_not_svelte": [], + "//conditions:default": [":malloc_not_svelte_static_lib_excludes"], + }), + srcs_c = ["common.c"], + whole_archive_deps = select({ + "//build/bazel/platforms/arch:arm": [], + "//conditions:default": [":arm_whole_static_lib_excludes"], + }) + select({ + "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib"], + "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes"], + }), +)`, + }, + }) +} diff --git a/cc/bp2build.go b/cc/bp2build.go index 1de45baed..752d43b50 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -112,6 +112,30 @@ func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { } } + // product variables only support a limited set of fields, this is the full list of field names + // related to cc module dependency management that are supported. + productVariableDepFields := [4]string{ + "Shared_libs", + "Static_libs", + "Exclude_static_libs", + "Whole_static_libs", + } + + productVariableProps := android.ProductVariableProperties(ctx) + for _, name := range productVariableDepFields { + props, exists := productVariableProps[name] + if !exists { + continue + } + for _, prop := range props { + if p, ok := prop.Property.([]string); !ok { + ctx.ModuleErrorf("Could not convert product variable %s property", name) + } else { + allDeps = append(allDeps, p...) + } + } + } + ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...) } @@ -441,7 +465,7 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName)) } newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable) - attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.ProductConfigVariable), prop.ProductConfigVariable, newFlags) + attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.FullConfig), prop.FullConfig, newFlags) } } } @@ -481,37 +505,37 @@ func getBp2BuildLinkerFlags(linkerProperties *BaseLinkerProperties) []string { // bp2BuildParseLinkerProps parses the linker properties of a module, including // configurable attribute values. func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes { - var deps bazel.LabelListAttribute + var headerDeps bazel.LabelListAttribute + var staticDeps bazel.LabelListAttribute var exportedDeps bazel.LabelListAttribute var dynamicDeps bazel.LabelListAttribute var wholeArchiveDeps bazel.LabelListAttribute var linkopts bazel.StringListAttribute var versionScript bazel.LabelAttribute - getLibs := func(baseLinkerProps *BaseLinkerProperties) []string { - libs := baseLinkerProps.Header_libs - libs = append(libs, baseLinkerProps.Static_libs...) - libs = android.SortedUniqueStrings(libs) - return libs - } - for _, linkerProps := range module.linker.linkerProps() { if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok { - libs := getLibs(baseLinkerProps) - exportedLibs := baseLinkerProps.Export_header_lib_headers - wholeArchiveLibs := baseLinkerProps.Whole_static_libs - deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs)) - exportedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, exportedLibs)) - linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps) - wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) + // Excludes to parallel Soong: + // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0 + staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs) + staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs) + wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs) + wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) + sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs) + dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) + headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs) + headerDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, headerLibs)) + // TODO(b/188796939): also handle export_static_lib_headers, export_shared_lib_headers, + // export_generated_headers + exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers) + exportedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, exportedLibs)) + + linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps) if baseLinkerProps.Version_script != nil { versionScript.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)) } - sharedLibs := baseLinkerProps.Shared_libs - dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, sharedLibs)) - break } } @@ -519,26 +543,83 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) { for config, props := range configToProps { if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok { - libs := getLibs(baseLinkerProps) - exportedLibs := baseLinkerProps.Export_header_lib_headers - wholeArchiveLibs := baseLinkerProps.Whole_static_libs - deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, libs)) - exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs)) - linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps)) - wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) + staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs) + staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)) + wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs) + wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) + sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs) + dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) + headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs) + headerDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, headerLibs)) + exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers) + exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs)) + + linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps)) if baseLinkerProps.Version_script != nil { versionScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)) } - - sharedLibs := baseLinkerProps.Shared_libs - dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, sharedLibs)) } } } + type productVarDep struct { + // the name of the corresponding excludes field, if one exists + excludesField string + // reference to the bazel attribute that should be set for the given product variable config + attribute *bazel.LabelListAttribute + } + + productVarToDepFields := map[string]productVarDep{ + // product variables do not support exclude_shared_libs + "Shared_libs": productVarDep{attribute: &dynamicDeps}, + "Static_libs": productVarDep{"Exclude_static_libs", &staticDeps}, + "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps}, + } + + productVariableProps := android.ProductVariableProperties(ctx) + 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 !exists && !excludesExists { + continue + } + // collect all the configurations that an include or exclude property exists for. + // we want to iterate all configurations rather than either the include or exclude because for a + // particular configuration we may have only and include or only an exclude to handle + configs := make(map[string]bool, len(props)+len(excludeProps)) + for config := range props { + configs[config] = true + } + for config := range excludeProps { + configs[config] = true + } + + for config := range configs { + prop, includesExists := props[config] + excludesProp, excludesExists := excludeProps[config] + var includes, excludes []string + var ok bool + // if there was no includes/excludes property, casting fails and that's expected + if includes, ok = prop.Property.([]string); includesExists && !ok { + ctx.ModuleErrorf("Could not convert product variable %s property", name) + } + if excludes, ok = excludesProp.Property.([]string); excludesExists && !ok { + ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField) + } + dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, android.BazelLabelForModuleDepsExcludes(ctx, android.FirstUniqueStrings(includes), excludes)) + } + } + + staticDeps.ResolveExcludes() + dynamicDeps.ResolveExcludes() + wholeArchiveDeps.ResolveExcludes() + + headerDeps.Append(staticDeps) + return linkerAttributes{ - deps: deps, + deps: headerDeps, exportedDeps: exportedDeps, dynamicDeps: dynamicDeps, wholeArchiveDeps: wholeArchiveDeps,