CMake: Linking multiple libraries to two executables in one command - cmake

I've got two executables both of which need to be linked to N libraries which are the same:
add_executable(MyExe1 main1.cpp)
add_executable(MyExe2 main2.cpp)
target_link_libraries(MyExe1 lib1 lib2 lib3 ... libN)
target_link_libraries(MyExe2 lib1 lib2 lib3 ... libN)
So I have to write target_link_libraries twice; once for MyExe1 and once for MyExe2. Is there any way to shorten the way some common libraries are linked to two different executables? I am wondering if it's possible to link lib1 ... libN libraries to both MyExe1 and MyExe2 in one command to avoid redundancy and make the CMake file cleaner.

You can use the set command to set a variable from a list of arguments:
add_executable(MyExe1 main1.cpp)
add_executable(MyExe2 main2.cpp)
set(LIBS lib1 lib2 lib3 ... libN)
target_link_libraries(MyExe1 ${LIBS})
target_link_libraries(MyExe2 ${LIBS})

Related

CMake: target_link_libraries based on flag

There are 4 libs linked already and want to link a new library if FLAG is ON.
target_link_libraries (lib1 lib2 lib3 lib4 ${CMAKE_DL_LIBS})
I want it to be something like
target_link_libraries (lib1 lib2 lib3 lib4 if(FLAG) lib5 endif() ${CMAKE_DL_LIBS})
Is there anyway to implement this in cmakelists.txt?
Simply use multiple target_link_library commands.
E.g. the following could be used to add a lib for unix targets
target_link_libraries (lib1 lib2 lib3 lib4 ${CMAKE_DL_LIBS})
if(UNIX)
target_link_libraries(lib1 lib5)
endif()
Alternatively you could use a list containing the libs to include
set(LIBS lib2 lib3 lib4 ${CMAKE_DL_LIBS})
if(UNIX)
list(APPEND LIBS lib5)
endif()
target_link_libraries(lib1 ${LIBS})
In theory you could also use generator expressions, but imho this would make for the least readable alternative
target_link_libraries(lib1 lib2 lib3 lib4 ${CMAKE_DL_LIBS} $<$<BOOL:${UNIX}>:lib5>)

CMake: Fortran modules and compilation order

I have a big Fortran program which contains many directories. Each directory is compiled separately in a pseudo-library, but there is still an interdependency mess, so at the end all pseudo-libraries are combined in a single usable library. I'd like to use Fortran modules, but it's very fragile, since I cannot rely on automatic dependency checking, and compilation may fail depending on the order.
For instance, consider the following CMakeLists.txt file:
project (test Fortran)
add_library (lib1 dir1/lib1.f90)
add_library (lib2 dir2/lib2.f90 dir2/mod.f90)
add_executable (exe dir3/exe.f90)
target_link_libraries (exe lib1 lib2)
With the sources:
dir1/lib1.f90:
subroutine bar
use foo, only: foofoo
implicit none
write(6,*) foofoo
end subroutine bar
dir2/lib2.f90:
subroutine bar2
use foo, only: foofoo
implicit none
write(6,*) foofoo,' again'
end subroutine bar2
dir2/mod.f90:
module foo
implicit none
integer :: foofoo=3
end module foo
dir3/exe.f90:
program meh
implicit none
call bar()
call bar2()
end program meh
Compiling from scratch fails:
$ make
[ 25%] Building Fortran object CMakeFiles/lib1.dir/dir1/lib1.f90.o
/home/user/cmake/dir1/lib1.f90:2.4:
use foo, only: foofoo
1
Fatal Error: Can't open module file 'foo.mod' for reading at (1): No such file or directory
make[2]: *** [CMakeFiles/lib1.dir/dir1/lib1.f90.o] Error 1
make[1]: *** [CMakeFiles/lib1.dir/all] Error 2
make: *** [all] Error 2
but doing it in the right order works:
$ make lib2
Scanning dependencies of target lib2
[ 50%] Building Fortran object CMakeFiles/lib2.dir/dir2/mod.f90.o
[100%] Building Fortran object CMakeFiles/lib2.dir/dir2/lib2.f90.o
Linking Fortran static library liblib2.a
[100%] Built target lib2
$ make
[ 25%] Building Fortran object CMakeFiles/lib1.dir/dir1/lib1.f90.o
Linking Fortran static library liblib1.a
[ 25%] Built target lib1
[ 75%] Built target lib2
Scanning dependencies of target exe
[100%] Building Fortran object CMakeFiles/exe.dir/dir3/exe.f90.o
Linking Fortran executable exe
[100%] Built target exe
Is there any way CMake can figure out the dependency and compile lib2 (or at least mod.f90) before lib1?
ETA: A robust solution should work regardless of the order in which lib1 and lib2 are defined in the CMakeLists.txt file and, once the program has been compiled, after running rm foo.mod ; touch ../dir1/lib1.f90.
The upcoming ninja build system version (1.10.0) has support for dynamic dependencies, which will resolve modules compilation order correctly.
To use it with CMake, you have to specify the Ninja generator:
cmake .. -DCMAKE_GENERATOR=Ninja # generate project
ninja # build the project
Here problem is that lib1 target required object file for mod.f90. But there is no rule mention in CMakeLists.txt to create mod.f90.o while creating liblib1.a. For lib2 target mod.f90.o is created.
There can be two possible solution as below.
Solution-1
Add mod.f90 to both library.
project (test Fortran)
add_library (lib1 dir1/lib1.f90 dir2/mod.f90)
add_library (lib2 dir2/lib2.f90 dir2/mod.f90)
add_executable (exe dir3/exe.f90)
target_link_libraries (exe lib1 lib2)
Solution-2
Link library with mod.f90.o to other one.
project (test Fortran)
add_library (lib1 dir1/lib1.f90)
add_library (lib2 dir2/lib2.f90 dir2/mod.f90)
add_dependencies(lib1 lib2)
add_executable (exe dir3/exe.f90)
target_link_libraries (exe lib1 lib2)

