Make, CMake and others
В этой лекции речь пойдет о системах сборки.
Допустим мы написаили какой-то код. Мы умеем его компилировать, проставлять флаги и т.д.
#include "hello.h"
int main() {
hello();
}
Задача:
мы хотим распространять программу, то есть каждый должен знать, как собирать программу
Можем сделать сборочный скрипт, в который запишем команду для компиляции.
#!/bin/sh -ex
CC=gcc
LD=gcc
CFLAGS="-Wall -Werror"
$CC hello.c -c $CFLAGS
$CC main.c -c $CFLAGS
$LD hello.o main.o -o hello
Но есть проблема:
- хотим различные сборки: dev, realease
- не хотим все перекомпилировать каждый раз
Make
Классическая система сборки. Умеет читать правила из конфига. Умеет понимать, что и когда надо перекомпилировать, перекомпоновывать.
- Пример Makefile.1:
hello: main.o hello.o
gcc main.o hello.o -o hello
main.o: main.c
gcc -c main.c -o main.o -Wall -Werror
hello.o: hello.c
gcc -c hello.c -o hello.o -Wall -Werror
Пробелы очень важны, а также нужно использовать именно табы, а не 4 пробела.
Make сам может понять, какие файлы меняются и что надо компилировать.
Попросить Make собрать цель:
make -f Makefile.1 hello
make -f Makefile.1 main.o
- У Make множество внутренних правил, поэтому мы можем не обязательно явно указывать все параметры. Еще можем просить запускать утилиты.
hello: main.o hello.o
main.o: main.c
gcc -c main.c -o main.o -Wall -Werror
hello.o: hello.c
gcc -c hello.c -o hello.o -Wall -Werror
clean:
rm -f *.o hello
- Можем пользоваться переменными для более точного контроля. Причем некоторые переменные (например CC - c compiler) используются самим Make:
CC=gcc
CFLAGS=-Wall -Werror
hello: main.o hello.o
main.o: main.c
$(CC) -c main.c -o main.o $(CFLAGS)
hello.o: hello.c
$(CC) -c hello.c -o hello.o $(CFLAGS)
clean:
rm -f *.o hello
- Есть специальные переменные для цели и всех зависимостей:
main.o: main.c
$(CC) -c $^ -o $@ $(CFLAGS)
- Шаблонные правила:
CC=gcc
CFLAGS=-Wall -Werror
hello: main.o hello.o
%.o: %.c
$(CC) -c $^ -o $@ $(CFLAGS)
clean:
rm -f *.o hello
- Шаблонные правила, тоже могут быть встроены уже в Make:
CC=gcc
CFLAGS=-Wall -Werror
all: hello
hello: main.o hello.o
clean:
rm -f *.o hello
Проблема make, он не может отследить изменения в header’ах. И в целом он не особо умнее, он просто смотрим, изменились ли сами файлы или нет.
Мы можем попросить компилятор для нас явно выписать абсолютно все зависимости.
[!WARNING] Но если мы хотим нормально распространять свою программу на разные системы разным разработчиком, то работа с make превращается в полный кошмар.
CMake
Приходит нам на помощь и помогает для нам генерировать Make файлы.
В нем собраны различные заготовки на различные случаи в жизни.
Чтобы управлять CMake мы пишем CMakeList.txt
cmake_minimum_required(VERSION 3.20)
project(Hello)
add_executable(hello hello.c main.c)
Сборку лучше делать в директории /build, чтобы поддерживать чистоту
Чтобы попросить CMake сделать свою работу можем:
cmake .. #из папки /build
CMake может позаботиться об очень многом, сделает кучу полезных файлов.
Дальше из директории /build можем просто вызвать и получить исполняемый файлик:
make
Можем генерировать сборочные файлы и для других систем сборок, например ninja. Будет побыстрее.
Что мы хотим дальше? - xотим работать с библиотеками
Через make это супер неудобно + сложно.
cmake может проверять сам проверять какие библиотеки есть, в зависимости от этого собирать разные билды
А еще cmake может для нас установить нужную библиотеку.
Еще cmake может решать проблемы с версиями и совместимостью.
Но в целом, мы хотим гермитичности наших сборок, все должно собираться отдельно, чтоб через кучу лет, мы могли все сорать и все будет всегда работать одинаково.
cmake нам с этим может помочь только частично