Files
2026-06-02 13:56:18 -07:00

285 lines
12 KiB
CMake

# ------------------------------------------------------------------------------
# Linux Packaging Configuration
# ------------------------------------------------------------------------------
# Common macro to copy Linux binaries and dependencies
macro(linux_copy_files DEST_DIR TARGET_NAME)
# Copy app target binaries
foreach(_target ${WS_LINUX_APP_TARGETS})
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:${_target}>" "${DEST_DIR}${WS_LINUX_INSTALL_DIR}/"
)
endforeach()
# Copy shared libraries
foreach(_entry ${WS_SHARED_LIBS})
string(REPLACE "|" ";" _pair "${_entry}")
list(GET _pair 0 _src)
list(GET _pair 1 _dest)
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_src}" "${DEST_DIR}${WS_LINUX_INSTALL_DIR}/lib/${_dest}"
)
endforeach()
# Copy bundled helper binaries
foreach(_entry ${WS_BUNDLED_HELPERS})
string(REPLACE "|" ";" _pair "${_entry}")
list(GET _pair 0 _src)
list(GET _pair 1 _dest)
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_src}" "${DEST_DIR}${WS_LINUX_INSTALL_DIR}/${_dest}"
)
endforeach()
# Copy open source licenses
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/src/client/client-common/licenses/open_source_licenses.txt"
"${DEST_DIR}${WS_LINUX_INSTALL_DIR}/"
)
# Fix RPATH for binaries and libraries
find_program(PATCHELF_EXECUTABLE patchelf)
if(PATCHELF_EXECUTABLE)
foreach(_bin ${WS_LINUX_RPATH_BINARIES})
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${PATCHELF_EXECUTABLE} --set-rpath "${WS_LINUX_INSTALL_DIR}/lib"
"${DEST_DIR}${WS_LINUX_INSTALL_DIR}/${_bin}"
)
endforeach()
endif()
endmacro()
set(INSTALLER_LINUX_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/installer/${INSTALLER_TYPE}/linux")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/installer/${INSTALLER_TYPE}/linux/common")
set(INSTALLER_LINUX_COMMON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/installer/${INSTALLER_TYPE}/linux/common")
else()
set(INSTALLER_LINUX_COMMON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/installer/${WS_PRODUCT_NAME_LOWER}/linux")
endif()
# Package signing setup. Linux package signing rides along with the existing
# --sign-app flag (Windows signs the app binary; Linux signs the packages).
# Trigger: SIGN_APP=ON and LINUX_SIGNING_GPG_KEY_ID set in the build env.
# If SIGN_APP is set without the key id, fail loudly — silently producing
# unsigned packages from a signing pipeline would be worse than not building.
if(SIGN_APP)
if(NOT DEFINED ENV{LINUX_SIGNING_GPG_KEY_ID} OR "$ENV{LINUX_SIGNING_GPG_KEY_ID}" STREQUAL "")
message(FATAL_ERROR "SIGN_APP=ON but LINUX_SIGNING_GPG_KEY_ID env var is not set")
endif()
set(LINUX_SIGNING_KEY_ID "$ENV{LINUX_SIGNING_GPG_KEY_ID}")
find_program(GPG_EXECUTABLE gpg)
if(NOT GPG_EXECUTABLE)
message(FATAL_ERROR "SIGN_APP=ON but gpg not found on PATH")
endif()
if(BUILD_RPM OR BUILD_RPM_OPENSUSE)
find_program(RPMSIGN_EXECUTABLE rpmsign)
if(NOT RPMSIGN_EXECUTABLE)
message(FATAL_ERROR "SIGN_APP=ON with RPM target but rpmsign not found on PATH")
endif()
endif()
message(STATUS "Linux package signing enabled with key ${LINUX_SIGNING_KEY_ID}")
else()
message(STATUS "Linux package signing disabled (SIGN_APP not set)")
endif()
# DEB Packaging
if(BUILD_DEB)
set(DEB_PACKAGE_DIR "${CMAKE_BINARY_DIR}/debian_package")
ws_resolve_output_name("${WS_LINUX_OUTPUT_NAME}" DEB_PACKAGE_NAME)
add_custom_target(package-deb
DEPENDS build-app
)
# Create package directory structure
add_custom_command(TARGET package-deb PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory "${DEB_PACKAGE_DIR}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}${WS_LINUX_INSTALL_DIR}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}${WS_LINUX_INSTALL_DIR}/lib"
)
# Copy DEBIAN control files and scripts
add_custom_command(TARGET package-deb PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${INSTALLER_LINUX_DIR}/debian_package/DEBIAN"
"${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}/DEBIAN"
COMMAND chmod +x "${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}/DEBIAN/preinst"
COMMAND chmod +x "${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}/DEBIAN/postinst"
COMMAND chmod +x "${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}/DEBIAN/prerm"
)
# Update version and architecture in control file
add_custom_command(TARGET package-deb PRE_BUILD
COMMAND sed -i "s/Version: .*/Version: ${PROJECT_VERSION}/"
"${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}/DEBIAN/control"
COMMAND sed -i "s/Architecture: .*/Architecture: ${PACKAGE_ARCH}/"
"${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}/DEBIAN/control"
)
# Copy common files (systemd services, scripts, etc.)
add_custom_command(TARGET package-deb PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${INSTALLER_LINUX_COMMON_DIR}"
"${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}"
)
# Copy overlay files (package-specific systemd units, scripts)
add_custom_command(TARGET package-deb PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${INSTALLER_LINUX_DIR}/overlay"
"${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}"
)
linux_copy_files("${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}" package-deb)
# Build DEB package
find_program(DPKG_DEB_EXECUTABLE dpkg-deb)
if(DPKG_DEB_EXECUTABLE)
add_custom_command(TARGET package-deb POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Building DEB package..."
COMMAND ${DPKG_DEB_EXECUTABLE} --build "${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${DEB_PACKAGE_DIR}/${DEB_PACKAGE_NAME}.deb"
"${BUILD_EXE_DIR}/"
)
else()
message(FATAL_ERROR "dpkg-deb not found - cannot build DEB package")
endif()
endif()
# RPM Architecture mapping
# RPM_BUILD_ARCH: architecture for rpmbuild (x86_64, aarch64)
# RPM_FILENAME_ARCH: architecture for final filename (x86_64, arm64)
set(RPM_BUILD_ARCH "${PACKAGE_ARCH}")
set(RPM_FILENAME_ARCH "${PACKAGE_ARCH}")
if(PACKAGE_ARCH STREQUAL "amd64")
set(RPM_BUILD_ARCH "x86_64")
set(RPM_FILENAME_ARCH "amd64")
elseif(PACKAGE_ARCH STREQUAL "arm64")
set(RPM_BUILD_ARCH "aarch64")
set(RPM_FILENAME_ARCH "arm64")
endif()
# Common macro to build RPM packages
macro(build_rpm_package TARGET_NAME RPM_TYPE_DIR DISTRO_SUFFIX DISTRO_NAME)
set(RPM_BUILD_ROOT "$ENV{HOME}/rpmbuild")
add_custom_target(${TARGET_NAME}
DEPENDS build-app
)
# Setup RPM build environment
add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${RPM_BUILD_ROOT}/SPECS"
COMMAND ${CMAKE_COMMAND} -E make_directory "${RPM_BUILD_ROOT}/BUILD"
COMMAND ${CMAKE_COMMAND} -E make_directory "${RPM_BUILD_ROOT}/BUILDROOT"
COMMAND ${CMAKE_COMMAND} -E make_directory "${RPM_BUILD_ROOT}/RPMS"
COMMAND ${CMAKE_COMMAND} -E make_directory "${RPM_BUILD_ROOT}/SOURCES"
COMMAND ${CMAKE_COMMAND} -E make_directory "${RPM_BUILD_ROOT}/BUILD${WS_LINUX_INSTALL_DIR}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${RPM_BUILD_ROOT}/BUILD${WS_LINUX_INSTALL_DIR}/lib"
)
# Copy spec file and update version
add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${INSTALLER_LINUX_DIR}/${RPM_TYPE_DIR}/SPECS/rpm.spec"
"${RPM_BUILD_ROOT}/SPECS/"
COMMAND sed -i "s/Version:.*/Version: ${PROJECT_VERSION}/"
"${RPM_BUILD_ROOT}/SPECS/rpm.spec"
)
# Copy files to BUILD directory
add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${INSTALLER_LINUX_COMMON_DIR}"
"${RPM_BUILD_ROOT}/BUILD"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${INSTALLER_LINUX_DIR}/overlay"
"${RPM_BUILD_ROOT}/BUILD"
)
# Copy binaries, libraries, and fix RPATH
linux_copy_files("${RPM_BUILD_ROOT}/BUILD" ${TARGET_NAME})
# Build RPM package
find_program(RPMBUILD_EXECUTABLE rpmbuild)
if(RPMBUILD_EXECUTABLE)
set(RPM_SOURCE_NAME "${WS_LINUX_PACKAGE_NAME}-${PROJECT_VERSION}-0.${RPM_BUILD_ARCH}.rpm")
set(PACKAGE_ARCH "${RPM_FILENAME_ARCH}${DISTRO_SUFFIX}")
ws_resolve_output_name("${WS_LINUX_OUTPUT_NAME}" _rpm_resolved_name)
set(RPM_FINAL_NAME "${_rpm_resolved_name}.rpm")
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Building ${DISTRO_NAME} RPM package..."
COMMAND ${RPMBUILD_EXECUTABLE} -bb "${RPM_BUILD_ROOT}/SPECS/rpm.spec"
COMMAND ${CMAKE_COMMAND} -E copy
"${RPM_BUILD_ROOT}/RPMS/${RPM_BUILD_ARCH}/${RPM_SOURCE_NAME}"
"${BUILD_EXE_DIR}/${RPM_FINAL_NAME}"
)
else()
message(FATAL_ERROR "rpmbuild not found - cannot build RPM package")
endif()
endmacro()
# Fedora RPM Packaging
if(BUILD_RPM)
build_rpm_package(package-rpm-fedora "rpm_fedora_package" "_fedora" "Fedora")
endif()
# OpenSUSE RPM Packaging
if(BUILD_RPM_OPENSUSE)
build_rpm_package(package-rpm-opensuse "rpm_opensuse_package" "_opensuse" "OpenSUSE")
# Both RPMs use the same staging directory, so they must build sequentially
if(BUILD_RPM)
add_dependencies(package-rpm-opensuse package-rpm-fedora)
endif()
endif()
add_custom_target(init-build-exe
COMMAND ${CMAKE_COMMAND} -E remove_directory "${BUILD_EXE_DIR}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${BUILD_EXE_DIR}"
COMMENT "Initializing build-exe directory..."
)
add_custom_target(deploy ALL)
if(BUILD_DEB)
add_dependencies(package-deb init-build-exe)
add_dependencies(deploy package-deb)
endif()
if(BUILD_RPM)
add_dependencies(package-rpm-fedora init-build-exe)
add_dependencies(deploy package-rpm-fedora)
endif()
if(BUILD_RPM_OPENSUSE)
add_dependencies(package-rpm-opensuse init-build-exe)
add_dependencies(deploy package-rpm-opensuse)
endif()
# Linux package signing target. Mirrors the Windows `sign-app` pattern: a separate
# target in ALL so it runs whenever SIGN_APP is set, operating on whatever .deb/.rpm
# files are in BUILD_EXE_DIR. This supports both
# ./build_all --build-deb --sign-app (combined; deps below order it)
# ./build_all --build-deb && ./build_all --sign-app (split; signs prior output)
# rpmsign + gpg --detach-sign are idempotent so the combined case is safe even
# though the package-build targets and this target both touch the same files.
if(SIGN_APP)
add_custom_target(sign-linux-packages ALL
COMMAND bash "${CMAKE_CURRENT_SOURCE_DIR}/tools/sign-linux-packages.sh"
"${LINUX_SIGNING_KEY_ID}"
"${BUILD_EXE_DIR}"
COMMENT "Signing Linux packages..."
VERBATIM
)
if(TARGET package-deb)
add_dependencies(sign-linux-packages package-deb)
endif()
if(TARGET package-rpm-fedora)
add_dependencies(sign-linux-packages package-rpm-fedora)
endif()
if(TARGET package-rpm-opensuse)
add_dependencies(sign-linux-packages package-rpm-opensuse)
endif()
endif()