I am learning how to use ExternalProject to download header only libaries and link to my executable.
My workflow is following:
I download header library Eigen using ExtenalProject:
cmake -DGET_LIBS=ON -DBUILD_SHARED_LIBS=ON -DBUILD_MY_PROJECTS=OFF -G
"Visual Studio 17 2022" -A x64 .. && cmake --build . --config Release
Then I run the same CMakeLists a second time, but this time I disable ExternalProject and compile the executable that links the Eigen:
cmake -DGET_LIBS=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_MY_PROJECTS=ON
-G "Visual Studio 17 2022" -A x64 .. && cmake --build . --config Release
Question
Why I need to use both of these commands since in target_include_directories command I specify the same path as in include_directories?
In the code below I need two commands include_directories and target_include_directories.
I thought that it would be enough to use only target_include_directories, but without include_directories it wont work.
if (BUILD_MY_PROJECTS)
add_executable(my_exe main.cpp)
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
endif ()
My full CMakeLists.txt is following:
project(superbuild LANGUAGES CXX)
cmake_minimum_required(VERSION 3.19)
########################################################################
# EIGEN
########################################################################
SET(GET_LIBS "" CACHE STRING "Set option to download dependencies")
if (GET_LIBS)
message(AUTHOR_WARNING ${GET_LIBS})
ExternalProject_Add(eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/install/eigen #this does nothing...
SOURCE_DIR "${CMAKE_BINARY_DIR}/install/eigen" #install in my local build dir
#SOURCE_DIR ${CMAKE_INSTALL_PREFIX}
BUILD_COMMAND "" #do not build
INSTALL_COMMAND "" #do not install
)
endif ()
###############################################################################
#EXECUTABLES
###############################################################################
if (BUILD_MY_PROJECTS)
add_executable(my_exe main.cpp)
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
endif ()
Tl;dr never use include_directories. Ever. Learn how property visibility works in CMake instead.
include_directories("${CMAKE_BINARY_DIR}/install/eigen/") #add directory of the header-only library without this the next line wort work
target_include_directories(my_exe INTERFACE "${CMAKE_BINARY_DIR}/install/eigen/")# link exe to exectable
In the code you supplied, include_directories is implicitly setting the INCLUDE_DIRECTORIES property on your my_exe target. Then the second line sets the INTERFACE_INCLUDE_DIRECTORIES property.
INCLUDE_DIRECTORIES is a list of include directories that must be passed to the compiler when building the target. INTERFACE_INCLUDE_DIRECTORIES is a list of include directories that must be passed to the compiler when building targets that link to this one. This doesn't make much sense for an executable
Slightly better would be to write:
target_include_directories(my_exe PRIVATE "${CMAKE_BINARY_DIR}/install/eigen/")
This will just populate INCLUDE_DIRECTORIES without touching the INTERFACE_ version. If you wanted both you could write PUBLIC instead of PRIVATE or INTERFACE. By definition, PUBLIC is just both of the others.
But this isn't a great dependency management strategy anyway... digging into the source tree guts of a project isn't scalable. It's also difficult to try different versions of Eigen without editing your build files.
I would just write:
cmake_minimum_required(VERSION 3.23)
project(example)
find_package(Eigen3 REQUIRED)
add_executable(my_exe main.cpp)
target_link_libraries(my_exe PRIVATE Eigen3::Eigen)
Use a proper package manager like vcpkg or Conan to handle downloading Eigen when it isn't available on the system.
Before saying anything: Yes, I googled and searched for a long time before asking this here so 99% the solution of other similar questions doesn't work for me.
I have an opengl application that uses the following libraries:
glfw
imgui
glad
stb
assimp
glm
filebrowser (an extension of imgui for dialog boxes)
all these libraries are in a folder called external each one in its dir.
Moreover, glfw, assimp, glm are git submodules of my repo so they're downloaded when i clone my repo with git clone --recursive mygitrepo.
You can have a better understanding of the structure of the project looking at my git repo.
My idea was to use the add_subdirectories to make them compile with their own CMakeLists and my main one looks like this:
cmake_minimum_required(VERSION 3.21.3)
project(Reskinner)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_VERBOSE_MAKEFILE ON)
add_executable(${PROJECT_NAME} Main.cpp)
add_subdirectory(external/glfw)
add_subdirectory(external/assimp)
add_subdirectory(external/glad)
add_subdirectory(external/imgui)
add_subdirectory(external/stb)
add_subdirectory(external/glm)
add_subdirectory(external/FileBrowser)
add_subdirectory(src)
find_package(OpenGL REQUIRED)
target_include_directories(${PROJECT_NAME}
PUBLIC external/glfw/include
PUBLIC external/assimp/include
PUBLIC external/glm
PUBLIC external
PUBLIC src
)
target_link_libraries(${PROJECT_NAME} glfw glad imgui stb glm Engine assimp FileBrowser)
The project was developed with visual studio on windows and I want to compile it on linux without using visualstudio. The cmake doesn't give me any errors but when i run the make command it gives me this error:
#include <assimp/quaternion.h> there's no file or directory with this name.
like i didn't put the assimp include folder in the cmake (but i did).
Do you have any idea how to make it works? I want to make a bash script to install all the dependencies and tools needed like xorg-dev, build-essential, git, cmake, make etc. So if there's any simple fix using some bash commands it's ok for me.
I figured out how to fix it. I just installed conan and used it to download and find assimp. So I removed assimp, glm, glfw and glad from 'external' folder and just added a conanfile.txt.
Here the conanfile.txt:
[requires]
assimp/5.2.2
glfw/3.3.7
glm/0.9.9.8
glad/0.1.35
[generators]
cmake_find_package
cmake_paths
my main CMakeLists.txt now it's as following:
cmake_minimum_required(VERSION 3.21.3)
project(Reskinner)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_VERBOSE_MAKEFILE ON)
add_executable(${PROJECT_NAME} Main.cpp)
include(${CMAKE_BINARY_DIR}/conan_paths.cmake)
# Retrieve conan.cmake
if (NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/master/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake")
endif ()
include("${CMAKE_BINARY_DIR}/conan.cmake")
conan_cmake_run(CONANFILE "conanfile.txt" BASIC_SETUP UPDATE BUILD missing)
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
find_package(OpenGL REQUIRED)
find_package(glad REQUIRED)
find_package(glfw3 REQUIRED)
find_package(glm REQUIRED)
find_package(assimp REQUIRED)
add_subdirectory(external/stb)
add_subdirectory(external/imgui)
add_subdirectory(external/FileBrowser)
add_subdirectory(src)
file(MAKE_DIRECTORY
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Shaders")
function(install_file_to_bin file_name)
file(INSTALL "${CMAKE_SOURCE_DIR}/Shaders/${file_name}" DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Shaders")
endfunction()
install_file_to_bin(1.model_loading.fs)
install_file_to_bin(1.model_loading.vs)
install_file_to_bin(4.1.fshader.fs)
install_file_to_bin(4.1.vshader.vs)
install_file_to_bin(animated_model_loading.fs)
install_file_to_bin(animated_model_loading.gs)
install_file_to_bin(animated_model_loading.vs)
install_file_to_bin(bb.vs)
install_file_to_bin(bb.fs)
install_file_to_bin(default.frag)
install_file_to_bin(default.fs)
install_file_to_bin(default.geom)
install_file_to_bin(default.vert)
install_file_to_bin(default.vs)
install_file_to_bin(floor.fs)
install_file_to_bin(floor.vs)
install_file_to_bin(framebuffer.frag)
install_file_to_bin(framebuffer.vert)
install_file_to_bin(grey_model.fs)
install_file_to_bin(grey_model.vs)
install_file_to_bin(hover.fs)
install_file_to_bin(hover.vs)
install_file_to_bin(influence_of_single_bone.fs)
install_file_to_bin(influence_of_single_bone.vs)
install_file_to_bin(mouse_shader.fs)
install_file_to_bin(mouse_shader.vs)
install_file_to_bin(normal_visualizer.fs)
install_file_to_bin(normal_visualizer.gs)
install_file_to_bin(normal_visualizer.vs)
install_file_to_bin(no_lighting_shader.fs)
install_file_to_bin(no_lighting_shader.vs)
install_file_to_bin(num_bones_visualization.fs)
install_file_to_bin(num_bones_visualization.vs)
install_file_to_bin(screen_shader.fs)
install_file_to_bin(screen_shader.vs)
install_file_to_bin(selected.fs)
install_file_to_bin(selected.vs)
install_file_to_bin(smooth_lighting_shader.fs)
install_file_to_bin(smooth_lighting_shader.vs)
install_file_to_bin(wireframe.vs)
install_file_to_bin(wireframe.fs)
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE 1)
set(CMAKE_LINK_WHAT_YOU_USE 1)
set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
include_directories(
${OpenGL_INCLUDE_DIRS}
${glad_INCLUDE_DIRS}
${glfw3_INCLUDE_DIRS}
${glm_INCLUDE_DIRS}
${assimp_INCLUDE_DIRS}
external
src
)
target_link_libraries(${PROJECT_NAME}
${OpenGL_LIBRARIES}
${glad_LIBRARIES}
${glfw3_LIBRARIES}
${glm_LIBRARIES}
stb
${assimp_LIBRARIES}
imgui
FileBrowser
Engine
)
Some notes that can be usefull for other people having this troubles:
Assimp has some linking problem when used with conan because it seems that it doesn't download everything but just what you need so it works for the #include but it complains in the linking phase. Adding the following 3 rows fixed the problem:
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE 1)
set(CMAKE_LINK_WHAT_YOU_USE 1)
set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
I have a script to install everything you need on linux and install the stuff with conan, launch the cmake etc.. Even if I install the conan packages with the command conan install .. [...] I need the conan.cmake file so the lines at the beginning looking for the conan.cmake files fixed a lot of problems
The shell scripts is the following:
#!/bin/bash
sudo apt-get update
sudo apt-get install git
sudo apt-get install xorg-dev
sudo apt-get install make
sudo apt-get install cmake
sudo apt-get install build-essential
cd build
rm -rf *
conan install .. --settings os="Linux" --settings compiler="gcc" --settings compiler.version=11 --build missing
cmake -S .. -B . -D CMAKE_BUILD_TYPE=Release
make
cd build
Hope this can help you
Coming from other package managers I expect to be able to do the following on a project:
configure the required libraries (in a config file, e.g. CMakelists.txt or nuget's packages.config)
install/upgrade all the required libraries
This seems to not be possible with vcpkg and CMake:
I managed to set up vcpkg and CMakefile (linux):
# in ternimal
vcpkg install <package>
# CMakeLists.txt
find_package(nlohmann_json CONFIG REQUIRED)
target_link_libraries(<app> <package>)
This however requires to manually install all the required libraries (vcpkg install <package>).
How can I list the required packages and have vcpkg or CMake managed them as required (e.g. automatically install them if they are missing)?
I've recently spent some time working with CMake and vcpkg. I dislike the typical approach of managing vcpkg and its installed packages separately from my CMake projects, so I wrote some CMake scripts to handle vcpkg during the CMake build process.
The CMake script to install vcpkg is primarily a call to ExternalProject_Add.
# ./vcpkg_utilities.cmake (part 1)
include(ExternalProject)
function(get_vcpkg)
ExternalProject_Add(vcpkg
GIT_REPOSITORY https://github.com/microsoft/vcpkg.git
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
UPDATE_COMMAND ""
BUILD_COMMAND "<SOURCE_DIR>/bootstrap-vcpkg.sh"
)
ExternalProject_Get_Property(vcpkg SOURCE_DIR)
set(VCPKG_DIR ${SOURCE_DIR} PARENT_SCOPE)
set(VCPKG_DEPENDENCIES "vcpkg" PARENT_SCOPE)
endfunction()
The script to have vcpkg install a package requires a custom command and target.
# ./vcpkg_utilities.cmake (part 2)
function(vcpkg_install PACKAGE_NAME)
add_custom_command(
OUTPUT ${VCPKG_DIR}/packages/${PACKAGE_NAME}_x64-linux/BUILD_INFO
COMMAND ${VCPKG_DIR}/vcpkg install ${PACKAGE_NAME}:x64-linux
WORKING_DIRECTORY ${VCPKG_DIR}
DEPENDS vcpkg
)
add_custom_target(get${PACKAGE_NAME}
ALL
DEPENDS ${VCPKG_DIR}/packages/${PACKAGE_NAME}_x64-linux/BUILD_INFO
)
list(APPEND VCPKG_DEPENDENCIES "get${PACKAGE_NAME}")
set(VCPKG_DEPENDENCIES ${VCPKG_DEPENDENCIES} PARENT_SCOPE)
endfunction()
Getting the main project linked to vcpkg appropriately (via toolchain file) requires vcpkg to be available when the main project is configured, so the main project is configured as an ExternalProject.
# /CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
project(top LANGUAGES CXX)
include(vcpkg_utilities)
get_vcpkg()
vcpkg_install(nlohmann_json)
include(ExternalProject)
set_property(DIRECTORY PROPERTY EP_BASE main_projectname)
ExternalProject_Add(main_projectname
DEPENDS ${VCPKG_DEPENDENCIES}
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/src
INSTALL_COMMAND ""
CMAKE_ARGS --no-warn-unused-cli;
-DCMAKE_TOOLCHAIN_FILE=${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake
)
Using this structure, CMake will manage the installation of both vcpkg and packages maintained by it.
Note that this is a point solution. I have a more generalized template for this in my Github that covers Windows and Linux, and allows the use of a separately-maintained vcpkg instance.
I've done something similar in my projects using ExternalProject_Add.
Code will be similar to the below pattern (I've replaced my dependency with your)
find_package(nlohmann_json CONFIG) # make package not REQUIRED to avoid error...
if(NOT NLOHMANN_JSON_FOUND) # or whatever variable indicating _FOUND
...
# here ExternalProject_Add from https://github.com/nlohmann/json.git
# using specific tag...
...
ExternalProject_Get_Property(... INSTALL_DIR)
# create imported target here similar to nlohmann_json, as if we found it
...
else()
# We've found lib already installed... will use it...
...
endif()
Another option can be using FetchContent, but I haven't tried it yet.
I'm trying to use CLion to create a SDL2 project.
The problem is that the SDL headers can't be found when using #include's.
My CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8.4)
project(ChickenShooter)
set(SDL2_INCLUDE_DIR C:/SDL/SDL2-2.0.3/include)
set(SDL2_LIBRARY C:/SDL/SDL2-2.0.3/lib/x64)
include_directories(${SDL2_INCLUDE_DIR})
set(SOURCE_FILES main.cpp)
add_executable(ChickenShooter ${SOURCE_FILES})
target_link_libraries(ChickenShooter ${SDL2_LIBRARY})
My test main.cpp:
#include <iostream>
#include "SDL.h" /* This one can't be found */
int main(){
if (SDL_Init(SDL_INIT_VIDEO) != 0){
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
SDL_Quit();
return 0;
}
Thank you for any help you could give me.
Edit:
I'm using Windows and CLion is configured to use cygwin64.
This blog post shows how you can do it: Using SDL2 with CMake
On Linux you can use a recent CMake (e.g. version 3.7) and using SDL2 works out of the box.
cmake_minimum_required(VERSION 3.7)
project(SDL2Test)
find_package(SDL2 REQUIRED)
include_directories(SDL2Test ${SDL2_INCLUDE_DIRS})
add_executable(SDL2Test Main.cpp)
target_link_libraries(SDL2Test ${SDL2_LIBRARIES})
Under Windows you can download the SDL2 development package, extract it somewhere and then create a sdl-config.cmake file in the extracted location with the following content:
set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include")
# Support both 32 and 64 bit builds
if (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2main.lib")
else ()
set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2main.lib")
endif ()
string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES)
When you now configure inside the CMake-GUI application there will be a SDL2_DIR variable. You have to point it to the SDL2 directory where you extracted the dev package and reconfigure then everything should work.
You can then include SDL2 headers by just writing #include "SDL.h".
Don't set the path to SDL2 by hand. Use the proper find command which uses FindSDL. Should look like:
find_file(SDL2_INCLUDE_DIR NAME SDL.h HINTS SDL2)
find_library(SDL2_LIBRARY NAME SDL2)
add_executable(ChickenShooter main.cpp)
target_include_directories(ChickenShooter ${SDL2_INCLUDE_DIR})
target_link_libraries(ChickenShooter ${SDL2_LIBRARY})
If SDL2 is not found, you have to add the path to SDL2 to CMAKE_PREFIX_PATH, that's the place where CMake looks for installed software.
If you can use Pkg-config, its use might be easier, see How to use SDL2 and SDL_image with cmake
If you feel more comfortable to use a FindSDL2.cmake file similar to FindSDL.cmake provided by CMake, see https://brendanwhitfield.wordpress.com/2015/02/26/using-cmake-with-sdl2/
You can also pull in the SDL source repository as a submodule and build/link it statically along with your main program via add_subdirectory() and target_link_libraries():
cmake_minimum_required( VERSION 3.18.0 )
project( sdl2-demo )
set( SDL_STATIC ON CACHE BOOL "" FORCE )
set( SDL_SHARED OFF CACHE BOOL "" FORCE )
# 'external/sdl' should point at a SDL
# repo clone or extracted release tarball
add_subdirectory( external/sdl )
add_executable(
${CMAKE_PROJECT_NAME}
"src/main.cpp"
)
target_link_libraries( ${CMAKE_PROJECT_NAME} SDL2main SDL2-static )
(At least as of the release-2.0.9 tag, possibly earlier.)
I recently discovered the latest version of SDL2 (version 2.0.12) now comes with all the required CMake config/install scripts, so there's no need to use FindSDL anymore.
I downloaded the SDL source from https://www.libsdl.org/download-2.0.php then from the root folder ran...
cmake -S . -B build/debug -G Ninja -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_BUILD_TYPE=Debug
cmake --build build/debug --target install
This will build and install the debug version of the library, you can then also run...
cmake -S . -B build/release -G Ninja -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_BUILD_TYPE=Release
cmake --build build/release --target install
Which will build and install the release version of the library (and because the SDL CMake script uses DEBUG_POSTFIX the release version of the library won't overwrite the debug one as the debug versions all have 'd' appended to their name).
In your CMakeLists.txt file you can then simply do this:
find_package(SDL2 REQUIRED)
add_executable(${PROJECT_NAME} ...)
target_link_libraries(
${PROJECT_NAME} PRIVATE
SDL2::SDL2
SDL2::SDL2main
You'll need to tell your application where to find the SDL install folder if you used a custom location as I've done in the example. To do this from the root folder of your app run:
cmake -S . -B build/debug -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=</absolute/path/to/install/dir>
cmake --build build/debug
Note: You can use $(pwd) (*nix/macOS) or %cd% (Windows) to create a hybrid relative path which can be very useful.
You can omit both DCMAKE_INSTALL_PREFIX and DCMAKE_PREFIX_PATH if you want to install SDL to the default system location.
In the examples I've opted to use the Ninja generator as it is consistent across macOS/Windows - it can be used with MSVC/Visual Studio, just make sure you run this (path may differ slightly depending on year/version) to add Ninja to your path.
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat
Update:
One other thing I remembered which is useful on Windows is the ability to copy the SDL .dll file into the application binary directory, this can be achieved like so:
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:SDL2::SDL2>
$<TARGET_FILE_DIR:${PROJECT_NAME}>
VERBATIM)
endif()
Using the SDL2 CMake module that I developed, you can integrate the SDL2 library easily in a modern and portable approach.
You should just copy the module in cmake/sdl2 (Or just clone the modules repo) in your project:
git clone https://github.com/aminosbh/sdl2-cmake-modules cmake/sdl2
Then add the following lines in your CMakeLists.txt:
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2)
find_package(SDL2 REQUIRED)
target_link_libraries(${PROJECT_NAME} SDL2::Main)
Note: If CMake didn't find the SDL2 library (in Windows), we can specify the CMake option SDL2_PATH as follows:
cmake .. -DSDL2_PATH="/path/to/sdl2"
For more details, please read the README.md file.
The SDL2 CMake modules support other related libraries : SDL2_image, SDL2_ttf, SDL2_mixer, SDL2_net and SDL2_gfx.
You can find a list of examples/samples and projects that uses these modules here : https://github.com/aminosbh/sdl-samples-and-projects
With the compiled version of SDL2-2.0.9 with MinGW-w64 in Windows, the following configuration works for me:
find_package(SDL2 REQUIRED)
add_executable(sdl-test ${SOURCES})
target_link_libraries(sdl-test
mingw32
SDL2::SDL2main
SDL2::SDL2
)
A longer explanation
By reading SDL2Targets.cmake file, I've learned that SDL2 is providing several targets:
SDL2::SDL2main (lib/libSDL2main.a)
SDL2::SDL2 (lib/libSDL2.dll.a)
SDL2::SDL2-static (lib/libSDL2-static.a)
Each of them has INTERFACE_INCLUDE_DIRECTORIES defined, which means we don't need to manually specify include_directories for SDL2.
But by only adding SDL2::SDL2main and SDL2::SDL2 as target_link_libraries is not enough. The g++ compiler might be complaining about "undefined reference to `WinMain'".
By inspecting the compiler options, I found that the SDL2 libraries are added before -lmingw32 option. In order to make the -lmingw32 option comes before SDL2 libraries, we have to also specify mingw32 as the first target_link_libraries. Which will make this configuration working.
The command that I have used for building it is:
$ mkdir build && cd build && cmake .. -G"MinGW Makefiles" && cmake --build .
The only small problem here is in the finally generated compiler options, the -lmingw32 option is duplicated. But since it doesn't affect the linking process, I've ignored it for now.
On Linux, in Clion, this works:
cmake_minimum_required(VERSION 3.20)
project(first_game)
set(CMAKE_CXX_STANDARD 14)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES})
You don't seems to have a CMake error whike generating your make file. But I think your problem is, the SDL Header are located in a subfolder named "SDL2".
Change your CMakeLists.txt to include
C:/SDL/SDL2-2.0.3/include/SDL2
Instead of
C:/SDL/SDL2-2.0.3/include
I had the same problem and none of the other solutions worked.
But I finally got it working by following this solution : How to properly link libraries with cmake?
In a nutshell, the problem was that the SDL2 library was not linked properly in my CMakeLists.txt. And by writing this into the file, it worked (more explainations in the other thread) :
project (MyProgramExecBlaBla) #not sure whether this should be the same name of the executable, but I always see that "convention"
cmake_minimum_required(VERSION 2.8)
ADD_LIBRARY(LibsModule
file1.cpp
file2.cpp
)
target_link_libraries(LibsModule -lpthread)
target_link_libraries(LibsModule liblapack.a)
target_link_libraries(LibsModule -L/home/user/libs/somelibpath/)
ADD_EXECUTABLE(MyProgramExecBlaBla main.cpp)
target_link_libraries(MyProgramExecBlaBla LibsModule)
Highlighting the steps of how I was able to eventually accomplish this using the FindSDL2.cmake module:
Download SDL2-devel-2.0.9-VC.zip (or whatever version is out after this answer is posted) under the Development Libraries section of the downloads page.
Extract the zip folder and you should see a folder similar to "SDL2-2.0.9". Paste this folder in your C:\Program Files(x86)\ directory.
Copy the FindSDL2.cmake module and place it in a new "cmake" directory within your project. I found a FindSDL2.cmake file in the answer referenced in the Accepted Answer: https://brendanwhitfield.wordpress.com/2015/02/26/using-cmake-with-sdl2/
Find the SET(SDL2_SEARCH_PATHS line in the FindSDL2.cmake and add your copied development directory for SDL2 as a new line: "/Program Files (x86)/SDL2-2.0.9" # Windows
Within my CMakeLists.txt, add this line: set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
After this, running CMake worked for me. I'm including the rest of my CMakeLists just in case it further clarifies anything I may have left out:
cmake_minimum_required(VERSION 2.8.4)
project(Test_Project)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# includes cmake/FindSDL2.cmake
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
set(SOURCE_FILES src/main.cpp src/test.cpp)
add_executable(test ${SOURCE_FILES})
# The two lines below have been removed to run on my Windows machine
#INCLUDE(FindPkgConfig)
#PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2)
find_package(SDL2 REQUIRED)
INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(chip8 ${SDL2_LIBRARY})
Hope this helps somebody in the near future.
by the time of my answer, SDL2 is provided with sdl2-config executable (as I understand, developers call him "experimental").
After "make install" of SDL2 you can try calling it from terminal with
sdl2-config --cflags --libs to see what it outputs.
And then you can add call to it in your makefile:
set(PROJECT_NAME SomeProject)
project(${PROJECT_NAME})
execute_process(COMMAND /usr/local/bin/sdl2-config --libs RESULT_VARIABLE CMD_RES OUTPUT_VARIABLE SDL2_CFLAGS_LIBS ERROR_VARIABLE ERR_VAR OUTPUT_STRIP_TRAILING_WHITESPACE)
message("SDL2_CFLAGS_LIBS=${SDL2_CFLAGS_LIBS}; CMD_RES=${CMD_RES}; ERR_VAR=${ERR_VAR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${SDL2_CFLAGS_LIBS}")
set(SOURCE_FILES main.cpp)
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
Here I have a problem - if I only put an executable name without path like
execute_process(COMMAND sdl2-config --libs <...>
I get error "No such file", i.e. cmake does not search in current path and I don't know how to write it properly by now.
One more notice: in my makefile I do not user --cflags option, because cmake finds includes correctly and I do not need to specify them explicitly.
For your information, I was able to successfully cmake and compile SDL2_ttf while linking to SDL2 source code.
At first I was getting errors due to cmake not being able to locate SDL2, even though it was specified in cmake using the SLD2_DIR variable in cmake.
It seems that for some reason cmaking SDL2 fails to create the SDL2Targets.cmake file which is searched for by SDL2_ttf
If this is the case for you, get the SDL2Targets.cmake file from https://bugs.archlinux.org/task/57972 and modify the file like so:
You can remove the following lines:
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
if(_IMPORT_PREFIX STREQUAL "/")
set(_IMPORT_PREFIX "")
endif()
and add this one:
set(_IMPORT_PREFIX "C:/SDL2-2.0.12")
Obviously change the filepath to the place you unpacked the SDL2 source code
I'm not sure if this is exactly your issue, but there it is.
I just installed the Qt 5.1.1 for Windows 32-bit (MinGW 4.8, OpenGL) and tried to add it to my cmake. But CMake just does not want to find it and i dont know why. What am i missing? Do i need to set an environment variable or something?
Here is my cmake :
cmake_minimum_required( VERSION 2.8.11 )
PROJECT(Blemmer)
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
# Detect and add SFML
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules" ${CMAKE_MODULE_PATH})
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
find_package(SFML REQUIRED system window graphics network audio)
# The QUIET option disables messages if the package cannot be found.
FIND_PACKAGE(Qt5Widgets)
add_subdirectory(Entity)
add_subdirectory(Engine)
add_subdirectory(Game)
add_executable(Blemmer main.cpp)
include_directories(${SFML_INCLUDE_DIR} ${PROJECT_SOURCE_DIR})
target_link_libraries(Blemmer ${SFML_LIBRARIES} Game Engine Qt5::Widgets)
and this is the output of cmake-gui :
CMake Warning at CMakeLists.txt:14 (FIND_PACKAGE):
By not providing "FindQt5Widgets.cmake" in CMAKE_MODULE_PATH this project
has asked CMake to find a package configuration file provided by
"Qt5Widgets", but CMake did not find one.
Could not find a package configuration file provided by "Qt5Widgets" with
any of the following names:
Qt5WidgetsConfig.cmake
qt5widgets-config.cmake
Add the installation prefix of "Qt5Widgets" to CMAKE_PREFIX_PATH or set
"Qt5Widgets_DIR" to a directory containing one of the above files. If
"Qt5Widgets" provides a separate development package or SDK, be sure it has
been installed.
I successly build my GUI on MacOSX 10.10 by exporting Linux environment variables
$ brew install qt5
$ ls /usr/local/opt/qt5/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
$ /usr/local/opt/qt5/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
$ export CMAKE_PREFIX_PATH=/usr/local/opt/qt5/
$ cd ./build
$ cmake ../CMakeLists.txt
$ make -j8
Acroding to http://www.cmake.org/cmake/help/v3.0/variable/CMAKE_PREFIX_PATH.html, the cmake function FIND_LIBRARY() will appends /lib to each of the directories. So done.
You need to set the CMAKE_PREFIX_PATH to the Qt installation.
See http://doc.qt.io/qt-5/cmake-manual.html
Fixed it with the following in a CMakeLists.txt file:
set(CMAKE_PREFIX_PATH $ENV{HOME}/Qt5.5.0/5.5/gcc_64)
For me, the context ended up something like:
# Ubuntu 14.04 LTS, CMake 2.8.12.2
wget http://download.qt.io/official_releases/qt/5.5/5.5.0/qt-opensource-linux-x64-5.5.0.run
chmod u+x qt-opensource-linux-x64-5.5.0.run
./qt-opensource-linux-x64-5.5.0.run
# Follow GUI prompts, installed to default location