blob: c4df02ce73843d53790020e2089a772a4f4e2f0e [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package bazel2gn_test
import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"go.fuchsia.dev/fuchsia/build/tools/bazel2gn"
)
func TestLinuxResourceFileToGN(t *testing.T) {
for _, tc := range []struct {
name string
bazel string
wantGN string
}{
{
name: "hello_starnix fx_cc_binary and linux_resource",
bazel: `fx_cc_binary(
name = "hello_starnix_bin_bin",
srcs = select({
"@platforms//cpu:x86_64": ["hello_x64.S"],
"@platforms//cpu:arm64": ["hello_aarch64.S"],
"@platforms//cpu:riscv64": ["hello_riscv64.S"],
}),
disable_build_flags = ["//build/config/linux:implicit-host-libs"],
disable_instrumentation = True,
disable_syslog_backend = True,
linkopts = [
"-nostdlib",
"-nostartfiles",
"-static",
"-Wno-unused-command-line-argument",
],
tags = ["manual"],
)
linux_resource(
name = "hello_starnix_bin",
src = ":hello_starnix_bin_bin",
dest = "data/tests/hello_starnix",
visibility = ["//src/starnix/kernel/core:__pkg__"],
)
fx_component_manifest(
name = "manifest",
component_name = "hello_starnix",
manifest = "meta/hello_starnix.cml",
)
fx_component(
name = "component",
compiled_manifest = ":manifest",
deps = [":hello_starnix_bin"],
)
fx_package(
name = "hello_starnix",
components = [":component"],
)`,
wantGN: `if (is_linux && !is_host) {
executable("hello_starnix_bin_bin") {
if (current_cpu == "x64") {
sources = [
"hello_x64.S",
]
} else if (current_cpu == "arm64") {
sources = [
"hello_aarch64.S",
]
} else if (current_cpu == "riscv64") {
sources = [
"hello_riscv64.S",
]
}
configs -= [
"//build/config/linux:implicit-host-libs",
]
exclude_toolchain_tags = [ "instrumented" ]
disable_syslog_backend = true
ldflags = [
"-nostdlib",
"-nostartfiles",
"-static",
"-Wno-unused-command-line-argument",
]
}
}
linux_resource("hello_starnix_bin") {
src = ":hello_starnix_bin_bin"
dest = "data/tests/hello_starnix"
visibility = [
":*",
"//src/starnix/kernel/core:*",
]
}
fuchsia_component_manifest("manifest") {
component_name = "hello_starnix"
manifest = "meta/hello_starnix.cml"
}
fuchsia_component("component") {
cm_label = ":manifest"
deps = [
":hello_starnix_bin",
]
}`,
},
{
name: "rustc_binary with custom crate_name and linux_resource",
bazel: `rustc_binary(
name = "thermal_client_exe",
testonly = True,
srcs = ["src/thermal_client.rs"],
crate_name = "thermal_client",
crate_root = "src/thermal_client.rs",
edition = "2024",
tags = [
"exclude_toolchain_tags=instrumented",
"manual",
],
deps = ["//third_party/rust_crates/vendor:libc"],
)
linux_resource(
name = "thermal_client_bin",
testonly = True,
src = ":thermal_client_exe",
dest = "data/tests/thermal_client",
tags = ["assert_no_deps=//sdk/lib/fdio"],
)`,
wantGN: `if (is_linux && !is_host) {
rustc_binary("thermal_client_exe") {
testonly = true
sources = [
"src/thermal_client.rs",
]
output_name = "thermal_client"
source_root = "src/thermal_client.rs"
edition = "2024"
exclude_toolchain_tags = [
"instrumented",
]
deps = [
"//third_party/rust_crates:libc",
]
}
}
linux_resource("thermal_client_bin") {
testonly = true
src = ":thermal_client_exe"
bin_output_name = "thermal_client"
dest = "data/tests/thermal_client"
assert_no_deps = [
"//sdk/lib/fdio",
]
}`,
},
{
name: "cross-package linux_resource",
bazel: `linux_resource(
name = "default_init",
src = "//src/starnix/containers/default_init",
dest = "data/init",
visibility = [
"//src/starnix/containers:__subpackages__",
"//src/starnix/kernel:__pkg__",
],
)`,
wantGN: `linux_resource("default_init") {
src = "//src/starnix/containers/default_init"
dest = "data/init"
visibility = [
":*",
"//src/starnix/containers/*",
"//src/starnix/kernel:*",
]
}`,
},
{
name: "linkshared fx_cc_binary with arch32_src select",
bazel: `fx_cc_binary(
name = "vdso_lib",
srcs = ["vdso_common.cc"] + select({
"@platforms//cpu:arm": ["vdso_arm_eabi.cc"],
"//conditions:default": [],
}),
disable_build_flags = ["//build/config/linux:implicit-host-libs"],
disable_instrumentation = True,
linkopts = [
"-Wl,-T,vdso.ld", # @bazel2gn:raw_overwrite:"-Wl,-T," + rebase_path("vdso.ld", root_build_dir)
],
linkshared = True,
tags = ["manual"],
target_compatible_with = [
"//build/bazel/platforms:linux_flavor_starnix",
],
)
linux_resource(
name = "vdso_data",
src = ":vdso_lib", # @bazel2gn:path_overwrite::vdso_lib($bionic_linux_toolchain)
dest = "data/libvdso.so",
arch32_src = select({
"@platforms//cpu:arm64": ":vdso_lib",
"//conditions:default": None,
}),
arch32_dest = "data/libvdso_arch32.so",
)`,
wantGN: `if (is_linux && !is_host) {
loadable_module("vdso_lib") {
sources = []
sources += [
"vdso_common.cc",
]
if (current_cpu == "arm") {
sources += [
"vdso_arm_eabi.cc",
]
}
configs -= [
"//build/config/linux:implicit-host-libs",
]
exclude_toolchain_tags = [ "instrumented" ]
ldflags = [
"-Wl,-T," + rebase_path("vdso.ld", root_build_dir),
]
}
}
linux_resource("vdso_data") {
src = ":vdso_lib($bionic_linux_toolchain)"
bin_output_name = "vdso_lib.so"
dest = "data/libvdso.so"
if (target_cpu == "arm64") {
arch32_src = ":vdso_lib"
arch32_bin_output_name = "vdso_lib.so"
arch32_dest = "data/libvdso_arch32.so"
}
}`,
},
{
name: "linkshared fx_cc_binary named with .so suffix",
bazel: `fx_cc_binary(
name = "libvdso.so",
srcs = ["vdso.cc"],
linkshared = True,
)
linux_resource(
name = "vdso_data",
src = ":libvdso.so",
dest = "data/libvdso.so",
)`,
wantGN: `if (is_linux && !is_host) {
loadable_module("libvdso.so") {
output_name = "libvdso"
sources = [
"vdso.cc",
]
}
}
linux_resource("vdso_data") {
src = ":libvdso.so"
dest = "data/libvdso.so"
}`,
},
{
name: "standalone arch32 linux_resource and arm constraint on binary",
bazel: `fx_cc_binary(
name = "bin32_bin",
srcs = ["hello_debian32.S"],
target_compatible_with = ["@platforms//cpu:arm"],
)
linux_resource(
name = "bin32",
arch32_src = ":bin32_bin",
arch32_dest = "data/tests/hello_debian32_asm",
target_compatible_with = ["@platforms//cpu:arm64"],
)`,
wantGN: `if (is_linux && !is_host && current_cpu == "arm") {
executable("bin32_bin") {
sources = [
"hello_debian32.S",
]
}
}
if (current_cpu == "arm64") {
linux_resource("bin32") {
arch32_src = ":bin32_bin"
arch32_dest = "data/tests/hello_debian32_asm"
}
}`,
},
{
name: "binary shared with non-linux_resource consumer is not auto-wrapped in is_linux",
bazel: `rustc_binary(
name = "shared_bin",
srcs = ["src/main.rs"],
edition = "2024",
with_unit_tests = True,
)
linux_resource(
name = "shared_linux_res",
src = ":shared_bin",
dest = "data/shared_bin",
)`,
wantGN: `rustc_binary("shared_bin") {
sources = [
"src/main.rs",
]
edition = "2024"
with_unit_tests = true
}
linux_resource("shared_linux_res") {
src = ":shared_bin"
dest = "data/shared_bin"
}`,
},
{
name: "same-file helper visibility allowlisting linux binary does not prevent wrapping",
bazel: `fx_cc_library(
name = "helper",
srcs = ["helper.cc"],
visibility = [":linux_bin"],
)
fx_cc_binary(
name = "linux_bin",
srcs = ["main.cc"],
deps = [":helper"],
)
linux_resource(
name = "linux_res",
src = ":linux_bin",
dest = "data/linux_bin",
)`,
wantGN: `static_library("helper") {
sources = [
"helper.cc",
]
visibility = [
":*",
":linux_bin",
]
}
if (is_linux && !is_host) {
executable("linux_bin") {
sources = [
"main.cc",
]
deps = [
":helper",
]
}
}
linux_resource("linux_res") {
src = ":linux_bin"
dest = "data/linux_bin"
}`,
},
{
name: "fx_cc_test_binary with custom gn_output_name and linux_resource",
bazel: `fx_cc_test_binary(
name = "syscall_test_bin",
testonly = True,
srcs = ["syscall_test.cc"],
disable_build_flags = ["//build/config/linux:implicit-host-libs"],
gn_output_name = "custom_syscall_test",
deps = ["//third_party/googletest:gtest_main"],
)
linux_resource(
name = "syscall_test_res",
testonly = True,
src = ":syscall_test_bin",
dest = "data/tests/custom_syscall_test",
)`,
wantGN: `if (is_linux && !is_host) {
cc_test_executable("syscall_test_bin") {
sources = [
"syscall_test.cc",
]
configs -= [
"//build/config/linux:implicit-host-libs",
]
output_name = "custom_syscall_test"
deps = [
"//third_party/googletest:gtest_main",
]
testonly = true
emit_tool_path = false
}
}
linux_resource("syscall_test_res") {
testonly = true
src = ":syscall_test_bin"
bin_output_name = "custom_syscall_test"
dest = "data/tests/custom_syscall_test"
}`,
},
} {
t.Run(tc.name, func(t *testing.T) {
f := toSyntaxFile(t, tc.bazel)
gotLines, err := bazel2gn.FileToGN(f)
if err != nil {
t.Fatalf("Unexpected error converting Bazel file: %v", err)
}
gotGN := strings.Join(gotLines, "\n")
if diff := cmp.Diff(tc.wantGN, gotGN); diff != "" {
t.Errorf("Diff found afterGN conversion (-want +got):\n%s\nBazel source:\n%s", diff, tc.bazel)
}
})
}
}
func TestLinuxResourceErrors(t *testing.T) {
for _, tc := range []struct {
name string
bazel string
}{
{
name: "missing name",
bazel: `linux_resource(
src = ":bin",
dest = "data/bin",
)`,
},
{
name: "missing src and arch32_src",
bazel: `linux_resource(
name = "res",
)`,
},
{
name: "missing dest when src is set",
bazel: `linux_resource(
name = "res",
src = ":bin",
)`,
},
{
name: "empty dest",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "",
)`,
},
{
name: "leading slash in dest",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "/data/bin",
)`,
},
{
name: "dest without src",
bazel: `linux_resource(
name = "res",
dest = "data/bin",
arch32_src = ":bin32",
arch32_dest = "data/bin32",
)`,
},
{
name: "missing arch32_dest when arch32_src is set",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "data/bin",
arch32_src = select({
"@platforms//cpu:arm64": ":bin32",
"//conditions:default": None,
}),
)`,
},
{
name: "leading slash in arch32_dest",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "data/bin",
arch32_src = ":bin32",
arch32_dest = "/data/bin32",
)`,
},
{
name: "arch32_dest without arch32_src",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "data/bin",
arch32_dest = "data/bin32",
)`,
},
{
name: "unsupported condition in arch32_src select",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "data/bin",
arch32_src = select({
"@platforms//cpu:x86_64": ":bin32",
"//conditions:default": None,
}),
arch32_dest = "data/bin32",
)`,
},
{
name: "non-None default in arch32_src select",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "data/bin",
arch32_src = select({
"@platforms//cpu:arm64": ":bin32",
"//conditions:default": ":other_bin",
}),
arch32_dest = "data/bin32",
)`,
},
{
name: "unsupported attribute on linux_resource",
bazel: `linux_resource(
name = "res",
src = ":bin",
dest = "data/bin",
deps = [":other"],
)`,
},
{
name: "raw_attribute on linux_resource",
bazel: `# @bazel2gn:raw_attribute:allow_binary_output_dir = true
linux_resource(
name = "res",
src = ":bin",
dest = "data/bin",
)`,
},
{
name: "non-boolean linkshared on fx_cc_binary",
bazel: `fx_cc_binary(
name = "lib",
srcs = ["lib.cc"],
linkshared = select({
"@platforms//os:linux": True,
"//conditions:default": False,
}),
)`,
},
} {
t.Run(tc.name, func(t *testing.T) {
f := toSyntaxFile(t, tc.bazel)
if _, err := bazel2gn.FileToGN(f); err == nil {
t.Fatalf("Expected error converting invalid Bazel file, got nil:\n%s", tc.bazel)
}
})
}
}