1
0
mirror of https://github.com/raspberrypi/pico-sdk.git synced 2025-08-09 04:22:44 +03:00

[Bazel] Fix bazel build, add presubmit (#1973)

* [Bazel] Fix bazel build, add presubmit

* Fixes a missing dep in the Bazel build breaking the host build.
* Automagically finds all board headers.
* Improves presubmit script polish for GH Action readiness.
* Adds a GitHub action workflow for the Bazel build.
* Disable failing checks
* Disables Windows, as there's a mix of real build errors and
  overly-ambitious checks that don't work on Windows.
* Disables extra checks temporarily since it's currently failing.
This commit is contained in:
armandomontanez
2024-10-12 15:41:43 -07:00
committed by GitHub
parent 03a82f3d2f
commit 07d6dc1315
8 changed files with 151 additions and 120 deletions

76
.github/workflows/bazel_build.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Bazel presubmit checks
on:
push:
pull_request:
jobs:
bazel-build-check:
strategy:
matrix:
# TODO: Windows is currently broken.
os: [ubuntu-latest, macos-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get Bazel
uses: bazel-contrib/setup-bazel@0.9.0
with:
# Avoid downloading Bazel every time.
bazelisk-cache: true
# Store build cache per workflow.
disk-cache: ${{ github.workflow }}
# Share repository cache between workflows.
repository-cache: true
# Only needed to drive the presbumit scripts.
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Fetch latest Picotool
uses: actions/checkout@v4
with:
repository: raspberrypi/picotool
ref: develop
fetch-depth: 0
path: lib/picotool
- name: Full Bazel build with develop Picotool
run: python3 tools/run_all_bazel_checks.py --program=build --picotool-dir=lib/picotool
# Checks that the current BCR-requested version of Picotool builds.
- name: Bazel Picotool backwards compatibility
run: bazel build @picotool//:picotool
# Add back when it begins to pass.
# other-bazel-checks:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# with:
# fetch-depth: 0
# - name: Get Bazel
# uses: bazel-contrib/setup-bazel@0.9.0
# with:
# # Avoid downloading Bazel every time.
# bazelisk-cache: true
# # Store build cache per workflow.
# disk-cache: ${{ github.workflow }}
# # Share repository cache between workflows.
# repository-cache: true
# # Only needed to drive the presbumit scripts.
# - name: Setup Python
# uses: actions/setup-python@v5
# with:
# python-version: '3.10'
# - name: Fetch latest Picotool
# uses: actions/checkout@v4
# with:
# repository: raspberrypi/picotool
# ref: develop
# fetch-depth: 0
# path: lib/picotool
# - name: Other Bazel checks
# run: python3 tools/run_all_bazel_checks.py --program=other --picotool-dir=lib/picotool

View File

@@ -3,103 +3,14 @@ load("//bazel/util:multiple_choice_flag.bzl", "declare_flag_choices", "flag_choi
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
# Known board choices: # Find all boards.
BOARD_CHOICES = [ BOARD_CHOICE_FILES = glob(["include/boards/*.h"])
"0xcb_helios",
"adafruit_feather_rp2040_usb_host",
"adafruit_feather_rp2040",
"adafruit_feather_rp2350",
"adafruit_itsybitsy_rp2040",
"adafruit_kb2040",
"adafruit_macropad_rp2040",
"adafruit_qtpy_rp2040",
"adafruit_trinkey_qt2040",
"amethyst_fpga",
"archi",
"arduino_nano_rp2040_connect",
"cytron_maker_pi_rp2040",
"datanoisetv_rp2040_dsp",
"datanoisetv_rp2350_dsp",
"defcon32_badge",
"eetree_gamekit_rp2040",
"garatronic_pybstick26_rp2040",
"gen4_rp2350_24",
"gen4_rp2350_24ct",
"gen4_rp2350_24t",
"gen4_rp2350_28",
"gen4_rp2350_28ct",
"gen4_rp2350_28t",
"gen4_rp2350_32",
"gen4_rp2350_32ct",
"gen4_rp2350_32t",
"gen4_rp2350_35",
"gen4_rp2350_35ct",
"gen4_rp2350_35t",
"hellbender_2350A_devboard",
"ilabs_challenger_rp2350_bconnect",
"ilabs_challenger_rp2350_wifi_ble",
"ilabs_opendec02",
"melopero_perpetuo_rp2350_lora",
"melopero_shake_rp2040",
"metrotech_xerxes_rp2040",
"net8086_usb_interposer",
"none",
"nullbits_bit_c_pro",
"phyx_rick_tny_rp2350",
"pi-plates_micropi",
"pico_w",
"pico",
"pico2",
"pimoroni_badger2040",
"pimoroni_interstate75",
"pimoroni_keybow2040",
"pimoroni_motor2040",
"pimoroni_pga2040",
"pimoroni_pga2350",
"pimoroni_pico_plus2_rp2350",
"pimoroni_picolipo_16mb",
"pimoroni_picolipo_4mb",
"pimoroni_picosystem",
"pimoroni_plasma2040",
"pimoroni_plasma2350",
"pimoroni_servo2040",
"pimoroni_tiny2040_2mb",
"pimoroni_tiny2040",
"pimoroni_tiny2350",
"pololu_3pi_2040_robot",
"pololu_zumo_2040_robot",
"seeed_xiao_rp2040",
"seeed_xiao_rp2350",
"solderparty_rp2040_stamp_carrier",
"solderparty_rp2040_stamp_round_carrier",
"solderparty_rp2040_stamp",
"solderparty_rp2350_stamp_xl",
"solderparty_rp2350_stamp",
"sparkfun_micromod",
"sparkfun_promicro_rp2350",
"sparkfun_promicro",
"sparkfun_thingplus",
"switchscience_picossci2_conta_base",
"switchscience_picossci2_dev_board",
"switchscience_picossci2_micro",
"switchscience_picossci2_rp2350_breakout",
"switchscience_picossci2_tiny",
"tinycircuits_thumby_color_rp2350",
"vgaboard",
"waveshare_rp2040_lcd_0.96",
"waveshare_rp2040_lcd_1.28",
"waveshare_rp2040_one",
"waveshare_rp2040_plus_16mb",
"waveshare_rp2040_plus_4mb",
"waveshare_rp2040_zero",
"weact_studio_rp2040_16mb",
"weact_studio_rp2040_2mb",
"weact_studio_rp2040_4mb",
"weact_studio_rp2040_8mb",
"wiznet_w5100s_evb_pico",
]
BOARD_CHOICE_FILES = ["include/boards/" + c + ".h" for c in BOARD_CHOICES] # Extract just the name of the board.
BOARD_CHOICES = [
path.removeprefix("include/boards/").removesuffix(".h")
for path in BOARD_CHOICE_FILES
]
BOARD_CHOICE_MAP = {c: [":{}".format(c)] for c in BOARD_CHOICES} BOARD_CHOICE_MAP = {c: [":{}".format(c)] for c in BOARD_CHOICES}

View File

@@ -15,7 +15,10 @@ cc_library(
defines = _DEFINES, defines = _DEFINES,
includes = ["include"], includes = ["include"],
target_compatible_with = ["//bazel/constraint:host"], target_compatible_with = ["//bazel/constraint:host"],
visibility = ["//src/common/pico_time:__pkg__"], visibility = [
"//src/common/pico_time:__pkg__",
"//src/host/pico_platform:__pkg__",
],
deps = ["//src/common/pico_base_headers"], deps = ["//src/common/pico_base_headers"],
) )

View File

@@ -30,5 +30,6 @@ cc_library(
":pico_platform_internal", ":pico_platform_internal",
":platform_defs", ":platform_defs",
"//src/common/pico_base_headers", "//src/common/pico_base_headers",
"//src/host/hardware_timer:hardware_timer_headers",
], ],
) )

