1package crosstooltostarlarklib 2 3import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "log" 9 crosstoolpb "third_party/com/github/bazelbuild/bazel/src/main/protobuf/crosstool_config_go_proto" 10 "github.com/golang/protobuf/proto" 11) 12 13func makeCToolchainString(lines []string) string { 14 return fmt.Sprintf(`toolchain { 15 %s 16}`, strings.Join(lines, "\n ")) 17} 18 19func makeCrosstool(CToolchains []string) *crosstoolpb.CrosstoolRelease { 20 crosstool := &crosstoolpb.CrosstoolRelease{} 21 requiredFields := []string{ 22 "major_version: '0'", 23 "minor_version: '0'", 24 "default_target_cpu: 'cpu'", 25 } 26 CToolchains = append(CToolchains, requiredFields...) 27 if err := proto.UnmarshalText(strings.Join(CToolchains, "\n"), crosstool); err != nil { 28 log.Fatalf("Failed to parse CROSSTOOL:", err) 29 } 30 return crosstool 31} 32 33func getSimpleCToolchain(id string) string { 34 lines := []string{ 35 "toolchain_identifier: 'id-" + id + "'", 36 "host_system_name: 'host-" + id + "'", 37 "target_system_name: 'target-" + id + "'", 38 "target_cpu: 'cpu-" + id + "'", 39 "compiler: 'compiler-" + id + "'", 40 "target_libc: 'libc-" + id + "'", 41 "abi_version: 'version-" + id + "'", 42 "abi_libc_version: 'libc_version-" + id + "'", 43 } 44 return makeCToolchainString(lines) 45} 46 47func getCToolchain(id, cpu, compiler string, extraLines []string) string { 48 lines := []string{ 49 "toolchain_identifier: '" + id + "'", 50 "host_system_name: 'host'", 51 "target_system_name: 'target'", 52 "target_cpu: '" + cpu + "'", 53 "compiler: '" + compiler + "'", 54 "target_libc: 'libc'", 55 "abi_version: 'version'", 56 "abi_libc_version: 'libc_version'", 57 } 58 lines = append(lines, extraLines...) 59 return makeCToolchainString(lines) 60} 61 62func TestStringFieldsConditionStatement(t *testing.T) { 63 toolchain1 := getSimpleCToolchain("1") 64 toolchain2 := getSimpleCToolchain("2") 65 toolchains := []string{toolchain1, toolchain2} 66 crosstool := makeCrosstool(toolchains) 67 68 testCases := []struct { 69 field string 70 expectedText string 71 }{ 72 {field: "toolchain_identifier", 73 expectedText: ` 74 if (ctx.attr.cpu == "cpu-1"): 75 toolchain_identifier = "id-1" 76 elif (ctx.attr.cpu == "cpu-2"): 77 toolchain_identifier = "id-2" 78 else: 79 fail("Unreachable")`}, 80 {field: "host_system_name", 81 expectedText: ` 82 if (ctx.attr.cpu == "cpu-1"): 83 host_system_name = "host-1" 84 elif (ctx.attr.cpu == "cpu-2"): 85 host_system_name = "host-2" 86 else: 87 fail("Unreachable")`}, 88 {field: "target_system_name", 89 expectedText: ` 90 if (ctx.attr.cpu == "cpu-1"): 91 target_system_name = "target-1" 92 elif (ctx.attr.cpu == "cpu-2"): 93 target_system_name = "target-2" 94 else: 95 fail("Unreachable")`}, 96 {field: "target_cpu", 97 expectedText: ` 98 if (ctx.attr.cpu == "cpu-1"): 99 target_cpu = "cpu-1" 100 elif (ctx.attr.cpu == "cpu-2"): 101 target_cpu = "cpu-2" 102 else: 103 fail("Unreachable")`}, 104 {field: "target_libc", 105 expectedText: ` 106 if (ctx.attr.cpu == "cpu-1"): 107 target_libc = "libc-1" 108 elif (ctx.attr.cpu == "cpu-2"): 109 target_libc = "libc-2" 110 else: 111 fail("Unreachable")`}, 112 {field: "compiler", 113 expectedText: ` 114 if (ctx.attr.cpu == "cpu-1"): 115 compiler = "compiler-1" 116 elif (ctx.attr.cpu == "cpu-2"): 117 compiler = "compiler-2" 118 else: 119 fail("Unreachable")`}, 120 {field: "abi_version", 121 expectedText: ` 122 if (ctx.attr.cpu == "cpu-1"): 123 abi_version = "version-1" 124 elif (ctx.attr.cpu == "cpu-2"): 125 abi_version = "version-2" 126 else: 127 fail("Unreachable")`}, 128 {field: "abi_libc_version", 129 expectedText: ` 130 if (ctx.attr.cpu == "cpu-1"): 131 abi_libc_version = "libc_version-1" 132 elif (ctx.attr.cpu == "cpu-2"): 133 abi_libc_version = "libc_version-2" 134 else: 135 fail("Unreachable")`}} 136 137 got, err := Transform(crosstool) 138 if err != nil { 139 t.Fatalf("CROSSTOOL conversion failed: %v", err) 140 } 141 142 failed := false 143 for _, tc := range testCases { 144 if !strings.Contains(got, tc.expectedText) { 145 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 146 tc.field, tc.expectedText) 147 failed = true 148 } 149 } 150 if failed { 151 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 152 strings.Join(toolchains, "\n"), got) 153 } 154} 155 156func TestConditionsSameCpu(t *testing.T) { 157 toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) 158 toolchainAB := getCToolchain("2", "cpuA", "compilerB", []string{}) 159 toolchains := []string{toolchainAA, toolchainAB} 160 crosstool := makeCrosstool(toolchains) 161 162 testCases := []struct { 163 field string 164 expectedText string 165 }{ 166 {field: "toolchain_identifier", 167 expectedText: ` 168 if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerA"): 169 toolchain_identifier = "1" 170 elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerB"): 171 toolchain_identifier = "2" 172 else: 173 fail("Unreachable")`}, 174 {field: "host_system_name", 175 expectedText: ` 176 host_system_name = "host"`}, 177 {field: "target_system_name", 178 expectedText: ` 179 target_system_name = "target"`}, 180 {field: "target_cpu", 181 expectedText: ` 182 target_cpu = "cpuA"`}, 183 {field: "target_libc", 184 expectedText: ` 185 target_libc = "libc"`}, 186 {field: "compiler", 187 expectedText: ` 188 if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerA"): 189 compiler = "compilerA" 190 elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerB"): 191 compiler = "compilerB" 192 else: 193 fail("Unreachable")`}, 194 {field: "abi_version", 195 expectedText: ` 196 abi_version = "version"`}, 197 {field: "abi_libc_version", 198 expectedText: ` 199 abi_libc_version = "libc_version"`}} 200 201 got, err := Transform(crosstool) 202 if err != nil { 203 t.Fatalf("CROSSTOOL conversion failed: %v", err) 204 } 205 206 failed := false 207 for _, tc := range testCases { 208 if !strings.Contains(got, tc.expectedText) { 209 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 210 tc.field, tc.expectedText) 211 failed = true 212 } 213 } 214 if failed { 215 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 216 strings.Join(toolchains, "\n"), got) 217 } 218} 219 220func TestConditionsSameCompiler(t *testing.T) { 221 toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) 222 toolchainBA := getCToolchain("2", "cpuB", "compilerA", []string{}) 223 toolchains := []string{toolchainAA, toolchainBA} 224 crosstool := makeCrosstool(toolchains) 225 226 testCases := []struct { 227 field string 228 expectedText string 229 }{ 230 {field: "toolchain_identifier", 231 expectedText: ` 232 if (ctx.attr.cpu == "cpuA"): 233 toolchain_identifier = "1" 234 elif (ctx.attr.cpu == "cpuB"): 235 toolchain_identifier = "2" 236 else: 237 fail("Unreachable")`}, 238 {field: "target_cpu", 239 expectedText: ` 240 if (ctx.attr.cpu == "cpuA"): 241 target_cpu = "cpuA" 242 elif (ctx.attr.cpu == "cpuB"): 243 target_cpu = "cpuB" 244 else: 245 fail("Unreachable")`}, 246 {field: "compiler", 247 expectedText: ` 248 compiler = "compilerA"`}} 249 250 got, err := Transform(crosstool) 251 if err != nil { 252 t.Fatalf("CROSSTOOL conversion failed: %v", err) 253 } 254 255 failed := false 256 for _, tc := range testCases { 257 if !strings.Contains(got, tc.expectedText) { 258 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 259 tc.field, tc.expectedText) 260 failed = true 261 } 262 } 263 if failed { 264 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 265 strings.Join(toolchains, "\n"), got) 266 } 267} 268 269func TestNonMandatoryStrings(t *testing.T) { 270 toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{"cc_target_os: 'osA'"}) 271 toolchainBB := getCToolchain("2", "cpuB", "compilerB", []string{}) 272 toolchains := []string{toolchainAA, toolchainBB} 273 crosstool := makeCrosstool(toolchains) 274 275 testCases := []struct { 276 field string 277 expectedText string 278 }{ 279 {field: "cc_target_os", 280 expectedText: ` 281 if (ctx.attr.cpu == "cpuB"): 282 cc_target_os = None 283 elif (ctx.attr.cpu == "cpuA"): 284 cc_target_os = "osA" 285 else: 286 fail("Unreachable")`}, 287 {field: "builtin_sysroot", 288 expectedText: ` 289 builtin_sysroot = None`}} 290 291 got, err := Transform(crosstool) 292 if err != nil { 293 t.Fatalf("CROSSTOOL conversion failed: %v", err) 294 } 295 296 failed := false 297 for _, tc := range testCases { 298 if !strings.Contains(got, tc.expectedText) { 299 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 300 tc.field, tc.expectedText) 301 failed = true 302 } 303 } 304 if failed { 305 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 306 strings.Join(toolchains, "\n"), got) 307 } 308} 309 310func TestBuiltinIncludeDirectories(t *testing.T) { 311 toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) 312 toolchainBA := getCToolchain("2", "cpuB", "compilerA", []string{}) 313 toolchainCA := getCToolchain("3", "cpuC", "compilerA", 314 []string{"cxx_builtin_include_directory: 'dirC'"}) 315 toolchainCB := getCToolchain("4", "cpuC", "compilerB", 316 []string{"cxx_builtin_include_directory: 'dirC'", 317 "cxx_builtin_include_directory: 'dirB'"}) 318 toolchainDA := getCToolchain("5", "cpuD", "compilerA", 319 []string{"cxx_builtin_include_directory: 'dirC'"}) 320 321 toolchainsEmpty := []string{toolchainAA, toolchainBA} 322 323 toolchainsOneNonempty := []string{toolchainAA, toolchainBA, toolchainCA} 324 325 toolchainsSameNonempty := []string{toolchainCA, toolchainDA} 326 327 allToolchains := []string{toolchainAA, toolchainBA, toolchainCA, toolchainCB, toolchainDA} 328 329 testCases := []struct { 330 field string 331 toolchains []string 332 expectedText string 333 }{ 334 {field: "cxx_builtin_include_directories", 335 toolchains: toolchainsEmpty, 336 expectedText: ` 337 cxx_builtin_include_directories = []`}, 338 {field: "cxx_builtin_include_directories", 339 toolchains: toolchainsOneNonempty, 340 expectedText: ` 341 if (ctx.attr.cpu == "cpuA" 342 or ctx.attr.cpu == "cpuB"): 343 cxx_builtin_include_directories = [] 344 elif (ctx.attr.cpu == "cpuC"): 345 cxx_builtin_include_directories = ["dirC"] 346 else: 347 fail("Unreachable")`}, 348 {field: "cxx_builtin_include_directories", 349 toolchains: toolchainsSameNonempty, 350 expectedText: ` 351 cxx_builtin_include_directories = ["dirC"]`}, 352 {field: "cxx_builtin_include_directories", 353 toolchains: allToolchains, 354 expectedText: ` 355 if (ctx.attr.cpu == "cpuA" 356 or ctx.attr.cpu == "cpuB"): 357 cxx_builtin_include_directories = [] 358 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" 359 or ctx.attr.cpu == "cpuD"): 360 cxx_builtin_include_directories = ["dirC"] 361 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): 362 cxx_builtin_include_directories = ["dirC", "dirB"]`}} 363 364 for _, tc := range testCases { 365 crosstool := makeCrosstool(tc.toolchains) 366 got, err := Transform(crosstool) 367 if err != nil { 368 t.Fatalf("CROSSTOOL conversion failed: %v", err) 369 } 370 if !strings.Contains(got, tc.expectedText) { 371 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 372 tc.field, tc.expectedText) 373 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 374 strings.Join(tc.toolchains, "\n"), got) 375 } 376 } 377} 378 379func TestMakeVariables(t *testing.T) { 380 toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) 381 toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) 382 toolchainA1 := getCToolchain("3", "cpuC", "compilerA", 383 []string{"make_variable {name: 'A', value: 'a/b/c'}"}) 384 toolchainA2 := getCToolchain("4", "cpuC", "compilerB", 385 []string{"make_variable {name: 'A', value: 'a/b/c'}"}) 386 toolchainAB := getCToolchain("5", "cpuC", "compilerC", 387 []string{"make_variable {name: 'A', value: 'a/b/c'}", 388 "make_variable {name: 'B', value: 'a/b/c'}"}) 389 toolchainBA := getCToolchain("6", "cpuD", "compilerA", 390 []string{"make_variable {name: 'B', value: 'a/b/c'}", 391 "make_variable {name: 'A', value: 'a b c'}"}) 392 393 toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} 394 395 toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} 396 397 toolchainsSameNonempty := []string{toolchainA1, toolchainA2} 398 399 toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} 400 401 allToolchains := []string{ 402 toolchainEmpty1, 403 toolchainEmpty2, 404 toolchainA1, 405 toolchainA2, 406 toolchainAB, 407 toolchainBA, 408 } 409 410 testCases := []struct { 411 field string 412 toolchains []string 413 expectedText string 414 }{ 415 {field: "make_variables", 416 toolchains: toolchainsEmpty, 417 expectedText: ` 418 make_variables = []`}, 419 {field: "make_variables", 420 toolchains: toolchainsOneNonempty, 421 expectedText: ` 422 if (ctx.attr.cpu == "cpuA"): 423 make_variables = [] 424 elif (ctx.attr.cpu == "cpuC"): 425 make_variables = [make_variable(name = "A", value = "a/b/c")] 426 else: 427 fail("Unreachable")`}, 428 {field: "make_variables", 429 toolchains: toolchainsSameNonempty, 430 expectedText: ` 431 make_variables = [make_variable(name = "A", value = "a/b/c")]`}, 432 {field: "make_variables", 433 toolchains: toolchainsDifferentOrder, 434 expectedText: ` 435 if (ctx.attr.cpu == "cpuC"): 436 make_variables = [ 437 make_variable(name = "A", value = "a/b/c"), 438 make_variable(name = "B", value = "a/b/c"), 439 ] 440 elif (ctx.attr.cpu == "cpuD"): 441 make_variables = [ 442 make_variable(name = "B", value = "a/b/c"), 443 make_variable(name = "A", value = "a b c"), 444 ] 445 else: 446 fail("Unreachable")`}, 447 {field: "make_variables", 448 toolchains: allToolchains, 449 expectedText: ` 450 if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): 451 make_variables = [ 452 make_variable(name = "A", value = "a/b/c"), 453 make_variable(name = "B", value = "a/b/c"), 454 ] 455 elif (ctx.attr.cpu == "cpuD"): 456 make_variables = [ 457 make_variable(name = "B", value = "a/b/c"), 458 make_variable(name = "A", value = "a b c"), 459 ] 460 elif (ctx.attr.cpu == "cpuA" 461 or ctx.attr.cpu == "cpuB"): 462 make_variables = [] 463 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" 464 or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): 465 make_variables = [make_variable(name = "A", value = "a/b/c")] 466 else: 467 fail("Unreachable")`}} 468 469 for _, tc := range testCases { 470 crosstool := makeCrosstool(tc.toolchains) 471 got, err := Transform(crosstool) 472 if err != nil { 473 t.Fatalf("CROSSTOOL conversion failed: %v", err) 474 } 475 if !strings.Contains(got, tc.expectedText) { 476 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 477 tc.field, tc.expectedText) 478 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 479 strings.Join(tc.toolchains, "\n"), got) 480 } 481 } 482} 483 484func TestToolPaths(t *testing.T) { 485 toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) 486 toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) 487 toolchainA1 := getCToolchain("3", "cpuC", "compilerA", 488 []string{"tool_path {name: 'A', path: 'a/b/c'}"}) 489 toolchainA2 := getCToolchain("4", "cpuC", "compilerB", 490 []string{"tool_path {name: 'A', path: 'a/b/c'}"}) 491 toolchainAB := getCToolchain("5", "cpuC", "compilerC", 492 []string{"tool_path {name: 'A', path: 'a/b/c'}", 493 "tool_path {name: 'B', path: 'a/b/c'}"}) 494 toolchainBA := getCToolchain("6", "cpuD", "compilerA", 495 []string{"tool_path {name: 'B', path: 'a/b/c'}", 496 "tool_path {name: 'A', path: 'a/b/c'}"}) 497 498 toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} 499 500 toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} 501 502 toolchainsSameNonempty := []string{toolchainA1, toolchainA2} 503 504 toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} 505 506 allToolchains := []string{ 507 toolchainEmpty1, 508 toolchainEmpty2, 509 toolchainA1, 510 toolchainA2, 511 toolchainAB, 512 toolchainBA, 513 } 514 515 testCases := []struct { 516 field string 517 toolchains []string 518 expectedText string 519 }{ 520 {field: "tool_paths", 521 toolchains: toolchainsEmpty, 522 expectedText: ` 523 tool_paths = []`}, 524 {field: "tool_paths", 525 toolchains: toolchainsOneNonempty, 526 expectedText: ` 527 if (ctx.attr.cpu == "cpuA"): 528 tool_paths = [] 529 elif (ctx.attr.cpu == "cpuC"): 530 tool_paths = [tool_path(name = "A", path = "a/b/c")] 531 else: 532 fail("Unreachable")`}, 533 {field: "tool_paths", 534 toolchains: toolchainsSameNonempty, 535 expectedText: ` 536 tool_paths = [tool_path(name = "A", path = "a/b/c")]`}, 537 {field: "tool_paths", 538 toolchains: toolchainsDifferentOrder, 539 expectedText: ` 540 if (ctx.attr.cpu == "cpuC"): 541 tool_paths = [ 542 tool_path(name = "A", path = "a/b/c"), 543 tool_path(name = "B", path = "a/b/c"), 544 ] 545 elif (ctx.attr.cpu == "cpuD"): 546 tool_paths = [ 547 tool_path(name = "B", path = "a/b/c"), 548 tool_path(name = "A", path = "a/b/c"), 549 ] 550 else: 551 fail("Unreachable")`}, 552 {field: "tool_paths", 553 toolchains: allToolchains, 554 expectedText: ` 555 if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): 556 tool_paths = [ 557 tool_path(name = "A", path = "a/b/c"), 558 tool_path(name = "B", path = "a/b/c"), 559 ] 560 elif (ctx.attr.cpu == "cpuD"): 561 tool_paths = [ 562 tool_path(name = "B", path = "a/b/c"), 563 tool_path(name = "A", path = "a/b/c"), 564 ] 565 elif (ctx.attr.cpu == "cpuA" 566 or ctx.attr.cpu == "cpuB"): 567 tool_paths = [] 568 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" 569 or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): 570 tool_paths = [tool_path(name = "A", path = "a/b/c")] 571 else: 572 fail("Unreachable")`}} 573 574 for _, tc := range testCases { 575 crosstool := makeCrosstool(tc.toolchains) 576 got, err := Transform(crosstool) 577 if err != nil { 578 t.Fatalf("CROSSTOOL conversion failed: %v", err) 579 } 580 if !strings.Contains(got, tc.expectedText) { 581 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 582 tc.field, tc.expectedText) 583 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 584 strings.Join(tc.toolchains, "\n"), got) 585 } 586 } 587} 588 589func getArtifactNamePattern(lines []string) string { 590 return fmt.Sprintf(`artifact_name_pattern { 591 %s 592}`, strings.Join(lines, "\n ")) 593} 594 595func TestArtifactNamePatterns(t *testing.T) { 596 toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) 597 toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) 598 toolchainA1 := getCToolchain("3", "cpuC", "compilerA", 599 []string{ 600 getArtifactNamePattern([]string{ 601 "category_name: 'A'", 602 "prefix: 'p'", 603 "extension: '.exe'"}), 604 }, 605 ) 606 toolchainA2 := getCToolchain("4", "cpuC", "compilerB", 607 []string{ 608 getArtifactNamePattern([]string{ 609 "category_name: 'A'", 610 "prefix: 'p'", 611 "extension: '.exe'"}), 612 }, 613 ) 614 toolchainAB := getCToolchain("5", "cpuC", "compilerC", 615 []string{ 616 getArtifactNamePattern([]string{ 617 "category_name: 'A'", 618 "prefix: 'p'", 619 "extension: '.exe'"}), 620 getArtifactNamePattern([]string{ 621 "category_name: 'B'", 622 "prefix: 'p'", 623 "extension: '.exe'"}), 624 }, 625 ) 626 toolchainBA := getCToolchain("6", "cpuD", "compilerA", 627 []string{ 628 getArtifactNamePattern([]string{ 629 "category_name: 'B'", 630 "prefix: 'p'", 631 "extension: '.exe'"}), 632 getArtifactNamePattern([]string{ 633 "category_name: 'A'", 634 "prefix: 'p'", 635 "extension: '.exe'"}), 636 }, 637 ) 638 toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} 639 640 toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} 641 642 toolchainsSameNonempty := []string{toolchainA1, toolchainA2} 643 644 toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} 645 646 allToolchains := []string{ 647 toolchainEmpty1, 648 toolchainEmpty2, 649 toolchainA1, 650 toolchainA2, 651 toolchainAB, 652 toolchainBA, 653 } 654 655 testCases := []struct { 656 field string 657 toolchains []string 658 expectedText string 659 }{ 660 {field: "artifact_name_patterns", 661 toolchains: toolchainsEmpty, 662 expectedText: ` 663 artifact_name_patterns = []`}, 664 {field: "artifact_name_patterns", 665 toolchains: toolchainsOneNonempty, 666 expectedText: ` 667 if (ctx.attr.cpu == "cpuC"): 668 artifact_name_patterns = [ 669 artifact_name_pattern( 670 category_name = "A", 671 prefix = "p", 672 extension = ".exe", 673 ), 674 ] 675 elif (ctx.attr.cpu == "cpuA"): 676 artifact_name_patterns = [] 677 else: 678 fail("Unreachable")`}, 679 {field: "artifact_name_patterns", 680 toolchains: toolchainsSameNonempty, 681 expectedText: ` 682 artifact_name_patterns = [ 683 artifact_name_pattern( 684 category_name = "A", 685 prefix = "p", 686 extension = ".exe", 687 ), 688 ]`}, 689 {field: "artifact_name_patterns", 690 toolchains: toolchainsDifferentOrder, 691 expectedText: ` 692 if (ctx.attr.cpu == "cpuC"): 693 artifact_name_patterns = [ 694 artifact_name_pattern( 695 category_name = "A", 696 prefix = "p", 697 extension = ".exe", 698 ), 699 artifact_name_pattern( 700 category_name = "B", 701 prefix = "p", 702 extension = ".exe", 703 ), 704 ] 705 elif (ctx.attr.cpu == "cpuD"): 706 artifact_name_patterns = [ 707 artifact_name_pattern( 708 category_name = "B", 709 prefix = "p", 710 extension = ".exe", 711 ), 712 artifact_name_pattern( 713 category_name = "A", 714 prefix = "p", 715 extension = ".exe", 716 ), 717 ] 718 else: 719 fail("Unreachable")`}, 720 {field: "artifact_name_patterns", 721 toolchains: allToolchains, 722 expectedText: ` 723 if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): 724 artifact_name_patterns = [ 725 artifact_name_pattern( 726 category_name = "A", 727 prefix = "p", 728 extension = ".exe", 729 ), 730 artifact_name_pattern( 731 category_name = "B", 732 prefix = "p", 733 extension = ".exe", 734 ), 735 ] 736 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" 737 or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): 738 artifact_name_patterns = [ 739 artifact_name_pattern( 740 category_name = "A", 741 prefix = "p", 742 extension = ".exe", 743 ), 744 ] 745 elif (ctx.attr.cpu == "cpuD"): 746 artifact_name_patterns = [ 747 artifact_name_pattern( 748 category_name = "B", 749 prefix = "p", 750 extension = ".exe", 751 ), 752 artifact_name_pattern( 753 category_name = "A", 754 prefix = "p", 755 extension = ".exe", 756 ), 757 ] 758 elif (ctx.attr.cpu == "cpuA" 759 or ctx.attr.cpu == "cpuB"): 760 artifact_name_patterns = [] 761 else: 762 fail("Unreachable")`}} 763 764 for _, tc := range testCases { 765 crosstool := makeCrosstool(tc.toolchains) 766 got, err := Transform(crosstool) 767 if err != nil { 768 t.Fatalf("CROSSTOOL conversion failed: %v", err) 769 } 770 if !strings.Contains(got, tc.expectedText) { 771 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 772 tc.field, tc.expectedText) 773 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 774 strings.Join(tc.toolchains, "\n"), got) 775 } 776 } 777} 778 779func getFeature(lines []string) string { 780 return fmt.Sprintf(`feature { 781 %s 782}`, strings.Join(lines, "\n ")) 783} 784 785func TestFeatureListAssignment(t *testing.T) { 786 toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) 787 toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) 788 toolchainA1 := getCToolchain("3", "cpuC", "compilerA", 789 []string{getFeature([]string{"name: 'A'"})}, 790 ) 791 toolchainA2 := getCToolchain("4", "cpuC", "compilerB", 792 []string{getFeature([]string{"name: 'A'"})}, 793 ) 794 toolchainAB := getCToolchain("5", "cpuC", "compilerC", 795 []string{ 796 getFeature([]string{"name: 'A'"}), 797 getFeature([]string{"name: 'B'"}), 798 }, 799 ) 800 toolchainBA := getCToolchain("6", "cpuD", "compilerA", 801 []string{ 802 getFeature([]string{"name: 'B'"}), 803 getFeature([]string{"name: 'A'"}), 804 }, 805 ) 806 toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} 807 808 toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} 809 810 toolchainsSameNonempty := []string{toolchainA1, toolchainA2} 811 812 toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} 813 814 allToolchains := []string{ 815 toolchainEmpty1, 816 toolchainEmpty2, 817 toolchainA1, 818 toolchainA2, 819 toolchainAB, 820 toolchainBA, 821 } 822 823 testCases := []struct { 824 field string 825 toolchains []string 826 expectedText string 827 }{ 828 {field: "features", 829 toolchains: toolchainsEmpty, 830 expectedText: ` 831 features = []`}, 832 {field: "features", 833 toolchains: toolchainsOneNonempty, 834 expectedText: ` 835 if (ctx.attr.cpu == "cpuA"): 836 features = [] 837 elif (ctx.attr.cpu == "cpuC"): 838 features = [a_feature] 839 else: 840 fail("Unreachable")`}, 841 {field: "features", 842 toolchains: toolchainsSameNonempty, 843 expectedText: ` 844 features = [a_feature]`}, 845 {field: "features", 846 toolchains: toolchainsDifferentOrder, 847 expectedText: ` 848 if (ctx.attr.cpu == "cpuC"): 849 features = [a_feature, b_feature] 850 elif (ctx.attr.cpu == "cpuD"): 851 features = [b_feature, a_feature] 852 else: 853 fail("Unreachable")`}, 854 {field: "features", 855 toolchains: allToolchains, 856 expectedText: ` 857 if (ctx.attr.cpu == "cpuA" 858 or ctx.attr.cpu == "cpuB"): 859 features = [] 860 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" 861 or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): 862 features = [a_feature] 863 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): 864 features = [a_feature, b_feature] 865 elif (ctx.attr.cpu == "cpuD"): 866 features = [b_feature, a_feature] 867 else: 868 fail("Unreachable")`}} 869 870 for _, tc := range testCases { 871 crosstool := makeCrosstool(tc.toolchains) 872 got, err := Transform(crosstool) 873 if err != nil { 874 t.Fatalf("CROSSTOOL conversion failed: %v", err) 875 } 876 if !strings.Contains(got, tc.expectedText) { 877 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 878 tc.field, tc.expectedText) 879 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 880 strings.Join(tc.toolchains, "\n"), got) 881 } 882 } 883} 884 885func getActionConfig(lines []string) string { 886 return fmt.Sprintf(`action_config { 887 %s 888}`, strings.Join(lines, "\n ")) 889} 890 891func TestActionConfigListAssignment(t *testing.T) { 892 toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) 893 toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) 894 toolchainA1 := getCToolchain("3", "cpuC", "compilerA", 895 []string{ 896 getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), 897 }, 898 ) 899 toolchainA2 := getCToolchain("4", "cpuC", "compilerB", 900 []string{ 901 getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), 902 }, 903 ) 904 toolchainAB := getCToolchain("5", "cpuC", "compilerC", 905 []string{ 906 getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), 907 getActionConfig([]string{"action_name: 'B'", "config_name: 'B'"}), 908 }, 909 ) 910 toolchainBA := getCToolchain("6", "cpuD", "compilerA", 911 []string{ 912 getActionConfig([]string{"action_name: 'B'", "config_name: 'B'"}), 913 getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), 914 }, 915 ) 916 toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} 917 918 toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} 919 920 toolchainsSameNonempty := []string{toolchainA1, toolchainA2} 921 922 toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} 923 924 allToolchains := []string{ 925 toolchainEmpty1, 926 toolchainEmpty2, 927 toolchainA1, 928 toolchainA2, 929 toolchainAB, 930 toolchainBA, 931 } 932 933 testCases := []struct { 934 field string 935 toolchains []string 936 expectedText string 937 }{ 938 {field: "action_configs", 939 toolchains: toolchainsEmpty, 940 expectedText: ` 941 action_configs = []`}, 942 {field: "action_configs", 943 toolchains: toolchainsOneNonempty, 944 expectedText: ` 945 if (ctx.attr.cpu == "cpuA"): 946 action_configs = [] 947 elif (ctx.attr.cpu == "cpuC"): 948 action_configs = [a_action] 949 else: 950 fail("Unreachable")`}, 951 {field: "action_configs", 952 toolchains: toolchainsSameNonempty, 953 expectedText: ` 954 action_configs = [a_action]`}, 955 {field: "action_configs", 956 toolchains: toolchainsDifferentOrder, 957 expectedText: ` 958 if (ctx.attr.cpu == "cpuC"): 959 action_configs = [a_action, b_action] 960 elif (ctx.attr.cpu == "cpuD"): 961 action_configs = [b_action, a_action] 962 else: 963 fail("Unreachable")`}, 964 {field: "action_configs", 965 toolchains: allToolchains, 966 expectedText: ` 967 if (ctx.attr.cpu == "cpuA" 968 or ctx.attr.cpu == "cpuB"): 969 action_configs = [] 970 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" 971 or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): 972 action_configs = [a_action] 973 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): 974 action_configs = [a_action, b_action] 975 elif (ctx.attr.cpu == "cpuD"): 976 action_configs = [b_action, a_action] 977 else: 978 fail("Unreachable")`}} 979 980 for _, tc := range testCases { 981 crosstool := makeCrosstool(tc.toolchains) 982 got, err := Transform(crosstool) 983 if err != nil { 984 t.Fatalf("CROSSTOOL conversion failed: %v", err) 985 } 986 if !strings.Contains(got, tc.expectedText) { 987 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 988 tc.field, tc.expectedText) 989 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 990 strings.Join(tc.toolchains, "\n"), got) 991 } 992 } 993} 994 995func TestAllAndNoneAvailableErrorsWhenMoreThanOneElement(t *testing.T) { 996 toolchainFeatureAllAvailable := getCToolchain("1", "cpu", "compiler", 997 []string{getFeature([]string{ 998 "name: 'A'", 999 "flag_set {", 1000 " action: 'A'", 1001 " flag_group {", 1002 " flag: 'f'", 1003 " expand_if_all_available: 'e1'", 1004 " expand_if_all_available: 'e2'", 1005 " }", 1006 "}", 1007 })}, 1008 ) 1009 toolchainFeatureNoneAvailable := getCToolchain("1", "cpu", "compiler", 1010 []string{getFeature([]string{ 1011 "name: 'A'", 1012 "flag_set {", 1013 " action: 'A'", 1014 " flag_group {", 1015 " flag: 'f'", 1016 " expand_if_none_available: 'e1'", 1017 " expand_if_none_available: 'e2'", 1018 " }", 1019 "}", 1020 })}, 1021 ) 1022 toolchainActionConfigAllAvailable := getCToolchain("1", "cpu", "compiler", 1023 []string{getActionConfig([]string{ 1024 "config_name: 'A'", 1025 "action_name: 'A'", 1026 "flag_set {", 1027 " action: 'A'", 1028 " flag_group {", 1029 " flag: 'f'", 1030 " expand_if_all_available: 'e1'", 1031 " expand_if_all_available: 'e2'", 1032 " }", 1033 "}", 1034 })}, 1035 ) 1036 toolchainActionConfigNoneAvailable := getCToolchain("1", "cpu", "compiler", 1037 []string{getActionConfig([]string{ 1038 "config_name: 'A'", 1039 "action_name: 'A'", 1040 "flag_set {", 1041 " action: 'A'", 1042 " flag_group {", 1043 " flag: 'f'", 1044 " expand_if_none_available: 'e1'", 1045 " expand_if_none_available: 'e2'", 1046 " }", 1047 "}", 1048 })}, 1049 ) 1050 1051 testCases := []struct { 1052 field string 1053 toolchain string 1054 expectedText string 1055 }{ 1056 {field: "features", 1057 toolchain: toolchainFeatureAllAvailable, 1058 expectedText: "Error in feature 'A': Flag group must not have more " + 1059 "than one 'expand_if_all_available' field"}, 1060 {field: "features", 1061 toolchain: toolchainFeatureNoneAvailable, 1062 expectedText: "Error in feature 'A': Flag group must not have more " + 1063 "than one 'expand_if_none_available' field"}, 1064 {field: "action_configs", 1065 toolchain: toolchainActionConfigAllAvailable, 1066 expectedText: "Error in action_config 'A': Flag group must not have more " + 1067 "than one 'expand_if_all_available' field"}, 1068 {field: "action_configs", 1069 toolchain: toolchainActionConfigNoneAvailable, 1070 expectedText: "Error in action_config 'A': Flag group must not have more " + 1071 "than one 'expand_if_none_available' field"}, 1072 } 1073 1074 for _, tc := range testCases { 1075 crosstool := makeCrosstool([]string{tc.toolchain}) 1076 _, err := Transform(crosstool) 1077 if err == nil || !strings.Contains(err.Error(), tc.expectedText) { 1078 t.Errorf("Expected error: %s, got: %v", tc.expectedText, err) 1079 } 1080 } 1081} 1082 1083func TestFeaturesAndActionConfigsSetToNoneWhenAllOptionsAreExausted(t *testing.T) { 1084 toolchainFeatureAEnabled := getCToolchain("1", "cpuA", "compilerA", 1085 []string{getFeature([]string{"name: 'A'", "enabled: true"})}, 1086 ) 1087 toolchainFeatureADisabled := getCToolchain("2", "cpuA", "compilerB", 1088 []string{getFeature([]string{"name: 'A'", "enabled: false"})}, 1089 ) 1090 1091 toolchainWithoutFeatureA := getCToolchain("3", "cpuA", "compilerC", []string{}) 1092 1093 toolchainActionConfigAEnabled := getCToolchain("4", "cpuA", "compilerD", 1094 []string{getActionConfig([]string{ 1095 "config_name: 'A'", 1096 "action_name: 'A'", 1097 "enabled: true", 1098 })}) 1099 1100 toolchainActionConfigADisabled := getCToolchain("5", "cpuA", "compilerE", 1101 []string{getActionConfig([]string{ 1102 "config_name: 'A'", 1103 "action_name: 'A'", 1104 })}) 1105 1106 toolchainWithoutActionConfigA := getCToolchain("6", "cpuA", "compilerF", []string{}) 1107 1108 testCases := []struct { 1109 field string 1110 toolchains []string 1111 expectedText string 1112 }{ 1113 {field: "features", 1114 toolchains: []string{ 1115 toolchainFeatureAEnabled, toolchainFeatureADisabled, toolchainWithoutFeatureA}, 1116 expectedText: ` 1117 if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerB"): 1118 a_feature = feature(name = "A") 1119 elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerA"): 1120 a_feature = feature(name = "A", enabled = True) 1121 else: 1122 a_feature = None 1123`}, 1124 {field: "action_config", 1125 toolchains: []string{ 1126 toolchainActionConfigAEnabled, toolchainActionConfigADisabled, toolchainWithoutActionConfigA}, 1127 expectedText: ` 1128 if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerE"): 1129 a_action = action_config(action_name = "A") 1130 elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerD"): 1131 a_action = action_config(action_name = "A", enabled = True) 1132 else: 1133 a_action = None 1134`}, 1135 } 1136 1137 for _, tc := range testCases { 1138 crosstool := makeCrosstool(tc.toolchains) 1139 got, err := Transform(crosstool) 1140 if err != nil { 1141 t.Fatalf("CROSSTOOL conversion failed: %v", err) 1142 } 1143 if !strings.Contains(got, tc.expectedText) { 1144 t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", 1145 tc.field, tc.expectedText) 1146 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 1147 strings.Join(tc.toolchains, "\n"), got) 1148 } 1149 } 1150} 1151 1152func TestActionConfigDeclaration(t *testing.T) { 1153 toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) 1154 toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) 1155 1156 toolchainNameNotInDict := getCToolchain("3", "cpBC", "compilerB", 1157 []string{ 1158 getActionConfig([]string{"action_name: 'A-B.C'", "config_name: 'A-B.C'"}), 1159 }, 1160 ) 1161 toolchainNameInDictA := getCToolchain("4", "cpuC", "compilerA", 1162 []string{ 1163 getActionConfig([]string{"action_name: 'c++-compile'", "config_name: 'c++-compile'"}), 1164 }, 1165 ) 1166 toolchainNameInDictB := getCToolchain("5", "cpuC", "compilerB", 1167 []string{ 1168 getActionConfig([]string{ 1169 "action_name: 'c++-compile'", 1170 "config_name: 'c++-compile'", 1171 "tool {", 1172 " tool_path: '/a/b/c'", 1173 "}", 1174 }), 1175 }, 1176 ) 1177 toolchainComplexActionConfig := getCToolchain("6", "cpuC", "compilerC", 1178 []string{ 1179 getActionConfig([]string{ 1180 "action_name: 'action-complex'", 1181 "config_name: 'action-complex'", 1182 "enabled: true", 1183 "tool {", 1184 " tool_path: '/a/b/c'", 1185 " with_feature {", 1186 " feature: 'a'", 1187 " feature: 'b'", 1188 " not_feature: 'c'", 1189 " not_feature: 'd'", 1190 " }", 1191 " with_feature{", 1192 " feature: 'e'", 1193 " }", 1194 " execution_requirement: 'a'", 1195 "}", 1196 "tool {", 1197 " tool_path: ''", 1198 "}", 1199 "flag_set {", 1200 " flag_group {", 1201 " flag: 'a'", 1202 " flag: '%b'", 1203 " iterate_over: 'c'", 1204 " expand_if_all_available: 'd'", 1205 " expand_if_none_available: 'e'", 1206 " expand_if_true: 'f'", 1207 " expand_if_false: 'g'", 1208 " expand_if_equal {", 1209 " variable: 'var'", 1210 " value: 'val'", 1211 " }", 1212 " }", 1213 " flag_group {", 1214 " flag_group {", 1215 " flag: 'a'", 1216 " }", 1217 " }", 1218 "}", 1219 "flag_set {", 1220 " with_feature {", 1221 " feature: 'a'", 1222 " feature: 'b'", 1223 " not_feature: 'c'", 1224 " not_feature: 'd'", 1225 " }", 1226 "}", 1227 "env_set {", 1228 " action: 'a'", 1229 " env_entry {", 1230 " key: 'k'", 1231 " value: 'v'", 1232 " }", 1233 " with_feature {", 1234 " feature: 'a'", 1235 " }", 1236 "}", 1237 "requires {", 1238 " feature: 'a'", 1239 " feature: 'b'", 1240 "}", 1241 "implies: 'a'", 1242 "implies: 'b'", 1243 }), 1244 }, 1245 ) 1246 1247 testCases := []struct { 1248 toolchains []string 1249 expectedText string 1250 }{ 1251 { 1252 toolchains: []string{toolchainEmpty1, toolchainEmpty2}, 1253 expectedText: ` 1254 action_configs = []`}, 1255 { 1256 toolchains: []string{toolchainEmpty1, toolchainNameNotInDict}, 1257 expectedText: ` 1258 a_b_c_action = action_config(action_name = "A-B.C")`}, 1259 { 1260 toolchains: []string{toolchainNameInDictA, toolchainNameInDictB}, 1261 expectedText: ` 1262 if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): 1263 cpp_compile_action = action_config( 1264 action_name = ACTION_NAMES.cpp_compile, 1265 tools = [tool(path = "/a/b/c")], 1266 ) 1267 elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA"): 1268 cpp_compile_action = action_config(action_name = ACTION_NAMES.cpp_compile)`}, 1269 { 1270 toolchains: []string{toolchainComplexActionConfig}, 1271 expectedText: ` 1272 action_complex_action = action_config( 1273 action_name = "action-complex", 1274 enabled = True, 1275 flag_sets = [ 1276 flag_set( 1277 flag_groups = [ 1278 flag_group( 1279 flags = ["a", "%b"], 1280 iterate_over = "c", 1281 expand_if_available = "d", 1282 expand_if_not_available = "e", 1283 expand_if_true = "f", 1284 expand_if_false = "g", 1285 expand_if_equal = variable_with_value(name = "var", value = "val"), 1286 ), 1287 flag_group(flag_groups = [flag_group(flags = ["a"])]), 1288 ], 1289 ), 1290 flag_set( 1291 with_features = [ 1292 with_feature_set( 1293 features = ["a", "b"], 1294 not_features = ["c", "d"], 1295 ), 1296 ], 1297 ), 1298 ], 1299 implies = ["a", "b"], 1300 tools = [ 1301 tool( 1302 path = "/a/b/c", 1303 with_features = [ 1304 with_feature_set( 1305 features = ["a", "b"], 1306 not_features = ["c", "d"], 1307 ), 1308 with_feature_set(features = ["e"]), 1309 ], 1310 execution_requirements = ["a"], 1311 ), 1312 tool(path = "NOT_USED"), 1313 ], 1314 )`}} 1315 1316 for _, tc := range testCases { 1317 crosstool := makeCrosstool(tc.toolchains) 1318 got, err := Transform(crosstool) 1319 if err != nil { 1320 t.Fatalf("CROSSTOOL conversion failed: %v", err) 1321 } 1322 if !strings.Contains(got, tc.expectedText) { 1323 t.Errorf("Failed to correctly declare an action_config, expected to contain:\n%v\n", 1324 tc.expectedText) 1325 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 1326 strings.Join(tc.toolchains, "\n"), got) 1327 } 1328 } 1329} 1330 1331func TestFeatureDeclaration(t *testing.T) { 1332 toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) 1333 toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) 1334 1335 toolchainSimpleFeatureA1 := getCToolchain("3", "cpuB", "compilerB", 1336 []string{ 1337 getFeature([]string{"name: 'Feature-c++.a'", "enabled: true"}), 1338 }, 1339 ) 1340 toolchainSimpleFeatureA2 := getCToolchain("4", "cpuC", "compilerA", 1341 []string{ 1342 getFeature([]string{"name: 'Feature-c++.a'"}), 1343 }, 1344 ) 1345 toolchainComplexFeature := getCToolchain("5", "cpuC", "compilerC", 1346 []string{ 1347 getFeature([]string{ 1348 "name: 'complex-feature'", 1349 "enabled: true", 1350 "flag_set {", 1351 " action: 'c++-compile'", // in ACTION_NAMES 1352 " action: 'something-else'", // not in ACTION_NAMES 1353 " flag_group {", 1354 " flag: 'a'", 1355 " flag: '%b'", 1356 " iterate_over: 'c'", 1357 " expand_if_all_available: 'd'", 1358 " expand_if_none_available: 'e'", 1359 " expand_if_true: 'f'", 1360 " expand_if_false: 'g'", 1361 " expand_if_equal {", 1362 " variable: 'var'", 1363 " value: 'val'", 1364 " }", 1365 " }", 1366 " flag_group {", 1367 " flag_group {", 1368 " flag: 'a'", 1369 " }", 1370 " }", 1371 "}", 1372 "flag_set {", // all_compile_actions 1373 " action: 'c-compile'", 1374 " action: 'c++-compile'", 1375 " action: 'linkstamp-compile'", 1376 " action: 'assemble'", 1377 " action: 'preprocess-assemble'", 1378 " action: 'c++-header-parsing'", 1379 " action: 'c++-module-compile'", 1380 " action: 'c++-module-codegen'", 1381 " action: 'clif-match'", 1382 " action: 'lto-backend'", 1383 "}", 1384 "flag_set {", // all_cpp_compile_actions 1385 " action: 'c++-compile'", 1386 " action: 'linkstamp-compile'", 1387 " action: 'c++-header-parsing'", 1388 " action: 'c++-module-compile'", 1389 " action: 'c++-module-codegen'", 1390 " action: 'clif-match'", 1391 "}", 1392 "flag_set {", // all_link_actions 1393 " action: 'c++-link-executable'", 1394 " action: 'c++-link-dynamic-library'", 1395 " action: 'c++-link-nodeps-dynamic-library'", 1396 "}", 1397 "flag_set {", // all_cpp_compile_actions + all_link_actions 1398 " action: 'c++-compile'", 1399 " action: 'linkstamp-compile'", 1400 " action: 'c++-header-parsing'", 1401 " action: 'c++-module-compile'", 1402 " action: 'c++-module-codegen'", 1403 " action: 'clif-match'", 1404 " action: 'c++-link-executable'", 1405 " action: 'c++-link-dynamic-library'", 1406 " action: 'c++-link-nodeps-dynamic-library'", 1407 "}", 1408 "flag_set {", // all_link_actions + something else 1409 " action: 'c++-link-executable'", 1410 " action: 'c++-link-dynamic-library'", 1411 " action: 'c++-link-nodeps-dynamic-library'", 1412 " action: 'some.unknown-c++.action'", 1413 "}", 1414 "env_set {", 1415 " action: 'a'", 1416 " env_entry {", 1417 " key: 'k'", 1418 " value: 'v'", 1419 " }", 1420 " with_feature {", 1421 " feature: 'a'", 1422 " }", 1423 "}", 1424 "env_set {", 1425 " action: 'c-compile'", 1426 "}", 1427 "env_set {", // all_compile_actions 1428 " action: 'c-compile'", 1429 " action: 'c++-compile'", 1430 " action: 'linkstamp-compile'", 1431 " action: 'assemble'", 1432 " action: 'preprocess-assemble'", 1433 " action: 'c++-header-parsing'", 1434 " action: 'c++-module-compile'", 1435 " action: 'c++-module-codegen'", 1436 " action: 'clif-match'", 1437 " action: 'lto-backend'", 1438 "}", 1439 "requires {", 1440 " feature: 'a'", 1441 " feature: 'b'", 1442 "}", 1443 "implies: 'a'", 1444 "implies: 'b'", 1445 "provides: 'c'", 1446 "provides: 'd'", 1447 }), 1448 }, 1449 ) 1450 1451 testCases := []struct { 1452 toolchains []string 1453 expectedText string 1454 }{ 1455 { 1456 toolchains: []string{toolchainEmpty1, toolchainEmpty2}, 1457 expectedText: ` 1458 features = [] 1459`}, 1460 { 1461 toolchains: []string{toolchainEmpty1, toolchainSimpleFeatureA1}, 1462 expectedText: ` 1463 feature_cpp_a_feature = feature(name = "Feature-c++.a", enabled = True)`}, 1464 { 1465 toolchains: []string{toolchainSimpleFeatureA1, toolchainSimpleFeatureA2}, 1466 expectedText: ` 1467 if (ctx.attr.cpu == "cpuC"): 1468 feature_cpp_a_feature = feature(name = "Feature-c++.a") 1469 elif (ctx.attr.cpu == "cpuB"): 1470 feature_cpp_a_feature = feature(name = "Feature-c++.a", enabled = True)`}, 1471 { 1472 toolchains: []string{toolchainComplexFeature}, 1473 expectedText: ` 1474 complex_feature_feature = feature( 1475 name = "complex-feature", 1476 enabled = True, 1477 flag_sets = [ 1478 flag_set( 1479 actions = [ACTION_NAMES.cpp_compile, "something-else"], 1480 flag_groups = [ 1481 flag_group( 1482 flags = ["a", "%b"], 1483 iterate_over = "c", 1484 expand_if_available = "d", 1485 expand_if_not_available = "e", 1486 expand_if_true = "f", 1487 expand_if_false = "g", 1488 expand_if_equal = variable_with_value(name = "var", value = "val"), 1489 ), 1490 flag_group(flag_groups = [flag_group(flags = ["a"])]), 1491 ], 1492 ), 1493 flag_set(actions = all_compile_actions), 1494 flag_set(actions = all_cpp_compile_actions), 1495 flag_set(actions = all_link_actions), 1496 flag_set( 1497 actions = all_cpp_compile_actions + 1498 all_link_actions, 1499 ), 1500 flag_set( 1501 actions = all_link_actions + 1502 ["some.unknown-c++.action"], 1503 ), 1504 ], 1505 env_sets = [ 1506 env_set( 1507 actions = ["a"], 1508 env_entries = [env_entry(key = "k", value = "v")], 1509 with_features = [with_feature_set(features = ["a"])], 1510 ), 1511 env_set(actions = [ACTION_NAMES.c_compile]), 1512 env_set(actions = all_compile_actions), 1513 ], 1514 requires = [feature_set(features = ["a", "b"])], 1515 implies = ["a", "b"], 1516 provides = ["c", "d"], 1517 )`}} 1518 1519 for _, tc := range testCases { 1520 crosstool := makeCrosstool(tc.toolchains) 1521 got, err := Transform(crosstool) 1522 if err != nil { 1523 t.Fatalf("CROSSTOOL conversion failed: %v", err) 1524 } 1525 if !strings.Contains(got, tc.expectedText) { 1526 t.Errorf("Failed to correctly declare a feature, expected to contain:\n%v\n", 1527 tc.expectedText) 1528 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 1529 strings.Join(tc.toolchains, "\n"), got) 1530 } 1531 } 1532} 1533 1534func TestRule(t *testing.T) { 1535 simpleToolchain := getSimpleCToolchain("simple") 1536 expected := `load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", 1537 "action_config", 1538 "artifact_name_pattern", 1539 "env_entry", 1540 "env_set", 1541 "feature", 1542 "feature_set", 1543 "flag_group", 1544 "flag_set", 1545 "make_variable", 1546 "tool", 1547 "tool_path", 1548 "variable_with_value", 1549 "with_feature_set", 1550) 1551load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") 1552 1553def _impl(ctx): 1554 toolchain_identifier = "id-simple" 1555 1556 host_system_name = "host-simple" 1557 1558 target_system_name = "target-simple" 1559 1560 target_cpu = "cpu-simple" 1561 1562 target_libc = "libc-simple" 1563 1564 compiler = "compiler-simple" 1565 1566 abi_version = "version-simple" 1567 1568 abi_libc_version = "libc_version-simple" 1569 1570 cc_target_os = None 1571 1572 builtin_sysroot = None 1573 1574 all_compile_actions = [ 1575 ACTION_NAMES.c_compile, 1576 ACTION_NAMES.cpp_compile, 1577 ACTION_NAMES.linkstamp_compile, 1578 ACTION_NAMES.assemble, 1579 ACTION_NAMES.preprocess_assemble, 1580 ACTION_NAMES.cpp_header_parsing, 1581 ACTION_NAMES.cpp_module_compile, 1582 ACTION_NAMES.cpp_module_codegen, 1583 ACTION_NAMES.clif_match, 1584 ACTION_NAMES.lto_backend, 1585 ] 1586 1587 all_cpp_compile_actions = [ 1588 ACTION_NAMES.cpp_compile, 1589 ACTION_NAMES.linkstamp_compile, 1590 ACTION_NAMES.cpp_header_parsing, 1591 ACTION_NAMES.cpp_module_compile, 1592 ACTION_NAMES.cpp_module_codegen, 1593 ACTION_NAMES.clif_match, 1594 ] 1595 1596 preprocessor_compile_actions = [ 1597 ACTION_NAMES.c_compile, 1598 ACTION_NAMES.cpp_compile, 1599 ACTION_NAMES.linkstamp_compile, 1600 ACTION_NAMES.preprocess_assemble, 1601 ACTION_NAMES.cpp_header_parsing, 1602 ACTION_NAMES.cpp_module_compile, 1603 ACTION_NAMES.clif_match, 1604 ] 1605 1606 codegen_compile_actions = [ 1607 ACTION_NAMES.c_compile, 1608 ACTION_NAMES.cpp_compile, 1609 ACTION_NAMES.linkstamp_compile, 1610 ACTION_NAMES.assemble, 1611 ACTION_NAMES.preprocess_assemble, 1612 ACTION_NAMES.cpp_module_codegen, 1613 ACTION_NAMES.lto_backend, 1614 ] 1615 1616 all_link_actions = [ 1617 ACTION_NAMES.cpp_link_executable, 1618 ACTION_NAMES.cpp_link_dynamic_library, 1619 ACTION_NAMES.cpp_link_nodeps_dynamic_library, 1620 ] 1621 1622 action_configs = [] 1623 1624 features = [] 1625 1626 cxx_builtin_include_directories = [] 1627 1628 artifact_name_patterns = [] 1629 1630 make_variables = [] 1631 1632 tool_paths = [] 1633 1634 1635 out = ctx.actions.declare_file(ctx.label.name) 1636 ctx.actions.write(out, "Fake executable") 1637 return [ 1638 cc_common.create_cc_toolchain_config_info( 1639 ctx = ctx, 1640 features = features, 1641 action_configs = action_configs, 1642 artifact_name_patterns = artifact_name_patterns, 1643 cxx_builtin_include_directories = cxx_builtin_include_directories, 1644 toolchain_identifier = toolchain_identifier, 1645 host_system_name = host_system_name, 1646 target_system_name = target_system_name, 1647 target_cpu = target_cpu, 1648 target_libc = target_libc, 1649 compiler = compiler, 1650 abi_version = abi_version, 1651 abi_libc_version = abi_libc_version, 1652 tool_paths = tool_paths, 1653 make_variables = make_variables, 1654 builtin_sysroot = builtin_sysroot, 1655 cc_target_os = cc_target_os 1656 ), 1657 DefaultInfo( 1658 executable = out, 1659 ), 1660 ] 1661cc_toolchain_config = rule( 1662 implementation = _impl, 1663 attrs = { 1664 "cpu": attr.string(mandatory=True, values=["cpu-simple"]), 1665 }, 1666 provides = [CcToolchainConfigInfo], 1667 executable = True, 1668) 1669` 1670 crosstool := makeCrosstool([]string{simpleToolchain}) 1671 got, err := Transform(crosstool) 1672 if err != nil { 1673 t.Fatalf("CROSSTOOL conversion failed: %v", err) 1674 } 1675 if got != expected { 1676 t.Fatalf("Expected:\n%v\nGot:\n%v\nTested CROSSTOOL:\n%v", 1677 expected, got, simpleToolchain) 1678 } 1679} 1680 1681func TestAllowedCompilerValues(t *testing.T) { 1682 toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) 1683 toolchainBA := getCToolchain("2", "cpuB", "compilerA", []string{}) 1684 toolchainBB := getCToolchain("3", "cpuB", "compilerB", []string{}) 1685 toolchainCC := getCToolchain("4", "cpuC", "compilerC", []string{}) 1686 1687 testCases := []struct { 1688 toolchains []string 1689 expectedText string 1690 }{ 1691 { 1692 toolchains: []string{toolchainAA, toolchainBA}, 1693 expectedText: ` 1694cc_toolchain_config = rule( 1695 implementation = _impl, 1696 attrs = { 1697 "cpu": attr.string(mandatory=True, values=["cpuA", "cpuB"]), 1698 }, 1699 provides = [CcToolchainConfigInfo], 1700 executable = True, 1701) 1702`}, 1703 { 1704 toolchains: []string{toolchainBA, toolchainBB}, 1705 expectedText: ` 1706cc_toolchain_config = rule( 1707 implementation = _impl, 1708 attrs = { 1709 "cpu": attr.string(mandatory=True, values=["cpuB"]), 1710 "compiler": attr.string(mandatory=True, values=["compilerA", "compilerB"]), 1711 }, 1712 provides = [CcToolchainConfigInfo], 1713 executable = True, 1714) 1715`}, 1716 { 1717 toolchains: []string{toolchainAA, toolchainBA, toolchainBB}, 1718 expectedText: ` 1719cc_toolchain_config = rule( 1720 implementation = _impl, 1721 attrs = { 1722 "cpu": attr.string(mandatory=True, values=["cpuA", "cpuB"]), 1723 "compiler": attr.string(mandatory=True, values=["compilerA", "compilerB"]), 1724 }, 1725 provides = [CcToolchainConfigInfo], 1726 executable = True, 1727) 1728`}, 1729 { 1730 toolchains: []string{toolchainAA, toolchainBA, toolchainBB, toolchainCC}, 1731 expectedText: ` 1732cc_toolchain_config = rule( 1733 implementation = _impl, 1734 attrs = { 1735 "cpu": attr.string(mandatory=True, values=["cpuA", "cpuB", "cpuC"]), 1736 "compiler": attr.string(mandatory=True, values=["compilerA", "compilerB", "compilerC"]), 1737 }, 1738 provides = [CcToolchainConfigInfo], 1739 executable = True, 1740) 1741`}} 1742 1743 for _, tc := range testCases { 1744 crosstool := makeCrosstool(tc.toolchains) 1745 got, err := Transform(crosstool) 1746 if err != nil { 1747 t.Fatalf("CROSSTOOL conversion failed: %v", err) 1748 } 1749 if !strings.Contains(got, tc.expectedText) { 1750 t.Errorf("Failed to correctly declare the rule, expected to contain:\n%v\n", 1751 tc.expectedText) 1752 t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", 1753 strings.Join(tc.toolchains, "\n"), got) 1754 } 1755 } 1756} 1757