Here is a CMakeLists.txt that doesn’t work. I migrated a project from using the WinAPI interface directly to using SDL2. Here is the CMakeLists.txt that results. Except that, for purposes of this discussion, I’m using as source a vastly simplified test program that depends directly and only upon SDL2.
cmake_minimum_required(VERSION 3.28)
project(my-project)
set (CMAKE_CXX_STANDARD 11)
set (TEST TestCase.cpp)
#set (CMAKE_PREFIX_PATH C:\msys64\ucrt64)
#find_package(SDL2 REQUIRED)
#unset(CMAKE_PREFIX_PATH)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
include_directories(. Library Tools Toolkit APClass Library/jpeg ${SDL2_INCLUDE_DIRS})
# include_directories(SYSTEM C:\msys64\mingw64\include)
include(Library/CMakeLists.txt)
include(Tools/CMakeLists.txt)
include(APClass/CMakeLists.txt)
add_executable(TestProgram ${TEST} ${DRAWBOX} ${TOOLS} ${APCLASS})
target_compile_features(TestProgram PUBLIC cxx_std_11)
target_compile_options(TestProgram PRIVATE )
target_link_libraries(TestProgram ${SDL2_LIBRARIES} libSDL2_gfx.a
SDL2_image SDL2_ttf
)
It won’t compile. While building TestCase.cpp it gets the error:
Error C1083 Cannot open include file: 'unistd.h': No such file or directory
The problem became apparent as I experimented with CMAKE_PREFIX_PATH, but that may have no bearing. No amount of cache-clearing helps. A variety of other standard headers such as ’strings.h’ are reported likewise.
I tried adding line 13:
include_directories(SYSTEM C:\sys64\mingw64\include)
but as it turns out, that brings in the headers for a different compiler, and just makes a horrible mess.
Here is a CMakeLists.txt that works, the predecessor to the one that fails. Program compiles and runs.
cmake_minimum_required(VERSION 3.28)
project(my-project)
set (CMAKE_CXX_STANDARD 11)
set (TEST Test/CheckerCheckerBoardBoard.cpp)
include_directories(. Library Tools Toolkit APClass Library/jpeg)
include(Library/CMakeLists.txt)
include(Tools/CMakeLists.txt)
include(APClass/CMakeLists.txt)
message( STATUS DRAWBOX=${DRAWBOX})
add_library(Drawbox STATIC ${DRAWBOX} ${TOOLS} ${APCLASS})
add_executable(TestProgram WIN32 ${TEST} ${DRAWBOX} ${TOOLS} ${APCLASS})
target_compile_features(Drawbox PUBLIC cxx_std_11)
target_compile_options(Drawbox PRIVATE -showIncludes)
target_compile_features(TestProgram PUBLIC cxx_std_11)
target_compile_options(TestProgram PRIVATE
)
target_link_libraries(TestProgram winmm.lib)
I’m struggling to see a difference between the two CMake scripts that could account for this issue.
Where to look for such a problem? I’d like to present a nice detailed log file, but I’m going to need some help to know how to even create it. But here’s a CMake clean-generate log:
1> CMake generation started for configuration: 'x64-Debug'.
1> Command line: "C:Windowssystem32cmd.exe" /c "%SYSTEMROOT%System32chcp.com 65001 >NUL && "C:PROGRAM FILESMICROSOFT VISUAL STUDIO2022COMMUNITYCOMMON7IDECOMMONEXTENSIONSMICROSOFTCMAKECMakebincmake.exe" -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="Debug" -DCMAKE_INSTALL_PREFIX:PATH="C:UserskenDesktopprog24Drawboxishinstallx64-Debug" -DCMAKE_C_COMPILER:FILEPATH="C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/bin/Hostx64/arm64/cl.exe" -DCMAKE_CXX_COMPILER:FILEPATH="C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/bin/Hostx64/arm64/cl.exe" -DCMAKE_MAKE_PROGRAM="C:PROGRAM FILESMICROSOFT VISUAL STUDIO2022COMMUNITYCOMMON7IDECOMMONEXTENSIONSMICROSOFTCMAKENinjaninja.exe" "C:UserskenDesktopprog24Drawboxish" 2>&1"
1> Working directory: C:UserskenDesktopprog24Drawboxishbuildx64-Debug
1> [CMake] -- Configuring done (0.2s)
1> [CMake] -- Generating done (0.0s)
1> [CMake] -- Build files have been written to: C:/Users/ken/Desktop/prog24/Drawboxish/build/x64-Debug
1> Extracted CMake variables.
1> Extracted source files and headers.
1> Extracted code model.
1> Extracted toolchain configurations.
1> Extracted includes paths.
1> CMake generation finished.
Here’s TestCase.cpp.
#include <unistd.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#include <SDL2/SDL_ttf.h>
SDL_Window* gWindow = NULL; //The window we'll be rendering to
SDL_Renderer* gRenderer = NULL; // Renders to an offscreen "texture" (frequent)
static SDL_Texture *target;
SDL_Event event;
TTF_Font *gFont;
void DrawNow();
int points[][2] = {
{50, 50},
{200, 100},
{50, 300},
{400, 400},
{300, 70},
{70, 450}
};
void DrawText(int x, int y, const char* text, SDL_Color fg) {
int width, height;
if (strlen(text) == 0) return;
SDL_Color bgColor = { 0xff, 0xff, 0xff, 0xff};
SDL_Surface* textSurface = TTF_RenderText_Blended(gFont, text, fg);
if( textSurface == NULL )
printf( "Unable to render text surface! SDL_ttf Error: %sn", TTF_GetError() );
else {
//Create texture from surface pixels
SDL_Texture *mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
if( mTexture == NULL ) {
printf( "Unable to create texture from rendered text! SDL Error: %sn", SDL_GetError() );
} else {
//Get image dimensions
width = textSurface->w;
height = textSurface->h;
}
//Get rid of old surface
SDL_FreeSurface( textSurface );
//Render to screen
int descent = TTF_FontDescent(gFont);
//printf("height: %d descent:%dn", height, descent);
SDL_Rect renderQuad = { x, y-height-descent, width, height };
SDL_RenderCopy( gRenderer, mTexture, NULL, &renderQuad);
SDL_DestroyTexture(mTexture);
}
}
void TextTest() {
//Rectangle(30, 30, 140, 80, filled + blue);
SDL_Color c1 = {0xff, 0xff, 0x00, 0x00};
SDL_Color c2 = {0xff, 0x00, 0xff, 0x00};
DrawText(50, 50, "hello", c1);
DrawText(60, 55, "goodbye", c2);
DrawNow();
}
void AutoDraw() { // "User" graphics
SDL_Rect rr = {10, 10, 120, 120};
int outcome = SDL_RenderSetViewport(gRenderer, &rr);
filledCircleColor(gRenderer, 100, 100, 40, 0xff00ff00);
sleep(1);
DrawNow();
}
void DrawNow() {
while (SDL_PollEvent(&event)) {
}
SDL_SetRenderTarget(gRenderer, NULL);
SDL_RenderCopy(gRenderer, target, NULL, NULL);
SDL_RenderPresent(gRenderer);
SDL_SetRenderTarget(gRenderer, target);
}
bool drawbox_inited = false;
int main() {
if (! drawbox_inited) {
//Initialize SDL
if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
printf( "SDL could not initialize! SDL Error: %sn", SDL_GetError() );
return 1;
}
gWindow = SDL_CreateWindow("Drawbox", 0, 0, 1200, 900, 0);
if( gWindow == NULL ) {
printf( "Window could not be created! SDL Error: %sn", SDL_GetError() );
return -1;
}
//Create renderer for window
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
if( gRenderer == NULL )
{
printf( "Renderer could not be created! SDL Error: %sn", SDL_GetError() );
return -1;
}
/* Create texture for display buffering */
target = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 1200, 900);
SDL_SetRenderTarget(gRenderer, target);
//SDL_RenderSetLogicalSize(gRenderer, 2000, 1400);
//Clear buffer
SDL_SetRenderDrawColor( gRenderer, 0, 0, 0, 0xff );
SDL_RenderClear( gRenderer );
// Set up for fonts and text
if (TTF_Init() < 0) {
printf("ttf could not be initialized: %sn", SDL_GetError());
} else {
gFont = TTF_OpenFont( "Cabin-Regular.ttf", 20 );
if( gFont == NULL )
{
printf( "Failed to load default font! SDL_ttf Error: %sn", TTF_GetError() );
}
}
// handle window creation events: A MUST!
SDL_Event event;
while (SDL_PollEvent(&event)) {
// printf("Event: %dn", event.type);
}
}
//ClearClip();
srand(time(NULL));
AutoDraw();
sleep(6);
return 0;
}
2
Answers
The answer turned out quite surprising, and it took an hour with a colleague to understand it: the VC++ environment is so different from any of the MinGW/GCC environments that you have to download an entirely different version of SDL2 for it! Both libraries and headers are different. VC++ is not a POSIX compliant environment, which means in practice that things one might lightly presume to be part of C++ itself, including <unistd.h> and even <strings.h>, are not present.
I had made the mistake of taking an existing folder and installation set up for Visual Studio Code and GCC, and trying to slap a Visual Studio Solution on it.
This issue might not be evident to someone who was accustomed to using a package manager. But no! You wouldn't even use the same package manager! All becomes clearer if you download SDL2 directly from its website. This partial screenshot of their list of download options highlights the one I needed.
This also answers other questions I was struggling with, like why SDL (gcc version) was giving me libraries with the .a extension while CMake (VC++ world) was insisting on looking for libraries with the .lib extension.
‘unistd.h’ is native to POSIX-compliant systems, i’ve heard of a couple equivalents out there, but i think it depends on the functions you’re trying to grab from it, but most of them usually lie in ‘windows.h’
if unistd.h is necessary with no equivalents for your code, the only case it would work is in a POSIX environment, which means anything based in UNIX (a macOS may also be able to do the trick, but keep in mind not every OS is fully compatible with POSIX)