mirror of https://github.com/bazelbuild/rules_cc
1757 lines
51 KiB
Go
1757 lines
51 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|