# Always include these libraries through //src/rp2_common:*!
# This ensures that you'll get the right headers for the MCU you're targeting.

load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
load("@rules_python//python:defs.bzl", "py_binary")
load("//bazel/toolchain:objcopy.bzl", "objcopy_to_bin")
load("//bazel/util:multiple_choice_flag.bzl", "declare_flag_choices", "flag_choice")
load("//bazel/util:transition.bzl", "rp2040_bootloader_binary")

# There's a lot of implementation details in here that shouldn't be considered
# stable, so allowlist visibility to just the public-facing pieces.
package(default_visibility = ["//visibility:private"])

# Known choices for boot2:
BOOT2_CHOICES = [
    "boot2_at25sf128a",
    "boot2_generic_03h",
    "boot2_is25lp080",
    "boot2_usb_blinky",
    "boot2_w25q080",
    "boot2_w25x10cl",
    "compile_time_choice",
]

BOOT2_CHOICE_FILES = [c + ".S" for c in BOOT2_CHOICES]

BOOT2_CHOICE_FILE_MAP = {c: [c + ".S"] for c in BOOT2_CHOICES}

BOOT2_CHOICE_DEFINE_MAP = {c: ['PICO_BUILD_BOOT_STAGE2_NAME=\\"{}\\"'.format(c)] for c in BOOT2_CHOICES}

# Define shouldn't be set for compile_time_choice.
BOOT2_CHOICE_DEFINE_MAP["compile_time_choice"] = []

cc_library(
    name = "config",
    hdrs = [
        "asminclude/boot2_helpers/exit_from_boot2.S",
        "asminclude/boot2_helpers/read_flash_sreg.S",
        "asminclude/boot2_helpers/wait_ssi_ready.S",
        "include/boot_stage2/config.h",
    ] + BOOT2_CHOICE_FILES,
    defines = select(flag_choice(
        "//bazel/config:PICO_DEFAULT_BOOT_STAGE2",
        ":__pkg__",
        BOOT2_CHOICE_DEFINE_MAP,
    )),
    includes = [
        "asminclude",
        "include",
    ],
    target_compatible_with = ["//bazel/constraint:rp2040"],
    visibility = ["//visibility:public"],
)

# Creates a config_setting for each known boot2 option with the name:
#     PICO_DEFAULT_BOOT_STAGE2_[choice]
declare_flag_choices(
    "//bazel/config:PICO_DEFAULT_BOOT_STAGE2",
    BOOT2_CHOICES,
)

filegroup(
    name = "build_selected_boot2",
    srcs = select(flag_choice(
        "//bazel/config:PICO_DEFAULT_BOOT_STAGE2",
        ":__pkg__",
        BOOT2_CHOICE_FILE_MAP,
    )),
    visibility = ["//src/rp2_common:__pkg__"],
)

cc_binary(
    name = "boot_stage2_elf_actual",
    srcs = ["//bazel/config:PICO_DEFAULT_BOOT_STAGE2_FILE"],
    copts = ["-fPIC"],
    # Incompatible with section garbage collection.
    features = ["-gc_sections"],
    # Platforms will commonly depend on bootloader components in every
    # binary via `link_extra_libs`, so we must drop these deps when
    # building the bootloader binaries themselves in order to avoid a
    # circular dependency.
    link_extra_lib = "//bazel:empty_cc_lib",
    linkopts = [
        "-Wl,--no-gc-sections",
        "-nostartfiles",
        "-Wl,--entry=_stage2_boot",
        "-T$(location boot_stage2.ld)",
    ],
    # this does nothing if someone passes --custom_malloc, so the
    # rp2040_bootloader_binary transition forcibly clobbers --custom_malloc.
    malloc = "//bazel:empty_cc_lib",
    tags = ["manual"],  # Only build as an explicit dependency.
    target_compatible_with = ["//bazel/constraint:rp2040"],
    deps = [
        "boot_stage2.ld",
        ":config",
        "//src/common/pico_base_headers",
        "//src/rp2_common:pico_platform_internal",
    ],
)

# Always build the bootloader with the bootloader-specific platform.
rp2040_bootloader_binary(
    name = "boot_stage2_elf",
    src = "boot_stage2_elf_actual",
)

objcopy_to_bin(
    name = "boot_stage2_bin",
    src = ":boot_stage2_elf",
    out = "boot_stage2.bin",
    target_compatible_with = ["//bazel/constraint:rp2040"],
)

# WORKAROUND: Python rules always require a .py extension.
copy_file(
    name = "copy_tool_to_py",
    src = "pad_checksum",
    out = "pad_checksum_tool.py",
    target_compatible_with = ["//bazel/constraint:host"],
)

py_binary(
    name = "pad_checksum_tool",
    srcs = ["pad_checksum_tool.py"],
    target_compatible_with = ["//bazel/constraint:host"],
)

run_binary(
    name = "boot_stage2_padded",
    srcs = [":boot_stage2_bin"],
    outs = ["boot_stage2.S"],
    args = [
        "-s 0xffffffff",
        "$(location boot_stage2_bin)",
        "$(location boot_stage2.S)",
    ],
    target_compatible_with = ["//bazel/constraint:rp2040"],
    tool = ":pad_checksum_tool",
)

cc_library(
    name = "boot_stage2",
    srcs = [":boot_stage2_padded"],
    target_compatible_with = ["//bazel/constraint:rp2040"],
    visibility = ["//src/rp2_common:__pkg__"],
    # This isn't referenced as a symbol, so alwayslink is required to ensure
    # it doesn't get dropped before the linker script can find it.
    alwayslink = True,
)
