What does cmake do with Unnecessary links - c++

I'm cleaning/refactoring a quite old project and I realized they made the mistake(?) of adding unnecessary links to the targets in all the CMakeLists.txt files:
LIST(APPEND ${PROJECT_NAME}_LIBRARIES RecursiveLy_generated_list_with_unnecessarystuff)
target_link_libraries(sample PUBLIC ${${PROJECT_NAME}_LIBRARIES})
I replaced the list by one that contains only the necessary links and the compilation time got noticeably reduced.
So I'm wondering what does cmake do when the symbols of a library that was linked are not used?
I also cleaned the include directories (ALL the .h files of the project were recursively added (many not needed)), did cmake think the symbols were used because they appeared in the headers even if this headers were not #included ?

What does cmake do with Unnecessary links
Passes proper command line options to the compiler.
what does cmake do when the symbols of a library that was linked are not used?
Nothing. CMake is not even aware that symbols of a library are or are not used. CMake does not link your code, compiler does.
did cmake think the symbols were used because they appeared in the headers even if this headers were not #included ?
No, CMake is unaware of what symbols are used where. CMake is unaware of the content of header files. CMake creates a list of commands to run. What commands to run depends on what is written in CMakeLists.txt.

Related

Where does target_link_libraries look for the required files?

I am building a project from source and am trying to understand what is happening in the CMakeLists.txt files.
Where exactly is target_link_libraries looking for the required library files?
The specific CMakeLists.txt file I have has:
target_link_libraries(MyApplication PRIVATE
Magnum::Application
Magnum::GL
Magnum::Magnum
Magnum::Shaders)
I found folders that have the names GL and Shaders in a directory called Magnum in the project, and they have a collection of header files in them. I believed that target_link_libraries is telling CMake to include the libraries in the GL and Shaders folder.
However, I cannot seem to find a corresponding folder for Application, hence my line of reasoning must be flawed.
I do know target_link_libraries is doing something related to allowing the finally-put-together program to be able to use a set of libraries.
What exactly does target_link_libraries do? Where does it look for the required files in order to be able to use the libraries it needs to?
target_link_libraries doesn't link anything automatically. You should have a target previously created via add_library (or add_executable), where all files are listed.
The way these targets added into your CMake project may differ. E.g. you may have a library source files with CMakeLists.txt configuration (where the said add_library command is) under some folder libs/mylib. Then in your CMakeLists.txt you may have the library added with add_subdirectory(libs/mylib). Another option is to add the library with find_package.
In your specific case, you pass targets to target_link_libraries(). Targets with a shape like Magnum::Application etc are either imported targets or ALIAS targets.
From what you say, it seems than Magnum is vendored into your project, so I guess your are linking ALIAS targets, like the one defined here https://github.com/mosra/magnum/blob/cfc02599e54e02337dd56bb61f70b2e61eb9ce8d/src/Magnum/CMakeLists.txt#L295
Targets defined by add_library() or add_executable() in CMake are an abstraction carrying several informations, including location of files.

How to include source of external library for IntelliSense in a CMake project?

