Installing dependencies imported with FetchContent()

Say that I that I have two private projects: A and B. The project B is imported to project A by the FetchContent command, say:

#project_B.cmake in project A
FetchContent_Declare(project_B
      GIT_REPOSITORY "git@project_B.git"
      GIT_TAG main
)

The project_B was created with the good practices of consumable projects. Including the following guard for installation:

#CMakeLists.txt in project B
if (PROJECT_IS_TOPLEVEL)
  include(Install.cmake)
endif()

Where Install.cmake contains all the necessary configurations for project B installation.
Now, I am intended to install project A, how should I proceed to install project_B from project_A?
I mean, should I repeat all the configurations written in install.cmake in the subsequent part of the previous code?

#project_B.cmake in project A
FetchContent_Declare(project_B
      GIT_REPOSITORY "git@project_B.git"
      GIT_TAG main
)
...
#rewrite install.cmake from project B here?

If you have control over both A and B, I would define their install logic such that they always specify components for things they install, and those components should have project-specific names. Then a higher level parent project can pick and choose which components it deems relevant and only install those when it is creating a package. Take a look at the CPACK_COMPONENTS_ALL variable for how to specify which components to include in a package.

Thank you for your quick reply. Yes, I am in control of both projects. According to your instructions, should I proceed with the installation of B from A as follows?

#project_B.cmake in project A
FetchContent_Declare(project_B
      GIT_REPOSITORY "git@project_B.git"
      GIT_TAG main
)
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME project_B_lib)
install(TARGETS project_B EXPORT Project_BTargets)
install(DIRECTORY ${project_B_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
...

I tend to look at it as each project should be responsible for install() commands for its own targets and files. Hopefully you can avoid having to write install() commands for your dependencies, but if you do, try to only add those if you know you’re the top level project. That makes composing more complex projects with many dependencies easier to manage at the top level consuming project.

I tend to look at it as each project should be responsible for install() commands for its own targets and files.

I would like to do that too, but in the best practices of consumable projects say that we should put the following guard in the installation of project B.

#CMakeLists.txt in project B
if (PROJECT_IS_TOPLEVEL)
  include(Install.cmake)
endif()

So how can I call the install configurations in project B’s install.cmake from inside project A (where project B is no longer toplevel)?

…but in the best practices of consumable projects say that we should put the following guard in the installation of project B…

I don’t know where you’re getting that advice, but I wouldn’t agree with it. I would generally recommend you always want to have the install(...) commands, just make sure they use project-specific component names. Then a parent consumer project can decide for itself which components it wants and which it will ignore.

The part that you do want to only include if you’re the top level project is the packaging logic. By this I mean the things related specifically to CPack. That’s mostly setting CPACK_... variables and eventually including the CPack module.

I mean, some of my reference projects like Lagrange and GSL do this approach. I use them as a model, what do you think about this strategy in these projects?
I realize that a simple solution to this situation is just defining the <project-name>_INSTALL as true before the FetchContent_MakeAvailable().

FetchContent_Declare(GSL
	    GIT_REPOSITORY "https://github.com/microsoft/GSL"
	        GIT_TAG "v4.0.0"
		    GIT_SHALLOW ON
)
set(GSL_INSTALL YES)
FetchContent_MakeAvailable(GSL)

seems ok?

Making it conditional on a user variable makes more sense to me than a state (PROJECT_IS_TOPLEVEL). That’s mostly to say “I don’t need to install you” rather than “I’ll redo installation for you, don’t do anything yourself”.