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

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

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

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

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

MODULES = example1 example2 example3

all: $(MODULES)

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

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

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

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

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

.PHONY: clean

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

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

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

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

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

0

Решение

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

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

%: %.cpp

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

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

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

2

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

Вот решение:

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

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

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

MODULES = example1 example2 example3

all: $(MODULES)

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

.PHONY: clean

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

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

MODULES = example1 example2 example3

all: $(MODULES)

#disable built-in rule
%: %.cpp

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

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

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

.PHONY: clean

0

web-answers.ru

Мини-руководство по созданию Makefile-ов | sys.dmitrow.com

Эта статья представляет собой небольшое руководство по созданию 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 значительно упрощает жизнь, когда для сборки проекта необходимо выполнение длинных и сложных команд.
  • Проект иногда требует задания редко используемых, а потому сложных для запоминания опций компилятора. make избавит вас от необходимости удерживать их в памяти.
  • Единообразие, т.к. работа с этой утилитой поддерживается многими средами разработки.
  • Процесс сборки можно автоматизировать, поскольку make может быть вызвана из сценариев или из cron.

Несмотря на все свои достоинства, утилита 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
называется «правилом» и описывает действие, которое необходимо выполнить.

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

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

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

Ниже приводится пример 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. А за сим — успехов в работе.

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

 

 

sys.dmitrow.com

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

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

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

Обзор

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

Unix-подобный

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

Microsoft Windows

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

Содержание

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

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

Правила

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

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

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

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

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

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

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

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

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

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

libs_for_gcc = -lgnu
normal_libs =

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

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

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

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

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

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

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

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

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

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

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

libs_for_gcc = -lgnu
normal_libs =

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

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

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

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

conditional-directive
text-if-true
endif

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

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

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

или:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Выполнение

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

Примеры

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Источники

Ссылки

ru.bmstu.wiki

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

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

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

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

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

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

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

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

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

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

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

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

www.ap-impulse.ru

Make: cтруктура Makefile | MainLoop

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 $@и $

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
  • pro: очень легко читать
  • con: кошмар обслуживания, дублирование зависимостей на С++
  • con: проблема эффективности, мы перекомпилируем все С++, даже если был изменен только один

Чтобы улучшить вышеизложенное, мы компилируем только те файлы С++, которые были отредактированы. Затем мы просто связываем результирующие объектные файлы вместе.

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
  • pro: исправляет проблему с эффективностью
  • con: новый кошмар для обслуживания, потенциальная опечатка на правилах объектных файлов.

Чтобы улучшить это, мы можем заменить все правила объектных файлов одним правилом .cpp.o:

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

.cpp.o:
    g++ -c $< -o $@
  • pro: вернуться к созданию короткого файла makefile, несколько легко читаемому

Здесь правило .cpp.o определяет, как построить anyfile.o из anyfile.cpp.

  • $< соответствует первой зависимости, в данном случае anyfile.cpp
  • $@ соответствует цели, в данном случае anyfile.o.

Другие изменения, присутствующие в Makefile, следующие:

  • Упрощение преобразования компиляторов из g++ в любой компилятор С++.
  • Упрощение изменения параметров компилятора.
  • Упрощение изменения параметров компоновщика.
  • Упрощение изменения исходных файлов на С++ и вывод.
  • Добавлено правило «все» по умолчанию, которое действует как быстрая проверка, чтобы убедиться, что все исходные файлы присутствуют до того, как сделана попытка создать ваше приложение.
источник поделиться

qaru.site

универсальный : Как сделать SIMPLE C++Makefile? : Code Examples

Я предлагаю:

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

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

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