Make файлы linux. Make-файлы. Make- основные сведения

Make файлы linux. Make-файлы. Make- основные сведения

12.04.2019

В контексте данного мануала мы будем рассматривать адресацию IPv4. Настройки IP-адреса производятся через файл rc.conf, причем в нем мы прописываем как статический ip-адрес, так и в случае использования DHCP указываем о необходимости использования динамического.

Для начала определим какие сетевые интерфейсы используются на машине, сделаем это с помощью команды ifconfig.

Скриншот показывает результат выполнения команды ifconfig. На нем мы можем видеть одну сетевую карту с именем em0, а также виртуальный loopback интерфейс lo0. Наименование сетевого интерфейса зависит от производителя (Например для сетевых карт Realtek это будет re, у меня em это встроенная интеловская сетевая карта). В нашем случае мы видим наш сетевой интерфейс с присвоенным адресом 192.168.2.6. Сетевая маска написана в шестнадцатиричном виде

Установка статического ip-адреса.

Скажем я хочу поменять ip-адрес без перезагрузки машины. Это можно сделать с помощью одной команды через shell. Соответственно этот адрес измениться после перезагрузки, т.к. все настройке при загрузке берутся из файла /etc/rc.conf, а мы туда вносить ничего пока не будем.

Для примера я хочу поменять адрес на 192.168.2.78. Есть два варианта записи команды, оба они верны -

Ifconfig em0 192.168.2.78 netmask 255.255.255.0

Ifconfig em0 192.168.2.78/24

Обе команды сменят ай-пи адрес, различие состоит в формате записи, во втором случае мы указываем 24-ю подсеть, что соответствует маске сети 255.255.255.0. Для того, чтобы этот статический адрес стал постоянным (как ни сумбурно это звучит, в смысле сохранился после перезагрузки). Мы должны добавить запись в /etc/rc.conf

Указывать маску подсети можно также двумя путями:

Ifconfig_em0="192.168.2.78 netmask 255.255.255.0"

Ifconfig_em0="192.168.2.78/24"

После перезагрузуки Ваш сервер назначит интерфейс em0 ip-адрес 192.168.2.78 и маску 255.255.255.0

Получение динамического адреса от DHCP

В данном случае также существует два пути, первый временный (до перезаргузки), второй постоянный, то бишь получать адрес каждый раз после перезагрузки.

Соотвественно для первого пути, мы просто вводим команду

Dhclient em0 Для второго пути, добавляем строку в rc.conf

Ifconfig_em0="DHCP"

Установка шлюза по умолчанию.

Для установки шлюза по умолчанию, редактируем файл /etc/rc.conf, добавляем или изменяем строку -

Defaultrouter="192.168.2.254"

Также это можно проделать командами - route delete default route add default 192.168.2.254

Задание серверов DNS

Для указания DNS-серверов (например мы хотим установить публичные гугловские DNS), открываем на редактирование файл /etc/resolv.conf и прописываем - nameserver 8.8.8.8 nameserver 8.8.4.4

Столкнулся на днях с непонятной ошибкой. Ставлю минимальную версию Freebsd 10.1 на hyperv. После установки указываю через bsdconfig на единственном интерфейсе hn0 получать настройки от dhcp. Все в порядке, настройки получены, занимаюсь конфигурированием сервера. Потом перезагружаюсь. Во время загрузки сервер Freebsd задумывается 30 секунд на моменте прописки дефолтного шлюза, потом продолжает загрузку. Захожу в систему и вижу, что ip адрес не получен.

Начинаю разбираться в чем дело. Проверяю /etc/rc.conf:

# cat /etc/rc.conf hostname="hyperv-freebsd" ifconfig_hn0="DHCP"

Все, больше ничего нет. Ошибиться негде. Все должно работать, но не работает. Если вручную запустить dhcp клиент:

# /sbin/dhclient -c /etc/dhclient.conf hn0

то сетевые настройки благополучно получаются.

Я решил добавить настройки dhclient в rc.conf:

Dhclient_program="/sbin/dhclient" dhclient_flags=""

Перезагружаюсь, результата нет, настройки по dhcp опять не получены. Очень странная ситуация, я, честно говоря, тут призадумался. Десятки раз настраивал freebsd, но такое вижу впервые. Делать нечего, решил заглянуть в Handbook. Иду в раздел Автоматическая настройка сети (DHCP) и читаю. Нахожу там интересный момент:

