# How to install multiple FILE\_SET for a library in a generic way?

**URL:** https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489
**Category:** Development
**Tags:** gen:ninja
**Created:** [January 31, 2026, 9:07pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489 "2026-01-31T21:07:20Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [January 31, 2026, 9:07pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/1 "2026-01-31T21:07:20Z")

</div>

With this CMake code snippet from `boost/tools/cmake` I can install i.e Boost::any lib with `HEADERS` and `CXX_MODULES`.

## But it fails, if there is more than one FILE\_SET used in a library target!

```cmake
  if(NOT CMAKE_VERSION VERSION_LESS 3.28)
    get_target_property(INTERFACE_CXX_MODULE_SETS ${LIB} INTERFACE_CXX_MODULE_SETS)
    if(INTERFACE_CXX_MODULE_SETS)
      boost_message(DEBUG "boost_install_target: '${__TARGET}' has INTERFACE_CXX_MODULE_SETS=${INTERFACE_CXX_MODULE_SETS}")
      set(__INSTALL_CXX_MODULES FILE_SET ${INTERFACE_CXX_MODULE_SETS} DESTINATION ${CMAKE_INSTALL_DATADIR})
    endif()
  endif()

  if(NOT CMAKE_VERSION VERSION_LESS 3.23)
    get_target_property(INTERFACE_HEADER_SETS ${LIB} INTERFACE_HEADER_SETS)
    if(INTERFACE_HEADER_SETS)
      boost_message(DEBUG "boost_install_target: '${__TARGET}' has INTERFACE_HEADER_SETS=${INTERFACE_HEADER_SETS}")
      set(__INSTALL_HEADER_SETS FILE_SET ${INTERFACE_HEADER_SETS})
    endif()
  endif()

  install(TARGETS ${LIB} EXPORT ${LIB}-targets
    # explicit destination specification required for 3.13, 3.14 no longer needs it
    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    PRIVATE_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
    PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
    # explicit needed if used starting with cmake v3.28
    # XXX FILE_SET CXX_MODULES DESTINATION "${CMAKE_INSTALL_DATADIR}"
    ${__INSTALL_CXX_MODULES}
    # Any module files from C++ modules from PUBLIC sources in a file set of type CXX_MODULES will be installed to the given DESTINATION.
    # explicit needed if used starting with cmake v3.23
    # XXX FILE_SET HEADERS
    ${__INSTALL_HEADER_SETS}
  )

```

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [January 31, 2026, 9:13pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/2 "2026-01-31T21:13:02Z")

</div>

```auto
cmake --preset release --log-level=TRACE # XXX --fresh
Preset CMake variables:

  CMAKE_BUILD_TYPE="RelWithDebInfo"
  CMAKE_CXX_EXTENSIONS:BOOL="TRUE"
  CMAKE_CXX_SCAN_FOR_MODULES:BOOL="TRUE"
  CMAKE_CXX_STANDARD="23"
  CMAKE_CXX_STANDARD_REQUIRED:BOOL="TRUE"
  CMAKE_EXPORT_COMPILE_COMMANDS:BOOL="TRUE"
  CMAKE_INSTALL_MESSAGE="LAZY"
  CMAKE_INSTALL_PREFIX:PATH="/Users/clausklein/Workspace/cpp/beman-project/execution26/stagedir"
  CMAKE_PREFIX_PATH:STRING="/Users/clausklein/Workspace/cpp/beman-project/execution26/stagedir"
  CMAKE_SKIP_TEST_ALL_DEPENDENCY:BOOL="FALSE"

-- use ccache
-- The CXX compiler identification is GNU 15.2.0
-- Checking whether CXX compiler has -isysroot
-- Checking whether CXX compiler has -isysroot - yes
-- Checking whether CXX compiler supports OSX deployment target flag
-- Checking whether CXX compiler supports OSX deployment target flag - yes
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/local/bin/g++ - skipped
-- Detecting CXX compile features
CMake Warning (dev) at /Users/clausklein/.direnv/python-3.14/lib/python3.14/site-packages/cmake/data/share/cmake-4.2/Modules/Compiler/CMakeCommonCompilerMacros.cmake:248 (cmake_language):
  CMake's support for `import std;` in C++23 and newer is experimental. It
  is meant only for experimentation and feedback to CMake developers.
Call Stack (most recent call first):
  /Users/clausklein/.direnv/python-3.14/lib/python3.14/site-packages/cmake/data/share/cmake-4.2/Modules/CMakeDetermineCompilerSupport.cmake:113 (cmake_create_cxx_import_std)
  /Users/clausklein/.direnv/python-3.14/lib/python3.14/site-packages/cmake/data/share/cmake-4.2/Modules/CMakeTestCXXCompiler.cmake:83 (CMAKE_DETERMINE_COMPILER_SUPPORT)
  CMakeLists.txt:11 (project)
This warning is for project developers. Use -Wno-dev to suppress it.

-- Detecting CXX compile features - done
-- CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES=/usr/local/Cellar/gcc/15.2.0/include/c++/15;/usr/local/Cellar/gcc/15.2.0/include/c++/15/x86_64-apple-darwin23;/usr/local/Cellar/gcc/15.2.0/include/c++/15/backward;/usr/local/Cellar/gcc/15.2.0/lib/gcc/current/gcc/x86_64-apple-darwin23/15/include;/usr/local/Cellar/gcc/15.2.0/lib/gcc/current/gcc/x86_64-apple-darwin23/15/include-fixed;/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk/usr/include;/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk/System/Library/Frameworks
-- BEMAN_USE_STD_MODULE=ON
-- CMAKE_CXX_COMPILER_IMPORT_STD=23;26
-- CMAKE_CXX_MODULE_STD=ON
-- BEMAN_HAS_IMPORT_STD=ON
-- BEMAN_USE_MODULES=ON
-- CMAKE_CXX_SCAN_FOR_MODULES=ON
-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY
-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY - Success
-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY
-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY - Success
-- Performing Test COMPILER_HAS_DEPRECATED_ATTR
-- Performing Test COMPILER_HAS_DEPRECATED_ATTR - Success
-- beman-install-library: COMPONENT execution for TARGET 'beman.execution'
-- beman-install-library: 'beman.execution' has INTERFACE_CXX_MODULE_SETS=CXX_MODULES
-- beman-install-library: COMPONENT execution for TARGET 'beman.execution_headers'
-- beman-install-library: 'beman.execution_headers' has INTERFACE_HEADER_SETS=public_headers;detail_headers
CMake Error at cmake/beman-install-library-config.cmake:142 (install):
  install TARGETS given unknown argument "detail_headers".
Call Stack (most recent call first):
  CMakeLists.txt:77 (beman_install_library)

-- Configuring incomplete, errors occurred!

```

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [January 31, 2026, 9:41pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/3 "2026-01-31T21:41:43Z")

</div>

### The only working generic solution (today)

You must emit one install(TARGETS) call per file set.  
That feels wrong, but it is the only valid approach.

#### Minimal correct pattern:

```cmake
get_target_property(header_sets ${target} HEADER_SETS)

foreach(set IN LISTS header_sets)
  install(TARGETS ${target}
    FILE_SET ${set}
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
  )
endforeach()

```

#### Yes:

Multiple install(TARGETS) calls for the same target are allowed  
Each call installs only the referenced file set  
CMake merges them internally  
This is undocumented, but confirmed by Kitware developers in practice.

#### But:

## It does not work!

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [January 31, 2026, 9:42pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/4 "2026-01-31T21:42:55Z")

</div>

```auto
CMake Error at cmake/beman-install-library-config.cmake:134 (install):
  install TARGETS target beman.execution_headers is exported but not all of
  its interface file sets are installed
Call Stack (most recent call first):
  CMakeLists.txt:78 (beman_install_library)

CMake Error at cmake/beman-install-library-config.cmake:134 (install):
  install TARGETS target beman.execution_headers is exported but not all of
  its interface file sets are installed
Call Stack (most recent call first):
  CMakeLists.txt:78 (beman_install_library)

CMake Error at cmake/beman-install-library-config.cmake:152 (install):
  install TARGETS target beman.execution_headers is exported but not all of
  its interface file sets are installed
Call Stack (most recent call first):
  CMakeLists.txt:78 (beman_install_library)

-- Configuring incomplete, errors occurred!

```

---

<div class="post-metadata">

### Author: ![vito.gamberini](https://discourse.cmake.org/user_avatar/discourse.cmake.org/vito.gamberini/32/4376_2.png) [@vito.gamberini](https://discourse.cmake.org/u/vito.gamberini)
#### Post date: [January 31, 2026, 9:54pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/5 "2026-01-31T21:54:39Z")

</div>

> [@ClausKlein](#):
>
> This is undocumented, but confirmed by Kitware developers in practice.

It’s not confirmed by me. I don’t know who told you this would work.

Just list all the interface file sets in a single call to install, there is no “generic”.

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [January 31, 2026, 9:56pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/6 "2026-01-31T21:56:48Z")

</div>

> [@vito.gamberini](#):
>
> It’s not confirmed by me. I don’t know who told you this would work.

I know, it was from AI → [chatgpt.com](http://chatgpt.com) 😱

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [January 31, 2026, 10:06pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/7 "2026-01-31T22:06:32Z")

</div>

> [@vito.gamberini](#):
>
> Just list all the interface file sets in a single call to install, there is no “generic”.

The `Boost` project and `Beman` project need this!

### Would it be possible, that instead of sending an error message, CMake install() could do it implicit?

---

<div class="post-metadata">

### Author: ![vito.gamberini](https://discourse.cmake.org/user_avatar/discourse.cmake.org/vito.gamberini/32/4376_2.png) [@vito.gamberini](https://discourse.cmake.org/u/vito.gamberini)
#### Post date: [January 31, 2026, 10:29pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/8 "2026-01-31T22:29:30Z")

</div>

> [@ClausKlein](#):
>
> The `Boost` project and `Beman` project need this!

Can you better explain the use case? I don’t understand what’s wrong with listing the relevant file sets.

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [February 1, 2026, 2:54am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/9 "2026-02-01T02:54:42Z")

</div>

Both projects use a central infrastructure repo with CMake modules and one to install each library.

---

<div class="post-metadata">

### Author: ![vito.gamberini](https://discourse.cmake.org/user_avatar/discourse.cmake.org/vito.gamberini/32/4376_2.png) [@vito.gamberini](https://discourse.cmake.org/u/vito.gamberini)
#### Post date: [February 1, 2026, 2:56am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/10 "2026-02-01T02:56:21Z")

</div>

I’m aware, that doesn’t explain the use case here. There’s no reason the central infrastructure used by Beman and boost preclude projects from expressing the file sets associated with a given target

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [February 1, 2026, 2:58am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/11 "2026-02-01T02:58:50Z")

</div>

My question is how to write the needed code?

---

<div class="post-metadata">

### Author: ![vito.gamberini](https://discourse.cmake.org/user_avatar/discourse.cmake.org/vito.gamberini/32/4376_2.png) [@vito.gamberini](https://discourse.cmake.org/u/vito.gamberini)
#### Post date: [February 1, 2026, 3:03am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/12 "2026-02-01T03:03:11Z")

</div>

> [@ClausKlein](#):
>
> My question is how write the needed code?

The error is saying you didn’t install a file set associated with the target.

```cmake
install(
  TARGETS myTarget
  FILE_SET CXX_MODULES
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/myTarget/cxx-modules
  FILE_SET customFileSet
    DESTINATION ${CMAKE_INSTALL_DATADIR}/myTarget/custom-thing
)

```

Etc, for each file set you have associated with the target

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [February 1, 2026, 3:11am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/13 "2026-02-01T03:11:15Z")

</div>

This is not an option for a central cmake module.

Some targets do not have **FILE\_SET** , other more than one

---

<div class="post-metadata">

### Author: ![vito.gamberini](https://discourse.cmake.org/user_avatar/discourse.cmake.org/vito.gamberini/32/4376_2.png) [@vito.gamberini](https://discourse.cmake.org/u/vito.gamberini)
#### Post date: [February 1, 2026, 3:12am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/14 "2026-02-01T03:12:51Z")

</div>

If a target does not have a file set, the project owning that target will not list any file sets in the project’s call to `install()`. If a target has more than one file set, the project owning that target will list all the `INTERFACE` file sets in that project’s call to `install()`.

The person writing the `install()` command for a given target needs to know what the target is, what artifacts are associated with the target, and where they wish those artifacts to be installed. There is no way around this. That’s why we leave it to the project to call `install()`.

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [February 1, 2026, 3:27am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/15 "2026-02-01T03:27:17Z")

</div>

What to install and the destination to install is known.

How would you write the central generic cmake module?

---

<div class="post-metadata">

### Author: ![vito.gamberini](https://discourse.cmake.org/user_avatar/discourse.cmake.org/vito.gamberini/32/4376_2.png) [@vito.gamberini](https://discourse.cmake.org/u/vito.gamberini)
#### Post date: [February 1, 2026, 3:33am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/16 "2026-02-01T03:33:24Z")

</div>

I wouldn’t. In the same way any wrapper of the `install()` command needs to accept the list of targets from the project calling the wrapper, it also needs to accept the list of file sets.

```cmake
my_custom_install(
  TARGETS
    myTarget
    myOtherTarget
  FILE_SETS
    CXX_MODULES
    HEADERS
    myCustomFileSet
)

```

> [@ClausKlein](#):
>
> What to install and the destination to install is known.

If the file sets which need to be installed are known, list them in the `install()` command and the error will go away.

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [February 1, 2026, 3:41am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/17 "2026-02-01T03:41:00Z")

</div>

I don’t understand how to write the central code?

---

<div class="post-metadata">

### Author: ![vito.gamberini](https://discourse.cmake.org/user_avatar/discourse.cmake.org/vito.gamberini/32/4376_2.png) [@vito.gamberini](https://discourse.cmake.org/u/vito.gamberini)
#### Post date: [February 1, 2026, 3:45am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/18 "2026-02-01T03:45:33Z")

</div>

Then don’t, and have projects use `install()` directly. Or only wrap the parts you care about and forward the rest of `ARGN` directly to `install()` so projects can handle their file sets on their own.

---

<div class="post-metadata">

### Author: ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)
#### Post date: [February 1, 2026, 8:34pm UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/19 "2026-02-01T20:34:19Z")

</div>

From AI:

```cmake
function(beman_install_library name)
    set(options)
    set(oneValueArgs NAMESPACE EXPORT_NAME DESTINATION)
    set(multiValueArgs TARGETS)

    cmake_parse_arguments(
        BEMAN
        "${options}"
        "${oneValueArgs}"
        "${multiValueArgs}"
        ${ARGN}
    )

    if(NOT BEMAN_TARGETS)
        message(FATAL_ERROR
            "beman_install_library(${name}): TARGETS must be specified")
    endif()

    if(NOT BEMAN_NAMESPACE)
        set(BEMAN_NAMESPACE "${name}::")
    endif()

    if(NOT BEMAN_EXPORT_NAME)
        set(BEMAN_EXPORT_NAME "${name}-targets")
    endif()

    if(NOT BEMAN_DESTINATION)
        set(BEMAN_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
    endif()

    # --------------------------------------------------
    # Install each target with all of its file sets
    # --------------------------------------------------
    foreach(_tgt IN LISTS BEMAN_TARGETS)
        if(NOT TARGET "${_tgt}")
            message(FATAL_ERROR
                "beman_install_library(${name}): '${_tgt}' is not a target")
        endif()

        # Detect presence of C++ module file sets
        get_target_property(_module_sets "${_tgt}" CXX_MODULE_SETS)

        if(_module_sets)
            install(
                TARGETS "${_tgt}"
                EXPORT ${BEMAN_EXPORT_NAME}

                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}

                FILE_SET HEADERS
                    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}

                FILE_SET CXX_MODULES
                    DESTINATION "${BEMAN_DESTINATION}"
            )
        else()
            install(
                TARGETS "${_tgt}"
                EXPORT ${BEMAN_EXPORT_NAME}

                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}

                FILE_SET HEADERS
                    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
            )
        endif()
    endforeach()

    # --------------------------------------------------
    # Export targets
    # --------------------------------------------------
    install(
        EXPORT ${BEMAN_EXPORT_NAME}
        NAMESPACE ${BEMAN_NAMESPACE}
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${name}
    )
endfunction()

```

---

<div class="post-metadata">

### Author: ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)
#### Post date: [February 9, 2026, 3:35am UTC](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489/20 "2026-02-09T03:35:36Z")

</div>

Relevant `vtkModule.cmake` code which does this: [https://gitlab.kitware.com/vtk/vtk/-/blob/9579e280f3d7801ff1d88795ba0fa5b8ca784f3a/CMake/vtkModule.cmake#L5131-5166](https://gitlab.kitware.com/vtk/vtk/-/blob/9579e280f3d7801ff1d88795ba0fa5b8ca784f3a/CMake/vtkModule.cmake#L5131-5166)

[Next page](https://discourse.cmake.org/t/how-to-install-multiple-file-set-for-a-library-in-a-generic-way/15489.md?page=2)