Dependency between two subdirectories in CMake

In my root CMakeLists.txt, I have:
add_subdirectory(libs)
add_subdirectory(main)
In libs, I have my own CMakeLists.txt to build external projects.
In main, there is a CMakeLists.txt from another repository, which I do not control.
To build main, libs needs to be built. I do not know how to specify the
dependency between main and libs.
In libs, with my external projects lib1 and lib2, I used add_dependencies(lib1 lib2) and I have lib2 built before lib1. I did not find how to do that for
main and libs.
The difficulty for me is I have to mix external projects and subdirectories, and I did not find any answer or was not able to adapt them.
I converted add_subdirectory(main) into an external project. Since it is not possible to make a dependency on subdirectories, I use directly the inner targets. With all that I got:
include(ExternalProject)
add_subdirectory(libs)
ExternalProject_Add(main
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/main
...
)
add_dependencies(main lib1 lib2)

What to do with the output of ExternalProject_add? IMPORTED vs INTERFACE libraries

This is my topmost CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(test LANGUAGES Fortran)
add_executable(main main.f90)
add_subdirectory(external)
target_link_libraries(main extlib)
Subdirectory "external" defines one external project, which produces some libraries and header files. What is the best practice of collecting the output of ExternalProject_add so that I could link that to the main executable? Currently, I'm using an INTERFACE library, like this:
include(ExternalProject)
ExternalProject_add(my-external
SOURCE_DIR ext_source
CONFIGURE_COMMAND
${CMAKE_CURRENT_LIST_DIR}/configure
--prefix=${CMAKE_CURRENT_BINARY_DIR}
BUILD_COMMAND make)
add_library(extlib INTERFACE)
add_dependencies(extlib my-external)
target_include_directories(extlib INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include)
foreach(_lib lib1 lib2 lib3)
target_link_libraries(extlib INTERFACE
${CMAKE_CURRENT_BINARY_DIR}/lib/${_lib}.a)
endforeach()
Searching the web, it seems to me that people often use IMPORTED libraries instead for capturing the results of ExternalProject_add. However, you can only connect one IMPORTED library with one file produced by ExternalProject_add and any header directories would need to be separately propagated back to the parent directory so that main could use them. It seems to me than an INTERFACE library is better here because you can glob everything produced by the external project into a single target. In my actual project I have several external projects and ideally I would like to have one target per external project to keep things clean.
In general, should I use IMPORTED or INTERFACE libraries when dealing with external libraries and what would be the main benefits?
EDIT
Based on the dicussion below, I made an attempt using imported libraries only:
foreach(_lib lib1 lib2 lib3)
add_library(${_lib} STATIC IMPORTED GLOBAL)
add_dependencies(${_lib} my-external)
set_target_properties(${_lib} PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/lib/${_lib}.a)
endforeach()
set_target_properties(lib1 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/include)
target_link_libraries(lib1 INTERFACE lib2 lib3)
It takes about the same effort as with an interface library and I guess is ultimately a matter of style.

Overlapping dependencies between libraries in CMake