По умолчанию, конфигурирование FreeBSD по протоколу DHCP выполняется фоновым процессом, или асинхронно. Остальные стартовые скрипты продолжают работу не ожидая завершения процесса конфигурирования, тем самым ускоряя загрузку системы.

Фоновое конфигурирование не создает проблем в случае, если сервер DHCP быстро отвечает на запросы, и процесс конфигурирования происходит быстро. Однако, в некоторых случаях настройка по DHCP может длиться значительное время. При этом запуск сетевых сервисов может потерпеть неудачу, если будет произведен ранее завершения конфигурирования по DHCP. Запуск DHCP в синхронном режиме предотвращает проблему, откладывая выполнение остальных стартовых скриптов до момента завершения конфигурирования по DHCP.

Для откладывания запуска стартовых скриптов до завершения конфигурирования по DHCP (синхронный режим), укажите значение SYNCDHCP:

ifconfig_fxp0=»SYNCDHCP»

Стоит попробовать. Указываю в /etc/rc.conf:

Ifconfig_hn0="SYNCDHCP"

Перезагружаюсь и вуаля, все работает. То ли это особенность виртуальной машины на hyperv, то ли что-то еще, не знаю. Впервые с подобной ситуацией сталкиваюсь. Но решение найдено. Люблю я Freebsd за подробный handbook, но последнее время работаю с фрюшкой все реже и реже в силу различных причин, главная из которых отсутствие нужных дистрибутивов софта под данную систему. Производители программ все реже и реже включают поддержку Freebsd, а жаль.

Помогла статья? Есть возможность автора

Дополнительные материалы по Freebsd

Онлайн курс "Администратор Linux"

Если у вас есть желание научиться строить и поддерживать высокодоступные и надежные системы, рекомендую познакомиться с онлайн-курсом «Администратор Linux» в OTUS. Курс не для новичков, для поступления нужны базовые знания по сетям и установке Linux на виртуалку. Обучение длится 5 месяцев, после чего успешные выпускники курса смогут пройти собеседования у партнеров. Проверьте себя на вступительном тесте и смотрите программу детальнее по.
Рекомендую полезные материалы по :
  • Установка
  • Настройка
  • Обновление
  • Прокси сервер
  • Веб сервер NGINX
  • Веб сервер Apache

Описание на одиночный диск, либо на софтовый raid1, сделанный средствами zfs, которые поддерживает стандартный установщик.

Эта статья представляет собой небольшое руководство по созданию Makefile-ов. В ней объясняется для чего нужен Makefile и дается несколько правил, которых следует придерживаться при его создании.

Введение

Допустим, вы разрабатываете некую программу под названием foo , состоящую из пяти заголовочных файлов -- 1.h , 2.h , 3.h , 4.h и -- 5.h , и шести файлов с исходным текстом программы на языке С - 1.cpp , 2.cpp , 3.cpp , 4.cpp , 5.cpp и main.cpp . (Хочу заметить, что в реальных проектах следует избегать подобного стиля именования файлов).

Теперь представим себе, что вы обнаружили ошибку в файле 2.cpp и исправили ее. Далее, чтобы получить исправленную версию программы вы компилируете все файлы, входящие в состав проекта, хотя изменения коснулись только одного файла. Это приводит к нерациональной потере времени, особенно если компьютер не слишком быстрый.

Существует ли решение проблемы?

Не стоит беспокоиться, друзья мои! Эта проблема уже давно решена. Опытными программистами была разработана утилита make . Вместо того, чтобы производить повторную компиляцию всех файлов с исходными текстами, она обрабатывает только те файлы, которые претерпели изменения. В нашем случае будет скомпилирован только один файл - 2.cpp . Разве это не здорово!?

  • Утилита make значительно упрощает жизнь, когда для сборки проекта необходимо выполнение длинных и сложных команд.
  • Проект иногда требует задания редко используемых, а потому сложных для запоминания опций компилятора. make избавит вас от необходимости удерживать их в памяти.
  • Единообразие, т.к. работа с этой утилитой поддерживается многими средами разработки.
  • Процесс сборки можно автоматизировать, поскольку make может быть вызвана из сценариев или из cron.

Для чего нужен Makefile?

