В настоящее время я разрабатываю небольшой пакет 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
web-answers.ru
Эта статья представляет собой небольшое руководство по созданию Makefile-ов. В ней объясняется для чего нужен Makefile и дается несколько правил, которых следует придерживаться при его создании.
Допустим, вы разрабатываете некую программу под названием foo, состоящую из пяти заголовочных файлов — 1.h, 2.h, 3.h, 4.h и — 5.h, и шести файлов с исходным текстом программы на языке С — 1.cpp, 2.cpp, 3.cpp, 4.cpp, 5.cpp и main.cpp. (Хочу заметить, что в реальных проектах следует избегать подобного стиля именования файлов).
Теперь представим себе, что вы обнаружили ошибку в файле 2.cpp и исправили ее. Далее, чтобы получить исправленную версию программы вы компилируете все файлы, входящие в состав проекта, хотя изменения коснулись только одного файла. Это приводит к нерациональной потере времени, особенно если компьютер не слишком быстрый.
Существует ли решение проблемы?
Не стоит беспокоиться, друзья мои! Эта проблема уже давно решена. Опытными программистами была разработана утилита make. Вместо того, чтобы производить повторную компиляцию всех файлов с исходными текстами, она обрабатывает только те файлы, которые претерпели изменения. В нашем случае будет скомпилирован только один файл — 2.cpp. Разве это не здорово!?
Кроме того [2]:
Несмотря на все свои достоинства, утилита make ничего не знает о нашем проекте, поэтому необходимо создать простой текстовый файл, который будет содержать все необходимые инструкции по сборке. Файл с инструкциями по сборке проекта называется makefile (произносится как «мэйкфайл». прим. перев.).
Как правило этим файлам дается имя makefile или Makefile, в соответствии с соглашениями по именованию таких файлов. Если же вы дадите файлу инструкций другое имя, то вам потребуется вызывать утилиту make с ключом -f.
Например, если свой makefile вы назвали bejo, то команда на сборку проекта будет выглядеть так:
make -f bejo
Makefile содержит разделы для «целей» [targets], зависимостей [dependencies] и правил (rules) сборки. Все это оформляется следующим образом: сначала указывается имя цели (обычно это имя исполняемого или объектного файла), после которого следует двоеточие, затем следуют имена зависимостей, т.е. файлов, необходимых для получения данной цели. И, наконец, следует список правил: т.е. команд, которые необходимо выполнить для получения указанной цели.
Простой пример структуры makefile’а:
target: dependencies
command
command
...
Каждое правило command должно начинаться с символа табуляции — это обязательное условие! Отсутствие символа табуляции в начале строки с правилом — самая распространенная ошибка. К счастью, подобные ошибки легко обнаруживаются, так как утилита make сообщает о них.
Ниже приводится простой пример (номера строк добавлены для ясности).
1 client: conn.o
2 g++ client.cpp conn.o -o client
3 conn.o: conn.cpp conn.h
4 g++ -c conn.cpp -o conn.o
В этом примере строка, содержащая текст
client: conn.o,
называется «строкой зависимостей», а строка
g++ client.cpp conn.o -o client
называется «правилом» и описывает действие, которое необходимо выполнить.
А теперь более подробно о примере, приведенном выше:
Строки, начинающиеся с символа «#», являются комментариями
Ниже приводится пример makefile с комментариями:
1 # Создать исполняемый файл "client"
2 client: conn.o
3 g++ client.cpp conn.o -o client
4
5 # Создать объектный файл "conn.o"
6 conn.o: conn.cpp conn.h
7 g++ -c conn.cpp -o conn.o
Обычно «ложные» [phony] цели, представляющие «мнимое» имя целевого файла, используются в случае возникновения конфликтов между именами целей и именами файлов при явном задании имени цели в командной строке.
Допустим в makefile имеется правило, которое не создает ничего, например:
clean:
rm *.o temp
Поскольку команда rm не создает файл с именем clean, то такого файла никогда не будет существовать и поэтому команда make clean всегда будет отрабатывать.
Однако, данное правило не будет работать, если в текущем каталоге будет существовать файл с именем clean. Поскольку цель clean не имеет зависимостей, то она никогда не будет считаться устаревшей и, соответственно, команда ‘rm *.o temp’ никогда не будет выполнена. (при запуске make проверяет даты модификации целевого файла и тех файлов, от которых он зависит. И если цель оказывается «старше», то make выполняет соответствующие команды-правила — прим. ред.) Для устранения подобных проблем и предназначена специальная декларация .PHONY, объявляющая «ложную» цель. Например:
.PHONY : clean
Таким образом мы указываем необходимость исполнения цели, при явном ее указании, в виде make clean вне зависимости от того — существует файл с таким именем или нет.
Определить переменную в makefile вы можете следующим образом:
$VAR_NAME=value
В соответствии с соглашениями имена переменных задаются в верхнем регистре:
$OBJECTS=main.o test.o
Чтобы получить значение переменной, необходимо ее имя заключить в круглые скобки и перед ними поставить символ ‘$’, например:
$(VAR_NAME)
В makefile-ах существует два типа переменных: «упрощенно вычисляемые» и «рекурсивно вычисляемые».
В рекурсивно вычисляемых переменных все ссылки на другие переменные будут замещены их значениями, например:
TOPDIR=/home/tedi/project
SRCDIR=$(TOPDIR)/src
При обращении к переменной SRCDIR вы получите значение /home/tedi/project/src.
Однако рекурсивные переменные могут быть вычислены не всегда, например следующие определения:
CC = gcc -o
CC = $(CC) -O2
выльются в бесконечный цикл. Для разрешения этой проблемы следует использовать «упрощенно вычисляемые» переменные:
CC := gcc -o
CC += $(CC) -O2
Где символ ‘:=’ создает переменную CC и присваивает ей значение «gcc -o». А символ ‘+=’ добавляет «-O2» к значению переменной CC.
Я надеюсь, что это краткое руководство содержит достаточно информации, чтобы начать создавать свои makefile. А за сим — успехов в работе.
sys.dmitrow.com
Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 23:02, 23 мая 2018.
Makefile – файл, содержащий набор инструкций для программы make. Программа make с помощью этого файла позволяет автоматизировать процесс компиляции программы и выполнять при этом различные действия. При запуске make по умолчанию ищет файл Makefile в текущей папке и обрабатывает его (можно изменить это поведение, чтобы открывался другой файл с набором инструкций, если ввести команду make -f другое_имя_makefile
).
Чаще всего, Makefile показывает как компилировать и компоновать программу. Используя C / C ++ в качестве примера, когда исходный файл C / C ++ изменяется, он должен быть перекомпилирован. Если файл заголовка был изменен, каждый исходный файл C / C ++, содержащий заголовочный файл, должен быть перекомпилирован для обеспечения безопасности. Каждая компиляция создает объектный файл, соответствующий исходному файлу. Наконец, если какой-либо исходный файл был перекомпилирован, все объектные файлы, будь то новые или сохраненные из предыдущих компиляций, должны быть связаны друг с другом для создания новой исполняемой программы.
Комментарии, как это принято в 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, но для некоторых из них проверяются дополнительные зависимости.
ru.bmstu.wiki
Обновлено 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
Просмотрено 5974 раз.
www.ap-impulse.ru
make — это утилита для автоматической сборки программ. Позволяет отслеживать изменения в исходном коде программы и компилировать не весь проект целиком а только те файлы которые изменились или те которые зависят от внесенных изменений. При больших проектах это дает существенную экономию времени.
В этой заметке я попытаюсь рассказать как создать makefile.
По умолчанию правила сборки считываются из файла с именем Makefile.
Структуру Makefile можно представить так:
ЦЕЛЬ: ЗАВИСИМОСТЬ [TAB]ДЕЙСТВИЕ
Но обычно используются более сложные правила, например:
ЦЕЛЬ: ЦЕЛЬ1 ЦЕЛЬ2 [TAB]ДЕЙСТВИЕ ЦЕЛЬ1: ЗАВИСИМОСТЬ1 [TAB]ДЕЙСТВИЕ1 ЦЕЛЬ2: ЗАВИСИМОСТЬ2 [TAB]ДЕЙСТВИЕ2
ЦЕЛЬ — это то что мы получаем в результате ДЕЙСТВИЯ. Это может быть файл, директория или просто абстрактная ЦЕЛЬ не имеющая связи с каким-либо объектом на жестком диске. После имени цели ставится двоеточие. При запуске команды make без параметров выполнится первое найденное правило. Что бы выполнить другое правило надо указать его команде make
make ЦЕЛЬ2
ЗАВИСИМОСТЬ — это то, от чего зависит наша ЦЕЛЬ. Это могут быть файлы, каталоги или другие ЦЕЛИ. Make сравнивает дату и время изменения ЦЕЛИ и объектов о которых зависит цель. Если объекты от которых зависит цель были изменены позже чем создана цель, то будет выполнено ДЕЙСТВИЕ. ДЕЙСТВИЕ так же выполняется если ЦЕЛЬ не является именем файла или директории.
ДЕЙСТВИЕ — это набор команд которые надо выполнить. Перед командами должен быть введен символ табуляции. Если вместо символа табуляции будут введены пробелы то при компиляции будет выведено сообщение об ошибке:
Makefile:13: *** пропущен разделитель. Останов.
или
Makefile:13: *** missing separator. Stop.
Пример Makefile:
test.elf: test.c gcc test.c -o test.elf |
test.elf: test.c gcc test.c -o test.elf
Пример с абстрактной целью:
all: test.elf test.elf: test1.o test2.o gcc -o test.elf test1.o test2.o test1.o test1.c gcc -c test1.c -o test1.o test2.o test2.c gcc -c test2.c -o test2.o |
all: test.elf test.elf: test1.o test2.o gcc -o test.elf test1.o test2.o test1.o test1.c gcc -c test1.c -o test1.o test2.o test2.c gcc -c test2.c -o test2.o
Рассмотри последний пример:
Первым выполняется all т.к. находится в начале Makefile. all зависит от test.elf и файла или директории с именем all не существует, поэтому всегда будет происходить проверка цели с именем test.elf.
test.elf зависит от test1.o и test2.o, по этому сначала будет проверена цель test1.o затем test2.o
При проверке цели test1.o сравниваются дата и время изменения файла test1.o и test1.c. Если файл test1.o не существует или файл test1.c был изменены позднее чем test1.o то будет выполнена команда gcc -с test1.c -o test1.o.
Аналогично будет проверена цель test2.o.
После этого сравниваются дата и время изменения файла test.elf и файлов test1.o test2.o. Если test1.o или test2.o новее то будет выполнена команда gcc -o test.elf test1.o test2.o
Таким образом отслеживаются изменения в файлах test1.с и test2.c.
P/S надеюсь данная заметка упростит создание makefile, но если есть какие-то вопросы — напишите в комментарии, постараюсь ответить.
mainloop.ru
Makefile создает исполняемый файл hello
, если изменился какой-либо из main.cpp
, hello.cpp
, factorial.cpp
. Самый маленький возможный Makefile для достижения этой спецификации мог бы быть:
hello: main.cpp hello.cpp factorial.cpp
g++ -o hello main.cpp hello.cpp factorial.cpp
Чтобы улучшить вышеизложенное, мы компилируем только те файлы С++, которые были отредактированы. Затем мы просто связываем результирующие объектные файлы вместе.
OBJECTS=main.o hello.o factorial.o
hello: $(OBJECTS)
g++ -o hello $(OBJECTS)
main.o: main.cpp
g++ -c main.cpp
hello.o: hello.cpp
g++ -c hello.cpp
factorial.o: factorial.cpp
g++ -c factorial.cpp
Чтобы улучшить это, мы можем заменить все правила объектных файлов одним правилом .cpp.o
:
OBJECTS=main.o hello.o factorial.o
hello: $(OBJECTS)
g++ -o hello $(OBJECTS)
.cpp.o:
g++ -c $< -o $@
Здесь правило .cpp.o
определяет, как построить anyfile.o
из anyfile.cpp
.
$<
соответствует первой зависимости, в данном случае anyfile.cpp
$@
соответствует цели, в данном случае anyfile.o
.Другие изменения, присутствующие в Makefile, следующие:
qaru.site
Я предлагаю:
tool: tool.o file1.o file2.o
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o [email protected]
или же
LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
tool: tool.o file1.o file2.o
Последнее предложение немного лучше, поскольку он повторно использует GNU для неявных правил. Однако для работы исходный файл должен иметь то же имя, что и конечный исполняемый файл (например, tool.c
и tool
).
Обратите внимание, что нет необходимости объявлять источники. Промежуточные объектные файлы генерируются с использованием неявного правила. Следовательно, этот Makefile
работает для C и C ++ (а также для Fortran и т. Д.).
Также обратите внимание, что по умолчанию Makefile использует $(CC)
качестве компоновщика. $(CC)
не работает, чтобы связать объекты C ++. Мы модифицируем LINK.o
только из-за этого. Если вы хотите скомпилировать код C, вам не нужно форсировать значение LINK.o
Конечно, вы также можете добавить свои LDLIBS
компиляции с переменными CFLAGS
и добавить свои библиотеки в LDLIBS
. Например:
CFLAGS = -Wall
LDLIBS = -lm
Одностороннее примечание: если вам нужно использовать внешние библиотеки, я предлагаю использовать pkg-config , чтобы правильно установить CFLAGS
и LDLIBS
:
CFLAGS += $(shell pkg-config --cflags libssl)
LDLIBS += $(shell pkg-config --libs libssl)
Внимательный читатель замечает, что Makefile
не восстанавливается должным образом, если один заголовок изменен. Добавьте эти строки, чтобы исправить проблему:
override CPPFLAGS += -MMD
include $(wildcard *.d)
-MMD
позволяет создавать файлы .d, содержащие фрагменты Makefile о зависимостях заголовков. Вторая строка просто использует их.
Конечно, хорошо написанный Makefile должен также включать в себя clean
и distclean
правила:
clean:
$(RM) *.o *.d
distclean: clean
$(RM) tool
Обратите внимание, что $(RM)
эквивалентно rm -f
но это хорошая практика, чтобы напрямую не обращаться к rm
.
all
правила также приветствуются. Чтобы работать, это должно быть первое правило вашего файла:
all: tool
Вы также можете добавить правило install
:
PREFIX = /usr/local
install:
install -m 755 tool $(DESTDIR)$(PREFIX)/bin
По умолчанию DESTDIR
пуст. Пользователь может установить его для установки вашей программы в альтернативную систему (обязательно для процесса кросс-компиляции). PREFIX
пакеты для множественного распространения могут также изменить PREFIX
, чтобы установить ваш пакет в /usr
.
Одно последнее слово, не размещайте исходные файлы в подкаталогах. Если вы действительно хотите это сделать, сохраните этот Makefile
в корневом каталоге и используйте полные пути для идентификации ваших файлов (например, subdir/file.o
).
Итак, чтобы сводить итог, ваш полный Makefile должен выглядеть так:
LINK.o = $(CXX) $(LDFLAGS) $(TARGET_ARCH)
PREFIX = /usr/local
override CPPFLAGS += -MMD
include $(wildcard *.d)
all: tool
tool: tool.o file1.o file2.o
clean:
$(RM) *.o *.d
distclean: clean
$(RM) tool
install:
install -m 755 tool $(DESTDIR)$(PREFIX)/bin
code-examples.net