Appendix B

For Mac and Linux Users

This book builds everything with Visual Studio, on Windows, because it's the smoothest start there is for C++ games: one installer, and a debugger that's hard to beat. But nothing in the games themselves belongs to Windows. SDL runs on macOS and Linux just as happily, and every project in the book builds and runs on both without a single change to its code.

What changes is the tools around the code. Instead of Visual Studio, you'll write and debug in VS Code, a free editor from Microsoft that runs everywhere, and instead of Chapter 1's project settings, a tool called CMake builds the projects, and fetches SDL for you. This appendix takes you from a bare machine to running the book's games, and your own, and then goes through the places where the book's Windows steps work differently for you.

In this appendix, we will:

  • Install a compiler, CMake, and VS Code on a Mac, or on Linux
  • Build every project in the book's repository with one CMakeLists.txt file
  • Run and debug a project from VS Code
  • Start a project of your own, alongside the book's
  • Translate the book's Visual Studio steps, and its error messages, into their Mac and Linux equivalents

How It Fits Together

On Windows, each project in the book is a Visual Studio project, and Chapter 1 tells it where SDL is, one setting at a time. Visual Studio doesn't run on a Mac or on Linux, so the book's repository has one file that does that job there, for every project at once: SDL3 Projects/CMakeLists.txt.

CMake reads that file and works out how to build each program with whatever compiler your system has: Clang, on a Mac, or GCC, on Linux. It also checks whether SDL 3, SDL_image, and SDL_ttf are installed, and if they aren't, it downloads their source code and builds them, once, the first time. After that, VS Code's CMake Tools extension puts the whole thing behind a few buttons: build, run, and debug.

So the plan is short. Install the tools, download the book's repository, open its SDL3 Projects folder in VS Code, and let CMake do the rest.

Setting Up a Mac

You need Apple's command-line developer tools, which include the Clang compiler and the LLDB debugger; Homebrew, which installs everything else; and VS Code.

  1. Open Terminal, from Applications > Utilities, and type this, then press Return:
xcode-select --install

A window asks whether to install the command line developer tools. Click Install. The tools are a few hundred megabytes, so they can take a while to arrive. When it's done, check it worked:

clang --version

The answer should start with "Apple clang version", followed by a number.

  1. Go to https://brew.sh, and copy the one-line install command shown on the page into Terminal. It explains what it's about to do, and asks for your password. When it finishes, it may print two or three commands under "Next steps", to add Homebrew to your path; run those too.

  2. Install CMake, and FreeType, which SDL_ttf uses to read fonts:

brew install cmake freetype

Then check CMake's version, which needs to be 3.24 or later:

cmake --version
  1. Download VS Code from https://code.visualstudio.com, open the download, and drag Visual Studio Code into your Applications folder. Start it, click the Extensions icon on the left, the one made of four squares, search for C/C++ Extension Pack, and install the one published by Microsoft. It brings the two extensions we need: C/C++, for editing and debugging, and CMake Tools, for building.
Tip

If you'd rather not wait for SDL to build the first time, Homebrew can install it ready-made, with brew install sdl3 sdl3_image sdl3_ttf. The CMakeLists.txt uses those whenever they're recent enough, and downloads its own copies when they aren't, so either way works.

That's the Mac ready. Skip ahead to Getting the Projects.

Setting Up Linux

On Linux, the package manager installs everything: the compiler, the debugger, CMake, FreeType, and the development files SDL needs to open windows and play sound on your system. The commands here are for Ubuntu and Debian, and for Fedora. SDL lists the packages for other distributions, such as Arch and openSUSE, at https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md.

  1. Open a terminal, and install the tools and libraries. On Ubuntu or Debian, that's this one long command, which you can paste in as it is:
sudo apt install build-essential gdb git cmake ninja-build pkg-config \
    libfreetype-dev libasound2-dev libpulse-dev libaudio-dev libjack-dev \
    libsndio-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev \
    libxfixes-dev libxi-dev libxss-dev libxtst-dev libxkbcommon-dev \
    libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
    libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev \
    libfribidi-dev libthai-dev libpipewire-0.3-dev libwayland-dev \
    libdecor-0-dev liburing-dev

On Fedora, it's this one:

