Home Map Index Search News Archives Links About LF
[Top bar]
[Bottom bar]
Эта заметка доступна на: English  Castellano  Deutsch  Francais  Nederlands  Portugues  Russian  Turkce  

[Wilbert Berendsen]
автор Wilbert Berendsen

Об авторе:

Wilbert Berendsen - профессиональный музыкант и энтузиаст Linux. Когда-то увлекался ассемблером для Z80. Сегодня использует Linux для всей своей работы. Для разнообразия пишет заметки и поддерживает небольшой сайт на http://www.xs4all.nl/~wbsoft/. Viva open source!


Содержание:

Поручите свою работу make!

[Illustration]

Резюме:

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



 

Введение

Почти каждый пользователь Linux когда-нибудь сталкивался с make : будь то компиляция ядра или программы, инсталляция пакета и т.д. 'Make' - очень важный инструмент для разработки программного обеспечения, но не только.

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

Пример: построение сайта

Задача состоит в следующем - необходимо построить сайт, поддерживаемый разными людьми : Jan позаботиться о содержании, а Piet о форме.

Следовательно нам нужен такой метод построения при котором форма и содержание будут разделены. Одно из решений - брать содержание из базы данных при каждом обращении, используя PHP или Microsoft Active Server Pages. Но это не очень подходит для случая когда содержимое редко меняется.

Рассмотрим другой вариант - несколько простых команд и сайт готов.

Например заголовок помещается в header.html, а footer в footer.html. header.html выглядит следующим образом :

<html><!-- the header -->
<head>
<title>Piet and Jan productions</title>
</head>
<body bgcolor="white">
<table border="0" width="100%"><tr>
<td bgcolor="#c040ff" valign="top">
This is our website<br>
Some rubbish is written down here.<br>
We are very interactive<br>
so this is our telephone number:<br>
<b>0123-456789</b>
</td><td valign="top">
<!-- Put the contents here -->
а footer.html :
<!-- the footer -->
</td></tr></table>
</body></html>
Последовательность следующих команд unix обеспечивает построение странички, включая эти файлы и index.html, созданный Jan :
cat header.html  /home/jan/Docs/website/index.html
echo -n '<hr>Last modification: '
date '+%A %e %B'
cat footer.html
О командах подробнее читайте в манах. Результат работы команд направляется в стандартный поток вывода, где и собирается в файл :
{
  cat header.html  /home/jan/Docs/website/index.html
  echo -n '<hr>Last modification: '
  date '+%A %e %B'
  cat footer.html
} > /home/piet/public_html/index.html
То же самое можно повторить и для файла offer.html. В результате мы создали скрипт для построения вебсайта.

Теперь напишем небольшой shell-скрипт, который будет выполняться только в случае обновления index или header или footer! Так как мы используем Linux - решение должно быть изящным!

И в этот момент появляется make.  

Первое знакомство

Руководство пользователя для GNU make - впечатляющий документ. Но с самого начала описание идет в сторону программирования. Попробую немного расширить смысл объяснений :
    make determines whether a set of commands should be executed, based on the 
time-stamp of the target-file and the time-stamps of the source files.
Другими словами : если один из исходных файлов создан позже target - будет выполнена заданная последовательность команд. Цель этих команд - обновить результат.

Результат - это 'target', а исходные файлы - `prerequisites'. Команды выполняются, если хотя бы один `prerequisites' создан позже target ( или если target не существует ). Если все prerequisites созданы раньше или в одно время с target - команды не выполняются.

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

Make вызывается командой

make target1 target2 ....

target по необходимости (если не указана - создается первая из Makefile). Make ищет Makefile в текущем каталоге.  

Синтаксис Makefile

Makefile создается в любом редакторе и выглядит примерно так:
# This is an example of a Makefile.
# Comments can be put after a hash (#).

target: prerequisites
        command

target: prerequisites
        commando

# and so on and so on.
Начинаем с target, далее (:) и необходимые prerequisites. Если prerequisites слишком много - можно поставить (\\) и продолжить на следующей строке.

На следующей строке перечисляются команды необходимые для выполнения. Каждая строка принимается за отдельную команду. Если необходимо несколько строк для одной команды - ставим (\\) в конце строки. Make соединит команды таким образом, как-будто они перечислены в одной строке. В подобной ситуации необходимо разделить команды точкой с запятой (;) чтобы не возникли ошибки выполнения.

Обратите внимание: Команды начинаются с символа TAB, не с 8-ми пробелов!

Make читает Makefile и принимает решение о том, какие команды надо выполнить.