Несмотря на все свои достоинства, утилита make ничего не знает о нашем проекте, поэтому необходимо создать простой текстовый файл, который будет содержать все необходимые инструкции по сборке. Файл с инструкциями по сборке проекта называется makefile (произносится как "мэйкфайл". прим. перев. ) .

Как правило этим файлам дается имя makefile или Makefile , в соответствии с соглашениями по именованию таких файлов. Если же вы дадите файлу инструкций другое имя, то вам потребуется вызывать утилиту make с ключом -f .

Например, если свой makefile вы назвали bejo , то команда на сборку проекта будет выглядеть так:

Make -f bejo

Структура файла

Makefile содержит разделы для "целей" , зависимостей и правил (rules) сборки. Все это оформляется следующим образом: сначала указывается имя цели (обычно это имя исполняемого или объектного файла), после которого следует двоеточие, затем следуют имена зависимостей, т.е. файлов, необходимых для получения данной цели. И, наконец, следует список правил: т.е. команд, которые необходимо выполнить для получения указанной цели.

Простой пример структуры makefile"а:

Target: dependencies command command ...

Каждое правило command должно начинаться с символа табуляции -- это обязательное условие! Отсутствие символа табуляции в начале строки с правилом - самая распространенная ошибка. К счастью, подобные ошибки легко обнаруживаются, так как утилита make сообщает о них.

Пример Makefile.

Ниже приводится простой пример (номера строк добавлены для ясности).

1 client: conn.o 2g++ client.cpp conn.o -o client 3 conn.o: conn.cpp conn.h 4g++ -c conn.cpp -o conn.o

В этом примере строка, содержащая текст
client: conn.o ,
называется "строкой зависимостей", а строка
g++ client.cpp conn.o -o client
называется "правилом" и описывает действие, которое необходимо выполнить.

А теперь более подробно о примере, приведенном выше:

  • Задается цель -- исполняемый файл client , который зависит от объектоного файла conn.o
  • Правило для сборки данной цели
  • В третьей строке задается цель conn.o и файлы, от которых она зависит -- conn.cpp и conn.h .
  • В четвертой строке описывается действие по сборке цели conn.o .

Комментарии

Строки, начинающиеся с символа "#", являются комментариями

Ниже приводится пример makefile с комментариями:

1 # Создатьисполняемыйфайл "client" 2 client: conn.o 3g++ client.cpp conn.o -o client 4 5 # Создать объектный файл "conn.o" 6 conn.o: conn.cpp conn.h 7g++ -c conn.cpp -o conn.o

"Ложная" цель

Обычно "ложные" цели, представляющие "мнимое" имя целевого файла, используются в случае возникновения конфликтов между именами целей и именами файлов при явном задании имени цели в командной строке.

Допустим в makefile имеется правило, которое не создает ничего, например:

Clean: rm *.o temp

Поскольку команда rm не создает файл с именем clean , то такого файла никогда не будет существовать и поэтому команда make clean всегда будет отрабатывать.

Однако, данное правило не будет работать, если в текущем каталоге будет существовать файл с именем clean . Поскольку цель clean не имеет зависимостей, то она никогда не будет считаться устаревшей и, соответственно, команда "rm *.o temp" никогда не будет выполнена. (при запуске make проверяет даты модификации целевого файла и тех файлов, от которых он зависит. И если цель оказывается "старше", то make выполняет соответствующие команды-правила -- прим. ред.) Для устранения подобных проблем и предназначена специальная декларация .PHONY , объявляющая "ложную" цель. Например:

PHONY: clean

Таким образом мы указываем необходимость исполнения цели, при явном ее указании, в виде make clean вне зависимости от того - существует файл с таким именем или нет.

Переменные

Определить переменную в makefile вы можете следующим образом:

$VAR_NAME=value

В соответствии с соглашениями имена переменных задаются в верхнем регистре:

$OBJECTS=main.o test.o

Чтобы получить значение переменной, необходимо ее имя заключить в круглые скобки и перед ними поставить символ "$", например:

$(VAR_NAME)

В makefile-ах существует два типа переменных: "упрощенно вычисляемые" и "рекурсивно вычисляемые" .

TOPDIR=/home/tedi/project SRCDIR=$(TOPDIR)/src

При обращении к переменной SRCDIR вы получите значение /home/tedi/project/src .