sudo dnf install gcc-c++ gdb git-core make cmake ninja-build freetype-devel \
    alsa-lib-devel fribidi-devel pulseaudio-libs-devel pipewire-devel \
    libX11-devel libXext-devel libXrandr-devel libXcursor-devel \
    libXfixes-devel libXi-devel libXScrnSaver-devel libXtst-devel \
    dbus-devel ibus-devel systemd-devel mesa-libGL-devel libxkbcommon-devel \
    mesa-libGLES-devel mesa-libEGL-devel vulkan-devel wayland-devel \
    wayland-protocols-devel libdrm-devel mesa-libgbm-devel libusb1-devel \
    libdecor-devel pipewire-jack-audio-connection-kit-devel libthai-devel \
    liburing-devel zlib-ng-compat-static

Most of those packages are SDL's: they let it find your windowing system, whether it's X11 or Wayland, and your sound system, when it's built. The rest are the compiler (build-essential, or gcc-c++), the debugger, gdb, CMake, and FreeType, for SDL_ttf.

  1. Check CMake's version, which needs to be 3.24 or later:
cmake --version

Ubuntu 22.04's CMake is older than that. If yours is, swap it for a newer one: run sudo apt remove cmake, and then sudo snap install cmake --classic. Close the terminal, and open a new one before checking again. The old one has to go first, because Ubuntu looks for commands in the snap's folder last.

  1. Download VS Code from https://code.visualstudio.com, choosing the .deb package for Ubuntu or Debian, or the .rpm for Fedora, and open it to install it. Start VS Code, click the Extensions icon on the left, the one made of four squares, search for C/C++ Extension Pack, and install the one published by Microsoft. It brings the C/C++ extension, for editing and debugging, and CMake Tools, for building.

Getting the Projects

The book's repository is at https://github.com/EliteIntegrity/Learning-C-by-Building-Games-2nd-Edition. Choose Code > Download ZIP, and extract the zip anywhere you like. Inside it is the SDL3 Projects folder, with a folder for each of the book's projects, the Windows copies of SDL, which a Mac or Linux build ignores, and the CMakeLists.txt file.

If you're comfortable with Git, you can clone the repository instead, which makes it easy to fetch any fixes later.

The CMakeLists.txt File

Here's the whole of SDL3 Projects/CMakeLists.txt. You don't have to type it, since it's in the repository, but it's worth reading once, because it's short, and it's the only thing standing between the book's code and your system:

# Builds every project in this folder with CMake, on macOS, Linux, or
# Windows. Appendix B of the book, For Mac and Linux Users, explains it.
#
# Every folder here with .cpp files in it becomes a program called game,
# built into that folder, beside its assets, so it runs as it does in
# Visual Studio. A folder of your own works the same way: make it, put
# your .cpp and .h files in it, and configure again.

cmake_minimum_required(VERSION 3.24)
project(SDL3Projects LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Debug)
endif()

# On Windows, use the copies of SDL that sit beside the projects
if(WIN32)
    list(APPEND CMAKE_PREFIX_PATH
        "${CMAKE_CURRENT_SOURCE_DIR}/SDL3"
        "${CMAKE_CURRENT_SOURCE_DIR}/SDL3_image"
        "${CMAKE_CURRENT_SOURCE_DIR}/SDL3_ttf")
endif()

# Only what the book's games need: no SDL tests, no image formats that
# need other libraries (PNG needs none), and none of SDL_ttf's extras
set(SDL_TEST_LIBRARY OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_AVIF OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_JXL OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_TIF OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_WEBP OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_PNG_LIBPNG OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_SAMPLES OFF CACHE BOOL "" FORCE)
set(SDLTTF_HARFBUZZ OFF CACHE BOOL "" FORCE)
set(SDLTTF_PLUTOSVG OFF CACHE BOOL "" FORCE)
set(SDLTTF_SAMPLES OFF CACHE BOOL "" FORCE)

# SDL, SDL_image, and SDL_ttf: if they're installed already, in new enough
# versions, CMake uses them, and if not, it downloads and builds them, the
# first time only
include(FetchContent)
FetchContent_Declare(SDL3
    URL https://github.com/libsdl-org/SDL/releases/download/release-3.4.4/SDL3-3.4.4.tar.gz
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE
    FIND_PACKAGE_ARGS 3.4 CONFIG)
