| # 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. |
| |
| _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_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) |
| |
| # Provide paths to kconfiglib and kconfigfunctions.py. |
| pythonpath = repo_ctx.getenv("PYTHONPATH") |
| pythonpath = pythonpath + ":" + srctree + "/scripts/kconfig" |
| |
| # 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, |
| "PYTHONPATH": pythonpath, |
| "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, 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 = repo_ctx.execute( |
| [ |
| repo_ctx.which("python3"), |
| 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): |
| # 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 = [ |
| repo_ctx.which("python3"), |
| generate_configs_dot_c_script_path, |
| "--autoconf", |
| repo_ctx.path("generated/zephyr/autoconf.h"), |
| "-o", |
| repo_ctx.path(configs_c_filename), |
| ] |
| result = repo_ctx.execute(args) |
| 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) |
| _run_kconfig_gen(repo_ctx, 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) |
| srctree = environment["srctree"] |
| args = [ |
| repo_ctx.which("python3"), |
| bazel_config_script_path, |
| "--kconfig", |
| srctree + "/Kconfig", |
| "gen_kconfig", |
| "-o", |
| build_file_path, |
| ] |
| result = repo_ctx.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): |
| environment = _construct_environment(repo_ctx) |
| _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) |
| srctree = environment["srctree"] |
| args = [ |
| repo_ctx.which("python3"), |
| 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 = repo_ctx.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, |
| }, |
| ) |