Однако рекурсивные переменные могут быть вычислены не всегда, например следующие определения:

CC = gcc -o CC = $(CC) -O2

выльются в бесконечный цикл. Для разрешения этой проблемы следует использовать "упрощенно вычисляемые" переменные:

CC:= gcc -o CC += $(CC) -O2

Где символ ":=" создает переменную CC и присваивает ей значение "gcc -o". А символ "+=" добавляет "-O2" к значению переменной CC.

Заключение

Я надеюсь, что это краткое руководство содержит достаточно информации, чтобы начать создавать свои makefile. А за сим -- успехов в работе.

Библиография

  • 1 GNU Make Documentation File, info make.
  • Kurt Wall, et.al., Linux Programming Unleashed (Программирование под Linux на оперативном просторе -- прим. ред.) , 2001.

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

Сценарии Make описываются в т.н. файле проекта. Проектом называется совокупность файлов, зависящих друг от друга. Файл описания проекта перечисляет зависимости между файлами и задает команды для обновления зависимых файлов. Имя файла описания проекта задается опцией –f командной строки программы make и по умолчанию предполагается равным Makefile или makefile . Если имя файла проекта явно не задано, при запуске утилита ищет в текущем каталоге файл с указанными выше именами, и, если такой файл существует, выполняет команды из него.

по описанию проекта в файле Makefile или makefile программа make определяет, какие файлы устарели и нуждаются в обновлении и запускает соответствующие команды.

Обычно программы на языках Си или Си++ представляют собой совокупность нескольких.c (.cpp) файлов с реализациями функций и.h файлов с прототипами функций и определениями типов данных. Как правило, каждому.c файлу соответствует.h файл с тем же именем.

Предположим, что разрабатываемая программа называется earth и состоит из файлов arthur.c, arthur.h, trillian.c, trillian.h, prosser.c, prosser.h.

Разработка программы ведется в POSIX-среде с использованием компилятора GCC.

Простейший способ скомпилировать программу - указать все исходные.c файлы в командной строке gcc:

Gcc arthur.c trillian.c prosser.c -o earth

Компилятор gcc выполнит все этапы компиляции исходных файлов программы и компоновку исполняемого файла earth. Обратите внимание, что в командной строке gcc указываются только.c файлы и никогда не указываются.h файлы.

Компиляция и компоновка при помощи перечисления всех исходных файлов в аргументах командной строки GCC допустима лишь для совсем простых программ. С ростом числа исходных файлов ситуация очень быстро становится неуправляемой. Кроме того, каждый раз все исходные файлы будут компилироваться от начала до конца, что в случае больших проектов занимает много времени. Поэтому обычно компиляция программы выолняется в два этапа: компиляция объектных файлов и компоновка исполняемой программы из объектных файлов. Каждому.c файлу теперь соответствует объектный файл, имя которого в POSIX-системах имеет суффикс.o. Таким образом, в рассматриваемом случае программа earth компонуется из объектных файлов arthur.o, trillian.o и prosser.o следующей командой:

Gcc arthur.o trillian.o prosser.o -o earth

Каждый объектный файл должен быть получен из соответствующего исходного файла следующей командой:

Gcc -c arthur.c

Обратите внимание, что явно задавать имя выходного файла необязательно. Оно будет получено из имени компилируемого файла заменой суффикса.c на суффикс.o. Итак, для компиляции программы earth теперь необходимо выполнить четыре команды:

Gcc -c arthur.c gcc -c trillian.c gcc -c prosser.c gcc arthur.o trillian.o prosser.o -o earth

Хотя теперь для компиляции программы необходимо выполнить четыре команды вместо одной, взамен получаются следующие преимущества:

  • если изменение внесено в один файл, например, в файл prosser.c, нет необходимости перекомпилировать файлы trillian.o или arthur.o; достаточно перекомпилировать файл prosser.o, а затем выполнить компоновку программы earth;
  • компиляция объектных файлов arthur.o, trillian.o и prosser.o не зависит друг от друга, поэтому может выполняться параллельно на многопроцессорном (многоядерном) компьютере.

