Сообщения без ответов | Активные темы Текущее время: 25 ноя 2017, 17:26



Ответить на тему  [ 1 сообщение ] 
 Make для начинающих. Makefile с чего начать. Make для чайников. 
Автор Сообщение
Администратор
Аватара пользователя

Зарегистрирован: 18 янв 2012, 01:25
Сообщения: 688
Откуда: Первоуральск (Екатеринбург)
Make- основные сведения

make — утилита предназначенная для автоматизации преобразования файлов из одной формы в другую. Правила преобразования задаются в скрипте с именем Makefile, который должен находиться в корне рабочей директории проекта. Сам скрипт состоит из набора правил, которые в свою очередь описываются:

1) целями (то, что данное правило делает);
2) реквизитами (то, что необходимо для выполнения правила и получения целей);
3) командами (выполняющими данные преобразования).

В общем виде синтаксис makefile можно представить так:

Код:
# Индентация осуществляется исключительно при помощи символов табуляции,
# каждой команде должен предшествовать отступ
<цели>: <реквизиты>
    <команда #1>
    ...
    <команда #n>


То есть, правило make это ответы на три вопроса:
Код:
            {Из чего делаем? (реквизиты)} ---> [Как делаем? (команды)] ---> {Что делаем? (цели)}


Несложно заметить что процессы трансляции и компиляции очень красиво ложатся на эту схему:

Код:
                              {исходные файлы} ---> [трансляция] ---> {объектные файлы}


                              {объектные файлы} ---> [линковка] ---> {исполнимые файлы}


Простейший Makefile

Предположим, у нас имеется программа, состоящая всего из одного файла:

Код:
/*
 * main.c
 */
#include <stdio.h>
int main()
{
    printf("Hello World!\n");
    return 0;
}


Для его компиляции достаточно очень простого мэйкфайла:

Код:
hello: main.c
    gcc -o hello main.c


Данный Makefile состоит из одного правила, которое в свою очередь состоит из цели — «hello», реквизита — «main.c», и команды — «gcc -o hello main.c». Теперь, для компиляции достаточно дать команду make в рабочем каталоге. По умолчанию make станет выполнять самое первое правило, если цель выполнения не была явно указана при вызове:
Код:
    $ make <цель>


Компиляция из множества исходников

Предположим, что у нас имеется программа, состоящая из 2 файлов:
Код:
main.c

/*
 * main.c
 */
int main()
{
    hello();
    return 0;
}


и hello.c

Код:
/*
 * hello.c
 */
#include <stdio.h>
void hello()
{
    printf("Hello World!\n");
}


Makefile, выполняющий компиляцию этой программы может выглядеть так:

Код:
hello: main.c hello.c
        gcc -o hello main.c hello.c


Он вполне работоспособен, однако имеет один значительный недостаток: какой — раскроем далее.

Инкрементная компиляция

Представим, что наша программа состоит из десятка- другого исходных файлов. Мы вносим изменения в один из них, и хотим ее пересобрать. Использование подхода описанного в предыдущем примере приведет к тому, что все без исключения исходные файлы будут снова скомпилированы, что негативно скажется на времени перекомпиляции. Решение — разделить компиляцию на два этапа: этап трансляции и этап линковки.

Теперь, после изменения одного из исходных файлов, достаточно произвести его трансляцию и линковку всех объектных файлов. При этом мы пропускаем этап трансляции не затронутых изменениями реквизитов, что сокращает время компиляции в целом. Такой подход называется инкрементной компиляцией. Для ее поддержки make сопоставляет время изменения целей и их реквизитов (используя данные файловой системы), благодаря чему самостоятельно решает какие правила следует выполнить, а какие можно просто проигнорировать:

Код:
main.o: main.c
        gcc -c -o main.o main.c
hello.o: hello.c
        gcc -c -o hello.o hello.c
hello: main.o hello.o
        gcc -o hello main.o hello.o


Попробуйте собрать этот проект. Для его сборки необходимо явно указать цель, т.е. дать команду make hello.
После- измените любой из исходных файлов и соберите его снова. Обратите внимание на то, что во время второй компиляции, транслироваться будет только измененный файл.

После запуска make попытается сразу получить цель hello, но для ее создания необходимы файлы main.o и hello.o, которых пока еще нет. Поэтому выполнение правила будет отложено и make станет искать правила, описывающие получение недостающих реквизитов. Как только все реквизиты будут получены, make вернется к выполнению отложенной цели. Отсюда следует, что make выполняет правила рекурсивно.

Фиктивные цели

На самом деле, в качестве make целей могут выступать не только реальные файлы. Все, кому приходилось собирать программы из исходных кодов должны быть знакомы с двумя стандартными в мире UNIX командами:

Код:
    $ make
    $ make install


