How do I properly link my libraries in my project using CMake? - c++

I'm currently learning CMake and I'm trying to create my first test project. I'm able to get a simple project up and running in visual studio via CMake. However, I'm having trouble trying to add a library. I've read some guides and things but I keep getting errors. Basically, I'm trying to link SDL libraries (a game programming library) in my sample project. I've placed these libraries in a top level, 'ThirdParty' folder. Here is what my CmakeLists.txt file looks like in my top level directory:
cmake_minimum_required(VERSION 2.8.11)
project(Hello)
#Find necessary header files
find_path(SDL_INCLUDE_DIR SDL.h HINTS ${CMAKE_SOURCE_DIR}/ThirdParty/SDL2/include/)
#Find necessary library files
find_library(SDL_LIB_DIR SDL2 HINTS ${CMAKE_SOURCE_DIR}/ThirdParty/SDL2/lib/x86)
find_library(SDLMAIN_LIB_DIR SDLmain HINTS ${CMAKE_SOURCE_DIR}/ThirdParty/SDL2/lib/x86)
#Add/Link files to project
include_directories(${SDL_INCLUDE_DIR})
target_link_libraries(Test PUBLIC ${SDL_LIB_DIR})
target_link_libraries(Test PUBLIC ${SDLMAIN_LIB_DIR})
add_executable(Test "${CMAKE_SOURCE_DIR}/Source/Main.cpp")
I'm not 100 percent sure of the HINTS parameter, but I saw it used on another thread. Anyway, here's the error I keep getting:
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:
SDLMAIN_LIB_DIR
linked by target "Test" in directory C:/Users/Jason/Desktop/Test
What am I doing wrong and how do I properly link libraries in CMake?

In cmake, you first create the executable, and then you link it to a library
You have to understand how finding libraries and packages works in CMake. Typically the way it works is that you use find_library or find_package, and then cmake will set some variables that you can use to link to/use the library.
I'm not familiar with SDL, but by googling a little bit about it, I would say this is how it should look like:
find_file(SDL2_INCLUDE_DIR NAME SDL.h HINTS SDL2)
find_library(SDL2_LIBRARY NAME SDL2)
add_executable(MyExec main.cpp)
target_include_directories(MyExec ${SDL2_INCLUDE_DIR})
target_link_libraries(MyExec ${SDL2_LIBRARY})
That find_library will set the variables SDL2_INCLUDE_DIR and SDL2_LIBRARY, which you can use to link to SDL and add its includes to your project.

Related

How to make a lib before making the program in CMAKE?

I would like to be able to dynamically compile a library that will be used by my project before compiling my project.
I am setting up a Vulkan project in C++ (with Clion) and would like it to be multi-platforms, I am using GLFW3.3 to make that happen.
Instead of building my library for each platform and putting the libs and .h in a folder that will be linked through the CMakeLists.txt, I would like to be able to CMAKE+make the library, then put the lib and .h where they need to be and then start compiling my program that will be using those.
GLFW has a working CMakeLists.txt (I manage to make it manually through the console) but I don't know how to tell CMAKE to make it etc.
I am used to using CMake to define path to libs and includes but my last project what also multi-platforms and I didn't like the way I handled the library (build manually etc).
So I am looking for a way in CMake to do everything at once even if it will take time to do so but I have no idea how that works.
Take a look at how Glitter does it:
option(GLFW_BUILD_DOCS OFF)
option(GLFW_BUILD_EXAMPLES OFF)
option(GLFW_BUILD_TESTS OFF)
add_subdirectory(Glitter/Vendor/glfw)
target_link_libraries(${PROJECT_NAME} ... glfw)
They just include the CMakeLists.txt file that GLFW provides and depend on it for the main target.

CMakeLists C++ Beginner

