Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

[FEATURE] Deploying tools built against the developer SDK: openms_install_runtime(), third-party licenses in the SDK, and a deployment check

Abierto
#10,341 6 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@kg290 ya está trabajando en esto.

Desde el 1/10/2026.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
35/100
Tipo de issue
Nueva funcionalidad
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
cmake, cpp

Línea de trabajo

Start with cmake/OpenMSDeploy.cmake, the license installation rule in src/openms/CMakeLists.txt, and the existing packaging logic in cmake/package_general.cmake and cmake/package_nsis.cmake. Review tools/ci/package_sdk.sh and the installed-consumer tests to understand the deployment checks. Done means the SDK and installed consumers carry required licenses, install runtime files correctly, and pass deployment checks on all three platforms.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Summary

#10248 ships a relocatable developer SDK per platform, and building a TOPP-style tool against it is a find_package(OpenMS CONFIG REQUIRED COMPONENTS CLI) away. Shipping that tool is still manual, and every step of it is a place to get it subtly wrong. This issue proposes three follow-ups, in order of value and independence:

  1. Ship the third-party licenses of the bundled libraries in share/OpenMS/LICENSES. Today the SDK archive (and, for the same reason, the OpenMS installers) bundles roughly 200 third-party libraries but only the OpenMS BSD license text. Anyone who redistributes a tool built on the SDK cannot meet the attribution terms of those libraries without collecting the texts by hand.
  2. openms_install_runtime(), a CMake function provided by the installed package (find_package(OpenMS)), which turns "install my tool plus everything it needs to run" into one call: the target, the OpenMS libraries and their bundled dependencies, share/OpenMS, the install RPATH, the MSVC runtime on Windows, and optionally re-signing on macOS.
  3. A deployment check in the installed-consumer tests and in the SDK check of release.yml: deploy the example tool from the extracted SDK, take the SDK away, run the deployed tool with a scrubbed environment. That is the property a user of the helper relies on, so it should be what CI proves on all three platforms.

Motivation: what a deployer has to do today

With the SDK from #10248, a working deployment needs all of the following, and nothing tells the user so except the source of package_sdk.sh and File.cpp:

  • Copy the SDK's lib/ (Linux, macOS) or bin/ (Windows) next to the tool. The libraries are relocatable ($ORIGIN / @loader_path RPATHs, DLLs side by side), so copying works, but the directory also holds lib/cmake/, the static archives libIsoSpec.a and libeol-bspline.a, and on Windows the import libraries, none of which belong in a deployment.
  • Copy share/OpenMS (about 80 MB, of which CHEMISTRY is 54 MB and models 21 MB) to <prefix>/share/OpenMS. File::getOpenMSDataPath() resolves the data directory relative to the executable (../../../share/OpenMS, then ../share/OpenMS) before it falls back to OPENMS_DATA_PATH, so this layout works without any environment variable, and a layout that deviates from it fails at the first File::find().
  • Give the tool an install RPATH ($ORIGIN/../lib, @executable_path/../lib). CMake strips the build-tree RPATH at install time, so a tool that runs from the build directory dies at startup after cmake --install unless INSTALL_RPATH is set.
  • On Windows, ship the MSVC runtime: the SDK excludes it like every other System32 library, so an installer has to include(InstallRequiredSystemLibraries) the way cmake/package_nsis.cmake does, or require the redistributable.
  • On macOS, decide about signatures: the SDK's Mach-O files are signed (with the OpenMS identity in releases, ad hoc otherwise; package_sdk.sh re-signs after rewriting install names). Copying keeps those signatures valid, but a notarized distribution needs the distributor's Developer ID on every Mach-O file and the hardened runtime on the executable.
  • Collect the licenses of the bundled libraries from somewhere else, because the SDK does not carry them (see 1.).

The manual recipe, for reference, is about 15 lines of CMake and gets the size (all of lib/), the RPATH and the licenses wrong unless one knows the above:

