blob: 7128a5a534400b378e17593a84fee2420e8e0617 [file]
# Copyright 2026 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Bazel rules for standard header-based versioned enums."""
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
# This need to be disabled until the `module_name` variable is properly
# set by create_compile_variables().
_UNSUPPORTED_FEATURES = ["module_maps", "use_header_modules"]
def _pw_cc_enum_generator_impl(ctx):
internal_lib = ctx.attr.internal_lib
comp_context = internal_lib[CcInfo].compilation_context
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features + _UNSUPPORTED_FEATURES,
)
cc_compiler_path = cc_common.get_tool_for_action(
feature_configuration = feature_configuration,
action_name = ACTION_NAMES.cpp_compile,
)
base_cc = ctx.file.base_cc
# Use the placeholder C++ source file to create compilation variables,
# ensuring the exact compilation command is generated.
cc_compile_variables = cc_common.create_compile_variables(
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
source_file = base_cc.path,
user_compile_flags = ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts,
include_directories = comp_context.includes,
quote_include_directories = comp_context.quote_includes,
system_include_directories = comp_context.system_includes,
framework_include_directories = comp_context.framework_includes,
preprocessor_defines = depset(transitive = [
comp_context.defines,
comp_context.local_defines,
]),
)
cc_compile_flags = cc_common.get_memory_inefficient_command_line(
feature_configuration = feature_configuration,
action_name = ACTION_NAMES.cpp_compile,
variables = cc_compile_variables,
)
flags_file = ctx.actions.declare_file(ctx.label.name + "_flags.txt")
ctx.actions.write(
output = flags_file,
content = "\n".join(cc_compile_flags),
)
out_files = []
for h in ctx.files.hdrs:
out_file = ctx.actions.declare_file(h.basename, sibling = h)
out_files.append(out_file)
args = ctx.actions.args()
args.add_all(ctx.files.hdrs)
args.add("--outputs")
args.add_all(out_files)
args.add("--compiler")
args.add(cc_compiler_path)
args.add("--compiler-flags")
args.add(flags_file)
args.add("--base-cc")
args.add(base_cc.path)
ctx.actions.run(
inputs = depset(
[flags_file, base_cc] + ctx.files.hdrs,
transitive = [comp_context.headers, cc_toolchain.all_files],
),
outputs = out_files,
executable = ctx.executable._generator,
arguments = [args],
mnemonic = "PwCcEnumGen",
)
out_path = ctx.bin_dir.path
if ctx.label.workspace_root:
out_path += "/" + ctx.label.workspace_root
if ctx.label.package:
out_path += "/" + ctx.label.package
my_includes = [out_path]
if ctx.attr.strip_include_prefix:
prefix = ctx.attr.strip_include_prefix
if prefix.startswith("/"):
prefix = prefix[1:]
my_includes.append(out_path + "/" + prefix)
new_comp_context = cc_common.create_compilation_context(
headers = depset(out_files),
includes = depset(my_includes),
)
my_cc_info = CcInfo(compilation_context = new_comp_context)
# Merge with dependencies to ensure transitive includes are propagated
return [
cc_common.merge_cc_infos(cc_infos = [my_cc_info] + [dep[CcInfo] for dep in ctx.attr.deps]),
DefaultInfo(files = depset(out_files)),
]
_pw_cc_enum_generator = rule(
implementation = _pw_cc_enum_generator_impl,
attrs = {
"base_cc": attr.label(allow_single_file = [".cc"]),
"deps": attr.label_list(providers = [CcInfo]),
"hdrs": attr.label_list(allow_files = [".h"]),
"internal_lib": attr.label(providers = [CcInfo]),
"strip_include_prefix": attr.string(),
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
"_generator": attr.label(
default = Label("//pw_enum/py:enum_generator"),
executable = True,
cfg = "exec",
),
},
toolchains = use_cpp_toolchain(),
fragments = ["cpp"],
)
def pw_cc_enum_library(
name,
enum_headers,
hdrs = [],
srcs = [],
deps = [],
strip_include_prefix = None,
visibility = None,
**kwargs):
"""Defines a C++ library that automatically tokenizes versioned enums.
Args:
name: The name of the cc_library target.
enum_headers: Standard .h files containing enum definitions to tokenize (required).
hdrs: Public headers of the library.
srcs: Source files of the library.
deps: Dependencies.
strip_include_prefix: Prefix to strip from the include path of generated headers.
visibility: Visibility of the target.
**kwargs: Additional arguments passed to the underlying rule.
"""
testonly = kwargs.get("testonly", False)
native.genrule(
name = name + ".base_cc_gen",
outs = [name + ".base.cc"],
cmd = "echo '// placeholder' > $@",
visibility = ["//visibility:private"],
testonly = testonly,
)
cc_library(
name = name + ".base_lib",
srcs = [":" + name + ".base_cc_gen"],
hdrs = enum_headers + hdrs,
deps = [
"//pw_enum:_generate_internal_do_not_use",
"//pw_log:args",
] + deps,
strip_include_prefix = strip_include_prefix,
visibility = ["//visibility:private"],
testonly = testonly,
tags = ["no-clang-tidy-headers", "manual"],
)
_pw_cc_enum_generator(
name = name + ".generate",
internal_lib = ":" + name + ".base_lib",
base_cc = ":" + name + ".base_cc_gen",
hdrs = enum_headers,
deps = deps + ["//pw_enum:_generate_internal_do_not_use"],
strip_include_prefix = strip_include_prefix,
visibility = ["//visibility:private"],
testonly = testonly,
tags = ["no-clang-tidy-headers"],
)
cc_library(
name = name,
srcs = srcs,
hdrs = [":" + name + ".generate"] + hdrs,
strip_include_prefix = strip_include_prefix,
deps = [
"//pw_enum:_generate_internal_do_not_use",
"//pw_log:args",
"//pw_tokenizer",
] + deps,
visibility = visibility,
**kwargs
)