FetchContent_Declare(SDL3_image
    URL https://github.com/libsdl-org/SDL_image/releases/download/release-3.4.2/SDL3_image-3.4.2.tar.gz
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE
    FIND_PACKAGE_ARGS 3.4 CONFIG)
FetchContent_Declare(SDL3_ttf
    URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-3.2.2/SDL3_ttf-3.2.2.tar.gz
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE
    FIND_PACKAGE_ARGS 3.2 CONFIG)
FetchContent_MakeAvailable(SDL3 SDL3_image SDL3_ttf)

# One program for each folder that has .cpp files in it
file(GLOB folders LIST_DIRECTORIES true RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
    "${CMAKE_CURRENT_SOURCE_DIR}/*")
foreach(folder IN LISTS folders)
    set(dir "${CMAKE_CURRENT_SOURCE_DIR}/${folder}")
    if(NOT IS_DIRECTORY "${dir}")
        continue()
    endif()
    file(GLOB sources CONFIGURE_DEPENDS "${dir}/*.cpp")
    if(NOT sources)
        continue()
    endif()
    file(GLOB headers CONFIGURE_DEPENDS "${dir}/*.h")

    # A target's name can't have spaces, so "Rogue SDL Part 1" is Rogue_SDL_Part_1
    string(REGEX REPLACE "[^A-Za-z0-9_.+-]" "_" target "${folder}")
    add_executable(${target} ${sources} ${headers})
    target_link_libraries(${target} PRIVATE
        SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3::SDL3)
    set_target_properties(${target} PROPERTIES
        OUTPUT_NAME game
        RUNTIME_OUTPUT_DIRECTORY "${dir}"
        RUNTIME_OUTPUT_DIRECTORY_DEBUG "${dir}"
        RUNTIME_OUTPUT_DIRECTORY_RELEASE "${dir}")
endforeach()

In the preceding code, the first lines ask for C++20, the same standard as Chapter 1's projects, and a Debug build, the kind that's easiest to investigate, unless you ask for another. The if(WIN32) part only matters on Windows, where it points CMake at the copies of SDL that come with the repository. On a Mac or on Linux, it's skipped.

The set lines switch off the parts of SDL that the book never uses: SDL's own test library, the image formats that would need extra libraries of their own, and SDL_ttf's text shaping and color emoji. PNG files, which are all the book's pictures, need nothing extra, and switching them off saves a good deal of building.

Each FetchContent_Declare names one library, and says where to download its source code from: the release of each that the book was written with. The words FIND_PACKAGE_ARGS ask CMake to look for the library on your system first, in at least the version given, and only download it if it isn't there. Then FetchContent_MakeAvailable does whichever is needed, for all three at once.

The loop at the end is what makes the file work for every project. It looks at every folder beside it, and each one with .cpp files in it becomes a program, built from every .cpp file in that folder. CMake's name for each program is the folder's name, with underscores where the spaces were, so the folder Rogue SDL Part 1 gives a program that CMake calls Rogue_SDL_Part_1. The program's own file, though, is always called game, and it's built into its project's folder, right beside the assets folder, for a reason that the section on assets explains. The folders that hold SDL have no .cpp files of their own, so they're skipped.

Building and Running in VS Code

  1. Start VS Code, and open the SDL3 Projects folder itself with File > Open Folder, rather than one of the project folders inside it. If VS Code asks whether you trust the authors of the files in the folder, choose to trust them, since this is the book's code.

  2. CMake Tools notices the CMakeLists.txt file, and asks which kit to use: which compiler. On a Mac, choose the one that starts with Clang, and on Linux, the one that starts with GCC. If no question appears, press Cmd+Shift+P on a Mac, or Ctrl+Shift+P on Linux, for the Command Palette, and run CMake: Select a Kit from there.

  3. CMake Tools then configures the project: it reads CMakeLists.txt, and the first time, it downloads SDL, SDL_ttf, and SDL_image, about 27 MB in all, and prepares to build them. Watch the Output panel at the bottom of the window. It's done when the last lines say "Build files have been written to" a folder called build.

  4. Open the Command Palette, run CMake: Set Launch/Debug Target, and choose the program you want, such as ControllableSquare. The same choices are in the CMake view, which the icon of a triangle with a wrench, on the left, opens: it shows the program that's chosen, and whether you're building Debug or Release.

  5. Run CMake: Debug from the Command Palette. CMake Tools builds the program first. The first build takes a few minutes, because SDL itself is being built, and later builds only rebuild what you've changed. Then the window opens, and the game runs, exactly as it does on Windows.

