blob: f7f77db3b60a2f6af2d345783672dc64c1ddc0ca [file]
#!/usr/bin/env fuchsia-vendored-python
# 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.
import json
import tempfile
import unittest
from pathlib import Path
from build.bazel.scripts.bazel_build_flags import (
DefaultBuildFlagsMap,
DefaultBuildFlagsSet,
get_build_flags_targets_from_ast,
)
class DefaultBuildFlagsTest(unittest.TestCase):
def setUp(self) -> None:
self._td = tempfile.TemporaryDirectory()
self.fuchsia_dir = Path(self._td.name) / "fuchsia"
self.fuchsia_dir.mkdir()
self.build_dir = Path(self._td.name) / "build_dir"
self.build_dir.mkdir()
# Create the sub-directories for configs
self.default_configs_dir = self.build_dir / "bazel_default_configs"
self.default_configs_dir.mkdir()
def tearDown(self) -> None:
self._td.cleanup()
def test_new_from_gn_config_filtering_and_resolution(self) -> None:
# 1. Setup mock JSON files in build directory
fuchsia_json = {
"cxx_common": [
"//build/config:compiler",
"//build/config:unknown_config",
],
"cxx_executable_extra": ["//build/config:executable_config"],
"cxx_shared_library_extra": [
"//build/config:shared_library_config"
],
"rust_common": [
"//build/config:rust_compiler",
"//build/config:unknown_rust_config",
],
}
host_json = {
"cxx_common": ["//build/config:compiler"],
"cxx_executable_extra": [],
"cxx_shared_library_extra": [],
"rust_common": ["//build/config:rust_compiler"],
"rust_executable_extra": [],
"rust_shared_library_extra": [],
}
with (self.default_configs_dir / "fuchsia.json").open("w") as f:
json.dump(fuchsia_json, f)
with (self.default_configs_dir / "host.json").open("w") as f:
json.dump(host_json, f)
# 2. Setup mock BUILD.bazel files in fuchsia directory
# We will define 'compiler' and 'executable_config', but NOT 'shared_library_config' or 'unknown_config'.
build_config_dir = self.fuchsia_dir / "build" / "config"
build_config_dir.mkdir(parents=True)
build_bazel_content = """
compiler_build_flags(
name = "compiler",
cflags = ["-O2"],
)
build_flags(
name = "rust_compiler",
rustflags = ["-.."],
)
build_flags(
name = "executable_config",
)
"""
(build_config_dir / "BUILD.bazel").write_text(build_bazel_content)
# 3. Invoke parsing
build_flags_map = DefaultBuildFlagsMap.new_from_gn_config(
self.build_dir, self.fuchsia_dir
)
# 4. Verify results
self.assertIn("fuchsia", build_flags_map)
self.assertIn("host", build_flags_map)
fuchsia_set = build_flags_map["fuchsia"]
# 'compiler' exists, 'unknown_config' is filtered out
self.assertEqual(
fuchsia_set.cxx_common_build_flags, ["//build/config:compiler"]
)
# 'executable_config' exists
self.assertEqual(
fuchsia_set.cxx_executable_build_flags,
["//build/config:executable_config"],
)
# 'shared_library_config' does not exist in BUILD.bazel
self.assertEqual(fuchsia_set.cxx_shared_library_build_flags, [])
# 'rust_compiler' exists, 'unknown_rust_config' is filtered out
self.assertEqual(
fuchsia_set.rust_common_build_flags,
["//build/config:rust_compiler"],
)
self.assertEqual(fuchsia_set.rust_shared_library_build_flags, [])
self.assertEqual(fuchsia_set.rust_executable_build_flags, [])
# Host verification
host_set = build_flags_map["host"]
self.assertEqual(
host_set.cxx_common_build_flags, ["//build/config:compiler"]
)
self.assertEqual(host_set.cxx_executable_build_flags, [])
self.assertEqual(host_set.cxx_shared_library_build_flags, [])
self.assertEqual(
host_set.rust_common_build_flags, ["//build/config:rust_compiler"]
)
self.assertEqual(host_set.rust_executable_build_flags, [])
self.assertEqual(host_set.rust_shared_library_build_flags, [])
self.assertEqual(host_set.target_compatible_with, "HOST_OS_CONSTRAINTS")
# Check missing configs recorded
expected_missing = [
"//build/config:shared_library_config",
"//build/config:unknown_config",
"//build/config:unknown_rust_config",
]
self.assertEqual(build_flags_map.missing_configs, expected_missing)
def test_syntax_error_in_build_bazel_raises(self) -> None:
fuchsia_json = {
"cxx_common": ["//build/config:compiler"],
}
host_json: dict[str, list[str]] = {
"cxx_common": [],
}
with (self.default_configs_dir / "fuchsia.json").open("w") as f:
json.dump(fuchsia_json, f)
with (self.default_configs_dir / "host.json").open("w") as f:
json.dump(host_json, f)
build_config_dir = self.fuchsia_dir / "build" / "config"
build_config_dir.mkdir(parents=True)
# Missing comma after cflags
invalid_build_bazel = """
compiler_build_flags(
name = "compiler",
cflags = ["-O2"]
visibility = ["//visibility:public"],
)
"""
(build_config_dir / "BUILD.bazel").write_text(invalid_build_bazel)
with self.assertRaises(SyntaxError):
DefaultBuildFlagsMap.new_from_gn_config(
self.build_dir, self.fuchsia_dir
)
def test_generate_bazel_toolchain_definitions(self) -> None:
custom_map = DefaultBuildFlagsMap(
{
"target_a": DefaultBuildFlagsSet(
cxx_common_build_flags=[
"//build/flags:cxx_common1",
"//build/flags:cxx_common2",
],
cxx_executable_build_flags=["//build/flags:cxx_exec1"],
cxx_shared_library_build_flags=["//build/flags:cxx_shlib1"],
rust_common_build_flags=["//build/flags:rust_common1"],
rust_executable_build_flags=["//build/flags:rust_exec1"],
rust_shared_library_build_flags=[
"//build/flags:rust_shlib1"
],
target_compatible_with='["@platforms//os:target_a_os"]',
),
"target_b": DefaultBuildFlagsSet(
target_compatible_with='["@platforms//os:target_b_os"]',
),
}
)
custom_map.missing_configs = [
"//build/flags:missing1",
"//build/flags:missing2",
]
generated_content = custom_map.generate_bazel_toolchain_definitions()
# Check targets generated
self.assertIn(
"""\
build_flags_toolchain_instance(
name = "target_a_default_build_flags",
cxx_common_build_flags = [
"@@//build/flags:cxx_common1",
"@@//build/flags:cxx_common2",
],
cxx_executable_build_flags = [
"@@//build/flags:cxx_exec1",
],
cxx_shared_library_build_flags = [
"@@//build/flags:cxx_shlib1",
],
rust_common_build_flags = [
"@@//build/flags:rust_common1",
],
rust_executable_build_flags = [
"@@//build/flags:rust_exec1",
],
rust_shared_library_build_flags = [
"@@//build/flags:rust_shlib1",
],
)""",
generated_content,
)
self.assertIn('name = "target_a_toolchain"', generated_content)
self.assertIn(
'target_compatible_with = ["@platforms//os:target_a_os"]',
generated_content,
)
self.assertIn(
'toolchain = ":target_a_default_build_flags"', generated_content
)
self.assertIn(
'name = "target_b_default_build_flags"', generated_content
)
self.assertIn("cxx_common_build_flags = []", generated_content)
self.assertIn("cxx_executable_build_flags = []", generated_content)
self.assertIn("cxx_shared_library_build_flags = []", generated_content)
self.assertIn("rust_common_build_flags = []", generated_content)
self.assertIn("rust_executable_build_flags = []", generated_content)
self.assertIn("rust_shared_library_build_flags = []", generated_content)
self.assertIn('name = "target_b_toolchain"', generated_content)
self.assertIn(
'target_compatible_with = ["@platforms//os:target_b_os"]',
generated_content,
)
# Check comments for missing configs at the bottom
self.assertIn("# - //build/flags:missing1", generated_content)
self.assertIn("# - //build/flags:missing2", generated_content)
def test_get_build_flags_targets_from_ast_custom_macros_and_comprehensions(
self,
) -> None:
build_bazel_content = """
riscv64_abi_build_flags(
name = "abi",
visibility = ["//visibility:public"],
)
riscv64_abi_build_flags(
name = "no-vector",
exclude_letters = ["v"],
)
extra_linker_flags(
name = "extra_link",
ldflags = ["-Wl,-z,now"],
)
[
compiler_build_flags(
name = v,
c_family_flags = ["-march=arm{}-a".format(v)],
)
for v in ["v9", "v9.1", "v9.2"]
]
[
custom_build_flags(name = t)
for t in ("tuple_flag_a", "tuple_flag_b")
]
# Unrelated rules and unbound variables should be ignored.
cc_library(
name = "ignored_library",
)
[
cc_library(name = lib)
for lib in ["ignored_comp_a", "ignored_comp_b"]
]
compiler_build_flags(
name = unknown_var,
)
"""
targets = get_build_flags_targets_from_ast(
build_bazel_content, Path("BUILD.bazel")
)
self.assertEqual(
targets,
{
"abi",
"extra_link",
"no-vector",
"v9",
"v9.1",
"v9.2",
"tuple_flag_a",
"tuple_flag_b",
},
)
if __name__ == "__main__":
unittest.main()