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 bp2build 16 17import ( 18 "android/soong/android" 19 "android/soong/cc" 20 "fmt" 21 "strings" 22 "testing" 23) 24 25func TestCcObjectBp2Build(t *testing.T) { 26 testCases := []struct { 27 description string 28 moduleTypeUnderTest string 29 moduleTypeUnderTestFactory android.ModuleFactory 30 moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) 31 blueprint string 32 expectedBazelTargets []string 33 filesystem map[string]string 34 }{ 35 { 36 description: "simple cc_object generates cc_object with include header dep", 37 moduleTypeUnderTest: "cc_object", 38 moduleTypeUnderTestFactory: cc.ObjectFactory, 39 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 40 filesystem: map[string]string{ 41 "a/b/foo.h": "", 42 "a/b/bar.h": "", 43 "a/b/exclude.c": "", 44 "a/b/c.c": "", 45 }, 46 blueprint: `cc_object { 47 name: "foo", 48 local_include_dirs: ["include"], 49 cflags: [ 50 "-Wno-gcc-compat", 51 "-Wall", 52 "-Werror", 53 ], 54 srcs: [ 55 "a/b/*.c" 56 ], 57 exclude_srcs: ["a/b/exclude.c"], 58} 59`, 60 expectedBazelTargets: []string{`cc_object( 61 name = "foo", 62 copts = [ 63 "-fno-addrsig", 64 "-Wno-gcc-compat", 65 "-Wall", 66 "-Werror", 67 "-Iinclude", 68 "-I.", 69 ], 70 srcs = ["a/b/c.c"], 71)`, 72 }, 73 }, 74 { 75 description: "simple cc_object with defaults", 76 moduleTypeUnderTest: "cc_object", 77 moduleTypeUnderTestFactory: cc.ObjectFactory, 78 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 79 blueprint: `cc_object { 80 name: "foo", 81 local_include_dirs: ["include"], 82 srcs: [ 83 "a/b/*.h", 84 "a/b/c.c" 85 ], 86 87 defaults: ["foo_defaults"], 88} 89 90cc_defaults { 91 name: "foo_defaults", 92 defaults: ["foo_bar_defaults"], 93} 94 95cc_defaults { 96 name: "foo_bar_defaults", 97 cflags: [ 98 "-Wno-gcc-compat", 99 "-Wall", 100 "-Werror", 101 ], 102} 103`, 104 expectedBazelTargets: []string{`cc_object( 105 name = "foo", 106 copts = [ 107 "-Wno-gcc-compat", 108 "-Wall", 109 "-Werror", 110 "-fno-addrsig", 111 "-Iinclude", 112 "-I.", 113 ], 114 srcs = ["a/b/c.c"], 115)`, 116 }, 117 }, 118 { 119 description: "cc_object with cc_object deps in objs props", 120 moduleTypeUnderTest: "cc_object", 121 moduleTypeUnderTestFactory: cc.ObjectFactory, 122 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 123 filesystem: map[string]string{ 124 "a/b/c.c": "", 125 "x/y/z.c": "", 126 }, 127 blueprint: `cc_object { 128 name: "foo", 129 srcs: ["a/b/c.c"], 130 objs: ["bar"], 131} 132 133cc_object { 134 name: "bar", 135 srcs: ["x/y/z.c"], 136} 137`, 138 expectedBazelTargets: []string{`cc_object( 139 name = "bar", 140 copts = [ 141 "-fno-addrsig", 142 "-I.", 143 ], 144 srcs = ["x/y/z.c"], 145)`, `cc_object( 146 name = "foo", 147 copts = [ 148 "-fno-addrsig", 149 "-I.", 150 ], 151 deps = [":bar"], 152 srcs = ["a/b/c.c"], 153)`, 154 }, 155 }, 156 { 157 description: "cc_object with include_build_dir: false", 158 moduleTypeUnderTest: "cc_object", 159 moduleTypeUnderTestFactory: cc.ObjectFactory, 160 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 161 filesystem: map[string]string{ 162 "a/b/c.c": "", 163 "x/y/z.c": "", 164 }, 165 blueprint: `cc_object { 166 name: "foo", 167 srcs: ["a/b/c.c"], 168 include_build_directory: false, 169} 170`, 171 expectedBazelTargets: []string{`cc_object( 172 name = "foo", 173 copts = ["-fno-addrsig"], 174 srcs = ["a/b/c.c"], 175)`, 176 }, 177 }, 178 { 179 description: "cc_object with product variable", 180 moduleTypeUnderTest: "cc_object", 181 moduleTypeUnderTestFactory: cc.ObjectFactory, 182 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 183 blueprint: `cc_object { 184 name: "foo", 185 include_build_directory: false, 186 product_variables: { 187 platform_sdk_version: { 188 asflags: ["-DPLATFORM_SDK_VERSION=%d"], 189 }, 190 }, 191} 192`, 193 expectedBazelTargets: []string{`cc_object( 194 name = "foo", 195 asflags = ["-DPLATFORM_SDK_VERSION={Platform_sdk_version}"], 196 copts = ["-fno-addrsig"], 197)`, 198 }, 199 }, 200 } 201 202 dir := "." 203 for _, testCase := range testCases { 204 filesystem := make(map[string][]byte) 205 toParse := []string{ 206 "Android.bp", 207 } 208 for f, content := range testCase.filesystem { 209 if strings.HasSuffix(f, "Android.bp") { 210 toParse = append(toParse, f) 211 } 212 filesystem[f] = []byte(content) 213 } 214 config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem) 215 ctx := android.NewTestContext(config) 216 // Always register cc_defaults module factory 217 ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() }) 218 219 ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) 220 ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) 221 ctx.RegisterBp2BuildConfig(bp2buildConfig) 222 ctx.RegisterForBazelConversion() 223 224 _, errs := ctx.ParseFileList(dir, toParse) 225 if Errored(t, testCase.description, errs) { 226 continue 227 } 228 _, errs = ctx.ResolveDependencies(config) 229 if Errored(t, testCase.description, errs) { 230 continue 231 } 232 233 codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) 234 bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) 235 if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { 236 fmt.Println(bazelTargets) 237 t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) 238 } else { 239 for i, target := range bazelTargets { 240 if w, g := testCase.expectedBazelTargets[i], target.content; w != g { 241 t.Errorf( 242 "%s: Expected generated Bazel target to be '%s', got '%s'", 243 testCase.description, 244 w, 245 g, 246 ) 247 } 248 } 249 } 250 } 251} 252 253func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { 254 testCases := []struct { 255 description string 256 moduleTypeUnderTest string 257 moduleTypeUnderTestFactory android.ModuleFactory 258 moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) 259 blueprint string 260 expectedBazelTargets []string 261 filesystem map[string]string 262 }{ 263 { 264 description: "cc_object setting cflags for one arch", 265 moduleTypeUnderTest: "cc_object", 266 moduleTypeUnderTestFactory: cc.ObjectFactory, 267 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 268 blueprint: `cc_object { 269 name: "foo", 270 srcs: ["a.cpp"], 271 arch: { 272 x86: { 273 cflags: ["-fPIC"], // string list 274 }, 275 arm: { 276 srcs: ["arch/arm/file.S"], // label list 277 }, 278 }, 279} 280`, 281 expectedBazelTargets: []string{ 282 `cc_object( 283 name = "foo", 284 copts = [ 285 "-fno-addrsig", 286 "-I.", 287 ] + select({ 288 "//build/bazel/platforms/arch:x86": ["-fPIC"], 289 "//conditions:default": [], 290 }), 291 srcs = ["a.cpp"] + select({ 292 "//build/bazel/platforms/arch:arm": ["arch/arm/file.S"], 293 "//conditions:default": [], 294 }), 295)`, 296 }, 297 }, 298 { 299 description: "cc_object setting cflags for 4 architectures", 300 moduleTypeUnderTest: "cc_object", 301 moduleTypeUnderTestFactory: cc.ObjectFactory, 302 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 303 blueprint: `cc_object { 304 name: "foo", 305 srcs: ["base.cpp"], 306 arch: { 307 x86: { 308 srcs: ["x86.cpp"], 309 cflags: ["-fPIC"], 310 }, 311 x86_64: { 312 srcs: ["x86_64.cpp"], 313 cflags: ["-fPIC"], 314 }, 315 arm: { 316 srcs: ["arm.cpp"], 317 cflags: ["-Wall"], 318 }, 319 arm64: { 320 srcs: ["arm64.cpp"], 321 cflags: ["-Wall"], 322 }, 323 }, 324} 325`, 326 expectedBazelTargets: []string{ 327 `cc_object( 328 name = "foo", 329 copts = [ 330 "-fno-addrsig", 331 "-I.", 332 ] + select({ 333 "//build/bazel/platforms/arch:arm": ["-Wall"], 334 "//build/bazel/platforms/arch:arm64": ["-Wall"], 335 "//build/bazel/platforms/arch:x86": ["-fPIC"], 336 "//build/bazel/platforms/arch:x86_64": ["-fPIC"], 337 "//conditions:default": [], 338 }), 339 srcs = ["base.cpp"] + select({ 340 "//build/bazel/platforms/arch:arm": ["arm.cpp"], 341 "//build/bazel/platforms/arch:arm64": ["arm64.cpp"], 342 "//build/bazel/platforms/arch:x86": ["x86.cpp"], 343 "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"], 344 "//conditions:default": [], 345 }), 346)`, 347 }, 348 }, 349 { 350 description: "cc_object setting cflags for multiple OSes", 351 moduleTypeUnderTest: "cc_object", 352 moduleTypeUnderTestFactory: cc.ObjectFactory, 353 moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, 354 blueprint: `cc_object { 355 name: "foo", 356 srcs: ["base.cpp"], 357 target: { 358 android: { 359 cflags: ["-fPIC"], 360 }, 361 windows: { 362 cflags: ["-fPIC"], 363 }, 364 darwin: { 365 cflags: ["-Wall"], 366 }, 367 }, 368} 369`, 370 expectedBazelTargets: []string{ 371 `cc_object( 372 name = "foo", 373 copts = [ 374 "-fno-addrsig", 375 "-I.", 376 ] + select({ 377 "//build/bazel/platforms/os:android": ["-fPIC"], 378 "//build/bazel/platforms/os:darwin": ["-Wall"], 379 "//build/bazel/platforms/os:windows": ["-fPIC"], 380 "//conditions:default": [], 381 }), 382 srcs = ["base.cpp"], 383)`, 384 }, 385 }, 386 } 387 388 dir := "." 389 for _, testCase := range testCases { 390 filesystem := make(map[string][]byte) 391 toParse := []string{ 392 "Android.bp", 393 } 394 config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem) 395 ctx := android.NewTestContext(config) 396 // Always register cc_defaults module factory 397 ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() }) 398 399 ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) 400 ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) 401 ctx.RegisterBp2BuildConfig(bp2buildConfig) 402 ctx.RegisterForBazelConversion() 403 404 _, errs := ctx.ParseFileList(dir, toParse) 405 if Errored(t, testCase.description, errs) { 406 continue 407 } 408 _, errs = ctx.ResolveDependencies(config) 409 if Errored(t, testCase.description, errs) { 410 continue 411 } 412 413 codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) 414 bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) 415 if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { 416 fmt.Println(bazelTargets) 417 t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) 418 } else { 419 for i, target := range bazelTargets { 420 if w, g := testCase.expectedBazelTargets[i], target.content; w != g { 421 t.Errorf( 422 "%s: Expected generated Bazel target to be '%s', got '%s'", 423 testCase.description, 424 w, 425 g, 426 ) 427 } 428 } 429 } 430 } 431} 432