find_package(OpenMS CONFIG REQUIRED COMPONENTS CLI)
add_executable(MyTool MyTool.cpp)
target_link_libraries(MyTool PRIVATE OpenMS::OpenMS_CLI)
set_target_properties(MyTool PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")   # macOS: @executable_path/../lib
get_filename_component(OPENMS_SDK "${OpenMS_DIR}/../../.." ABSOLUTE)
install(TARGETS MyTool RUNTIME DESTINATION bin)
install(DIRECTORY "${OPENMS_SDK}/lib/"   DESTINATION lib)     # Windows: bin/, DLLs only
install(DIRECTORY "${OPENMS_SDK}/share/" DESTINATION share)
include(CPack)

With this issue done, the same deployment is:

find_package(OpenMS CONFIG REQUIRED COMPONENTS CLI)
add_executable(MyTool MyTool.cpp)
target_link_libraries(MyTool PRIVATE OpenMS::OpenMS_CLI)
openms_install_runtime(TARGETS MyTool)
include(CPack)

Goals and non-goals

Goals:

  • A tool built against the SDK (or against a cmake --install prefix of OpenMS) can be installed and packaged with one function call, on Linux, macOS and Windows, producing a tree that runs on a machine without OpenMS, without the SDK and without environment variables.
  • The deployed tree carries the license texts its contents require.
  • CI proves both, on every platform, from the SDK the release workflow produces.

Non-goals:

  • The GUI layer (OpenMS::OpenMS_GUI, Qt): the SDK does not ship it, and Qt deployment (plugins, frameworks, macdeployqt/windeployqt) is a different problem.
  • macOS .app bundles and notarization: the helper can re-sign, the distributor notarizes with their own account, as OpenMS does for its own installer.
  • pyOpenMS.

Design

1. Third-party licenses in share/OpenMS/LICENSES

Current state: src/openms/CMakeLists.txt installs License.txt as share/OpenMS/LICENSES/OpenMS-BSD-3-Clause.txt (component share); cmake/cmake_findExternalLibs.cmake adds the Thermo RawFileReader license for a WITH_THERMO_RAW build. Nothing else. The release artifacts (installers and SDK) are built with vcpkg, and vcpkg installs a copyright file for every port under <vcpkg_installed>/<triplet>/share/<port>/copyright; that is the source of truth to ship.

Proposal:

  • A new cmake/install_third_party_licenses.cmake, included from src/openms/CMakeLists.txt next to the existing license rule. When VCPKG_INSTALLED_DIR and VCPKG_TARGET_TRIPLET are set (vcpkg build), it globs ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/share/*/copyright, skips the vcpkg-* helper ports, and installs each file as share/OpenMS/LICENSES/<port>.txt in the share component. share is the right component: the installers, the deb and the SDK all contain it, and share/OpenMS/LICENSES is where the existing license lives. The trade-off of "every port in the install tree" versus "only the ports whose libraries were actually bundled" is deliberate: computing the latter needs the runtime-dependency closure at configure time, and a few superfluous license texts (a few hundred KB in total) are cheaper than a missing one.
  • Non-vcpkg builds (contrib submodule, conda-forge, distribution packages) get a message(STATUS) that third-party licenses were not collected, and nothing else. Their dependencies come from the system or from conda, which carry their own license files.
  • cmake/OpenMSSDKReadme.txt gets a "Licenses" section: what is under share/OpenMS/LICENSES, and that a redistributed tool has to carry that directory (which openms_install_runtime() does by copying share/OpenMS).
  • tools/ci/package_sdk.sh checks, when the build tree's cache holds VCPKG_INSTALLED_DIR, that share/OpenMS/LICENSES in the archive holds more than the OpenMS license, and fails otherwise.

This part is independent of the rest, fixes the same gap in the installers, and should be its own PR.

2. openms_install_runtime() in OpenMSDeploy.cmake

Placement: cmake/OpenMSDeploy.cmake in the sources, installed by cmake/export_macros.cmake next to OpenMSConfig.cmake (component cmake, so it is in the SDK and in every cmake --install prefix), and included at the end of cmake/OpenMSConfig.cmake.in. find_package(OpenMS) then provides the function; consumers include nothing extra.

API:

openms_install_runtime(
  TARGETS <target>...              # your executables (and shared libraries)
  [BIN_DESTINATION <dir>]          # where the targets go; default bin
  [LIB_DESTINATION <dir>]          # where the OpenMS libraries go; default lib (Windows: BIN_DESTINATION)
  [SHARE_DESTINATION <dir>]        # where share/OpenMS goes; default share
  [COMPONENT <name>]               # install component of everything installed here; default Runtime
  [NO_SHARE]                       # do not install share/OpenMS
  [NO_MODELS]                      # install share/OpenMS without models/ (21 MB; only PeptDeep users need it)
  [NO_SYSTEM_RUNTIME]              # Windows: do not include(InstallRequiredSystemLibraries)
  [RESOLVE_DEPENDENCIES]           # copy only the libraries the targets load (see below)
  [SIGNING_IDENTITY <identity>]    # macOS: re-sign every installed Mach-O file with this identity
)

What it does:

  1. install(TARGETS <targets> RUNTIME DESTINATION <bin> LIBRARY DESTINATION <lib> COMPONENT <component>).
  2. Sets INSTALL_RPATH on each target that does not set one, computed from the two destinations: $ORIGIN/<relative path from bin to lib> on Linux, @executable_path/... (executables) or @loader_path/... (shared libraries) on macOS. Nothing on Windows.
  3. Installs the OpenMS runtime libraries into <lib>:
    • For an SDK (see 3.), install(DIRECTORY "${OPENMS_LIB_DIR}/" ...) restricted to shared libraries (*.so*, *.dylib, *.framework; on Windows ${OPENMS_BIN_DIR}/*.dll), excluding cmake/, static archives and import libraries. This is correct for an SDK by construction: sdk_prune_dependencies.cmake has reduced lib/ to the runtime closure of libOpenMS, libOpenSwathAlgo and libOpenMS_CLI, so there is nothing to resolve and nothing to get wrong, and it needs no dumpbin/otool/objdump at install time.
    • For a plain cmake --install prefix, or with RESOLVE_DEPENDENCIES, install(TARGETS ... RUNTIME_DEPENDENCY_SET openms_runtime) plus install(RUNTIME_DEPENDENCY_SET openms_runtime DESTINATION <lib> DIRECTORIES ${OPENMS_LIB_DIR} ${OPENMS_BIN_DIR} PRE_EXCLUDE_REGEXES ... POST_EXCLUDE_REGEXES ...), the mechanism the installers use. In such a prefix lib/ holds only the OpenMS libraries and the dependencies live in the system or conda prefixes, so they have to be resolved. The exclusion lists (System32, api-ms-*, MSVC runtime DLLs, /usr/lib, /System/, libc/libstdc++/libgomp on Linux) are the ones in cmake/package_general.cmake; factor them into cmake/OpenMSRuntimeExcludes.cmake, installed alongside, so that the installers and the helper cannot drift apart.
  4. install(DIRECTORY "${OPENMS_DATA_DIR}/" DESTINATION <share>/OpenMS) unless NO_SHARE; with NO_MODELS, PATTERN "models" EXCLUDE. The function checks that <share> is ../share relative to <bin> (or ../../../share), the two layouts File::getOpenMSDataPath() probes, and warns otherwise; the deployed tool would not find its data.
  5. Windows: set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION <bin>), set(CMAKE_INSTALL_OPENMP_LIBRARIES TRUE), include(InstallRequiredSystemLibraries) unless NO_SYSTEM_RUNTIME, as cmake/package_nsis.cmake does for the OpenMS installer.
  6. macOS with SIGNING_IDENTITY: an install(CODE) step that runs codesign --force --options runtime --timestamp --sign <identity> over every Mach-O file under <bin> and <lib> of the install prefix. Without the option, signatures are left as they are: copying does not invalidate them, so a locally deployed tree runs; notarization remains the distributor's step.
  7. openms_install_runtime() records nothing global and can be called once per consumer project; calling it from the top-level CMakeLists.txt before include(CPack) is the documented use.

Sizes for orientation, from the Linux x86_64 SDK of #10248 (with ONNX): lib/ 203 files, 259 MB; share/OpenMS 81 MB. A deployed tool is therefore about 340 MB unpacked, 120 MB compressed, dominated by libOpenMS's own closure; NO_MODELS and RESOLVE_DEPENDENCIES are the only real levers, and both are small.

3. An SDK marker in the package

The helper has to know whether OPENMS_LIB_DIR is a self-contained runtime directory (SDK) or a bare library directory (cmake --install prefix). package_sdk.sh writes lib/cmake/OpenMS/OpenMSSDK.cmake (Windows: CMake/OpenMSSDK.cmake) into the staged SDK:

set(OpenMS_SDK TRUE)
set(OpenMS_SDK_PLATFORM "Linux-x86_64")      # from the archive name
set(OpenMS_SDK_MACOS_MINIMUM "15.0")         # macOS only, the value the README states

cmake/OpenMSConfig.cmake.in includes the file when it exists and sets OpenMS_SDK FALSE otherwise, the way it already includes the CLI and GUI target files only when present. Consumers get OpenMS_SDK for their own messages and checks; the helper uses it to choose between copying and resolving.

4. Documentation
  • cmake/OpenMSSDKReadme.txt: a section "Deploying your program" with the five-line example, the resulting layout (bin/, lib/, share/OpenMS), and the per-platform notes: Linux inherits the SDK's glibc baseline (Ubuntu 24.04), and a .deb that bundles libraries is fine for a vendor package but not for a distribution; Windows needs the MSVC runtime, which the helper installs; macOS needs re-signing and notarization for distribution.
  • doc/openms/docs/manual/develop/link-external-code-to-openms.md: a "Deploying" section with the same content, plus RESOLVE_DEPENDENCIES for source installations.
  • CHANGELOG.
5. Tests
  • src/tests/external: the example consumer project gets an install stage with openms_install_runtime(TARGETS TestExternalCodeCLI TestExternalCodePeptDeep) (the latter for OpenMS_WITH_ONNX), so the example that documents building also documents deploying.
  • src/tests/CMakeLists.txt (OPENMS_TEST_INSTALLED_CONSUMER): three new fixture steps after TestExternalCode_build, in the pattern of the existing _install/_move/_configure/_build/_run chain and of TestExternalCodeRelocated_move:
    • TestExternalCode_deploy: cmake --install <consumer build> --prefix <scratch>/deployed.
    • TestExternalCode_deploy_move: rename the installation prefix the consumer was built against, so that the deployed tree cannot reach back to it.
    • TestExternalCode_deploy_run: run <deployed>/bin/TestExternalCodeCLI -greeting ... and, with ONNX, TestExternalCodePeptDeep, under a scrubbed environment (--unset LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, DYLD_FALLBACK_LIBRARY_PATH, OPENMS_DATA_PATH; on Windows a PATH without the installation). Then assert on the tree: no headers, no lib/cmake, no .a/.lib; share/OpenMS/LICENSES holds the third-party texts in a vcpkg build; on Windows vcruntime*.dll and msvcp*.dll are present; on macOS codesign --verify passes on every Mach-O file.
  • tools/ci/package_sdk.sh: after the consumer build against the extracted SDK, deploy it, delete the extracted SDK, run the deployed tools the same way. This runs in release.yml on all four build jobs and is the check a user of the helper actually relies on: the deployed tree is self-contained.
  • A CPack smoke test in the same chain: cpack -G TGZ (ZIP on Windows) in the consumer build, and a listing of the archive that shows bin/, lib/ and share/OpenMS/LICENSES/. Cheap, and it catches a helper that emits install rules CPack does not pick up (component mismatches, missing CPACK_COMPONENTS_ALL).

Implementation plan

Two PRs, the first independent of the second:

PR A: third-party licenses (about half a day)

  1. cmake/install_third_party_licenses.cmake, included from src/openms/CMakeLists.txt.
  2. "Licenses" section in cmake/OpenMSSDKReadme.txt.
  3. Check in tools/ci/package_sdk.sh.
  4. CHANGELOG.

PR B: deployment helper, marker, tests, docs (two to three days including the three-platform CI iterations)

  1. cmake/OpenMSRuntimeExcludes.cmake: the exclusion regexes moved out of cmake/package_general.cmake; that file includes it. No behaviour change for the installers.
  2. cmake/OpenMSDeploy.cmake with openms_install_runtime(); installed by cmake/export_macros.cmake; included from cmake/OpenMSConfig.cmake.in.
  3. SDK marker: package_sdk.sh writes OpenMSSDK.cmake; OpenMSConfig.cmake.in includes it when present.
  4. Install stage in src/tests/external/CMakeLists.txt; fixture steps in src/tests/CMakeLists.txt; deployment step in package_sdk.sh.
  5. README, developer manual, CHANGELOG.

Suggested order inside PR B: 1 and 2 first with the Linux consumer test, then macOS (signing) and Windows (system runtime, DLL directory), then the SDK check. The consumer tests run in the ci-base presets on every PR, so the three platforms are covered before release.yml ever runs the SDK path.

Acceptance criteria

  • share/OpenMS/LICENSES in the SDK archives and the installers of a vcpkg build holds one license text per bundled port, and package_sdk.sh fails when it does not.
  • find_package(OpenMS CONFIG) provides openms_install_runtime() and OpenMS_SDK from the SDK and from a cmake --install prefix, on Linux, macOS and Windows.
  • The five-line consumer above, installed with cmake --install, yields a tree that runs its tool with the SDK removed and a scrubbed environment, and finds share/OpenMS without OPENMS_DATA_PATH; proven by the installed-consumer tests on every PR and by the SDK check in release.yml.
  • The deployed tree contains no headers, CMake files, static archives or import libraries; on Windows it contains the MSVC runtime; on macOS every Mach-O file passes codesign --verify, and with SIGNING_IDENTITY carries that identity.
  • cpack -G TGZ/ZIP of the consumer produces an archive with bin/, lib/ and share/OpenMS/LICENSES/.
  • SDK README and developer manual document deployment; CHANGELOG entries for both PRs.

Open questions

  • Default for a non-SDK installation: resolve dependencies automatically (proposed), or require RESOLVE_DEPENDENCIES explicitly and otherwise copy OPENMS_LIB_DIR even though it is incomplete? Automatic is the useful default; the option is then only a way to force resolution for an SDK.
  • Should the license texts also carry a mapping "port to bundled files"? Generating it needs the runtime closure at configure time. Proposed: no; the port names are enough for attribution.
  • Should the OpenMS installers get a "Licenses" page or menu entry now that the texts are there? Separate, small, optional.
  • share/OpenMS/THIRDPARTY and TOOLS are needed by TOPP tools that spawn search engines and by the tool registry; a deployed custom tool needs neither, but excluding them saves little. Proposed: copy everything unless NO_SHARE.

Related

  • #10248: the SDK this builds on; the README there already states the runtime layout and OPENMS_DATA_PATH.
  • TestExternalCodeRelocated (src/tests/external/relocated): the "move the prefix away" pattern the deployment check reuses.
  • cmake/package_general.cmake, cmake/package_nsis.cmake: the installers' runtime-dependency and system-runtime handling the helper mirrors.
  • #10230 (Debian package hygiene): unrelated in code, but the same class of "what exactly do we ship" questions.
Lenguaje dominante
C++
Estrellas
623
Forks
441
Merge medio
18 h 15 min
PR fusionados (30 d)
255

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de OpenMS/OpenMS

Todos los issues de OpenMS/OpenMS

Issues similares

Más issues de C++

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.