В случае нескольких исходных.c и.h файлов и соответствующих промежуточных.o файлов отслеживать, какой файл нуждается в перекомпиляции, становится сложно, и здесь на помощь приходит программа make. По описанию файлов и команд для компиляции программа makе определяет, какие файлы нуждаются в перекомпиляции, и может выполнять перекомпиляцию независимых файлов параллельно.

Файл A зависит от файла B, если для получения файла A необходимо выполнить некоторую команду над файлом B. Можно сказать, что в программе существует зависимость файла A от файла B. В нашем случае файл arthur.o зависит от файла arthur.c, а файл earth зависит от файлов arthur.o, trillian.o и prosser.o. Можно сказать, что файл earth транзитивно зависит от файла arthur.c. Зависимость файла A от файла B называется удовлетворенной , если:

  • все зависимости файла B от других файлов удовлетворены;
  • файл A существует в файловой системе;
  • файл A имеет дату последней модификации не раньше даты последней модификации файла B.

Если все зависимости файла A удовлетворены, то файл A не нуждается в перекомпиляции. В противном случае сначала удовлетворяются все зависимости файла B, а затем выполняется команда перекомпиляции файла A.

Например, если программа earth компилируется в первый раз, то в файловой системе не существует ни файла earth, ни объектных файлов arthur.o, trillian.o, prosser.o. Это значит, что зависимости файла earth от объектных файлов, а также зависимости объектных файлов от.c файлов не удовлетворены, то есть все они должны быть перекомпилированы. В результате в файловой системе появятся файлы arthur.o, trillian.o, prosser.o, даты последней модификации которых будут больше дат последней модификации соответствующих.c файлов (в предположении, что часы на компьютере идут правильно, и что в файловой системе нет файлов "из будущего"). Затем будет создан файл earth, дата последней модификации которого будет больше даты последней модификации объектных файлов.

В получившейся конфигурации все зависимости всех файлов друг от друга удовлетворены, и поэтому для компиляции программы earth не нужно выполнять никаких команд

Предположим теперь, что в процессе разработки был изменен файл prosser.c. Его время последнего изменения теперь больше времени последнего изменения файла prosser.o. Зависимость prosser.o от prosser.c становится неудовлетворенной, и, как следствие, зависимость earth от prosser.o также становится неудовлетворенной. Чтобы удовлетворить зависимости необходимо перекомпилировать файл prosser.o, а затем файл earth. Файлы arthur.o и trillian.o можно не трогать, так как зависимости этих файлов от соответствующих.c файлов удовлетворены. Такова общая идея работы программы make и, на самом деле, всех программ управления сборкой проекта: ant http://ant.apache.org/ , scons http://www.scons.org/ и др

Хотя утилита make присутствует во всех системах программирования, вид управляющего файла или набор опций командной строки могут сильно различаться. Далее будет рассматриваться командный язык и опции командной строки программы GNU make. В дистрибутивах операционной системы Linux программа называется make. В BSD, как правило, программа GNU make доступна под именем gmake.

Файл описания проекта может содержать описания переменных, описания зависимостей и описания команд, которые используются для компиляции. Каждый элемент файла описания проекта должен, как правило, располагаться на отдельной строке. Для размещения элемента описания проекта на нескольких строках используется символ продолжения \ точно так же, как в директивах препроцессора языка Си.

Определения переменных записываются следующим образом:

<имя> = <определение>

Использование переменной записывается в одной из двух форм:

$(<имя>) или ${<имя>} - Эти формы равнозначны.

Переменные рассматриваются как макросы, то есть использование переменной означает подстановку текста из определения переменной в точку использования. Если при определении переменной были использованы другие переменные, подстановка их значений происходит при использовании переменной (опять так же, как и в препроцессоре языка Си)

Зависимости между компонентами определяются следующим образом:

<цель> : <цель1> <цель2> ... <цельN>

Где <цель> - имя цели, которое может быть либо именем файла, либо некоторым именем, обозначающим действие, которому не соответствует никакой файл, например clean. Список целей в правой части задает цели, от которых зависит <цель> .

Если описание проекта содержит циклическую зависимость, то есть, например, файл A зависит от файла B, а файл B зависит от файла A, такое описание проекта является ошибочным.

Команды для перекомпиляции цели записываются после описания зависимости. Каждая команда должна начинаться с символа табуляции (\t). Если ни одной команды для перекомпиляции цели не задано, будут использоваться стандартные правила, если таковые имеются. Для определения, каким стандартным правилом необходимо воспользоваться, обычно используются суффиксы имен файлов. Если ни одна команда для перекомпиляции цели не задана и стандартное правило не найдено, программа make завершается с ошибкой.

