fix(cmake): incremental no-op rebuild 44 s -> 0.15 s on NuttX targets (#27324)

* fix(cmake): use Python3 module; cache PYTHON_EXECUTABLE properly

The legacy `find_package(PythonInterp 3)` is deprecated (warned with
noisy CMP0148 by cmake 3.27+, currently visible on Ubuntu 24.04 CI
runs).

It also stores its result as PYTHON_EXECUTABLE without a proper CACHE
type, which interacts badly with the Makefile's `cmake-cache-check`.
`cmake -L` skips UNINITIALIZED entries, so the
`-DPYTHON_EXECUTABLE=...` passed by the top-level Makefile is never
matched in the cache and every invocation forces a full reconfigure.

Switch to `find_package(Python3 COMPONENTS Interpreter REQUIRED)`,
then bridge to the legacy `PYTHON_EXECUTABLE` name that the rest of
the codebase still references.

Promote it to a CACHE FILEPATH entry so cmake -L lists it, preserving
any user-supplied value verbatim (find_package(Python3) canonicalises
e.g. bin/python3 to bin/python3.13, defeating a string-based cache
match).

Signed-off-by: Daniel Fanache <dan@rts.ro>

* fix(cmake): promote CONFIG to CACHE STRING for incremental builds

When CONFIG is passed via `-DCONFIG=...` on the cmake command line, it
is stored as an UNINITIALIZED cache entry. `cmake -L` skips
UNINITIALIZED entries by design, so the Makefile's
`cmake-cache-check` (which uses `cmake -L` to diff desired vs cached
options) never finds CONFIG in the output, concludes the cache is
stale, and triggers a full reconfigure on every `make <target>`
invocation.

If a config identifier is given, we force promote it to CACHE
STRING. This preserves the user supplied value as it was, while making
it visible to `cmake -L`, so the cache-check succeeds when nothing has
changed.

Signed-off-by: Daniel Fanache <dan@rts.ro>

* fix(cmake): track default.px4board as a configure dependency

Non-default labels merge `default.px4board` + `{label}.px4board` via
`merge_config.py`, but only the label file was listed as a configure
dependency. Changes to `default.px4board` were silently ignored until
a clean build, which is surprising and easy to debug for hours.

Register `default.px4board` as a CMAKE_CONFIGURE_DEPENDS in the
non-default-label branch so edits trigger reconfigure as expected.

Signed-off-by: Daniel Fanache <dan@rts.ro>

* fix(cmake): correct NuttX apps/library build dependency tracking

Three dependency graph defects in the libapps.a and per NuttX library
custom_commands caused either spurious full rebuilds or stale outputs
on incremental builds.

(1) `builtin_list.h` and `builtin_proto.h` are regenerated by the apps
build from `px4.bdat`/`px4.pdat` on every invocation. They were
included in `nuttx_apps_files`, so on each build we saw them as
changed inputs and re-triggered the apps target perpetually. Exclude
them with a `list(FILTER)`.

(2) libapps.a's custom_command lacked `px4.bdat`/`px4.pdat` as
dependencies, so module additions or renames (which regenerate those
registries) did not propagate to a rebuild of the builtin command
table. Add them to DEPENDS.

(3) NuttX's recursive make does not always notice that
`builtin_list.h` has been regenerated and that `builtin_list.c`
therefore needs recompiling. Touch `builtin_list.c` so NuttX's make
picks up the indirect change.

Additionally, drop the destructive cleanup COMMANDs that ran at the
start of libapps.a and each per-library custom_command (`remove
-f *.a`, `find ... -delete *.o`). These were workarounds for the now
fixed dependency tracking; without them removed, they would also force
unnecessary full rebuilds every time.

Signed-off-by: Daniel Fanache <dan@rts.ro>

---------

Signed-off-by: Daniel Fanache <dan@rts.ro>
Co-authored-by: Ramon Roche <mrpollo@gmail.com>
This commit is contained in:
Daniel Fanache
2026-05-17 12:23:24 -06:00
committed by GitHub
co-authored by Ramon Roche
parent 537b2b7b39
commit b990430cdc
4 changed files with 30 additions and 11 deletions
+12 -7
View File
@@ -172,14 +172,19 @@ include(px4_add_module)
set(config_module_list)
set(config_kernel_list)
# Find Python
find_package(PythonInterp 3)
# We have a custom error message to tell users how to install python3.
if(NOT PYTHONINTERP_FOUND)
message(FATAL_ERROR "Python 3 not found. Please install Python 3:\n"
" Ubuntu: sudo apt install python3 python3-dev python3-pip\n"
" macOS: brew install python")
# Find Python. The call below sets Python3_EXECUTABLE; we then provide
# PYTHON_EXECUTABLE as a backward-compat alias for the rest of the
# codebase that still uses the legacy name.
find_package(Python3 COMPONENTS Interpreter REQUIRED)
# Preserve PYTHON_EXECUTABLE as passed if any (e.g. via -D on command
# line, or from the top Makefile); or else default to what
# find_package(Python3) resolved. Promote to a CACHE FILEPATH entry so
# cmake -L lists it, to avoid spurious reconfiguration.
if(NOT DEFINED PYTHON_EXECUTABLE OR PYTHON_EXECUTABLE STREQUAL "")
set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}")
endif()
set(PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}" CACHE FILEPATH "Python 3 interpreter" FORCE)
option(PYTHON_COVERAGE "Python code coverage" OFF)
if(PYTHON_COVERAGE)
+4
View File
@@ -44,6 +44,10 @@ if(EXISTS ${BOARD_DEFCONFIG})
OUTPUT_VARIABLE DUMMY_RESULTS
)
else()
# Non-default labels merge default.px4board + {label}.px4board,
# so reconfigure must also trigger on changes to default.px4board.
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PX4_BOARD_DIR}/default.px4board)
# Generate boardconfig from default.px4board and {label}.px4board
execute_process(
COMMAND ${CMAKE_COMMAND} -E env ${COMMON_KCONFIG_ENV_SETTINGS}
+5
View File
@@ -41,6 +41,11 @@ if(NOT CONFIG)
else()
set(CONFIG "px4_sitl_default" CACHE STRING "desired configuration")
endif()
else()
# Promote CONFIG from UNINITIALIZED (set by -D on command line) to STRING
# so that cmake -L lists it. The Makefile's cmake-cache-check relies on
# finding CONFIG in cmake -L output to avoid unnecessary reconfiguration.
set(CONFIG "${CONFIG}" CACHE STRING "desired configuration" FORCE)
endif()
if(NOT PX4_CONFIG_FILE)
+9 -4
View File
@@ -144,15 +144,22 @@ file(GLOB_RECURSE nuttx_apps_files LIST_DIRECTORIES false
${APPS_DIR}/*.c
${APPS_DIR}/*.h
)
# Exclude generated builtin headers - they are regenerated by the apps
# build from registry files and would create a perpetual dirty cycle
# if tracked.
list(FILTER nuttx_apps_files EXCLUDE REGEX "builtin/builtin_list\\.h|builtin/builtin_proto\\.h")
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/apps/libapps.a
COMMAND ${CMAKE_COMMAND} -E remove -f ${APPS_DIR}/libapps.a ${APPS_DIR}/builtin/builtin_list.h ${APPS_DIR}/builtin/builtin_proto.h
COMMAND find ${APPS_DIR} -type f -name \*.o -delete
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/px4.bdat ${APPS_DIR}/builtin/registry/px4.bdat
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/px4.pdat ${APPS_DIR}/builtin/registry/px4.pdat
COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${APPS_DIR}/builtin/registry/.updated
COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${APPS_DIR}/builtin/builtin_list.c
COMMAND make --no-print-directory --silent TOPDIR="${NUTTX_DIR}" > ${CMAKE_CURRENT_BINARY_DIR}/nuttx_apps.log
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${APPS_DIR}/libapps.a ${CMAKE_CURRENT_BINARY_DIR}/apps/libapps.a
DEPENDS ${nuttx_apps_files} nuttx_context ${NUTTX_DIR}/include/nuttx/config.h
${CMAKE_CURRENT_BINARY_DIR}/px4.bdat
${CMAKE_CURRENT_BINARY_DIR}/px4.pdat
WORKING_DIRECTORY ${APPS_DIR}
#USES_TERMINAL
)
@@ -175,8 +182,6 @@ function(add_nuttx_dir nuttx_lib nuttx_lib_dir kernel extra target)
)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/nuttx/${nuttx_lib_dir}/lib${nuttx_lib}.a
COMMAND ${CMAKE_COMMAND} -E remove -f ${NUTTX_DIR}/${nuttx_lib_dir}/lib${nuttx_lib}.a
COMMAND find ${nuttx_lib_dir} -type f -name \*.o -delete
COMMAND make -C ${nuttx_lib_dir} --no-print-directory --silent ${nuttx_lib_target} TOPDIR="${NUTTX_DIR}" KERNEL=${kernel} EXTRAFLAGS=${extra}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${NUTTX_DIR}/${nuttx_lib_dir}/lib${nuttx_lib}.a ${CMAKE_CURRENT_BINARY_DIR}/nuttx/${nuttx_lib_dir}/lib${nuttx_lib}.a
DEPENDS