1// Copyright 2021 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package android 16 17import ( 18 "fmt" 19 "io/ioutil" 20 "path/filepath" 21 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/proptools" 25) 26 27type bazelModuleProperties struct { 28 // The label of the Bazel target replacing this Soong module. When run in conversion mode, this 29 // will import the handcrafted build target into the autogenerated file. Note: this may result in 30 // a conflict due to duplicate targets if bp2build_available is also set. 31 Label *string 32 33 // If true, bp2build will generate the converted Bazel target for this module. Note: this may 34 // cause a conflict due to the duplicate targets if label is also set. 35 // 36 // This is a bool pointer to support tristates: true, false, not set. 37 // 38 // To opt-in a module, set bazel_module: { bp2build_available: true } 39 // To opt-out a module, set bazel_module: { bp2build_available: false } 40 // To defer the default setting for the directory, do not set the value. 41 Bp2build_available *bool 42} 43 44// Properties contains common module properties for Bazel migration purposes. 45type properties struct { 46 // In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing 47 // this Soong module. 48 Bazel_module bazelModuleProperties 49} 50 51// BazelModuleBase contains the property structs with metadata for modules which can be converted to 52// Bazel. 53type BazelModuleBase struct { 54 bazelProperties properties 55} 56 57// Bazelable is specifies the interface for modules that can be converted to Bazel. 58type Bazelable interface { 59 bazelProps() *properties 60 HasHandcraftedLabel() bool 61 HandcraftedLabel() string 62 GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string 63 ConvertWithBp2build(ctx BazelConversionPathContext) bool 64 GetBazelBuildFileContents(c Config, path, name string) (string, error) 65 ConvertedToBazel(ctx BazelConversionPathContext) bool 66} 67 68// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules. 69type BazelModule interface { 70 Module 71 Bazelable 72} 73 74// InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion 75// properties. 76func InitBazelModule(module BazelModule) { 77 module.AddProperties(module.bazelProps()) 78} 79 80// bazelProps returns the Bazel properties for the given BazelModuleBase. 81func (b *BazelModuleBase) bazelProps() *properties { 82 return &b.bazelProperties 83} 84 85// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label. 86func (b *BazelModuleBase) HasHandcraftedLabel() bool { 87 return b.bazelProperties.Bazel_module.Label != nil 88} 89 90// HandcraftedLabel returns the handcrafted label for this module, or empty string if there is none 91func (b *BazelModuleBase) HandcraftedLabel() string { 92 return proptools.String(b.bazelProperties.Bazel_module.Label) 93} 94 95// GetBazelLabel returns the Bazel label for the given BazelModuleBase. 96func (b *BazelModuleBase) GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string { 97 if b.HasHandcraftedLabel() { 98 return b.HandcraftedLabel() 99 } 100 if b.ConvertWithBp2build(ctx) { 101 return bp2buildModuleLabel(ctx, module) 102 } 103 return "" // no label for unconverted module 104} 105 106// Configuration to decide if modules in a directory should default to true/false for bp2build_available 107type Bp2BuildConfig map[string]BazelConversionConfigEntry 108type BazelConversionConfigEntry int 109 110const ( 111 // A sentinel value to be used as a key in Bp2BuildConfig for modules with 112 // no package path. This is also the module dir for top level Android.bp 113 // modules. 114 BP2BUILD_TOPLEVEL = "." 115 116 // iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map, 117 // which can also mean that the key doesn't exist in a lookup. 118 119 // all modules in this package and subpackages default to bp2build_available: true. 120 // allows modules to opt-out. 121 Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1 122 123 // all modules in this package (not recursively) default to bp2build_available: false. 124 // allows modules to opt-in. 125 Bp2BuildDefaultFalse 126) 127 128var ( 129 // Do not write BUILD files for these directories 130 // NOTE: this is not recursive 131 bp2buildDoNotWriteBuildFileList = []string{ 132 // Don't generate these BUILD files - because external BUILD files already exist 133 "external/boringssl", 134 "external/brotli", 135 "external/dagger2", 136 "external/flatbuffers", 137 "external/gflags", 138 "external/google-fruit", 139 "external/grpc-grpc", 140 "external/grpc-grpc/test/core/util", 141 "external/grpc-grpc/test/cpp/common", 142 "external/grpc-grpc/third_party/address_sorting", 143 "external/nanopb-c", 144 "external/nos/host/generic", 145 "external/nos/host/generic/libnos", 146 "external/nos/host/generic/libnos/generator", 147 "external/nos/host/generic/libnos_datagram", 148 "external/nos/host/generic/libnos_transport", 149 "external/nos/host/generic/nugget/proto", 150 "external/perfetto", 151 "external/protobuf", 152 "external/rust/cxx", 153 "external/rust/cxx/demo", 154 "external/ruy", 155 "external/tensorflow", 156 "external/tensorflow/tensorflow/lite", 157 "external/tensorflow/tensorflow/lite/java", 158 "external/tensorflow/tensorflow/lite/kernels", 159 "external/tflite-support", 160 "external/tinyalsa_new", 161 "external/wycheproof", 162 "external/libyuv", 163 } 164 165 // Configure modules in these directories to enable bp2build_available: true or false by default. 166 bp2buildDefaultConfig = Bp2BuildConfig{ 167 "bionic": Bp2BuildDefaultTrueRecursively, 168 "external/gwp_asan": Bp2BuildDefaultTrueRecursively, 169 "system/core/libcutils": Bp2BuildDefaultTrueRecursively, 170 "system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively, 171 "system/libbase": Bp2BuildDefaultTrueRecursively, 172 "system/logging/liblog": Bp2BuildDefaultTrueRecursively, 173 "external/jemalloc_new": Bp2BuildDefaultTrueRecursively, 174 "external/fmtlib": Bp2BuildDefaultTrueRecursively, 175 "external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively, 176 "external/scudo": Bp2BuildDefaultTrueRecursively, 177 } 178 179 // Per-module denylist to always opt modules out of both bp2build and mixed builds. 180 bp2buildModuleDoNotConvertList = []string{ 181 // Things that transitively depend on unconverted libc_* modules. 182 "libc_nopthread", // http://b/186821550, cc_library_static, depends on //bionic/libc:libc_bionic_ndk (http://b/186822256) 183 // also depends on //bionic/libc:libc_tzcode (http://b/186822591) 184 // also depends on //bionic/libc:libstdc++ (http://b/186822597) 185 "libc_common", // http://b/186821517, cc_library_static, depends on //bionic/libc:libc_nopthread (http://b/186821550) 186 "libc_common_static", // http://b/186824119, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) 187 "libc_common_shared", // http://b/186824118, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) 188 "libc_nomalloc", // http://b/186825031, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) 189 190 "libbionic_spawn_benchmark", // http://b/186824595, cc_library_static, depends on //external/google-benchmark (http://b/186822740) 191 // also depends on //system/logging/liblog:liblog (http://b/186822772) 192 193 "libc_malloc_debug", // http://b/186824339, cc_library_static, depends on //system/libbase:libbase (http://b/186823646) 194 "libc_malloc_debug_backtrace", // http://b/186824112, cc_library_static, depends on //external/libcxxabi:libc++demangle (http://b/186823773) 195 196 "libcutils", // http://b/186827426, cc_library, depends on //system/core/libprocessgroup:libprocessgroup_headers (http://b/186826841) 197 "libcutils_sockets", // http://b/186826853, cc_library, depends on //system/libbase:libbase (http://b/186826479) 198 199 "liblinker_debuggerd_stub", // http://b/186824327, cc_library_static, depends on //external/zlib:libz (http://b/186823782) 200 // also depends on //system/libziparchive:libziparchive (http://b/186823656) 201 // also depends on //system/logging/liblog:liblog (http://b/186822772) 202 "liblinker_main", // http://b/186825989, cc_library_static, depends on //external/zlib:libz (http://b/186823782) 203 // also depends on //system/libziparchive:libziparchive (http://b/186823656) 204 // also depends on//system/logging/liblog:liblog (http://b/186822772) 205 "liblinker_malloc", // http://b/186826466, cc_library_static, depends on //external/zlib:libz (http://b/186823782) 206 // also depends on //system/libziparchive:libziparchive (http://b/186823656) 207 // also depends on //system/logging/liblog:liblog (http://b/186822772) 208 "libc_jemalloc_wrapper", // http://b/187012490, cc_library_static, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) 209 "libc_ndk", // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661) 210 "libc", // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) 211 "libc_bionic_ndk", // http://b/186822256, cc_library_static, fatal error: 'generated_android_ids.h' file not found 212 "libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook 213 "libm", // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list 214 215 // http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx 216 // c++_static 217 "libbase_ndk", // http://b/186826477, cc_library, no such target '//build/bazel/platforms/os:darwin' when --platforms //build/bazel/platforms:android_x86 is added 218 // libcxx 219 "libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx 220 "fmtlib", // cc_library_static, fatal error: 'cassert' file not found, from libcxx 221 "libbase", // http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx 222 223 // http://b/186024507: Includes errors because of the system_shared_libs default value. 224 // Missing -isystem bionic/libc/include through the libc/libm/libdl 225 // default dependencies if system_shared_libs is unset. 226 "liblog", // http://b/186822772: cc_library, 'sys/cdefs.h' file not found 227 "libjemalloc5_jet", // cc_library, 'sys/cdefs.h' file not found 228 "libseccomp_policy", // http://b/186476753: cc_library, 'linux/filter.h' not found 229 "note_memtag_heap_async", // http://b/185127353: cc_library_static, error: feature.h not found 230 "note_memtag_heap_sync", // http://b/185127353: cc_library_static, error: feature.h not found 231 232 // Tests. Handle later. 233 "libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found 234 "libjemalloc5_integrationtest", 235 "libjemalloc5_stresstestlib", 236 "libjemalloc5_unittest", 237 } 238 239 // Per-module denylist of cc_library modules to only generate the static 240 // variant if their shared variant isn't ready or buildable by Bazel. 241 bp2buildCcLibraryStaticOnlyList = []string{ 242 "libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno 243 } 244 245 // Per-module denylist to opt modules out of mixed builds. Such modules will 246 // still be generated via bp2build. 247 mixedBuildsDisabledList = []string{ 248 "libc_netbsd", // lberki@, cc_library_static, version script assignment of 'LIBC_PRIVATE' to symbol 'SHA1Final' failed: symbol not defined 249 "libc_openbsd", // ruperts@, cc_library_static, OK for bp2build but error: duplicate symbol: strcpy for mixed builds 250 "libsystemproperties", // cparsons@, cc_library_static, wrong include paths 251 "libpropertyinfoparser", // cparsons@, cc_library_static, wrong include paths 252 "libarm-optimized-routines-string", // jingwen@, cc_library_static, OK for bp2build but b/186615213 (asflags not handled in bp2build), version script assignment of 'LIBC' to symbol 'memcmp' failed: symbol not defined (also for memrchr, strnlen) 253 "fmtlib_ndk", // http://b/187040371, cc_library_static, OK for bp2build but format-inl.h:11:10: fatal error: 'cassert' file not found for mixed builds 254 } 255 256 // Used for quicker lookups 257 bp2buildDoNotWriteBuildFile = map[string]bool{} 258 bp2buildModuleDoNotConvert = map[string]bool{} 259 bp2buildCcLibraryStaticOnly = map[string]bool{} 260 mixedBuildsDisabled = map[string]bool{} 261) 262 263func init() { 264 for _, moduleName := range bp2buildDoNotWriteBuildFileList { 265 bp2buildDoNotWriteBuildFile[moduleName] = true 266 } 267 268 for _, moduleName := range bp2buildModuleDoNotConvertList { 269 bp2buildModuleDoNotConvert[moduleName] = true 270 } 271 272 for _, moduleName := range bp2buildCcLibraryStaticOnlyList { 273 bp2buildCcLibraryStaticOnly[moduleName] = true 274 } 275 276 for _, moduleName := range mixedBuildsDisabledList { 277 mixedBuildsDisabled[moduleName] = true 278 } 279} 280 281func GenerateCcLibraryStaticOnly(ctx BazelConversionPathContext) bool { 282 return bp2buildCcLibraryStaticOnly[ctx.Module().Name()] 283} 284 285func ShouldWriteBuildFileForDir(dir string) bool { 286 if _, ok := bp2buildDoNotWriteBuildFile[dir]; ok { 287 return false 288 } else { 289 return true 290 } 291} 292 293// MixedBuildsEnabled checks that a module is ready to be replaced by a 294// converted or handcrafted Bazel target. 295func (b *BazelModuleBase) MixedBuildsEnabled(ctx BazelConversionPathContext) bool { 296 if !ctx.Config().BazelContext.BazelEnabled() { 297 return false 298 } 299 if len(b.GetBazelLabel(ctx, ctx.Module())) == 0 { 300 return false 301 } 302 if GenerateCcLibraryStaticOnly(ctx) { 303 // Don't use partially-converted cc_library targets in mixed builds, 304 // since mixed builds would generally rely on both static and shared 305 // variants of a cc_library. 306 return false 307 } 308 return !mixedBuildsDisabled[ctx.Module().Name()] 309} 310 311// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build. 312func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool { 313 if bp2buildModuleDoNotConvert[ctx.Module().Name()] { 314 return false 315 } 316 317 // Ensure that the module type of this module has a bp2build converter. This 318 // prevents mixed builds from using auto-converted modules just by matching 319 // the package dir; it also has to have a bp2build mutator as well. 320 if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false { 321 return false 322 } 323 324 packagePath := ctx.ModuleDir() 325 config := ctx.Config().bp2buildPackageConfig 326 327 // This is a tristate value: true, false, or unset. 328 propValue := b.bazelProperties.Bazel_module.Bp2build_available 329 if bp2buildDefaultTrueRecursively(packagePath, config) { 330 // Allow modules to explicitly opt-out. 331 return proptools.BoolDefault(propValue, true) 332 } 333 334 // Allow modules to explicitly opt-in. 335 return proptools.BoolDefault(propValue, false) 336} 337 338// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the 339// set of package prefixes where all modules must be converted. That is, if the 340// package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will 341// return true. 342// 343// However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry 344// exactly, this module will return false early. 345// 346// This function will also return false if the package doesn't match anything in 347// the config. 348func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool { 349 ret := false 350 351 // Return exact matches in the config. 352 if config[packagePath] == Bp2BuildDefaultTrueRecursively { 353 return true 354 } 355 if config[packagePath] == Bp2BuildDefaultFalse { 356 return false 357 } 358 359 // If not, check for the config recursively. 360 packagePrefix := "" 361 // e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist. 362 for _, part := range strings.Split(packagePath, "/") { 363 packagePrefix += part 364 if config[packagePrefix] == Bp2BuildDefaultTrueRecursively { 365 // package contains this prefix and this prefix should convert all modules 366 return true 367 } 368 // Continue to the next part of the package dir. 369 packagePrefix += "/" 370 } 371 372 return ret 373} 374 375// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or 376// an error if there are errors reading the file. 377// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do 378// something more targeted based on the rule type and target. 379func (b *BazelModuleBase) GetBazelBuildFileContents(c Config, path, name string) (string, error) { 380 if !strings.Contains(b.HandcraftedLabel(), path) { 381 return "", fmt.Errorf("%q not found in bazel_module.label %q", path, b.HandcraftedLabel()) 382 } 383 name = filepath.Join(path, name) 384 f, err := c.fs.Open(name) 385 if err != nil { 386 return "", err 387 } 388 defer f.Close() 389 390 data, err := ioutil.ReadAll(f) 391 if err != nil { 392 return "", err 393 } 394 return string(data[:]), nil 395} 396 397// ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically 398// or manually 399func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool { 400 return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel() 401} 402