CMakeLists C++ Beginner - c++

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)

Related

Resolving competing include directories in CMake

I have a C++ project that requires two external libraries (boost and htslib). I link the libraries to my projects target octopus as follows:
find_package (Boost 1.65 REQUIRED COMPONENTS ${REQUIRED_BOOST_LIBRARIES} REQUIRED)
if (Boost_FOUND)
target_include_directories (octopus PRIVATE ${Boost_INCLUDE_DIR})
target_link_libraries (octopus ${Boost_LIBRARIES})
endif (Boost_FOUND)
find_package (HTSlib 1.4 REQUIRED)
if (HTSlib_FOUND)
target_include_directories (octopus PRIVATE ${HTSlib_INCLUDE_DIRS})
target_link_libraries (octopus ${HTSlib_LIBRARIES})
endif (HTSlib_FOUND)
Both boost and htslib are usually installed into /usr/local, and therefore have header files in /usr/local/include. However, users can specify alternative library locations by specifying CMake variables BOOST_ROOT and HTSLIB_ROOT.
The problem is that if only one of the libraries is given an alternative location, then the header files in the include directory of the other linked library (e.g. in /usr/local/include) get included for both libraries, and if incomparable versions of the library are installed then compilation can fail. For example, if I set BOOST_ROOT to ~/.linuxbrew then Boost_INCLUDE_DIR is correctly set to ~/.linuxbrew/include, but HTSlib_INCLUDE_DIRS is /usr/local/include, which contains /usr/local/include/boost, and for reasons I don't quite understand, these are the headers used for building, even though they are incompatible with the libraries in ~/.linuxbrew/lib/boost.
How can I ensure that the include directory for a linked library are used only for that library?
Which header file is considered is a matter of include directories order - the compiler will typically consider the first file that matches the given name in any of the include folders. While you cannot tell the compiler to look for one file in one specific directory and for the other in another specific directory, what you can determine to some degree is the order of the include directories. It sounds like in your case, the boost include directory should be checked before the other, so try the BEFORE keyword in the target_include_directories call, like this:
target_include_directories (octopus BEFORE PRIVATE ${Boost_INCLUDE_DIR})

Link external library CMakeLists.txt on Windows

