How to search for libraries in multiplatform cmake builds? - dll

I am searching for a nice way to handle linked libs in cmake. In my case the cmake configuration file(CMakeLists.txt) is executed in Linux environments and on cygwin on Windows.
The build process contains two libs. libA is build from sources and only depends on the std. C API. libB is build, as well, from sources and includes the libA. The libs are build separately(with an own git repro).
By defining in cmake at "libB/CMakeLists.txt"
find_library (libB A)
I am linking libB against libA. When linking under cygwin, this line fails. When I change it to
find_library (libB ${CMAKE_LIBRARY_PATH}/static/libA.dll.a)
it works.
Both libs are build as shared libs.
"libA/CMakeLists.txt":
add_library(libA SHARED ${libA_source})
install(
TARGETS libA
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/static
)
and
"libB/CMakeLists.txt":
add_library(libB SHARED ${libB_source})
install(
TARGETS libB
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/static
)
Under Linux this works fine. The libs are placed into "/user/local/lib". Under Windows the libs are placed into "/user/local/lib/shared" + the dlls are placed into "/user/local/bin".
The statement to link libB against libA changes to ("libB/CMakeLists.txt"):
if (UNIX)
find_library (libB A)
elseif (CYGWIN)
find_library (libB ${CMAKE_LIBRARY_PATH}/static/libA.dll.a)
endif()
Any idea how to handle the linker references of these two builds in a simple - maybe - platform independent line?

This should work within your libB/CMakeLists.txt:
find_library(libA
NAMES A libA libA.so libA.dll libA.dll.a
HINTS ${CMAKE_INSTALL_FULL_LIBDIR}
)
target_link_libraries(libB ${libA})
Check the path to libA with
message(STATUS "libA=${libA}")

Related

find_package static library instead of shared library

When building my project with CMake, I would like it to link to libraries statically (if available). Right now, it finds .dll.a files, regardless of the existence of .a files.
For example, take linking to libpng in a small sample project:
cmake_minimum_required(VERSION 3.15)
project(Test)
add_executable(Test main.cpp)
find_package(PNG REQUIRED)
message(${PNG_LIBRARIES})
target_link_libraries(Test PRIVATE ${PNG_LIBRARIES})
For the message, it outputs
C:/msys64/mingw64/lib/libpng.dll.aC:/msys64/mingw64/lib/libz.dll.a
But the libpng.a and libz.a files are also available in the same directory. How can I tell CMake to link with .a files?
I am using MinGW-w64 with msys64 on Windows 10, but would prefer a solution that is cross-platform.

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

Link one project to another CMAKE

I am building a project which contains lots of sub projects. for example as ...
LibA (build as shared library)
LibB (depends on LibA & build as shared library)
AppB (depends on LinB)
Directory structure (Which I want) is as ...
bin/
output/
src/libA/
src/libB/
src/appB/
Each of sub projects (LibA, LibB & AppB) has their own CMakeLists.txt file.
I want ..
1. Build LibA as shared library (I know how to do it)
2. Build LibB as shared library with linking of LibA (Don't Know how to do)
Explanation: When I start building LibB,
LibA build first and
ready to link for LibB
when LibB ready to finish
3. Build AppB : If I start building AppB,
LibA build first and
LibB build after and
both linked to AppB
Now I know classic way, build separately LibA & LibB and supplied path of lib and include to AppB. But I want to build them at once like
Build LibA
Build LibB (if LibA is already build then ignore, else build LibA)
Build AppB (if LibA, LibB are already build then ignore, else build them)
What I want
How can I achieve such behavior using CMAKE ?
It should be cross platform
Simple enough to include many more sub projects
Here is one solution. You can use a top-level CMakeLists.txt file to tie all the projects together. So in your directory structure, this would be placed here:
bin/
output/
src/libA/
src/libB/
src/appB/
CMakeLists.txt <--- Top-level CMakeLists.txt
Your top-level CMakeLists.txt file (as a sibling to the src directory) could look like this:
cmake_minimum_required(VERSION 3.11)
# Add your dependencies in the order you want them to build.
add_subdirectory(src/libA)
add_subdirectory(src/libB)
add_subdirectory(src/appB)
With each of the src directories having their own CMakeLists.txt file, here's an example of each of these individually.
You can set up LibA as a shared library with CMake with the CMakeLists.txt file in src/libA:
project(LibA_Project)
add_library(LibA SHARED sourceA.cpp ... more sources here ...)
Next, CMake will traverse to the src/libB directory to configure LibB. Here is what the src/libB/CMakeLists.txt file could look like:
project(LibB_Project)
# Create a shared library for LibB as well.
add_library(LibB SHARED sourceB.cpp ... more sources here ...)
# Link LibA to LibB as a dependency.
target_link_libraries(LibB LibA)
Finally, CMake will go to the src/appB directory. Here is that CMakeLists.txt file:
project(AppB_Project)
# Create the executable AppB.
add_executable(AppB main.cpp ... more sources here ...)
# Link LibA and LibB to AppB as dependencies.
target_link_libraries(AppB LibA LibB)
This approach can easily be expanded to include more sub-projects (e.g. LibC, LibD, AppE, etc) if necessary.

Why does CMAKE installed target fail to link to provided libraries?

I'm building a target that depends on some provided libraries. My src directory hierarchy looks like this:
I use the following CMakeLists.txt to build a target and install it within the build/install directory:
cmake_minimum_required(VERSION 2.8.3)
project(example)
include_directories(include)
link_directories(lib)
add_executable(${PROJECT_NAME}
src/example.cpp)
target_link_libraries(${PROJECT_NAME}
curlpp)
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
install(TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES lib/libcurlpp.a lib/libcurlpp.so lib/libcurlpp.so.1 lib/libcurlpp.so.1.0.0 DESTINATION deps)
When I do a simple build, everything is fine and the target is properly linked to the provided libraries. But when I do make install, the target is generated but fails to link to the libraries:
I understand the linkage failure of the installed target: the install/deps directory is not in the LD_LIBRARY_PATH of my environment. But what has cmake done to make the directly built target link correctly? Can I do something similar to make the installed target work properly?
Here is a minimal replication of the problem

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