CMake: Run Without Debugging runs the program without the debugger, like Visual Studio's Ctrl+F5. Messages from SDL_Log, which the Windows console shows, appear in VS Code instead, in the Debug Console or in a terminal panel below the editor.

Note

On a Mac, the first time you debug, macOS may ask for your password so that the debugger can take control of the program. That's normal: it's the permission that every debugger on a Mac needs.

The debugger works just like Visual Studio's, with the same keys. Click in the margin beside a line's number, or press F9, to set a breakpoint. When the program stops on it, the Variables view on the left shows every variable, as Visual Studio's Locals and Autos windows do, F10 steps over a line, F11 steps into a function, and F5 carries on. The Call Stack view is the one that Chapter 8 met in Visual Studio, under the same name.

Assets and the Working Directory

From Chapter 11 on, the games load pictures, fonts, and sounds with paths like assets/grass.png, which are measured from the program's working directory, the folder it treats as "here." Visual Studio runs each program with its project folder as the working directory, and the CMakeLists.txt file arranges the same thing, by building each program into its own project folder. CMake Tools runs a program in the folder it was built in, with the debugger or without it, so each game finds its assets folder right beside it.

If a game ever can't find its files, the window opens and closes again at once, and the Debug Console says which file it couldn't load. You can always run a game from a terminal in its own folder instead, where the working directory is certain. From the SDL3 Projects folder, for example:

cd "Whack-a-Mole"
./game

The quotes are there because some folder names have spaces in them.

Names Must Match Exactly

Windows doesn't care whether you write Player.h or player.h: it finds the same file either way. Linux does care, and treats them as two different names, so an #include "player.h" that works on Windows stops the build on Linux, when the file is called Player.h. Most Macs don't care either, until code written on one moves to Linux.

Every name in the book's projects matches its file exactly, letter for letter, and the same goes for the names of the pictures and sounds in each assets folder. Keep to that in your own projects, and the code you write on one system will build on all three.

A Project of Your Own

Every folder with .cpp files in it becomes a program, so starting a project of your own is a matter of making a folder:

  1. In VS Code's Explorer, right-click the empty space below the folders, choose New Folder, and give it a name, such as My Game. Right-click the new folder, choose New File, and call it main.cpp.

  2. Write your program in it. Chapter 1's controllable square is a good first one.

  3. Open the Command Palette, and run CMake: Configure, so that CMake finds the new folder. Then run CMake: Set Launch/Debug Target, choose My_Game, and run CMake: Debug, as before.

If your project has pictures or sounds, put them in an assets folder inside your project's folder, just as the book's projects do. The sandbox that Chapter 2 uses for its experiments works the same way: a folder called Sandbox, with a main.cpp in it, and what it prints with std::cout appears in VS Code, just as SDL_Log's messages do.

Where the Book's Windows Steps Differ