I've started playing a little bit with C++ and to make it happen I decided to write a simple game engine.
For this purpose, I'm using CLion as my IDE and it works all good but adding libraries is just a nightmare. First I've installed all required libraries like glew, glfw or glm using brew, all went fine. Then I spent almost 2 hours to get it to work on my project.
My biggest mystery is the reason why it works, I've worked with build systems in java, python or golang and everything was always clear to me. However, I have no idea why it works the way it works and I'd love to know!
Here is my CMakeLists file.
cmake_minimum_required(VERSION 3.10)
project(untitled2)
find_package(GLEW REQUIRED)
find_package(GLFW3 REQUIRED)
set(CMAKE_CXX_STANDARD 17)
add_executable(untitled2 main.cpp)
target_link_libraries(untitled2 ${GLEW_LIBRARIES})
target_link_libraries(untitled2 glfw)
Now I have a few questions:
1. Why am I able to use GLM library without including it in the CMakeLists?
2. Why do I need to include glfw and glew but not glm?
3. Why do I need to use ${GLEW_LIBRARIES} and not some name like glew? (I tried different names, but nothing worked.)
btw. I'm using macOS.
The first thing to remember is that C++ doesn't (yet) have a real module system like newer languages. It just has a list of directories that it searches for header files, a list of directories that it searches for libraries, and a list of libraries to search for symbols when linking. The target_link_libraries directive just adds compiler flags that add to those three lists.
Now, on to this specific scenario. Most of the magic happens in the find_package directive. That directive really just ends up running cmake scripts. Sometimes those are packaged with cmake, sometimes they're installed along with the package you're finding. In the end, those scripts can do basically whatever they want. They all have the same objective, to give you a way to add the appropriate compiler flags to use the package, but there are a couple common ways they do that.
The older way is to set variables that you can use to tell the compiler what directories to search for headers and libraries and what libraries to link to. That's the approach GLEW seems to have taken. It sets the variables GLEW_LIBRARIES and GLEW_INCLUDE_DIRS and you then have to use link_libraries and include_directories to tell the compiler what to do. This was the only approach available in older versions of cmake (pre 2.8 IIRC), so while it's not as nice to use, it is still how many libraries' find_package scripts work.
The newer way is to create imported targets. Those targets have appropriate properties set so that any targets that link to the imported target inherit the appropriate include directories and library flags. This is the approach GLFW took. It creates an imported target named glfw that has the INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES properties set. When you pass that to target_link_libraries, your untitled2 target will inherit those include directories and libraries.
Finally, GLM is a header-only library. There are no library files to link to, so as long as the appropriate directory is added to the compilers header search path you'll be able to include and use GLM.
Since you used homebrew to install your libraries, all of their headers are likely under the same base directory; most likely "/usr/local/include". All of their library files are similarly likely under the same directory; probably something "/usr/local/lib". That means that your compiler will be able to find any of their headers and libraries is you tell it to search "/usr/local/include" for headers and "/usr/local/lib" for libraries.
So, to finally answer the question: Thing's work because the glfw target told cmake that it should set the compiler flags to add "/usr/local/include" to its list of include directories. Since that's the same directory it needs to search for GLM and GLEW, the compiler is able to find the headers for all of your libraries. The compiler is also able to find the library files it need to link to because cmake told it to look for them explicitly via the list GLEW_LIBRARIES and the inherited properties from the glfw target. GLM doesn't have any library files to link to, so there's nothing to tell it about.
You really shouldn't rely on everything being in the same place though. You should be able to tell the compiler about everything like this (note that I haven't actually tested this):
cmake_minimum_required(VERSION 3.10)
project(untitled2)
set(CMAKE_CXX_STANDARD 17)
add_executable(untitled2 main.cpp)
# This will fill the variables GLEW_INCLUDE_DIRES and GLEW_LIBRARIES
# that you can use to add the appropriate compiler flags
find_package(GLEW REQUIRED)
# This will create an imported target named glfw that you can link to
# to inherit the appropriate include directories and libraries
find_package(GLFW3 REQUIRED)
# This also creates an imported target named glm that you can "link to"
# to inherit the appropriate include directories
find_package(glm REQUIRED)
# GLEW uses an old-style find_package script, so you have to
# explicitly tell cmake about GLEW's include directories
target_include_directories(untitled2 PUBLIC ${GLEW_INCLUDE_DIRS})
# And the library files to link to
target_link_libraries(untitled ${GLEW_LIBRARIES})
# cmake will automatically add the appropriate include directories
# and library files that the imported glfw target tells it about
target_link_libraries(untitled2 glfw)
# You use the target_link_libraries directive with the glm imported target
# even though you're not actually linking to any libraries. It's just how
# you tell cmake you want your untitled2 target to inherit the appropriate
# include directories from the imported glm target
target_link_libraries(untitled2 glm)

C++ How to run programs in Clion when you need to include OpenGL libraries?

