Содержание


Jam - система создания программ из файлов исходного кода

Часть 2. Создание выполняемых программ и библиотек

Comments

Серия контента:

Этот контент является частью # из серии # статей: Jam - система создания программ из файлов исходного кода

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Jam - система создания программ из файлов исходного кода

Следите за выходом новых статей этой серии.

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

1. Правила для создания выполняемой программы

Для сборки целевых выполняемых программ используется правило Main, а его дополняет правило Objects, которое позволяет создавать промежуточные и дополнительные цели.

1.1. Правило Main

Правило Main определяет компиляцию и компоновку (связывание) исходных файлов и соответствующих им объектных файлов в целевой выполняемый файл. Например:

Main my_prog : main.c io_sys.c drv.c util.c ;

Здесь выполняется компиляция исходных файлов main.c, io_sys.c, drv.c и util.c с последующим связыванием (компоновкой - link) полученных после компиляции объектных файлов main.o, io_sys.o, drv.o и util.o в выполняемый файл my_prog. Промежуточные объектные файлы и целевая программа получают расширения имён в соответствии с правилами текущей используемой платформы.

Кроме того, правило Main может быть использовано для создания разделяемых (совместно используемых - shared) и/или динамически связываемых (dynamic link libraries) библиотек, так как принцип их формирования почти тот же, что и для обычных программ. Например:

Main libsupport$(SUFSHR) : sched.c driver.c ;

Обычно Main присваивает создаваемому целевому файлу расширение (suffix), определяемое значением переменной $(SUFEXE). Для того, чтобы изменить присваиваемое расширение, вы можете записать переменную расширения $(SUFSHR) в явной форме, как в приведённом выше примере. В данном случае целевому файлу будет назначено зависимое от платформы расширение, поскольку значение переменной $(SUFSHR) в файле Jamrules задаётся следующим образом (возможны и другие варианты):

if $(UNIX)    { SUFSHR = .so ; }
else if $(NT) { SUFSHR = .dll ; }

1.2. Правило Objects

Как уже было сказано выше, Main использует правила Objects для создания промежуточных целей, как в явной, так и в неявной форме. В вышеприведённом примере имеет место неявная форма правила Objects - создание объектных файлов main.o, io_sys.o, drv.o и util.o скрыто от пользователя. О применении правил Objects в явной форме мы поговорим несколько позже.

2. Правила для создания и связывания библиотек

При создании библиотек применяются правила Library и LinkLibraries.

2.1. Правило Library

По правилу Library выполняется компиляция исходных файлов, сборка полученных объектных файлов в библиотеку, затем удаление этих объектных файлов. Например:

Library libsupport : io_sys.c sched.c drv.c ;
Library libsearch : srch_engn.c reg_exp.c fsm.c ;

Компилируются шесть файлов, из полученных объектных файлов первые три объединяются в библиотеку libsupport, остальные - в библиотеку libsearch. Имена файлов этих библиотек формируются с помощью содержимого переменной $(SUFLIB). После успешного создания библиотечных файлов все объектные файлы удаляются. Здесь также используется правило Objects в неявной, скрытой форме для компиляции исходных файлов.

2.2. Правило LinkLibraries

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

Main my_prog : main.c util.c ;
LinkLibraries my_prog : libsupport libsearch ;

Правило LinkLibraries позволяет установить зависимости цели от указанных библиотек, вследствие чего эти библиотеки будут созданы в первую очередь. Порядок приведённых выше строк не имеет значения, так как Jam в любом случае создаёт цели в "правильном" порядке.

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

LinkLibraries my_prog1 my_prog2 my_prog3 : libsupport libsearch ;

3. Переменные, используемые при создании программ и библиотек

В процессе работы Jam пользуется следующими внутренними переменными:

  • AR - Команда создания архива, используемая для создания целей правила Library
  • SUFEXE - Расширение имени для выполняемых файлов, создаваемых по правилам Main и LinkLibraries
  • LINK - Команда компоновки (связывания), предназначеная для создания целей правила Main
  • LINKFLAGS - Флаги компоновщика (редактора связей)
  • LINKLIBS - Библиотеки, участвующие в компоновке, но не являющиеся зависимостями (подробности см. ниже)
  • EXEMODE - Права доступа к файлам, соответствующим целям правила Main
  • MODE - Права доступа к различным целевым файлам, от которых зависят цели правила Main (устанавливаются на основе переменной $(EXEMODE))
  • RANLIB - Имя ranlib-программы, если такая требуется