Most of the book reads exactly the same on a Mac or on Linux, but the steps that belong to Visual Studio or to Windows have their own equivalents:

  • Chapter 1's setup. Downloading SDL, the project's settings, the include and library folders, and copying SDL3.dll are all the CMakeLists.txt file's job. There's no DLL to copy on a Mac or on Linux: the build tells each program where SDL's libraries are.
  • Creating a project. Wherever a chapter starts a fresh Visual Studio project, make a folder instead, as in A Project of Your Own, and configure again. Where a chapter starts from an earlier project, copy that project's .cpp and .h files, and its assets folder, into your new folder.
  • F5 and Ctrl+F5. Debugging is CMake: Debug, and running without the debugger is CMake: Run Without Debugging, from the Command Palette or the CMake view.
  • Checking your typing at a checkpoint. Where a checkpoint compiles main.cpp on its own, with Ctrl+F7, run CMake: Compile Active File from the Command Palette instead, with main.cpp open.
  • Error messages. The book quotes Visual Studio's errors, such as C2065 and LNK2019. Clang and GCC report the same mistakes in their own words: C2065, an undeclared identifier, is "use of undeclared identifier" from Clang, and "was not declared in this scope" from GCC; LNK2019, an unresolved external symbol, is "Undefined symbols" on a Mac, and "undefined reference to" on Linux. The explanation in each Common Errors section still applies, so search for what the message is about, rather than its number.
  • Warnings. Chapter 4 turns Visual Studio's warnings up to level 4. To do the same with Clang or GCC, add a line after add_executable in CMakeLists.txt: target_compile_options(${target} PRIVATE -Wall -Wextra).
  • Dividing by zero. With its first check taken out, Chapter 4's division by zero stops the program on a Linux PC with a floating point exception, SIGFPE, rather than Visual Studio's 0xC0000094. On a Mac with Apple's own processor, it doesn't stop at all: the answer comes out as 0, and the program carries on, so the check matters even more there.
  • Crashes. Where Visual Studio reports an access violation, as in Chapter 10, a Mac's debugger reports EXC_BAD_ACCESS, and Linux's reports a segmentation fault. They mean the same thing: the program used memory that wasn't its to use.
  • Leak checks. The _CrtDumpMemoryLeaks and _CrtSetDbgFlag experiments in Chapters 12 and 14 use Windows' own debug library, and won't build elsewhere. On Linux, add -fsanitize=address to both target_compile_options and target_link_options for your program, and leaks are reported when it ends. A Mac has a leaks tool for the job instead: run the program with it, as in leaks --atExit -- ./game.
  • Debug-only code. Chapter 24's #ifdef _DEBUG uses a name that only Visual Studio defines, so on a Mac or Linux, the line it guards is never compiled. Write #ifndef NDEBUG instead: every Release build defines NDEBUG, so the line is kept in a Debug build, on all three systems.
  • Release builds and profiling. For Chapter 29's timings, run CMake: Select Variant and choose Release, then choose Debug again afterward. Visual Studio's Performance Profiler has its equivalents: on a Mac, the Time Profiler in Instruments, which is part of Xcode, from the App Store, and on Linux, perf.
  • Where a game saves. Chapter 24's SDL_GetPrefPath gives a folder in ~/Library/Application Support on a Mac, and in ~/.local/share on Linux, rather than in Windows' AppData. Rogue SDL's save file, from Chapter 33, goes in the working directory, so it lands in the project's own folder, as it does on Windows.
  • Function keys. On a Mac keyboard, F5 and F9, which Rogue SDL uses to save and load, usually control the brightness and the media instead. Hold fn and press them, and the game gets them.

When Something Goes Wrong

CMake says it needs version 3.24 or later. Your CMake is too old. Install a newer one, as in Setting Up a Mac or Setting Up Linux, and configure again.

Configuring stops with "Could NOT find Freetype". SDL_ttf needs FreeType to read fonts, and it isn't installed. Run brew install freetype on a Mac, sudo apt install libfreetype-dev on Ubuntu or Debian, or sudo dnf install freetype-devel on Fedora, then run CMake: Delete Cache and Reconfigure.

On Linux, configuring stops because SDL can't find X11 or Wayland, or a game opens no window. SDL was built without the packages it needs for your windowing system. Install the whole list from Setting Up Linux, then run CMake: Delete Cache and Reconfigure, so that SDL is built again with them.

Configuring fails while downloading. The first configure needs an internet connection, to download SDL. Once it has succeeded, the downloads are kept in the build folder, and it doesn't need the internet again, unless you delete that folder.

A game's window opens and closes at once. Look at the game's messages, in the Debug Console or the terminal panel. If they say a file couldn't be loaded, check that the file is in the project's assets folder, with exactly that name, capitals and all. A game started some other way than from VS Code can look for its assets in the wrong folder, so run it from a terminal in its own folder instead, as in Assets and the Working Directory.

The build can't find a header, such as SDL3_ttf/SDL_ttf.h. The configure step didn't finish. Read the Output panel from the top, fix the first problem it reports, and configure again.

Everything was working, and now it isn't. Run CMake: Delete Cache and Reconfigure. It starts CMake's bookkeeping afresh, which cures most of the strange problems that come from moving folders around or changing tools.

With that, you're set up the way the rest of the book assumes: a project open, a program running, and a debugger ready when you need it. Everything from Chapter 1's orange square to Rogue SDL is yours to build, on whichever computer you have.