View File

@@ -15,6 +15,7 @@ from bazel_common import (
override_picotool_arg, override_picotool_arg,
parse_common_args, parse_common_args,
print_framed_string, print_framed_string,
print_to_stderr,
run_bazel, run_bazel,
setup_logging, setup_logging,
) )
@@ -196,12 +197,12 @@ def build_all_configurations(picotool_dir):
) )
if result.returncode != 0: if result.returncode != 0:
failed_builds.append(config["name"]) failed_builds.append(config["name"])
print() print_to_stderr()
if failed_builds: if failed_builds:
print_framed_string("ERROR: One or more builds failed.") print_framed_string("ERROR: One or more builds failed.")
for build in failed_builds: for build in failed_builds:
print(f" * FAILED: {build} build") print_to_stderr(f" * FAILED: {build} build")
return 1 return 1
print_framed_string("All builds successfully passed!") print_framed_string("All builds successfully passed!")

View File

@@ -13,6 +13,7 @@ from pathlib import Path
import shlex import shlex
import shutil import shutil
import subprocess import subprocess
import sys
_LOG = logging.getLogger(__file__) _LOG = logging.getLogger(__file__)
@@ -34,13 +35,16 @@ SDK_ROOT = subprocess.run(
def parse_common_args(): def parse_common_args():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
add_common_args(parser)
return parser.parse_args()
def add_common_args(parser):
parser.add_argument( parser.add_argument(
"--picotool-dir", "--picotool-dir",
help="Use a local copy of Picotool rather than the dynamically fetching it", help="Use a local copy of Picotool rather than the dynamically fetching it",
default=None, default=None,
type=Path, type=Path,
) )
return parser.parse_args()
def override_picotool_arg(picotool_dir): def override_picotool_arg(picotool_dir):
return f"--override_module=picotool={picotool_dir.resolve()}" return f"--override_module=picotool={picotool_dir.resolve()}"
@@ -49,7 +53,7 @@ def override_picotool_arg(picotool_dir):
def bazel_command() -> str: def bazel_command() -> str:
"""Return the path to bazelisk or bazel.""" """Return the path to bazelisk or bazel."""
if shutil.which("bazelisk"): if shutil.which("bazelisk"):
return "bazelisk" return shutil.which("bazelisk")
if shutil.which("bazel"): if shutil.which("bazel"):
return "bazel" return "bazel"
@@ -93,12 +97,16 @@ def run_bazel(args, check=False, **kwargs):
return proc return proc
def print_to_stderr(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def print_framed_string(s): def print_framed_string(s):
"""Frames a string of text and prints it to highlight script steps.""" """Frames a string of text and prints it to highlight script steps."""
header_spacer = "#" * (len(s) + 12) header_spacer = "#" * (len(s) + 12)
print(header_spacer) print_to_stderr(header_spacer)
print("### " + s + " ###") print_to_stderr("### " + s + " ###")
print(header_spacer) print_to_stderr(header_spacer)
def setup_logging(): def setup_logging():

View File

@@ -14,12 +14,15 @@
from dataclasses import dataclass from dataclasses import dataclass
import glob import glob
import logging
import os import os
import re import re
import sys import sys
from typing import Dict from typing import Dict
from bazel_common import SDK_ROOT from bazel_common import SDK_ROOT, setup_logging
_LOG = logging.getLogger(__file__)
CMAKE_FILE_TYPES = ( CMAKE_FILE_TYPES = (
"**/CMakeLists.txt", "**/CMakeLists.txt",
@@ -182,17 +185,17 @@ def OptionsAreEqual(bazel_option, cmake_option):
if bazel_option is None: if bazel_option is None:
if cmake_option.name in CMAKE_ONLY_ALLOWLIST: if cmake_option.name in CMAKE_ONLY_ALLOWLIST:
return True return True
print(f" {cmake_option.name} does not exist in Bazel") _LOG.warning(f" {cmake_option.name} does not exist in Bazel")
return False return False
elif cmake_option is None: elif cmake_option is None:
if bazel_option.name in BAZEL_ONLY_ALLOWLIST: if bazel_option.name in BAZEL_ONLY_ALLOWLIST:
return True return True
print(f" {bazel_option.name} does not exist in CMake") _LOG.warning(f" {bazel_option.name} does not exist in CMake")
return False return False
elif not bazel_option.matches(cmake_option): elif not bazel_option.matches(cmake_option):
print(" Bazel and CMAKE definitions do not match:") _LOG.error(" Bazel and CMAKE definitions do not match:")
print(f" [CMAKE] {bazel_option}") _LOG.error(f" [CMAKE] {bazel_option}")
print(f" [BAZEL] {cmake_option}") _LOG.error(f" [BAZEL] {cmake_option}")
return False return False
return True return True
@@ -224,22 +227,23 @@ def compare_build_systems():
for f in glob.glob(os.path.join(SDK_ROOT, p), recursive=True) for f in glob.glob(os.path.join(SDK_ROOT, p), recursive=True)
] ]
print("[1/2] Checking build system configuration flags...") _LOG.info("[1/2] Checking build system configuration flags...")
build_options_ok = CompareOptions( build_options_ok = CompareOptions(
"PICO_BAZEL_CONFIG", bazel_files, "PICO_CMAKE_CONFIG", cmake_files "PICO_BAZEL_CONFIG", bazel_files, "PICO_CMAKE_CONFIG", cmake_files
) )
print("[2/2] Checking build system defines...") _LOG.info("[2/2] Checking build system defines...")
build_defines_ok = CompareOptions( build_defines_ok = CompareOptions(
"PICO_BUILD_DEFINE", bazel_files, "PICO_BUILD_DEFINE", cmake_files "PICO_BUILD_DEFINE", bazel_files, "PICO_BUILD_DEFINE", cmake_files
) )
if build_options_ok and build_defines_ok: if build_options_ok and build_defines_ok:
print("OK") _LOG.info("OK")
return 0 return 0
return 1 return 1
if __name__ == "__main__": if __name__ == "__main__":
setup_logging()
sys.exit(compare_build_systems()) sys.exit(compare_build_systems())

View File

@@ -6,10 +6,16 @@
# #
# Runs all Bazel checks. # Runs all Bazel checks.
import argparse
import sys import sys
from bazel_build import build_all_configurations from bazel_build import build_all_configurations
from bazel_common import setup_logging, print_framed_string, parse_common_args from bazel_common import (
setup_logging,
print_framed_string,
print_to_stderr,
add_common_args,
)
from compare_build_systems import compare_build_systems from compare_build_systems import compare_build_systems
from check_source_files_in_bazel_build import check_sources_in_bazel_build from check_source_files_in_bazel_build import check_sources_in_bazel_build
@@ -18,34 +24,55 @@ def main():
setup_logging() setup_logging()
failed_steps = [] failed_steps = []
args = parse_common_args() parser = argparse.ArgumentParser()
add_common_args(parser)
steps = ( parser.add_argument(
"--program",
help="A program to run",
choices = [
"all",
"build",
"other",
],
default="all",
)
args = parser.parse_args()
build_steps = (
{ {
"step_name": "build",
"description": "Bazel build", "description": "Bazel build",
"action": lambda : build_all_configurations(args.picotool_dir), "action": lambda : build_all_configurations(args.picotool_dir),
}, },
)
other_steps = (
{ {
"description": "Ensure build system configurations options match", "description": "Ensure build system configurations options match",
"action": compare_build_systems, "action": compare_build_systems,
}, },
{ {
"step_name": "check_srcs_in_build",
"description": "Ensure source files are present in Bazel build", "description": "Ensure source files are present in Bazel build",
"action": lambda : check_sources_in_bazel_build(args.picotool_dir), "action": lambda : check_sources_in_bazel_build(args.picotool_dir),
}, },
) )
steps_to_run = []
run_all_steps = args.program == "all"
if args.program == "build" or run_all_steps:
steps_to_run.extend(build_steps)
if args.program == "other" or run_all_steps:
steps_to_run.extend(other_steps)
for step in steps: for step in steps_to_run:
print_framed_string(f"{step['description']}...") print_framed_string(f"{step['description']}...")
returncode = step["action"]() returncode = step["action"]()
if returncode != 0: if returncode != 0:
failed_steps.append(step["description"]) failed_steps.append(step["description"])
print() print_to_stderr()
if failed_steps: if failed_steps:
print_framed_string("ERROR: One or more steps failed.") print_framed_string("ERROR: One or more steps failed.")
for build in failed_steps: for build in failed_steps:
print(f" * FAILED: {build}") print_to_stderr(f" * FAILED: {build}")
return 1 return 1
print_framed_string("All checks successfully passed!") print_framed_string("All checks successfully passed!")