chemikadze — Все самое необходимое, что нужно знать о make
Любой, начавший программировать для linux в лучших традициях TrueUnixWay рано или поздно приходит к выводу что компилировать программы отдельными коммандами для обработки каждого файла - не самая лучшая трата времени. Вот тут-то как раз приходит время автоматизированных средств сборки - make, cmake, или даже qmake.
Мы остановимся на первом варианте, как на самом распространенном. Все, что нужно для работы make - файл с описанием правил сборки, находящийся в каталоге с исходным кодом - Makefile. Что же входит в состав Makefile?
Еще стоит упомянуть об одной особенности обработки комманд make: каждая строка выполняется в собственном коммандном интерпритаторе, то есть после выполнения комманды cd в следующей строке текущий каталог не изменится. Для этого нужно использовать многострочные комманды:
Вот в принципе и все, что нужно знать для создания небольших Makefile'ов, так что вот вам реальный примерчик:
PS: источник - "Разработка приложений в среде Linux" Майкл К. Джонсон, Эрик В. Троан, полезное чтиво.
Мы остановимся на первом варианте, как на самом распространенном. Все, что нужно для работы make - файл с описанием правил сборки, находящийся в каталоге с исходным кодом - Makefile. Что же входит в состав Makefile?
-
Переменные
В них можно хранить все что угодно: имена комманд, исходных файлов и прочий мусор. Объявляются они так:
1
2
3
4OBJ = main.o stuff_one.o stuff_two.o # обычный способ, при котором значение определяется при использовании
OBJ2 := main.o # здесь значение переменной определяется при присваивании, до использования
OBJ2 := $(OBJ2) stuff_one.o
OBJ2 += stuff_two.o # другая запись предыдущей строки
Обратите внимание на разницу в интерпритации. Если вы сделаете так:
1
2OBJ3 = main.o
OBJ3 = $(OBJ3) stuff.o
make войдет в цикл, вычисляя значение переменной. Все дело в том, что при сипользовании = make хранит значение в виде, который присваивался, тоесть "$(OBJ3) stuff.o". Переменная замыкается сама на себя и вуаля: у нас есть нерабочий Makefile. Если же писать через := или =, значение после выполнения аналогичного кода будет "main.o stuff.o". -
Правила
С помощью них вы, собственно, и выставляете зависимости исходных файлов при сборке. Выглядят они примерно так:
1
2binary: main.o stuff_one.o stuff_two.o
g++ -o binary main.o stuff_one.o stuff_two.o
Сначала задается имя целевого файла, двоеточие, список зависимостей, потом в следующей строке после символа табуляции (не забудьте, иначе получите ошибку) комманда для получения целевого файла из зависимостей.
Более красиво это можно сделать, если у вас определены переменные со списком файлов:
1
2binary: $(OBJS)
g++ -o binary $(OBJS)
Кроме файлов, целью может служить операция:
1
2
3install:
install -m 744 binary /usr/bin
.PHONY: install
Так как у нас нет целевого файла install, мы вынуждены использовать директиву .PHONY, сообщающую make о том, что install - не имя файла. -
Суффиксы
Применяются для автоматического составления зависимостей. Такие распространенные расширения как .c, .cpp make обрабатывает автоматически, создавая из них объектные файлы с тем же именем, а для нестандартных нам потребуется создание суффиксов. Вот вам стандартный пример:
1
2
3.c.o:
$(CC) $(CFLAGS) $(CXXFLAGS) -o $@ $<
.SUFFIXES: .c .o
Здесь каждому файлу .o ставится в зависимость файл .c, который нужно обработать компилятором с соответствующими параметрами коммандной строки, причем $@ - подстановка имени выходного файла, а $< - шаблон подстановки первой зависимости (также есть $^ означающий все зависимости).
Кроме того, есть еще так называемые шаблонные правила, имеющие синтаксис сходный с обычными правилами:
1
2%.c: %.o:
$(CC) $(CFLAGS) $(CXXFLAGS) -o $@ $<
Еще стоит упомянуть об одной особенности обработки комманд make: каждая строка выполняется в собственном коммандном интерпритаторе, то есть после выполнения комманды cd в следующей строке текущий каталог не изменится. Для этого нужно использовать многострочные комманды:
1 2 3 4 5 |
cd dir1; \ command; \ if (условие); then \ dosomethingelse; \ fi |
Вот в принципе и все, что нужно знать для создания небольших Makefile'ов, так что вот вам реальный примерчик:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# CXXFLAGS += -D DEBUG_BFVM CXXFLAGS += -ggdb # CXXFLAGS += -O2 CFLAGS = $(CXXFLAGS) all: brainfuck brainfuck: bfvm.o brainfuck.o g++ $(CXXFLAGS) bfvm.o brainfuck.o -o brainfuck clean: rm -vf *.o .PHONY: clean clear: rm -vf *.o brainfuck .PHONY: clear install: install -o root -g root -m 0755 brainfuck /usr/bin/brainfuck mkdir -p -m 0755 /usr/share/brainfuck chown root:root /usr/share/brainfuck install -o root -g root -m 0755 ./programs/helloworld.bf /usr/share/brainfuck/helloworld.bf .PHONY: install uninstall: rm -vf /usr/bin/brainfuck rm -rvf /usr/share/brainfuck .PHONY: uninstall |
PS: источник - "Разработка приложений в среде Linux" Майкл К. Джонсон, Эрик В. Троан, полезное чтиво.