Переменные SUFEXE и EXEMODE используются в правилах Main, Library и LinkLibraries. Во время вызова и применения правил значения этих переменных служат основой для установки специализированных переменных, связанных с целями.

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

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

  1. Правило LinkLibraries задаёт требуемые библиотеки, а именно, библиотеки, которые создаются по правилам Library. При этом гарантируется, что эти библиотеки будут созданы в первую очередь, а цели из правила Main всегда будут обновляться при внесении любых изменений в указанные библиотеки.
  2. Можно воспользоваться переменной LINKLIBS, чтобы определить внешние библиотеки, то есть, системные библиотеки, готовые к употреблению библиотеки "третьих сторон" и т.п. Переменная LINKLIBS обязательно должна содержать определения действующих флагов команды компоновки (связывания), которые задают требуемые библиотеки. Например:
# Фрагмент файла Jamrules
if $(UNIX) { X11LINKLIBS = -lXext -lX11 ; }
else if $(NT) { X11LINKLIBS = libXext.lib libX11.lib ; }

# Файл Jamfile
Main my_xprog : my_xprog.c ;
LINKLIBS on my_xprog$(SUFEXE) = $(X11LINKLIBS) ;
LinkLibraries my_xprog : libXsupport ;
Library libXsupport : xfuncts.c xaddons.c xutils.c ;

В этом примере используется особый синтаксис Jam, обозначаемый как "variable on target" (переменная, определяемая по конкретной цели). В данном случае только главная цель my_xprog будет скомпонована с использованием специально заданного значения $(X11LINKLIBS), даже если с помощью этого Jamfile могут быть созданы другие выполняемые файлы. Обратите внимание на то, что при определении переменной для конкретной цели необходимо задавать идентификатор этой цели в явной форме, то есть, в рассматриваемом примере это имя файла с расширением, в котором будет размещена создаваемая программа. Например, в Unix-среде строка команды создания целевого файла со всеми действительными флагами должна выглядеть приблизительно так:

cc -o my_xprog my_xprog.o libXsupport.a -lXext -lX11

4. Компиляция

Компиляция исходных файлов обычно происходит, как промежуточный этап выполнения правил Main и/или Library, которые в скрытой форме вызывают правила, рассматриваемые ниже. Впрочем, если Main и Library действуют не так, как вам нужно, вы в любой момент можете явно вызывать эти "тайные" правила.

4.1. Правило Objects

Это правило вызывается из Main и Library для обработки исходных файлов. Скомпилированные объектные файлы, создаваемые правилом Objects, представляют собой зависимости псевдоцели "obj", поэтому команда jam obj будет формировать все объектные файлы, которые используются при создании целей, определённых в правилах Main и Library.

Вызов этого правила в явной форме выглядит предельно просто:

Objects io_sys.c sched.c drv.c ;

При этом будут созданы объектные файлы io_sys.o, sched.o и drv.o соответственно (расширения имён файлов даны для Unix-платформ; на других платформах расширения могут быть иными).

4.2. Правило Object

Описанное выше правило Objects для каждого из своих исходных файлов вызывает правило Object. Вы сами можете использовать это правило в явной форме:

Object drv.o : drv.c ;

Главный недостаток непосредственного использования правила Object состоит в том, что само по себе оно не различает кроссплатформенные расширения файлов и относительные путевые имена при формировании идентификаторов целей в тех случаях, когда в проекте применяются правила SubDir (то есть, исходные файлы распределены по многим подкаталогам). Если можно так выразиться, "переносимый и обеспечивающий надёжную работу" Jamfile должен вызывать правила Object следующим образом:

SubDir TOP src utils ;
...
Object <src!utils>util$(SUFOBJ) : <src!utils>util.c

Вполне очевидно, что это очень громоздко и очень неудобно. Именно поэтому рекомендуется везде, где возможно, пользоваться правилом Objects вместо Object.

