I’ve been building a game with SDL2 and CMake lately. On Linux, my setup was simple and life was good. All I had to do was drop this into my CMakeLists.txt
:
include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)
target_link_libraries(MYLIB SDL2)
everything compiled perfectly on Fedora.
Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)
checking for one of the modules 'sdl2'
CMake Error at C:/Program Files (x86)/CMake/share/cmake-3.0/Modules/FindPkgConfig.cmake:425 (message):
None of the required 'sdl2' found
Understanding the Error
What’s going on here is simple but sneaky. The pkg_search_module()
function depends on pkg config (a standalone tool) and .pc files (metadata about libraries).
On Linux, pkg-config is already there, and .pc
files for SDL2 are in standard system directories. On a fresh Windows install.
- No pkg-config.
- No
.pc
files in known locations.
So CMake tries to run pkg-config and fails before it even looks for SDL2.
The code itself isn’t wrong it’s just assuming a Linux style environment.
Two Ways I Fix This
Option A – Keep using pkg_search_module on Windows
If I want to stick with pkg config, I just install it (and SDL2) using MSYS2:
pacman -S mingw-w64-x86_64-pkg-config mingw-w64-x86_64-SDL2
Then in CMake:
set(PKG_CONFIG_EXECUTABLE "C:/msys64/mingw64/bin/pkg-config.exe")
set(ENV{PKG_CONFIG_PATH} "C:/msys64/mingw64/lib/pkgconfig")
include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)
add_library(MYLIB ...)
target_include_directories(MYLIB PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(MYLIB ${SDL2_LIBRARIES})
Pros: minimal changes to my Linux setup.
Cons: drags MSYS2 into my Windows workflow, not super “native” for Visual Studio.
Option B – Switch to SDL2 CMake package
Modern SDL2 ships with its own SDL2Config.cmake
, so I can skip pkg-config entirely:
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(MYLIB PRIVATE SDL2::SDL2 SDL2::SDL2main)
On Windows, I can grab SDL2’s CMake package via:
- vcpkg:
vcpkg install sdl2
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
- Official SDL2 dev zip:
Download from libsdl.org, unzip, and set:
-DSDL2_DIR="C:/path/to/SDL2/cmake"
Pros: works cleanly with Visual Studio, no extra tools.
Cons: needs a proper SDL2 build with the CMake package.
My hybrid CMakeLists.txt
Here’s the cross-platform CMakeLists.txt
I ended up with. It tries the CMake package first (best on Windows) and falls back to pkg-config if that fails:
cmake_minimum_required(VERSION 3.20)
project(ExampleSDL2 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(MYLIB STATIC src/mylib.cpp include/mylib.hpp)
target_include_directories(MYLIB PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(SDL2_FOUND_BY_CONFIG OFF)
find_package(SDL2 CONFIG QUIET)
if(SDL2_FOUND)
set(SDL2_FOUND_BY_CONFIG ON)
target_link_libraries(MYLIB PRIVATE SDL2::SDL2 SDL2::SDL2main)
else()
include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)
target_include_directories(MYLIB PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(MYLIB PRIVATE ${SDL2_LIBRARIES})
endif()
add_executable(ExampleApp src/main.cpp)
target_link_libraries(ExampleApp PRIVATE MYLIB)
if(WIN32 AND SDL2_FOUND_BY_CONFIG)
add_custom_command(TARGET ExampleApp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E cmake_copy_if_different
$<TARGET_FILE:SDL2::SDL2> $<TARGET_FILE_DIR:ExampleApp>)
endif()
Test Main.cpp
#include <SDL.h>
#include <cstdio>
int main(int, char**) {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::printf("SDL_Init failed: %s\n", SDL_GetError());
return 1;
}
SDL_Window* win = SDL_CreateWindow("Hello SDL2",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480, SDL_WINDOW_SHOWN);
if (!win) {
std::printf("SDL_CreateWindow failed: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
SDL_Delay(500);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
Final thought
I learned the hard way that pkg_search_module()
is fantastic if pkg-config is there. On Linux, it’s a no-brainer. On Windows, it’s hit-or-miss unless I set up MSYS2. For maximum portability, I now use SDL2’s CMake package when I can, and only fall back to pkg config when I must. That way, my Linux builds keep working, and my Windows builds stay smooth in Visual Studio.