| # Copyright 2025 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. | 
 |  | 
 | load("//bazel/private:repo_rule_python.bzl", "COMMON_PY_REPO_RULE_ATTRS", "get_python") | 
 |  | 
 | _COMMON_KCONFIG_TREE_ATTRS = { | 
 |     "zephyr_root": attr.label( | 
 |         mandatory = True, | 
 |         allow_single_file = True, | 
 |     ), | 
 |     "board_name": attr.string( | 
 |         mandatory = True, | 
 |     ), | 
 |     "board_qualifiers": attr.string(), | 
 | } | COMMON_PY_REPO_RULE_ATTRS | 
 |  | 
 | _COMMON_CONF_FILE_ATTRS = { | 
 |     "conf_file": attr.label( | 
 |         mandatory = True, | 
 |         allow_single_file = True, | 
 |     ), | 
 |     "extra_conf_files": attr.label_list(), | 
 | } | 
 |  | 
 | def _construct_environment(repo_ctx): | 
 |     srctree = str(repo_ctx.path(repo_ctx.attr.zephyr_root).dirname) | 
 |  | 
 |     # Directories to hold outputs of kconfig_gen. | 
 |     kconfig_binary_dirname = "Kconfig" | 
 |     kconfig_board_dirname = "Kconfig/boards" | 
 |     # Zephyr 4.3 added an "env file" in the Kconfig tree. This is for post-build | 
 |     # metadata generation but is unconditionally sourced. We create an empty env | 
 |     # file here since we don't care about post-build metadata. | 
 |     kconfig_env_filename = "kconfig_module_dirs.env" | 
 |     repo_ctx.file(kconfig_env_filename) | 
 |  | 
 |     environment = { | 
 |         "srctree": srctree, | 
 |         "ZEPHYR_BASE": srctree, | 
 |         "KCONFIG_BINARY_DIR": str(repo_ctx.path(kconfig_binary_dirname)), | 
 |         "KCONFIG_BOARD_DIR": str(repo_ctx.path(kconfig_board_dirname)), | 
 |         "KCONFIG_ENV_FILE": str(repo_ctx.path(kconfig_env_filename)), | 
 |         "BOARD": repo_ctx.attr.board_name, | 
 |         "BOARD_QUALIFIERS": repo_ctx.attr.board_qualifiers, | 
 |         "ARCH_DIR": srctree + "/arch", | 
 |         "HWM_SCHEME": "v2", | 
 |     } | 
 |     return environment | 
 |  | 
 | def _run_kconfig_gen(repo_ctx, python, environment): | 
 |     # Generate Kconfig snippets pointing to soc and arch directories. | 
 |     kconfig_gen_script_path = repo_ctx.path(Label("//scripts/build:kconfig_gen.py")) | 
 |     repo_ctx.watch(kconfig_gen_script_path) | 
 |     srctree = environment["srctree"] | 
 |     result = python.execute( | 
 |         [ | 
 |             kconfig_gen_script_path, | 
 |             "--arch-root", | 
 |             srctree, | 
 |             "--soc-root", | 
 |             srctree, | 
 |         ], | 
 |         environment=environment, | 
 |     ) | 
 |     if result.return_code != 0: | 
 |         fail("Failed to run kconfig_gen (%d):\n%s" % (result.return_code, result.stderr)) | 
 |  | 
 | def _write_configs_dot_c(repo_ctx, python, environment): | 
 |     # Generate configs.c | 
 |     # Only applies to project-specific configs repository. | 
 |     configs_c_filename = "generated/configs.c" | 
 |     repo_ctx.file(configs_c_filename) | 
 |     generate_configs_dot_c_script_path = repo_ctx.path( | 
 |         Label("//scripts/build:generate_configs_dot_c.py")) | 
 |     repo_ctx.watch(generate_configs_dot_c_script_path) | 
 |     args = [ | 
 |         generate_configs_dot_c_script_path, | 
 |         "--autoconf", | 
 |         repo_ctx.path("generated/zephyr/autoconf.h"), | 
 |         "-o", | 
 |         repo_ctx.path(configs_c_filename), | 
 |     ] | 
 |     result = python.execute( | 
 |         args, | 
 |         environment=environment, | 
 |     ) | 
 |     if result.return_code != 0: | 
 |         fail("Failed to generate configs.c from autoconf.h (%d):\n%s" % (result.return_code, result.stderr)) | 
 |  | 
 | def _write_build_file(repo_ctx): | 
 |     # Declare the autoconf cc library | 
 |     # Only applies to project-specific configs repository. | 
 |     repo_ctx.file( | 
 |         "BUILD.bazel", | 
 |         content= | 
 | """package(default_visibility = ["//visibility:public"]) | 
 |  | 
 | cc_library( | 
 |     name = "autoconf", | 
 |     hdrs = ["generated/zephyr/autoconf.h"], | 
 |     includes = ["generated"], | 
 | ) | 
 |  | 
 | filegroup( | 
 |     name = "configs_file", | 
 |     srcs = ["generated/configs.c"], | 
 | ) | 
 | """,) | 
 |  | 
 | def _gen_kconfiglib_impl(repo_ctx): | 
 |     environment = _construct_environment(repo_ctx) | 
 |     srctree = environment["srctree"] | 
 |     python = get_python( | 
 |         repo_ctx, | 
 |         extra_import_paths=[ | 
 |             # Provide paths to kconfiglib and kconfigfunctions.py. | 
 |             srctree + "/scripts/kconfig", | 
 |         ], | 
 |     ) | 
 |     _run_kconfig_gen(repo_ctx, python, environment) | 
 |  | 
 |     # Parse the Kconfig tree. | 
 |     build_file_name = "BUILD.bazel" | 
 |     build_file_path = repo_ctx.path(build_file_name) | 
 |     repo_ctx.file(build_file_name) | 
 |     bazel_config_script_path = repo_ctx.path(Label("//scripts/build:bazel_config.py")) | 
 |     repo_ctx.watch(bazel_config_script_path) | 
 |     args = [ | 
 |         bazel_config_script_path, | 
 |         "--kconfig", | 
 |         srctree + "/Kconfig", | 
 |         "gen_kconfig", | 
 |         "-o", | 
 |         build_file_path, | 
 |     ] | 
 |     result = python.execute( | 
 |         args, | 
 |         environment=environment, | 
 |     ) | 
 |     if result.return_code != 0: | 
 |         fail("Failed to generate kconfig BUILD file (%d):\n%s" % (result.return_code, result.stderr)) | 
 |  | 
 | gen_kconfiglib = repository_rule( | 
 |     implementation = _gen_kconfiglib_impl, | 
 |     attrs = _COMMON_KCONFIG_TREE_ATTRS, | 
 | ) | 
 |  | 
 | def _gen_projectlib_impl(repo_ctx, python, environment): | 
 |     environment = _construct_environment(repo_ctx) | 
 |     srctree = environment["srctree"] | 
 |     python = get_python( | 
 |         repo_ctx, | 
 |         extra_import_paths=[ | 
 |             # Provide paths to kconfiglib and kconfigfunctions.py. | 
 |             srctree + "/scripts/kconfig", | 
 |         ], | 
 |     ) | 
 |     _run_kconfig_gen(repo_ctx, environment) | 
 |  | 
 |     # Parse the Kconfig tree. | 
 |     bazel_config_script_path = repo_ctx.path(Label("//scripts/build:bazel_config.py")) | 
 |     repo_ctx.watch(bazel_config_script_path) | 
 |     bzl_file_name = "kconfig_flag_values.bzl" | 
 |     bzl_file_path = repo_ctx.path(bzl_file_name) | 
 |     repo_ctx.file(bzl_file_name) | 
 |     args = [ | 
 |         bazel_config_script_path, | 
 |         "--kconfig", | 
 |         srctree + "/Kconfig", | 
 |         "gen_project", | 
 |         "--project", | 
 |         str(repo_ctx.path(repo_ctx.attr.conf_file)), | 
 |         "-o", | 
 |         bzl_file_path, | 
 |         "--extra_configs_in", | 
 |     ] | 
 |     for config in repo_ctx.attr.extra_conf_files: | 
 |         args.append(repo_ctx.path(config)) | 
 |     repo_ctx.file("generated/zephyr/autoconf.h") | 
 |     result = python.execute( | 
 |         args, | 
 |         environment=environment, | 
 |     ) | 
 |     if result.return_code != 0: | 
 |         fail("Failed to generate kconfig BUILD file (%d):\n%s" % (result.return_code, result.stderr)) | 
 |  | 
 |     _write_configs_dot_c(repo_ctx) | 
 |     _write_build_file(repo_ctx) | 
 |  | 
 | gen_projectlib = repository_rule( | 
 |     implementation = _gen_projectlib_impl, | 
 |     attrs = _COMMON_KCONFIG_TREE_ATTRS | _COMMON_CONF_FILE_ATTRS, | 
 | ) | 
 |  | 
 | def _kconfig_impl(module_ctx): | 
 |     if not module_ctx.modules[0].tags.tree: | 
 |         fail( | 
 |             msg = "kconfig requires inputs via the `tree` tag", | 
 |         ) | 
 |     if len(module_ctx.modules[0].tags.tree) > 1: | 
 |         fail( | 
 |             msg = "kconfig only supports a single Kconfig tree", | 
 |         ) | 
 |  | 
 |     zephyr_root = module_ctx.modules[0].tags.tree[0].zephyr_root | 
 |     board_name = module_ctx.modules[0].tags.tree[0].board_name | 
 |     board_qualifiers = module_ctx.modules[0].tags.tree[0].board_qualifiers | 
 |  | 
 |     # Generate a repository to hold all the flags parsed from Kconfig symbols. | 
 |     # These flags don't have values yet. | 
 |     gen_kconfiglib( | 
 |         name = "kconfig", | 
 |         zephyr_root = zephyr_root, | 
 |         board_name = board_name, | 
 |         board_qualifiers = board_qualifiers, | 
 |     ) | 
 |  | 
 |     # Generate one project configs repository for each project. | 
 |     for project in module_ctx.modules[0].tags.project: | 
 |         # Each project must have one prj.conf file and may have zero or more | 
 |         # extra conf files. | 
 |         conf_file = project.conf_file | 
 |         extra_conf_files = project.extra_conf_files | 
 |         # Tell the user the namespace of the generated flags. | 
 |         print("Generating Kconfig-Bazel flags in repository: " + project.name) | 
 |         gen_projectlib( | 
 |             name = project.name, | 
 |             zephyr_root = zephyr_root, | 
 |             board_name = board_name, | 
 |             board_qualifiers = board_qualifiers, | 
 |             conf_file = conf_file, | 
 |             extra_conf_files = extra_conf_files, | 
 |         ) | 
 |  | 
 | _kconfig_tree = tag_class( | 
 |     attrs = _COMMON_KCONFIG_TREE_ATTRS, | 
 | ) | 
 | _kconfig_project = tag_class( | 
 |     attrs = { | 
 |         "name": attr.string( | 
 |             mandatory = True, | 
 |         ), | 
 |     } | _COMMON_CONF_FILE_ATTRS, | 
 | ) | 
 |  | 
 | kconfig = module_extension( | 
 |     doc = """kconfig parses a Kconfig tree and generates a repository | 
 | containing Bazel config settings, each corresponding to a Kconfig symbol. | 
 | These config settings can be used with select() to conditionally compile | 
 | sources within Zephyr. Also generates a repository for each configured | 
 | project containing the config setting values parsed from prj.conf.""", | 
 |     implementation = _kconfig_impl, | 
 |     tag_classes = { | 
 |         "tree": _kconfig_tree, | 
 |         "project": _kconfig_project, | 
 |     }, | 
 | ) |