Как скомпилировать исходники из разных каталогов в объектные файлы в одном каталоге (makefile)?

СОДЕРЖАНИЕ

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

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

.PHONY

Если теперь выполнить

make site

echo «HeiHei.ru»
HeiHei.ru

Удалите site из первой строки, а всё остальное не трогайте

make site

echo «HeiHei.ru»
HeiHei.ru

Вроде бы ничего не изменилось, но теперь создайте файл

site

рядом с

Makefile

touch site

make site

make: ‘site’ is up to date.

Так как таргет теперь реальный — make не нашёл изменений и ничего не сделал. Из-за такого простого
совпадения имени цели (target) и какого-то файла в директории может перестать работать скрипт.

Для защиты от таких неприятностей и применяют PHONY

Также PHONY удобен тем, что можно перечислить все цели в самом начале файла.

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

Специальные цели

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

  • — определяет набор phony targets;
  • — определяет цель, которая будет вызываться, если не найдена зависимая цель, необходимая для выполнения другой цели;
  • — указывает цели, для которых необходимо игнорировать ошибки при выполнении цели;
  • — определяет набор целей, для которых необходимо подавлять вывод команд, выполняющих цель;
  • — запрещает параллельное выполнение makefile;
  • — выполняет набор команд цели в одном процессе shell.

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

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

Unix-подобный

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

Microsoft Windows

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

Правила

Простой make-файл состоит из «правил» со следующей из форм :

target …  prerequisites …
        recipe
        …
        …
target dependencies
    system command(s)

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

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

Рецепт (recipe)- это действие , которое make выполняет. Рецепт может иметь более одной команды, либо в одной строке, либо в каждой строке

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

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

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

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

Параллельный make

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

Параллельное выполнение может быть запрещено с помощью специальной цели .

пример

В каталоге test присутствуют следующие файлы:

В make-файле правило определяется следующим образом:

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

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

И файл hello (текстовый файл) будет перезаписан новым двоичным файлом hello (результат вышеуказанной команды компиляции).

Если мы используем .PHONY в make-файле следующим образом:

а затем вызвать make hello, он проигнорирует любой файл, присутствующий в pwd ‘test’, и будет выполнять команду каждый раз.

Теперь предположим, что у цели ‘hello’ не объявлены зависимости:

и файл ‘hello’ уже присутствует в pwd ‘test’, тогда ‘make hello’ всегда будет отображаться как:

  • 3 Это не только придает смысл выполняемым мной командам, но и в конечном итоге вызывает в целом, чтобы иметь смысл, все дело в файлах! Спасибо за этот ответ.
  • Вот как выглядит простое правило: « target: dependencies … commands … » Ref: gnu.org/software/make
  • означает, что слово «install» не представляет имя файла в этом Makefile;
  • означает, что Makefile не имеет ничего общего с файлом с именем «install» в том же каталоге.

Это цель сборки, которая не является именем файла.

Лучшее объяснение — это само руководство GNU make: 4.6 Раздел «Фальшивые цели».

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

Вас также могут заинтересовать стандартные цели make, такие как а также .

Есть еще одна важная хитрость «.PHONY» — когда физическая цель зависит от фальшивой цели, которая зависит от другой физической цели:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

Вы просто ожидаете, что если вы обновили TARGET2, то TARGET1 будет считаться устаревшим по сравнению с TARGET1, поэтому TARGET1 следует перестроить. И это действительно так работает.

Сложность — это когда TARGET2 не устаревшие по отношению к TARGET1 — в этом случае вы должны ожидать, что TARGET1 не следует перестраивать.

На удивление это не работает, потому что: фальшивая цель все равно была запущена (как обычно делают фальшивые цели), что обозначает фальшивая цель считалась обновленной. И из-за этого TARGET1 считается устаревшим по отношению к фальшивой цели.

Рассмотреть возможность:

Вы можете поиграть с этим:

  • сначала сделайте «make prepare», чтобы подготовить «исходные файлы»
  • поиграйте с этим, коснувшись определенных файлов, чтобы увидеть их обновленные

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

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

Можно поставить несколько в твоем :

Есть еще один способ объявить фальшивые цели: просто поставить «::»

‘::’ имеет особое значение: цели фальшивые. плюс они могут появляться несколько раз:

Командные блоки будут вызываться один за другим.

Я часто использую их, чтобы указать цели по умолчанию не стрелять.

Без PHONY, будет стрелять , , а также ; но с PHONY, не будет стрелять .

Нам не нужно беспокоиться о том, чтобы сказать target — PHONY, потому что это не совсем фальшивка. Хотя он никогда не создает чистый файл, у него есть команды для запуска, поэтому make будет думать, что это конечная цель.

Тем не менее цель на самом деле фальшивая, поэтому make попытается объединить ее с чем-либо еще, что обеспечивает цель сюда входят другие цели и цель.

Обратите внимание, что мы вообще ничего не говорим о или , поэтому они явно идут в ловушку. Результат выглядит примерно так:

Результат выглядит примерно так:

Игнорировать ошибки

Если какая-то команда выполнена с ошибкой выполнение сценария прерывается.

Рассмотрим пример

RPM_DIR=/home/$$(whoami)/rpms/

.PHONY: clean-repo
clean-repo:
@sudo rm $(RPM_DIR)release/*
@sudo rm $(RPM_DIR)master/*

Если в …release/ пусто, то удалять в …master/ make уже не будет.

Вместо этого появится ошибка:

sudo rm /home/$(whoami)/rpms/release/*
rm: cannot remove ‘/home/andrei/rpms/release/*’: No such file or directory
make: *** Error 1

Избежать этой проблемы можно поставив — перед командой

RPM_DIR=/home/$$(whoami)/rpms/

.PHONY: clean-repo
clean-repo:
@-sudo rm $(RPM_DIR)release/*
@-sudo rm $(RPM_DIR)master/*

$ make clean-repo

rm: cannot remove ‘/home/andrei/rpms/release/*’: No such file or directory

make: Error 1 (ignored)

make жалуется, но переходит ко второй команде и чистит директорию.

Примеры make-файлов

Makefile:

PACKAGE	 = package
VERSION	 = ` date "+%Y.%m%d%" `
RELEASE_DIR  = ..
RELEASE_FILE = $(PACKAGE)-$(VERSION)

# Notice that the variable LOGNAME comes from the environment in
# POSIX shells.
#
# target: all - Default target. Does nothing.
all
	echo "Hello $(LOGNAME), nothing to do by default"
		# sometimes: echo "Hello ${LOGNAME}, nothing to do by default"
	echo "Try 'make help'"

# target: help - Display callable targets.
help
	egrep "^# target:" Mmakefile

# target: list - List source files
list
	# Won't work. Each command is in separate shell
	cd src
	ls

	# Correct, continuation of the same shell
	cd src; \
	ls

# target: dist - Make a release.
dist
	tar -cf  $(RELEASE_DIR)/$(RELEASE_FILE) && \
	gzip -9  $(RELEASE_DIR)/$(RELEASE_FILE).tar

Ниже приведен очень простой make-файл, который по умолчанию (правило «все» указано первым) компилирует исходный файл с именем «helloworld.c» с использованием системного компилятора C, а также предоставляет «чистую» цель для удаления сгенерированных файлов, если пользователь желает начать все сначала. И являются двумя из так называемых внутренних макросов (также известные как автоматические переменные) и стоять имя цели и «неявный» источник, соответственно. В приведенном ниже примере расширяется до списка предварительных требований, разделенных пробелами. Есть ряд других внутренних макросов.

CFLAGS ?= -g

all helloworld

helloworld helloworld.o
	# Commands start with TAB not spaces
	$(CC) $(LDFLAGS) -o $@ $^

helloworld.o helloworld.c
	$(CC) $(CFLAGS) -c -o $@ $<

clean FRC
	$(RM) helloworld helloworld.o

# This pseudo target causes all targets that depend on FRC
# to be remade even in case a file with the name of the target exists.
# This works with any make implementation under the assumption that
# there is no file FRC in the current directory.
FRC

Многие системы поставляются с предопределенными правилами Make и макросами для определения общих задач, таких как компиляция на основе суффикса файла. Это позволяет пользователям опускать фактические (часто непереносимые) инструкции о том, как сгенерировать цель из источника (ов). В такой системе make-файл выше может быть изменен следующим образом:

all helloworld

helloworld helloworld.o
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^

clean FRC
	$(RM) helloworld helloworld.o

# This is an explicit suffix rule. It may be omitted on systems
# that handle simple rules like this automatically.
.c.o
	$(CC) $(CFLAGS) -c $<

FRC
.SUFFIXES .c

То, что helloworld.o зависит от helloworld.c, теперь автоматически обрабатывается Make. В таком простом примере, как проиллюстрированный здесь, это вряд ли имеет значение, но реальная сила суффиксных правил становится очевидной, когда количество исходных файлов в программном проекте начинает расти. Достаточно написать правило для шага связывания и объявить объектные файлы как предварительные условия. Затем make будет неявно определять, как создавать все объектные файлы, и искать изменения во всех исходных файлах.

Простые правила суффиксов работают хорошо, пока исходные файлы не зависят друг от друга и от других файлов, таких как файлы заголовков. Еще один способ упростить процесс сборки — использовать так называемые правила сопоставления с образцом, которые можно комбинировать с генерацией зависимостей с помощью компилятора. В качестве последнего примера, требующего компилятора gcc и GNU Make, вот общий make-файл, который компилирует все файлы C в папке в соответствующие объектные файлы, а затем связывает их с окончательным исполняемым файлом. Перед компиляцией зависимости собираются в формате, удобном для make-файла, в скрытый файл «.depend», который затем включается в make-файл. В переносимых программах следует избегать конструкций, используемых ниже.

# Generic GNUMakefile

# Just a snippet to stop executing under other make(1) commands
# that won't understand these lines
ifneq (,)
This makefile requires GNU Make.
endif

PROGRAM = foo
C_FILES := $(wildcard *.c)
OBJS := $(patsubst %.c, %.o, $(C_FILES))
CC = cc
CFLAGS = -Wall -pedantic
LDFLAGS =
LDLIBS = -lm

all $(PROGRAM)

$(PROGRAM) .depend $(OBJS)
	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM) $(LDLIBS)

depend .depend

.depend cmd = gcc -MM -MF depend $(var); cat depend >> .depend;
.depend
	@echo "Generating dependencies..."
	@$(foreach var, $(C_FILES), $(cmd))
	@rm -f depend

-include .depend

# These are the pattern matching rules. In addition to the automatic
# variables used here, the variable $* that matches whatever % stands for
# can be useful in special cases.
%.o %.c
	$(CC) $(CFLAGS) -c $< -o $@

% %.o
	$(CC) $(CFLAGS) -o $@ $<

clean
	rm -f .depend $(OBJS)

.PHONY clean depend

PHONY targets

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

.PHONY clean
clean
  rm *.o temp

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

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

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

Соответственно, в Makefile мы можем написать:

.PHONY all css js php
all css js php
css wwwstyle.css
  ... тут команды
js  wwwjsapp.js
  ... тут еще команды
php
  ... тут снова команды

В результате мы можем использовать для пересборки всех файлов и, скажем, для пересборки только CSS-файлов.

Примеры

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

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 вызываться не будет. Для его выполнения необходимо явно указать цель при запуске .

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 в операционной системе Unix, однако может быть использоваться для работы с любыми проектами. Первая версия системы была создана в 1977 году.

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

  • GNU make — самый распространенный и функциональный вариант
  • BSD make (pmake) — используется в проектах BSD, по функциональности примерно соответствует GNU make
  • nmake (Microsoft make) — работает под Windows, малофункционален, только базовые принципы make.

Мы работаем с GNU make. На BSD системах (в частности, FreeBSD, он может быть доступен как gmake, на Linux — просто make).

Для чего используются Makefiles

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

В подавляющем большинстве случаев компилируются файлы

C

или

C&plus;&plus;
.

Другие языки обычно имеют свои собственные инструменты, которые служат той же цели, что и Make.

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

В этой статье вы узнаете про использование компиляции C/C&plus;&plus;.

Вот пример графика зависимостей, который вы можете построить с помощью Make.

Если какие-либо зависимости файла изменятся, то файл будет перекомпилирован:

Граф зависимостей

wikipedia.org

Пример

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

  • это макрос, который относится к цели
  • это макрос, который относится к первой зависимости
  • это макрос, который относится ко всем зависимостям
  • это макрос для создания шаблона, который мы хотим наблюдать как в цели, так и в зависимости

Файл make перекомпилирует все объекты, если какой-либо из заголовков изменится, но если изменяется отдельный файл, единственная работа, которая должна быть сделана, — это перекомпилировать этот файл, а затем повторно связать все объекты. Хорошо написанные правила make могут помочь сократить время компиляции за счет определения того, что изменилось, а что нет.

Обратите внимание на то, как используются переменные и правила статического шаблона, чтобы сделать make-файл более расширяемым и читаемым. Мы определяем одно и то же правило многократного использования для создания каждого из каждого и создания каждого из объектов.. Также обратите внимание, что мы можем связать только одну главную сеть за раз, поэтому мы должны отфильтровать другие сети при связывании.

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

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

CC      := gcc
CCFLAGS := 
LDFLAGS :=

TARGETS:= edit
MAINS  := $(addsuffix .o, $(TARGETS) )
OBJ    := kbd.o command.o display.o $(MAINS)
DEPS   := defs.h command.h

.PHONY all clean

all $(TARGETS)

clean
	rm -f $(TARGETS) $(OBJ)

$(OBJ) %.o : %.c $(DEPS)
	$(CC) -c -o $@ $< $(CCFLAGS)

$(TARGETS) % : $(filter-out $(MAINS), $(OBJ)) %.o
	$(CC) -o $@ $(LIBS) $^ $(CCFLAGS) $(LDFLAGS)

Чтобы использовать этот make-файл для создания исполняемого файла с именем edit , введите или . Чтобы использовать этот make-файл для удаления исполняемого файла и всех объектных файлов из каталога, введите .

Базовый синтаксис make

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

target dep1 dep2 ...
  command1
  command2
  ...

Здесь:

  • — цель
  • , — цели, от которых зависит цель

  • , — команды, выполняемые для достижения цели

Например:

style.css srclessapp.less
  lessc srclessapp.less > style.css

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

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

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

Каждая команда запускается в отдельном интерпретаторе shell, таким образом, команды не связаны друг с другом. Иначе говоря, одна строка команды — один shell. Это поведение может быть переопределено с помощью специальной цели .

Если команду (или список зависимостей) необходимо записать в несколько строк, используют символ переноса .

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: