build(packaging): add PX4 SITL .deb packages

Add cmake/cpack infrastructure for building .deb packages from
px4_sitl_sih and px4_sitl_default targets. Includes install rules,
package scripts, Gazebo wrapper, and CI workflow.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
This commit is contained in:
Ramon Roche
2026-04-02 15:03:04 -06:00
parent ebe0b727d8
commit 1079c57fd0
16 changed files with 940 additions and 41 deletions
+94 -1
View File
@@ -23,6 +23,9 @@ px4_posix_generate_alias(
add_definitions(-DPX4_SOURCE_DIR="${PX4_SOURCE_DIR}" -DPX4_BINARY_DIR="${PX4_BINARY_DIR}")
# PX4_INSTALL_PREFIX is set by cmake/package.cmake after this file is processed.
# It is passed via target_compile_definitions on the px4 target from package.cmake.
add_executable(px4
src/px4/common/main.cpp
apps.cpp
@@ -80,10 +83,16 @@ target_link_libraries(px4 PRIVATE uORB)
# install
#
# Detect dpkg early so install rules can branch on it.
# find_program caches the result, so the later call in package.cmake is a no-op.
find_program(DPKG_PROGRAM dpkg)
# Generic install rules (skipped when board provides its own install.cmake)
if(NOT EXISTS "${PX4_BOARD_DIR}/cmake/install.cmake")
# px4 dirs
# Legacy top-level install (posix-configs, test data, etc into px4/ prefix).
# Skip when building .deb packages which use a clean /opt/px4-sitl{,-sih}/ layout.
if(NOT DPKG_PROGRAM)
install(
DIRECTORY
${PROJECT_SOURCE_DIR}/posix-configs
@@ -94,6 +103,7 @@ install(
${PROJECT_NAME}
USE_SOURCE_PERMISSIONS
)
endif()
endif() # NOT board install.cmake
@@ -163,6 +173,11 @@ elseif("${PX4_BOARD}" MATCHES "sitl")
# install
# Legacy install rules (used by ROS launch, existing CPack tarball workflow).
# Skip when building .deb packages: the .deb rules below provide a clean
# /opt/px4-sitl{,-sih}/ layout and these would duplicate/bloat the package.
if(NOT DPKG_PROGRAM)
# px4 dirs
install(
DIRECTORY
@@ -227,4 +242,82 @@ elseif("${PX4_BOARD}" MATCHES "sitl")
OPTIONAL
)
endif() # NOT DPKG_PROGRAM
####################################################################
# Install rules for .deb package (only when dpkg is available)
####################################################################
if(DPKG_PROGRAM AND NOT CMAKE_C_COMPILER MATCHES "emcc$")
# px4 binary
install(TARGETS px4
RUNTIME DESTINATION bin
)
# module symlinks and alias script
install(
DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/
DESTINATION bin
USE_SOURCE_PERMISSIONS
FILES_MATCHING
PATTERN "px4-*"
PATTERN "px4-alias.sh"
)
# ROMFS (init scripts, mixers, etc.)
install(
DIRECTORY ${PX4_BINARY_DIR}/etc
DESTINATION .
USE_SOURCE_PERMISSIONS
)
if(CONFIG_MODULES_SIMULATION_GZ_BRIDGE)
# Gazebo wrapper script (sets GZ_SIM_* env vars pointing at installed resources)
install(
PROGRAMS ${PROJECT_SOURCE_DIR}/Tools/packaging/px4-gazebo.sh
DESTINATION bin
RENAME px4-gazebo
)
endif()
if(CONFIG_MODULES_SIMULATION_GZ_BRIDGE)
# Gazebo models
install(
DIRECTORY ${PROJECT_SOURCE_DIR}/Tools/simulation/gz/models
DESTINATION share/gz
OPTIONAL
)
# Gazebo worlds
install(
DIRECTORY ${PROJECT_SOURCE_DIR}/Tools/simulation/gz/worlds
DESTINATION share/gz
OPTIONAL
)
# Gazebo server config
install(
FILES ${PROJECT_SOURCE_DIR}/Tools/simulation/gz/server.config
DESTINATION share/gz
OPTIONAL
)
# Gazebo plugins (built .so files)
install(
DIRECTORY ${CMAKE_BINARY_DIR}/src/modules/simulation/gz_plugins/
DESTINATION lib/gz/plugins
FILES_MATCHING PATTERN "*.so"
PATTERN "CMakeFiles" EXCLUDE
)
# OpticalFlow external library (runtime dep of OpticalFlowSystem plugin)
install(
FILES ${CMAKE_BINARY_DIR}/OpticalFlow/install/lib/libOpticalFlow.so
DESTINATION lib/gz/plugins
OPTIONAL
)
endif()
endif() # DPKG_PROGRAM AND NOT emcc
endif()
+51 -4
View File
@@ -259,6 +259,32 @@ int main(int argc, char **argv)
PX4_INFO("instance: %i", instance);
}
#if defined(PX4_INSTALL_PREFIX)
// When installed as a .deb package, default to the baked-in install prefix.
// Working directory defaults to XDG_DATA_HOME/px4/rootfs/<instance>.
if (commands_file.empty() && data_path.empty() && working_directory.empty()
&& dir_exists(PX4_INSTALL_PREFIX"/etc")
) {
data_path = PX4_INSTALL_PREFIX"/etc";
const char *xdg_data_home = getenv("XDG_DATA_HOME");
std::string state_base;
if (xdg_data_home) {
state_base = xdg_data_home;
} else {
const char *home = getenv("HOME");
state_base = std::string(home ? home : "/tmp") + "/.local/share";
}
working_directory = state_base + "/px4/rootfs";
working_directory_default = true;
}
#endif // PX4_INSTALL_PREFIX
#if defined(PX4_BINARY_DIR)
// data_path & working_directory: if no commands specified or in current working directory),
@@ -745,13 +771,34 @@ std::string pwd()
return (getcwd(temp, PATH_MAX) ? std::string(temp) : std::string(""));
}
static int mkdir_p(const std::string &path)
{
std::string tmp = path;
for (size_t i = 1; i < tmp.size(); ++i) {
if (tmp[i] == '/') {
tmp[i] = '\0';
if (mkdir(tmp.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0 && errno != EEXIST) {
return -1;
}
tmp[i] = '/';
}
}
if (mkdir(tmp.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0 && errno != EEXIST) {
return -1;
}
return 0;
}
int change_directory(const std::string &directory)
{
// create directory
// create directory (including intermediate components)
if (!dir_exists(directory)) {
int ret = mkdir(directory.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
if (ret == -1) {
if (mkdir_p(directory) != 0) {
PX4_ERR("Error creating directory: %s (%s)", directory.c_str(), strerror(errno));
return -1;
}