Let's say there's the following directory structure:
projects
|
+--lib1
| |
| +-CMakeFiles.txt
|
+--lib2
| |
| +-CMakeFiles.txt
|
+--test
|
+-CMakeFiles.txt
lib1/CMakeFiles.txt:
cmake_minimum_required(VERSION 2.0)
add_library(lib1 STATIC lib1.cpp)
lib2/CMakeFiles.txt:
cmake_minimum_required(VERSION 2.0)
add_subdirectory(../lib1 ${CMAKE_CURRENT_BINARY_DIR}/lib1)
add_library(lib2 STATIC lib2.cpp)
target_link_libraries(lib2 lib1)
test/CMakeFiles.txt:
cmake_minimum_required(VERSION 2.0)
project(test)
add_subdirectory(../lib1 ${CMAKE_CURRENT_BINARY_DIR}/lib1)
add_subdirectory(../lib2 ${CMAKE_CURRENT_BINARY_DIR}/lib2)
add_executable(test main.cpp)
target_link_libraries(test lib1 lib2)
I.e. lib2 depends on lib1 and test depends on both of them. (I know that technically static libraries don't "link", but that's just an example.)
The problem is that with the current setup, lib1 compiles twice - the first time it is within the "test" build directory, and a second time it is within "test/build_directory/lib2/build_directory". I'd like to avoid that.
I want to be able to add a dependency on lib1, lib2 or both of them (using add_subdirectory) to any project that's located elsewhere. So moving CMakeFiles isn't an option. I also want to avoid compiling any library several times.
How can I do that?
Platform: CMake v. 2.8.4 and Windows XP SP3
A top-level CMakeLists.txt file isn't an option, because I want to keep a clean top-level directory and be able to include libraries in other projects that can be located elsewhere. Because it is Windows, I can't "install package system-wide" - I don't want to lose ability to switch compiler on the fly. Utility libraries built with different compilers will use different C runtime libraries/ABI, and as a result will be incompatible.
One other solution is to add a guard at the top of the subdirectory-CMakeLists.txt:
if(TARGET targetname)
return()
endif(TARGET targetname)
Which will cause cmake to do nothing the second time the subdirectory is added (if targetname is being defined in that file, of course).
This will lead to the lib beeing build in an sort-of-arbitrary place (depending on which module added it first) in the build/ tree, but it will be built only once and linked everywhere.
In your example, you would add
if(TARGET lib1)
return()
endif(TARGET lib1)
at the top of lib1/CMakeFiles.txt
With CMake, library dependencies are transitive, so you shouldn't call add_subdirectory twice in test/CMakeFiles.txt (nor do you need to list lib1 as a dependency of test since it is already a dependency of lib2's).
So you could modify test's CMakeFiles.txt to:
cmake_minimum_required(VERSION 2.8.7) # Prefer the most current version possible
project(test)
add_subdirectory(../lib2 ${CMAKE_CURRENT_BINARY_DIR}/lib2)
add_executable(test main.cpp)
target_link_libraries(test lib2)
Also, you should probably remove the cmake_minimum_required calls from your non-project CMakeFiles.txt files (the lib ones). For further info, run:
cmake --help-policy CMP0000
This setup will still cause all libs to be recompiled if you add a similar test2 subdirectory and project which depends on lib1 and lib2. If you really don't want to have a top-level CMakeFiles.txt in projects/, then you're stuck with what you're doing, or you could use either the export or install command.
export would create a file which could be included by other projects and which imports the targets into the project which calls include.
install could install the libraries to another common subdirectory of projects/. Depending on your source directory structure, this could have the benefit of only making the intended library API headers available to dependent projects.
However, both of these options require the dependent library projects to be rebuilt (and installed) if they are modified, whereas your current setup includes all the dependent targets in your project, so any change to a source file in a dependent library will cause your test target to go out of date.
For further details about export and install, run:
cmake --help-command export
cmake --help-command install
Perhaps add a top-level CMakeLists.txt in your projects dir. Something like:
project( YourProjects )
add_subdirectory( lib1 )
add_subdirectory( lib2 )
add_subdirectory( test )
This should be sufficient and will give you a solution-file or makefile in your top-level build-dir. You should then remove the add_subdirectory( ../lib1 ... from your lib1 and lib2 projects, but instead simply link to them. CMake will know how to find lib1 and lib2 when compiling test.
I.e. in lib2:
project( lib2)
add_library(lib2 STATIC lib2.cpp)
target_link_libraries(lib2 lib1)
and in test:
project( test )
add_executable(test main.cpp)
target_link_libraries(test lib1 lib2)
Added bonus: you will get makefiles/solutionfiles for building lib2 (with dependent lib1) in the lib2 directory...