Hello I need to work with OpenGL and want to create my project in Clion. But Clion cannot compile and run my projects because of the libraries I need to include. I can create my own makefile and run the program in terminal, but I want to do it in the IDE. How can I make this happen?
First make sure you installed all libraries correctly using the compiler you configured in clion/cmake. Assuminf you have a fresh CMakeLists.txt like
cmake_minimum_required(VERSION 3.3.2)
project(MyGL CPP)
add_executable(demo-run main.cpp)
For linking your libraries you need two things. First tell the compiler where to find the include files and second which libraries to link. You could just hard code you local installation like
target_link_libraries(demo-run path/to/glfw.lib path/to/opengl.lib path/to/jpeg.lib ...)
target_include_directories(demo-run PRIVATE path/to/glfw/include path/to/opengl/include path/to/jpeg/include ...)
however this is not very portable and if you want to work with another compiler or on another machine your project file will fail. Instead you can use the package system of cmake
find_package(PkgConfig REQUIRED)
pkg_search_module(GLFW REQUIRED glfw3)
find_package(JPEG REQUIRED)
find_package(GLEW REQUIRED)
find_package (OpenGL REQUIRED)
find_package (GLM REQUIRED)
target_link_libraries(demo-run ${GLFW_LIBRARIES} ${GLEW_LIBRARIES} ${JPEG_LIBRARIES} ${OPENGL_LIBRARIES})
target_include_directories(demo-run PRIVATE ${GLFW_INCLUDE_DIRS} ${GLEW_INCLUDE_DIR} ${JPEG_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR} ${GLM_INCLUDE_DIR})
The glfw part is a bit tricky and works only on linux i guess see http://www.glfw.org/docs/3.0/build.html.
This code is not tested at all and you may need to specify some enviroment variables so cmake can find the packages or provide additional find scripts like https://github.com/lighttransport/nanogi/blob/master/cmake/FindGLM.cmake.
I would recommend to use the CMake build tool which does the work generating Makefiles for you and is also directly supported by clion. When you open the directory containing a CMakeLists.txt (CMake Project File) with clion, it should be automatically be loaded and compiled (if not just hit build)
A very simple example CMake project would look like this
cmake_minimum_required (VERSION 2.8.9)
project (OpenGl-Stuff)
include_directories(src)
add_executable(your-binary src/your-code.c src/your-code.h)
target_link_libraries(your-binary opengl)
# target_link_libraries will search for libopengl on standard system paths,
# maybe the library is not called libopengl, then you have to adjust the name above
this cmake project will generate the binary for you and link it against opengl

CMake include 3rd party project

My background is in regular makefiles, I'm trying to convert our project to a CMake setup.
I get stuck when I try to include g3log as a subproject.
I added the following lines in my CMakeLists.txt.
Note, that ../../external/g3log_src is the top level directory of the third party product.
target_link_libraries( myproject LINK_PUBLIC ${Boost_LIBRARIES} g3log)
set(DG3_SHARED_LIB OFF)
include_directories("../../external/g3log_src/include")
add_subdirectory (../../external/g3log_src LIBRARY_OUTPUT_PATH/g3log)
However, it would not actually build g3log and so I cannot link it:
ld: library not found for -lg3log
There is a line
project (g3log)
included in the g3log CMakeLists.txt
I guess I'm missing something fundamental and easy here, but I'm lacking pointers on how to tackle the problem. Any help is appreciated.
When link with the 3d-party library, you should use library name, not a project name. In case of g3log, the library name is g3logger.
Also, building 3d-party project should prepend using it:
add_subdirectory (../../external/g3log_src g3log)
...
target_link_libraries( myproject LINK_PUBLIC ${Boost_LIBRARIES} g3logger)
This is because linking should be performed with target name, which is known to CMake only after processing 3d-party project.
Otherwise, you just pass -lg3logger option to the linker, but the linker cannot find this library because it hasn't built at the moment.

Using Cmake, how do I prevent library sources from being included in my IDE?

Using a bunch of different libraries in my project (from GitHub sources, not precompiled), I add them to my target like this in my root CMakeLists.txt file:
add_subdirectory(lib/glew-1.13.0/build/cmake)
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/lib/glew-1.13.0/include/)
...
target_link_libraries(MyApp glew ${GLEW_LIBRARIES} ... )
However, you can see from the screenshot below that Xcode includes all of the sources for those libraries in my project, which makes an insanely long list that I have to scroll through to find my code.
I have tried the EXCLUDE_FROM_ALL flag in the add_subdirectory command, which removes the library sources from my Xcode project, but then I cannot compile my project because Xcode doesn't compile the library at all.
Additionally, Xcode gives me tons of warnings from the libraries that I don't really care about. Using the SYSTEM flag with the include_directories command doesn't fix it.
What's the best way to solve this? Should I be compiling my libraries as a completely separate part of my build process rather than compiling them with my executable?
I'm not sure how it will work, but try this:
turn on the USE_FOLDERS in your root CMakeLists.txt
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
And then after you've added all the projects, set the FOLDER target property on all of the third party libraries:
set_property(TARGET target1 target2 ...
PROPERTY FOLDER "ThirdPartyLibs")
Being unfamiliar with C++, I thought that all of my libraries should be compiled along with my project every time. I ended up solving this by writing a shell script that precompiles all of my libraries once as static libraries, and now I don't have to worry about their sources in my IDE, plus I get faster compile times.