Makefile — это текстовый файл, который контролирует работу программы make
. Программа make
обычно используется для управления созданием программ из их исходных файлов, но ее можно более широко использовать для обработки любого процесса, в котором файлы (или целевые объекты ) необходимо восстановить после того, как другие файлы (или предпосылки ) были изменены. Файл makefile описывает взаимосвязь между целями и предварительными условиями, а также указывает команды, необходимые для обновления цели, когда одно или несколько предварительных условий были изменены. Единственный способ make
«устаревшим» — это сравнить время модификации целевых файлов и их предварительные условия.
Makefiles несколько уникальны несколькими способами, которые могут быть запутаны изначально.
Во-первых, make-файл состоит из двух совершенно разных языков программирования в одном файле. Основная часть файла написано на языке , который
может понять: это обеспечивает назначение переменного и расширение, некоторые возможности препроцессора (включая другие файлы, условный разбор секций файла и т.д.), а также определение целей и их предпосылки. Кроме того, у каждой цели может быть связанный с ней рецепт, который указывает, какие команды следует вызывать, чтобы заставить эту цель обновляться. Рецепт написан как сценарий оболочки (по умолчанию POSIX sh). Программа make
не анализирует этот скрипт: он запускает оболочку и передает сценарий в оболочку для запуска. Тот факт, что рецепты не анализируются make
, а обрабатывается отдельным процессом оболочки, занимает центральное место в понимании make-файлов.
Во-вторых, make-файл не является процедурным языком, как скрипт: поскольку make
анализирует makefile, он конструирует ориентированный граф
make
выбор один узел (цель) и попытаться довести его до настоящего времени. Чтобы гарантировать, что цель обновлена, она должна сначала обеспечить, чтобы каждый из предварительных условий этой цели обновлялся и т. Д. Рекурсивно. Подумайте о написании «привет мир!». программа в c. Допустим, наш исходный код находится в файле source.c, теперь, чтобы запустить нашу программу, нам нужно ее скомпилировать, как правило, на Linux (с использованием gcc) нам нужно будет ввести $> gcc source.c -o output
где вывод это имя исполняемого файла. Для базовой программы это работает хорошо, но по мере того, как программы становятся более сложными, наша команда компиляции также может усложняться. Здесь появляется
Основной 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
выполняет только правило, если цель устарела, то есть либо она не существует, либо время ее модификации старше любого из ее предварительных условий. Если список предварительных условий пуст, правило будет выполняться только при первом вызове для создания целей. Однако, когда правило не создает файл, а цель — фиктивная переменная, правило всегда будет выполняться.
Правила шаблонов используются для указания нескольких целей и создания имен требуемых имен из целевых имен. Они более общие и более мощные по сравнению с обычными правилами, поскольку каждая цель может иметь свои собственные предпосылки. В правилах шаблонов связь между целью и предварительным условием строится на основе префиксов, включая имена и суффиксы путей, или и то, и другое.
Представьте, что мы хотим создать цели 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)
Каталог неявных правил и используемых им переменных можно найти здесь .
если каталог содержит 2 файла:
$ ls
makefile
example.txt
и makefile
содержат следующий текст
%.gz: %
gzip $<
то вы можете получить example.txt.gz
, введя в оболочку
$ make -f makefile example.txt.gz
Makefile , состоит только из одного правила , которое инструктирует сделать , как создать файл, имя которого оканчивается .gz , если есть файл с таким же именем , но .gz суффиксом.
C: \ Makefile:
helloWorld :
[TAB]echo hello world
результаты:
C:\>make
echo hello world
hello world
Примечание: [TAB] следует заменить фактической вкладкой, stackoverflow заменяет вкладки пробелами, а пробелы не используются так же, как вкладки в make-файле.
В настоящее время я разрабатываю небольшой пакет 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 — файл, содержащий набор инструкций для программы 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
Если вы уже собирали прикладную программу из исходных кодов, то обратили внимание на стандартную последовательность команд: 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 | Вывод текущего каталога до и после выполнения команды |
Читать книгу целиком
Поделитесь на страничкеОбновлено 11.02.15. Всем привет. В предыдущей статье №1 мы описали ту литературу и программы, которые нам понадобится для работы с микроконтроллерами AVR. Так вот, что бы показать первый пример написания и заливки программы в “камень”, мы начнем работу с программы WinAVR. Если Вы откроете какие-нибудь исходники для контроллеров, то заметите, что в папке помимо содержания в себе файлов с расширением .с и .h также содержат еще файл Makefile (рис. ниже). Так вот нас пока интересует только этот файл.
Что же это за файл? Это файл который содержит в себе инструкции, для программы (утилиты) make, которая в свою очередь автоматизирует компиляцию программы.
Давайте попробуем его сами создать. В наборе инструментальных средств WinAVR содержится программа Mfile, которая является простым Makefile-генератором для компилятора AVR-GCC. Также в дополнение можно посмотреть пример настройки в файле мониторинг памяти. Запускаем данное приложение (рис. ниже)
При запуске приложения, код уже сгенерированный! Все что нам надо это настроить. Сверху на рисунке Вы видите две вкладки, нас интересует Makefile. Переходим и настраиваем.
В данной вкладке как видите можно выбрать тип контроллера, программатор, порты т.д. Далее переходим к вкладке File –> Save as. Файл необходимо сохранить в папку в которой будут исходники программы. Данный файл можно редактировать вручную. Для этого запускаете редактор Notepad и открываете файл Makefile. Например нам необходимо изменить частоту на которой будет работать МК, ищем следующие строки
В комментариях перечислены константы которые мы можем установить. Выбираем одну из них.
Итак сам файл делится на разделы: для объектов (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
Просмотрено 6856 раз.
Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 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
$@
, $<
, $^
называются автоматическими (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) по шагам:
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.
Makefiles происходит от Unix-подобных систем и по-прежнему является основным механизмом создания программного обеспечения в таких средах.
Windows поддерживает изменение make-файлов с помощью утилиты nmake . Стандартные Unix, такие как make-файлы, могут быть выполнены в Windows в среде Cygwin.
Makefiles содержит пять видов вещей: явные правила , неявные правила , определения переменных , директивы и комментарии .
Простой make-файл состоит из «правил» со следующей из форм [Источник 2]:
target … : prerequisites … recipe … …или
target: dependencies system command(s)
Зависимость или «prerequisites» (также называемая предварительным условием) — это файл, который используется как вход для создания цели. Необходимым условием является файл, который используется как вход для создания цели.
Цель(target) часто зависит от нескольких файлов.Однако правило, определяющее рецепт для цели, не обязательно имеет какие-либо предварительные условия. Например, правило, содержащее команду delete, связанную с целевым «чистым», не имеет предварительных условий.
Рецепт (recipe)- это действие , которое make выполняет. Рецепт может иметь более одной команды, либо в одной строке, либо в каждой строке. Обратите внимание: вам нужно поместить символ табуляции в начале каждой линии рецептов. Это неясность, на которой попадаются неосторожные. Если вы предпочитаете префикс своих рецептов символом, отличным от табуляции, вы можете установить RECIPEPREFIX переменную на альтернативный символ.
Затем правило объясняет, как и когда переделывать определенные файлы, которые являются объектами конкретного правила. make выполняет рецепт на предпосылках для создания или обновления цели. Правило также может объяснить, как и когда выполнять действие.
Файл makefile может содержать другой текст помимо правил, но простой makefile должен содержать только правила. Правила могут выглядеть несколько сложнее, чем показано в этом шаблоне, но все они более или менее соответствуют шаблону .
В следующем примере условного выражения 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.
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, но для некоторых из них проверяются дополнительные зависимости.
В настоящее время я разрабатываю небольшой пакет 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
Проблема в том, что существует встроенное правило для построения программы непосредственно из исходного файла. 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, который я бы изменил так, что он будет генерировать более одной цели, когда я запускаю make . В моей программе я использую self predefined macros (например, TIME , REG и _DEBUG ),…
Как зависимости работают на gnu Makefile ? Я хочу создать Makefile, который: — придется применить некоторые патчи (в нашем, например, применять main.patch на main.c) — создайте программу под…
У меня есть несколько каталогов, представляющих подразделы проекта, каждый со своим собственным файлом Makefile. Я хочу создать мастер-файл Makefile с целями для каждого из этих подразделов, каждая…
У меня есть GNU makefile, который компилирует набор исходных кодов в моей системе, и я хочу передать исходный код другому человеку (другой системе), так будет ли тот же GNU makefile функционировать…
Поэтому в рамках нескольких инструментов, которые я делаю в C++, я хотел бы иметь возможность тестировать каждый инструмент индивидуально. Есть несколько инструментов с несколькими файлами…
Я пытаюсь отредактировать файл makefile, я уже построил свой собственный, но я изо всех сил пытаюсь понять этот очень минимальный пример, который явно использует соглашения, о которых я не знаю. Я…
No related posts.