Able to find Qt5 library with target_link_libraries, but not with find_library. What gives? - cmake

I noticed a strange behavior when trying to change some existing code. What I noticed is that we call target_link_libraries() and this finds a Qt5 library with no issues, but calling find_library() doesn't find the same library. Here's the code I have:
list(APPEND CMAKE_PREFIX_PATH ${QT_ROOT})
find_package(Qt5Widgets REQUIRED)
target_link_libraries(projectName Qt5::Widgets)
find_library(QTWIDGETSFOUND NAMES Qt5::Widgets)
message(STATUS ${QTWIDGETSFOUND})
The result of this is:
QTWIDGETSFOUND-NOTFOUND
However, if I look into a generated file, link.txt, I can find this line:
/home/user/Documents/work/test/build/venv/.conan/data/Qt/5.11.3/org/dev/package/80043e232e8ab07f4b25e67652a9490d9ad33d91/lib/libQt5Widgets.so.5.11.3
I conclude that target_link_libraries() can find the Qt5:Widgets library, but find_library() cannot. I find this behavior quite strange.
My use case is that I want to target my library, which I do just fine, but then get the path to the library it found and copy that library file somewhere. I can't find a way to get a path from target_link_libraries(), so I figured that I would just call find_library() after to get the path of that library and copy it from there. The only possible answer I see to my problem is that Qt5:Widgets isn't the actual name of the library, but that target_link_libraries() can resolve it from the find_package() statement I made above and that find_library() isn't able to for some reason.
So, here are my questions:
Why is the piece of code I porived not doing what I thought it would do?
How can I make find_library() work in the provided code sample?
If no one can find a solution for the above, what other ways could I get a path to a library I just linked with target_link_libraries() other than find_library() that would be simple?
Thanks!

Related

Why is VULKAN_LIBRARY set to VULKAN_LIBRARY-NOTFOUND yet VULKAN_FOUND is TRUE?

I'm interested in trying out Vulkan for myself, but I'm having difficulty getting CMake to link to it reliably. I decided to use CMake's FindVulkan module... or at least how I think it should work. Here's how I did it:
# Hey CMake. Look for Vulkan.
find_package(Vulkan REQUIRED)
# Alright, no errors? Tell me what you found!
message("Vulkan found? " ${VULKAN_FOUND})
message("Alright, where is it? " ${VULKAN_LIBRARY})
message("And I can include it? " ${VULKAN_INCLUDE_DIR})
And a little later in the file:
# Let's make a library and link vulkan
include_directories(${VULKAN_INCLUDE_DIR})
add_library(myLib myLib.cpp myLib.h)
target_link_libraries(myLib ${VULKAN_LIBRARY})
So, I get my results! First off, my CMake output:
Vulkan found? TRUE
Alright, where is it? VULKAN_LIBRARY-NOTFOUND
And I can include it? C:/VulkanSDK/1.0.65.1/Include
-- Could NOT find Vulkan (missing: VULKAN_LIBRARY)
-- Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
-- Using Win32 for window creation
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
VULKAN_LIBRARY (ADVANCED)
linked by target "TOVE" in directory C:/Users/User/Desktop/TOVE
Odd, looks like you found my include directory, but you can't find my library. The messages in the middle are actually GLFW. I kept them in just incase they meant something more. Finally, CMake stops with an error.
Some additional testing reveals that both ${VULKAN_LIBRARIES} and ${VULKAN_INCLUDE_DIRS} are blank. As expected, swapping them out with their singular counterparts makes Visual Studio 2017 mountains of confused about my vulkan/vulkan.h include.
I can't find any case on the internet where someone gets a VULKAN_LIBRARY-NOTFOUND, but there might be another library that has similar issues. Why am I finding only half of the information here? Is it an issue with Vulkan or CMake, or am I just really bad at writing with CMake. I'm relatively new to CMake, and I'm just experimenting with it so I apologize if it was just me misusing some important function or something among those lines.
I had the same error while trying to compile GLFW 3.2.1 on Windows. The problem is that GLFW CMakeLists uses its own FindVulkan.cmake in "${GLFW_SOURCE_DIR}/CMake/modules" which seems a bit outdated.
Taking some code from the FindVulkan.cmake inside CMake distribution (3.10) to modify the GLFW file works as expected and VULKAN_LIBRARY cache var is filled with the path of the .lib file.

Cmake Internals - target_link_libraries

Suppose I have a directory dir that contains the followings files: libpippo.so and libpippo.a
#CMAKE CODE
...
link_directories(${dir})
...
target_link_libraries(program pippo).
What happens practically with the command target_link_libraries ?
If am I right, Cmake prefer to use dynamically library if not specified, that's true?
If so, in which way it control that everything is fine? It just check that the library and the required symbolic link are present? It also save the link "dir/libpippo.so" in order to know how where to found the library when the programm will be launched?

Use CMake find_package with a custom, bugfixed file

