Excluding targets/libraries from top level

Suppose I have a repo that contains code that is compiled for multiple applications, each with their own flavour of that code base (files, compile defines, options, etc). Within that code base I would have a cmakelists.txt file that specifies the targets, call them app1 and app2.

In a second (and third) repo of code I have the other portions of the two applications. In each I have the top level cmake file that specifies the executable. So for app1 I would have:
add_executable( app1 ) and sub directory paths into the common code repo.

The problem is that cmake will complain because it finds app2 targets within the common repo and it doesn’t have a target to link them to. To get around this I can simply define an app2 executable within app1’s cmake file, but to me that seems a hack.

Is there a way within app1’s cmakefile to have it ONLY look (and parse) for the targets specified within the top level cmake file?

CMake has no idea where a target is defined just by its name; it certainly can’t exclude a directory without reading and parsing it first (and CMake has no way to “undo” an add_subdirectory). You’ll need to include only the directories that matter based on other factors.

Thanks Ben, based on your response however I’m not sure that I was asking the correct thing. Let me rephrase/confirm.

If I have an application level cmake file that specifies a subdirectory to look in, that cmake file MUST be able to resolve all target_link_libraries() that it finds in it’s sub directories?

That seems a little restrictive to me. Let’s say that I have a repo that contains code for two different video codecs and within it I create target_link_libraries() for each codec. If I then want to use that code base to create an application that only uses one of those libraries I still have to specify an add_library() line for the other codec library even though I’m not planning on building it within my application?

There is no need to have a 1:1 correspondence between CMakeLists.txt files and code directories. You should be able to have two CMakeLists.txt files (each in its own directory) operating on one codebase, each defining only the targets relevant to one project. Logically, a CMakeLists.txt file corresponds to a project, not to a directory.

Thanks Petr,

I guess it’s just a side effect of how the cmake files were created in this instance. What you are saying is that in a library codespace where you have dirCommon, dirCodecA and dirCodecB you should setup your cmake files to have one in dirCodecA and one in dirCodecB (that both include what they need from dirCommon). Instead, we have a cmake file in dirCommon that defines the files/options to use for each of the two variants.

If that’s the way that cmake works then I guess we can look at changing it, but to me that’s a failing of cmake in that your project can only include paths to cmake files that you are interested in and that those directories have to be unique to your project. For small projects that may be workable, but for large projects that either means massive cmake files or a series of dummy directories to hold the various cmake files.

On the other hand, I would have thought that there would be a cmake directive to force it to exclude any unresolved/unused libraries.

Defining this is hard. It could be used via export mechanisms or by loading as a plugin, so pruning is difficult in the abstract sense. What you can probably do is use add_subdirectory(EXCLUDE_FROM_ALL) in some way to make them at least not part of the default build. Your enabled projects will then pull them into the build graph only as needed.

I obviously don’t have knowledge of the inner workings of cmake and only have a vague high level arm waving knowledge at best, but to me I would have thought that cmake would have been driven off of the top level cmake file and from that define the targets that you’re interested in. The requirement to resolve everything that it finds in it’s paths seems harsh to me.

I mean if I create a cmake that defines an application (and it’s linking libraries and paths to search), why would it care that it finds libraries fragments in those paths that I’m not using?

The simple solution is to create a dummy target in the top level that you’ll never need/build, if there isn’t an explicit directive to essentially do that, then that’s fine, just seems odd to me. Thanks for the response(s).

CMake has no correlation between targets and directories. If you ask for target foo, it could be in path bar/baz. CMake has no idea without reading bar/baz first. Similarly, a directory can make multiple targets too, so wanting one of those possibly many targets is a tricky proposition since there’s no “undo” once CMake reads a directory.

@craig.scott might have some suggestions here, but I think using EXCLUDE_FROM_ALL to disable default building of libraries that you don’t need by other mechanisms is likely the best available today.

Use CMake cache variables to enable/disable parts of the project. Don’t try to have incomplete information in the things that CMake sees and then try to avoid problems by just not building the incomplete targets.

A typical arrangement I would use for this sort of scenario might look something like this:

option(MYPROJ_ENABLE_SOMEFEATURE "Helpful doc string here" ON)  # See below
if(MYPROJ_ENABLE_SOMEFEATURE)
    add_subdirectory(somefeature_subdir)
endif()

In the above, I’ve explicitly specified the default value for the switch to be ON. If I left that out, the default would be OFF. In your project, you may want to do some logic before that to work out whether you want the default to be ON or OFF. For example, you may check if certain required dependencies are available.

The above also assumes your project is nicely structured such that this optional part of the project is self-contained in a subdirectory. It doesn’t have to be that way, but it is often clearer and easier to work with and maintain like that. If you need to keep things in the same directory, just put the feature-specific bits directly in the if() block. Eg

option(MYPROJ_ENABLE_SOMEFEATURE "Helpful doc string here" ON)  # See below
if(MYPROJ_ENABLE_SOMEFEATURE)
    add_library(somefeature src1.cpp src2.cpp)
    target_link_library(someotherthing PRIVATE somefeature)
    # ... etc ...
endif()

Another variation is to choose between a set of implementations (kinda of like an enum in C++). This would be suitable where exactly one choice from a limited set of choices has to be made.

set(MYPROJ_FEATUREIMPL SLOW_BUT_SAFE CACHE STRING "Helpful doc string here")
if(MYPROJ_FEATUREIMPL STREQUAL SLOW_BUT_SAFE)
    # ... Do whatever
elseif(MYPROJ_FEATUREIMPL STREQUAL SCARY_FAST)
    # ... Do whatever
else()
    message(FATAL_ERROR "${MYPROJ_FEATUREIMPL} is not a supported implementation.")
endif()

You can add more niceties to the above, such as making CMake GUI aware of the supported choices so that it presents a combobox to the user instead of an arbitrary text entry field.

Thanks Craig, I’ll look at using the variable option.