Для программы earth простейший пример файла Makefile для компиляции проекта может иметь вид:

Earth: arthur.o trillian.o prosser.o gcc arthur.o trillian.o prosser.o -o earth arthur.o: arthur.c gcc -c arthur.c trillian.o: trillian.c gcc -c trillian.c prosser.o: prosser.c gcc -c prosser.c

Однако, в этом описании зависимостей не учтены.h файлы. Например, если файл arthur.h подключается в файлах arthur.c и trillian.c, то изменение файла arthur.h должно приводить к перекомпиляции как arthur.c, так и trillian.c. Получается, что.o файлы зависят не только от.c файлов, но и от.h файлов, которые включаются данными.c файлами непосредственно или косвенно. С учетом этого файл Makefile может приобрести следующий вид:

Earth: arthur.o trillian.o prosser.o gcc arthur.o trillian.o prosser.o -o earth arthur.o: arthur.c arthur.h gcc -c arthur.c trillian.o: trillian.c trillian.h arthur.h gcc -c trillian.c prosser.o: prosser.c prosser.h arthur.h gcc -c prosser.c

Первой в списке зависимостей обычно записывается «главная» зависимость, а затем записываются все остальные файлы-зависимости.

В командной строке программы make можно задать имя цели, которую требуется (при необходимости) перекомпилировать. Так, при запуске

Make prosser.o

будет при необходимости перекомпилирован только файл prosser.o и те файлы, от которых он зависит, все прочие файлы затронуты не будут. Если в командной строке имя цели не указано, берется первая цель в файле. В нашем случае это будет цель earth.

Если придерживаться хорошего стиля написания Makefile, то каждый Makefile должен содержать как минимум два правила: all – основное правило, которое соответствует основному предназначению файла, и правило clean, которое предназначено для удаления всех рабочих файлов, создаваемых в процессе компиляции. В случае программы earth рабочими файлами можно считать сам исполняемый файл программы earth, а также все объектные файлы.

С учетом этих дополнений файл Makefile примет вид:

All: earth earth: arthur.o trillian.o prosser.o gcc arthur.o trillian.o prosser.o -o earth arthur.o: arthur.c arthur.h gcc -c arthur.c trillian.o: trillian.c trillian.h arthur.h gcc -c trillian.c prosser.o: prosser.c prosser.h arthur.h gcc -c prosser.c clean: rm -f earth *.o

Обратите внимание, что у правила clean отсутствует список файлов, от которых этот файл зависит. Поскольку существование файла с именем clean в рабочем каталоге не предполагается, команда rm -f ... будет выполняться каждый раз, когда make запускается на выполнение командой

Make clean

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

Во-первых, можно параметризовать название используемого компилятора, а также предоставить возможность управлять параметрами командной строки компилятора. Для задания компилятора можно определить переменную CC , для задания опций командной командной строки компиляции объектных файлов - переменную CFLAGS , а для задания опций командной строки компоновки выходной программы - переменную LDFLAGS .

Получим следующий файл:

CC = gcc CFLAGS = -Wall -O2 LDFLAGS = -s all: earth earth: arthur.o trillian.o prosser.o $(CC) $(LDFLAGS) arthur.o trillian.o prosser.o -o earth arthur.o: arthur.c arthur.h $(CC) $(CFLAGS) -c arthur.c trillian.o: trillian.c trillian.h arthur.h $(CC) $(CFLAGS) -c trillian.c prosser.o: prosser.c prosser.h arthur.h $(CC) $(CFLAGS) -c prosser.c clean: rm -f earth *.o

Теперь можно изменить используемый компилятор, не только отредактировав Makefile, но и из командной строки. Например, запуск программы make в виде

Make CC=icc

Позволит для компиляции программы использовать не gcc, а Intel компилятор Си. Аналогично запуск

Make CFLAGS="-g" LDFLAGS="-g"

Позволит включить отладочную информацию в генерируемые объектные файлы и исполняемую программу