Если make вызывается без аргументов - будет построена только первая target.  

Makefile для нашего примера

Для нашего примера Makefile будет выглядеть следующим образом :
# This Makefile builds Piets' and Jans' website, the potato-eaters.

all: /home/piet/public_html/index.html /home/piet/public_html/offer.html

/home/piet/public_html/index.html:  header.html footer.html \
                                    /home/jan/Docs/website/index.html
        { \
          cat header.html  /home/jan/Docs/website/index.html ;\
          echo -n '<hr>Last modification: '               ;\
          date '+%A %e %B'                                   ;\
          cat footer.html                                    ;\
        } > /home/piet/public_html/index.html

/home/piet/public_html/offer.html:  header.html footer.html \
                                    /home/jan/Docs/website/offer.html
        { \
          cat header.html  /home/jan/Docs/website/index.html ;\
          echo -n '<hr>Last modification: '               ;\
          date '+%A %e %B'                                   ;\
          cat footer.html                                    ;\
        } > /home/piet/public_html/offer.html

# the end

Мы определил три targets - 'all' и файлы index.html и offer.html. Единственное назначение 'all' - обеспечить наличие index.html и offer.html 'all' не является именем файла, поэтому эта target всегда будет выполняться. (Позже рассмотрим более удобный способ создания targets, которые не являются файлами).

Если header и footer обновились - обновляются обе страницы. Если Jan изменил только одну - одна и обновится. Команда 'make' выполнит всю работу!  

Сделаем Makefile проще

 

Переменные

Благодаря переменным Makefile становится намного проще. Определить их можно следующим образом:
variable = value
# This Makefile builds Piets' and Jans' website, the potato-eaters.

# Directory where the website is stored:
TARGETDIR = /home/piet/public_html

# Jans' directory:
JANSDIR = /home/jan/Docs/website

# Files needed for the layout:
LAYOUT = header.html footer.html

all: $(TARGETDIR)/index.html $(TARGETDIR)/offer.html

