Introduction to CMake
Article Info
- Last Updated
- (24b0f86)
- Tags
- Guide
CMake is driven by a file called CMakeLists.txt
which contains:
-
Rules for how the software should be compiled
-
Rules for which libraries and executables are built by this project (called targets)
-
Rules for which libraries are needed, and how to find them
You can include other CMakeLists.txt
files, and other .CMake
files directly to make your build scripts tidier, but you don’t need to do this.
An example CMakeLists
This is what a CMake file with dependencies should look like:
CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
# if VCPKG exsits, inject its build chain
if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "")
endif()
# create a project called Comp270
project(comp270)
# This is how we should handle dependencies in CMake
find_package(glfw REQUIRED CONFIG)
// create an executable called worksheet 1
add_executable(ws1)
// link the dependencies for worksheet 1
target_link_libraries( ws1 PRIVATE SDL2::SDL2 SDL2::SDL2main )
// this is where our include files will live
target_include_directories( ws1 PUBLIC include )
// now we list our C++ sources
target_sources( ws1
PRIVATE
src/main.cpp
)
// Extra command to manage resources when a build is triggered for ws1
add_custom_command(TARGET ws1 PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/assets/ $<TARGET_FILE_DIR:ws1>)
Tip
|
3rd party code you rely on should really be vendored into it’s own directory. It’s best not to mix your own code and 3rd party code. |
You can see an example of how you might vendor a header-only library in FGGL.
Using included files
Your header files should be structured as standard header files should be:
include/main.h
#ifndef PROJECT_HEADER_H
#define PROJECT_HEADER_H
#include <glad/glad.h>
#include <GLFW/glfw.h>
namespace project {
}
#endif // PROJECT_HEADER_H
CPP file contents
src/main.cpp
#include "main.h"
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void process_input(GLFWwindow* window);
void glfw_error(int code, const char* description);
int main(int argc, char* argv[]) {
auto status = glfwInit();
if ( status == GLFW_FALSE ) {
std::cerr << "Could not init GLFW" << std::endl;
return 1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow("Demo Application", 800, 600);
if ( !window ){
std::cerr << "could not create GLFW window" << std::endl;
glfwTerminate();
return 1;
}
glfwMakeContextCurrent( window );
// GLAD dynamically loads OpenGL
if ( !gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress) ) {
std::cerr << "" << std::endl;
glfwTerminate();
return 1;
}
// Cleanup GLFW
glfwTerminate();
return 0;
}