Skip to content

Fix transitive dependencies for static LSL::lsl - #304

Open
myd7349 wants to merge 1 commit into
sccn:devfrom
SharpLSL:fix-cmake-deps
Open

myd7349 wants to merge 1 commit into
sccn:devfrom
SharpLSL:fix-cmake-deps

Conversation

@myd7349

@myd7349 myd7349 commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Description

Previously, the required system and third-party dependencies were linked only to the lslobj object library. The lsl target linked lslobj through the build interface:

target_link_libraries(lsl PRIVATE $<BUILD_INTERFACE:lslobj>)

Because lslobj is not part of the installed export set, its dependencies were not included in the generated LSLConfig*.cmake files. Shared-library builds were unaffected, but consumers of a static liblsl had to link those dependencies manually.

For example, consumers currently need to provide pugixml(LSL_FETCH_PUGIXML=OFF) and platform libraries themselves:

find_package(LSL CONFIG REQUIRED)
find_package(pugixml CONFIG REQUIRED)

add_executable(EegDataSourceLSLRelay EegDataSourceLSLRelay.cpp)

target_link_libraries(EegDataSourceLSLRelay PRIVATE
    LSL::lsl
    pugixml::pugixml
    winmm
    iphlpapi
)

Ideally, consumers should only need to link LSL::lsl:

find_package(LSL CONFIG REQUIRED)

add_executable(EegDataSourceLSLRelay EegDataSourceLSLRelay.cpp)
target_link_libraries(EegDataSourceLSLRelay PRIVATE LSL::lsl)

Solution

For static builds, linking the dependencies directly to lsl, rather than only indirectly through lslobj, is important. CMake records the PRIVATE link dependencies of a static library as $<LINK_ONLY:...> entries in its exported INTERFACE_LINK_LIBRARIES. This allows consumers of the installed LSL::lsl target to receive the dependencies transitively without having to link them manually.

This is also why some properties are configured on both lslobj and lsl, for example:

# TargetObjLib.cmake
target_include_directories(lslobj
    PUBLIC
        $<INSTALL_INTERFACE:include>
)
# TargetLib.cmake
target_include_directories(lsl
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:${LSL_INSTALL_INTERFACE_INCLUDE_DIR}>
)

The same approach is required for the library dependencies.

The PR:

  1. Defines lsllinklibs in Dependencies.cmake to collect the system and third-party libraries required by lslobj and lsl.
  2. Links those dependencies to both lslobj and lsl.
  3. Updates Installation.cmake and the package configuration template to add the required find_dependency() calls to the generated LSLConfig.cmake.

pugixml Target Handling

Before pugixml 1.11, its CMake target was named pugixml; newer versions use pugixml::pugixml. When LSL_FETCH_PUGIXML is enabled, the fetched version is recent enough to use pugixml::pugixml directly, so the compatibility alias for the fetched target is unnecessary.

When LSL_FETCH_PUGIXML=OFF, the alias is retained because testing/CMakeLists.txt still links against pugixml::pugixml. It is created after the actual pugixml target name has been added to lsllinklibs.

Why Boost Is Excluded

Boost is a header-only, private compile-time dependency of lslobj, provided through the lslboost interface target. No Boost binaries are linked, and no Boost types appear in liblsl’s public headers. Since lslobj is linked into lsl only through $<BUILD_INTERFACE:lslobj>, Boost is not part of the installed target’s exported interface. Therefore, it does not belong in lsllinklibs, and the installed LSLConfig.cmake does not need find_dependency(Boost).

An earlier version of this change added Boost::boost and Boost::disable_autolinking to lsllinklibs; those entries and the corresponding find_dependency(Boost) call have been removed.

Testing

Manual testing was performed on Windows 11 and an Ubuntu 24.04 VM. Automated tests were run through GitHub Actions on the liblsl-issue-304-amend branch.

The branch contains three commits, each corresponding to one of the test runs below:

  1. Reproduces the issue described in this PR.
  2. Tests the patch included in this PR.
  3. Tests a separate patch that compiles pugixml’s source directly into lslobj when LSL_FETCH_PUGIXML=ON.

The third test uses the patch from the fix-cmake-deps-v3 branch; that patch is not included in this PR.

Each test run used the following GitHub Actions matrix, for 24 configurations:

matrix:
  os: [ubuntu-latest, macos-latest, windows-latest]
  linkage: [static, shared]
  bundled_boost: [ON, OFF]
  fetch_pugixml: [ON, OFF]

linkage maps to LSL_BUILD_STATIC; bundled_boost and fetch_pugixml map to LSL_BUNDLED_BOOST and LSL_FETCH_PUGIXML. Where supported, LSL_TOOLS and LSL_UNITTESTS were also enabled.

The consumer test project uses find_package(LSL CONFIG REQUIRED) and links its executables only to LSL::lsl. GetFullinfo exercises stream-info APIs that directly depend on pugixml. On Windows, the test also copies the shared LSL library beside lslver when testing a shared build.

Shared builds passed in all three test runs. The tables below show the results for static builds.

1. Reproducing the issue

