автор Wilbert Berendsen Об авторе: Wilbert Berendsen - профессиональный музыкант и энтузиаст Linux. Когда-то увлекался ассемблером для Z80. Сегодня использует Linux для всей своей работы. Для разнообразия пишет заметки и поддерживает небольшой сайт на http://www.xs4all.nl/~wbsoft/. Viva open source! Содержание: |
Резюме:
В заметке рассказывается о принципе работы make и использования этого инструмента для других целей, кроме разработки программного обеспечения.
Почти каждый пользователь Linux когда-нибудь сталкивался с make : будь то компиляция ядра или программы, инсталляция пакета и т.д. 'Make' - очень важный инструмент для разработки программного обеспечения, но не только.
В этой заметке мы увидим, что make - отличный инструмент для решения обыденных задач - написание заметки, книги, построение сайта и т.д. Попутно рассмотрим всякие хитрости, которые позволяет делать unix. В конце заметки перечислим области применения make. Обратите внимание - мы будем говорить о Linux, но в принципе make можно использовать на любой ОС.
Следовательно нам нужен такой метод построения при котором форма и содержание будут разделены. Одно из решений - брать содержание из базы данных при каждом обращении, используя 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.
make determines whether a set of commands should be executed, based on theДругими словами : если один из исходных файлов создан позже target - будет выполнена заданная последовательность команд. Цель этих команд - обновить результат.
time-stamp of the target-file and the time-stamps of the source files.
Результат - это 'target', а исходные файлы - `prerequisites'. Команды выполняются, если хотя бы один `prerequisites' создан позже target ( или если target не существует ). Если все prerequisites созданы раньше или в одно время с target - команды не выполняются.
В каталоге необходимо создать файл с именем Makefile, который содержит инструкции для make. Создав такой файл - выполняем 'make' и наблюдаем работу инструмента.
Make вызывается командой
make target1 target2 ....
target по необходимости (если не указана - создается первая из Makefile). Make ищет 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.
# 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' выполнит всю работу!
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 - он, по всей видимости, станет очень большим, из-за повторов. Это также можно упростить!
При использовании 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.
Переменная $\ используется для хранения имени первой 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!
Использование 'phony' targets (.PHONY: target), позволяет легко связать вместе простые функции. Примером может служить настройка ядра Linux.
make menuconfig вызывает интерактивное текстовое меню настройки. make xconfig вызывает интерактивное меню настройки на основе Tcl/Tk под X.
Эти targets ничего не делают с реальной настройкой ядра. Это просто оболочка для других функций (в данном примере функций для настройки ядра).
Вам необходимо создать Makefile со следующими PHONY targets:
Таким образом можно создавать HTML из обычных текстовых файлов с одновременным улучшением внешнего вида документа, например :
TEMPLATE = layout1/Template1.txt /home/httpd/sales/sales.html: sales.html $(TEMPLATE) perl Scripts/BuildPage.pl -template $(TEMPLATE) $< > $@-new mv -f $@-new $@ sales.html: sales.txt aptconvert -toc $@ $<Посмотрите как произойдет обновление если Template1.txt будет изменен.
Если перед командой стоит '@' - она не будет отображена утилитой make:
target: prerequisite @cc -o target prerequisiteЕсли команда начинается с '-', процесс работы make не прекратится при возникновении ошибки (например при удалении несуществующего файла):
.PHONY: clean clean: -rm -r $(tempdir)Если вы просто хотите посмотреть что произойдет при выполнении определенной команды, например make install, но не хотите чтобы она выполнялась - используйте опцию -n в командной строке:
wilbert@nutnix:~ > make -n install install -m 755 program /usr/local/bin install -m 644 program.1 /usr/local/man/man1 wilbert@nutnix:~ >
Если вам нужен следующий знак - $ в качестве части имени файла - используйте двойной ($$):
# A Makefile # Don't try this at home! :-) source = menu.txt help.txt target: $(source) for i in $(source) ;\ do \ if [ "$$i" = "menu.txt" ] ;\ then \ doThis $$i ;\ else \ doThat $$i ;\ fi ;\ done > target
info makeКонечно можно читать GNU Make Manual и в GNOME, и в KDE - help browsers или handy tkinfo помогут вам.
Ссылки на дополнительную информацию о make:
|
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:
|
2001-05-04, generated by lfparser version 2.8