CMake link directory passing when compiling shared library - cmake

Say I have C project with the following structure (simplified):
|- CMakeLists.txt <- This is root CMake
|- lib
|- <some source files>
|- CMakeLists.txt <- CMake file for building the library
|- demo
|- <some source files>
|- CMakeLists.txt <- CMake for building demo apps
|- extra_lib
|- <some source files>
|- CMakeLists.txt <- CMake for building supplementary library
Now, I want to build my library (living in lib) as a shared library to be used by demo apps from demo directory.
Additional library, that can not be a part of my library (it is essentially a wrapper for some C++ external library) is also to be compiled as a shared library and then linked to my library.
I have a problem with including dependencies for additional library. In its CMakeLists.txt I've defined link_directories to point location where .so libs are stored and then target_link_libraries to point which should be linked. At the end I did export target.
include_directories(${EXTERNAL_DIR}/include)
link_directories(${EXTERNAL_DIR}/lib)
add_library(extra_lib SHARED extra_lib.cpp)
target_link_libraries(extra_lib
some_lib
)
export(TARGETS extra_lib FILE extra_lib.cmake)
The point is that when I try to compile lib and link it against extra_lib I get an error that some_lib is not found what I guess means that link_directories is local to the extra_lib.
Now, question is how can I make it propagate together with dependencies? I'd like it to work in the way that adding extra_lib as subdirectory and as a dependency for my lib would automatically add linked directories from extra_lib to the lib linking process.
The linking process would look like:
(some external library) --> extra_lib --> lib --> demo app

First off, the CMake docs state that commands like include_directories and link_directories are rarely necessary. In fact, it is almost always better to use target_include_directories and target_link_libraries instead.
Secondly, the reason your approach fails is because you need to let CMake know about the existence of some_lib. You can do this like so:
add_library(some_lib SHARED IMPORTED)
set_target_properties(some_lib
PROPERTIES
IMPORTED_LOCATION ${EXTERNAL_DIR}/lib/libsome_lib.so)
Then, afterwards:
target_link_libraries(extra_lib some_lib)

Related

Copy compilation/linking settings from project in CMake object library?

