| package crosstooltostarlarklib |
| |
| import ( |
| "fmt" |
| "strings" |
| "testing" |
| |
| "log" |
| crosstoolpb "third_party/com/github/bazelbuild/bazel/src/main/protobuf/crosstool_config_go_proto" |
| "github.com/golang/protobuf/proto" |
| ) |
| |
| func makeCToolchainString(lines []string) string { |
| return fmt.Sprintf(`toolchain { |
| %s |
| }`, strings.Join(lines, "\n ")) |
| } |
| |
| func makeCrosstool(CToolchains []string) *crosstoolpb.CrosstoolRelease { |
| crosstool := &crosstoolpb.CrosstoolRelease{} |
| requiredFields := []string{ |
| "major_version: '0'", |
| "minor_version: '0'", |
| "default_target_cpu: 'cpu'", |
| } |
| CToolchains = append(CToolchains, requiredFields...) |
| if err := proto.UnmarshalText(strings.Join(CToolchains, "\n"), crosstool); err != nil { |
| log.Fatalf("Failed to parse CROSSTOOL:", err) |
| } |
| return crosstool |
| } |
| |
| func getSimpleCToolchain(id string) string { |
| lines := []string{ |
| "toolchain_identifier: 'id-" + id + "'", |
| "host_system_name: 'host-" + id + "'", |
| "target_system_name: 'target-" + id + "'", |
| "target_cpu: 'cpu-" + id + "'", |
| "compiler: 'compiler-" + id + "'", |
| "target_libc: 'libc-" + id + "'", |
| "abi_version: 'version-" + id + "'", |
| "abi_libc_version: 'libc_version-" + id + "'", |
| } |
| return makeCToolchainString(lines) |
| } |
| |
| func getCToolchain(id, cpu, compiler string, extraLines []string) string { |
| lines := []string{ |
| "toolchain_identifier: '" + id + "'", |
| "host_system_name: 'host'", |
| "target_system_name: 'target'", |
| "target_cpu: '" + cpu + "'", |
| "compiler: '" + compiler + "'", |
| "target_libc: 'libc'", |
| "abi_version: 'version'", |
| "abi_libc_version: 'libc_version'", |
| } |
| lines = append(lines, extraLines...) |
| return makeCToolchainString(lines) |
| } |
| |
| func TestStringFieldsConditionStatement(t *testing.T) { |
| toolchain1 := getSimpleCToolchain("1") |
| toolchain2 := getSimpleCToolchain("2") |
| toolchains := []string{toolchain1, toolchain2} |
| crosstool := makeCrosstool(toolchains) |
| |
| testCases := []struct { |
| field string |
| expectedText string |
| }{ |
| {field: "toolchain_identifier", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| toolchain_identifier = "id-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| toolchain_identifier = "id-2" |
| else: |
| fail("Unreachable")`}, |
| {field: "host_system_name", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| host_system_name = "host-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| host_system_name = "host-2" |
| else: |
| fail("Unreachable")`}, |
| {field: "target_system_name", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| target_system_name = "target-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| target_system_name = "target-2" |
| else: |
| fail("Unreachable")`}, |
| {field: "target_cpu", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| target_cpu = "cpu-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| target_cpu = "cpu-2" |
| else: |
| fail("Unreachable")`}, |
| {field: "target_libc", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| target_libc = "libc-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| target_libc = "libc-2" |
| else: |
| fail("Unreachable")`}, |
| {field: "compiler", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| compiler = "compiler-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| compiler = "compiler-2" |
| else: |
| fail("Unreachable")`}, |
| {field: "abi_version", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| abi_version = "version-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| abi_version = "version-2" |
| else: |
| fail("Unreachable")`}, |
| {field: "abi_libc_version", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpu-1"): |
| abi_libc_version = "libc_version-1" |
| elif (ctx.attr.cpu == "cpu-2"): |
| abi_libc_version = "libc_version-2" |
| else: |
| fail("Unreachable")`}} |
| |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| |
| failed := false |
| for _, tc := range testCases { |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| failed = true |
| } |
| } |
| if failed { |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(toolchains, "\n"), got) |
| } |
| } |
| |
| func TestConditionsSameCpu(t *testing.T) { |
| toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainAB := getCToolchain("2", "cpuA", "compilerB", []string{}) |
| toolchains := []string{toolchainAA, toolchainAB} |
| crosstool := makeCrosstool(toolchains) |
| |
| testCases := []struct { |
| field string |
| expectedText string |
| }{ |
| {field: "toolchain_identifier", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerA"): |
| toolchain_identifier = "1" |
| elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerB"): |
| toolchain_identifier = "2" |
| else: |
| fail("Unreachable")`}, |
| {field: "host_system_name", |
| expectedText: ` |
| host_system_name = "host"`}, |
| {field: "target_system_name", |
| expectedText: ` |
| target_system_name = "target"`}, |
| {field: "target_cpu", |
| expectedText: ` |
| target_cpu = "cpuA"`}, |
| {field: "target_libc", |
| expectedText: ` |
| target_libc = "libc"`}, |
| {field: "compiler", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerA"): |
| compiler = "compilerA" |
| elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerB"): |
| compiler = "compilerB" |
| else: |
| fail("Unreachable")`}, |
| {field: "abi_version", |
| expectedText: ` |
| abi_version = "version"`}, |
| {field: "abi_libc_version", |
| expectedText: ` |
| abi_libc_version = "libc_version"`}} |
| |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| |
| failed := false |
| for _, tc := range testCases { |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| failed = true |
| } |
| } |
| if failed { |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(toolchains, "\n"), got) |
| } |
| } |
| |
| func TestConditionsSameCompiler(t *testing.T) { |
| toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainBA := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchains := []string{toolchainAA, toolchainBA} |
| crosstool := makeCrosstool(toolchains) |
| |
| testCases := []struct { |
| field string |
| expectedText string |
| }{ |
| {field: "toolchain_identifier", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA"): |
| toolchain_identifier = "1" |
| elif (ctx.attr.cpu == "cpuB"): |
| toolchain_identifier = "2" |
| else: |
| fail("Unreachable")`}, |
| {field: "target_cpu", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA"): |
| target_cpu = "cpuA" |
| elif (ctx.attr.cpu == "cpuB"): |
| target_cpu = "cpuB" |
| else: |
| fail("Unreachable")`}, |
| {field: "compiler", |
| expectedText: ` |
| compiler = "compilerA"`}} |
| |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| |
| failed := false |
| for _, tc := range testCases { |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| failed = true |
| } |
| } |
| if failed { |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(toolchains, "\n"), got) |
| } |
| } |
| |
| func TestNonMandatoryStrings(t *testing.T) { |
| toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{"cc_target_os: 'osA'"}) |
| toolchainBB := getCToolchain("2", "cpuB", "compilerB", []string{}) |
| toolchains := []string{toolchainAA, toolchainBB} |
| crosstool := makeCrosstool(toolchains) |
| |
| testCases := []struct { |
| field string |
| expectedText string |
| }{ |
| {field: "cc_target_os", |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuB"): |
| cc_target_os = None |
| elif (ctx.attr.cpu == "cpuA"): |
| cc_target_os = "osA" |
| else: |
| fail("Unreachable")`}, |
| {field: "builtin_sysroot", |
| expectedText: ` |
| builtin_sysroot = None`}} |
| |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| |
| failed := false |
| for _, tc := range testCases { |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| failed = true |
| } |
| } |
| if failed { |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(toolchains, "\n"), got) |
| } |
| } |
| |
| func TestBuiltinIncludeDirectories(t *testing.T) { |
| toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainBA := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchainCA := getCToolchain("3", "cpuC", "compilerA", |
| []string{"cxx_builtin_include_directory: 'dirC'"}) |
| toolchainCB := getCToolchain("4", "cpuC", "compilerB", |
| []string{"cxx_builtin_include_directory: 'dirC'", |
| "cxx_builtin_include_directory: 'dirB'"}) |
| toolchainDA := getCToolchain("5", "cpuD", "compilerA", |
| []string{"cxx_builtin_include_directory: 'dirC'"}) |
| |
| toolchainsEmpty := []string{toolchainAA, toolchainBA} |
| |
| toolchainsOneNonempty := []string{toolchainAA, toolchainBA, toolchainCA} |
| |
| toolchainsSameNonempty := []string{toolchainCA, toolchainDA} |
| |
| allToolchains := []string{toolchainAA, toolchainBA, toolchainCA, toolchainCB, toolchainDA} |
| |
| testCases := []struct { |
| field string |
| toolchains []string |
| expectedText string |
| }{ |
| {field: "cxx_builtin_include_directories", |
| toolchains: toolchainsEmpty, |
| expectedText: ` |
| cxx_builtin_include_directories = []`}, |
| {field: "cxx_builtin_include_directories", |
| toolchains: toolchainsOneNonempty, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" |
| or ctx.attr.cpu == "cpuB"): |
| cxx_builtin_include_directories = [] |
| elif (ctx.attr.cpu == "cpuC"): |
| cxx_builtin_include_directories = ["dirC"] |
| else: |
| fail("Unreachable")`}, |
| {field: "cxx_builtin_include_directories", |
| toolchains: toolchainsSameNonempty, |
| expectedText: ` |
| cxx_builtin_include_directories = ["dirC"]`}, |
| {field: "cxx_builtin_include_directories", |
| toolchains: allToolchains, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" |
| or ctx.attr.cpu == "cpuB"): |
| cxx_builtin_include_directories = [] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" |
| or ctx.attr.cpu == "cpuD"): |
| cxx_builtin_include_directories = ["dirC"] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): |
| cxx_builtin_include_directories = ["dirC", "dirB"]`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func TestMakeVariables(t *testing.T) { |
| toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchainA1 := getCToolchain("3", "cpuC", "compilerA", |
| []string{"make_variable {name: 'A', value: 'a/b/c'}"}) |
| toolchainA2 := getCToolchain("4", "cpuC", "compilerB", |
| []string{"make_variable {name: 'A', value: 'a/b/c'}"}) |
| toolchainAB := getCToolchain("5", "cpuC", "compilerC", |
| []string{"make_variable {name: 'A', value: 'a/b/c'}", |
| "make_variable {name: 'B', value: 'a/b/c'}"}) |
| toolchainBA := getCToolchain("6", "cpuD", "compilerA", |
| []string{"make_variable {name: 'B', value: 'a/b/c'}", |
| "make_variable {name: 'A', value: 'a b c'}"}) |
| |
| toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} |
| |
| toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} |
| |
| toolchainsSameNonempty := []string{toolchainA1, toolchainA2} |
| |
| toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} |
| |
| allToolchains := []string{ |
| toolchainEmpty1, |
| toolchainEmpty2, |
| toolchainA1, |
| toolchainA2, |
| toolchainAB, |
| toolchainBA, |
| } |
| |
| testCases := []struct { |
| field string |
| toolchains []string |
| expectedText string |
| }{ |
| {field: "make_variables", |
| toolchains: toolchainsEmpty, |
| expectedText: ` |
| make_variables = []`}, |
| {field: "make_variables", |
| toolchains: toolchainsOneNonempty, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA"): |
| make_variables = [] |
| elif (ctx.attr.cpu == "cpuC"): |
| make_variables = [make_variable(name = "A", value = "a/b/c")] |
| else: |
| fail("Unreachable")`}, |
| {field: "make_variables", |
| toolchains: toolchainsSameNonempty, |
| expectedText: ` |
| make_variables = [make_variable(name = "A", value = "a/b/c")]`}, |
| {field: "make_variables", |
| toolchains: toolchainsDifferentOrder, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC"): |
| make_variables = [ |
| make_variable(name = "A", value = "a/b/c"), |
| make_variable(name = "B", value = "a/b/c"), |
| ] |
| elif (ctx.attr.cpu == "cpuD"): |
| make_variables = [ |
| make_variable(name = "B", value = "a/b/c"), |
| make_variable(name = "A", value = "a b c"), |
| ] |
| else: |
| fail("Unreachable")`}, |
| {field: "make_variables", |
| toolchains: allToolchains, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): |
| make_variables = [ |
| make_variable(name = "A", value = "a/b/c"), |
| make_variable(name = "B", value = "a/b/c"), |
| ] |
| elif (ctx.attr.cpu == "cpuD"): |
| make_variables = [ |
| make_variable(name = "B", value = "a/b/c"), |
| make_variable(name = "A", value = "a b c"), |
| ] |
| elif (ctx.attr.cpu == "cpuA" |
| or ctx.attr.cpu == "cpuB"): |
| make_variables = [] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" |
| or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): |
| make_variables = [make_variable(name = "A", value = "a/b/c")] |
| else: |
| fail("Unreachable")`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func TestToolPaths(t *testing.T) { |
| toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchainA1 := getCToolchain("3", "cpuC", "compilerA", |
| []string{"tool_path {name: 'A', path: 'a/b/c'}"}) |
| toolchainA2 := getCToolchain("4", "cpuC", "compilerB", |
| []string{"tool_path {name: 'A', path: 'a/b/c'}"}) |
| toolchainAB := getCToolchain("5", "cpuC", "compilerC", |
| []string{"tool_path {name: 'A', path: 'a/b/c'}", |
| "tool_path {name: 'B', path: 'a/b/c'}"}) |
| toolchainBA := getCToolchain("6", "cpuD", "compilerA", |
| []string{"tool_path {name: 'B', path: 'a/b/c'}", |
| "tool_path {name: 'A', path: 'a/b/c'}"}) |
| |
| toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} |
| |
| toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} |
| |
| toolchainsSameNonempty := []string{toolchainA1, toolchainA2} |
| |
| toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} |
| |
| allToolchains := []string{ |
| toolchainEmpty1, |
| toolchainEmpty2, |
| toolchainA1, |
| toolchainA2, |
| toolchainAB, |
| toolchainBA, |
| } |
| |
| testCases := []struct { |
| field string |
| toolchains []string |
| expectedText string |
| }{ |
| {field: "tool_paths", |
| toolchains: toolchainsEmpty, |
| expectedText: ` |
| tool_paths = []`}, |
| {field: "tool_paths", |
| toolchains: toolchainsOneNonempty, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA"): |
| tool_paths = [] |
| elif (ctx.attr.cpu == "cpuC"): |
| tool_paths = [tool_path(name = "A", path = "a/b/c")] |
| else: |
| fail("Unreachable")`}, |
| {field: "tool_paths", |
| toolchains: toolchainsSameNonempty, |
| expectedText: ` |
| tool_paths = [tool_path(name = "A", path = "a/b/c")]`}, |
| {field: "tool_paths", |
| toolchains: toolchainsDifferentOrder, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC"): |
| tool_paths = [ |
| tool_path(name = "A", path = "a/b/c"), |
| tool_path(name = "B", path = "a/b/c"), |
| ] |
| elif (ctx.attr.cpu == "cpuD"): |
| tool_paths = [ |
| tool_path(name = "B", path = "a/b/c"), |
| tool_path(name = "A", path = "a/b/c"), |
| ] |
| else: |
| fail("Unreachable")`}, |
| {field: "tool_paths", |
| toolchains: allToolchains, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): |
| tool_paths = [ |
| tool_path(name = "A", path = "a/b/c"), |
| tool_path(name = "B", path = "a/b/c"), |
| ] |
| elif (ctx.attr.cpu == "cpuD"): |
| tool_paths = [ |
| tool_path(name = "B", path = "a/b/c"), |
| tool_path(name = "A", path = "a/b/c"), |
| ] |
| elif (ctx.attr.cpu == "cpuA" |
| or ctx.attr.cpu == "cpuB"): |
| tool_paths = [] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" |
| or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): |
| tool_paths = [tool_path(name = "A", path = "a/b/c")] |
| else: |
| fail("Unreachable")`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func getArtifactNamePattern(lines []string) string { |
| return fmt.Sprintf(`artifact_name_pattern { |
| %s |
| }`, strings.Join(lines, "\n ")) |
| } |
| |
| func TestArtifactNamePatterns(t *testing.T) { |
| toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchainA1 := getCToolchain("3", "cpuC", "compilerA", |
| []string{ |
| getArtifactNamePattern([]string{ |
| "category_name: 'A'", |
| "prefix: 'p'", |
| "extension: '.exe'"}), |
| }, |
| ) |
| toolchainA2 := getCToolchain("4", "cpuC", "compilerB", |
| []string{ |
| getArtifactNamePattern([]string{ |
| "category_name: 'A'", |
| "prefix: 'p'", |
| "extension: '.exe'"}), |
| }, |
| ) |
| toolchainAB := getCToolchain("5", "cpuC", "compilerC", |
| []string{ |
| getArtifactNamePattern([]string{ |
| "category_name: 'A'", |
| "prefix: 'p'", |
| "extension: '.exe'"}), |
| getArtifactNamePattern([]string{ |
| "category_name: 'B'", |
| "prefix: 'p'", |
| "extension: '.exe'"}), |
| }, |
| ) |
| toolchainBA := getCToolchain("6", "cpuD", "compilerA", |
| []string{ |
| getArtifactNamePattern([]string{ |
| "category_name: 'B'", |
| "prefix: 'p'", |
| "extension: '.exe'"}), |
| getArtifactNamePattern([]string{ |
| "category_name: 'A'", |
| "prefix: 'p'", |
| "extension: '.exe'"}), |
| }, |
| ) |
| toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} |
| |
| toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} |
| |
| toolchainsSameNonempty := []string{toolchainA1, toolchainA2} |
| |
| toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} |
| |
| allToolchains := []string{ |
| toolchainEmpty1, |
| toolchainEmpty2, |
| toolchainA1, |
| toolchainA2, |
| toolchainAB, |
| toolchainBA, |
| } |
| |
| testCases := []struct { |
| field string |
| toolchains []string |
| expectedText string |
| }{ |
| {field: "artifact_name_patterns", |
| toolchains: toolchainsEmpty, |
| expectedText: ` |
| artifact_name_patterns = []`}, |
| {field: "artifact_name_patterns", |
| toolchains: toolchainsOneNonempty, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC"): |
| artifact_name_patterns = [ |
| artifact_name_pattern( |
| category_name = "A", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| ] |
| elif (ctx.attr.cpu == "cpuA"): |
| artifact_name_patterns = [] |
| else: |
| fail("Unreachable")`}, |
| {field: "artifact_name_patterns", |
| toolchains: toolchainsSameNonempty, |
| expectedText: ` |
| artifact_name_patterns = [ |
| artifact_name_pattern( |
| category_name = "A", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| ]`}, |
| {field: "artifact_name_patterns", |
| toolchains: toolchainsDifferentOrder, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC"): |
| artifact_name_patterns = [ |
| artifact_name_pattern( |
| category_name = "A", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| artifact_name_pattern( |
| category_name = "B", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| ] |
| elif (ctx.attr.cpu == "cpuD"): |
| artifact_name_patterns = [ |
| artifact_name_pattern( |
| category_name = "B", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| artifact_name_pattern( |
| category_name = "A", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| ] |
| else: |
| fail("Unreachable")`}, |
| {field: "artifact_name_patterns", |
| toolchains: allToolchains, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): |
| artifact_name_patterns = [ |
| artifact_name_pattern( |
| category_name = "A", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| artifact_name_pattern( |
| category_name = "B", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| ] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" |
| or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): |
| artifact_name_patterns = [ |
| artifact_name_pattern( |
| category_name = "A", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| ] |
| elif (ctx.attr.cpu == "cpuD"): |
| artifact_name_patterns = [ |
| artifact_name_pattern( |
| category_name = "B", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| artifact_name_pattern( |
| category_name = "A", |
| prefix = "p", |
| extension = ".exe", |
| ), |
| ] |
| elif (ctx.attr.cpu == "cpuA" |
| or ctx.attr.cpu == "cpuB"): |
| artifact_name_patterns = [] |
| else: |
| fail("Unreachable")`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func getFeature(lines []string) string { |
| return fmt.Sprintf(`feature { |
| %s |
| }`, strings.Join(lines, "\n ")) |
| } |
| |
| func TestFeatureListAssignment(t *testing.T) { |
| toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchainA1 := getCToolchain("3", "cpuC", "compilerA", |
| []string{getFeature([]string{"name: 'A'"})}, |
| ) |
| toolchainA2 := getCToolchain("4", "cpuC", "compilerB", |
| []string{getFeature([]string{"name: 'A'"})}, |
| ) |
| toolchainAB := getCToolchain("5", "cpuC", "compilerC", |
| []string{ |
| getFeature([]string{"name: 'A'"}), |
| getFeature([]string{"name: 'B'"}), |
| }, |
| ) |
| toolchainBA := getCToolchain("6", "cpuD", "compilerA", |
| []string{ |
| getFeature([]string{"name: 'B'"}), |
| getFeature([]string{"name: 'A'"}), |
| }, |
| ) |
| toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} |
| |
| toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} |
| |
| toolchainsSameNonempty := []string{toolchainA1, toolchainA2} |
| |
| toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} |
| |
| allToolchains := []string{ |
| toolchainEmpty1, |
| toolchainEmpty2, |
| toolchainA1, |
| toolchainA2, |
| toolchainAB, |
| toolchainBA, |
| } |
| |
| testCases := []struct { |
| field string |
| toolchains []string |
| expectedText string |
| }{ |
| {field: "features", |
| toolchains: toolchainsEmpty, |
| expectedText: ` |
| features = []`}, |
| {field: "features", |
| toolchains: toolchainsOneNonempty, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA"): |
| features = [] |
| elif (ctx.attr.cpu == "cpuC"): |
| features = [a_feature] |
| else: |
| fail("Unreachable")`}, |
| {field: "features", |
| toolchains: toolchainsSameNonempty, |
| expectedText: ` |
| features = [a_feature]`}, |
| {field: "features", |
| toolchains: toolchainsDifferentOrder, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC"): |
| features = [a_feature, b_feature] |
| elif (ctx.attr.cpu == "cpuD"): |
| features = [b_feature, a_feature] |
| else: |
| fail("Unreachable")`}, |
| {field: "features", |
| toolchains: allToolchains, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" |
| or ctx.attr.cpu == "cpuB"): |
| features = [] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" |
| or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): |
| features = [a_feature] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): |
| features = [a_feature, b_feature] |
| elif (ctx.attr.cpu == "cpuD"): |
| features = [b_feature, a_feature] |
| else: |
| fail("Unreachable")`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func getActionConfig(lines []string) string { |
| return fmt.Sprintf(`action_config { |
| %s |
| }`, strings.Join(lines, "\n ")) |
| } |
| |
| func TestActionConfigListAssignment(t *testing.T) { |
| toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchainA1 := getCToolchain("3", "cpuC", "compilerA", |
| []string{ |
| getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), |
| }, |
| ) |
| toolchainA2 := getCToolchain("4", "cpuC", "compilerB", |
| []string{ |
| getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), |
| }, |
| ) |
| toolchainAB := getCToolchain("5", "cpuC", "compilerC", |
| []string{ |
| getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), |
| getActionConfig([]string{"action_name: 'B'", "config_name: 'B'"}), |
| }, |
| ) |
| toolchainBA := getCToolchain("6", "cpuD", "compilerA", |
| []string{ |
| getActionConfig([]string{"action_name: 'B'", "config_name: 'B'"}), |
| getActionConfig([]string{"action_name: 'A'", "config_name: 'A'"}), |
| }, |
| ) |
| toolchainsEmpty := []string{toolchainEmpty1, toolchainEmpty2} |
| |
| toolchainsOneNonempty := []string{toolchainEmpty1, toolchainA1} |
| |
| toolchainsSameNonempty := []string{toolchainA1, toolchainA2} |
| |
| toolchainsDifferentOrder := []string{toolchainAB, toolchainBA} |
| |
| allToolchains := []string{ |
| toolchainEmpty1, |
| toolchainEmpty2, |
| toolchainA1, |
| toolchainA2, |
| toolchainAB, |
| toolchainBA, |
| } |
| |
| testCases := []struct { |
| field string |
| toolchains []string |
| expectedText string |
| }{ |
| {field: "action_configs", |
| toolchains: toolchainsEmpty, |
| expectedText: ` |
| action_configs = []`}, |
| {field: "action_configs", |
| toolchains: toolchainsOneNonempty, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA"): |
| action_configs = [] |
| elif (ctx.attr.cpu == "cpuC"): |
| action_configs = [a_action] |
| else: |
| fail("Unreachable")`}, |
| {field: "action_configs", |
| toolchains: toolchainsSameNonempty, |
| expectedText: ` |
| action_configs = [a_action]`}, |
| {field: "action_configs", |
| toolchains: toolchainsDifferentOrder, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC"): |
| action_configs = [a_action, b_action] |
| elif (ctx.attr.cpu == "cpuD"): |
| action_configs = [b_action, a_action] |
| else: |
| fail("Unreachable")`}, |
| {field: "action_configs", |
| toolchains: allToolchains, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" |
| or ctx.attr.cpu == "cpuB"): |
| action_configs = [] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA" |
| or ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): |
| action_configs = [a_action] |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerC"): |
| action_configs = [a_action, b_action] |
| elif (ctx.attr.cpu == "cpuD"): |
| action_configs = [b_action, a_action] |
| else: |
| fail("Unreachable")`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func TestAllAndNoneAvailableErrorsWhenMoreThanOneElement(t *testing.T) { |
| toolchainFeatureAllAvailable := getCToolchain("1", "cpu", "compiler", |
| []string{getFeature([]string{ |
| "name: 'A'", |
| "flag_set {", |
| " action: 'A'", |
| " flag_group {", |
| " flag: 'f'", |
| " expand_if_all_available: 'e1'", |
| " expand_if_all_available: 'e2'", |
| " }", |
| "}", |
| })}, |
| ) |
| toolchainFeatureNoneAvailable := getCToolchain("1", "cpu", "compiler", |
| []string{getFeature([]string{ |
| "name: 'A'", |
| "flag_set {", |
| " action: 'A'", |
| " flag_group {", |
| " flag: 'f'", |
| " expand_if_none_available: 'e1'", |
| " expand_if_none_available: 'e2'", |
| " }", |
| "}", |
| })}, |
| ) |
| toolchainActionConfigAllAvailable := getCToolchain("1", "cpu", "compiler", |
| []string{getActionConfig([]string{ |
| "config_name: 'A'", |
| "action_name: 'A'", |
| "flag_set {", |
| " action: 'A'", |
| " flag_group {", |
| " flag: 'f'", |
| " expand_if_all_available: 'e1'", |
| " expand_if_all_available: 'e2'", |
| " }", |
| "}", |
| })}, |
| ) |
| toolchainActionConfigNoneAvailable := getCToolchain("1", "cpu", "compiler", |
| []string{getActionConfig([]string{ |
| "config_name: 'A'", |
| "action_name: 'A'", |
| "flag_set {", |
| " action: 'A'", |
| " flag_group {", |
| " flag: 'f'", |
| " expand_if_none_available: 'e1'", |
| " expand_if_none_available: 'e2'", |
| " }", |
| "}", |
| })}, |
| ) |
| |
| testCases := []struct { |
| field string |
| toolchain string |
| expectedText string |
| }{ |
| {field: "features", |
| toolchain: toolchainFeatureAllAvailable, |
| expectedText: "Error in feature 'A': Flag group must not have more " + |
| "than one 'expand_if_all_available' field"}, |
| {field: "features", |
| toolchain: toolchainFeatureNoneAvailable, |
| expectedText: "Error in feature 'A': Flag group must not have more " + |
| "than one 'expand_if_none_available' field"}, |
| {field: "action_configs", |
| toolchain: toolchainActionConfigAllAvailable, |
| expectedText: "Error in action_config 'A': Flag group must not have more " + |
| "than one 'expand_if_all_available' field"}, |
| {field: "action_configs", |
| toolchain: toolchainActionConfigNoneAvailable, |
| expectedText: "Error in action_config 'A': Flag group must not have more " + |
| "than one 'expand_if_none_available' field"}, |
| } |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool([]string{tc.toolchain}) |
| _, err := Transform(crosstool) |
| if err == nil || !strings.Contains(err.Error(), tc.expectedText) { |
| t.Errorf("Expected error: %s, got: %v", tc.expectedText, err) |
| } |
| } |
| } |
| |
| func TestFeaturesAndActionConfigsSetToNoneWhenAllOptionsAreExausted(t *testing.T) { |
| toolchainFeatureAEnabled := getCToolchain("1", "cpuA", "compilerA", |
| []string{getFeature([]string{"name: 'A'", "enabled: true"})}, |
| ) |
| toolchainFeatureADisabled := getCToolchain("2", "cpuA", "compilerB", |
| []string{getFeature([]string{"name: 'A'", "enabled: false"})}, |
| ) |
| |
| toolchainWithoutFeatureA := getCToolchain("3", "cpuA", "compilerC", []string{}) |
| |
| toolchainActionConfigAEnabled := getCToolchain("4", "cpuA", "compilerD", |
| []string{getActionConfig([]string{ |
| "config_name: 'A'", |
| "action_name: 'A'", |
| "enabled: true", |
| })}) |
| |
| toolchainActionConfigADisabled := getCToolchain("5", "cpuA", "compilerE", |
| []string{getActionConfig([]string{ |
| "config_name: 'A'", |
| "action_name: 'A'", |
| })}) |
| |
| toolchainWithoutActionConfigA := getCToolchain("6", "cpuA", "compilerF", []string{}) |
| |
| testCases := []struct { |
| field string |
| toolchains []string |
| expectedText string |
| }{ |
| {field: "features", |
| toolchains: []string{ |
| toolchainFeatureAEnabled, toolchainFeatureADisabled, toolchainWithoutFeatureA}, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerB"): |
| a_feature = feature(name = "A") |
| elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerA"): |
| a_feature = feature(name = "A", enabled = True) |
| else: |
| a_feature = None |
| `}, |
| {field: "action_config", |
| toolchains: []string{ |
| toolchainActionConfigAEnabled, toolchainActionConfigADisabled, toolchainWithoutActionConfigA}, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerE"): |
| a_action = action_config(action_name = "A") |
| elif (ctx.attr.cpu == "cpuA" and ctx.attr.compiler == "compilerD"): |
| a_action = action_config(action_name = "A", enabled = True) |
| else: |
| a_action = None |
| `}, |
| } |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly convert '%s' field, expected to contain:\n%v\n", |
| tc.field, tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func TestActionConfigDeclaration(t *testing.T) { |
| toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| |
| toolchainNameNotInDict := getCToolchain("3", "cpBC", "compilerB", |
| []string{ |
| getActionConfig([]string{"action_name: 'A-B.C'", "config_name: 'A-B.C'"}), |
| }, |
| ) |
| toolchainNameInDictA := getCToolchain("4", "cpuC", "compilerA", |
| []string{ |
| getActionConfig([]string{"action_name: 'c++-compile'", "config_name: 'c++-compile'"}), |
| }, |
| ) |
| toolchainNameInDictB := getCToolchain("5", "cpuC", "compilerB", |
| []string{ |
| getActionConfig([]string{ |
| "action_name: 'c++-compile'", |
| "config_name: 'c++-compile'", |
| "tool {", |
| " tool_path: '/a/b/c'", |
| "}", |
| }), |
| }, |
| ) |
| toolchainComplexActionConfig := getCToolchain("6", "cpuC", "compilerC", |
| []string{ |
| getActionConfig([]string{ |
| "action_name: 'action-complex'", |
| "config_name: 'action-complex'", |
| "enabled: true", |
| "tool {", |
| " tool_path: '/a/b/c'", |
| " with_feature {", |
| " feature: 'a'", |
| " feature: 'b'", |
| " not_feature: 'c'", |
| " not_feature: 'd'", |
| " }", |
| " with_feature{", |
| " feature: 'e'", |
| " }", |
| " execution_requirement: 'a'", |
| "}", |
| "tool {", |
| " tool_path: ''", |
| "}", |
| "flag_set {", |
| " flag_group {", |
| " flag: 'a'", |
| " flag: '%b'", |
| " iterate_over: 'c'", |
| " expand_if_all_available: 'd'", |
| " expand_if_none_available: 'e'", |
| " expand_if_true: 'f'", |
| " expand_if_false: 'g'", |
| " expand_if_equal {", |
| " variable: 'var'", |
| " value: 'val'", |
| " }", |
| " }", |
| " flag_group {", |
| " flag_group {", |
| " flag: 'a'", |
| " }", |
| " }", |
| "}", |
| "flag_set {", |
| " with_feature {", |
| " feature: 'a'", |
| " feature: 'b'", |
| " not_feature: 'c'", |
| " not_feature: 'd'", |
| " }", |
| "}", |
| "env_set {", |
| " action: 'a'", |
| " env_entry {", |
| " key: 'k'", |
| " value: 'v'", |
| " }", |
| " with_feature {", |
| " feature: 'a'", |
| " }", |
| "}", |
| "requires {", |
| " feature: 'a'", |
| " feature: 'b'", |
| "}", |
| "implies: 'a'", |
| "implies: 'b'", |
| }), |
| }, |
| ) |
| |
| testCases := []struct { |
| toolchains []string |
| expectedText string |
| }{ |
| { |
| toolchains: []string{toolchainEmpty1, toolchainEmpty2}, |
| expectedText: ` |
| action_configs = []`}, |
| { |
| toolchains: []string{toolchainEmpty1, toolchainNameNotInDict}, |
| expectedText: ` |
| a_b_c_action = action_config(action_name = "A-B.C")`}, |
| { |
| toolchains: []string{toolchainNameInDictA, toolchainNameInDictB}, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerB"): |
| cpp_compile_action = action_config( |
| action_name = ACTION_NAMES.cpp_compile, |
| tools = [tool(path = "/a/b/c")], |
| ) |
| elif (ctx.attr.cpu == "cpuC" and ctx.attr.compiler == "compilerA"): |
| cpp_compile_action = action_config(action_name = ACTION_NAMES.cpp_compile)`}, |
| { |
| toolchains: []string{toolchainComplexActionConfig}, |
| expectedText: ` |
| action_complex_action = action_config( |
| action_name = "action-complex", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| flag_groups = [ |
| flag_group( |
| flags = ["a", "%b"], |
| iterate_over = "c", |
| expand_if_available = "d", |
| expand_if_not_available = "e", |
| expand_if_true = "f", |
| expand_if_false = "g", |
| expand_if_equal = variable_with_value(name = "var", value = "val"), |
| ), |
| flag_group(flag_groups = [flag_group(flags = ["a"])]), |
| ], |
| ), |
| flag_set( |
| with_features = [ |
| with_feature_set( |
| features = ["a", "b"], |
| not_features = ["c", "d"], |
| ), |
| ], |
| ), |
| ], |
| implies = ["a", "b"], |
| tools = [ |
| tool( |
| path = "/a/b/c", |
| with_features = [ |
| with_feature_set( |
| features = ["a", "b"], |
| not_features = ["c", "d"], |
| ), |
| with_feature_set(features = ["e"]), |
| ], |
| execution_requirements = ["a"], |
| ), |
| tool(path = "NOT_USED"), |
| ], |
| )`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly declare an action_config, expected to contain:\n%v\n", |
| tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func TestFeatureDeclaration(t *testing.T) { |
| toolchainEmpty1 := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainEmpty2 := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| |
| toolchainSimpleFeatureA1 := getCToolchain("3", "cpuB", "compilerB", |
| []string{ |
| getFeature([]string{"name: 'Feature-c++.a'", "enabled: true"}), |
| }, |
| ) |
| toolchainSimpleFeatureA2 := getCToolchain("4", "cpuC", "compilerA", |
| []string{ |
| getFeature([]string{"name: 'Feature-c++.a'"}), |
| }, |
| ) |
| toolchainComplexFeature := getCToolchain("5", "cpuC", "compilerC", |
| []string{ |
| getFeature([]string{ |
| "name: 'complex-feature'", |
| "enabled: true", |
| "flag_set {", |
| " action: 'c++-compile'", // in ACTION_NAMES |
| " action: 'something-else'", // not in ACTION_NAMES |
| " flag_group {", |
| " flag: 'a'", |
| " flag: '%b'", |
| " iterate_over: 'c'", |
| " expand_if_all_available: 'd'", |
| " expand_if_none_available: 'e'", |
| " expand_if_true: 'f'", |
| " expand_if_false: 'g'", |
| " expand_if_equal {", |
| " variable: 'var'", |
| " value: 'val'", |
| " }", |
| " }", |
| " flag_group {", |
| " flag_group {", |
| " flag: 'a'", |
| " }", |
| " }", |
| "}", |
| "flag_set {", // all_compile_actions |
| " action: 'c-compile'", |
| " action: 'c++-compile'", |
| " action: 'linkstamp-compile'", |
| " action: 'assemble'", |
| " action: 'preprocess-assemble'", |
| " action: 'c++-header-parsing'", |
| " action: 'c++-module-compile'", |
| " action: 'c++-module-codegen'", |
| " action: 'clif-match'", |
| " action: 'lto-backend'", |
| "}", |
| "flag_set {", // all_cpp_compile_actions |
| " action: 'c++-compile'", |
| " action: 'linkstamp-compile'", |
| " action: 'c++-header-parsing'", |
| " action: 'c++-module-compile'", |
| " action: 'c++-module-codegen'", |
| " action: 'clif-match'", |
| "}", |
| "flag_set {", // all_link_actions |
| " action: 'c++-link-executable'", |
| " action: 'c++-link-dynamic-library'", |
| " action: 'c++-link-nodeps-dynamic-library'", |
| "}", |
| "flag_set {", // all_cpp_compile_actions + all_link_actions |
| " action: 'c++-compile'", |
| " action: 'linkstamp-compile'", |
| " action: 'c++-header-parsing'", |
| " action: 'c++-module-compile'", |
| " action: 'c++-module-codegen'", |
| " action: 'clif-match'", |
| " action: 'c++-link-executable'", |
| " action: 'c++-link-dynamic-library'", |
| " action: 'c++-link-nodeps-dynamic-library'", |
| "}", |
| "flag_set {", // all_link_actions + something else |
| " action: 'c++-link-executable'", |
| " action: 'c++-link-dynamic-library'", |
| " action: 'c++-link-nodeps-dynamic-library'", |
| " action: 'some.unknown-c++.action'", |
| "}", |
| "env_set {", |
| " action: 'a'", |
| " env_entry {", |
| " key: 'k'", |
| " value: 'v'", |
| " }", |
| " with_feature {", |
| " feature: 'a'", |
| " }", |
| "}", |
| "env_set {", |
| " action: 'c-compile'", |
| "}", |
| "env_set {", // all_compile_actions |
| " action: 'c-compile'", |
| " action: 'c++-compile'", |
| " action: 'linkstamp-compile'", |
| " action: 'assemble'", |
| " action: 'preprocess-assemble'", |
| " action: 'c++-header-parsing'", |
| " action: 'c++-module-compile'", |
| " action: 'c++-module-codegen'", |
| " action: 'clif-match'", |
| " action: 'lto-backend'", |
| "}", |
| "requires {", |
| " feature: 'a'", |
| " feature: 'b'", |
| "}", |
| "implies: 'a'", |
| "implies: 'b'", |
| "provides: 'c'", |
| "provides: 'd'", |
| }), |
| }, |
| ) |
| |
| testCases := []struct { |
| toolchains []string |
| expectedText string |
| }{ |
| { |
| toolchains: []string{toolchainEmpty1, toolchainEmpty2}, |
| expectedText: ` |
| features = [] |
| `}, |
| { |
| toolchains: []string{toolchainEmpty1, toolchainSimpleFeatureA1}, |
| expectedText: ` |
| feature_cpp_a_feature = feature(name = "Feature-c++.a", enabled = True)`}, |
| { |
| toolchains: []string{toolchainSimpleFeatureA1, toolchainSimpleFeatureA2}, |
| expectedText: ` |
| if (ctx.attr.cpu == "cpuC"): |
| feature_cpp_a_feature = feature(name = "Feature-c++.a") |
| elif (ctx.attr.cpu == "cpuB"): |
| feature_cpp_a_feature = feature(name = "Feature-c++.a", enabled = True)`}, |
| { |
| toolchains: []string{toolchainComplexFeature}, |
| expectedText: ` |
| complex_feature_feature = feature( |
| name = "complex-feature", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ACTION_NAMES.cpp_compile, "something-else"], |
| flag_groups = [ |
| flag_group( |
| flags = ["a", "%b"], |
| iterate_over = "c", |
| expand_if_available = "d", |
| expand_if_not_available = "e", |
| expand_if_true = "f", |
| expand_if_false = "g", |
| expand_if_equal = variable_with_value(name = "var", value = "val"), |
| ), |
| flag_group(flag_groups = [flag_group(flags = ["a"])]), |
| ], |
| ), |
| flag_set(actions = all_compile_actions), |
| flag_set(actions = all_cpp_compile_actions), |
| flag_set(actions = all_link_actions), |
| flag_set( |
| actions = all_cpp_compile_actions + |
| all_link_actions, |
| ), |
| flag_set( |
| actions = all_link_actions + |
| ["some.unknown-c++.action"], |
| ), |
| ], |
| env_sets = [ |
| env_set( |
| actions = ["a"], |
| env_entries = [env_entry(key = "k", value = "v")], |
| with_features = [with_feature_set(features = ["a"])], |
| ), |
| env_set(actions = [ACTION_NAMES.c_compile]), |
| env_set(actions = all_compile_actions), |
| ], |
| requires = [feature_set(features = ["a", "b"])], |
| implies = ["a", "b"], |
| provides = ["c", "d"], |
| )`}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly declare a feature, expected to contain:\n%v\n", |
| tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |
| |
| func TestRule(t *testing.T) { |
| simpleToolchain := getSimpleCToolchain("simple") |
| expected := `load("@rules_cc//cc:cc_toolchain_config_lib.bzl", |
| "action_config", |
| "artifact_name_pattern", |
| "env_entry", |
| "env_set", |
| "feature", |
| "feature_set", |
| "flag_group", |
| "flag_set", |
| "make_variable", |
| "tool", |
| "tool_path", |
| "variable_with_value", |
| "with_feature_set", |
| ) |
| load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") |
| |
| def _impl(ctx): |
| toolchain_identifier = "id-simple" |
| |
| host_system_name = "host-simple" |
| |
| target_system_name = "target-simple" |
| |
| target_cpu = "cpu-simple" |
| |
| target_libc = "libc-simple" |
| |
| compiler = "compiler-simple" |
| |
| abi_version = "version-simple" |
| |
| abi_libc_version = "libc_version-simple" |
| |
| cc_target_os = None |
| |
| builtin_sysroot = None |
| |
| all_compile_actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.linkstamp_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ACTION_NAMES.cpp_header_parsing, |
| ACTION_NAMES.cpp_module_compile, |
| ACTION_NAMES.cpp_module_codegen, |
| ACTION_NAMES.clif_match, |
| ACTION_NAMES.lto_backend, |
| ] |
| |
| all_cpp_compile_actions = [ |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.linkstamp_compile, |
| ACTION_NAMES.cpp_header_parsing, |
| ACTION_NAMES.cpp_module_compile, |
| ACTION_NAMES.cpp_module_codegen, |
| ACTION_NAMES.clif_match, |
| ] |
| |
| preprocessor_compile_actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.linkstamp_compile, |
| ACTION_NAMES.preprocess_assemble, |
| ACTION_NAMES.cpp_header_parsing, |
| ACTION_NAMES.cpp_module_compile, |
| ACTION_NAMES.clif_match, |
| ] |
| |
| codegen_compile_actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.linkstamp_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ACTION_NAMES.cpp_module_codegen, |
| ACTION_NAMES.lto_backend, |
| ] |
| |
| all_link_actions = [ |
| ACTION_NAMES.cpp_link_executable, |
| ACTION_NAMES.cpp_link_dynamic_library, |
| ACTION_NAMES.cpp_link_nodeps_dynamic_library, |
| ] |
| |
| action_configs = [] |
| |
| features = [] |
| |
| cxx_builtin_include_directories = [] |
| |
| artifact_name_patterns = [] |
| |
| make_variables = [] |
| |
| tool_paths = [] |
| |
| |
| out = ctx.actions.declare_file(ctx.label.name) |
| ctx.actions.write(out, "Fake executable") |
| return [ |
| cc_common.create_cc_toolchain_config_info( |
| ctx = ctx, |
| features = features, |
| action_configs = action_configs, |
| artifact_name_patterns = artifact_name_patterns, |
| cxx_builtin_include_directories = cxx_builtin_include_directories, |
| toolchain_identifier = toolchain_identifier, |
| host_system_name = host_system_name, |
| target_system_name = target_system_name, |
| target_cpu = target_cpu, |
| target_libc = target_libc, |
| compiler = compiler, |
| abi_version = abi_version, |
| abi_libc_version = abi_libc_version, |
| tool_paths = tool_paths, |
| make_variables = make_variables, |
| builtin_sysroot = builtin_sysroot, |
| cc_target_os = cc_target_os |
| ), |
| DefaultInfo( |
| executable = out, |
| ), |
| ] |
| cc_toolchain_config = rule( |
| implementation = _impl, |
| attrs = { |
| "cpu": attr.string(mandatory=True, values=["cpu-simple"]), |
| }, |
| provides = [CcToolchainConfigInfo], |
| executable = True, |
| ) |
| ` |
| crosstool := makeCrosstool([]string{simpleToolchain}) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if got != expected { |
| t.Fatalf("Expected:\n%v\nGot:\n%v\nTested CROSSTOOL:\n%v", |
| expected, got, simpleToolchain) |
| } |
| } |
| |
| func TestAllowedCompilerValues(t *testing.T) { |
| toolchainAA := getCToolchain("1", "cpuA", "compilerA", []string{}) |
| toolchainBA := getCToolchain("2", "cpuB", "compilerA", []string{}) |
| toolchainBB := getCToolchain("3", "cpuB", "compilerB", []string{}) |
| toolchainCC := getCToolchain("4", "cpuC", "compilerC", []string{}) |
| |
| testCases := []struct { |
| toolchains []string |
| expectedText string |
| }{ |
| { |
| toolchains: []string{toolchainAA, toolchainBA}, |
| expectedText: ` |
| cc_toolchain_config = rule( |
| implementation = _impl, |
| attrs = { |
| "cpu": attr.string(mandatory=True, values=["cpuA", "cpuB"]), |
| }, |
| provides = [CcToolchainConfigInfo], |
| executable = True, |
| ) |
| `}, |
| { |
| toolchains: []string{toolchainBA, toolchainBB}, |
| expectedText: ` |
| cc_toolchain_config = rule( |
| implementation = _impl, |
| attrs = { |
| "cpu": attr.string(mandatory=True, values=["cpuB"]), |
| "compiler": attr.string(mandatory=True, values=["compilerA", "compilerB"]), |
| }, |
| provides = [CcToolchainConfigInfo], |
| executable = True, |
| ) |
| `}, |
| { |
| toolchains: []string{toolchainAA, toolchainBA, toolchainBB}, |
| expectedText: ` |
| cc_toolchain_config = rule( |
| implementation = _impl, |
| attrs = { |
| "cpu": attr.string(mandatory=True, values=["cpuA", "cpuB"]), |
| "compiler": attr.string(mandatory=True, values=["compilerA", "compilerB"]), |
| }, |
| provides = [CcToolchainConfigInfo], |
| executable = True, |
| ) |
| `}, |
| { |
| toolchains: []string{toolchainAA, toolchainBA, toolchainBB, toolchainCC}, |
| expectedText: ` |
| cc_toolchain_config = rule( |
| implementation = _impl, |
| attrs = { |
| "cpu": attr.string(mandatory=True, values=["cpuA", "cpuB", "cpuC"]), |
| "compiler": attr.string(mandatory=True, values=["compilerA", "compilerB", "compilerC"]), |
| }, |
| provides = [CcToolchainConfigInfo], |
| executable = True, |
| ) |
| `}} |
| |
| for _, tc := range testCases { |
| crosstool := makeCrosstool(tc.toolchains) |
| got, err := Transform(crosstool) |
| if err != nil { |
| t.Fatalf("CROSSTOOL conversion failed: %v", err) |
| } |
| if !strings.Contains(got, tc.expectedText) { |
| t.Errorf("Failed to correctly declare the rule, expected to contain:\n%v\n", |
| tc.expectedText) |
| t.Fatalf("Tested CROSSTOOL:\n%v\n\nGenerated rule:\n%v\n", |
| strings.Join(tc.toolchains, "\n"), got) |
| } |
| } |
| } |