fix(uxrce-dds): switch nested ExternalProject to NMake on Windows

The Micro-XRCE-DDS-Client SuperBuild nests ExternalProjects three deep:

  px4_sitl_default (parent ninja)
    -> libmicroxrceddsclient_project (ExternalProject)
      -> microcdr (ExternalProject in cmake/SuperBuild.cmake)
        -> ucdr (ExternalProject)

When all three layers inherit the parent's Ninja generator, the inner
cmake's post-configure invocations of `ninja -t recompact` and
`ninja -t restat build.ninja` race the outer ninja that still holds
microcdr-build/build.ninja open, producing
`ninja: error: failed recompaction: Permission denied` deterministically
on Windows where files cannot be replaced while another process has a
handle on them. The CI failure has reproduced across multiple SHAs on
this branch.

Force the libmicroxrceddsclient_project sub-build (and the nested
microcdr / ucdr ExternalProjects that inherit its generator) to use
NMake Makefiles instead of Ninja on Windows / non-MinGW. nmake.exe
ships with the MSVC BuildTools and is on PATH after vcvars64. The
microcdr/ucdr compile is small enough that the loss of Ninja's
parallelism is not measurable in the overall build.

If nmake is not on PATH (developer running outside vcvars), fall back
to the inherited generator and emit a build-time WARNING explaining
why the inner build may flake on `failed recompaction`, with a pointer
to vcvars64.

MinGW and POSIX paths are untouched.

Local validation (Windows 11, MSVC 14.36.32532, vcvars64):
  cmake --build build/px4_sitl_default --target px4
  -> 1102/1102 steps, bin/px4.exe produced (10.8 MiB), exit 0
  -> libmicroxrceddsclient_project build + install + complete clean,
     no `Permission denied` anywhere in the log.

Signed-off-by: Nuno Marques <n.marques21@hotmail.com>
This commit is contained in:
Nuno Marques
2026-05-08 12:07:40 -07:00
parent 1fdfe7f1aa
commit 3ced7609b2
@@ -75,6 +75,42 @@ else()
file(TO_CMAKE_PATH "${microxrceddsclient_make_program}" microxrceddsclient_make_program) file(TO_CMAKE_PATH "${microxrceddsclient_make_program}" microxrceddsclient_make_program)
endif() endif()
# On Windows / MSVC, force the libmicroxrceddsclient sub-build (and the
# nested microcdr/uclient ExternalProjects it spawns via SuperBuild.cmake)
# to use NMake Makefiles instead of inheriting the parent's Ninja
# generator. The Micro-XRCE-DDS-Client SuperBuild nests ExternalProjects
# three deep (libmicroxrceddsclient_project -> microcdr -> uclient); when
# all three layers run Ninja concurrently, the inner cmake's
# `ninja -t recompact` and `ninja -t restat build.ninja` steps race the
# outer ninja that still holds microcdr-build/build.ninja open, producing
# `ninja: error: failed recompaction: Permission denied` deterministically
# on Windows where files cannot be replaced while another process has a
# handle on them. Switching the sub-build to NMake side-steps the lock
# collision entirely; nmake.exe ships with the MSVC BuildTools and is on
# PATH after vcvars64. The microcdr/uclient compile is small enough that
# the loss of Ninja's parallelism is not measurable in the overall build.
set(microxrceddsclient_extra_args)
if(WIN32 AND NOT MINGW)
find_program(microxrceddsclient_nmake_program nmake)
if(microxrceddsclient_nmake_program)
file(TO_CMAKE_PATH "${microxrceddsclient_nmake_program}"
microxrceddsclient_make_program)
list(APPEND microxrceddsclient_extra_args
CMAKE_GENERATOR "NMake Makefiles")
else()
# Without nmake we can't switch generators; fall through to the
# parent's Ninja generator and warn so the developer sees why
# the inner build may flake on `failed recompaction`.
message(WARNING
"uxrce_dds_client: nmake not found on PATH; the "
"libmicroxrceddsclient sub-build will inherit the parent "
"Ninja generator and may hit "
"'ninja: error: failed recompaction: Permission denied'. "
"Run from a Visual Studio Developer Command Prompt "
"(vcvars64) so nmake.exe is available.")
endif()
endif()
set(microxrceddsclient_library ${CMAKE_CURRENT_BINARY_DIR}/${lib_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}microxrcedds_client${CMAKE_STATIC_LIBRARY_SUFFIX}) set(microxrceddsclient_library ${CMAKE_CURRENT_BINARY_DIR}/${lib_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}microxrcedds_client${CMAKE_STATIC_LIBRARY_SUFFIX})
set(microcdr_library ${CMAKE_CURRENT_BINARY_DIR}/${lib_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}microcdr${CMAKE_STATIC_LIBRARY_SUFFIX}) set(microcdr_library ${CMAKE_CURRENT_BINARY_DIR}/${lib_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}microcdr${CMAKE_STATIC_LIBRARY_SUFFIX})
@@ -84,6 +120,7 @@ else()
PREFIX ${microxrceddsclient_build_dir} PREFIX ${microxrceddsclient_build_dir}
SOURCE_DIR ${microxrceddsclient_src_dir} SOURCE_DIR ${microxrceddsclient_src_dir}
INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR} INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}
${microxrceddsclient_extra_args}
CMAKE_CACHE_ARGS CMAKE_CACHE_ARGS
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}