| # 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. |
| |
| import argparse |
| import pathlib |
| import sys |
| import kconfiglib |
| |
| |
| def write_kconfig_build_file( |
| ostream, |
| kconfig: kconfiglib.Kconfig, |
| ) -> None: |
| ostream.write("""load("@bazel_skylib//rules:common_settings.bzl", |
| "bool_flag", |
| "int_flag", |
| "string_flag", |
| ) |
| |
| package(default_visibility = ["//visibility:public"]) |
| """) |
| for sym_name, sym in kconfig.syms.items(): |
| if sym is None or sym.type == kconfiglib.UNKNOWN: |
| continue |
| for sym_str in str(sym).splitlines(): |
| ostream.write(f"# {sym_str}\n") |
| if sym.type == kconfiglib.INT or sym.type == kconfiglib.HEX: |
| ostream.write(f"""int_flag( |
| name = "CONFIG_{sym_name}", |
| build_setting_default=0, |
| visibility = ["//visibility:public"], |
| ) |
| """) |
| elif sym.type == kconfiglib.BOOL: |
| ostream.write(f"""bool_flag( |
| name = "CONFIG_{sym_name}", |
| build_setting_default=False, |
| visibility = ["//visibility:public"], |
| ) |
| config_setting( |
| name = "CONFIG_{sym_name}=true", |
| flag_values = {{ |
| ":CONFIG_{sym_name}": "true", |
| }}, |
| ) |
| """) |
| elif sym.type == kconfiglib.STRING: |
| ostream.write(f"""string_flag( |
| name = "CONFIG_{sym_name}", |
| build_setting_default="", |
| visibility = ["//visibility:public"], |
| ) |
| """) |
| |
| |
| def write_project_build_file( |
| ostream, |
| kconfig: kconfiglib.Kconfig, |
| ) -> None: |
| ostream.write("KCONFIG_FLAGS = [\n") |
| for sym in kconfig.unique_defined_syms: |
| if sym is None or sym.type == kconfiglib.UNKNOWN: |
| continue |
| |
| name = f"@kconfig//:CONFIG_{sym.name}" |
| if sym.type == kconfiglib.INT or sym.type == kconfiglib.HEX: |
| value = sym.str_value |
| if value == "": |
| value = "0" |
| elif sym.type == kconfiglib.BOOL: |
| value = "true" if sym.str_value == 'y' else "false" |
| elif sym.type == kconfiglib.STRING: |
| value = f'\\"{sym.str_value}\\"' |
| else: |
| raise RuntimeError(f"Unsupported symbol type: {sym.type}") |
| ostream.write(f" \"--{name}={value}\",\n") |
| ostream.write("]\n") |
| |
| |
| # Creates a list of options from Kconfig symbols without concerning their |
| # values in a project. |
| def generate_kconfig_build_file( |
| kconfig_path: pathlib.Path, |
| out: pathlib.Path | None, |
| ) -> None: |
| print("Loading Kconfig file: " + str(kconfig_path)) |
| kconfig = kconfiglib.Kconfig(filename=kconfig_path) |
| if out: |
| with open(out, "w", encoding="utf-8") as f: |
| write_kconfig_build_file(ostream=f, kconfig=kconfig) |
| else: |
| write_kconfig_build_file(ostream=sys.stdout, kconfig=kconfig) |
| |
| |
| def generate_project_build_file( |
| kconfig_path: pathlib.Path, |
| project_path: pathlib.Path, |
| extra_conf_files: list[pathlib.Path], |
| out: pathlib.Path | None, |
| ) -> None: |
| print("Loading Kconfig file: " + str(kconfig_path)) |
| kconfig = kconfiglib.Kconfig(filename=kconfig_path) |
| kconfig.load_config(filename=project_path) |
| for conf_file in extra_conf_files: |
| print(kconfig.load_config(conf_file, replace=False)) |
| if out: |
| with open(out, "w", encoding="utf-8") as f: |
| write_project_build_file(ostream=f, kconfig=kconfig) |
| else: |
| write_project_build_file(ostream=sys.stdout, kconfig=kconfig) |
| kconfig.write_autoconf("generated/zephyr/autoconf.h") |
| |
| |
| def main() -> None: |
| parser = argparse.ArgumentParser(description="Kconfig bazel port") |
| |
| parser.add_argument( |
| "--kconfig", |
| type=pathlib.Path, |
| help="Root Kconfig file to load", |
| required=True, |
| ) |
| |
| subparsers = parser.add_subparsers( |
| dest="subcommand", |
| required=True, |
| ) |
| |
| gen_kconfig_parser = subparsers.add_parser( |
| "gen_kconfig", |
| help="Generate generic kconfig BUILD", |
| ) |
| gen_kconfig_parser.add_argument( |
| "-o", |
| type=pathlib.Path, |
| help="Output file", |
| ) |
| |
| gen_project_parser = subparsers.add_parser( |
| "gen_project", |
| help="Generate BUILD file for a specific peroject", |
| ) |
| gen_project_parser.add_argument( |
| "--project", |
| type=pathlib.Path, |
| help="Project config file", |
| ) |
| gen_project_parser.add_argument( |
| "-o", |
| type=pathlib.Path, |
| help="Output file", |
| ) |
| gen_project_parser.add_argument( |
| "--extra_configs_in", |
| type=pathlib.Path, |
| nargs="*", |
| help="Input configuration fragments. Will be merged together.", |
| ) |
| |
| args = parser.parse_args() |
| |
| kconfig_path = args.kconfig |
| subcommand = args.subcommand |
| |
| if subcommand == "gen_kconfig": |
| generate_kconfig_build_file( |
| kconfig_path=kconfig_path, |
| out=args.o, |
| ) |
| elif subcommand == "gen_project": |
| generate_project_build_file( |
| kconfig_path=kconfig_path, |
| project_path=args.project, |
| extra_conf_files=args.extra_configs_in, |
| out=args.o, |
| ) |
| else: |
| parser.print_help() |
| |
| |
| if __name__ == "__main__": |
| main() |