| # 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_GLOBAL_ATTRS = { |
| "oot_dts_roots": attr.label_list(), |
| "modules": attr.string_list( |
| doc = """Zephyr modules to enable for Kconfig purposes. A module's |
| Kconfig symbols are all set to false unless the module name is passed in here. |
| Listing a module here tells this module extension that this module is present. |
| The equivalent in cmake zephyr is west will enable all modules in west.yml.""", |
| ), |
| } |
| |
| _COMMON_KCONFIG_TREE_ATTRS = { |
| "zephyr_root": attr.label( |
| mandatory = True, |
| allow_single_file = True, |
| ), |
| "workspace_root": attr.label( |
| mandatory = True, |
| allow_single_file = True, |
| doc = """A label to a file in the workspace root, e.g. @//:MODULE.bazel, this sets |
| the root for paths to local Zephyr modules""", |
| ), |
| "boards": attr.string_list( |
| allow_empty = False, |
| doc = "All Zephyr boards used in the project including qualifiers", |
| ), |
| "oot_board_dir": attr.label(), |
| } | COMMON_PY_REPO_RULE_ATTRS |
| |
| _COMMON_CONF_FILE_ATTRS = { |
| "dir": attr.label( |
| mandatory = True, |
| allow_single_file = True, |
| ), |
| "extra_conf_files": attr.label_list(), |
| } |
| |
| def _gen_kconfiglib_impl(repo_ctx): |
| srctree = str(repo_ctx.path(repo_ctx.attr.zephyr_root).dirname) |
| python = get_python( |
| repo_ctx, |
| extra_import_paths = [ |
| # Provide paths to kconfiglib and kconfigfunctions.py. |
| srctree + "/scripts/kconfig", |
| ], |
| ) |
| |
| # Parse the Kconfig tree. |
| script_path = repo_ctx.path(Label("//scripts/build:kconfig_gen_repos.py")) |
| repo_ctx.watch_tree(script_path.dirname) |
| |
| workspace_root_path = repo_ctx.path(repo_ctx.attr.workspace_root).dirname |
| for module in repo_ctx.attr.modules: |
| if module.startswith("//"): |
| repo_ctx.watch_tree(workspace_root_path.get_child(module[2:])) |
| |
| args = [ |
| script_path, |
| "--zephyr_root", |
| srctree, |
| "--workspace_root", |
| str(repo_ctx.path(repo_ctx.attr.workspace_root).dirname), |
| ] |
| for module in repo_ctx.attr.modules: |
| args.append("--module") |
| args.append(module) |
| for board in repo_ctx.attr.boards: |
| args.append("--board") |
| args.append(board) |
| if repo_ctx.attr.oot_board_dir: |
| oot_board_dir_path = repo_ctx.path(repo_ctx.attr.oot_board_dir).dirname |
| repo_ctx.watch_tree(oot_board_dir_path) |
| args += [ |
| "--oot_board_dir", |
| str(oot_board_dir_path), |
| ] |
| for dts_root in repo_ctx.attr.oot_dts_roots: |
| oot_dts_root_path = repo_ctx.path(dts_root).dirname |
| repo_ctx.watch_tree(oot_dts_root_path) |
| args.append("--oot_dts_root") |
| args.append(str(oot_dts_root_path)) |
| args += [ |
| "tree", |
| "--output_dir", |
| repo_ctx.path("."), |
| ] |
| result = python.execute( |
| args, |
| ) |
| if result.return_code != 0: |
| fail("Failed to generate kconfig BUILD file (%d):\n%s\n%s" % (result.return_code, result.stdout, result.stderr)) |
| |
| gen_kconfiglib = repository_rule( |
| implementation = _gen_kconfiglib_impl, |
| attrs = _COMMON_GLOBAL_ATTRS | _COMMON_KCONFIG_TREE_ATTRS, |
| ) |
| |
| def _gen_projectlib_impl(repo_ctx): |
| srctree = str(repo_ctx.path(repo_ctx.attr.zephyr_root).dirname) |
| python = get_python( |
| repo_ctx, |
| extra_import_paths = [ |
| # Provide paths to kconfiglib and kconfigfunctions.py. |
| srctree + "/scripts/kconfig", |
| ], |
| ) |
| |
| # Invalidate generated stuff if conf file changed. |
| repo_ctx.watch(repo_ctx.attr.conf_file) |
| repo_ctx.watch(repo_ctx.attr.kconfig_tree) |
| for config in repo_ctx.attr.extra_conf_files: |
| repo_ctx.watch(config) |
| |
| # Invalidate generated stuff if app directory changes. |
| repo_ctx.watch_tree(repo_ctx.path(repo_ctx.attr.dir).dirname) |
| |
| # Invalidate generated stuff if app Kconfig or boards directory changes. |
| # We avoid watching the root directory to prevent circular dependencies |
| # with targets defined in the same package. |
| repo_ctx.watch(repo_ctx.attr.dir.relative(":Kconfig")) |
| repo_ctx.watch(repo_ctx.attr.dir.relative(":boards")) |
| |
| # Parse the Kconfig tree. |
| script_path = repo_ctx.path(Label("//scripts/build:kconfig_gen_repos.py")) |
| repo_ctx.watch_tree(script_path.dirname) |
| |
| workspace_root_path = repo_ctx.path(repo_ctx.attr.workspace_root).dirname |
| for module in repo_ctx.attr.modules: |
| if module.startswith("//"): |
| repo_ctx.watch_tree(workspace_root_path.get_child(module[2:])) |
| |
| args = [ |
| script_path, |
| "--zephyr_root", |
| srctree, |
| "--workspace_root", |
| str(repo_ctx.path(repo_ctx.attr.workspace_root).dirname), |
| "--board", |
| repo_ctx.attr.board, |
| ] |
| for module in repo_ctx.attr.modules: |
| args.append("--module") |
| args.append(module) |
| if repo_ctx.attr.oot_board_dir: |
| oot_board_dir_path = repo_ctx.path(repo_ctx.attr.oot_board_dir).dirname |
| repo_ctx.watch_tree(oot_board_dir_path) |
| args += [ |
| "--oot_board_dir", |
| str(oot_board_dir_path), |
| ] |
| for dts_root in repo_ctx.attr.oot_dts_roots: |
| oot_dts_root_path = repo_ctx.path(dts_root).dirname |
| repo_ctx.watch_tree(oot_dts_root_path) |
| args.append("--oot_dts_root") |
| args.append(str(oot_dts_root_path)) |
| args += [ |
| "project", |
| "--conf_file", |
| repo_ctx.attr.conf_file, |
| "--output_dir", |
| repo_ctx.path("."), |
| ] |
| if repo_ctx.attr.project_name: |
| args += ["--project_name", repo_ctx.attr.project_name] |
| if repo_ctx.attr.extra_conf_files: |
| args.append("--extra_conf_files") |
| for config in repo_ctx.attr.extra_conf_files: |
| args.append(str(repo_ctx.path(config))) |
| board_conf_file = repo_ctx.attr.dir.relative(":boards/{}.conf".format(repo_ctx.attr.board.replace("/", "_"))) |
| board_conf_file_path = repo_ctx.path(board_conf_file) |
| if board_conf_file_path.exists: |
| args.append("--board_conf_file") |
| args.append(board_conf_file_path) |
| result = python.execute( |
| args, |
| ) |
| if result.return_code != 0: |
| fail("Failed to generate project config files (%d):\n%s\n%s" % (result.return_code, result.stdout, result.stderr)) |
| |
| gen_projectlib = repository_rule( |
| implementation = _gen_projectlib_impl, |
| attrs = { |
| "board": attr.string( |
| mandatory = True, |
| doc = "Full Zephyr board name including qualifiers, e.g. board/core", |
| ), |
| "conf_file": attr.label( |
| mandatory = True, |
| allow_single_file = True, |
| ), |
| "project_name": attr.string(), |
| "kconfig_tree": attr.label( |
| doc = "Reference to the @kconfig repository generated by gen_kconfiglib.", |
| ), |
| } | _COMMON_GLOBAL_ATTRS | _COMMON_KCONFIG_TREE_ATTRS | _COMMON_CONF_FILE_ATTRS, |
| ) |
| |
| def _kconfig_impl(module_ctx): |
| root_module = module_ctx.modules[0] |
| for mod in module_ctx.modules: |
| if mod.is_root: |
| root_module = mod |
| break |
| |
| if not root_module.tags.tree: |
| fail( |
| msg = "kconfig requires inputs via the `tree` tag", |
| ) |
| if len(root_module.tags.tree) > 1: |
| fail( |
| msg = "kconfig only supports a single Kconfig tree", |
| ) |
| |
| zephyr_root = root_module.tags.tree[0].zephyr_root |
| boards = root_module.tags.tree[0].boards |
| oot_board_dir = root_module.tags.tree[0].oot_board_dir |
| workspace_root = root_module.tags.tree[0].workspace_root |
| |
| oot_dts_roots = [] |
| zephyr_modules = [] |
| if root_module.tags.global_setup: |
| oot_dts_roots = root_module.tags.global_setup[0].oot_dts_roots |
| zephyr_modules = root_module.tags.global_setup[0].modules |
| |
| # 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, |
| workspace_root = workspace_root, |
| boards = boards, |
| oot_board_dir = oot_board_dir, |
| oot_dts_roots = oot_dts_roots, |
| modules = zephyr_modules, |
| ) |
| |
| # Generate one project configs repository for each project board combination. |
| for project in root_module.tags.project: |
| # The conf_file for a project can be manually specified. If it is not, the prj.conf |
| # in the project directory is used. |
| conf_file = project.conf_file |
| if conf_file == None: |
| conf_file = project.dir.relative(":prj.conf") |
| for board in project.boards: |
| # Tell the user the namespace of the generated flags. |
| board_name = board.replace("/", "_") |
| repo_name = "{}_{}".format(project.name, board_name) |
| print("Generating Kconfig-Bazel flags in repository: " + repo_name) |
| extra_conf_files = list(project.extra_conf_files) |
| if board == "native_sim": |
| extra_conf_files.append(Label("//bazel_overlay/boards/native/native_sim:native_sim.conf")) |
| |
| gen_projectlib( |
| name = repo_name, |
| project_name = project.name, |
| dir = project.dir, |
| zephyr_root = zephyr_root, |
| workspace_root = workspace_root, |
| # May be different from the board in gen_kconfiglib(). |
| board = board, |
| oot_board_dir = oot_board_dir, |
| oot_dts_roots = oot_dts_roots, |
| conf_file = conf_file, |
| extra_conf_files = extra_conf_files, |
| modules = zephyr_modules, |
| kconfig_tree = "@kconfig//:BUILD.bazel", |
| ) |
| |
| _kconfig_global_setup = tag_class( |
| attrs = _COMMON_GLOBAL_ATTRS, |
| ) |
| |
| _kconfig_tree = tag_class( |
| attrs = _COMMON_KCONFIG_TREE_ATTRS, |
| ) |
| _kconfig_project = tag_class( |
| attrs = { |
| "name": attr.string( |
| mandatory = True, |
| ), |
| "boards": attr.string_list( |
| mandatory = True, |
| doc = "Full Zephyr board names including qualifiers, e.g. board/soc/core", |
| ), |
| "conf_file": attr.label( |
| allow_single_file = True, |
| doc = "Optional field to override using the default prj.conf config file.", |
| ), |
| } | _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 = { |
| "global_setup": _kconfig_global_setup, |
| "tree": _kconfig_tree, |
| "project": _kconfig_project, |
| }, |
| ) |