02 CMake Project - 2altoids/squeak-game-engine GitHub Wiki
Previous: 01 Setup
Estimated completion time: 1-2 hours.
After the 01 Setup stage comes the "zero-feature release", a skeleton of the project used to test the build system. There are a few things needed for the ZFR:
- Libraries
- Build automation
- Test application that compiles and runs
Libraries
Currently, the needed libraries are:
- glad - OpenGL extension loading library comparable to GLEW (but a better option nowadays).
- GLFW - OpenGL utility library needed for handling windows and keyboard/mouse input.
Since I want Squeak to be cross-platform, all libraries will be built from source.
glad
To download glad, use this website with the following options:
- Language:
C/C++ - Specification:
OpenGL - Profile:
Core - GL API:
4.0 - "Generate loader" box checked.
The GL API specifies the OpenGL version used. Newer versions have new features but won't work on older machines. This Reddit thread suggests 4.0+ is a safe bet for all machines except Intel i3 laptops. APIs lower than 3.3 may break modern OpenGL code. Most computers bought in the past few years will be 4.0+.
To check the latest supported OpenGL version on Linux, use the command glxinfo | grep "OpenGL core profile version string".
To check the latest supported OpenGL version on Windows, idk man, google it.
Once the files are generated, download and extract the ZIP archive.
GLFW
Download GLFW from the GLFW website and extract the ZIP archive.
Add Libraries to the Project
Rename the extracted GLFW folder to glfw, then move the extracted library folders to a new folder lib within the root directory of the repo. When finished, the project directory should look something like this:
squeak-game-engine/
├─ lib/
│ ├─ glfw/
│ │ └─ tests/, src/, include/, examples/, etc.
│ ├─ glad/
│ │ ├─ src/
│ │ └─ include/
├─ README.md
├─ .gitignore
└─ .git/
Build automation
Build automation frees the programmer from the drudgery of creating finished binaries from source. Build automation can:
- Compile libraries from source.
- Compile multiple binaries at once.
- Link libraries to binaries.
- Create binaries for many different platforms using the same source code.
- Create binaries without an IDE.
- Create IDE project/solution files.
- Run tests.
To CMake or not to CMake
I use CMake, but it's not the only option. TheCherno's Game Engine YouTube series uses a combination of a VisualStudio solution and Premake.
CMake
CMake is a "meta build system" for C++. CMake runs scripts called "CMakeLists.txt" to generate a build system (makefile, VisualStudio solution, etc.). That build system can then be run to build the project.
In this project I will use CMake to generate makefiles.
For a tutorial on CMake, see vector-of-bool's "How to CMake Good" video series. Especially important is how to use CMake with VSCode (video).
CMakeLists.txt
Here is the CMakeLists.txt for the project root directory. Note: some functions used are deprecated, but it's good enough for now. For info on each function see the CMake documentation. Here's a line-by-line breakdown:
# This version of CMake should work
cmake_minimum_required(VERSION 3.16)
# Name project
project(squeak-game-engine)
# Language and compiler options
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions(-c -Wall)
- The
cmake_minimum_required()function enforces a minimum version of CMake. This ensures a known set of features is available. Check your CMake version with the commandcmake --version. project()saves the given string into the variablePROJECT_NAME.set()assigns values to variables (named in all caps with underscores). Here we set built-in variables to enforce the C++11 standard, so the compiler will reject noncompliant code.add_definitions()gives command line options to the compiler. I'm not sure this is how it should be used, or if it works with compilers other thangcc, but it works.-ckeeps the compiler from linking (we do that later), and-Wallprints any compiler warning messages.
# Add source files
file(GLOB_RECURSE SOURCE_FILES
${PROJECT_SOURCE_DIR}/src/*.c
${PROJECT_SOURCE_DIR}/src/*.cpp)
# Add header files
file(GLOB_RECURSE HEADER_FILES
${PROJECT_SOURCE_DIR}/src/*.h
${PROJECT_SOURCE_DIR}/src/*.hpp)
# Copy shaders
file(GLOB SHADERS ${PROJECT_SOURCE_DIR}/res/shaders/*.glsl)
file(COPY ${SHADERS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/res/shaders)
file()executes filesystem operations (renaming, moving, deletion, etc). Here, it recursively adds all source and header files from thesrcdirectory to two lists aptly namedSOURCE_FILESandHEADER_FILES. Getting multiple files in one line like this is called "globbing". Globbing is usually discouraged (see this Stack Overflow answer), but it also means I don't have to hardcode a list of files inCMakeLists.txt. The disadvantage is CMake doesn't automatically add new files to the glob; whenever a file is added, moved, or deleted, CMake needs to be re-run or CMakeLists.txt needs to be touched withtouch. I may change it to the proper way later but right now re-running CMake before pushing isn't too hard or inconvenient.file()is used again to copy OpenGL shaders (which I write later) into the build directory where the executable can find them.
# GLFW
# Don't build docs, tests, or examples
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
# Add GLFW to build
set(GLFW_DIR ${PROJECT_SOURCE_DIR}/lib/glfw)
add_subdirectory(${GLFW_DIR} ${CMAKE_CURRENT_BINARY_DIR}/glfw)
include_directories(${GLFW_DIR}/include)
# Glad
set(GLAD_DIR ${PROJECT_SOURCE_DIR}/lib/glad)
include_directories(${GLAD_DIR}/include)
# Prevent GLFW from defining gl symbols and conflicting with Glad if
# GLFW was included first
add_definitions(-DGLFW_INCLUDE_NONE)
- Here we deal with the libraries. Following the GLFW Build Guide, the first three set() lines turn off building all the unneeded bits of GLFW.
GLFW_DIRis set to the glfw source directory, and thenadd_subdirectory()creates a folderglfwinside the build folder where glfw will be built.include_directories()adds GLFW header file directory to the include paths. This will later be changed totarget_include_directories()when we have multiple build targets that will include different things.- Glad does not need to be built separately as it is composed of one C source file and two header files.
GLAD_DIRis defined for later use and the headers are added to the include paths withinclude_directories(). Theadd_definitions()defines the macroGLFW_INCLUDE_NONEwhich keeps GLFW from conflicting with glad, as described in the GLFW getting started guide
add_executable(${PROJECT_NAME} ${HEADER_FILES} ${SOURCE_FILES} ${GLAD_DIR}/src/glad.c)
# Link to libraries
target_link_libraries(${PROJECT_NAME} glfw -static-libstdc++ -static-libgcc ${GLFW_LIBRARIES})
add_executable()creates an executable file calledsqueak-game-engine(asPROJECT_NAMEwas set way back in theproject()function) by compiling from the headers, the engine's source files, andglad.c, the only C file from glad.- Unlike glad, which is compiled into the binary, GLFW is a separately-compiled library which must be linked to before it can be used.
target_link_libraries()statically links GLFW to the project, embedding it right into the executable file.
Source code
The only C++ file needed for testing the ZFR is main.cpp (link). It belongs in the src directory in the project root folder.
The ZFR also requires two shader files, vert.glsl (link) and frag.glsl (link). These belong in the directory res/shaders.
The above files are slightly modified versions of the source code from the shaders tutorial at learnopengl.com.
The repo should now look like this:
squeak-game-engine/
├─ .git/
├─ lib/
├─ res/
│ ├─ shaders/
│ ├─ frag.glsl
│ └─ vert.glsl
├─ src/
│ └─ main.cpp
├─ .gitignore
├─ CMakeLists.txt
└─ README.md
Building
.gitignore for CMake
The following should be appended to .gitignore so no CMake-generated files get tracked by Git:
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
(source)
Additionally, add the following line so the build directory is also ignored:
build*
The Moment of Truth
To test the ZFR, run the following commands starting in the project root directory:
mkdir build
cd build
cmake ..
make
./squeak-game-engine
If nothing breaks, then you should see a window with a multicolored rendered on a maroon background.
The ZFR is not yet finished, though. The engine needs to be separated into a dynamically-linked library containing engine functionality and a test executable which uses the library to test that functionality. This is addressed in the next chapter.
Next: 03 Build Targets