8-900-374-94-44
[email protected]
Slide Image
Меню

Универсальный makefile: Makefile для самых маленьких / Хабр

makefile — Начало работы с makefile

замечания

Makefile — это текстовый файл, который контролирует работу программы make . Программа make обычно используется для управления созданием программ из их исходных файлов, но ее можно более широко использовать для обработки любого процесса, в котором файлы (или целевые объекты ) необходимо восстановить после того, как другие файлы (или предпосылки ) были изменены. Файл makefile описывает взаимосвязь между целями и предварительными условиями, а также указывает команды, необходимые для обновления цели, когда одно или несколько предварительных условий были изменены. Единственный способ make «устаревшим» — это сравнить время модификации целевых файлов и их предварительные условия.

Makefiles несколько уникальны несколькими способами, которые могут быть запутаны изначально.

Во-первых, make-файл состоит из двух совершенно разных языков программирования в одном файле. Основная часть файла написано на языке , который

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

Во-вторых, make-файл не является процедурным языком, как скрипт: поскольку make анализирует makefile, он конструирует ориентированный граф

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

Версии

Основной Makefile

Подумайте о написании «привет мир!». программа в c. Допустим, наш исходный код находится в файле source.c, теперь, чтобы запустить нашу программу, нам нужно ее скомпилировать, как правило, на Linux (с использованием gcc) нам нужно будет ввести $> gcc source.c -o output где вывод это имя исполняемого файла. Для базовой программы это работает хорошо, но по мере того, как программы становятся более сложными, наша команда компиляции также может усложняться. Здесь появляется

Makefile , make- файлы позволяют нам выписывать довольно сложный набор правил для того, как скомпилировать программу, а затем просто скомпилировать ее, набрав make в командной строке. Например, вот пример Makefile для примера hello wold выше.

Основной Makefile

Давайте сделаем базовый Makefile и сохраним его в нашей системе в том же каталоге, что и наш исходный код с именем Makefile . Обратите внимание, что этот файл должен быть назван Makefile, однако Capitol M является необязательным. Тем не менее, относительно условно использовать капитолий М.

output: source.c
    gcc source.c -o output
 

Обратите внимание, что перед командой gcc на второй строке есть ровно одна вкладка (это важно в make-файлах). Как только этот Makefile будет написан каждый раз, когда пользовательские типы make (в том же каталоге, что и Makefile) make, проверит, изменил ли source.c (проверяет метку времени), если он был изменен совсем недавно, чем вывод, который он будет запускать правило компиляции в следующей строке.

Переменные в Make-файлах

В зависимости от проекта вы можете ввести некоторые переменные в ваш файл make. Вот пример Makefile с присутствующими переменными.

CFLAGS = -g -Wall

output: source.c
    gcc $< $(CFLAGS) -o $@
 

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

