(Press S to open the speaker’s notes)
// hello.c
#include <stdio.h>
const char* getGreeting();
int main() {
printf("%s\n", getGreeting());
}
// greeting.c
const char* getGreeting() {
return "Hello, compiler!";
}
$ gcc -o hello hello.c greeting.c
$ ./hello
Hello, compiler!
If we only update the greeting, do we really need to recompile hello.c as well?
$ gcc -o hello.o -c hello.c
$ gcc -o greeting.o -c greeting.c
$ gcc -o hello hello.o greeting.o
$ ./hello
Hello, compiler!
$ vim greeting.c
$ gcc -o greeting.o -c greeting.c
$ gcc -o hello hello.o greeting.o
$ ./hello
Hello, object files!
hello
hello
Easy? Yes, but tedious, and difficult to scale.
We haven’t touched on compiler/linker flags, libraries, installers, …
make
and similar tools help orchestrate builds.
# Makefile
hello.o: hello.c
gcc -o hello.o -c hello.c
greeting.o: greeting.c
gcc -o greeting.o -c greeting.c
hello: hello.o greeting.o
gcc -o hello hello.o greeting.o
$ make hello
gcc -o hello.o -c hello.c
gcc -o greeting.o -c greeting.c
gcc -o hello hello.o greeting.o
$ ./hello
Hello, object files!
$ vim greeting.c
$ make hello
gcc -o greeting.o -c greeting.c
gcc -o hello hello.o greeting.o
$ ./hello
Hello, make!
make
to build object files and link them
togethermake
again to rebuild only what’s
necessarymake
-flto
vs. /LTCG
)The same program, but let’s use CMake to generate our Makefile for us.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(hello)
add_executable(
hello
hello.c greeting.c
)
$ mkdir build
$ cd build
$ cmake ..
...
$ make hello
...
$ ./hello
Hello, make!
$ vim ../greeting.c
$ make hello
...
$ ./hello
Hello, cmake!
The same CMake works across operating systems and compilers.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(hello)
add_executable(
hello
hello.c greeting.c
)
> mkdir build
> cd build
> cmake ..
...
> MSBuild.exe hello.sln
...
> .\Debug\hello.exe
Hello, make!
> notepad.exe ..\greeting.c
> MSBuild.exe hello.sln
...
> .\Debug\hello.exe
Hello, cmake!
CMakeLists.txt
files are the bulk of the scripts*.cmake
files provide extra functionality, shared tools, etc.
$ tree demo
demo
├── cmake
│ ├── dist.cmake
│ └── tools.cmake
├── CMakeLists.txt
├── include
├── src
│ └── CMakeLists.txt
└── test
└── CMakeLists.txt
# CMakeLists.txt
add_executable(hello)
target_sources(hello PRIVATE
hello.c
greeting.c
)
# Makefile
hello.o: hello.c
gcc -o hello.o -c hello.c
greeting.o: greeting.c
gcc -o greeting.o -c greeting.c
hello: hello.o greeting.o
gcc -o hello hello.o greeting.o
add_*
functions declare new targets
add_executable()
add_library()
add_custom_target()
target_*
functions modify properties on existing targets
target_compile_definitions()
target_compile_options()
target_include_directories()
target_link_libraries()
target_sources()
target_*
functionsPRIVATE
-scoped properties only affect the named targetINTERFACE
-scoped properties only affect other targets that link to the named
target
INTERFACE
-scoped properties are applied transitivelyPUBLIC
scope modifies both the private- and interface-scoped properties
add_library(translator)
target_sources(
translator
PRIVATE
Core.cpp Dictionary.cpp Grammar.cpp
)
add_library(translator)
target_include_directories(
translator
PUBLIC
"${CMAKE_SOURCE_DIR}/include"
)
add_library(translator)
target_compile_definitions(
translator
INTERFACE
-DTRANSLATOR_VERSION=100297
)
cmake_minimum_required(VERSION 3.25 FATAL_ERROR)
project(ezra-vm)
add_library(ezra-vm)
target_include_directories(ezra-vm PUBLIC include)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(ezra-vm PUBLIC -Wall -Wextra -Werror)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(ezra-vm PUBLIC /Zc:__cplusplus /utf-8)
endif()
target_sources(ezra-vm PRIVATE
include/ezra/vm/Assembler.hpp
include/ezra/vm/Instruction.hpp
...
src/Vm.cpp
src/Word.cpp
)
add_executable(testEzraVm)
target_link_libraries(testEzraVm PRIVATE ezra-vm)
target_sources(testEzraVm PRIVATE
testAssembler.cpp
testVm.cpp
testWord.cpp
)