Optional add_subdirectory (like find_package) - cmake

In a project I have an external dependency (a library). This is optional but unlikely to be on the system already and may change a lot
So what I did was find_package the library and if it wasn't found (including the specific version) I download it from Github and call add_subdirectory on it (the download makes sure, the version is correct)
However this may fail due to unmet dependencies of that library which fails the whole build.
Is there a ways to try including the subdirectory and catching the failure w/o failing the build?
Note: The failure comes from a find_package(...REQUIRED) in the submodule.

You can modify subpackage's CMakeLists.txt to check if it is being built as part of another project and then handle its unmet dependencies gracefully.
To check for that use
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
# standalone build
else()
# build as subdirectory of main project
endif()
To return early try return().
If you can't alter subpackage, the only way I see is to check for all its dependencies in the main CMakeLists.txt and only if all of them are fulfilled, call add_subdirectory().

Related

Use find_package() on external project

I have an External project called messages. I am using ExternalProject_Add in order to fetch and build the project.
If i use find_package(messages REQUIRED) in top level CMakeLists.txt the cmake .. fails because it couldn't find the package installation files, which is logical as they are only build during make command invocation.
I am not sure, if there is way use find_package() on ExternalProjects. If so, please point me to an example.
Thanks
BhanuKiran
You have misunderstood how ExternalProject is supposed to work. You cannot find_package(messages REQUIRED) because it hasn't been built yet. ExternalProject merely creates the build steps necessary to build the subproject.
You have two options:
Use add_subdirectory or FetchContent in place of ExternalProject. In this case, you don't need a find_package call. This effectively adds the sub-project to the main build and imports the subproject's targets.
Use two ExternalProject calls: one for messages and another for main_project, which depends on messages. If messages uses the export(EXPORT) function, you can point CMAKE_PREFIX_PATH or messages_ROOT to the build directory. Otherwise you'll need to run the install step for messages and set up an install prefix inside your build directory. Then the find_project(messages REQUIRED) call inside main_project will succeed. This will likely require re-structuring your build.
Generally speaking, ExternalProject is only useful for defining super-builds, which are chains of CMake builds that depend on one another. And super builds are only useful when you need completely different configure-time options, like different toolchains (eg. you're cross compiling, but need a code generator to run on the build machine). If that's not the case, prefer FetchContent or add_subdirectory with a git submodule.
It is best to use FetchContent with CMake 3.14+ since it adds the FetchContent_MakeAvailable macro that cuts down on boilerplate.
Docs:
https://cmake.org/cmake/help/latest/module/ExternalProject.html
https://cmake.org/cmake/help/latest/module/FetchContent.html
Since I like keeping my CMake file agnostic on how I get my packages.
I was using FetchContent and added this file (Findalib.cmake):
if(NOT alib_POPULATED)
set(alib_BUILD_TESTS OFF CACHE BOOL INTERNAL)
set(alib_BUILD_EXAMPLES OFF CACHE BOOL INTERNAL)
set(alib_BUILD_DOCS OFF CACHE BOOL INTERNAL)
FetchContent_MakeAvailable(alib)
endif()
set(alib_FOUND TRUE)
Then, in my CMake files:
find_package(alib REQUIRED)
target_link_libraries(my-executable PUBLIC alib::alib)
That way, packages are only declared in my file in which I declare dependencies, and I fetch them only if I try to find them.

How do you prevent CMake from install()-ing targets from within projects included with add_subdirectory()?

My top-level CMake project depends on some-library, which I include into my project using
add_subdirectory (some-library)
and then link against it using
target_link_libraries (my-project PRIVATE some-library)
I also want to add install (TARGETS my-rpoject ...) directives so that my project can be installed to the system via whatever build system is generated (e.g. via sudo ninja install if using the -GNinja generator).
However, if some-library also defines some install (TARGETS some-library ...) directives, they get lumped in with the installation of my project's targets, which given a few subdirectory dependencies creates a bunch of extra, needless cruft in the system's installation directories which I'd like to avoid.
How can I get CMake to exclude any install (...) targets from submodules/dependency projects added with add_subdirectory () and keep only the ones in my top-level project?
At least as of CMake 3.0, add_subdirectory () has the EXCLUDE_FROM_ALL flag that can be added to the end of the call.
add_subdirectory (some-library EXCLUDE_FROM_ALL)
This removes all targets (including installation targets) from being evaluated at build time by the default rule, meaning they won't be built unless they are explicitly specified on the command line or are otherwise depended upon by a target that is included in the default rule.
Since your top-level project's targets are inherently included in the default rule (unless you create a custom target that also specifies EXCLUDE_FROM_ALL, for example), then passing EXCLUDE_FROM_ALL to add_subdirectory () and then linking against the dependency targets will automatically build any dependencies your project needs as you'd expect but omits install targets from the default install rule.
Therefore, all that remain are your own install() targets - none of your subdirectories'.

How to get the include directories from a dependency in meson build system

In meson build system, I want to get the include directories from a dependency:
Simple example meson.build, using wxWidgets dependecy as example:
project('project1', ['cpp'])
wxdep = dependency('wxWidgets')
wxincludes = # ... how to get the include directories from wxdep ?
# in this case, wxincludes will be used to compile a resource file:
windows = import('windows')
windows.compile_resources('test.rc', include_directories: [wxincludes])
How can I get the include directories from a dependency for use in subsequent commands?
A bit late response, but hope it will help you anyway.
Actually, meson's dependency object contains following things: source and header files, libraries to link with, compiler flags and linker flags.
So, answering your question directly - you should only pass wxdep as a dependency to your target, and meson will handle all other things inside.

Automatic recompilation: if a CMake client project depends on a separate CMake library, how to have the client project re build its dependency?

With a growing codebase, it makes sense to organize it between separate repositories, each repo being a separate CMake-managed project.
Because of modularity, this usually means you end up in a situation where a CMake-managed project Application depends on another CMake-managed project Library, while both are internal code (i.e., code owned and maintained by your structure).
The automatic dependency recompilation issue
Then, if some sources in Library are modified, it needs to be recompiled in order to build Application. The question being:
Is it possible to have the "build Application" command (a button in an IDE, or a call to make on the command line) to first rebuild Library if Library files changed ?
I'd suggest to use the ExternalProject_Add command.
The documentation has slightly changed for the different versions:
CMake v2.8.9 ExternalProject
CMake v3.0. ExternalProject
CMake v3.3 ExternalProject
In case you encounter problems with getting the dependencies right, this thread might help you.
By looking at how the OpenChemistry parent-project does it, and with the confirmation by normanius's answer, it turns out this can be achieved with relatively few CMake script code.
It turns out that CMake CLI is offering an abstraction over the "build" action of the targeted build systems. See --build option.
ExternalProject_Add can be seen as a wrapper to use this CLI interface directly from CMake scripts.
Imagine there is a CMake-managed repository, building libuseful, and a separate CMake-managed repo, building appawesome with a dependency on libuseful.
find_package(libuseful CONFIG) # The usual way to find a dependency
# appawesome is the executable we are building, it depends on libuseful
add_executable(appawesome main.cpp)
target_link_libraries(appawesome libuseful)
 Adding automatic rebuild
Then it is possible to make building appawesome systematically first try to rebuild libuseful with some code looking like:
ExternalProject_Add(EP_libuseful)
SOURCE_DIR <libuseful_sourcedir> # containing libuseful's root CMakeLists.txt
BINARY_DIR <libuseful_binarydir> # containing libuseful's CMakeCache.txt
BUILD_ALWAYS 1 # Always rebuild libuseful
)
add_dependencies(libuseful EP_libuseful)
The last line is quite important: find_package() in config mode should make a libuseful imported targed available. The call to ExternalProject_Add made a build target EP_libuseful available (which is a custom build step, building libuseful). The last line just makes sure that libuseful depends on its build step.