$(TARGETDIR)/index.html:  $(LAYOUT) $(JANSDIR)/index.html
        { \
          cat header.html $(JANSDIR)/index.html     ;\
          echo -n '<hr>Last modification: '      ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $(TARGETDIR)/index.html

$(TARGETDIR)/offer.html:  $(LAYOUT) $(JANSDIR)/offer.html
        { \
          cat header.html  $(JANSDIR)/index.html    ;\
          echo -n '<hr>Last modification: '      ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $(TARGETDIR)/offer.html

# the end
Хорошим тоном является использование заглавных букв для имен переменных. Теперь намного проще сменить каталог.

Также можно использовать другой метод для определения документов. Что произойдет, если необходимо поместить определения многих документов в один Makefile - он, по всей видимости, станет очень большим, из-за повторов. Это также можно упростить!  

Pattern Rules

Применение `Pattern Rules' позволяет использовать один и тот же набор команд для разных targets.

При использовании rules немного изменяется синтаксис строки : добавляется поле pattern :

Multiple targets: pattern : prerequisite prerequisite ...
        command
Это поле - выражение, применяемое для всех targets. Знак % используется для объединения частей target-name.

Пример :

/home/bla/target1.html /home/bla/target2.html: /home/bla/% : %
        commands
После прочтения make эта строка разделится на две - вот здесь шаблон и определит какую часть target-name использовать с %.

Знак % в поле prerequisites указывает на ту часть, которая будет скопирована им.

Make представляет вышесказанное следующим образом :

/home/bla/target1.html: target1.html
        commands

/home/bla/target2.html: target2.html
        commands
Знак % в шаблоне`/home/bla/%' берет значение `target1.html' из `/home/bla/target1.html' - следовательно `%' заменяется на `target1.html'.

Для нашего сайта это будет выглядеть так :

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : $(JANSDIR)/% \
                                                  $(LAYOUT)
Теперь осталась одна проблема : как использовать эти переменные с командами? Ведь команды немного различались для разных targets.  

Автоматические переменные

make определяет некоторые переменные для своих нужд. Некоторые из них мы и называем автоматическими. В качестве значений они, во время выполнения команд, содержат target и/или prerequisite.

Переменная $\ используется для хранения имени первой prerequisite, а $@ - для target.

Используя их можно написать следующим образом :

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : $(JANSDIR)/% \
                                                  $(LAYOUT)
        { \
          cat header.html  $<                       ;\
          echo -n '<hr>Last modification: '      ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@
Voilр! Всего одна строка для обоих файлов!

Далее приведем полностью получившийся Makefile :

# This Makefile builds Piets' and Jans' website, the potato-eaters.

# Directory where the website is published:
TARGETDIR = /home/piet/public_html

# Jans' directory:
JANSDIR = /home/jan/Docs/website

# Files needed for the layout:
LAYOUT = header.html footer.html

# These are the webpages:
DOCS = $(TARGETDIR)/index.html $(TARGETDIR)/offer.html


# Please change nothing below this line;-)
# -------------------------------------------------------------

all: $(DOCS)

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT)
        { \
          cat header.html  $<                       ;\
          echo -n '<hr>Last modification: '         ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@

# the end
Картина начинает вырисовываться. Теперь при добавлении новых документов - их легко включить в Makefile, использую переменную DOCS.

Ну и теперь намного проще для человека, использующего этот Makefile - работа его теперь выглядит более наглядно.  

И еще немного оптимизации

Теперь хотелось бы просто перечислить имена файлов в одном месте, без добавления каждый раз названия каталога. Сделаем это следующим образом :
TEXTS = index.html  offer.html  yetanotherfile.html

# Please change nothing below this line;-)
# -------------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))

all: $(DOCS)

# and so on
Здесь мы применяем специальную функцию make : вместо переменной можно использовать полное выражение внутри скобок.

Работа функции $(addprefix prefix,list) заключается в добавлении к каждому элементу из списка префикса. В нашем примере мы добавляем содержимое переменной TARGETDIR и (/).

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

В начале заметки мы говорили, что, несмотря на то, что target 'all' не создает файл 'all'(в строке нет ни одной команды) - эта target всегда строится. Но как быть, если файл с таким именем есть и он создан позже других...?

Очень просто - сообщаем make название target для обязательного построения. Сделаем это следующим образом:

.PHONY: all
Теперь наш Makefile выглядит следующим образом :
# This Makefile builds Piets' and Jans' website, the potato-eaters.

# Directory where the website is published:
TARGETDIR = /home/piet/public_html

# Jans' directory:
JANSDIR = /home/jan/Docs/website

# Files needed for the layout:
LAYOUT = header.html footer.html

# These are the names of the webpages:
TEXTS = index.html  offer.html  yetanotherfile.html

# Please change nothing below this line;-)
# ------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))
.PHONY: all

all: $(DOCS)

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT)
        { \
          cat header.html  $<                       ;\
          echo -n '<hr>Last modification: '         ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@

# the end
Сохраните его и забудьте о нем! Теперь поддержка страниц осуществляется этим скриптом, помещенным возможно в crontab, и, что самое главное для нас, с разделенными формой и содержанием.  

Последние штрихи

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

Данное решение не защищено от нестандартных ситуаций - например Jan в конце своих файлов поставил </body></html> и как следствие footer, созданный Piet, не смогут отобразить многие браузеры. Применение grep, perl или tcl улучшит отображение информации - например размещение некоторых заголовков в начале страницы.

Также Jan может писать заметки не отформатированным тестом и последующее применение команды sed заменит все пустые строки на <P>:

sed -e 's/^\s*$/<p>/g'
Еще один из вариантов - написание текста в LyX и обработка его командой lyx2html для преобразования в HTML. На самом деле подобных вариантов огромное количество!

Мы не приняли во внимание процесс перемещения картинок в каталог веб - его также можно автоматизировать!

В нашем примере Jan должен предоставить Piet права на чтение своего веб каталога. Такой вариант подходит как для больших огранизаций, так и для одного человека. Кроме того можно воспользоваться возможностями NFS.

Надеюсь вы теперь видите как можно упростить ежедневный рутинный труд, создав хороший Makefile!  

Хитрости

 

Дополнительная информация

Более подробную информацию о работе и возможностях make вы найдете в `GNU Make Manual'. Чтение его на компьютере с Linux осуществляется с помощью следующей команды :
info make
Конечно можно читать GNU Make Manual и в GNOME, и в KDE - help browsers или handy tkinfo помогут вам.

Ссылки на дополнительную информацию о make:

Have Fun!  

Страница отзывов

У каждой заметки есть страница отзывов. На этой странице вы можете оставить свой комментарий или просмотреть комментарии других читателей.
 talkback page 

Webpages maintained by the LinuxFocus Editor team
© Wilbert Berendsen, FDL
LinuxFocus.org

Click here to report a fault or send a comment to LinuxFocus
Translation information:
nl -> -- Wilbert Berendsen
nl -> en Philip de Groot
en -> ru Kirill Poukhliakov

2001-05-04, generated by lfparser version 2.8