I am working on a C++ application in Visual Studio, using the Visual Studio's CMake project template.
To build my application I only need the header and the external library and I can link it like this:
# CMakeList.txt : CMake project for myapp, include source and define project specific logic here.
cmake_minimum_required (VERSION 3.8)
# Add source to this project's executable.
add_executable (myapp "myapp.cpp" "myapp.h")
# Add TIFF library
set(TIFF_INCLUDE_DIR "C:\\libs\\tiff\\out\\install\\x64-Debug\\include")
set(TIFF_LIBRARY "C:\\libs\\tiff\\out\\install\\x64-Debug\\lib\\tiffd.lib")
find_package(TIFF REQUIRED)
target_link_libraries(myapp PRIVATE TIFF::TIFF)
So far, so good. I can use the tiff library to open, read, write tiff files, etc., and IntelliSense is catching up with declarations in the header files. But, I would like IntelliSense to also be aware of the full source code of the TIFF library. For example, if I am in myapp.cpp and I ctrl+click on TIFFOpen it opens the header corresponding to TIFFOpen, but when I ctrl+click TIFFOpen in the header file, it doesn't go to the corresponding source file, which is the normal behaviour for source files in myapp. This is understandable, since I never told Visual Studio where to find the source files of the external library.
CMake doesn't need to know where the source files of the external libraries are, since it won't build the external library, therefore I guess I don't/shouldn't change anything in CMakeLists.txt
One option (I haven't tried yet, but I'm fairly sure it would work), would be to just include the entire tiff library as a sub-project of myapp. I do have some problems with this solution though:
The external library is not conceptually an integral part of the project, and I don't plan to modify the external library. This is more of a principle issue.
Simply having it as a subfolder in my project makes it a risk of changing something I didn't intend to change.
I don't want to rebuild the external library when I do a rebuild all. I know Visual Studio / CMake is smart enough to figure out that nothing changed and doesn't rebuild, but I would rather have Visual Studio / CMake not even try.
The way I see it, I have to set the directory with the source files somewhere in Visual Studio settings, but still related to the project. My best guess is that the .vs/ProjectSettings.json is the file I need to edit somehow, but honestly, I have no clue.
Alternatively, maybe I could write some command in CMakeLists.txt that doesn't do anything, but triggers the IntelliSense to look in the folder with source files. Again, I have no clue how should I go about this.
In a nutshell, I want IntelliSense to see all source files of an external library, the same way it sees the source files of myapp, without including all source files of the external library as a sub-project of myapp. How should I go about it, if even possible?
If relevant, I use Visual Studio 2019 Community and the CMake it comes with it (3.15).
Regarding your last comment, writing code in comment section is inconvenient so I'll just post it here although not an answer.
libtiff-4.pc is for pkg-config, not cmake, and find_package() can't deal with it directly on Windows and would take some work if you really want to. It might be easier to just write everything manually. Remember to set the tiff.lib and tiffd.lib according to your configuration. You can use CMAKE_BUILD_TYPE variable and if() command such as:
# set build type to release if not specified
if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE)
endif()
# switch lib to link according to build type
if(CMAKE_BUILD_TYPE STREQUAL "Release")
# for release
set(TIFF_LIBRARY "<your_path_to_installed_lib>/tiff.lib")
else()
# for debug. If you have other configs like relwithdebinfo you can add more
set(TIFF_LIBRARY "<your_path_to_installed_lib>/tiffd.lib")
endif()
Also, remove find_package() and use target_link_libraries() and target_inlude_directories():
set(TIFF_INCLUDE_DIR "<your_path_to_installed_headers>/include")
target_link_libraries(myapp PRIVATE ${TIFF_LIBRARY})
target_include_directories(myapp PRIVATE ${TIFF_INCLUDE_DIR})
You can also skip setting TIFF_LIBRARY and TIFF_INCLUDE_DIR and pass the string directly if you like.
I use Visual Studio a lot and it's my favorite IDE. But package management with cmake on Windows is not as smooth as Linux. Always remember to set environment variables after compiling and installing external libraries.
Normally find_package() will look for a system environment variable named <libname>_DIR (for example TIFF_DIR, which is not found in your case), which is used to store path to installed lib, then look for <libname>Config.cmake and <libname>ConfigVersion.cmake in that folder (and would fail for TIFF since it doesn't have them).
It also searches other places, check https://cmake.org/cmake/help/v3.15/command/find_package.html?highlight=find_package for details.
So for those libs with cmake exported files, add a variable with correct name and value. And that's only for compiling.
If you want your application to run after compiling, you also need to add the path of installed lib's binaries (usually *.dll) to system's Path variable. In your case you should find something like tiff.dll and tiffd.dll after compiling and installation, add that folder to Path and you are good to go.
Actually, showing the source in the IDE is easy enough.
Just add the source with add_library(tiff_for_ide EXCLUDE_FROM_ALL ${SOURCES}) but don't link with it in the main program. You'll want to use a different target name for the library. For this you'd need the source in your project directly or as a submodule (if using Git, something else if available by you VCS).
Other options are using ExternalModule_Add; or FetchContent_MakeAvailable with the add_library as above, to avoid adding third party deps into the repository directly.
Now, just cross your fingers and hope that the IDE is not intelligent enough to restrict searching for sources that are linked against the target which compiles the current file. Or that it's intelligent enough to detect this situation but fallback to searching the whole project files anyway when it's linking against a binary.

What can linking to a CMake target impact?

I ran into an interesting problem today. I am trying to compile and link a test executable to the Boost unit test framework and I tried it in two different ways.
The classic approach of linking directly to the "boost_unit_test_framework" library using -lboost_unit_test_framework
The modern CMake approach of linking to the Boost::unit_test_framework CMake target.
Interestingly when I link to the library directly my code compiles and links fine; however when I link to the CMake target my code fails to compile before it even gets to the linking stage!
The errors I get are related to a header file that it suddenly can't seem to find anymore. This suggests that linking to the Boost::unit_test_framework somehow messed with my include path.
I know linking to a CMake target is supposed to be the more modern and preferred approach, but if it can have such unexpected and unexplainable side effects, it seems worse than just linking straight to the library...
Why would linking the CMake target cause header files to not be found anymore? Also what other kinds of things can linking to a CMake target instead of linking directly to a library impact?
In both scenarios I am using target_link_libraries to link to the boost library. For example
target_link_libraries(mytest_exe
testlib
-lboost_unit_test_framework
)
or
target_link_libraries(mytest_exe
testlib
Boost::unit_test_framework
)
The fact that it is failing before linking means that the target_link_libraries command in CMake actually effects more than just linking. It is effecting the compilation as well.
Yes, it is true that new include directories are added when you link with a library target instead of the library file. This is why the approach is called "modern" - a single target_link_libraries call does all things which are needed to use the library (Boost in your case).
Reason of failing with "modern" approach could be that "true" Boost headers conflict with other headers you use. You may detect that via inspecting chain of include files in the error message.

Including SO files without headers

I have recently involved myself in making a basic game engine. I have decided to place all of my middleware into an Externals directory, where a bash script builds all my middleware into shared object files in the Binaries directory.
In my CmakeLists.txt file, I am able to link these libraries into my main executable, like so:
add_library (glfw SHARED IMPORTED)
set_target_properties (glfw PROPERTIES IMPORTED_LOCATION
${ENGINE_BINARIES_DIR}/Libraries/Shared/libglfw.so.3.2
)
...
target_link_libraries (Engine glfw)
However, when I try to include GLFW/glfw3.h in my main file, it fails to compile. This makes sense, because there is no glfw3.h file; however, all other attempts at including glfw into my project have failed.
TL ; DR
What is the best way to include headers from an existing shared object library? Is it possible to achieve this without an includes/glfw directory, or would one need to modify their code / project structure in order to load these functions?
I realize my mistake now.
I did a bit of research on shared objects / DLL's and building GLFW before posting this question; however, I believe I was still rather confused as to what a shared library was and how it worked.
I now realize that you do, in fact, need to include the header file; as it contains the declarations of the middleware code, while the libglfw.so.3.2 library contained the definitions of the code.
I have thus solved my issue by keeping the .so file in the Libraries/Shared directory and the GLFW header file in the External directory, and using CMake to point the compiler / linker to both.

CMake: Should changes to libraries linked by "target_link_libraries" be automatically detected?

I am using target_link_libraries to link external libraries to my application. But when one of the linked libraries changes, the make command does not detect and link the changed libraries again. Is there anything I have to include in the CMakeLists.txt file to observe and detect library changes?
CMake doesn't have this feature. Nor does it have a feature to detect changes in system-level header files (in /usr/include). It's probably not a good idea to add such features either, as it would slow down the common case. I suggest you just do a clean build after you have updated your system libraries.