Naming CMake Targets by directory - cmake

I am new to CMake and am having a bit of trouble. Is it possible to name CMake targets according to the directory they were created in? For instance, I would like to write my libraries to a lib/ directory that would echo my source directory structure.
Imagine i have source directories dir1 and dir2, and both create a library libmylib.a Normally CMake might complain about creating libraries/targets with the same name, but if the libraries were written to lib/dir1/libmylib.a and lib/dir2/libmylib.a then you could reference these targets as dir1/libmylib and dir2/libmylib in your CMakeLists.txt files (presumably) without conflict.

In CMake, logical library name can be totally decoupled from the name of the file produced by the target. There is no problem in having a / in your target names. You could add your libraries like this:
add_library(dir1/mylib STATIC source1 source2 ...)
set_property(TARGET dir1/mylib PROPERTY OUTPUT_NAME mylib)

Related

Python library and CMake target with the same name

I'm constructing a library "mylib" that is C++ header-only and has a Python API using pybind11.
I want to use "mylib" both as CMake target, containing compile instructions, and as name of the Python API. However, this leads to a name conflict.
Problem description
Consider the following file structure:
CMakeLists.txt
include/mylib.hpp
python_api.cpp
In reality there are also tests and examples, each with their own CMakeLists.txt, but for the purpose of this example the only thing that matters is:
In the (main) CMakeLists.txt I am defining a CMake target "mylib" that has the include path to the header(s), but also 'links' the targets of dependencies. So that the user (or tests, examples, or build of the Python API) only has to 'link' the target and be good to go. (Finally, I'm also installing the target in mylibTargets.cmake when I install the headers such that there is CMake support for the end user).
Now the problem: My Python package should have the same name, "mylib". However, if I call pybind11_add_module with "mylib", CMake complains that
CMake Error at .../share/cmake/pybind11/pybind11Tools.cmake:166 (add_library):
add_library cannot create target "mylib" because another target with the
same name already exists. The existing target is an interface library
created in source directory "..".
See documentation for policy CMP0002 for more details.
It has the right to complain. At the same time I cannot use a different name for either the CMake target (since I want to install and use it using the only logical name, "mylib") or the pybind11 target (since it has to encode "mylib").
So: how do I solve this?
(The only solution I found was to rename one of targets, but as described I don't want to do this)
Detailed example
Consider the simplified, single, CMakeLists.txt:
cmake_minimum_required(VERSION 3.1..3.19)
# configure target
project(mylib)
find_package(xtensor REQUIRED)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(${PROJECT_NAME} INTERFACE xtensor)
# installation of headers and of CMake target
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION include)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets)
install(
EXPORT ${PROJECT_NAME}-targets
FILE "${PROJECT_NAME}Targets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Build Python module
find_package(pybind11 CONFIG REQUIRED)
pybind11_add_module(${PROJECT_NAME} python_api.cpp) # <- target name conflict
target_link_libraries(example PUBLIC pybind11::module)
Too limited work around
I could entirely split building (and later install) the Python API to an independent CMakeLists.txt. However, I want to use the target "mylib", that I already equipped with everything it needs, to build the Python API. Since I want to do this without being forced to install the library forced, I don't know how to do this in a 'single' CMakeLists.txt
pybind11_add_module is just a wrapper around add_library, this is explicitely written in the documentation for that function. So, most of the "tricks", which works for the common libraries, works for python modules too.
That is, if you want resulted file to be named as mylib.so but cannot afford you to use mylib as a target name, then you could use any other name for the target but adjust OUTPUT_NAME property for that target. For example:
# Python library target has suffix '_python'
pybind11_add_module(mylib_python ...)
# But name of the library file doesn't have this suffix
set_target_properties(mylib_python PROPERTIES OUTPUT_NAME mylib)

CMake copy dll transitively

Basically I want CMake to copy dependency's dll to the same directory of the executable. Suppose I have the following directory structure:
my-project/
CMakeLists.txt
lib/
CMakeLists.txt
... # Some source files
app/
CMakeLists.txt
... # Some source files
The library lib depends on some third party dll, say foo.dll. The executable in app, say app.exe, depends on lib.
I've written a FindFOO.cmake to make the third party library foo.dll an imported target named foo.
Now when I compile app, in order to run the executable, foo.dll is required to be in the same directory as app.exe. How can this be achieved automatically with cmake? And what if I want to use CPack to package the application into an installer?
CMAKE_RUNTIME_OUTPUT_DIRECTORY is your friend.
If this variable is created before creating some target, if the target is RUNTIME, it will define where the output of the target will be placed.
In your case, it can be used to force foo.dll and app.exe to be in the same folder. Your root CMakeLists.txt should look like this:
cmake_minimum_required(VERSION 3.15)
project(foo_and_app)
# app.exe and foo.dll will be in bin subfolder of build dir
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_subdirectory(lib)
add_subdirectory(app)
#not connected to placement, but only to package creation
include(CPack)
It should be noted that this variable is used to initialize the properties of the targets added, meaning that everything may also be achieved by directly manipulating appropriate target properties.
Regarding packaging, what you ask is possible, regardless of the placement of runtime targets, by using install cmake statement. In lib/CMakeLists.txt you should add something like this:
# suppose that the target named `foo`,
# i.e. it is added by add_library(foo SHARED .....)
install(TARGETS foo
RUNTIME DESTINATION bin
)
same should be done for app/CMakeLists.txt:
# suppose that the target named `app`,
# i.e. it is added by add_executable(app .....)
install(TARGETS app
RUNTIME DESTINATION bin
)
If you have these install statements, the final destination will be bin folder within the chosen install folder.
In the end, here are the links for CMake documentation describing:
CMAKE_RUNTIME_OUTPUT_DIRECTORY variable
RUNTIME cmake targets
install(TARGETS ...)

Install a target defined in an included directory [duplicate]

Consider the following CMakeLists.txt file:
add_subdirectory(execA)
add_subdirectory(libB)
install(TARGETS execA libB
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
I get the following error:
install TARGETS given target "execA" which does not exist in this
directory
execA and libB have their own CMakeList.txt files and are located under project directory, as well as the build directory I'm running cmake (cmake ..):
project
|------ CMakeList.txt (the one with the code)
|----execA
| \- .cpp, .hpp and CMakelist.txt
|----libB
| \- .cpp, .hpp and CMakelist.txt
|---- lib
|---- bin
\---- build (where I´m commanding: $ cmake ..
How do I fix this error?
According to this bugreport, install(TARGETS) command flow accepts only targets created within the same directory.
So you need either move the add_library() call into the top-level directory, or split install(TARGETS) call into per-target ones, and move each of them into the corresponding subdirectory.
Since CMake 3.13 install(TARGETS) can work even with targets created in other directories.
install(TARGETS) can install targets that were created in other directories. When using such cross-directory install rules, running make install (or similar) from a subdirectory will not guarantee that targets from other directories are up-to-date.
Even though it would help seeing the CMakeLists.txt files contained in the subdirectories, I guess they contain add_executable and/or add_library statements to create your stuff.
Also, because of your example, I guess you are using the same name of your directories for your targets.
That said, you should know that symbols defined in a CMakeLists.txt file in a subdirectory are not visible by default within the context of the CMakeLists.txt file in the parent directory. Because of that, you should rather move your install statements within the CMakeLists.txt files within your subdirectories.
This should solve the problem, if my thoughts were right. Otherwise, I strongly suggest you to post in your question also the content of the other files above mentioned.
Anyway, the error is quite clear.
The file that contains the install statement for the target named X does not contain a target creation statement (add_executable and the others) that gives birth to that target, so it goes on saying that that target does not exist in that directory.
This still seems to be a pain point in CMake 3.11.
In our codebase, we have many targets defined in subdirectories and need to create an assortment of installers with different configurations and (potentially overlapping) combinations of targets.
Here's my solution:
Before calling add_subdirectory in your root CMakeLists.txt file, create a GLOBAL property with the names of the target(s) you want to include in your installer.
Wrap target creation functions (add_executable, etc.) in your own custom functions. Within those functions check if the target is present in the global property, and invoke install accordingly.
That approach allows you to centralize installer configuration.
Also: To support creation of multiple installers, we populate our global list along with other installer properties in separate .cmake files. When we invoke cmake, we pass the name of the installer configuration CMake file as a command-line argument. Our root CMakeLists.txt file simply calls include with that file.

cmake subdirectories install

I would like to set up a new project using CMake.
My working dir is the following :
CMakeLists.txt
include
- file1.h
src
- file1.cpp
- CMakeLists.txt
tests
- test1.cpp
- CMakeLists.txt
The main CMakeList.txt is the following:
cmake_minimum_required(VERSION 2.8)
project(bench CXX)
include_directories(include)
subdirs(src tests)
The src CMakeList.txt is :
add_library(foo SHARED file1.cpp)
install(TARGETS foo DESTINATION lib)
And the tests CMakeList.txt is:
add_executable(test1 test1.cpp)
target_link_libraries(test1 foo)
install(TARGETS test1 DESTINATION bin)
I would expect the files to be located in
<build_dir>/bin/test1
<build_dir>/lib/libfoo.so
But instead they are organized like this :
<build_dir>/src/test1
<build_dir>/tests/libfoo.so
What am I doing wrong ?
Thanks in advance,
You are giving instructions for the install step/build target, which is independent from the build tree under <binary_dir>.
Set CMAKE_INSTALL_PREFIX to a folder of your choice and run make install (assuming make build system here) after your build. The you will find CMAKE_INSTALL_PREFIX/lib/libfoo.so and CMAKE_INSTALL_PREFIX/bin/test1 as you told cmake. The DESTINATION path is always relative to CMAKE_INSTALL_PREFIX (unless you specify an absolute path, of course).
The compiled libs and binaries within the build tree (i.e. <build_dir>) will always reflect the source tree structure with respect to location; this cannot be changed and is not the intended way of use.
Generally there are three different "things": Source/Build/Install trees. The build tree mirrors the source tree structure and contains cmake-generated extra files and objects. The install "tree" is a setting of your choice, where CMAKE_INSTALL_PREFIX is the root directory under which libraries, archives and binaries are placed. You can control the behaviour completely (what you already intend to do!), although there are reasonable default settings.

Changing cmake directories

I'm writing a cmake file for a project which has the following structure
project/ (root)
libraries/ (contains (precompiled) libraries
src/
code/ (contains a set of fortran files)
My CMakeLists.txt file is currently in project/ and effectively is just
cmake_minimum_required(VERSION 2.6)
enable_language(Fortran)
project(project1)
set(projsrc src/code)
set(libdir lib/)
find_library(PROJ_LIBRARY pr10 PATHS ${libdir})
add_executable (sc1 sc1.f90)
target_link_libraries(sc1 ${PROJ_LIBRARY})
This creates my binary in the same folder as the source code, when I actually want it in the level above (i.e. in the src folder - this structure will be changed so we have a bin folder eventually), but haven't worked out how to do it.
Some answers on SO say you have to have a CMakeLists.txt file in every folder - is this correct? Is it possible to set an environment variable or use a CMake variable (e.g. http://cmake.org/cmake/help/v2.8.8/cmake.html#command:set). It's also not very clear from some answers whether the solutions they have posted are C++ specific (as that is what language CMake most often seems to be used for).
Edit
I found out that I can change it to the behaviour I want by modifying it slightly:
cmake_minimum_required(VERSION 2.6)
enable_language(Fortran)
project(project1)
set(projsrc src/code)
set(libdir lib/)
find_library(PROJ_LIBRARY pr10 PATHS ${libdir})
add_executable (src/sc1 ${projsrc}/sc1.f90)
target_link_libraries(src/sc1 ${PROJ_LIBRARY})
However, this doesn't explain why my behaviour is different to how it should be, according to arrowdodger below. I'm also still trying to work out how to display the values of environment variables; I've tried the following with no luck:
message(${RUNTIME_OUTPUT_DIRECTORY})
message($ENV{RUNTIME_OUTPUT_DIRECTORY})
message(${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
message($ENV{CMAKE_RUNTIME_OUTPUT_DIRECTORY})
By default, binaries will appear in respective subdirectory of your build dir. By respective i mean the directory that contains CMakeLists.txt with add_executable() call.
For example, if you have following CMakeLists.txt
add_executable(tgt1 src1.f90)
add_executable(tgt2 subdir/src2.f90)
in the root folder, you will get both binaries in ${CMAKE_BINARY_DIR}. So if you wish tgt2 to be in ${CMAKE_BINARY_DIR}/subdir, you need to add CMakeLists.txt there and call add_executable(tgt2 src2.f90) from there.
You can change this behavior:
CMAKE_LIBRARY_OUTPUT_DIRECTORY, CMAKE_RUNTIME_OUTPUT_DIRECTORY and others.
You can also set respective target properties.