[CLN-list] Why CMake (Was: Re: CMake build patches)
Alexey Sheplyakov
asheplyakov at yandex.ru
Fri Jan 8 13:44:35 CET 2021
Dear Richard,
On 1/1/21 11:06 PM, Richard B. Kreckel wrote:
> Maintaining a second build system /is/ an additional burden and requires
> good justification. If it helps people build CLN on common platforms,
> well, that seems to be a good enough justification! That /I/ neither use
> or care about these platforms is, then, clearly subordinate.
Portability is nice and important. However I prefer CMake for a different
reason: dependency management. Consider tools/CMakeLists.txt:
```
add_executable(viewgar viewgar.cpp)
target_link_libraries(viewgar ginac::ginac)
```
What happens here is similar to
$CXX `pkg-config --cflags --libs ginac` viewgar.cpp
I.e. there's no need to set includes/definitions like in Makefile.am:
AM_CPPFLAGS = -I$(srcdir)/../ginac -DIN_GINAC ...`
It's enough to specify dependencies of `ginac` target once, and
`target_link_libraries(viewgar ginac::ginac)` applies them automatically.
The dependencies of `ginac` target are (see ginac/CMakeLists.txt)
```
target_compile_definitions(ginac
PUBLIC $<BUILD_INTERFACE:IN_GINAC>
PRIVATE -DLIBEXECDIR="${LIBEXECDIR}/" HAVE_CONFIG_H
)
target_link_libraries(ginac PUBLIC cln::cln ${LIBDL_LIBRARIES})
target_include_directories(ginac PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/..>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
```
This is roughly equivalent to
a) `CPPFLAGS += -DLIBEXECDIR=$(LIBEXECDIR) HAVE_CONFIG_H`
when compiling (lib)ginac itself (but NOT targets which depend on it)
b) `CPPFLAGS += -DIN_GINAC` when compiling both (lib)ginac _and_ targets
which depend on it. But if `ginac` target is exported (i.e. installed)
this definition should NOT be added
c) `CPPFLAGS += -I$(srcdir) -I$(builddir) -I$(builddir)/..`
for both libginac and targets which depend on it. However, if libginac
is exported (installed) use `-I${CMAKE_INSTALL_INCLUDEDIR}` instead
(usually something like `$(PREFIX)/include/ginac`).
d) LIBS += -lcln
when linking both libginac and targets which depend on it. This also
sets proper rpath when linking with shared libraries.
e) Also add PUBLIC and INTERFACE definitions/includes/libraries from
cln::cln target. Thus when compiling libcln as a shared library
`-lgmp` is added only to libcln itself, but not to libginac
(and viewgar, tests, etc)
And EXPORT installs this information along with the target (library) itself.
So that `target_link_libraries(viewgar ginac::ginac)` works when libginac
is installed (exported).
There's no way to express such dependencies with autotools.
libtool tracks dependencies between libraries, so it's enough to
viewgar_LDADD = ../ginac/libginac.la
However
a) includes, definitions, etc have to be specified manually
b) libtool does not distinguish between private and public dependencies
(this is why Linux distros don't include .la files in -devel packages)
On the other hand pkg-config handles both --libs and --cflags, but
a) it's not designed to handle dependencies between **targets**
b) using uninstalled packages is next to impossible
c) one has to describe the dependencies twice: both in .pc file
and Makefile.am (as a result .pc files often get out of sync)
d) rpath should be handled manually too (unlike libtool)
As a result reusing code is much easier with CMake. For instance, to
build a program one can put the whole CLN source into a subdirectory
(git submodule) and use the following CMakeLists.txt:
```
cmake_minimum_required(VERSION 3.10)
project(pi)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cln)
add_executable(pi src/pi.cc)
target_link_libraries(pi cln::cln)
```
This automatically builds CLN (and its dependencies) before building `pi`.
No more
cd cln && ./autogen.sh && ./configure && make && make install
env PKG_CONFIG_PATH=/oh/let/me/guess g++ src/pi.cc `pkg-config --libs --cflags cln`
Best regards,
Alexey
More information about the CLN-list
mailing list