Во-вторых, можно избавиться от дублирования имен файлов сначала в зависимостях, а потом в выполняемых командах. Для этого могут быть использованы специальные переменные $^ , $< и $@ . Переменная $@ раскрывается в имя цели, стоящей в левой части правила. Переменная $< раскрывается в имя первой зависимости в правой части правила. Переменная $^ раскрывается в список всех зависимостей в правой части. Правило для компиляции файла arthur.o приобретет следующий вид:

Arthur.o: arthur.c arthur.h $(CC) $(CFLAGS) -c $<

Именно такое правило для компиляции.o файлов из.c файлов уже встроено в make, поэтому строку компиляции можно просто удалить. Останется следующий Makefile:

CC = gcc CFLAGS = -Wall -O2 LDFLAGS = -s all: earth earth: arthur.o trillian.o prosser.o $(CC) $(LDFLAGS) $^ -o $@ arthur.o: arthur.c arthur.h trillian.o: trillian.c trillian.h arthur.h prosser.o: prosser.c prosser.h arthur.h clean: rm -f earth *.o

При желании можно создавать новые шаблонные зависимости, то есть зависимости не конкретных файлов друг от друга, а файлов, имена которых удовлетворяют заданному шаблону. Тогда команды в зависимостях конкретных файлов также могут быть опущены. Например, стандартное шаблонное правило для зависимостей.o файлов от.c файлов может быть определено следующим образом:

%.o: %.c: $(CC) -c $(CFLAGS) $<

Тем не менее, в этом файле проекта осталось слабое место. Оно связано с тем, что зависимости объектных файлов включают в себя помимо.c файлов и.h файлы, подключаемые.c файлами непосредственно или транзитивно. Представим себе, что в файл prosser.c была добавлена директива

#include "trillian.h"

Но Makefile не был соответствующим образом изменен. Теперь может получиться так, что в файле trillian.h будет изменена некоторая структура данных, но файл prosser.o не будет перекомпилирован и код модуля prosser.o будет продолжать работать со старой версией структуры данных, в то время как остальная программа - с новой версией структуры данных. Такое расхождение в описании данных в рамках одной программы может привести к "загадочным" ошибкам при ее работе.

Хотелось бы каким-либо образом строить списки зависимостей объектных файлов от.c и.h файлов автоматически. Для этого мы воспользуемся специальными опциями компилятора gcc и расширенными возможностями GNU make.

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

include deps.make

Для генерации файла deps.make с зависимостями воспользуемся опцией -MM компилятора gcc:

Deps.make: arthur.c trillian.c prosser.c arthur.h trillian.h prosser.h gcc -MM arthur.c trillian.c prosser.c > deps.make

Файл deps.make зависит от всех.c и.h файлов, из которых собирается программа. Может показаться, что это правило не будет работать, так как в Makefile необходимо включить файл deps.make, для генерации которого необходимо выполнить Makefile, то есть возникает циклическая зависимость, однако GNU make умеет корректно обрабатывать такие ситуации.

Для того, чтобы не выписывать списки.c и.h файлов несколько раз, в начале Makefile можно определить переменные:

CFILES = arthur.c trillian.c prosser.c HFILES = arthur.h trillian.h prosser.h

Более того, список объектных файлов можно получать из списка.c файлов заменой суффикса.c на.o:

OBJECTS = $(CFILES:.c=.o)

В итоге получили следующий Makefile:

CC = gcc CFLAGS = -Wall -O2 LDFLAGS = -s CFILES = arthur.c trillian.c prosser.c HFILES = arthur.h trillian.h prosser.h OBJECTS = $(CFILES:.c=.o) TARGET = earth all: $(TARGET) earth: $(OBJECTS) $(CC) $(LDFLAGS) $^ -o $@ include deps.make deps.make: $(CFILES) $(HFILES) gcc -MM $(CFILES) > deps.make clean: rm -f $(TARGET) *.o

Этот файл можно легко модифицировать для сборки других проектов с помощью изменения значений переменных CFILES, HFILES и TARGET.

Пример файла C++ проекта:

CXX = g++ LDFLAGS = CXXFLAGS = -Wall -O2 -g CXXFILES = main.cpp fn.cpp HFILES = fn.h OBJECTS = $(CXXFILES:.cpp=.o) TARGET = proga all: $(TARGET) proga: $(OBJECTS) $(CXX) $(LDFLAGS) $^ -o $@ include deps.make deps.make: $(CXXFILES) $(HFILES) $(CXX) -MM $(CXXFILES) > deps.make clean: rm -f proga *.o