I have a library (namely CGAL). It provides a FindMODULE.cmake file for a third-party library not shipped with it CGAL (namely Intel TBB). Unfortunately, this file has a bug that I need to fix. (The bug seems to be related to incompatible directory structures, but that's not the point here.)
So the CMakeLists.txt of my project has a line:
find_package( TBB )
This will invoke FindTBB.cmake which is provided in the directory structure of CGAL.
Now, I need to fix a bug in FindTBB.cmake. I'd like to just copy that file and put the fixed version directly into my project directory.
How can I tell CMake to use FindTBB.cmake in my project directory (instead of the one provided by CGAL) when calling find_package?
Alternative approach:
Copy the file to your module directory and modify it.
Call find_package(TBB) before you do anything related to CGAL.
Use the CMake call used to find / interact with CGAL. If you are lucky, the results of all checks are cached and it work. It still might not work, depending on what is actually done.
The find_package function might be too heavy in this case, you can try just including your relocated .cmake file like this.
include(<local path>/FindTBB.cmake)

How to link a static library to an executable using CMake

In CMake, we use TARGET_LINK_LIBRARIES() to link a shared library to an library/executable.
For example:
TARGET_LINK_LIBRARIES(ExecutableName xxx)
where ExecutableName - is the name of executable
xxx - is the library name.
As of my understanding CMake searches for "libxxx.so" at the paths mentioned in LINK_DIRECTORIES() macro.
But if I have a third party library with name "libxxx.a" then how do I link the library to the executable using CMake.
Thank you for your help in advance!
You should always try to give either full paths or CMake targets to target_link_libraries.
Since you do not seem to build the dependency as part of the CMake project, the only way to obtain a CMake target to link against, is to create an imported target. This is usually quite tedious to do manuall, so unless the dependency already provides a config file with an imported target, you probably do not want to go down that road. Imported targets are the most convenient to use, but only if you can get CMake to write the for you.
So, absolute paths it is then. You obviously would not want to hardcode absolute library paths in your CMakeLists. As pointed out in your question, the desired behavior is that you specify just the name of a library and CMake should be able to figure out its location automatically. This is exactly what find_library does for you.
To link against a library xxx, you would do something like this:
find_library(LIB_LOCATION xxx)
target_link_libraries(ExecutableName ${LIB_LOCATION})
Note that find_library provides a plethora of options to further specify where to look for the requested library. Get rid of your existing link_directories call and add the respective paths as hints to find_library instead.
This approach is both more flexible when porting your CMake code to other platforms and more easy to debug if something goes wrong than your initial approach.
Just specifying the library filename should work:
TARGET_LINK_LIBRARIES(ExecutableName libxxx.a)
and
TARGET_LINK_LIBRARIES(ExecutableName xxx)
actually should work too as that would not look for the .so but for a libxxx.a file in the search paths.

How to get CMake to build a Fortran program with MPI support?

I was trying to parallelize a Fortran program using MPI. I use CMake to do the build of my program. It was difficult to find support on getting CMake to create a working makefile for Fortran with MPI support on google, but from what I could gather, I added the following commands to my CMakeLists.txt script:
find_package(MPI REQUIRED)
add_definitions(${MPI_Fortran_COMPILE_FLAGS})
include_directories(${MPI_Fortran_INCLUDE_DIRS})
link_directories(${MPI_FortranLIBRARY_DIRS})
This will locate MPI on my system and then set the variables found in the following three commands. In my linking line, I added the MPI libraries variable to the list of the other libraries that my program needed to build.
target_link_libraries(${exe_name} otherlibs ${MPI_FortranLIBRARY_DIRS})
Doing cmake and then make worked to build the program and the program ran; however, when I tried to add more to the source which required me to include the mpif.h include file, my compilation failed due to not being able to find this header file. I also could not use mpi because the compiler cannot find the mpi.mod file in the path.
I inserted "message" commands into my CMakeLists.txt file and printed out the values of the variables that I was using for including and linking. It turns out that the variables, MPI_Fortran_INCLUDE_DIRS and MPI_FortranLIBRARY_DIRS weren't set to anything. A check of the module that CMake is actually using to set these variables (FindMPI.cmake) showed these variables to be non-existent. I changed my CMakeLists.txt file to use the correct variables:
find_package(MPI REQUIRED)
add_definitions(${MPI_Fortran_COMPILE_FLAGS})
include_directories(${MPI_Fortran_INCLUDE_PATH})
link_directories(${MPI_Fortran_LIBRARIES})
target_link_libraries(${exe_name} otherlibs ${MPI_Fortran_LIBRARIES})
Now when I execute make, the compiler could find both mpif.h as well as mpi.mod.
UPDATE:
I want to mention that this solution worked for cmake version 2.8.10.1. When I moved my CMakeLists.txt scripts to a different machine that has cmake version 2.8.4, I get the same error about mpi.mod missing during the link stage. I checked the FindMPI.cmake module and, sure enough, there are no variables that specify the language (i.e. there is no MPI_Fortran_LIBRARIES variable, just a MPI_LIBRARIES variable, and this variable is not getting set to the correct location of the mpi library on that system. So this solution will be dependent on cmake version.
Sounds like you are not using the mpi compiler. That is fine, but you have to add a lot of flags then. There is not really an mpi compiler but a wrapper that sets the flags to be able to use mpi. With cmake I was able to do this by defining the fortran compiler I was going to use BEFORE the call to cmake. It's not a nice solution since you loose portability, but it works. I'm trying to find a better solution and define inside cmake what compiler to use, but have not been able to do so. In other words, this works for me:
FC=mpif90 cmake ../.
make
I was having the same problem as you. Hope this solves the issue. If anybody finds how to define the fortran compiler INSIDE cmake please post it!
as you've already noticed, you misspelled the name of two variables, namely MPI_Fortran_LIBRARIES and MPI_Fortran_LIBRARIES
It is useful also to add:
cmake_minimum_required(VERSION 2.8.10)
at the very beginning of your CMake to be sure that these variables will be defined.