How to exclude "Unspecified" components from CPack archive generator?

I would like to exclude nearly all the files installed by external projects (that are otherwise needed as dependencies) when installing a project and when creating the packages, but I cannot quite figure out how to do that properly.

I’m trying to use CPack on a project with tons of external dependencies which are fetched via FetchContent and built from source. But most of those dependencies are just build-time dependencies and I don’t want to end up with them being installed alongside our own targets.

For example: the sources need Google Test for running the internal tests, but I don’t want to install GTest when the project gets installed. The project needs Boost. It can also simply take Boost from the system, but in case it fetches the sources during the configure stage, it just compiles static libraries and the headers are not needed by the end user, they are just private dependencies required during the build of the project.

Basically: except for a small percentage of external dependencies that I want to get installed, I don’t want anything else than what’s defined inside the project.

Maybe I just don’t know how to prevent the installation of external targets (I would be grateful if someone can point it out how to do that). The only useful way I found was to define COMPONENT for each internal installation target and install just the components I need. It is still annoying because cmake --install . doesn’t work out of the box, but it’s somewhat acceptable.

Now I tried to build packages with CPack.

With

set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
set(CPACK_DEB_COMPONENT_INSTALL YES)

and a carefully crafted set of cpack_add_install_type, cpack_add_component_group, cpack_add_component, … I can get precisely the Debian packages that I want, except that I don’t find a way to get rid of myproject-unspecified_0.1.1_amd64.deb that bundles all the unwanted targets into something I didn’t ask for. Somewhat annoying, but I can easily just throw that file away, that’s not a big deal.

I also tried using

get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")

without any success.

But now I want to create a .zip file for Windows.

If I set

set(CPACK_ARCHIVE_COMPONENT_INSTALL YES)

then I get precisely the files I need, except that they are in two separate zip files when I would in fact prefer a single one. In contrast to DEB, I don’t get any Unspecified targets which is really really good, I’m just baffled why it’s inconsistent.

Now, the weird thing is that as soon as I set that variable to NO (which is the default), all the “junk files” (installation targets from external projects) that I wanted to get rid of end up being in that single zip.

The installer generated by NSIS seems fine, but I would really like to provide a simple zip as well.

So my main question is: how do I avoid all the “Unspecified” components from ending up in the ZIP file for Windows as generated by CPack?

Subquestion: what’s the proper way to set a different CPACK_PACKAGING_INSTALL_PREFIX for Debian packages (for example /opt/myproject) and for Windows zips (ideally empty)?

This issue might be of interest to you, though it is way more indirect than you probably want. I don’t really have enough experience with CPack to answer the rest though, sorry.

The CPack generators really need a sweep of consistency done over them :confused: .

From the first sight the linked issue seems unrelated.

I started looking at CMake source.

First, I found an example of a slightly more advanced usage of CPack at https://gitlab.kitware.com/cmake/cmake/-/blob/master/CMakeCPackOptions.cmake.in
which is something that comes extremely handy as a role model for handling some scenarios.

Second, I found the following code which sounds super strange to me. Why should Unspecified components always be installed?

# CMake always generates a component named "Unspecified", which is
# used to install everything that doesn't have an explicitly-provided
# component. Since these files should always be installed, we'll make
# them hidden and required.
set(CPACK_COMPONENT_UNSPECIFIED_HIDDEN TRUE)
set(CPACK_COMPONENT_UNSPECIFIED_REQUIRED TRUE)

Maybe it already helps to rename the default component to e.g. “Default” (IMHO a much better name) or “base” (better for package naming).

Or just get used to always specify a component for install rules.

Because historically (when CPack did not provide component install) the fact that some install rule did not specify COMPONENT was considered an unwanted left-over. The idea here was to be conservative for project going from monolithic to component install.

If you go for component install either you specify COMPONENT for ALL your install rule or you specify
the explicit list of component in CPACK_COMPONENTS_ALL.
That was the “historical” design idea.

I agree with @hsattler that naming it “Default” may be better, note however that you may specify
CPACK_COMPONENT_UNSPECIFIED_xxxx as with any other component including
CPACK_COMPONENT_UNSPECIFIED_DISPLAY_NAME

Concerning the inconsistency between Debian and Archive generator it’s certainly a bug.
Now if one keeps CPACK_COMPONENT_UNSPECIFIED_REQUIRED to TRUE I would expect the package to be build. May be this could be changed or at least properly documented.

I cannot really specify a component name for dependencies, except by maybe explicitly setting something like

set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "Boost")

just before compiling an external dependency.

Anyway, I tried to add

set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "Trash")

to the top of my project.

If I set set(CPACK_ARCHIVE_COMPONENT_INSTALL YES) then I just get one zip with a different name (called Trash instead of Unspecified) that needs to be removed. But with

set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "Trash")
set(CPACK_ARCHIVE_COMPONENT_INSTALL NO)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Trash")

all the unwanted files are still there in the zip.

I think you need to set
CPACK_COMPONENT_TRASH_REQUIRED to FALSE

I tested it and that doesn’t help either.

My understanding is that this is the default behaviour already anyway. If that value is set to ON/TRUE, it only means that in cases like the NSIS installer the user doesn’t have a choice of whether or not to install the component: it will force include it. The default value OFF/FALSE just means “do not force inclusion” rather than “exclude it”.

The following seems to help, but only when combined with set(CPACK_ARCHIVE_COMPONENT_INSTALL YES). A monolithic zip continues to include all the files I don’t want.

get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Trash")