Тем не менее, у правила Object есть и свой "плюс": имена целевых и исходных файлов не обязаны быть одинаковыми. Поэтому при необходимости один исходный файл может быть скомпилирован в различные объектные:

Object xxx.o : my_src.c ;
Object yyy.o : my_src.c ;
Object zzz.o : my_src.c ;

Такой "трюк" может оказаться полезным в некоторых экзотических случаях.

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

Object grammar$(SUFOBJ) : grammar.y ;
Object parser$(SUFOBJ) : parser.l ;
Object util$(SUBOBJ) : util.c ;

И всё же - ещё раз напомню, насколько проще и компактнее то же самое можно записать, используя правило Objects:

Objects grammar.y parser.l util.c ;

Правила для вызова конкретных средств обработки исходных файлов (в данном примере: yacc, lex и C-компилятор соответственно) определены в Jambase. Вы можете определять свои правила в файле Jamrules.

4.3. Переменные, используемые при компиляции

CC - определяет компилятор языка C

C++ - определяет компилятор языка C++

CCFLAGS и C++FLAGS

Флаги компиляции, используемые для создания и обновления компилируемых объектов

SUBDIRCCFLAGS и SUBDIRC++FLAGS

Дополнительные флаги для компиляции исходные файлы в текущем каталоге и подкаталогах.

OPTIM - флаги оптимизации при компиляции (оптимизация выделена специально, для большей гибкости)

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

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

SUBDIRHDRS - Пути в дополнение к HDRS для исходных файлов в текущем каталоге.

LEX - Определяет команду lex (или её аналог).

YACC - Определяет команду yacc (или её аналог).

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

Library libXimage ; xjpeg.c xpng.c xgif.c ;
...
HDRS on xjpeg$(SUFOBJ) = /usr/local/src/jpeg ;
CCFLAGS on xgif$(SUBOBJ) = -DHAVE_GIF ;

5. Обработка заголовочных файлов

Одной из задач правила Object является настройка поиска в исходных файлах с целью выявления имён требуемых включаемых заголовочных файлов. Для этого предназначены специальные переменные $(HDRSCAN) и $(HDRRULE), определяемые именно для исходных файлов. Если эти переменные установлены, то включается механизм Jam, выполняющий сканирование файлов в поисках включаемых файлов-заголовков и вызов правил в соответствии с результатами сканирования. Переменная $(HDRSCAN) указывает на утилиту egrep, для которой задан образец поиска "#include". Переменная $(HDRRULE) указывает на имя правила, вызываемого следующим образом:

$(HDRRULE) имя_исходного_файла : имена_включаемых_файлов ;

Предполагается, что это правило устанавливает зависимости между исходным файлом и включаемыми файлами. Для этого в Object используется правило HdrRule, которое в свою очередь обращается к другой переменной, $(HDRSEARCH), чтобы получить список каталогов для поиска включаемых файлов. Обычно при установке $(HDRSEARCH) объединяются значения $(HDRS) и $(STDHDRS).

5.1. Правило HdrRule

В большинстве случаев HdrRule вызывается не напрямую, а из правила Object, которое, в свою очередь, вызывается из правил Main и Library.

Если необходимо установить какие-либо особенные зависимости, то вы можете написать собственное правило и обеспечить в нём вызов HdrRule. Например:

# В файле Jamrules определяется новое правило
rule BuiltHeaders
{
  DEPENDS $(>) : make_header$(SUFEXE) ;
  HdrRule $(<) : $(>) ;
}

# В Jamfile это правило вызывается
Main make_header : make_header.c ;
Main foobar : foobar.c ;

HDRRULE on foobar.c = BuiltHeaders ;

В этом примере показано, что файлы, включаемые в исходный файл foobar.c, генерируются программой make_header, которая создаётся из исходного файла make_header.c. На этапе связывания (binding) Jam сканирует foobar.c, и если находит, например, включаемые файлы foo.h и bar.h, то автоматически вызывает ранее определённое нами правило:

BuiltHeaders foobar.c : foo.h bar.h ;

Заключение

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


Ресурсы для скачивания


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source, Linux
ArticleID=513280
ArticleTitle=Jam - система создания программ из файлов исходного кода: Часть 2. Создание выполняемых программ и библиотек
publish-date=08262010