How to force meson to use only wrap subproject - meson-build

I have a few subprojects defined in wrap files in the subprojects directory and declared in the meson.build file. Unfortunately I am forced to have some of the subprojects installed on my host system. Meson by default checks if a subproject is installed in the host os filesystem then eventually downloads and builds the subproject if it is unavailable. How to force meson to not use system libraries/headers but to always download/build subprojects independently in own build directory and link it during compilation?
subprojects/xyz.wrap:
[wrap-git]
url = https://github.com/bar/xyz.git
revision = HEAD
[provide]
xyz = xyz_dep
meson.build:
xyz = dependency('xyz')
...
deps = [
...
xyz
...
]
executable(foo, dependencies: deps)

You can force a dependency to fallback to its local subprojects version using --force-fallback-for=<dependency_name> during meson setup ....
For example, I have SDL2 installed as a system package, but I can use the WrapDB version with the following command:
meson setup build --force-fallback-for=sdl2
Reference:
https://github.com/mesonbuild/meson/issues/7218

You can also force fallback of all dependencies with -Dwrap_mode=forcefallback.
See meson options : https://mesonbuild.com/Builtin-options.html#core-options

Related

What is the meson equivalent of cmake's `FetchContent`?

How would I fetch a dependency from GitHub in meson? I am trying to convert the following cmake snippet into meson
FetchContent_Declare(
some_dep
GIT_REPOSITORY https://github.com/some/repo
GIT_TAG sometag
SOURCE_SUBDIR src
)
FetchContent_MakeAvailable(some_dep)
Note that this dependency may not be using meson as its build system.
The meson equivalent is subprojects and the wrap dependency system. Here a simple example:
subprojects/fmt.wrap:
[wrap-file]
directory = fmt-7.1.3
source_url = https://github.com/fmtlib/fmt/archive/7.1.3.tar.gz
source_filename = fmt-7.1.3.tar.gz
source_hash = 5cae7072042b3043e12d53d50ef404bbb76949dad1de368d7f993a15c8c05ecc
patch_url = https://wrapdb.mesonbuild.com/v1/projects/fmt/7.1.3/1/get_zip
patch_filename = fmt-7.1.3-1-wrap.zip
patch_hash = 6eb951a51806fd6ffd596064825c39b844c1fe1799840ef507b61a53dba08213
[provide]
fmt = fmt_dep
It is a meson convention that the directory must be named subprojects and be a top-level directory.
meson.build:
project('demo', 'cpp')
fmt_dep = dependency('fmt-7', required: false)
if not fmt_dep.found()
fmt_proj = subproject('fmt')
fmt_dep = fmt_proj.get_variable('fmt_dep')
endif
executable('demo', 'main.cpp', dependencies: fmt_dep, install: true)
main.cpp:
#include <fmt/core.h>
int main() {
fmt::print("Hello, world!\n");
}
This will search libfmt on your system and fallback to downloading it as specified in the wrap file. The subproject() function requires a meson.build file in the root directory, similar to how FetchContent expects a CMakeLists.txt. If the respective project does not have meson build files, you can provide patches through the patch_* or diff_files properties in the wrap file. This is e.g. the case for libfmt does not have meson build files. You can also use [wrap-git] instead of [wrap-file] to specify a git repository instead of a release tarball. Please refer to the documentation.
Tip: The meson developers maintain the Wrap DB, where you can find wrap files for some common libraries/projects.

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'.

CMake force install after add_subdirectory

We are converting a large Makefile based project to a CMake based system. I have numerous dependencies that I need to build prior to building our code. The first three dependencies are build using the following:
add_subdirectory(dependencies/libexpat/expat)
add_subdirectory(dependencies/libuuid-1.0.3)
add_subdirectory(dependencies/log4c-1.2.4)
expat has it's own CMakeLists.txt file and build with no problems. I would like to force expat to install to the staging directory before continuing. For libuuid I am using a ExternalProject_Add and as part of that process it does install into the staging directory.
Then when I build log4c, which needs expat, I can point it to the location of expat. Otherwise I would need to someone get access to the absolutely path for the temporary build location of expat.
I've tried to add the following after add_subdirectory:
add_subdirectory(dependencies/libexpat/expat)
add_subdirectory(dependencies/libuuid-1.0.3)
install(TARGETS expat LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/usr/lib)
add_subdirectory(dependencies/log4c-1.2.4)
Unfortunately CMake will not run expat's install code. How do I force expat to install after building but before it builds the rest of the project?
This looks like the primary use case for ExternalProject_Add, which is best used as a superbuild setup. This means that your top-level project (the "superbuild") does not build any actual code and instead consists only of ExternalProject_Add calls. Your "real" project is added as one of these "external" projects. This allows you to set up the superbuild with all dependencies, ordering, etc.
The workflow is then as follows:
Generate the superbuild project.
Build the superbuild project. This will build and install all dependencies, and also generate (and build) your real project.
Switch to the buildsystem generated for your real project and start doing further development using that. Your dependencies are already correctly set up and installed by the build of the superbuild project in the previous step.

How to support multiple package versions in CMake's user package registry?

I'm trying to understand how I can use the CMake user package registry to support multiple builds, for example a Debug and a Release build.
I have a package1, which properly calls export(PACKAGE package1) so it registers the build directory with CMake's user package registry. I then proceed to creating 2 separate build directories, one for Debug and one for Release (note that I made the version for the Debug build 3.0.2000000, while the version for the Release build is 3.0.1000000, because I wanted CMake to prefer the Debug build in my use case). As expected, each of these builds registered an item in the registry:
> ls ~/.cmake/packages/package1/
212e973d0f1858e4bdf95b9d105bed5a 78094ed9b729d420c3f782cd8e668e64
Now in a different project, package2, I use find_package(package1 3.0.740) but it only ever finds the one in the Release build. I tried inspecting the package1_CONSIDERED_CONFIGS and package1_CONSIDERED_VERSIONS right after calling find_package but again only the Release build's config and version are there.
The code in package2's CMakeLists.txt file:
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
message("CMAKE_FIND_PACKAGE_SORT_ORDER="
"${CMAKE_FIND_PACKAGE_SORT_ORDER}")
message("CMAKE_FIND_PACKAGE_SORT_DIRECTION="
${CMAKE_FIND_PACKAGE_SORT_DIRECTION}")
find_package(package1 3.0.740)
message("Considered Configs: ${package1_CONSIDERED_CONFIGS}")
message("Considered Versions: ${package1_CONSIDERED_VERSIONS}")
Result of CMake running above code:
cmake ../..
-- Using link pool size - 16 jobs
CMAKE_FIND_PACKAGE_SORT_ORDER=NATURAL
CMAKE_FIND_PACKAGE_SORT_DIRECTION=DEC
Considered Configs: /path/to/package1/Release/package1Config.cmake
Considered Versions: 3.0.1000000
What am I doing wrong? How is the user registry supposed to be used?

cmake: install header order and dependencies on target

I've a set of libraries and executables all with their own CMakeLists.txt. All libraries are building their targets to the same path (for example ../build/bin and ../build/lib)... as well as exporting their header files (../build/inc).
Now I wish to build one build system and let CMake figure out the dependencies (using add_subdirectory and global build settings).
The problem is: all libraries do export their headers to build/inc after they are build (when make install in invoked). When I do a whole system build make install is not invoked until after the end (and everything has build). So for example executable progfoo with target_link_libraries( progfoo onelib ) will fail, because CMake figures out the dependency to onelib (which builds fine), but progfoo fails because it looks for headers in build/inc... which were not exported yet. The same thing in general applies to many libraries.
What is the correct CMake-Way to handle these cases? Thank you!
Install is the final step, the one that should be visible to the user. So when you export binaries and headers you should already have binaries built against headers in their original locations.
From CMake point of view you have only 1 target at a time. For example you can build a Web Server and using as dependencies libcurl and boost::asio. It is very possible (and good) to add dependencies to current target using add_subdirectory, but when you have to add include directories you have to do that on a per dependency basis, it would be convenient in example if each of the dependencies provides already a variable with current absolute path to includes.
In example see this dummy libcurl's CMakeLists.txt that set path to absolute include directory
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
// export into parent scope libcurl header location
set (LIBCURL_INCLUDES ${_DIR}/include PARENT_SCOPE)
Then you can use it from Web Server's CMakeLists.txt for building and later use the same path again to extract files for installing them where required
add_subdirectory(PATH_TO_LIBCURL_CMAKELISTS)
# add include directories for building web server
include_directories( ${LIBCURL_INCLUDES})
#I assume you are installing headers because final user will need them,
#in case you needed them just for building you are already done here
#without even installing them
#gather headers list
file(GLOB libCurlHeadersList
"${LIBCURL_INCLUDES}/*.h"
"${LIBCURL_INCLUDES}/*.hpp"
)
#install header list
install( FILES
${libCurlHeadersList}
DESTINATION ../build/inc/libcurl
)