$< встроена в make (называемая автоматической переменной, см. Https://www.gnu.org/software/make/manual/ html_node / Automatic-Variables.html ), и это всегда означает источник, поэтому в данном случае source.c . $(CFLAGS) — это наша переменная, которую мы определили ранее, но обратите внимание, что нам пришлось поставить переменную в круглую скобку с $ front, как этот $(someVariable) . Это синтаксис для указания Make, чтобы расширить переменную до того, что вы набрали ранее. Наконец, мы имеем символ $ @, еще раз это переменная, встроенная в make, и она просто обозначает цель этапа компиляции, поэтому в этом случае это означает вывод .

чистый

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

CFLAGS = -g -Wall
TARGETS = output

output: source.c
    gcc $< $(CFLAGS) -o $@

clean:
    rm $(TARGETS)

Как вы можете видеть, мы просто добавили еще одно правило в наш Makefile и одну дополнительную переменную, которая содержит все наши цели. Это довольно распространенное правило в make-файлах, поскольку оно позволяет быстро удалить все созданные вами двоичные файлы, просто набрав $> make clean . Набрав make clean, вы сообщите программе make о запуске чистого правила, а затем make запустит команду rm, чтобы удалить все ваши цели.

Я надеюсь, что этот краткий обзор использования make поможет вам ускорить ваш рабочий процесс, Makefiles может стать очень сложным, но с этими идеями вы сможете начать использовать make и лучше понять, что происходит в других программистах Makefiles . Для получения дополнительной информации об использовании сделать отличный ресурс https://www.gnu.org/software/make/manual/ .

Определение правил

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

Правила следуют синтаксису ниже: (Обратите внимание, что команды, следующие за правилом, отступаются от вкладки )

targets: prerequisites
        <commands>
 

где цели и предварительные условия — это имена файлов или специальные зарезервированные имена и команды (если они есть), выполняются оболочкой для создания / перестройки целей , устаревших.

Чтобы выполнить правило, можно просто запустить команду make в терминале из того же каталога, где находится Makefile . Запуск make без указания цели, выполнит первое правило, определенное в

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

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

GNU make

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

Представьте, что мы хотим создать цели foo.o и bar.o , скомпилировав C-скрипты, foo.c и bar.c , соответственно. Это можно сделать, используя обычные правила:

foo.o: foo.c
    cc -c $< -o $@

bar.o: bar.c
    cc -c $< -o $@

 

где автоматическая переменная $< — это имя первого предпосылки и $@ имя цели (полный список автоматических переменных можно найти здесь ).

Однако, поскольку цели имеют один и тот же суффикс, приведенные выше два правила теперь могут быть заменены следующим шаблоном:

%.o: %.c
    cc -c $< -o $@
 

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

Пример правила шаблона, который мы видели в предыдущем разделе, на самом деле не должен быть объявлен в Makefile, поскольку make имеет неявное правило для компиляции C. Таким образом, в следующем правиле предварительные условия foo.o и bar.o будут построены с использованием неявного правила для компиляции C, прежде чем строить foo .

foo : foo.o bar.o
    cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
 

Каталог неявных правил и используемых им переменных можно найти здесь .

Общее правило для gzip файла

если каталог содержит 2 файла:

$ ls
makefile
example.txt
 

и makefile содержат следующий текст

%.gz: %
    gzip $<
 

то вы можете получить example.txt.gz , введя в оболочку

$ make -f makefile example.txt.gz

Makefile , состоит только из одного правила , которое инструктирует сделать , как создать файл, имя которого оканчивается .gz , если есть файл с таким же именем , но .gz суффиксом.

makefile Hello World

C: \ Makefile:

helloWorld :
[TAB]echo hello world
 

результаты:

C:\>make
echo hello world
hello world
 

Примечание: [TAB] следует заменить фактической вкладкой, stackoverflow заменяет вкладки пробелами, а пробелы не используются так же, как вкладки в make-файле.



C++ — GNU Makefile — универсальный Makefile для нескольких целей

В настоящее время я разрабатываю небольшой пакет C ++ с небольшими примерами кода для учебных целей. Мне удалось написать Makefile, подобный следующему, который скомпилирует все файлы * .cpp в файлы * .o и свяжет их с исполняемыми файлами:

CC=gcc
CFLAGS=-g
LDFLAGS= -lstdc++
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

#a generic rule to create .o files from .cpp files (e.g. example1.cpp -> example1.o)
%.o: %.cpp
$(SILENT) $(ECHO) "--- Compiling $< ---"$(SILENT) $(CC) -c $(CFLAGS) $<

#define targets and their dependencies
example1: example1.o
$(SILENT) $(ECHO) "--- Linking $@ ---"$(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

example2: example2.o
$(SILENT) $(ECHO) "--- Linking $@ ---"$(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

example3: example3.o
$(SILENT) $(ECHO) "--- Linking $@ ---"$(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

clean:
$(SILENT) $(ECHO) "--- Removing object files and binaries ---"$(SILENT) rm -f *.o
$(SILENT) rm -f $(MODULES)

.PHONY: clean

Пока все хорошо, это работает хорошо. Он возьмет example1.cpp, example2.cpp и example3.cpp и скомпилирует / свяжет его с 3 исполняемыми файлами «example1 example2 example3».

Но поскольку каждый исполняемый файл имеет то же имя, что и объект (например, «example1.o» будет связан с исполняемым файлом «example1»), мне интересно, есть ли способ использовать также общее правило.

Я пробовал несколько вещей, таких как:

%: %.o
$(SILENT) $(ECHO) "--- Linking $@ ---"$(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

Насколько я понимаю, это правило должно брать все объектные файлы и создавать исполняемый файл с тем же именем, что и объектный файл, но я не мог заставить его работать! У кого-нибудь есть подсказка, как этого добиться?

0

Решение

Проблема в том, что есть встроенное правило для сборки программы непосредственно из исходного файла. Make выберет это, а не объединяет два ваших правила.

Вы могли бы отменить правило переопределив его пустым правилом:

%: %.cpp

Или вы можете полностью удалить свои правила и позволить этому правилу делать правильные вещи. неявное правило имеет рецепт по линии

$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@

так что вы можете использовать эти переменные, чтобы выбрать компилятор и указать флаги для препроцессора, компилятора и компоновщика. Например, вы можете изменить CXX использовать компилятор для неправильного языка и исправить ущерб, добавив LDFLAGS=-lstdc++, как ваш make-файл, если вы действительно хотите.

2

Другие решения

Вот решение:

В приведенных выше примерах я не использую переменную $ (LDLIBS), которая используется встроенным правилом по умолчанию. Если я изменю свой Makefile на использование $ (LDLIBS) вместо $ (LDFLAGS), все будет хорошо!

Решение № 1: (встроенное правило)

CC=gcc
CFLAGS=-g -fopenmp
LDFLAGS=
LDLIBS = -lstdc++ -lgomp
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

clean:
$(SILENT) $(ECHO) "--- Removing object files and binaries ---"$(SILENT) rm -f *.o
$(SILENT) rm -f $(MODULES)

.PHONY: clean

Решение № 2: (пользовательские правила)

CC=gcc
CFLAGS=-g -fopenmp
LDFLAGS=
LDLIBS = -lstdc++ -lgomp
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

#disable built-in rule
%: %.cpp

#rule for *.cpp -> *.o
%.o: %.cpp
$(SILENT) $(ECHO) "--- Compiling $< ---"$(SILENT) $(CC) -c $(CFLAGS) $(LDFLAGS) $<

#rule for object -> exectutable
%: %.o
$(SILENT) $(ECHO) "--- Linking $@ ---"$(SILENT) $(CC) $^ $(LDLIBS) -o $@

clean:
$(SILENT) $(ECHO) "--- Removing object files and binaries ---"$(SILENT) rm -f *.o
$(SILENT) rm -f $(MODULES)

.PHONY: clean

0

Как устроен Makefile и что это такое? | avr

Makefile — файл, содержащий набор инструкций для программы make. Программа make с помощью этого файла позволяет автоматизировать процесс компиляции программы и выполнять при этом различные действия. При запуске make по умолчанию ищет файл Makefile в текущей папке и обрабатывает его (можно изменить это поведение, чтобы открывался другой файл с набором инструкций, если ввести команду make -f другое_имя_makefile).

Система make родилась в мире UNIX и постепенно переползла и на Windows вместе с портами GNU-компиляторов (gcc). Если открыть пример готового Makefile, то он поначалу может показаться полной абракадаброй, поскольку содержимое файла подчиняется заранее заданному набору правил, которые необходимо предварительно изучить. Для простоты рассмотрим пример работы с проектом AVR и компилятором gcc.

Немного о структуре файла.

— Комментарии, как это принято в UNIX скриптах, начинаются с символа # и продолжаются до конца строки.
— Обычно в файле содержатся метки, идентифицирующие «цели» (targets, см. далее). Метка начинается с начала строки и оканчивается двоеточием :. После двоеточия могут идти так называемые зависимости, dependencies (сразу непонятно, что это такое и для чего надо, но дальше по ходу дела станет яснее). Обычно это имена файлов, либо ссылки на цели.
— Если в качестве dependencies указана последовательность целей, то они будут выполняться друг за другом.
— Если в качестве dependencies указаны имена файлов (обычно объектных), то утилита make может проверить — нужно их компилировать, или нет (мне непонятно, как она проверяет, однако это работает). Например, если компилируется несколько исходных файлов в несколько объектных, то некоторые исходные файлы не требуется каждый раз перекомпилировать заново, если они не изменялись. Для больших проектов это важно, поскольку существенно экономит время сборки программы (в нашем случае — получение двоичной прошивки для AVR).
— Для упрощения содержимого Makefile и для удобства используются переменные. Пример задания переменной (здесь в переменную записана командная строка вызова программатора):

JTAGICEII = c:/Program Files/AtmelAVR Tools/JTAGICEmkII/jtagiceii.exe -d $(DEVICE) -e -mi

После задания переменной на неё можно ссылаться так:

flash: main.hex
    $(JTAGICEII) -pf -if main.hex

— После задания в одной строке цели (цель: [зависимость1] .. [зависимостьN]) в последующих строках могут задаваться так называемые правила (rules). Каждое правило должно ОБЯЗАТЕЛЬНО начинаться с символа табуляции (таким способом make отслеживает правила и другие цели). Правило — это просто обычная команда (вызов компилятора, копирование, удаление и проч.), выполняемая шеллом.
— Переменные $@, $< , $^ называются автоматическими (automatic variables).

$@ заменяется на текущую цель.
$< которая заменяется на первую зависимость из списка.
$^ которая заменяется на список всех зависимостей с их каталогами.

Итак, при работе с проектом может потребоваться автоматизировать следующие часто повторяющиеся действия:

1. Компиляция программы (вводимая команда будет выглядеть как make hex).
2. Запись двоичного файла (make flash).
3. Запись бит «перемычек» (make fuse, для микроконтроллеров AVR это обычно 2 байта).
4. Полная запись микроконтроллера (и памяти и перемычек), выполняются действия и 2, и 3.
5. Очистка проекта — удаление всех промежуточных файлов, образующихся при компиляции, обычно объектных (make clean).
6. Стирание микроконтроллера (очистка flash и сброс перемычек в исходное состояние).
7. Бэкап проекта (make backup), будет выполняться цель 5 (clean), а затем архивирование файлов.
8. Вывод подсказки по возможным вариантам работы с проектом (просто make, при этом выводится подсказка по make hex, make flash, make fuse, make clean).

Для каждого такого действия 1..8 в Makefile прописывается блок команд, этот блок идентифицируется целью (target). Имя цели по сути является меткой, по которой переходит управление при обработке команды, переданной программе make. Для действия 1 это будет запуск компилятора (цель hex), для 2 — вызов программатора (цель flash) и т. д. Рассмотрим для примера ветку обработки цели hex (команда make hex) по шагам — см. рабочий Makefile [4] на примере проекта из библиотеки V-USB.

1. Пользователь вводит команду make hex.
2. Программа make открывает файл Makefile, ищет цель hex и начинает её обработку.
3. Для цели hex указана зависимость main.hex и ни одного правила (строка 131). Программа make ищет цель main.hex и начинает её обработку.
4. Для цели main.hex указана зависимость main.elf (строка 175) и несколько правил. Программа make ищет цель main.elf и начинает её обработку (правила цели main.hex будут отрабатываться после окончания обработки цели main.elf).
5. Для цели main.elf указаны зависимости usbdrv и $(OBJECTS) (строка 172), а также одно правило (вызов компилятора для получения файла main.elf). Программа make ищет цель usbdrv и начинает её обработку.
6. Для цели usbdrv не указано зависимостей (строка 169), только одно правило (копирование папки usbdrv в текущий каталог). Выполняется это правило, цель usbdrv завершена и происходит возврат к обработке цели main.elf.
7. Зависимости, входящие в переменную $(OBJECTS) (строка 172), не являются целями, это просто имена файлов, которые должны быть получены при компиляции. Поэтому сразу начинается выполняться правило, запускающее компилятор (строка 173). В результате те объектные файлы, которые должны быть скомпилированы, появляются в соответствующих каталогах, и появляется выходной файл main.elf (двоичный файл, который может использоваться в качестве входного для эмулятора или симулятора при отладке программы). Цель main.elf завершена, происходит возврат к обработке цели main.hex (строка 175).
8. Начинается обработка правил цели main.hex. Команда rm удаляет старые файлы прошивок flash и eeprom, avr-objcopy генерирует новую прошивку main.hex из файла main.elf, avr-size просто отображает информацию о размере секций в файле main.hex. Обработка цели main.hex закончена, происходит возврат к обработке цели hex.
9. Все зависимости цели hex обработаны, правил у цели hex нет. Работа make на этом завершается.

Есть утилиты-визарды для автоматической генерации файлов Makefile, например входящая в пакет WinAVR утилита MFile (запускается ярлычком C:\WinAVR-20080610\bin\wish84.exe mfile.tcl).

[Проблемы и их решение]

1. Makefile в среде Windows (Makefile работает при помощи пакета MSYS) завершается с ошибкой на команде xcopy, например (выполнение команды make backup):

/usr/bin/sh: -c: line 3: syntax error: unexpected end of file
make: *** [backup] Error 258

Проблема решается добавлением в начало Makefile строки «SHELL=cmd.exe». Вот пример рабочего Makefile, в котором эта ошибка устранена:

RAR = "c:/Program Files/WinRAR/WinRAR.exe"ARCHIVE = myproject.rarSHELL=cmd.exe
 help:
    @echo "This Makefile has no default rule. Use one of the following:"
    @echo "make backup .... backup project" 
 backup:
    $(RAR) a -r -dh -ep1 $(ARCHIVE) ../myproject
    mv $(ARCHIVE) c:/archive/ARMmyproject
    autoname /pattern:YYMMDDhhmmss c:/archive/ARM/myproject/$(ARCHIVE)
    xcopy /M /Y c:/archive/ARMmyproject*.* "\serverWORK"

2. Сетевые (UNC) пути необходимо заключать в двойные кавычки, иначе они будут неправильно переданы интерпретатору cmd.exe. Вот так:

xcopy /M /Y c:/archive/ARMmyproject*.* "serverWORK"

3. Отлаживать makefile удобно с помощью команды echo, выводя в консоль значения переменных. Пример:

[Как создать простейший makefile]

Предположим, есть три файла: program.c, program.h и header.h. Нужно их скомпилировать компилятором gcc. Как это проще всего осуществить?

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

HEADERS = program.h header.h
 
default: program.exe
 
program.o: program.c $(HEADERS)
    gcc -c program.c -o program.o
 
program.exe: program.o
    gcc program.o -o program.exe
 
clean:
    -rm -f *.o *.exe

Обратите внимание, что вместо пробелов в начале строк должны быть символы табуляции, иначе makefile работать не будет. Это нужно исправить, когда будете копировать этот текст как шаблон нового makefile.

Это простейший шаблон, и недостаток у него в том, что если нужно поддерживать большое количество файлов кода, то для каждого файла нужно добавлять новое правило. Этот недостаток можно устранить так:

HEADERS = program.h headers.h
OBJECTS = program.o
 
default: program.exe
 
%.o: %.c $(HEADERS)
    gcc -c $< -o $@
 
program: $(OBJECTS)
    gcc $(OBJECTS) -o $@
 
clean:
    -rm -f $(OBJECTS) *.exe

Это простейший пример, и для упрощения здесь опущены такие макросы, как $(CC), $(CFLAGS) и другие. Вот пример несложного makefile, который я часто использую для компилирования исходного кода на языке C.

TARGET = prog
LIBS = -lm
CC = gcc
CFLAGS = -g -Wall
 
.PHONY: clean all default
 
default: $(TARGET)
all: default
 
OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c))
HEADERS = $(wildcard *.h)
 
%.o: %.c $(HEADERS)
    $(CC) $(CFLAGS) -c $< -o $@
 
.PRECIOUS: $(TARGET) $(OBJECTS)
 
$(TARGET): $(OBJECTS)
    $(CC) $(OBJECTS) -Wall $(LIBS) -o $@
 
clean:
    -rm -f *.o
    -rm -f $(TARGET)

В этом makefile используются функции wildcard и patsubst утилиты make для автоматического подключения файлов *.c и *.h в текущей директории. Это означает, что когда Вы добавляете новые модули исходного кода в корневой каталог проекта, то Вам уже не надо модифицировать содержимое makefile. Если Вы хотите поменять имя генерируемого исполняемого файла, то Вам нужно просто поменять значение переменной TARGET. Для очистки проекта нужно в корневом каталоге проекта ввести команду make clean, а для компиляции make all или просто make.

В любом случае, ИМХО, не стоит использовать Autoconf/Automake. Конечно, они хорошо работают, но если Вы к ним привыкнете, то не сможете делать самых простых вещей, и Вам будет трудно разобраться в содержимом готовых makefile, которые поставляются вместе с большинством open-source проектов.

addprefix. Функция addprefix позволяет добавить префикс с каждой строке в последовательности строк. Пример:

OBJDIR = Objects
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
@echo $(OBJECTS)
# выведется trim.o main.o bmpfile.o
OBJ = $(addprefix $(OBJDIR)/, $(OBJECTS))
@echo $(OBJ)
# выведется Objects/trim.o Objects/main.o Objects/bmpfile.o

Утилита gccmakedep. В зависимости от количества заголовков и модулей проекта, и Ваших потребностей в разработке, рассмотрите возможность использования утилиты gccmakedep [7]. Эта программа анализирует текущий каталог, и добавляет в конец makefile зависимости от заголовков для каждого найденного файла *.c/*.cpp. Конечно, если у Вас в проекте только 2 или 3 файла, то такой функционал излишен, но если модулей и заголовков намного больше, то утилита gccmakedep может пригодиться.

[Ссылки]

1. Installing MSYS site:mingw.org.
2. Home of the MinGW and MSYS Projects site:mingw.org.
3. Перевод Makefile Mini HOWTO (make) site:opennet.ru.
4. Рабочий Makefile на примере AVR-USB (USB HID-устройство и консольная утилита для работы с ним).
5. GNU Make программа управления компиляцией site:linux.yaroslavl.ru (руководство по программе make на русском языке).
6. Эффективное использование GNU Make site:linux.org.ru.
7. gccmakedep site:x.org.

21.2. Сборочная утилита make. Linux: Полное руководство

21.2. Сборочная утилита make

Если вы уже собирали прикладную программу из исходных кодов, то обратили внимание на стандартную последовательность команд: make; make install.

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

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

Разберемся, как пишутся make-файлы. Общий формат make-файла выглядит так:

цель1: список_необходимых_файлов

последовательность_команд

...

цельN: список_необходимых_файлов

последовательностъ_команд

Цель — это метка для некоторой последовательности команд (например, install) или результирующий файл, который нужно «построить» — скомпилировать или скомпоновать.

Цели должны отделяться друг от друга хотя бы одной пустой строкой. Список необходимых файлов — это перечень файлов или других целей, которые нужны для достижения данной цели; он может быть и пустым.

Последовательность команд — это команды, которые нужно выполнить для достижения цели. Последовательность команд должна отделяться от начала строки символом табуляции, иначе вы получите ошибку «missing separator» (нет разделителя).

Make-файл может содержать комментарии — они начинаются символом #.

В make-файлах вы можете использовать макроопределения:

CC=gcc

PATH=/usr/include /usr/src/linux/include

MODFLAGS:= -O3 -Wall -DLINUX -I$(PATH)

...

$(CC) $(MODFLAGS) -c proga.c

Чтобы обратиться к макроопределению в команде или в другом макроопределении, нужно использовать конструкцию $(имя). Макроопределение может включать в себя другое, ранее определенное, макроопределение.

Формат запуска утилиты make:

make [-f файл] [ключи] [цель]

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

Примечание

Если нужно избежать пересборки какого-то из файлов проекта, то можно искусственно «омолодить» его командой touch, которая присвоит ему в качестве времени последней модификации текущее время. Если нужно, наоборот, принудительно пересобрать цель, то следует «омолодить» один из файлов, от которых она зависит.

Работа программы make заканчивается, когда достигнута цель, указанная в командной строке. Обычно это цель all, собирающая все результирующие файлы проекта. Другими распространенными целями являются install (установить собранную программу) и clean (удалить ненужные файлы, созданные в процессе сборки).

В листинге 21.2 представлен make-файл, собирающий небольшой проект из двух программ client и server, каждая из которых компилируется из одного файла исходного кода.

Листинг 21.2. Примерный make-файл

CC=gcc

CFLAGS=-O

all: client server

client: client.с

$(CC) client.с -о client

server: server.с

$(CC) server.с -о server

Обычно при вызове утилиты make не нужно задавать никаких ключей. Но иногда использование ключей бывает очень кстати (таблица 21.1).

Ключи команды make Таблица 21.1

КлючНазначение
-C каталогПерейти в указанный каталог перед началом работы
-dВывод отладочной информации
-eПриоритет переменным окружения. Если у нас установлена переменная окружения CC и в Makefile есть переменная с таким же именем, то будет использована переменная окружения
-f файлИспользовать указанный файл вместо Makefile
-iИгнорировать ошибки компилятора
-I каталогВ указанном каталоге будет производиться поиск файлов, включаемых в Makefile
-j nЗапускать не более n команд одновременно
-kПродолжить работу после ошибки, если это возможно
-nВывести команды, которые должны были выполниться, но не выполнять их
-о файлПропустить данный файл, даже если в Makefile указано, что он должен быть создан заново
-rНе использовать встроенные правила
-sНе выводить команды перед их выполнением
-wВывод текущего каталога до и после выполнения команды
Данный текст является ознакомительным фрагментом.

Читать книгу целиком

Поделитесь на страничке

Makefile — создаем и настраиваем. Шаг№3.

Обновлено 11.02.15. Всем привет. В предыдущей статье №1  мы описали ту литературу и программы, которые нам понадобится для работы с микроконтроллерами AVR. Так вот, что бы показать первый пример написания и заливки программы в “камень”, мы начнем работу с программы WinAVR. Если Вы откроете какие-нибудь исходники для контроллеров, то заметите, что в папке помимо содержания в себе файлов с расширением .с и .h также содержат еще файл Makefile (рис. ниже). Так вот нас пока интересует только этот файл. 

Что же это за файлЭто файл который содержит в себе инструкции, для программы (утилиты) make, которая в свою очередь автоматизирует компиляцию программы.

Давайте попробуем его сами создать. В наборе инструментальных средств WinAVR содержится программа  Mfile, которая является простым Makefile-генератором для компилятора AVR-GCC. Также в дополнение можно посмотреть пример настройки в файле мониторинг памяти. Запускаем данное приложение (рис. ниже)

При запуске приложения, код уже сгенерированный! Все что нам надо это настроить. Сверху на рисунке Вы видите две вкладки, нас интересует Makefile. Переходим и настраиваем.

В данной вкладке как видите можно выбрать тип контроллера, программатор, порты т.д. Далее переходим к вкладке File –> Save as. Файл необходимо сохранить в папку в которой будут исходники программы. Данный файл можно редактировать вручную. Для этого запускаете редактор Notepad  и открываете файл Makefile. Например нам необходимо изменить частоту на которой будет работать МК, ищем следующие строки

……………………………………
#         F_CPU = 18432000
#         F_CPU = 20000000
F_CPU = 8000000

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

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

—  вписать имя МК
MY_MCU = attiny13    #здесь пишем свое название МК
— частоту кварцевого резонатора в Гц
MY_F_CPU = 1000000
— название главного исходного файла, только имя без расширения
MY_TARGET = my_test
— порт для подключения программатора, выбираем свой, хотя если будете заливать через AVRStudio, то нет смысла менять
MY_PROGRAMMER_PORT = lpt1
— то же самое и для
MY_AVRDUDE_PROGRAMMER = stk200 # Используемый программатор (для AVRDUDE) Можно оставить как есть, если используете сторонюю программу для заливки файла.
-если есть еще файлы с расширением .с то подключаем следующим образом, например вот так
# List C source files here. (C dependencies are automatically generated.)
SRC =$(TARGET).c Functions.c Functions_1.c Functions_2.c  files/crc.c
— В разделе Optimization level проверяем уровень оптимизации. Можно оставить все как есть (s — оптимизирует выходной файл по размеру).
# Optimization level, can be [0, 1, 2, 3, s].
# 0 = turn off optimization. s = optimize for size.
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s

Выше приведенные строкии, это  есть основные  базовые настройки. После корректировки файла, если это требуется, сохраняем все изменения. Теперь мы можем приступать к написанию первой программы, чем мы и займемся в следующей статье №3. Опишем создание программы в Programmers Notepad (входящей в WinAVR ), компилирование, заливку через AVRStudio. На этом все. Всем пока.

Просмотрено 6856 раз.

Makefile — Национальная библиотека им. Н. Э. Баумана

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 23:02, 23 мая 2018.

Makefile – файл, содержащий набор инструкций для программы make. Программа make с помощью этого файла позволяет автоматизировать процесс компиляции программы и выполнять при этом различные действия. При запуске make по умолчанию ищет файл Makefile в текущей папке и обрабатывает его (можно изменить это поведение, чтобы открывался другой файл с набором инструкций, если ввести команду make -f другое_имя_makefile).

Обзор

Чаще всего, Makefile показывает как компилировать и компоновать программу. Используя C / C ++ в качестве примера, когда исходный файл C / C ++ изменяется, он должен быть перекомпилирован. Если файл заголовка был изменен, каждый исходный файл C / C ++, содержащий заголовочный файл, должен быть перекомпилирован для обеспечения безопасности. Каждая компиляция создает объектный файл, соответствующий исходному файлу. Наконец, если какой-либо исходный файл был перекомпилирован, все объектные файлы, будь то новые или сохраненные из предыдущих компиляций, должны быть связаны друг с другом для создания новой исполняемой программы. [Источник 1] Эти инструкции с их зависимостями указаны в make- файле, Если ни один из файлов, которые являются предварительными условиями, не был изменен с момента компиляции программы, никаких действий не происходит. Для крупных программных проектов использование Make-файлов может существенно сократить время сборки, если изменилось только несколько исходных файлов.

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

Комментарии, как это принято в UNIX скриптах, начинаются с символа # и продолжаются до конца строки.
Обычно в файле содержатся метки, идентифицирующие «цели» (targets, см. далее). Метка начинается с начала строки и оканчивается двоеточием :. После двоеточия могут идти так называемые зависимости, dependencies (сразу непонятно, что это такое и для чего надо, но дальше по ходу дела станет яснее). Обычно это имена файлов, либо ссылки на цели.
Если в качестве dependencies указана последовательность целей, то они будут выполняться друг за другом.
Если в качестве dependencies указаны имена файлов (обычно объектных), то утилита make может проверить — нужно их компилировать, или нет (мне непонятно, как она проверяет, однако это работает). Например, если компилируется несколько исходных файлов в несколько объектных, то некоторые исходные файлы не требуется каждый раз перекомпилировать заново, если они не изменялись. Для больших проектов это важно, поскольку существенно экономит время сборки программы (в нашем случае — получение двоичной прошивки для AVR).
Для упрощения содержимого Makefile и для удобства используются переменные.

Пример задания переменной (здесь в переменную записана командная строка вызова программатора):

 JTAGICEII = c:/Program Files/AtmelAVR Tools/JTAGICEmkII/jtagiceii.exe -d $(DEVICE) -e -mi

После задания переменной на неё можно ссылаться так:

  flash: main.hex
    $(JTAGICEII) -pf -if main.hex
  • После задания в одной строке цели (цель: [зависимость1] .. [зависимостьN]) в последующих строках могут задаваться так называемые правила (rules). Каждое правило должно ОБЯЗАТЕЛЬНО начинаться с символа табуляции (таким способом make отслеживает правила и другие цели). Правило — это просто обычная команда (вызов компилятора, копирование, удаление и проч.), выполняемая шеллом.
  • Переменные $@, $< , $^ называются автоматическими (automatic variables).

$@ заменяется на текущую цель.
$< которая заменяется на первую зависимость из списка.
$^ которая заменяется на список всех зависимостей с их каталогами.

Итак, при работе с проектом может потребоваться автоматизировать следующие часто повторяющиеся действия:
1. Компиляция программы (вводимая команда будет выглядеть как make hex).
2. Запись двоичного файла (make flash).
3. Запись бит «перемычек» (make fuse, для микроконтроллеров AVR это обычно 2 байта).
4. Полная запись микроконтроллера (и памяти и перемычек), выполняются действия и 2, и 3.
5. Очистка проекта — удаление всех промежуточных файлов, образующихся при компиляции, обычно объектных (make clean).
6. Стирание микроконтроллера (очистка flash и сброс перемычек в исходное состояние).
7. Бэкап проекта (make backup), будет выполняться цель 5 (clean), а затем архивирование файлов.
8. Вывод подсказки по возможным вариантам работы с проектом (просто make, при этом выводится подсказка по make hex, make flash, make fuse, make clean).


Для каждого такого действия 1..8 в Makefile прописывается блок команд, этот блок идентифицируется целью (target). Имя цели по сути является меткой, по которой переходит управление при обработке команды, переданной программе make. Для действия 1 это будет запуск компилятора (цель hex), для 2 — вызов программатора (цель flash) и т. д.

Рассмотрим для примера ветку обработки цели hex (команда make hex) по шагам:

  • Пользователь вводит команду make hex.
  • Программа make открывает файл Makefile, ищет цель hex и начинает её обработку.
  • Для цели hex указана зависимость main.hex и ни одного правила (строка 131). Программа make ищет цель main.hex и начинает её обработку.
  • Для цели main.hex указана зависимость main.elf (строка 175) и несколько правил. Программа make ищет цель main.elf и начинает её обработку (правила цели main.hex будут отрабатываться после окончания обработки цели main.elf).
  • Для цели main.elf указаны зависимости usbdrv и $(OBJECTS) (строка 172), а также одно правило (вызов компилятора для получения файла main.elf). Программа make ищет цель usbdrv и начинает её обработку.
  • Для цели usbdrv не указано зависимостей (строка 169), только одно правило (копирование папки usbdrv в текущий каталог). Выполняется это правило, цель usbdrv завершена и происходит возврат к обработке цели main.elf.
  • Зависимости, входящие в переменную $(OBJECTS) (строка 172), не являются целями, это просто имена файлов, которые должны быть получены при компиляции. Поэтому сразу начинается выполняться правило, запускающее компилятор (строка 173). В результате те объектные файлы, которые должны быть скомпилированы, появляются в соответствующих каталогах, и появляется выходной файл main.elf (двоичный файл, который может использоваться в качестве входного для эмулятора или симулятора при отладке программы). Цель main.elf завершена, происходит возврат к обработке цели main.hex (строка 175).
  • Начинается обработка правил цели main.hex. Команда rm удаляет старые файлы прошивок flash и eeprom, avr-objcopy генерирует новую прошивку main.hex из файла main.elf, avr-size просто отображает информацию о размере секций в файле main.hex. Обработка цели main.hex закончена, происходит возврат к обработке цели hex.
  • Все зависимости цели hex обработаны, правил у цели hex нет. Работа make на этом завершается.

Проблемы и их решения

1. Makefile в среде Windows (Makefile работает при помощи пакета MSYS) завершается с ошибкой на команде xcopy, например (выполнение команды make backup):

 /usr/bin/sh: -c: line 3: syntax error: unexpected end of file
make: *** [backup] Error 258

Проблема решается добавлением в начало Makefile строки «SHELL=cmd.exe». Вот пример рабочего Makefile, в котором эта ошибка устранена:

 RAR = "c:/Program Files/WinRAR/WinRAR.exe"ARCHIVE = myproject.rarSHELL=cmd.exe
 help:
    @echo "This Makefile has no default rule. Use one of the following:"
    @echo "make backup .... backup project" 
 backup:
    $(RAR) a -r -dh -ep1 $(ARCHIVE) ../myproject
    mv $(ARCHIVE) c:/archive/ARMmyproject
    autoname /pattern:YYMMDDhhmmss c:/archive/ARM/myproject/$(ARCHIVE)
    xcopy /M /Y c:/archive/ARMmyproject*.* "\serverWORK"

2. Сетевые (UNC) пути необходимо заключать в двойные кавычки, иначе они будут неправильно переданы интерпретатору cmd.exe. Вот так:

 xcopy /M /Y c:/archive/ARMmyproject*.* "serverWORK"

3. Отлаживать makefile удобно с помощью команды echo, выводя в консоль значения переменных. Пример:

Операционная система

Система make родилась в мире UNIX и постепенно переползла и на Windows вместе с портами GNU-компиляторов (gcc). Если открыть пример готового Makefile, то он поначалу может показаться полной абракадаброй, поскольку содержимое файла подчиняется заранее заданному набору правил, которые необходимо предварительно изучить. Для простоты рассмотрим пример работы с проектом AVR и компилятором gcc.

Unix-подобный

Makefiles происходит от Unix-подобных систем и по-прежнему является основным механизмом создания программного обеспечения в таких средах.

Microsoft Windows

Windows поддерживает изменение make-файлов с помощью утилиты nmake . Стандартные Unix, такие как make-файлы, могут быть выполнены в Windows в среде Cygwin.

Содержание

Makefiles содержит пять видов вещей: явные правила , неявные правила , определения переменных , директивы и комментарии .

  • Явное правило говорит , когда и как переделать один или несколько файлов, называемых целями правила. В нем перечислены другие файлы, от которых зависят целевые объекты, называемые предпосылками цели, а также могут давать рецепт для создания или обновления целей.
  • Неявное правило говорит , когда и как переделать класс файлов на основе их имен. В нем описывается, как цель может зависеть от файла с именем, близким к целевому, и дает рецепт для создания или обновления такой цели.
  • Определение переменной — это строка, которая указывает значение текстовой строки для переменной, которая может быть заменена в тексте позже.
  • Директива является инструкцией для макияжа , чтобы сделать что — то особенное, читая Makefile , такие как чтение другого Makefile.
  • ‘#’ в строке файла makefile начинает комментарий . Он и остальная часть строки игнорируются.

Правила

Простой make-файл состоит из «правил» со следующей из форм [Источник 2]:

target … : prerequisites …
        recipe
        …
        …
или
target: dependencies
    system command(s)

Зависимость или «prerequisites» (также называемая предварительным условием) — это файл, который используется как вход для создания цели. Необходимым условием является файл, который используется как вход для создания цели.

Цель(target) часто зависит от нескольких файлов.Однако правило, определяющее рецепт для цели, не обязательно имеет какие-либо предварительные условия. Например, правило, содержащее команду delete, связанную с целевым «чистым», не имеет предварительных условий.

Рецепт (recipe)- это действие , которое make выполняет. Рецепт может иметь более одной команды, либо в одной строке, либо в каждой строке. Обратите внимание: вам нужно поместить символ табуляции в начале каждой линии рецептов. Это неясность, на которой попадаются неосторожные. Если вы предпочитаете префикс своих рецептов символом, отличным от табуляции, вы можете установить RECIPEPREFIX переменную на альтернативный символ.

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

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

Условные части Make-файлов

Пример условного

В следующем примере условного выражения makeиспользуется один набор библиотек, если CCпеременная ‘НКУ’и другой набор библиотек в противном случае. Он работает, контролируя, какая из двух линий рецепта будет использоваться для правила. В результате получается, что ‘CC = GCC’как аргумент для makeизменений не только того, какой компилятор используется, но и какие библиотеки связаны.

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
else
        $(CC) -o foo $(objects) $(normal_libs)
endif

В этом условном случае используются три директивы: один ifeq, один else и один endif.

ifeq Директива начинается условная, и определяет условие. Он содержит два аргумента, разделенных запятой и окруженных круглыми скобками. Подстановка переменных выполняется по обоим аргументам, а затем их сравнивают. Строки файла makefile, следующие за ними, ifeqвыполняются, если совпадают два аргумента; в противном случае они игнорируются.

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

endif Директива заканчивается условное. Каждое условие должно заканчиваться на endif. Далее следует текст безусловного make-файла.

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

Когда переменная CCимеет значение ‘НКУ’, приведенный выше пример имеет такой эффект:

foo: $(objects)
        $(CC) -o foo $(objects) $(libs_for_gcc)

Когда переменная CCимеет любое другое значение, эффект таков:

oo: $(objects)
        $(CC) -o foo $(objects) $(normal_libs)

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

libs_for_gcc = -lgnu
normal_libs =

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif

foo: $(objects)
        $(CC) -o foo $(objects) $(libs)

Синтаксис условий

Синтаксис простого условного с no else заключается в следующем:

conditional-directive
text-if-true
endif

Текста , если-правда могут быть любые строки текста, которые должны быть рассмотрены в рамках Makefile , если условие истинно. Если условие ложно, вместо этого текст не используется.

Синтаксис сложного условного выражения следующий:

conditional-directive
text-if-true
else
text-if-false
endif

или:

conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif

При необходимости может быть столько « else условно-директивных » положений. Когда данное условие истинно, используется text-if-true, и никакое другое предложение не используется; если условие не истинно, используется text-if-false . Текста , если правда и текст-невыполненный- может быть любым количеством строк текста.

Синтаксис условной-директивы одинаковый, является ли условное просто или сложным; после elseили нет. Существует четыре разных директивы, которые проверяют разные условия. Вот их таблица:

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"

Разверните все ссылки переменных в arg1 и arg2 и сравните их. Если они идентичны, текст-if-true эффективен; в противном случае текст-if-false , если таковой имеется, эффективен.

Часто вы хотите проверить, имеет ли переменная непустое значение. Когда значение получается из сложных разложений переменных и функций, разложения, которые вы считаете пустыми, могут фактически содержать пробельные символы и, следовательно, не считаются пустыми. Однако вы можете использовать эту stripфункцию (см. Текстовые функции ), чтобы избежать интерпретации пробела как непустого значения. Например:

ifeq ($ (strip $ (foo)),)
 text-if-empty
ENDIF

будет оценивать text-if-empty, даже если расширение $(foo)содержит пробельные символы.

ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"

Разверните все ссылки переменных в arg1 и arg2 и сравните их. Если они разные, текст-if-true эффективен; в противном случае текст-if-false , если таковой имеется, эффективен.

ifdef variable-name ifdef Форма принимает имя переменной в качестве аргумента, а не ссылка на переменную. Если значение этой переменной имеет непустое значение, текст-if-true действует; в противном случае текст-if-false , если таковой имеется, эффективен. Переменные, которые никогда не были определены, имеют пустое значение. Текстовое имя переменной расширяется, поэтому это может быть переменная или функция, которая расширяется до имени переменной. Например:

bar = true
foo = bar
ifdef $(foo)
frobozz = yes
endif

Ссылка на переменную $(foo)расширяется, давая bar, что считается именем переменной. Переменная barне разворачивается, но ее значение проверяется, чтобы определить, не является ли она непустой.

Обратите внимание, что ifdefпроверяет только, имеет ли переменная значение. Он не расширяет переменную, чтобы увидеть, не является ли это значение непустым. Следовательно, тесты с использованием ifdefreturn true для всех определений, за исключением тех, которые похожи foo =. Чтобы проверить пустое значение, используйте ifeq ($(foo),). Например,

bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endifF

наборы ‘frobozz’to’да’, в то время как:

foo =
ifdef foo
frobozz = yes
else
frobozz = no
endiF

наборы ‘frobozz’to’нет».

ifndef variable-name Если переменная variable-name имеет пустое значение, текст-if-true эффективен; в противном случае текст-if-false , если таковой имеется, эффективен. Правила расширения и тестирования имени переменной идентичны ifdefдирективе.

Дополнительные пробелы разрешены и игнорируются в начале строки условной директивы, но вкладка не допускается. (Если строка начинается с вкладки, она будет считаться частью рецепта правила). Помимо этого, дополнительные пробелы или вкладки могут быть вставлены без какого-либо эффекта, кроме как внутри имени директивы, так и внутри аргумента. Комментарий, начинающийся с ‘#’может появиться в конце строки.

Остальные две директивы, которые играют роль в условном выражении, являются else и endif. Каждая из этих директив написана как одно слово без аргументов. Дополнительные пробелы разрешены и игнорируются в начале строки, а также пробелы или вкладки в конце. Комментарий, начинающийся с ‘#’может появиться в конце строки.

Условные обозначения влияют на makeиспользуемые строки make-файла . Если условие истинно, makeчитает строки текста-if-true как часть файла makefile; если условие ложно, полностью makeигнорирует эти строки. Из этого следует, что синтаксические единицы make-файла, такие как правила, могут безопасно разделяться в начале или в конце условного.

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

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

Условные обозначения,которые проверяют флаги

Вы можете написать условие, которое проверяет makeфлаги команд, такие как ‘-t’, используя переменную MAKEFLAGSвместе с findstringфункцией (см. Функции для подстановки и анализа строк ). Это полезно, когда touchэтого недостаточно, чтобы обновить файл.

findstringФункция определяет , будет ли отображаться одна строка в качестве подстроки другого. Если вы хотите протестировать «-t’flag, use’T’как первая строка и значение MAKEFLAGSкак другое.

Например, вот как организовать использование ‘ranlib -t’для окончательной маркировки архивного файла:

archive.a: …
ifneq (,$(findstring t,$(MAKEFLAGS)))
        +touch archive.a
        +ranlib -t archive.a
else
        ranlib archive.a
endif

«+» префикс отмечает эти строки рецептов как «рекурсивные», так что они будут выполнены, несмотря на использование’-t’.

Выполнение

Makefile выполняется с make командой, например make [options] [target1 target2 ...]. По умолчанию, когда make ищет make-файл, если имя файла makefile не было включено в качестве параметра, он пытается использовать следующие имена: makefile и Makefile.

Примеры

1. Вот простой make-файл, который описывает способ, которым исполняемый файл называется редактированием, зависит от четырех объектных файлов, которые, в свою очередь, зависят от четырех исходных файлов C и двух файлов заголовков.
edit: main.o kbd.o command.o display.o 
    cc -o edit main.o kbd.o command.o display.o
     
main.o: main.c defs.h
    cc -c main.c
kbd.o: kbd.c defs.h command.h
    cc -c kbd.c
command.o: command.c defs.h command.h
    cc -c command.c
display.o: display.c defs.h
    cc -c display.c

clean:
     rm edit main.o kbd.o command.o display.o
Чтобы использовать этот make-файл для создания исполняемого файла с именем edit , введите make. Чтобы использовать этот файл makefile для удаления исполняемого файла и всех объектных файлов из каталога, введите make clean.

2. Использование действий по умолчанию.

#default target - file edit 
edit : main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o
         cc -o edit main.o kbd.o command.o display.o \ 
                    insert.o search.o files.o utils.o 

main.o : main.c defs.h
        cc -c main.c 
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h 
        cc -c command.c 
display.o : display.c defs.h buffer.h 
        cc -c display.c
insert.o : insert.c defs.h buffer.h 
        cc -c insert.c 
search.o : search.c defs.h buffer.h 
        cc -c search.c 
files.o : files.c defs.h buffer.h command.h 
        cc -c files.c 
utils.o : utils.c defs.h 
        cc -c utils.c
clean : 
       rm edit main.o kbd.o command.o display.o \ 
          insert.o search.o files.o utils.o

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

3. Для сокращения записи можно использовать переменные и действия по умолчанию (неявные правила)

objects = main.o kbd.o command.o display.o \ 
          insert.o search.o files.o utils.o 

edit : $(objects) 
        cc -o edit $(objects) 
main.o : defs.h 
kbd.o : defs.h command.h 
command.o : defs.h command.h 
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h 
.PHONY : clean 
clean :
        -rm edit $(objects)

Переменная objects позволила использовать единожды написанный список объектных файлов, а для объектных файлов в make встроено неявное правило по умолчанию

file.c: file.o   cc -c file.c

4. Специальная цель .PHONY является встроенной в make и определяет свои зависимости как цели-имена, которым нет соответствия в виде файлов. Если данное правило пропустить, то создание в текущем каталоге файла с именем clean заблокирует выполнение make clean. Использование правил по умолчанию позволяет изменить стиль записей зависимостей:

objects = main.o kbd.o command.o display.o \ 
          insert.o search.o files.o utils.o 

edit : $(objects) 
       cc -o edit $(objects) 

$(objects) : defs.h 
kbd.o command.o files.o : command.h 
display.o insert.o search.o files.o : buffer.h

Данная запись указывает, что все объектные файлы зависят от заголовочного файла defs.h, но для некоторых из них проверяются дополнительные зависимости.

Источники

Ссылки

GNU Makefile-универсальный Makefile для нескольких целей

В настоящее время я разрабатываю небольшой пакет C++ с небольшими примерами кода для учебных целей. Мне удалось написать Makefile, как показано ниже, который будет компилировать все *.cpp файлы в *.o файлы связывают их с исполняемыми файлами:

CC=gcc
CFLAGS=-g
LDFLAGS= -lstdc++
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

#a generic rule to create .o files from .cpp files (e.g. example1.cpp -> example1.o)
%.o: %.cpp
    $(SILENT) $(ECHO) "--- Compiling $< ---"
    $(SILENT) $(CC) -c $(CFLAGS) $<

#define targets and their dependencies
example1: example1.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

example2: example2.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

example3: example3.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

clean:
    $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
    $(SILENT) rm -f *.o 
    $(SILENT) rm -f $(MODULES)

.PHONY: clean

Пока все хорошо, это хорошо работает. Это займет example1.cpp, example2.cpp и example3.cpp и скомпилировать / связать его с 3 исполняемых файлов «example1 example2 example3».

Но поскольку каждый исполняемый файл имеет то же имя, что и объект (например, «example1.o» будет связан с исполняемым «example1»), мне интересно, есть ли способ использовать общее правило.

Я попробовал несколько вещей, таких как:

%: %.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

Насколько я понимаю, это правило должно принимать все объектные файлы и создавать исполняемый файл с тем же именем, что и объектный файл, но я не мог заставить его работать! У кого-нибудь есть подсказка, как этого добиться?

c++ makefile rules targets Поделиться Источник Marcel Stüttgen     12 декабря 2014 в 10:50

2 Ответа



2

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

Вы можете отменить правило , переопределив его пустым правилом:

%: %.cpp

Или вы можете полностью удалить свои правила и позволить этому правилу делать правильные вещи. Неявное правило имеет рецепт вдоль линий

$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@

таким образом, вы можете использовать эти переменные для выбора компилятора, указывающего флаги для препроцессора, компилятора и компоновщика. Например, вы можете изменить CXX , чтобы использовать компилятор для неправильного языка , и отменить повреждение, добавив LDFLAGS=-lstdc++, как это делает ваш makefile, если вы действительно хотите.

Поделиться Mike Seymour     12 декабря 2014 в 11:11



0

Вот решение этой проблемы:

В приведенных выше примерах я не использую переменную $(LDLIBS), которая используется встроенным правилом по умолчанию. Если я изменю свой Makefile, чтобы использовать $(LDLIBS) вместо $(LDFLAGS), все в порядке!

Решение #1: (встроенное правило)

CC=gcc
CFLAGS=-g -fopenmp
LDFLAGS= 
LDLIBS = -lstdc++ -lgomp
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

clean:
    $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
    $(SILENT) rm -f *.o 
    $(SILENT) rm -f $(MODULES)

.PHONY: clean

Решение #2: (пользовательские правила)

CC=gcc
CFLAGS=-g -fopenmp
LDFLAGS= 
LDLIBS = -lstdc++ -lgomp
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

#disable built-in rule
%: %.cpp

#rule for *.cpp -> *.o
%.o: %.cpp
    $(SILENT) $(ECHO) "--- Compiling $< ---"
    $(SILENT) $(CC) -c $(CFLAGS) $(LDFLAGS) $<

#rule for object -> exectutable
%: %.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ $(LDLIBS) -o $@ 

clean:
    $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
    $(SILENT) rm -f *.o 
    $(SILENT) rm -f $(MODULES)

.PHONY: clean

Поделиться Marcel Stüttgen     16 декабря 2014 в 09:59


Похожие вопросы:


создание Makefile для нескольких целей

я создал Makefile, который я бы изменил так, что он будет генерировать более одной цели, когда я запускаю make . В моей программе я использую self predefined macros (например, TIME , REG и _DEBUG ),…


Как зависимости работают на gnu Makefile?

Как зависимости работают на gnu Makefile ? Я хочу создать Makefile, который: — придется применить некоторые патчи (в нашем, например, применять main.patch на main.c) — создайте программу под…


Может ли GNU make иметь цели, которые зависят от конкуренции целей из других файлов Makefile?

У меня есть несколько каталогов, представляющих подразделы проекта, каждый со своим собственным файлом Makefile. Я хочу создать мастер-файл Makefile с целями для каждого из этих подразделов, каждая…


Преобразование GNU makefile одного пользователя в другого пользователя

У меня есть GNU makefile, который компилирует набор исходных кодов в моей системе, и я хочу передать исходный код другому человеку (другой системе), так будет ли тот же GNU makefile функционировать…


Оптимизация Makefile для нескольких целей

Поэтому в рамках нескольких инструментов, которые я делаю в C++, я хотел бы иметь возможность тестировать каждый инструмент индивидуально. Есть несколько инструментов с несколькими файлами…


Занижение очень минимального GNU makefile

Я пытаюсь отредактировать файл makefile, я уже построил свой собственный, но я изо всех сил пытаюсь понять этот очень минимальный пример, который явно использует соглашения, о которых я не знаю. Я…


90000 Makefile Tutorial by Example 90001 90002 This tutorial is based on the topics covered in the GNU Make book. This tutorial teaches mainly through examples in order to help quickly explain the concepts in the book. 90003 90002 90005 Makefile Syntax 90006 90007 A Makefile consists of a set of 90008 rules 90009. A rule generally looks like this: 90003 90011 90012 targets: prerequisities command command command 90013 90014 90015 90016 The 90008 targets 90009 are file names, seperated by spaces.Typically, there is only one per rule. 90019 90016 The 90008 commands 90009 are a series of steps typically used to make the target (s). These 90008 need to start with a tab character 90009, not spaces. 90019 90016 The 90008 prerequisites 90009 are also file names, seperated by spaces. These files need to exist before the commands for the target are run. 90019 90030 90002 90005 Make Overview 90006 90007 The main use of Make is to list out a set of directions to compile some c or c ++ files, although it can solve other similar problems.The user gives Make some 90008 goal 90009, say «generate the file hello». The Makefile specifies how to make this file. Here’s an example: 90003 90011 90039 # Since the blah target is first, it is the default target and will be run when we run «make» blah: blah.o cc blah.o -o blah blah.o: blah.c cc -c blah.c -o blah.o blah.c: echo «int main () {return 0;}»> blah.c clean: rm -f blah.o blah.c blah 90013 90014 90002 90005 Running the Examples 90006 90007 I made a quick video to show how to run these examples.You’ll need a terminal and «make» installed. For each example, put the contents in a file called 90046 Makefile 90013, and in that directory run the command 90046 make 90013. Here is the output of running the above example: 90003 90011 90046 $ make echo «int main () {return 0;}»> blah.c cc -c blah.c -o blah.o cc blah.o -o blah 90013 90014 90002 Some examples, like the above, have a target called «clean». Run it via 90046 make clean 90013 to delete the files that 90046 make 90013 generated: 90003 90011 90046 $ make clean rm -f blah.o blah.c blah 90013 90014 90002 90005 Simple Examples 90006 90007 This makefile has a single target, called 90046 some_file 90013. The default target is the first target, so in this case 90046 some_file 90013 will run. 90003 90011 90039 some_file: echo «This line will always print» 90013 90014 90002 This file will make 90046 some_file 90013 the first time, and the second time notice it’s already made, resulting in 90046 make: ‘some_file’ is up to date. 90013 90003 90011 90039 some_file: echo «This line will only print once» touch some_file 90013 90014 90002 Alternative syntax: same line 90003 90011 90039 some_file:; touch some_file 90013 90014 90002 The backslash ( «\») character gives us the ability to use multiple lines when the commands are too long 90003 90011 90039 some_file: echo This line is too long, so \ it is broken up into multiple lines 90013 90014 90002 Here, the target 90046 some_file 90013 «depends» on 90046 other_file 90013.When we run 90046 make 90013, the default target (90046 some_file 90013, since it’s first) will get called. It will first look at its list of 90008 dependencies 90009, and if any of them are older, it will first run the targets for those dependencies, and then run itself. The second time this is run, neither target will run because both targets exist. 90003 90011 90039 some_file: other_file echo «This will run second, because it depends on other_file» touch some_file other_file: echo «This will run first» touch other_file 90013 90014 90002 This will always make both targets, because 90046 some_file 90013 depends on other_file, which is never created.90003 90011 90039 some_file: other_file touch some_file other_file: echo «nothing» 90013 90014 90002 «clean» is often used as a target that removes the output of other targets. 90003 90011 90039 some_file: clean touch some_file clean: rm -f some_file 90013 90014 90002 Adding 90046 .PHONY 90013 to a target will prevent make from confusing the phony target with a file name. In this example, if the file «clean» is created, make clean will still be run. 90046 .PHONY 90013 is great to use, but I’ll skip it in the rest of the examples for simplicity.90003 90011 90039 some_file: touch some_file touch clean .PHONY: clean clean: rm -f some_file rm -f clean 90013 90014 90002 90005 Variables (Section 2.4) 90006 90007 Variables can only be strings. Here’s an example of using them: 90003 90011 90039 files = file1 file2 some_file: $ (files) echo «Look at this variable:» $ (files) touch some_file file1: touch file1 file2: touch file2 clean: rm -f file1 file2 some_file 90013 90014 90002 90005 Magic Implicit Commands (Section 2.5) 90006 90007 Probably one of the most confusing parts about Make is the hidden coupling between Make and GCC. Make was largely made for GCC, and so makes compiling C / C ++ programs «easy». 90003 90011 90039 # Implicit command of: «cc blah.o -o blah» # Note: Do not put a comment inside of the blah.o rule; the implicit rule will not run! blah: # Implicit command of: «cc -c blah.c -o blah.o» blah.o: blah.c: echo «int main () {return 0;}»> blah.c clean: rm -f blah.o blah blah.c 90013 90014 90002 90005 Using Wildcard Characters (Section 4.2) 90006 90007 We can use wildcards in the target, prerequisites, or commands. 90007 Valid wildcards are 90046 *,?, […] 90013 90003 90011 90039 some_file: * .c # Create the binary * .C: touch f1.c touch f2.c clean: rm -f * .c 90013 90014 90002 90005 The Wildcard Function (Section 4.2.3) 90006 90007 We CAN NOT use wildcards in other places, like variable declarations or function arguments 90007 Use the wildcard function instead. 90003 90011 90039 wrong = * .o # Wrong objects: = $ (wildcard *.c) # Right some_binary: touch f1.c touch f2.c echo $ (wrong) echo $ (objects) clean: rm -f * .c 90013 90014 90002 90005 The vpath Directive (Section 4.3.2) 90006 90007 Use vpath to specify where some set of prerequisites exist. The format is 90046 vpath 90013 90007 90046 90013 can have a 90046% 90013, which matches any zero or more characters. 90007 You can also do this globallyish with the variable VPATH 90003 90011 90039 vpath%.h ../headers ../other-directory some_binary: ../headers blah.h touch some_binary ../headers: mkdir ../headers blah.h: touch ../headers/blah.h clean: rm -rf ../headers rm -f some_binary 90013 90014 90002 90005 The all target (Section 4.4) 90006 90007 Making multiple targets and you want all of them to run? Make a 90046 all 90013 target and designate it as 90046 .PHONY 90013 90003 90011 90039 all: one two three .PHONY: all one: touch one two: touch two three: touch three clean: rm -f one two three 90013 90014 90002 90005 Multiple targets (Section 4.8) 90006 90007 When there are multiple targets for a rule, the commands will be run for each target 90007 90046 $ @ 90013 is a 90008 automatic variable 90009 that contains the target name. 90003 90011 90039 all: f1.o f2.o f1.o f2.o: echo $ @ # Equivalent to: # f1.o # Echo $ @ # f2.o # Echo $ @ 90013 90014 90002 90005 Multiple targets via wildcards (Section 4.8) 90006 90007 We can use the wildcard% in targets, that captures zero or more of any character. Note we do not use * .o, because that is just the string *.o, which might be useful in the commands, 90007 but is only one target and does not expand. 90007 90003 90011 90039 all: f1.o f2.o .PHONY: all % .O: echo $ @ 90013 90014 90002 90005 Static Pattern Rules (Section 4.10) 90006 90007 Make loves c compilation. And every time it expresses its love, things get confusing. Here’s the syntax for a new type of rule called a static pattern: 90003 90011 90039 targets …: target-pattern: prereq-patterns … commands 90013 90014 90002 The essence is that the a given target is matched by the target-pattern (via a 90046% 90013 wildcard).Whatever was matched is called the 90008 stem 90009. The stem is then substituted into the prereq-pattern, to generate the target’s prereqs. 90003 90002 A typical use case is to compile 90046 .c 90013 files into 90046 .o 90013 files. Here’s the 90008 manual way 90009: 90003 90011 90039 all: foo.o bar.o .PHONY: all # The automatic variable $ @ matches the target, and $

No related posts.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *