Is install() of a target with EXCLUDE_FROM_ALL == TRUE really undefined?

In the documentation of the install command we can read:

Installing a target with the EXCLUDE_FROM_ALL target property set to TRUE has undefined behavior.

I wonder whether this is really the case. Installation seems to be skipped for such targets.

Assuming there is undefined behavior which we must avoid:
For those targets I could skip install or call install with the EXCLUDE_FROM_ALL argument.

Now, the EXCLUDE_FROM_ALL target property however allows generator expressions.
The EXCLUDE_FROM_ALL argument of install does not.
For targets that have EXCLUDE_FROM_ALL set to some generator expression I cannot just skip install nor can I use install’s EXCLUDE_FROM_ALL option.

Can someone clarify whether the documentation is correct - and if yes, how to handle the latter case of targets with EXCLUDE_FROM_ALL set to a generator expression?

The main issue is that the install script has no idea how much of the build has completed. The install target just depends on all, so the installation of EXCLUDE_FROM_ALL targets doesn’t guarantee they’ll actually be available. The documentation is basically just a license for CMake to skip the if (EXISTS) check for complicated things (probably historical).

As for install(EXCLUDE_FROM_ALL <genex>), the main question is in what context is the genex evaluated? It’s easy for install(TARGETS) (it’d be evaluated once per target), but install(FILES) won’t have any implicit target context. If such questions have reasonable answers, I think it’d just be a feature request.

what about if the install command is an EXCLUDE_FROM_ALL target, but the install cmd had the OPTIONAL flag? That should be well defined, no?

That’s fine as OPTIONAL doesn’t care if the source file exists or not.

I see. Thanks for your answer!

For the install(EXCLUDE_FROM_ALL <genex>) case I was indeed thinking of install(TARGETS), not install(FILES).

IMHO: This is a bigger problem in use case with FetchContent or CPM.cmake!

see Show the EXCLUDE_FROM_ALL problem by ClausKlein · Pull Request #5 · ClausKlein/ModernCmakeStarter · GitHub

We want to compile only the target we use, and we try to prevent compiler warnings and clang-tidy issues.

But we need to install this target?

@craig.scott Is this behaiviour really undefined or intended?

Can you be specific about which aspect of behavior you are referring to? Rather than me guess/assume, it would be better to be crystal clear what you want to clarify.

NOTE: EXCLUDE_FROM_ALL must be OFF or fmt target will NOT be installed! CK

 option(FMT_INSTALL "create an installable target" YES)
 CPMAddPackage(
   NAME fmt
   GIT_TAG 9.1.0
   GITHUB_REPOSITORY fmtlib/fmt
   EXCLUDE_FROM_ALL NO
   SYSTEM YES # used if CMake v3.25 is available
 )

This use FetchContent to clone the git repo and add_subdirectory(... fmt SYSTEM)

I’m not familiar with the internals of what CPM does with the EXCLUDE_FROM_ALL keyword. FetchContent itself does not have that keyword, so you’d have to look at what CPM is doing with it. That keyword is used for a number of CMake commands, but not FetchContent. I think you should take that up with the CPM maintainers. Your query doesn’t seem to be directly aligned with the focus of this particular discussion topic (which does not involve FetchContent).

If set to ON, the default, it results in

add_subdirectory(... fmt SYSTEM EXCLUCE_FROM_ALL)

… and if I use cmake -D FMT_INSTALL=NO, it results in an CMake configure error:

CMake Error: install(EXPORT "greeterTargets" ...) includes target "greeter" which requires target "fmt" that is not in any export set.
-- Generating done
CMake Generate step failed.  Build files cannot be regenerated correctly.
make: *** [install] Error 1
bash-3.2$ 

Please take the discussion of CPM and EXCLUDE_FROM_ALL to a separate thread. FetchContent does not add EXCLUDE_FROM_ALL anywhere. Let’s keep this discussion here focused on the original poster’s topic, which has more to do with the EXCLUDE_FROM_ALL target property and the same keyword with the install() command.

Regarding the overarching theme of excluding things from the ALL target and then installing them, the thing to remember is that if a target is excluded from ALL, then you the user are responsible for ensuring it is built if you then do something that wants to install that target. The documentation for the install() command calls this undefined behavior, but that’s perhaps a bit strong. In reality, my preceding sentence is the real constraint, as far as I’m aware (but I haven’t checked how export sets affect the picture).

OK, thanks :confused:

IMHO: It is too complicated! :exploding_head:
see too CMake Error: install(EXPORT "ProjectTargets" ...) includes target "xxx" which requires target "yyy" that is not in any export set.

and Feature/exclude from all problem by ClausKlein · Pull Request #7 · ClausKlein/ModernCmakeStarter · GitHub