1load("@bazel_skylib//rules:write_file.bzl", "write_file")
2load("@rules_cc//cc:defs.bzl", "cc_library")
3
4def _pico_generate_pio_header_impl(ctx):
5    generated_headers = []
6    for f in ctx.files.srcs:
7        out = ctx.actions.declare_file(
8            "{}_pio_generated/{}.h".format(ctx.label.name, f.basename),
9        )
10        generated_headers.append(out)
11        ctx.actions.run(
12            executable = ctx.executable._pioasm_tool,
13            arguments = [
14                "-o",
15                "c-sdk",
16                f.path,
17                out.path,
18            ],
19            inputs = [f],
20            outputs = [out],
21        )
22
23    cc_ctx = cc_common.create_compilation_context(
24        headers = depset(direct = generated_headers),
25        includes = depset(direct = [generated_headers[0].dirname]),
26    )
27    return [
28        DefaultInfo(files = depset(direct = generated_headers)),
29        CcInfo(compilation_context = cc_ctx),
30    ]
31
32pico_generate_pio_header = rule(
33    implementation = _pico_generate_pio_header_impl,
34    doc = """Generates a .h header file for each listed pio source.
35
36Each source file listed in `srcs` will be available as `[pio file name].h` on
37the include path if you depend on this rule from a `cc_library`.
38
39pico_generate_pio_header(
40    name = "my_fun_pio",
41    srcs = ["my_fun_pio.pio"],
42)
43
44# This library can #include "my_fun_pio.pio.h".
45cc_library(
46    name = "libfoo",
47    deps = [":my_fun_pio"],
48    srcs = ["libfoo.c"],
49)
50""",
51    attrs = {
52        "srcs": attr.label_list(mandatory = True, allow_files = True),
53        "_pioasm_tool": attr.label(
54            default = "@pico-sdk//tools/pioasm:pioasm",
55            cfg = "exec",
56            executable = True,
57        ),
58    },
59    provides = [CcInfo],
60)
61
62# Because the syntax for target_compatible_with when used with config_setting
63# rules is both confusing and verbose, provide some helpers that make it much
64# easier and clearer to express compatibility.
65#
66# Context: https://github.com/bazelbuild/bazel/issues/12614
67
68def compatible_with_config(config_label):
69    """Expresses compatibility with a config_setting."""
70    return select({
71        config_label: [],
72        "//conditions:default": ["@platforms//:incompatible"],
73    })
74
75def incompatible_with_config(config_label):
76    """Expresses incompatibility with a config_setting."""
77    return select({
78        config_label: ["@platforms//:incompatible"],
79        "//conditions:default": [],
80    })
81
82def compatible_with_rp2():
83    """Expresses a rule is compatible with the rp2 family."""
84    return incompatible_with_config("//bazel/constraint:host")
85
86def compatible_with_pico_w():
87    """Expresses a rule is compatible a Pico W."""
88    return select({
89        "@pico-sdk//bazel/constraint:cyw43_wireless": [],
90        "@pico-sdk//bazel/constraint:is_pico_w": [],
91        "@pico-sdk//bazel/constraint:is_pico2_w": [],
92        "//conditions:default": ["@platforms//:incompatible"],
93    })
94
95def pico_board_config(name, platform_includes, **kwargs):
96    """A helper macro for declaring a Pico board to use with PICO_CONFIG_HEADER.
97
98    This generates pico_config_platform_headers.h using the list of
99    includes provided in `platform_includes`, and the final artifact is
100    a cc_library that you can configure //bazel/config:PICO_CONFIG_HEADER to
101    point to.
102    """
103    _hdr_dir = "{}_generated_includes".format(name)
104    _hdr_path = "{}/pico_config_platform_headers.h".format(_hdr_dir)
105    write_file(
106        name = "{}_platform_headers_file".format(name),
107        out = _hdr_path,
108        content = ['#include "{}"'.format(inc) for inc in platform_includes],
109    )
110    kwargs.setdefault("hdrs", [])
111    kwargs["hdrs"].append(_hdr_path)
112    kwargs.setdefault("includes", [])
113    kwargs["includes"].append(_hdr_dir)
114    cc_library(
115        name = name,
116        **kwargs
117    )
118