I'm trying to realize a CMakeLists.txt to compile my project under Linux and Windows. I use two libraries : SFML and boost. However, I want that my project can be built under Linux or Windows without the installation of libraries from a user.
I explain. Libraries SFML and boost are compiled and placed in my folder project and I want that no matter on which computer I download my project, I can build it.
And here is my problem, I can't link externally from the libraries folder in my folder project, the SFML and boost libraries.
Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.0)
project(r-type_client CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
include_directories(./boost/include
./SFML/include
./include
)
if(UNIX)
set(BOOST_LIBRARY
-L./boost/linux
-lboost_regex
)
set(SFML_LIBRARY
-L./SFML/linux
-lsfml-graphics
-lsfml-window
-lsfml-system
)
add_executable(r-type_client
srcs/Main.cpp
)
target_link_libraries(r-type_client
${BOOST_LIBRARY}
${SFML_LIBRARY}
)
elseif(WIN32)
add_library(bar SHARED IMPORTED)
set_property(TARGET bar PROPERTY IMPORTED_LOCATION
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-graphics.dll)
set_property(TARGET bar PROPERTY IMPORTED_IMPLIB
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-graphics.lib)
add_library(bor SHARED IMPORTED)
set_property(TARGET bor PROPERTY IMPORTED_LOCATION
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-window.dll)
set_property(TARGET bor PROPERTY IMPORTED_IMPLIB
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-window.lib)
add_library(bur SHARED IMPORTED)
set_property(TARGET bur PROPERTY IMPORTED_LOCATION
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-system.dll)
set_property(TARGET bur PROPERTY IMPORTED_IMPLIB
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-system.lib)
add_executable(r-type_client srcs/Main.cpp)
target_link_libraries(r-type_client bar bor bur)
endif(UNIX)
As I've mentioned in the comments, you should set up your CMake project in a proper way by using find_package() to properly locate everything.
CMake is a tool to allow people to creation files needed for compilation on the fly no matter the host system. By hardcoding paths and (unneeded) per-platform branches, you're basically negating the whole purpose of CMake.
To include a third party library in CMake, you'll typically want to use find_package(), which includes the necessary logic to locate the library and setup variables properly (it essentially does what you've done by hand so far).
Let's do it for SFML, since it's basically the same for Boost (and I don't really have Boost ready to test everything, because the variable names might differ from project to project).
First, you'll tell CMake that your project wants to use SFML:
find_package(SFML)
Depending on the actual "package", you can extend this. In SFML's case we can even define which minimum version we want and which sub libraries we actually want to use:
find_package(SFML 2.3 COMPONENTS graphics window system REQUIRED)
The REQUIRED flag makes the whole thing mandatory, i.e. CMake will error out, if it's not found.
Once this line succeeded, it will have set a few variables with the proper paths and libraries, which we can then use when defining a target:
include_directories(${SFML_INCLUDE_DIR})
add_executable(myprogram ${MY_SOURCE_FILES})
target_link_libraries(myprogram ${SFML_LIBRARIES} ${SFML_DEPENDENCIES})
And you're done. As you can see, I can setup a CMake project in less than 10 lines without any platform specific paths, code, or knowledge.
However, when running this, you'll most likely run into one error:
CMake by default might not be able to find a FindSFML.cmake file and complain.
You'll find this file in your SFML directory under the cmake path. Copy it to your project and tell CMake where to look for it, for example:
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
Once this is done as well, CMake might not be able to locate the actual SFML files, especially on Windows. To tell it where to look for it, you'd define SFML_ROOT when invoking CMake:
cmake -DSFML_ROOT=C:/Users/Hugo/Downloads/Old/client/SFML path/to/source
This gives you the big advantage that you won't have to hardcode the path to SFML (or any other library such as Boost). On your linux machine, you typically won't even have to provide any path and everything should just work out of the box.

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

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

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.

Convert from Xcode to CMake

I have a C++ project that I initially developed using Xcode on my Mac and I would like to set up the very same project using CMake. The project makes use of some external libraries, including eigen and boost. Xcode has a rather long list of Build Settings, including paths to external libraries, specification of the compiler version, additional linker flags, compiler optimization level, etc... and I am wondering where all of this information goes in the CMakeLists.txt file. I've searched extensively for help on this but have found precious little. I am new to CMake and have never written make files before. If there were a utility that could convert my Xcode project into a CMake project, that would be ideal. But I would be very glad to know of a tutorial on this, or to have some specific guidance. Any help on this would be greatly appreciated.
Conversion Strategy
I am pretty sure that the easiest and fastest way to move to CMake is a mixture of looking at comparable projects that link the same dependencies, and maybe copying a few relative file paths to your source files from XCode.
A well-written CMakeLists.txt that uses Eigen and Boost can be very small (and platform-independent).
One reason for this is CMake has a very different way to use dependencies.
For well-known libraries such as Boost, CMake includes scripts to include headers and link libraries.
Minimal CMakeLists.txt
A minimal example may look like this:
find_package(Eigen3 REQUIRED)
find_package(Boost REQUIRED COMPONENTS system)
add_executable(
app
src/main.cpp
)
target_include_directories(
app
${Eigen3_INCLUDE_DIRS}
${Boost_INCLUDE_DIR}
)
target_link_libraries(
app
${Boost_LIBRARIES}
)
Example Projects
For Eigen, there is no such pre-installed script that can be called by find_package.
Here are two real-world example projects that link Eigen and Boost: https://github.com/ompl/ompl/blob/master/CMakeLists.txt
https://github.com/roboticslibrary/rl
Here is an example for a find script for Eigen.
As a piece of advice, some CMake projects are out there are convoluted and use old syntax, newer project files tend to use small letters in commands.
If it happens that directly target linking libraries doesn't work, you'll have to write your own Find*.cmake files by yourself.
Here's a spec of what you should cover to make it (this is just one approach):
Imagine you have a library XYZ:
Your file
FindXYZ.cmake should have as a result a set of variables to use:
XYZ_LIBRARIES
XYZ_INCLUDE_DIR
For example:
set(XYZ_LIBRARIES ${SOME_PATH_TO_LIBRARY1} ${SOME_PATH_TO_LIBRARY2})
find_path(XYZ_INCLUDE_DIR NAMES xyz/xyz.h HINTS ${PLACE_WHERE_INCLUDE_SHOULD_BE}
PATH_SUFFIXES include
)
From your main cmake maybe you 'use' it:
find_package(XYZ REQUIRED)
# Now we use both _INCLUDE_DIR and _LIBRARIES
include_directories(SYSTEM "${XYZ_INCLUDE_DIR}")
# TARGET_NAME is the current target, the 'client' of XYZ
# append XYZ to our list of libraries to link
target_link_libraries(${TARGET_NAME} ${XYZ_LIBRARIES})
I'm surely missing details but that's the general idea. Here are some cmake scripts that I know work in a big project:
Example with OpenSSL
Find:
https://github.com/highfidelity/hifi/blob/Android/cmake/modules/FindOpenSSL.cmake
Client (in this case is a library that indeed needs OpenSSL):
https://github.com/highfidelity/hifi/blob/Android/domain-server/CMakeLists.txt