mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-10-06 09:02:52 +08:00
fix(ci): restore board target matrix generation after zenoh Kconfig change
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
Since b11e615810 src/modules/zenoh/Kconfig sources the generated topic
catalog through ZENOH_KCONFIG_TOPICS, which only cmake exports, so every
standalone Kconfig parse crashed and build_all_targets produced an empty
build matrix on every run while the scan job still reported success.
Add Tools/kconfig/loadconfig.py as the single entry point for parsing
the Kconfig tree outside cmake: it generates the topic catalog exactly
like cmake/kconfig.cmake and provides shared board/target enumeration
and config loading. Port generate_board_targets_json.py (CI matrix) and
updateconfig.py (make updateconfig) to it; both were independently
broken and each reimplemented board scanning on its own.
Harden the workflow so the next failure of this class is loud: run the
generator as a standalone assignment, so bash -e fails the step (the
previous echo "matrix=$(...)" form masked the exit code), and write
the JSON outputs through the heredoc GITHUB_OUTPUT form.
Matrix output verified byte-identical to the pre-b11e6158101 baseline
in all three modes; updateconfig output verified identical against the
same baseline.
Assisted-by: Claude:claude-fable-5
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
This commit is contained in:
@@ -86,17 +86,34 @@ jobs:
|
||||
- name: Install Python Dependencies
|
||||
run: pip3 install -U packaging -r ./Tools/setup/requirements.txt
|
||||
|
||||
# The generator runs as a standalone assignment so a non-zero exit
|
||||
# fails the step; embedded in `echo "x=$(...)"` it would be masked
|
||||
# and the empty output silently skips the whole build matrix.
|
||||
- id: set-matrix
|
||||
name: Generate Build Matrix
|
||||
run: echo "matrix=$(./Tools/ci/generate_board_targets_json.py --group)" >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
matrix=$(./Tools/ci/generate_board_targets_json.py --group)
|
||||
{
|
||||
echo "matrix<<EOF"
|
||||
echo "$matrix"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- id: set-seeders
|
||||
name: Generate Seeder Matrix
|
||||
run: echo "seeders=$(./Tools/ci/generate_board_targets_json.py --group --seeders)" >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
seeders=$(./Tools/ci/generate_board_targets_json.py --group --seeders)
|
||||
{
|
||||
echo "seeders<<EOF"
|
||||
echo "$seeders"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- id: set-timestamp
|
||||
name: Save Current Timestamp
|
||||
run: echo "timestamp=$(date +"%Y%m%d%H%M%S")" >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
timestamp=$(date +"%Y%m%d%H%M%S")
|
||||
echo "timestamp=$timestamp" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- id: set-branch
|
||||
name: Save Current Branch Name
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
"""Shared helpers for loading PX4 board configs and the Kconfig tree.
|
||||
|
||||
The single supported way to parse the PX4 Kconfig tree outside of CMake.
|
||||
src/modules/zenoh/Kconfig sources a generated topic catalog through the
|
||||
ZENOH_KCONFIG_TOPICS environment variable; CMake generates the catalog and
|
||||
exports the variable (cmake/kconfig.cmake), so tools that parse Kconfig
|
||||
without a CMake build must go through ensure_env()/load_kconfig() to get
|
||||
the same environment.
|
||||
"""
|
||||
|
||||
import atexit
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
|
||||
import kconfiglib
|
||||
|
||||
PX4_ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
|
||||
BOARDS_DIR = os.path.join(PX4_ROOT, 'boards')
|
||||
|
||||
_ZENOH_GENERATOR = os.path.join(PX4_ROOT, 'Tools', 'zenoh', 'px_generate_zenoh_topic_files.py')
|
||||
_ZENOH_TEMPLATES = os.path.join(PX4_ROOT, 'Tools', 'zenoh', 'templates', 'zenoh')
|
||||
|
||||
# Labels that load standalone; any other label overlays default.px4board
|
||||
STANDALONE_LABELS = ('default', 'performance-test', 'bootloader', 'canbootloader')
|
||||
|
||||
Board = namedtuple('Board', ['manufacturer', 'board', 'path'])
|
||||
BoardTarget = namedtuple('BoardTarget', ['manufacturer', 'board', 'label', 'path', 'name'])
|
||||
|
||||
|
||||
def ensure_env():
|
||||
"""Make the Kconfig tree parseable in this process.
|
||||
|
||||
Honors an existing ZENOH_KCONFIG_TOPICS (the CMake case); otherwise
|
||||
generates the topic catalog into a temporary directory, exactly as
|
||||
cmake/kconfig.cmake does, and exports the variable so child processes
|
||||
and later Kconfig constructions inherit it.
|
||||
"""
|
||||
if os.environ.get('ZENOH_KCONFIG_TOPICS'):
|
||||
return
|
||||
out_dir = tempfile.mkdtemp(prefix='px4_zenoh_kconfig_')
|
||||
atexit.register(shutil.rmtree, out_dir, ignore_errors=True)
|
||||
msg_files = sorted(glob.glob(os.path.join(PX4_ROOT, 'msg', '*.msg')) +
|
||||
glob.glob(os.path.join(PX4_ROOT, 'msg', 'versioned', '*.msg')))
|
||||
# stdout must stay clean: several callers print machine-parsed output
|
||||
subprocess.run([sys.executable, _ZENOH_GENERATOR, '--zenoh-config',
|
||||
'-f'] + msg_files + ['-o', out_dir, '-e', _ZENOH_TEMPLATES],
|
||||
check=True, stdout=subprocess.DEVNULL)
|
||||
os.environ['ZENOH_KCONFIG_TOPICS'] = os.path.join(out_dir, 'Kconfig.topics')
|
||||
|
||||
|
||||
def load_kconfig(suppress_warnings=False):
|
||||
"""Load the PX4 Kconfig tree.
|
||||
|
||||
Must run with the PX4 source root as working directory: relative
|
||||
`source` statements resolve against it.
|
||||
"""
|
||||
ensure_env()
|
||||
if not os.path.isfile('Kconfig'):
|
||||
sys.exit('loadconfig: run from the PX4 source root (no Kconfig in {})'.format(os.getcwd()))
|
||||
kconf = kconfiglib.Kconfig()
|
||||
if suppress_warnings:
|
||||
kconf.warn_assign_undef = False
|
||||
kconf.warn_assign_override = False
|
||||
kconf.warn_assign_redun = False
|
||||
return kconf
|
||||
|
||||
|
||||
def enumerate_boards():
|
||||
"""Yield a Board for every boards/<manufacturer>/<board> directory, sorted."""
|
||||
for manufacturer in sorted(os.scandir(BOARDS_DIR), key=lambda e: e.name):
|
||||
if not manufacturer.is_dir():
|
||||
continue
|
||||
for board in sorted(os.scandir(manufacturer.path), key=lambda e: e.name):
|
||||
if board.is_dir():
|
||||
yield Board(manufacturer.name, board.name, board.path)
|
||||
|
||||
|
||||
def enumerate_targets():
|
||||
"""Yield a BoardTarget for every *.px4board file, sorted."""
|
||||
for board in enumerate_boards():
|
||||
for entry in sorted(os.scandir(board.path), key=lambda e: e.name):
|
||||
if entry.is_file() and entry.name.endswith('.px4board'):
|
||||
label = entry.name[:-len('.px4board')]
|
||||
yield BoardTarget(board.manufacturer, board.board, label, entry.path,
|
||||
'_'.join((board.manufacturer, board.board, label)))
|
||||
|
||||
|
||||
def load_target_config(kconf, px4board_path):
|
||||
"""Load a target's config into kconf, replacing any previous state."""
|
||||
label = os.path.basename(px4board_path)[:-len('.px4board')]
|
||||
if label in STANDALONE_LABELS:
|
||||
kconf.load_config(px4board_path, replace=True)
|
||||
else:
|
||||
default_config = os.path.join(os.path.dirname(px4board_path), 'default.px4board')
|
||||
kconf.load_config(default_config, replace=True)
|
||||
kconf.load_config(px4board_path, replace=False)
|
||||
|
||||
|
||||
def chip_family(board_path):
|
||||
"""Chip family of a board, from its NuttX defconfig.
|
||||
|
||||
Returns one of: stm32h7, stm32f7, stm32f4, stm32f1, imxrt, kinetis,
|
||||
s32k, rp2040, native.
|
||||
"""
|
||||
nsh_defconfig = os.path.join(board_path, 'nuttx-config', 'nsh', 'defconfig')
|
||||
if not os.path.exists(nsh_defconfig):
|
||||
bl_defconfig = os.path.join(board_path, 'nuttx-config', 'bootloader', 'defconfig')
|
||||
if os.path.exists(bl_defconfig):
|
||||
nsh_defconfig = bl_defconfig
|
||||
else:
|
||||
return 'native'
|
||||
|
||||
arch_chip = None
|
||||
specific_chip = None
|
||||
with open(nsh_defconfig) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith('CONFIG_ARCH_CHIP='):
|
||||
arch_chip = line.split('=')[1].strip('"')
|
||||
elif line.startswith('CONFIG_ARCH_CHIP_STM32F') and line.endswith('=y'):
|
||||
specific_chip = line.split('=')[0].replace('CONFIG_ARCH_CHIP_', '')
|
||||
|
||||
if arch_chip is None:
|
||||
return 'native'
|
||||
|
||||
if arch_chip in ('stm32h7', 'stm32f7', 'imxrt', 'kinetis', 'rp2040'):
|
||||
return arch_chip
|
||||
elif arch_chip.startswith('s32k'):
|
||||
return 's32k'
|
||||
elif arch_chip == 'stm32':
|
||||
if specific_chip and specific_chip.startswith('STM32F1'):
|
||||
return 'stm32f1'
|
||||
return 'stm32f4'
|
||||
return 'native'
|
||||
@@ -37,56 +37,62 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
import glob
|
||||
import kconfiglib
|
||||
import tempfile
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import diffconfig
|
||||
import loadconfig
|
||||
import merge_config
|
||||
|
||||
px4_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))
|
||||
px4_dir = loadconfig.PX4_ROOT
|
||||
|
||||
for name in glob.glob(px4_dir + '/boards/*/*/default.px4board'):
|
||||
kconf = kconfiglib.Kconfig()
|
||||
kconf.load_config(name)
|
||||
print(kconf.write_min_config(name))
|
||||
loadconfig.ensure_env()
|
||||
targets = list(loadconfig.enumerate_targets())
|
||||
|
||||
board_path = Path(name)
|
||||
defconfig_path = board_path.parent / "nuttx-config" / "nsh" / "defconfig"
|
||||
for target in targets:
|
||||
if target.label != 'default':
|
||||
continue
|
||||
kconf = loadconfig.load_kconfig()
|
||||
kconf.load_config(target.path)
|
||||
print(kconf.write_min_config(target.path))
|
||||
|
||||
defconfig_path = Path(target.path).parent / "nuttx-config" / "nsh" / "defconfig"
|
||||
|
||||
if os.path.exists(defconfig_path):
|
||||
# Merge NuttX with default config
|
||||
kconf = merge_config.main(px4_dir + "/Kconfig", name, defconfig_path)
|
||||
print(kconf.write_min_config(name))
|
||||
kconf = merge_config.main(px4_dir + "/Kconfig", target.path, defconfig_path)
|
||||
print(kconf.write_min_config(target.path))
|
||||
|
||||
for name in glob.glob(px4_dir + '/boards/*/*/bootloader.px4board'):
|
||||
kconf = kconfiglib.Kconfig()
|
||||
kconf.load_config(name)
|
||||
print(kconf.write_min_config(name))
|
||||
for target in targets:
|
||||
if target.label != 'bootloader':
|
||||
continue
|
||||
kconf = loadconfig.load_kconfig()
|
||||
kconf.load_config(target.path)
|
||||
print(kconf.write_min_config(target.path))
|
||||
|
||||
for name in glob.glob(px4_dir + '/boards/*/*/canbootloader.px4board'):
|
||||
kconf = kconfiglib.Kconfig()
|
||||
kconf.load_config(name)
|
||||
print(kconf.write_min_config(name))
|
||||
for target in targets:
|
||||
if target.label != 'canbootloader':
|
||||
continue
|
||||
kconf = loadconfig.load_kconfig()
|
||||
kconf.load_config(target.path)
|
||||
print(kconf.write_min_config(target.path))
|
||||
|
||||
for name in glob.glob(px4_dir + '/boards/*/*/*.px4board'):
|
||||
if(os.path.basename(name) != "default.px4board" and
|
||||
os.path.basename(name) != "bootloader.px4board" and
|
||||
os.path.basename(name) != "canbootloader.px4board"):
|
||||
board_default = os.path.dirname(name) + "/default.px4board";
|
||||
for target in targets:
|
||||
if target.label in ('default', 'bootloader', 'canbootloader'):
|
||||
continue
|
||||
board_default = os.path.join(os.path.dirname(target.path), 'default.px4board')
|
||||
|
||||
# Merge with default config
|
||||
kconf = merge_config.main(px4_dir + "/Kconfig", board_default, name)
|
||||
tf = tempfile.NamedTemporaryFile()
|
||||
# Merge with default config
|
||||
kconf = merge_config.main(px4_dir + "/Kconfig", board_default, target.path)
|
||||
tf = tempfile.NamedTemporaryFile()
|
||||
|
||||
# Save minconfig
|
||||
kconf.write_min_config(tf.name)
|
||||
# Save minconfig
|
||||
kconf.write_min_config(tf.name)
|
||||
|
||||
# Diff with default config and save to label.px4board
|
||||
stdoutpipe = sys.stdout
|
||||
sys.stdout = open(name, "w")
|
||||
diffconfig.main(1, board_default, tf.name)
|
||||
sys.stdout.close()
|
||||
sys.stdout = stdoutpipe
|
||||
# Diff with default config and save to label.px4board
|
||||
stdoutpipe = sys.stdout
|
||||
sys.stdout = open(target.path, "w")
|
||||
diffconfig.main(1, board_default, tf.name)
|
||||
sys.stdout.close()
|
||||
sys.stdout = stdoutpipe
|
||||
|
||||
Reference in New Issue
Block a user