So I am building a tiny "game engine" in C++, using cmake to build it, and I am making it as a static library. In windows it works perfectly, but in linux I need to link X11.
Now this wouldn't be a problem, I can just add add_link_options(-lX11) in CMakeLists.txt, and mylib.a will build fine, but when I go and use my mylib.a, and link it into a C++ program, I also need to link X11 again using the add_link_options command.
I am trying to do something similar to what raylib does, and I also want to do linking and including the library how raylib does it (in raylib you only need to link libraylib.a, and everything else is taken care of), but in my case, every time I want to link mylib.a into a C++ project, I also need to link X11.
If I make mylib to be shared (mylib.so), it works without the need to link X11 in the second project as well, but I would like to have a static version of the library working as well.
Here's my CMakeLists.txt for my library:
cmake_minimum_required(VERSION 3.0.0)
project(ntt VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED TRUE)
add_link_options(-lX11)
# GLFW config (this one I copied from raylib directly)
set(BUILD_SHARED_LIBS OFF)
set(GLFW_BUILD_EXAMPLES OFF)
set(GLFW_BUILD_DOCS OFF)
set(GLFW_BUILD_TESTS OFF)
set(GLFW_INSTALL OFF)
add_subdirectory("${CMAKE_SOURCE_DIR}/src/glfw")
add_compile_definitions(_GLFW_X11)
# Generate source list
file(GLOB SOURCES "${CMAKE_SOURCE_DIR}/src/*.cpp")
list(APPEND SOURCES "${CMAKE_SOURCE_DIR}/src/glad/src/glad.c")
list(APPEND SOURCES $<TARGET_OBJECTS:glfw>)
# Create the library
add_library(ntt ${SOURCES})
set_target_properties(ntt PROPERTIES
PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/include/Engine.hpp"
)
# Add include directories
target_include_directories(
ntt
PUBLIC
"${CMAKE_SOURCE_DIR}/include"
PRIVATE
"${CMAKE_SOURCE_DIR}/src/glad/include"
"${CMAKE_SOURCE_DIR}/src/glfw/include"
)
And here's CMakeLists.txt for a project that uses my library, and doesn't work (unless I add the add_link_options(-lX11)):
cmake_minimum_required(VERSION 3.0.0)
project(TestCpp VERSION 0.1.0)
# I want to not need to have this line here
# add_link_options(-lX11)
add_executable(TestCpp main.cpp)
target_include_directories(TestCpp PUBLIC "${CMAKE_SOURCE_DIR}/include")
target_link_libraries(TestCpp PUBLIC "${CMAKE_SOURCE_DIR}/libntt.a")
When I try to build the second project I get a bunch of errors like this:
/usr/bin/ld: /media/hdd/Development/C-C++/test/libntt.a(x11_monitor.c.o): in function `_glfwPollMonitorsX11': x11_monitor.c:(.text+0x3e8): undefined reference to `XFree' (they all come from places like x11_window, or x11_monitor, and all of them are abount missing references to different X11 functions used by glfw)
I know I must be missing something really easy, but I just don't know what. I am not new to C++, but this is the first time I've made an actual proper library, and I'm also pretty new to cmake (so pls excuse bad practices you might see in my CMakeLists).
I mostly relied on Microsoft's Visual Studio build system until recently, and the only reason I switched to cmake was to be able to port my code to more than one platform(windows) without jumping through the weird hoops that VS makes you jump through when configuring the project, and now I'm starting to like it, but every once in a while I bump into something like this.
So, in short: X11 links properly with -lX11, but I would like to only have the user link my library, and not have to bother with also linking X11 manually.
With raylib you only need to do target_link_libraries(mytarget PUBLIC raylib.a) and that links X11 and opengl and everything else needed, but in my case you also need to add_link_options(-lX11), and I want to not have to do that.
Related
I'd like to import external boost in a c++ cmake project.
I did several tries and finally got the following CMakeLists.txt works:
cmake_minimum_required(VERSION 3.14)
project(my_project
VERSION 0.0.1
LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 11)
include(FetchContent)
FetchContent_Declare(
boost
URL https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.tar.bz2
)
FetchContent_MakeAvailable(boost)
set(Boost_ROOT "${CMAKE_BINARY_DIR}/_deps/boost-src")
set(BOOST_INCLUDEDIR ${Boost_ROOT})
find_package(Boost 1.78)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(
boost_test
tests/boost_test.cpp)
However, I feel a little uncomfortable because set(Boost_ROOT "${CMAKE_BINARY_DIR}/_deps/boost-src") looks hacky -- it has a strong assumption that the fetched content will be stored in _deps folder, which I rather want to put it in a dedicated folder like 3rd_party or external as typical C++ project layout conventions.
I found there're 2 ways for doing that:
Use FetchContent_Declare to download the project. However, I don't know how to execute extra build commands to compile non-header-only libraries.
Use ExternalProject_Add to download and compile the boost project. It looks like an old-fashing way, besides, I don't know how to add boost as part of dependencies to top-level CMakeLists.txt in this way.
The way I adopted is the former one, but I wonder if it's the correct way to include external boost (not system-default installed one) as part of a C++ project, or is there a best practice for doing this?
Any comments, suggestions, answers are welcome. Thanks.
The best way to use Boost in a CMake project is like this:
cmake_minimum_required(VERSION 3.25)
project(boost-example)
find_package(Boost REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE Boost::headers)
target_compile_features(myapp PRIVATE cxx_std_11)
Boost::headers is the library to link to for all of the Boost header-only components. If you need a component that includes a real library, then link to Boost::<component>, too, and be sure to name <component> after the REQUIRED argument to find_package.
Don't mess with FetchContent. Your users will know how to get the necessary dependencies. You can provide a vcpkg.json file or a conanfile.txt to make it easier to use Vcpkg and/or Conan, respectively.
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)
As a starter I would like to point out that I have spent few hours trying to resolve the problem with no success. I wrote a small project using wxWidgets library for C++ in Clion on Mac. Since majority of my classmates have done their projects in VisualStudio on Windows, nobody could help me out. The project must be handed in in a form of an executable which will work on any computer. I suppose that the solution I should be looking for is static compilation which would staticly link all of the libraries to the executable file. I tried adding some flags like the one below to the Cmake:
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread")
but it did not help whatsoever. I have absolutely no knowledge on how CMake works and I would greatly appreciate any help.
The whole cmake file which I currently have:
cmake_minimum_required(VERSION 3.9)
project(Lab3)
set(EXECUTABLE_NAME "Indexer")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra -DWX_PRECOMP")
# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
find_package(wxWidgets COMPONENTS core base REQUIRED)
include(${wxWidgets_USE_FILE})
include_directories(include/ ${wxWidgets_INCLUDE_DIRS})
file(GLOB SOURCES "src/*.cpp")
add_executable(${PROJECT_NAME} main.cpp Source.cpp Source.h)
target_link_libraries(${PROJECT_NAME} ${wxWidgets_LIBRARIES})
EDIT: I guess I didn't specify the problem clearly enough. The program compiles fine- everything works. It's just that whenever I try to copy the executable file (the end application) to a different computer it just doesn't work or shows up as a text file with corrupted characters instead of an application with a GUI etc..
You're not copying the right thing across. What you believe to be the application is probably just some kind of launcher.
Mac GUI applications do not consist of just a single file. Your Indexer app is actually what Apple call a bundle. The Finder presents this to the user as a single entity but it's really a folder.
You can look inside a bundle by right-clicking on it (or Ctrl+click) and selecting Show package contents. If you want to understand a bit more about what's in there, Apple document it here.
A good way to get a bundle from A to B might be to ZIP it. Once you have done that you will find out whether your efforts to statically link it have been successful. What I don't know is where your build system has put it, but maybe this post will help. It will be called Indexer.app, maybe Spotlight can find it.
To build wxWidgets with as few dependencies as possible, you should configure it with not only --disable-shared, but also --disable-sys-libs, to prevent it from using any third party libraries (e.g. libz, libpng, ...) that might be installed on your system.
After building your application using wxWidgets, you should confirm that otool -L YourApp.app/Contents/MacOS/YourApp doesn't show any non-system libraries.
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.
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