Makefile для самых маленьких
Не очень строгий перевод материала mrbook.org/tutorials/make Мне в свое время очень не хватило подобной методички для понимания базовых вещей о make. Думаю, будет хоть кому-нибудь интересно. Хотя эта технология и отмирает, но все равно используется в очень многих проектах. Кармы на хаб «Переводы» не хватило, как только появится возможность — добавлю и туда. Добавил в Переводы. Если есть ошибки в оформлении, то прошу указать на них. Буду исправлять.
Статья будет интересная прежде всего изучающим программирование на C/C++ в UNIX-подобных системах от самых корней, без использования IDE.
Компилировать проект ручками — занятие весьма утомительное, особенно когда исходных файлов становится больше одного, и для каждого из них надо каждый раз набивать команды компиляции и линковки. Но не все так плохо. Сейчас мы будем учиться создавать и использовать Мейкфайлы. Makefile — это набор инструкций для программы make, которая помогает собирать программный проект буквально в одно касание.
Для практики понадобится создать микроскопический проект а-ля Hello World из четырех файлов в одном каталоге:
#include #include "functions.h" using namespace std; int main()
#include #include "functions.h" using namespace std; void print_hello()
#include "functions.h" int factorial(int n) < if(n!=1)< return(n * factorial(n-1)); >else return 1; >
void print_hello(); int factorial(int n);
Все скопом можно скачать отсюда
Автор использовал язык C++, знать который совсем не обязательно, и компилятор g++ из gcc. Любой другой компилятор скорее всего тоже подойдет. Файлы слегка подправлены, чтобы собирались gcc 4.7.1
Программа make
Если запустить
make
то программа попытается найти файл с именем по умолчание Makefile в текущем каталоге и выполнить инструкции из него. Если в текущем каталоге есть несколько мейкфайлов, то можно указать на нужный вот таким образом:
make -f MyMakefile
Есть еще множество других параметров, нам пока не нужных. О них можно узнать в ман-странице.
Процесс сборки
Компилятор берет файлы с исходным кодом и получает из них объектные файлы. Затем линковщик берет объектные файлы и получает из них исполняемый файл. Сборка = компиляция + линковка.
Компиляция руками
Самый простой способ собрать программу:
g++ main.cpp hello.cpp factorial.cpp -o hello
Каждый раз набирать такое неудобно, поэтому будем автоматизировать.
Самый простой Мейкфайл
цель: зависимости [tab] команда
all: g++ main.cpp hello.cpp factorial.cpp -o hello
Обратите внимание, что строка с командой должна начинаться с табуляции! Сохраните это под именем Makefile-1 в каталоге с проектом и запустите сборку командой make -f Makefile-1
В первом примере цель называется all . Это цель по умолчанию для мейкфайла, которая будет выполняться, если никакая другая цель не указана явно. Также у этой цели в этом примере нет никаких зависимостей, так что make сразу приступает к выполнению нужной команды. А команда в свою очередь запускает компилятор.
Использование зависимостей
Использовать несколько целей в одном мейкфайле полезно для больших проектов. Это связано с тем, что при изменении одного файла не понадобится пересобирать весь проект, а можно будет обойтись пересборкой только измененной части. Пример:
all: hello hello: main.o factorial.o hello.o g++ main.o factorial.o hello.o -o hello main.o: main.cpp g++ -c main.cpp factorial.o: factorial.cpp g++ -c factorial.cpp hello.o: hello.cpp g++ -c hello.cpp clean: rm -rf *.o hello
Это надо сохранить под именем Makefile-2 все в том же каталоге
Теперь у цели all есть только зависимость, но нет команды. В этом случае make при вызове последовательно выполнит все указанные в файле зависимости этой цели.
Еще добавилась новая цель clean . Она традиционно используется для быстрой очистки всех результатов сборки проекта. Очистка запускается так: make -f Makefile-2 clean
Использование переменных и комментариев
Переменные широко используются в мейкфайлах. Например, это удобный способ учесть возможность того, что проект будут собирать другим компилятором или с другими опциями.
# Это комментарий, который говорит, что переменная CC указывает компилятор, используемый для сборки CC=g++ #Это еще один комментарий. Он поясняет, что в переменной CFLAGS лежат флаги, которые передаются компилятору CFLAGS=-c -Wall all: hello hello: main.o factorial.o hello.o $(CC) main.o factorial.o hello.o -o hello main.o: main.cpp $(CC) $(CFLAGS) main.cpp factorial.o: factorial.cpp $(CC) $(CFLAGS) factorial.cpp hello.o: hello.cpp $(CC) $(CFLAGS) hello.cpp clean: rm -rf *.o hello
Это Makefile-3
Переменные — очень удобная штука. Для их использования надо просто присвоить им значение до момента их использования. После этого можно подставлять их значение в нужное место вот таким способом: $(VAR)
Что делать дальше
После этого краткого инструктажа уже можно пробовать создавать простые мейкфайлы самостоятельно. Дальше надо читать серьезные учебники и руководства. Как финальный аккорд можно попробовать самостоятельно разобрать и осознать такой универсальный мейкфайл, который можно в два касания адаптировать под практически любой проект:
CC=g++ CFLAGS=-c -Wall LDFLAGS= SOURCES=main.cpp hello.cpp factorial.cpp OBJECTS=$(SOURCES:.cpp=.o) EXECUTABLE=hello all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@ .cpp.o: $(CC) $(CFLAGS) $< -o $@