How to build and install cmake targets only if the other targets depend on them?

My application consists of the core, many shared libraries and many plugins that use these shared libraries. I'm using cmake option() command to enable / disable each plugin.
What I'd like to do is to build and install the shared library only if it's required by one of the enabled plugins.
I tried using the following in the directories of the shared libraries:
set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL true)
However, the targets are still being built in Visual Studio. GNU make in Linux correctly avoids building them. However, the required libraries are no longer installed using install() in either
system.
I also tried adding EXCLUDE_FROM_DEFAULT_BUILD false to the library targets,
but cmake complained about the undefined behavior of install() with disabled targets.
It sounds like you have something like this:
OPTION( BUILD_OPTIONAL_PLUGINS "Build plugins that are not required for core install" FALSE)
IF(${BUILD_OPTIONAL_PLUGINS})
ADD_SUBDIRECTORY( Plugins/Optional) # Whatever it really is.
ENDIF()
So far so good. However, you now can have a library like "libCommon" that nobody needs if all those option flags are false.
So, your simplest solution is this: Treat the library as optional TOO!
After all, you don't need to even build it if none of the consuming projects themselves are going to be built. So have an ordinary 'SET' variable like 'BUILD_COMMON' in that same top-level CMakeLists.txt, defaulted to TRUE. Then just do this:
SET( BUILD_COMMON 0)
IF(${BUILD_OPTIONAL_1} OR ...) # Detect if you need to build the lib
SET( BUILD_COMMON 1)
ENDIF()
IF(${BUILD_COMMON})
ADD_SUBDIRECTORY( common)
ENDIF()
Then when you eventually do 'make install' on this build, everything works as expected. If turned off all the optional plugins, the library that was only being used by them never got built either. Since it was never in the build, you don't need to try and make its install logic aware of whether it is needed or not.
You should also add a line like this to the INSTALL() command for the library as well as every optional plugin:
INSTALL( TARGETS <plugin> .... OPTIONAL)
That tells 'make install' to not try and build or install the target if it doesn't see its binaries.