После того, как программа скомпилирована и скомпонована, можно переходить к этапу её установки в системе. Jam предлагает набор правил для копирования, управления и установки программ и вспомогательных файлов. Рассмотрим эти правила подробнее.
Это правило позволяет определять процедуры копирования файлов. Например:
switch $(OS)
{
case UNIX : File config.h : configunix.h ;
case MAC : File config.h : configmac.h ;
case NT* : File config.h : confignt.h ;
}
LOCATE on config.h = $(LOCATE_SOURCE) ;
|
В данном примере заголовочный файл, содержащий конфигурационные данные программы, создаётся в зависимости от целевой платформы.
Отметим, что правило File по умолчанию не использует значение переменной LOCATE_SOURCE, установленное правилом SubDir (несмотря на то, что значение SEARCH_SOURCE используется, как можно понять из вышеприведённых правил File), то есть, вы сами должны задать каталог, в который будут копироваться файлы. Это делается с помощью специальной переменной LOCATE с указанием конкретной цели (в данном случае - это содержимое переменной LOCATE_SOURCE) или через правило MakeLocate, которое мы рассмотрим несколько позже.
Правило HardLink определяет создание целевого файла, как жёсткой ссылки на исходный файл (при этом используется утилита ln):
HardLink config.h : configunix.h ; |
Разумеется, это правило применимо лишь на тех платформах, файловые системы которых поддерживают механизм создания ссылок (в первую очередь unix-подобные системы).
Данное правило позволяет избежать многочисленных вызовов правил File в тех случаях, когда необходимо копировать все файлы в один и тот же целевой каталог. Например:
# В файле Jamrules определяется переменная: MYPROG_DOC = /usr/local/share/doc/myprog ; # В Jamfile определяется правило: Bulk $(MYPROG_DOC) : README INSTALL TODO COPYING AUTHORS NEWS ; |
Это "расширенная" версия правила File, которая в unix-подобных системах гарантированно размещает в целевом файле первую строку "#!/bin/sh" и устанавливает для этого файла права доступа, как для выполняемого (как правило, 0755). Например, для программы, запускаемой через shell-скрипт, можно записать:
Shell /usr/local/bin/myprog : start_myprog.sh ; |
Кроме того, вы можете воспользоваться переменной $(SHELLHEADER) для того, чтобы явно определить первую строку, вставляемую в целевой файл:
Shell /usr/local/bin/myprog : adapt_config.awk ; SHELLHEADER on /usr/local/bin/myprog = "#!/bin/awk -f" |
В данном примере объявляется установка (копирование) awk-скрипта.
Это правило позволяет создать каталог и сделать его местом размещения целевого файла (установив соответствующим образом переменную LOCATE), а также включить этот каталог в список зависимостей для данной цели. Многие правила, определённые в Jambase, используют вызовы MakeLocate, но вы можете сами применять его при необходимости:
GenFile data.tbl : hxtract data.h ; MakeLocate data.tbl : $(DATADIR) ; |
Здесь правило GenFile создаёт целевой файл данных data.tbl из исходного файла data.h. В правиле MakeLocal определяется, что файл data.tbl должен быть помещён в каталог с именем, задаваемым значением переменной $(DATADIR). Если такой каталог отсутствует, то он будет создан.
Необходимо также отметить, что правило MakeLocal вызывает другое правило, определённое в Jambase, MkDir для создания каталогов (в том числе и "цепочки" вложенных подкаталогов, если это необходимо). В правиле MkDir используется значение переменной $(MKDIR) для выбора зависимой от платформы команды создания каталогов.
2.3. Переменные, используемые при управлении файлами
FILEMODE - определяет устанавливаемые по умолчанию права доступа для копируемых файлов.
SHELLMODE - определяет устанавливаемые по умолчанию права доступа для целевых файлов правила Shell.
MODE - определяет права доступа для файлов, копируемых по правилам File, Bulk и Shell. Для File/Bulk берётся текущее значение $(FILEMODE), для Shell - текущее значение $(SHELLMODE).
SHELLHEADER - определяет содержимое первой строки файла, являющегося целевым в правиле Shell (по умолчанию "#!/bin/sh").
3. Установка программ и вспомогательных файлов
В стандартном файле Jambase содержится набор правил и переменных, специально предназначенных для копирования файлов в целевые каталоги и для установки требуемых прав доступа для этих файлов. В unix-системах для этого используется утилита install. Если целевой каталог не был создан заранее, то Jam обеспечит его своевременное создание.
Все файлы, копируемые по правилам Install-группы, являются зависимостями для псевдоцели install. Таким образом, при выполнении команды jam install все предназначенные для установки подцели будут обновлены и скопированы в заданные каталоги. Имеется и противоположная по смыслу псевдоцель: по команде jam uninstall все ранее установленные копии файлов данного проекта будут удалены.
InstallBin - копирование выполняемых файлов и установка соответствующих прав доступа для них по значению переменной $(EXEMODE). Требуется указать имя выполняемого файла с расширением, зависящим от целевой платформы, то есть:
InstallBin $(BINDIR) : myprog$(SUFEXE) ; |
InstallFile - копирование вспомогательных файлов и установка соответствующих прав доступа для них по значению переменной $(FILEMODE):
InstallFile $(DESTDIR) : README INSTALL COPYING AUTHORS ; |
InstallLib - копирование файлов библиотек и установка соответствующих прав доступа для них по значению переменной $(FILEMODE). Требуется указать имя файла библиотеки с расширением, зависящим от целевой платформы, то есть:
InstallLib $(LIBDIR) : libsupport$(SUFLIB) ; |
InstallMan - копирование файлов справочной информации (руководств) в подкаталог manN целевого каталога и установка соответствующих прав доступа для них по значению переменной $(FILEMODE). Если вы подготовили руководство по использованию своей программы в формате man, то установить его можно следующим образом:
InstallMan /usr/local/share/man : myprog.1 |
Страница вашего руководства будет размещена в подкаталоге /usr/local/share/man/man1.
InstallShell - копирование файлов скриптов и установка соответствующих прав доступа для них по значению переменной $(SHELLMODE). Например:
InstallShell $(DESTDIR) : start_prog.sh ; |
В unix-системах переменной $(DESTDIR) в большинстве случаев присаивается значение /usr/local/bin - наиболее подходящее место для "не вполне стандартных" программ.
3.2. Переменные, используемые в правилах группы Install
INSTALL - специальная программа установки (в unix-системах - утилита install).
FILEMODE - права доступа, устанавливаемые по умолчанию для всех устанавливаемых вспомогательных файлов.
EXEMODE - права доступа, устанавливаемые по умолчанию для всех устанавливаемых выполняемых файлов.
SHELLMODE - права доступа, устанавливаемые по умолчанию для всех устанавливаемых файлов скриптов.
MODE - права доступа, устанавливаемые для конкретно заданного целевого файла.
Переменные, указывающие на каталоги (см. примеры из предыдущего раздела), определяются только для удобства и компактности записи. Такие переменные обязательно должны быть указаны, как цели в соответствующих правилах группы Install.
Переменная INSTALL и все *MODE-переменные должны быть глобально установлены до вызовов Install-правил, в которых эти переменные используются.
4. Дополнительные правила управления файлами
Это правило определяет файлы, которые будут удалены по команде jam clean. Каждое правило создания конкретной цели, определяемое в файле Jamrules, должно содержать вызов Clean, который "очищает" дерево целей и промежуточных подцелей, например:
rule ResourceCompiler
{
DEPENDS $(<) : $(>) ;
Clean clean : $(<) ;
}
|
Большинство правил в Jambase вызывают Clean для создаваемых собственных целей, поэтому команда jam clean удаляет все скомпилированные объектные файлы, библиотеки, выполняемые файлы и т.д.
Некоторые промежуточные целевые файлы являются временными, то есть, нужны только на отдельном этапе компиляции и/или компоновки. Правило RmTemps позволяет определить те файлы, которые можно безболезненно удалить сразу после их использования.
При этом необходимо выполнить следующие требования:
- Правило RmTemps должно быть самым последним правилом, вызываемым по отношению к постоянному файлу, который использует заданные временные файлы.
- Правило RmTemps должно вызываться с указанием постоянного (используемого в дальнейшем) файла в качестве "выходной" цели (output target) и временных файлов в качестве "входных" целей (input target).
- В вызове правила RmTemps должны быть заданы в явной и полной форме идентификаторы целей как для постоянного файла, так и для временных (удаляемых) файлов.
Вот пример применения правила RmTemps:
SubDir TOP src add ; GenFile add.y : joinfiles add1.y add2.y add3.y ; Main program : main.c add.y ; RmTemps program$(SUFEXE) : <src!add>add.y ; |
Здесь сразу после создания целевого выполняемого файла program промежуточный файл add.y будет удалён.
5. Пример файла Jamfile для создания приложения
В качестве комплексного примера рассмотрим Jamfile, предназначенный для сборки самой системы Jam на различных платформах. Практически все конструкции, используемые в этом файле, были рассмотрены в этой и в предыдущих статьях. Новыми для читателя являются: выражение "переменная += список_элементов" - добавляет "список_элементов" к содержимому переменной; и выражение "переменная ?= список_элементов" - присваивает "список_элементов" только в том случае, если ранее эта переменная не была определена.
# Jamfile для сборки системы Jam
if $(VMS) { LOCATE_TARGET ?= [.binvms] ; }
else if $(MAC) { LOCATE_TARGET ?= :bin.mac ; }
else { LOCATE_TARGET ?= bin.$(OSFULL[1]:L) ; }
# Выбор целевой платформы
if $(NT) { code = execunix.c filent.c pathunix.c ; }
else if $(OS2) { code = execunix.c fileos2.c pathunix.c ; }
else if $(VMS) { code = execvms.c filevms.c pathvms.c ; }
else if $(MAC) { code = execmac.c filemac.c pathmac.c ; }
else { code = execunix.c fileunix.c pathunix.c ; }
# Дополнительные исходные файлы
if $(UNIX) || $(NEXT_ROOT) { code += jamgram.y ; }
else { code += jamgram.c ; }
# Добавление флагов компиляции, зависящих от платформы
if $(NEXT_ROOT)
{
if $(NT) { CCFLAGS += -DNT ; }
CFLAGS += -g ;
}
else if $(OS) = NT { CCFLAGS += /DNT ; }
if $(OS) = MVS { CCFLAGS += -DMVS ; }
if $(OS)$(OSVER) = AIX41 { CCFLAGS += -D_AIX41 ; }
# Правило создания стандартного "встроенного" файла Jambase
Main mkjambase : mkjambase.c ;
# Определение имени программы jam на раздичных платформах
if $(OS) in NT { PRODUCT = jam.exe ; }
else { PRODUCT = jam ; }
# Правила создания основной цели - выполняемого файла jam
Main $(PRODUCT) : jam.c jambase.c ;
LinkLibraries $(PRODUCT) : libjam.a ;
GenFile jambase.c : mkjambase pbxJambase ;
Library libjam.a : command.c compile.c $(code) expand.c glob.c
hash.c headers.c lists.c make.c make1.c newstr.c
option.c parse.c regexp.c rules.c scan.c search.c
timestamp.c variable.c ;
# Определение условий установки программы, каталогов, в которые будут
# установлены целевые файлы, с учётом различий между платформами
if $(OS) in NT
{
NEXT_ROOT ?= C:\\Programs ;
DSTROOT ?= $(NEXT_ROOT) ;
BINDIR = $(DSTROOT)\\Local\\Developer\\Executables ;
}
else
{
NEXT_ROOT ?= "" ;
DSTROOT ?= $(NEXT_ROOT) ;
BINDIR = $(DSTROOT)/usr/bin ;
}
InstallBin $(BINDIR) : $(PRODUCT) ;
|
Итак, мы рассмотрели основные аспекты использования системы управления сборкой и установкой программ Jam. Кому-то она может показаться излишне запутанной, кому-то - наиболее подходящей для решения своих задач. Отмечу, что Jam используют разработчики таких проектов, как FreeType, ОС Haiku и другие. Разумеется, "старый добрый" make никто не отменял, да и кроме Jam существуют аналогичные системы (SCons, Ant, Waf и т.д.), так что выбор остаётся за вами.
В данной статье, завершающей цикл, рассматривалось управление файлами в системе Jam, то есть, копирование и установка готовых программ и вспомогательных файлов, а также пример файла Jamfile для создания приложения.