All configurations failed because consumers could not resolve required symbols. These were primarily pugixml symbols; on Windows, socket API symbols were also missing.

Platform LSL_BUNDLED_BOOST=OFF, LSL_FETCH_PUGIXML=OFF LSL_BUNDLED_BOOST=OFF, LSL_FETCH_PUGIXML=ON LSL_BUNDLED_BOOST=ON, LSL_FETCH_PUGIXML=OFF LSL_BUNDLED_BOOST=ON, LSL_FETCH_PUGIXML=ON
macOS ❌ ❌ ❌ ❌
Ubuntu ❌ ❌ ❌ ❌
Windows ❌ ❌ ❌ ❌

2. Testing this PR’s patch

Configurations using a system-installed pugixml passed. Configurations with LSL_FETCH_PUGIXML=ON still failed.

Platform LSL_BUNDLED_BOOST=OFF, LSL_FETCH_PUGIXML=OFF LSL_BUNDLED_BOOST=OFF, LSL_FETCH_PUGIXML=ON LSL_BUNDLED_BOOST=ON, LSL_FETCH_PUGIXML=OFF LSL_BUNDLED_BOOST=ON, LSL_FETCH_PUGIXML=ON
macOS ✅ ❌ ✅ ❌
Ubuntu ✅ ❌ ✅ ❌
Windows ✅ ❌ ✅ ❌

3. Testing the separate pugixml source-integration patch

The third commit tests the separate patch from fix-cmake-deps-v3. It compiles pugixml’s source directly into lslobj when LSL_FETCH_PUGIXML=ON, rather than linking pugixml through a separate target. All tested static configurations passed.

Platform LSL_BUNDLED_BOOST=OFF, LSL_FETCH_PUGIXML=OFF LSL_BUNDLED_BOOST=OFF, LSL_FETCH_PUGIXML=ON LSL_BUNDLED_BOOST=ON, LSL_FETCH_PUGIXML=OFF LSL_BUNDLED_BOOST=ON, LSL_FETCH_PUGIXML=ON
macOS ✅ ✅ ✅ ✅
Ubuntu ✅ ✅ ✅ ✅
Windows ✅ ✅ ✅ ✅

This source-integration patch is not included in this PR. If the approach is agreed upon, it can be cherry-picked into this PR or submitted separately.

Open Issues

  1. lsl.pc.in has not yet been updated to reflect these dependency changes.
  2. The separate pugixml source-integration patch needs discussion: should it be added to this PR, or submitted as a follow-up?

myd7349 added a commit to SharpLSL/liblsl-ci-build that referenced this pull request Sep 25, 2026
myd7349 added a commit to SharpLSL/liblsl-ci-build that referenced this pull request Sep 25, 2026
myd7349 added a commit to SharpLSL/liblsl-ci-build that referenced this pull request Sep 25, 2026
Static builds of lsl did not forward its transitive dependencies
(Threads, Windows iphlpapi/winmm/mswsock/ws2_32, Linux rt/dl, system
pugixml) because they were linked PRIVATE via the lslobj object
library. Consumers linking LSL::lsl therefore had to manually add
these transitive dependencies themselves.
myd7349 added a commit to SharpLSL/liblsl-ci-build that referenced this pull request Sep 26, 2026
myd7349 added a commit to SharpLSL/liblsl-ci-build that referenced this pull request Sep 26, 2026
myd7349 added a commit to SharpLSL/liblsl-ci-build that referenced this pull request Sep 26, 2026
myd7349 added a commit to SharpLSL/liblsl-ci-build that referenced this pull request Sep 26, 2026
@myd7349 myd7349 changed the title WIP: Fix transitive dependencies for static LSL::lsl Fix transitive dependencies for static LSL::lsl Sep 26, 2026
@myd7349
myd7349 marked this pull request as ready for review September 26, 2026 03:29
@myd7349

myd7349 commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Generated CMake Package Files

The following comparison shows the generated CMake package files without and with this PR(on Windows 11):

Screenshot 2026-09-26 124433

With this PR:

  1. The old LSLConfig.cmake now is LSLTargets.cmake, which includes the required link dependencies for consumers of the static LSL::lsl target.
  2. The new generated LSLConfig.cmake calls find_dependency() for those dependencies before including LSLTargets.cmake.
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was LSLConfig.cmake.in                            ########

get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)

macro(set_and_check _var _file)
  set(${_var} "${_file}")
  if(NOT EXISTS "${_file}")
    message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
  endif()
endmacro()

macro(check_required_components _NAME)
  foreach(comp ${${_NAME}_FIND_COMPONENTS})
    if(NOT ${_NAME}_${comp}_FOUND)
      if(${_NAME}_FIND_REQUIRED_${comp})
        set(${_NAME}_FOUND FALSE)
      endif()
    endif()
  endforeach()
endmacro()

####################################################################################

include(CMakeFindDependencyMacro)

find_dependency(Threads)

if(NOT OFF)
    find_dependency(pugixml)
endif()

include("${CMAKE_CURRENT_LIST_DIR}/LSLTargets.cmake")

check_required_components(LSL)

This allows consumers to link LSL::lsl without manually finding or linking its dependencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant