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

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.

Related

How can I specify library path when using Meson?

I'm trying to build a c++ project with Meson.
The thing is, I have some libraries under /opt/conda
but can't figure out how to link the project when running meson build.
It seems to be only searching through /usr/lib directory.
As far as I understood, meson uses cmake and pkg-config to look for libraries.
Then would setting something like CMAKE_PREFIX_PATH be a feasible solution? and if so, how can I do that?
Thanks in advance.
I see two possible approaches to solve your problem.
the first solution uses LIBRARY_PATH, which is different from LD_LIBRARY_PATH as explained later.
the second solution uses a modified meson file to directly pass options to the linker. Optionally, it also uses rpath that eliminates the need to modify LD_LIBRARY_PATH afterward.
First solution
When building your project the linker use LIBRARY_PATH (and not LD_LIBRARY_PATH)
LIBRARY_PATH is used by gcc before compilation to search directories
containing static and shared libraries that need to be linked to your
program.
LD_LIBRARY_PATH is used by your program to search directories
containing shared libraries after it has been successfully compiled
and linked.
further details: LD_LIBRARY_PATH vs LIBRARY_PATH
Maybe you can try
export LIBRARY_PATH=/opt/conda/:$LIBRARY_PATH
before running meson to build your project.
Second solution
Modifying your meson file and use rpath (optional)
An alternative to the previous first solution is to directly modify your Meson file to pass some options to the linker.
Here is something I used in the past you can easily adapt to your problem:
#
# blaspp
#
blaspp_lib = 'blaspp'
blaspp_lib_dir = '/opt/slate/lib'
blaspp_header_dir = '/opt/slate/include'
blaspp_dep = declare_dependency(
link_args : ['-L' + blaspp_lib_dir, '-l' + blaspp_lib],
include_directories : include_directories(blaspp_header_dir))
executable('test_blaspp',
'test_blaspp.cpp',
build_rpath : blaspp_lib_dir,
install_rpath : blaspp_lib_dir,
dependencies : [blaspp_dep])
declare_dependency(...) defines options to pass to the linker (this replaces the need to define LIBRARY_PATH in the first solution)
executable(...) defines rpath. This is an optional step that embeds the extra library path information directly into the executable. If you use this, you will not have to modify the LD_LIBRARY_PATH when running your executable.
Further details: https://amir.rachum.com/blog/2016/09/17/shared-libraries/ (have a look at the "rpath and runpath" section) and see wikipedia: https://en.wikipedia.org/wiki/Rpath
If I understand the documentation correctly, you could use different / others build system as subproject, and it doesn't seem basing on cmake.
You should be able to define CMAKE_PREFIX_PATH in a CMakeList.txt of a cmake project, and access the generated library within meson context:
in your cmake subproject:
add_library(cm_lib SHARED ${SOURCES})
in your meson:
cmake = import('cmake')
# Configure the CMake project
sub_proj = cmake.subproject('libsimple_cmake')
# Fetch the dependency object
cm_lib = sub_proj.dependency('cm_lib')
executable(exe1, ['sources'], dependencies: [cm_lib])
if you only want to propagate any specific library to meson, than it looks like you'll need to bundle those third party library, or using built-in options.
But first of all: Have you check, either /opt/conda is in your LD_LIBRARY_PATH ?
Surprised no one mentioned it but this is how it is done from within meson.
CXX = meson.get_compiler('cpp')
libs_you_need_to_link = ['lib_a', 'lib_b', 'lib_c']
deps = []
foreach lib_name : libs_you_need_to_link
deps += CXX.find_library(lib_name, dirs : ['/opt/conda', '/other/path'])
endforeach

Meson: how to make find_library() works with an unusual path?

For my Meson project I have a dependency that is in an "unusual" place:
/opt/MyDependence/lib/libmyLib.so
/opt/MyDependence/include/myLib.hpp
My meson file is:
project('Test', ['cpp'])
cpp = meson.get_compiler('cpp')
myLib_dep = cpp.find_library('myLib', required: true)
Obviously Meson cannot find the library
Meson.build:5:0: ERROR: C++ library 'myLib' not found
The problem is that I do not know the "canonical" way to add extra search paths so that Meson can found my lib. Any idea?
update: please note that even if I use:
meson --libdir=/opt/MyDepedence/lib build
I get this error message:
meson.build:1:0: ERROR: The value of the 'libdir' option is '/opt/MyDepedence/lib' which must be a subdir of the prefix '/usr/local'.
Note that if you pass a relative path, it is assumed to be a subdir of prefix.
find_library now has an optional argument dirs (since 0.53.0) that indicates an extra list of absolute paths where to look for program names.
cpp = meson.get_compiler('cpp')
myLib_dep = cpp.find_library('myLib', dirs: '/opt/MyDepedence/lib', required: true)
I finally got a solution, one must use LIBRARY_PATH
export LIBRARY_PATH=/opt/MyDepedence/lib
meson build
Note: attention this is not LD_LIBRARY_PATH, see there for the difference
Also read this Meson/issues/217 . For Windows, the LIBRARY_PATH equivalent seems to be LIBPATH (but I was not able to check as I only run under Linux).
An alternative is to "manually" define a new dependence. In your Meson project:
project('Test, ['cpp'])
myLib_dep = declare_dependency(link_args : ['-L/opt/MyDependence/lib', '-lmyLib'],
include_directories : ['/opt/MyDependence/include'])
exe1 = executable('main', ['main.cpp'], dependencies : [myLib_dep])
A refinement that could be done is to store this "manual" setting into meson_options.txt.
Note: I finally answered my question, but I am still open to better solutions.
Standard way to solve this is to use pkg-config. In short, library installation procedure should include stage where special "mylib.pc" file is generated (from scratch, or typically from template "mylib.pc.in" - search in the internet, there are lots of examples). Then these small key-value files which has info on include/library dirs, dependencies, etc are installed to some known location, typically /usr/lib/pkgconfig/. Meson naturally runs pkg-config under the hood and finds your library when you have something like this
mylib_dep = dependency('mylib', required: true)
in your meson.build.
Update
Regarding libdir meson option error, you can try add option prefix as well:
meson --prefix=/opt/MyDepedence --libdir=lib build
Note also that with this command line you actually call implicitly setup command (there is no command build, since you will build with ninja) and build is a build directory that will be created using your options. Check this. That is why it is more visible to write:
meson setup build_dir --prefix=/opt/MyDepedence --libdir=lib

Optional add_subdirectory (like find_package)

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().

Best practices with CMake with non-standard include and library directories

I have been trying to build Mozilla RR on a Linux box at work using CMake. We have a slightly eccentric arrangement where shared libraries are stored on network drives in locations like /sw/external/product-name/linux64_g63.dll/. Further, I have built some dependencies for the project in $HOME/sw/. (I am not a sudoer on this box.)
I am rather baffled as how I am supposed to communicate to CMake to look in non-standard directories. So far I have fudged:
PKG_CONFIG_PATH=$HOME/sw/capnproto-0.6.1/lib/pkconfig \
CC=gcc-6.3 CXX=g++-6.3 \
cmake \
-DCMAKE_INSTALL_PREFIX=$HOME/sw/rr-5.1.0 \
-DPYTHON_EXECUTABLE=$HOME/bin/python2 \
-DCMAKE_FIND_ROOT_PATH=$HOME/sw/libseccomp-2.2.3/ \
../src/
Which is obviously not a scalable solution, but it does at least complete the configuration successfully and emit some Makefiles.
If I omit -DCMAKE_FIND_ROOT_PATH=$HOME/sw/libseccomp-2.2.3/, CMake fails, complaining about a missing libseccomp-2.2.3 dependency. But it works if I do have that definition, telling me that CMake understands where the libseccomp-2.2.3 files are and so will properly add the paths to the necessary compiler invocations.
However, make does not succeed, because gcc fails to find a required header file from the libseccomp probject. Examining make VERBOSE=1, I find that CMake hasn't added -I$HOME/sw/libseccomp-2.2.3/include to the gcc invocation.
I feel like this is not the right approach. The other answers I have looked at tell me to modify the CMakeLists.txt file, but surely
that is not going to be scalable across multiple CMake projects, and
for each project, that will need me to maintain a separate CMakeLists.txt file for every platform (Solaris/Linux/Darwin/Cygwin) I build the software on.
Is there a canonical solution to solving this problem? Perhaps a per-site configuration file that will tell CMake how to find libraries and headers, for all projects I build on that site?
Your approach is correct, but cmake is never told to include SECCOMP - see end of this post.
The way cmake can be informed about custom dependency directory depends on how the dependency is searched (i.e. on what is written in CMakeLists.txt).
find_package/find_library/find_path/find_program
If dependency is found with one of above-mentioned commands, custom search directories can be easily added with CMAKE_PREFIX_PATH. There is no need to add full path to include, lib or bin - when package root is added find_-command will check appropriate sub-directories. CMAKE_PREFIX_PATH can be also set with environment variable.
Second option is CMAKE_FIND_ROOT_PATH. Every path added to CMAKE_FIND_ROOT_PATH list treated as separate root directory and is searched before system root directory.
Note that CMAKE_FIND_ROOT_PATH will be ignored by find_-commands with NO_CMAKE_FIND_ROOT_PATH argument.
Following four variables may be used to tune the usage of CMAKE_FIND_ROOT_PATH:
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
When use of host system default libraries is undesired setting CMAKE_FIND_ROOT_PATH_MODE_INCLUDE and CMAKE_FIND_ROOT_PATH_MODE_LIBRARY to ONLY is a good practice. If dependency library or header is not found in CMAKE_FIND_ROOT_PATH the configuration will fail. If cmake is allowed search system paths too, it is most likely that errors will occur during linking step or even runtime.
See find_package docs for more details.
find_package only
All above applies to find_package command too.
find_package can operate in two modes MODULE and CONFIG.
In MODULE mode cmake uses Find[PackageName].cmake script (module) to find dependent package. CMake comes with large number of modules and custom modules can be added with CMAKE_MODULE_PATH variable. Often find-modules can be informed about custom search paths via environment or cmake variables.
E.g. FindGTest.cmake searches path stored in GTEST_ROOT variable.
If no find module is available, find_package enters CONFIG mode. If a dependency package provides [PackageName]Config.cmake or [LowercasePackageName]-config.cmake cmake can be easily informed about that package with [PackageName]_DIR variable.
Example:
CMakeLists.txt contains:
find_package(Qt5)
FindQt5.cmake is not available, but ~/Qt5/Qt5.8/lib/cmake/Qt5Config.cmake file exists, so add
-DQt5_DIR="${HOME}/Qt5/Qt5.8/lib/cmake"
to cmake call.
pkg-config
CMake can use information provided by external pkg-config tool. It is usually done with pkg_check_modules command. Directory used by pkg-config can be customized with PKG_CONFIG_PATH environment variable. According to cmake documentation instead of setting PKG_CONFIG_PATH, custom .pc-files directories can be added via CMAKE_PREFIX_PATH. If CMake version is pre-3.1, PKG_CONFIG_USE_CMAKE_PREFIX_PATH have to be set to TRUE(ON) to enable this feature.
Methods of customizing dependencies search path is defined by CMakeLists.txt content. There is no universal solution here.
And now back to missing SECCOMP headers...
In CMakeLists.txt SECCOMP header is found with
find_path(SECCOMP NAMES "linux/seccomp.h")
but I cannot find any command telling CMake to use the found header. For example:
target_include_directories(<target_name> ${SECCOMP})
or globally:
include_directories(${SECCOMP})
I belive that CMakeLists.txt should be fixed. It is not a platform dependent solution.

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
)