From ff243198d6943ed0dcb6bd30c59b05b364fa15c4 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Mon, 7 Sep 2026 11:00:54 +0200 Subject: [PATCH] build(cmake): offline guard, version fallback, source-tarball script Add RS_FETCH_MISSING_DEPS (default ON) guarding the FetchContent network fallbacks of the GUI, service and friendserver: when OFF, a missing local dependency aborts the configure with an explicit error instead of silently downloading an unpinned upstream tip. Make git optional and fall back to the Source_Version file at the super-project root for RS_GUI_VERSION when git metadata is absent. Add build_scripts/make-source-tarball.sh, which archives the super-project plus every submodule at its pinned commit and stamps the Source_Version files, producing a self-contained tarball that configures offline with -DRS_FETCH_MISSING_DEPS=OFF. Needed for distribution packaging (e.g. Debian), where build-time network access is forbidden. Co-Authored-By: Claude Fable 5 --- build_scripts/make-source-tarball.sh | 112 +++++++++++++++++++++++++ retroshare-friendserver/CMakeLists.txt | 22 ++++- retroshare-gui/CMakeLists.txt | 37 +++++++- retroshare-service/CMakeLists.txt | 37 +++++--- 4 files changed, 193 insertions(+), 15 deletions(-) create mode 100755 build_scripts/make-source-tarball.sh diff --git a/build_scripts/make-source-tarball.sh b/build_scripts/make-source-tarball.sh new file mode 100755 index 000000000..7a0016d3c --- /dev/null +++ b/build_scripts/make-source-tarball.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# make-source-tarball.sh — build a self-contained RetroShare source tarball. +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# Produces a tarball that builds OFFLINE with CMake: it embeds every +# initialized git submodule at its checked-out commit and stamps the version +# in Source_Version files so the CMake configure works without any git +# metadata (see the Source_Version fallbacks in libretroshare/CMakeLists.txt +# and retroshare-gui/CMakeLists.txt). Intended for distribution source +# packages (e.g. Debian), where network access at build time is forbidden; +# such builds should configure with -DRS_FETCH_MISSING_DEPS=OFF so any +# missing dependency aborts the configure instead of being downloaded. +# +# Usage: run from anywhere inside the super-project checkout: +# +# ./build_scripts/make-source-tarball.sh [OUTPUT_DIR] +# +# Environment: +# RS_TARBALL_ALLOW_DIRTY=1 allow tracked modifications / out-of-sync +# submodules (the tarball then reflects the +# working tree commits, not a tagged release) +# RS_TARBALL_COMPRESS=xz|gz compression (default xz) +# +# The tarball is named retroshare_.tar. where is the +# super-project `git describe` with the leading 'v' dropped and the dashes +# after the tag turned into dots (same convention as the historical OBS +# packaging scripts). + +set -e + +TOP_DIR="$(git rev-parse --show-toplevel)" +OUT_DIR="${1:-$(pwd)}" +OUT_DIR="$(cd "${OUT_DIR}" && pwd)" +COMPRESS="${RS_TARBALL_COMPRESS:-xz}" + +cd "${TOP_DIR}" + +## Sanity: refuse silently wrong content unless explicitly allowed. + +DIRTY="$(git status --porcelain --untracked-files=no)" +if [ -n "${DIRTY}" ] && [ "${RS_TARBALL_ALLOW_DIRTY}" != "1" ]; then + echo "ERROR: the working tree has tracked modifications:" >&2 + echo "${DIRTY}" >&2 + echo "A release tarball must be built from a clean checkout of the release tag." >&2 + echo "Set RS_TARBALL_ALLOW_DIRTY=1 to override (testing only)." >&2 + exit 1 +fi + +MISSING="$(git submodule status --recursive | grep '^-' || true)" +if [ -n "${MISSING}" ]; then + echo "ERROR: uninitialized submodules, the tarball would not be self-contained:" >&2 + echo "${MISSING}" >&2 + echo "Initialize them first (see AGENTS.md / BUILD-cmake.md: init WITHOUT --remote)." >&2 + exit 1 +fi + +OUTOFSYNC="$(git submodule status --recursive | grep '^+' || true)" +if [ -n "${OUTOFSYNC}" ] && [ "${RS_TARBALL_ALLOW_DIRTY}" != "1" ]; then + echo "ERROR: submodules checked out on a different commit than the recorded gitlink:" >&2 + echo "${OUTOFSYNC}" >&2 + echo "Run 'git submodule update' first, or set RS_TARBALL_ALLOW_DIRTY=1 (testing only)." >&2 + exit 1 +fi + +## Version stamps, computed while git metadata is still around. + +ROOT_DESCRIBE="$(git describe --tags --always)" +LIB_DESCRIBE="$(git -C libretroshare describe --tags --long --match 'v*.*.*')" +VERSION="$(echo "${ROOT_DESCRIBE}" | sed -e 's/-/./2g' | sed -e 's/^v//')" + +echo "Super-project version: ${ROOT_DESCRIBE}" +echo "libretroshare version: ${LIB_DESCRIBE}" +echo "Tarball version: ${VERSION}" + +## Stage the super-project then overlay every submodule at its own commit. +## git archive honours export-ignore attributes and never includes .git. + +STAGE_ROOT="$(mktemp --directory)" +trap 'rm -rf "${STAGE_ROOT}"' EXIT +STAGE="${STAGE_ROOT}/retroshare-${VERSION}" +mkdir "${STAGE}" + +echo "Staging super-project ..." +git archive HEAD | tar -x -C "${STAGE}" + +git submodule status --recursive | sed -e 's/^[ +]//' | while read -r SM_COMMIT SM_PATH SM_REST; do + echo "Staging submodule ${SM_PATH} @ ${SM_COMMIT} ..." + rm -rf "${STAGE:?}/${SM_PATH}" + mkdir -p "${STAGE}/${SM_PATH}" + git -C "${SM_PATH}" archive "${SM_COMMIT}" | tar -x -C "${STAGE}/${SM_PATH}" +done + +echo "${ROOT_DESCRIBE}" > "${STAGE}/Source_Version" +echo "${LIB_DESCRIBE}" > "${STAGE}/libretroshare/Source_Version" + +## Pack. + +case "${COMPRESS}" in + xz) TAR_FLAGS="-cJf"; EXT="tar.xz" ;; + gz) TAR_FLAGS="-czf"; EXT="tar.gz" ;; + *) echo "ERROR: unsupported RS_TARBALL_COMPRESS=${COMPRESS} (xz|gz)" >&2; exit 1 ;; +esac + +TARBALL="${OUT_DIR}/retroshare_${VERSION}.${EXT}" +echo "Packing ${TARBALL} ..." +tar -C "${STAGE_ROOT}" ${TAR_FLAGS} "${TARBALL}" "retroshare-${VERSION}" + +echo "Done: ${TARBALL}" +echo "Offline configure check: extract it, then" +echo " cmake -S retroshare-${VERSION} -B build -DRS_FETCH_MISSING_DEPS=OFF" diff --git a/retroshare-friendserver/CMakeLists.txt b/retroshare-friendserver/CMakeLists.txt index bfbdd31ea..eb4191cc7 100644 --- a/retroshare-friendserver/CMakeLists.txt +++ b/retroshare-friendserver/CMakeLists.txt @@ -23,11 +23,30 @@ option( "Enable use RNP lib for PGP" ON ) +## Offline build guard. libretroshare falls back to FetchContent (network +## download at configure time) when its local sources are absent. For +## offline/distribution builds (e.g. a Debian source package) that fallback must +## be a hard error: it would silently build against an unpinned upstream tip. +option( + RS_FETCH_MISSING_DEPS + "Allow downloading missing dependencies at configure time via FetchContent. Turn OFF for offline/distribution builds: a missing dependency then stops the configure with an explicit error instead of a silent network download." ON ) + +macro(rs_fetch_fallback_guard RS_FFG_DEP_NAME) + if(RS_FETCH_MISSING_DEPS) + message(STATUS "${RS_FFG_DEP_NAME} not found locally, downloading it via FetchContent (pass -DRS_FETCH_MISSING_DEPS=OFF to make this an error)") + else() + message(FATAL_ERROR "${RS_FFG_DEP_NAME} not found locally and RS_FETCH_MISSING_DEPS is OFF: refusing to download it at configure time. Provide its sources (git submodule / complete source tarball) or the system library." ) + endif() +endmacro() + # NOTE: ZLIB, BZip2 and OpenSSL are NOT direct dependencies of the friendserver. # They are only used through libretroshare and are propagated transitively by the # "retroshare" target's link interface, so they are intentionally not searched or # linked here. -find_package(Git REQUIRED) +# Git is optional: it is only used to stamp versions (git describe, with a +# Source_Version file fallback for source tarballs) and by the FetchContent +# network fallbacks, which are guarded by RS_FETCH_MISSING_DEPS. +find_package(Git) if(RS_DEVELOPMENT_BUILD) set(CMAKE_VERBOSE_MAKEFILE ON) @@ -59,6 +78,7 @@ if(NOT TARGET retroshare) add_subdirectory("${LIBRETROSHARE_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/libretroshare" ) else() include(FetchContent) + rs_fetch_fallback_guard(libretroshare) FetchContent_Declare( libretroshare GIT_REPOSITORY "https://github.com/RetroShare/libretroshare.git" diff --git a/retroshare-gui/CMakeLists.txt b/retroshare-gui/CMakeLists.txt index db558e270..e4e720479 100644 --- a/retroshare-gui/CMakeLists.txt +++ b/retroshare-gui/CMakeLists.txt @@ -150,7 +150,28 @@ endif() set(FETCHCONTENT_QUIET OFF) include(FetchContent) -find_package(Git REQUIRED) +################################################################################ +## Offline build guard. Several dependencies fall back to FetchContent (network +## download at configure time) when their local sources are absent. For +## offline/distribution builds (e.g. a Debian source package) that fallback must +## be a hard error: it would silently build against an unpinned upstream tip. +option( + RS_FETCH_MISSING_DEPS + "Allow downloading missing dependencies at configure time via FetchContent. Turn OFF for offline/distribution builds: a missing dependency then stops the configure with an explicit error instead of a silent network download." ON ) + +macro(rs_fetch_fallback_guard RS_FFG_DEP_NAME) + if(RS_FETCH_MISSING_DEPS) + message(STATUS "${RS_FFG_DEP_NAME} not found locally, downloading it via FetchContent (pass -DRS_FETCH_MISSING_DEPS=OFF to make this an error)") + else() + message(FATAL_ERROR "${RS_FFG_DEP_NAME} not found locally and RS_FETCH_MISSING_DEPS is OFF: refusing to download it at configure time. Provide its sources (git submodule / complete source tarball) or the system library." ) + endif() +endmacro() +################################################################################ + +# Git is optional: it is only used to stamp versions (git describe, with a +# Source_Version file fallback for source tarballs) and by the FetchContent +# network fallbacks, which are guarded by RS_FETCH_MISSING_DEPS. +find_package(Git) ################################################################################ @@ -257,6 +278,7 @@ if(NOT TARGET retroshare) add_subdirectory( "${LIBRETROSHARE_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/libretroshare" ) else() + rs_fetch_fallback_guard(libretroshare) FetchContent_Declare( libretroshare GIT_REPOSITORY "https://gitlab.com/RetroShare/libretroshare.git" @@ -335,6 +357,7 @@ if(RS_GUI_CMARK) add_subdirectory( "${CMARK_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/cmark" ) set(CMARK_INCLUDE_DIR "${CMARK_DEVEL_DIR}/src") else() + rs_fetch_fallback_guard(cmark) FetchContent_Declare( cmark GIT_REPOSITORY "https://github.com/commonmark/cmark.git" @@ -649,6 +672,18 @@ execute_process( OUTPUT_VARIABLE RS_GUI_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE RS_GUI_GIT_RESULT ) # Avoid CMake failure if git fails + +# Source tarball fallback: an unpacked source tarball carries no git metadata, +# so the tarball preparation script stores the super-project `git describe` +# output in a Source_Version file at the super-project root. +if(NOT RS_GUI_GIT_RESULT EQUAL 0 OR RS_GUI_VERSION STREQUAL "") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../Source_Version") + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/../Source_Version" RS_GUI_VERSION) + string(STRIP "${RS_GUI_VERSION}" RS_GUI_VERSION) + set(RS_GUI_GIT_RESULT 0) + endif() +endif() + if(RS_GUI_GIT_RESULT EQUAL 0 AND NOT RS_GUI_VERSION STREQUAL "") string(REGEX REPLACE "^v" "" RS_GUI_VERSION "${RS_GUI_VERSION}") message(STATUS "RetroShare GUI version ${RS_GUI_VERSION}") diff --git a/retroshare-service/CMakeLists.txt b/retroshare-service/CMakeLists.txt index c08443deb..265864235 100644 --- a/retroshare-service/CMakeLists.txt +++ b/retroshare-service/CMakeLists.txt @@ -59,25 +59,34 @@ cmake_dependent_option( ################################################################################ +################################################################################ +## Offline build guard. Several dependencies fall back to FetchContent (network +## download at configure time) when their local sources are absent. For +## offline/distribution builds (e.g. a Debian source package) that fallback must +## be a hard error: it would silently build against an unpinned upstream tip. +option( + RS_FETCH_MISSING_DEPS + "Allow downloading missing dependencies at configure time via FetchContent. Turn OFF for offline/distribution builds: a missing dependency then stops the configure with an explicit error instead of a silent network download." ON ) + +macro(rs_fetch_fallback_guard RS_FFG_DEP_NAME) + if(RS_FETCH_MISSING_DEPS) + message(STATUS "${RS_FFG_DEP_NAME} not found locally, downloading it via FetchContent (pass -DRS_FETCH_MISSING_DEPS=OFF to make this an error)") + else() + message(FATAL_ERROR "${RS_FFG_DEP_NAME} not found locally and RS_FETCH_MISSING_DEPS is OFF: refusing to download it at configure time. Provide its sources (git submodule / complete source tarball) or the system library." ) + endif() +endmacro() +################################################################################ + set(FETCHCONTENT_QUIET OFF) include(FetchContent) # Find required dependencies # find_package for Botan and json-c failed to populate variables, remove them. # find_package(Botan 3 REQUIRED) -# message(STATUS "Botan_FOUND=${Botan_FOUND}") -# message(STATUS "BOTAN_LIBRARIES=${BOTAN_LIBRARIES}") -# message(STATUS "BOTAN_INCLUDE_DIRS=${BOTAN_INCLUDE_DIRS}") -# -# find_package(json-c REQUIRED) -# message(STATUS "json-c_FOUND=${json-c_FOUND}") -# message(STATUS "JSON-C_LIBRARIES=${JSON-C_LIBRARIES}") -# message(STATUS "JSON-C_INCLUDE_DIRS=${JSON-C_INCLUDE_DIRS}") - -find_package(ZLIB REQUIRED) -find_package(BZip2 REQUIRED) - -find_package(Git REQUIRED) +# Git is optional: it is only used to stamp versions (git describe, with a +# Source_Version file fallback for source tarballs) and by the FetchContent +# network fallbacks, which are guarded by RS_FETCH_MISSING_DEPS. +find_package(Git) ################################################################################ @@ -120,6 +129,7 @@ if(NOT TARGET retroshare) add_subdirectory( "${LIBRETROSHARE_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/libretroshare" ) else() + rs_fetch_fallback_guard(libretroshare) FetchContent_Declare( libretroshare GIT_REPOSITORY "https://github.com/RetroShare/libretroshare.git" @@ -183,6 +193,7 @@ if(RS_WEBUI) add_subdirectory( "${RS_WEBUI_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/webui" ) else() + rs_fetch_fallback_guard(webui) FetchContent_Declare( webui GIT_REPOSITORY "https://github.com/RetroShare/RSNewWebUI.git"