| # 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. |
| """Repository rule for app-board specific Zephyr configuration. (forced update)""" |
| |
| load("//:naming.bzl", "normalize_app_label") |
| load("//bazel/private:kconfig_args.bzl", "get_kconfig_args") |
| load("//bazel/private:repo_rule_python.bzl", "COMMON_PY_REPO_RULE_ATTRS", "get_python") |
| |
| def _gen_zephyr_config_impl(rctx): |
| rctx.watch(rctx.attr.state_file) |
| state_file = rctx.path(rctx.attr.state_file) |
| state = json.decode(rctx.read(state_file)) |
| |
| zephyr_root_path = str(rctx.path(Label(state["zephyr_root"])).dirname) |
| |
| # Robust normalization: strip repo and redundant target name |
| norm_app = normalize_app_label(rctx.attr.app_label) |
| |
| app_dir_label = state["apps"].get(norm_app) |
| if not app_dir_label: |
| fail("App %s (normalized: %s) not found in discovery index. Discovered: %s" % (rctx.attr.app_label, norm_app, state["apps"].keys())) |
| app_dir_path = str(rctx.path(Label(app_dir_label)).dirname) |
| |
| board_dir_label = state["board_index"].get(rctx.attr.board_id) |
| if not board_dir_label: |
| fail("Board %s not found in discovery index" % rctx.attr.board_id) |
| board_dir_path = str(rctx.path(Label(board_dir_label)).dirname) |
| |
| resolved_oot_dts_roots = [str(rctx.path(Label(l)).dirname) for l in state.get("oot_dts_roots", [])] |
| |
| resolved_modules = [] |
| for m_label_str in state["modules"].values(): |
| m_label = Label(m_label_str) |
| m_path = rctx.path(m_label) |
| if m_path.basename == "module.yml" and m_path.dirname.basename == "zephyr": |
| mod_root = m_path.dirname.dirname |
| else: |
| mod_root = m_path.dirname |
| resolved_modules.append(str(mod_root)) |
| |
| kconfig_gen_values_py = rctx.path(rctx.attr._kconfig_gen_values_py) |
| |
| kconfig_utils_py = rctx.path(Label("//scripts/build:kconfig_utils.py")) |
| rctx.watch(kconfig_utils_py) |
| |
| python = get_python(rctx, extra_import_paths = [ |
| zephyr_root_path + "/scripts/kconfig", |
| str(kconfig_gen_values_py.dirname), |
| ]) |
| rctx.watch(kconfig_gen_values_py) |
| rctx.watch(rctx.path(Label("//scripts/build:kconfig_utils.py"))) |
| |
| # Watch application configuration files |
| if app_dir_path: |
| # Recursively watch the app directory to catch created configuration files. |
| rctx.watch_tree(rctx.path(app_dir_path)) |
| |
| if board_dir_path: |
| rctx.watch_tree(rctx.path(board_dir_path)) |
| |
| resolved_conf_fragments = [] |
| for fragment in rctx.attr.conf_fragments: |
| if not fragment.startswith("/"): |
| fragment_path = rctx.path(str(rctx.workspace_root) + "/" + fragment) |
| else: |
| fragment_path = rctx.path(fragment) |
| rctx.watch(fragment_path) |
| resolved_conf_fragments.append(str(fragment_path)) |
| |
| if rctx.attr.extra_kconfig: |
| rctx.file("sysbuild_generated.conf", rctx.attr.extra_kconfig) |
| resolved_conf_fragments.append(str(rctx.path("sysbuild_generated.conf"))) |
| |
| args = get_kconfig_args( |
| script = kconfig_gen_values_py, |
| zephyr_root = zephyr_root_path, |
| modules = resolved_modules, |
| app_dir = app_dir_path, |
| board = rctx.attr.board_id, |
| board_dir = board_dir_path, |
| parent_platform = rctx.attr.parent_platform, |
| output_dir = ".", |
| app_name = rctx.attr.app_label.name, |
| oot_dts_roots = resolved_oot_dts_roots, |
| conf_fragments = resolved_conf_fragments, |
| ) |
| |
| result = python.execute(args) |
| if result.return_code != 0: |
| fail("Zephyr config generation failed: %s" % result.stderr) |
| |
| # We need to replace @zc_target with the actual repo name in BUILD.bazel |
| # since the script doesn't know it. |
| build_bazel = rctx.read("BUILD.bazel") |
| build_bazel = build_bazel.replace("@zc_target", "@@" + rctx.name) |
| rctx.file("BUILD.bazel", build_bazel) |
| |
| gen_zephyr_config = repository_rule( |
| implementation = _gen_zephyr_config_impl, |
| attrs = { |
| "app_label": attr.label(mandatory = True), |
| "board_id": attr.string(mandatory = True), |
| "parent_platform": attr.label(mandatory = True), |
| "state_file": attr.label(mandatory = True), |
| "conf_fragments": attr.string_list(), |
| "extra_kconfig": attr.string(), |
| "_kconfig_gen_values_py": attr.label(default = "//scripts/build:kconfig_gen_values.py"), |
| } | COMMON_PY_REPO_RULE_ATTRS, |
| ) |