I'm in a similar situation to Adding object-file dependencies where it is mentioned:
... I need some object files that are part of the same library to be compiled before others.
Don't try to declare dependencies between object files. If there are files that have a dependency, break them out into a separate library with add_library and then declare the dependency with add_dependencies and target_link_libraries. There is no extra cost for doing this. Particularly, consider looking at Object Libraries.
I tried to implement this in my original project, where I'm working with a massive CMakeLists.txt file, where include & linker paths may be conditionally inserted or removed, and I failed. In essence, that CMakeLists.txt file would looks like this simplified:
project(MY_PROGRAM C CXX ASM)
set(MY_PROGRAM_sources
main.c
file_one.c
file_two.c
)
add_executable(MY_PROGRAM ${MY_PROGRAM_sources})
target_include_directories(MY_PROGRAM PRIVATE
${CUSTOM_PATH}/src/custom/include
...
)
add_compile_options(-Wall
-Wno-format
...
)
target_link_libraries(MY_PROGRAM
somelib1
somelib2
)
This builds fine, using Unix Makefiles.
Now, I've tried to extract file_one and file_two objects like this:
project(MY_PROGRAM C CXX ASM)
set(MY_PROGRAM_sources
main.c
#file_one.c
#file_two.c
$<TARGET_OBJECTS:SEPFILE_OBJS>
)
add_library(SEPFILE_OBJS OBJECT
file_one.c
file_two.c
)
add_executable(MY_PROGRAM ${MY_PROGRAM_sources})
target_include_directories(MY_PROGRAM PRIVATE
${CUSTOM_PATH}/src/custom/include
...
)
add_compile_options(-Wall
-Wno-format
...
)
target_link_libraries(MY_PROGRAM
somelib1
somelib2
)
cmake passes fine here, but when I hit make, there is breakage as soon as file_one.c starts compiling:
...
[ 9%] Building C object CMakeFiles/SEPFILE_OBJS.dir/file_one.c.obj
In file included from C:/tmp/file_one.c:14:
C:/tmp/file_one.h:19:10: fatal error: ExoticHeader.h: No such file or directory
19 | #include <ExoticHeader.h>
Clearly, the SEPFILE_OBJS library did not inherit the include libraries nor other compilation/linker settings from the MY_PROGRAM executable.
I could probably repeat all target_* lines for SEPFILE_OBJS - but that looks quite unmanageable, plus my original CMakeLists.txt is huge, and I'd like to avoid that kind of work.
So is there a way to say to CMake "keep the same compilation and linker settings as in executable for MY_PROGRAM, also for the object library SEPFILE_OBJS"?
I would do it the way around. I would first define a library section, including file_one.c and file_two.c, and then a main executable section, which would link against this library target.
I would also follow something along the lines below for a directory structure (ideally, I would put the source files within a src directory):
/ MY_PROGRAM
|- include
| \- MY_PROGRAM
| |- file_one.h
| \- fine_two.h
|- file_one.c
|- file_two.c
|- main.c
\- CMakeLists.txt
Put your header files in an include/${PROJECT_NAME} folder, and set an include_dir var pointing to it.
set(include_dir ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME})
Define the list of library and application sources:
set(lib_sources
"${CMAKE_CURRENT_SOURCE_DIR}/file_one.c"
"${CMAKE_CURRENT_SOURCE_DIR}/file_two.c"
)
set(app_sources
"${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
)
Library section. Define a lib_MY_PROGRAM target, and set the include directories, compile options, and link libraries for it. Notice it's better to use the target_... specific commands than the more general ones, such as add_compile_options:
add_library(lib_${PROJECT_NAME} ${lib_sources})
target_include_directories(lib_${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${include_dir}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
target_include_directories(lib_${PROJECT_NAME} SYSTEM PUBLIC
${CUSTOM_PATH}/src/custom/include
)
target_compile_options(lib_${PROJECT_NAME} PRIVATE
-Wall
-Wno-format
)
target_link_libraries(lib_${PROJECT_NAME} PRIVATE
somelib1
somelib2
)
Main binary section:
add_executable(${PROJECT_NAME} ${app_sources})
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${include_dir}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
target_compile_options(lib_${PROJECT_NAME} PRIVATE
-Wall
-Wno-format
)
target_link_libraries(${PROJECT_NAME} PRIVATE lib_${PROJECT_NAME})

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

Cannot build library in subdirectory when top-level CMakeLists.txt calls install(EXPORT)

I'm trying to add a feature as a sub-library to
an existing application. But I met troubles of install(EXPORT ...)
My project source code structure shows as below:
app (the existing application)
|-- top-level CMakeLists.txt
|
|-- sublib (my new feature)
| |-- src
| |-- include
| `-- CMakeLists.txt
|
|-- other existing src ...
I build sublib in the CMakeLists.txt inside my own feature as:
add_library(sublib ${LIB_SRC})
I modify the top-level CMakeLists.txt in the existing application to
link sublib:
add_subdirectory(subdirectory of sublib)
...
target_link_directories(app sublib)
I thought it was enough. But CMake threw out an error:
CMake Error: install(EXPORT "appTargets" ...) includes target "app"
which requires target "sublib" that is not in the export set.
I guess it is because app itself is exported by install(EXPORT .. ) in the
top-level CMakeLists.txt. Thus I also try to install and export sublib.
I add the install and export into sublib CMakeLists.txt:
install(TARGETS sublib
EXPORT sublibTargets
ARCHIVE DESTINATION ${BIN_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${LIB_INSTALL_DIR}
)
install(EXPORT sublibTargets
FILE sublib-config.cmake
DESTINATION ${LIB_INSTALL_DIR}/cmake/sublib
)
I then add find_package() in top-level CMakeLists.txt:
find_package(sublib REQUIRED)
target_link_directories(app sublib)
However, it becomes worse. sublib is never built at all and
sublib-config.cmake is not found.
I manually set the PATHS to sublib-config.cmake in find_package() but it still failed.
Could you please tell me how fix the EXPORT issue?
Thank you.
Best regards,
David Hu
You should not have to call find_package(sublib) in your top-level CMakeLists.txt, this is intended to be used by a project that uses sublib once it is installed. Because you call add_subdirectory(sublib) in your CMakeLists.txt, the target sublib is already available where you call target_link_directories(app sublib).
I tried to answer it by myself. I'm inspired by #piwi.
If the sublib is put under app as a sub-directory, sublib source code should be built together with those of app, rather than treating sublib as an standalone library. No need to export sublib.
Export and install of sublib is only necessary when sublib is placed as an independent library. Export and install the sublib in sublib CMakeLists.txt. Put a Findsublib.cmake under app's cmake/modules. Thus later when app CMakeLists.txt call find_package(sublib), Findsublib.cmake tells app how to find out sublib and build or link sublib

CMake include_directories vs file GLOB

I've been trying to migrate a project from VS to CMake, but I'm not sure my project structure is quite fit to a simple migration:
project/
|- CMakeLists.txt
|- build/
|- (cmake stuff)
|- src/
|- main.cpp
|- tests.cpp // also contains a main()
|- class1.hpp
|- class1.cpp
|- class2.hpp
|- class2.cpp
|- ...
|- included/
| - (external libs)
My CMakeLists.txt attempt so far has been:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
add_executable(webnectar src/main.cpp
src/test.cpp)
enable_testing()
add_test(tests project)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/included)
But I get linking errors with my own classes., I don't understand why.
In several other SO questions I've seen people using file GLOBs to include all sources from a subfolder, which I guess would be a solution. Still, I wish I understood why include_directories is not enough and why (if it would work) file GLOB is OK.
Also, using file GLOB would mess with my build because both test.cpp and main.cpp have a main function.
Although it would look like a different matter (for a different question), please consider this question as more general in the sense of how could I fix all these issues with either a CMake syntax or with a more suitable file structure.
Regarding include_directories This directive corresponds to -I compiler flag and allows compiler to find header files, i.e. those which are included in #include ....
You should mention all your source files in the arguments of add_executable. That's unavoidable.
You could form the complete list of sources with FILE(GLOB..):
FILE(GLOB webnectar_SOURCES RELATIVE src/ *.cpp)
and then use it in add_executable(webnectar ${webnectar_SOURCES}).
However, this would not be the best and safest option, because it contains a significant flaw. The list of files is formed during the "configuring" stage of the build process (e.g. cmake -D<....> -D<.....> .) and then it's never rebuilt until CMake-related files (CMakeLists.txt, CMakeCache.txt and so on) somehow change. So if you first run cmake... and then add a new file, it won't be noticed, and Makefiles won't be regenerated.
Additionally, if some extra files (e.g. left after an interrupted merge) fit the mask you will get some quite unexpected results.
So it's safer to form and maintain an explicit list of sources, that is,
set(webnectar_SOURCES
src/main.cpp
src/class1.cpp
src/class2.cpp
...
)
and then use it in add_executable(webnectar ${webnectar_SOURCES}). The name of variable can be any but some IDEs like KDevelop prefer a standard naming <artifact>_SOURCES, so they can mantain the list automatically for you (or at least try to maintain :) )

Moving Headers/Libraries/Executables to specific directories

I've recently started using CMake for one of my multi-platform projects, but I'm having a little trouble figuring out how to do something.
Basically, inside the project I've got multiple libraries and executables, all in their own folders. I would like to place all of the compiled libraries into one directory on build i.e. a lib folder inside the CMake build folder. I would like to do the same things for the executables.
CMake Build Directory
| ----------> bin (where i want the executables to go)
| ----------> lib (where i want the libraries to go)
| ----------> utils (where the libraries are ordinarily compiled)
| ----------> test (where the executables are ordinarily compiled)
There are directories inside utils and apps for all the different libraries and executables I'm making. I have a CMakeLists in the base folder of my source directory which adds all the subdirectories. If anything does not make sense then feel free to ask.
You can also use this:
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
Look at install CMake command.
Here example from that page:
install(TARGETS myExe mySharedLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
install(TARGETS mySharedLib DESTINATION /some/full/path)
So you can use CMAKE_BINARY_DIR instead /some/full/path
In addition, for includes:
install( FILES ${HEADERS}
DESTINATION inc )
[when set of header files to be installed]
install( DIRECTORY include/${PROJECT_NAME}/
DESTINATION inc)
[when header directory to be installed]
More Info