Для просмотра результирующих значений переменных полезно просматривать вывод команды: make -p

У вас, вероятно, появился вопрос: можно ли не компилировать эти файлы по отдельности, а собрать сразу всю программу одной командой? Можно.

gcc calculate.c main.c -o kalkul -lm

Вы скажете, что это удобно? Удобно для нашей программы, потому что она состоит всего из двух c-файлов. Однако профессиональная программа может состоять из нескольких десятков таких файлов. Каждый раз набирать названия их всех в одной строке было бы делом чрезмерно утомительным. Но есть возможность решить эту проблему. Названия всех исходных файлов и все команды для сборки программы можно поместить в отдельный текстовый файл. А потом считывать их оттуда одной короткой командой.

Давайте создадим такой текстовый файл и воспользуемся им. В каталоге проекта kalkul2 удалите все файлы, кроме calculate.h, calculate.c, main.c. Затем создайте в этом же каталоге новый файл, назовите его Makefile (без расширений). Поместите туда следующий текст.

Kalkul: calculate.o main.o gcc calculate.o main.o -o kalkul -lm calculate.o: calculate.c calculate.h gcc -c calculate.c main.o: main.c calculate.h gcc -c main.c clean: rm -f kalkul calculate.o main.o install: cp kalkul /usr/local/bin/kalkul uninstall: rm -f /usr/local/bin/kalkul

Обратите внимание на строки, введённые с отступом от левого края. Этот отступ получен с помощью клавиши Tab. Только так его и надо делать! Если будете использовать клавишу «Пробел», команды не будут исполняться.

Затем дадим команду, состоящую всего из одного слова:

И сразу же в нашем проекте появляются и объектные файлы и запускаемый. Программа make как раз и предназначена для интерпретации команд, находящихся в файле со стандартным названием Makefile. Рассмотрим его структуру.

Makefile является списком правил. Каждое правило начинается с указателя, называемого «Цель». После него стоит двоеточие, а далее через пробел указываются зависимости. В нашем случае ясно, что конечный файл kalkul зависит от объектных файлов calculate.o и main.o. Поэтому они должны быть собраны прежде сборки kalkul. После зависимостей пишутся команды. Каждая команда должна находиться на отдельной строке, и отделяться от начала строки клавишей Tab. Структура правила Makefile может быть очень сложной. Там могут присутствовать переменные, конструкции ветвления, цикла. Этот вопрос требует отдельного подробного изучения.

Если мы посмотрим на три первых правила, то они нам хорошо понятны. Там те же самые команды, которыми мы уже пользовались. А что же означают правила clean, install и uninstall?

В правиле clean стоит команда rm, удаляющая исполняемый и объектные файлы. Флаг -f означает, что, если удаляемый файл отсутствует, программа должна это проигнорировать, не выдавая никаких сообщений. Итак, правило clean предназначено для «очистки» проекта, приведения его к такому состоянию, в каком он был до команды make.

Запустите

Появились объектные файлы и исполняемый. Теперь

Объектные и исполняемый файлы исчезли. Остались только c-файлы, h-файл и сам Makefile. То есть, проект «очистился» от результатов команды make.

Правило install помещает исполняемый файл в каталог /usr/local/bin - стандартный каталог размещения пользовательских программ. Это значит, что её можно будет вызывать из любого места простым набором её имени. Но помещать что-либо в этот каталог можно только, зайдя в систему под «суперпользователем». Для этого надо дать команду su и набрать пароль «суперпользователя». В противном случае система укажет, что вам отказано в доступе. Выход из «суперпользователя» осуществляется командой exit. Итак,

Теперь вы можете запустить это программу просто, введя имя программы, без прописывания пути.

Можете открыть каталог /usr/local/bin. Там должен появиться файл с названием kalkul.

Давайте теперь «уберём за собой», не будем засорять систему.

Посмотритекаталог /usr/local/bin. Файл kalkul исчез. Итак, правило uninstall удаляет программу из системного каталога.

Дмитрий Пантелеичев (dimanix2006 at rambler dot ru) - Make-файлы

© 2024 beasthackerz.ru - Браузеры. Аудио. Жесткий диск. Программы. Локальная сеть. Windows