Командой make производят компиляцию программы, командой make install — установку. Такой подход весьма удобен, поскольку все необходимое для сборки и развертывания приложения в целевой системе включено в один файл (забудем на время о скрипте configure). Обратите внимание на то, что в первом случае мы не указываем цель, а во втором целью является вовсе не создание файла install, а процесс установки приложения в систему. Проделывать такие фокусы нам позволяют так называемые фиктивные (phony) цели. Вот краткий список стандартных целей:

all — является стандартной целью по умолчанию. При вызове make ее можно явно не указывать.
clean — очистить каталог от всех файлов полученных в результате компиляции.
install — произвести инсталляцию
uninstall — и деинсталляцию соответственно.

Для того чтобы make не искал файлы с такими именами, их следует определить в Makefile, при помощи директивы .PHONY. Далее показан пример Makefile с целями all, clean, install и uninstall:

Код:
.PHONY: all clean install uninstall
   
all: hello
   
clean:
            rm -rf hello *.o
main.o: main.c
            gcc -c -o main.o main.c
hello.o: hello.c
            gcc -c -o hello.o hello.c
hello: main.o hello.o
            gcc -o hello main.o hello.o
install:
            install ./hello /usr/local/bin
uninstall:
            rm -rf /usr/local/bin/hello


Теперь мы можем собрать нашу программу, произвести ее инсталлцию/деинсталляцию, а так же очистить рабочий каталог, используя для этого стандартные make цели.

Обратите внимание на то, что в цели all не указаны команды; все что ей нужно — получить реквизит hello. Зная о рекурсивной природе make, не сложно предположить как будет работать этот скрипт. Так же следует обратить особое внимание на то, что если файл hello уже имеется (остался после предыдущей компиляции) и его реквизиты не были изменены, то команда make ничего не станет пересобирать. Это классические грабли make. Так например, изменив заголовочный файл, случайно не включенный в список реквизитов, можно получить долгие часы головной боли. Поэтому, чтобы гарантированно полностью пересобрать проект, нужно предварительно очистить рабочий каталог:

Код:
    $ make clean
    $ make


Для выполнения целей install/uninstall вам потребуются использовать sudo.

Переменные

Все те, кто знакомы с правилом DRY (Don't repeat yourself), наверняка уже заметили неладное, а именно — наш Makefile содержит большое число повторяющихся фрагментов, что может привести к путанице при последующих попытках его расширить или изменить. В императивных языках для этих целей у нас имеются переменные и константы; make тоже располагает подобными средствами. Переменные в make представляют собой именованные строки и определяются очень просто:

Код:
    <VAR_NAME> = <value string>


Существует негласное правило, согласно которому следует именовать переменные в верхнем регистре, например:
Код:
    SRC = main.c hello.c


Так мы определили список исходных файлов. Для использования значения переменной ее следует разименовать при помощи конструкции $(<VAR_NAME>); например так:

Код:
    gcc -o hello $(SRC)


Ниже представлен мэйкфайл, использующий две переменные: TARGET — для определения имени целевой программы и PREFIX — для определения пути установки программы в систему.

Код:
TARGET = hello
PREFIX = /usr/local/bin

.PHONY: all clean install uninstall

all: $(TARGET)
   
clean:
            rm -rf $(TARGET) *.o
main.o: main.c
            gcc -c -o main.o main.c
hello.o: hello.c
            gcc -c -o hello.o hello.c
$(TARGET): main.o hello.o
            gcc -o $(TARGET) main.o hello.o
install:
            install $(TARGET) $(PREFIX)
uninstall:
            rm -rf $(PREFIX)/$(TARGET)


Это уже посимпатичней. Думаю, теперь вышеприведенный пример для вас в особых комментариях не нуждается.

Автоматические переменные


Автоматические переменные предназначены для упрощения мейкфайлов, но на мой взгляд негативно сказываются на их читабельности. Как бы то ни было, я приведу здесь несколько наиболее часто используемых переменных, а что с ними делать (и делать ли вообще) решать вам:

$@ Имя цели обрабатываемого правила
$< Имя первой зависимости обрабатываемого правила
$^ Список всех зависимостей обрабатываемого правила

Если кто либо хочет произвести полную обфускацию своих скриптов — черпать вдохновение можете здесь:
Автоматические переменные

Заключение

В этой статье я попытался подробно объяснить основы написания и работы мэйкфайлов. Надеюсь, что она поможет вам приобрести понимание сути make и в кратчайшие сроки освоить этот провереный временем инструмент.

Взято тут

_________________
cайт: http://yourdevice.net/
форум: http://yourdevice.net/forum/


27 ноя 2014, 16:09
Профиль ICQ WWW
Показать сообщения за:  Поле сортировки  
Ответить на тему   [ 1 сообщение ] 

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron


Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software.
Русская поддержка phpBB