Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Extender Manual

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 45. Boost.Build User Manual

BoostC++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Extender Manual

Introduction

В этом разделе рассказывается о том, как увеличить объем. Постройте, чтобы удовлетворить ваши местные требования и #8212; прежде всего, чтобы добавить поддержку нестандартных инструментов. Прежде чем мы начнем, убедитесь, что вы прочитали и подтолкнули концепцию метацели,раздел под названием “ Концепции ”, который имеет решающее значение для понимания оставшегося материала.

Текущая версия Boost. Строительство имеет три уровня целей, перечисленных ниже.

metatarget

Объект, созданный из деклараций в Jamfiles. Может быть вызван с набором свойств для получения конкретных целей.

concrete target

Объект, соответствующий файлу или действию.

jam target

Конкретная цель низкого уровня, характерная для Boost. Двигатель джема. По сути, строка & #8212; чаще всего имя файла.

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

[Warning] Warning

Весь рост. Встроенные функции, связанные с джемом, такие как<DEPENDS>или<ALWAYS>, работают на джем-мишенях. Применение их к метацелям или конкретным целям не имеет никакого эффекта.

Metatargets

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

Способгенерироватьберет свойства сборки (в качестве примеракласса свойств) и возвращает список, содержащий:

  • Как передний элемент & #8212; Требования к использованию из этого вызова (примернабор свойств)

  • В качестве последующих элементов— созданы конкретные цели ( экземпляры<virtual-target>класса).

Вы можете искать метатагет с помощью функции<targets.resolve-reference>, а функция<targets.generate-from-reference>может как искать, так и генерировать метатаргет.

Классабстрактная цельимеет три непосредственных производных класса:

  • проект-цель, который соответствует проекту и не предназначен для дальнейшей подклассировки. Методгенерацииэтого класса строит все цели в проекте, которые не обозначены как явные.

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

  • базовая цельсоответствует конкретной целевой альтернативе. Это базовый класс с рядом производных классов. Методгенерируетобрабатывает целевые требования и запрашиваемые свойства построения для определения конечных свойств для цели, строит все источники и, наконец, вызывает абстрактныйметод построениясо списком исходных виртуальных целей и конечных свойств.

Примеры классовпроект-цельиосновная цельсоздаются неявно— при загрузке нового Jamfiles или при создании новой альтернативы цели с пока неизвестным названием. Примеры классов, полученных избазовой цели, обычно создаются, когда Jamfile называетправило метацели, такое как<exe>.

Допускается создание пользовательского класса, полученного избазовой цели, и создание нового правила метацели, создающего экземпляр такой цели. Однако в большинстве случаев используется конкретный подклассбазовой целии #8212;типизированной цели. Этот класс связан стипоми реле кгенераторамдля построения конкретных целей этого типа. Этот процесс будет объяснен ниже. При объявлении нового типа автоматически определяется новое правило метацели. Это правило создает новый пример типа-цели, связанный с этим типом.

Concrete targets

Конкретные цели представлены примерами классов, полученных из<virtual-target>. Наиболее часто используемым подклассом является<file-target>. Целевой файл связан с действием, которое создает его— экземпляр класса<action>. Акция, в свою очередь, содержит список целей. Он также содержитпример набора свойствсо свойствами сборки, которые должны использоваться для действия.

Вот пример создания цели из другой цели<source>.

local a = [ new action $(source) : common.copy : $(property-set) ] ;
local t = [ new file-target $(name) : CPP : $(project) : $(a) ] ;

Первая линия создает экземпляр класса<action>. Первым параметром является список источников. Второй параметр — это название джем-уровнядействия. Третий параметр — это набор свойств, применяемый к этому действию. Вторая линия создает цель. Мы определяем имя, тип и проект. Мы также передаем объект действия, созданный ранее. Если действие создает несколько целей, мы можем повторить вторую линию несколько раз.

В некоторых случаях код, который создает конкретные цели, может быть использован более одного раза с теми же свойствами. Возвращение к другому экземпляру<file-target>, который соответствует одному и тому же файлу, явно приведет к проблемам. Поэтому всякий раз, когда вы возвращаете цели, вы должны передавать их через функцию<virtual-target.register>, кроме того, что позволяет увеличить. Чтобы отслеживать, какие виртуальные цели были созданы для каждой мета-мишени, это также заменит цели на ранее созданные идентичные.Вот несколько примеров:

return [ virtual-target.register $(t) ] ;
return [ sequence.transform virtual-target.register : $(targets) ] ;

Generators

Теоретически, все виды мета-целей в росте. Построение (например,<exe>,<lib>или<obj>) может быть реализовано путем написания нового класса метацелей, который независимо от другого кода определяет, какие файлы создавать и какие команды использовать. Однако это было бы довольно негибко. Например, добавление поддержки нового компилятора потребует редактирования нескольких метацелей.

На практике большинство файлов имеют определенные типы, и большинство инструментов потребляют и производят файлы определенного типа. Чтобы воспользоваться этим фактом, Буст. Build определяет понятие целевого типа игенераторови имеет специальный метацелевой класстипизированный-цель. Целевой тип — это просто идентификатор. Он связан с набором расширений файлов, которые соответствуют этому типу. Генератор — это абстракция инструмента. Он рекламирует типы, которые он производит, и, если его вызвать с набором входных целей, пытается построить выходные цели рекламируемых типов. Наконец,типизированная цельсвязана с конкретным типом цели и ретранслирует генератор (или генераторы) для этого типа.

Генератор является примером класса, полученного из<generator>. Класс<generator>подходит для обычных случаев. Вы можете определить производные классы для пользовательских сценариев.

Example: 1-to-1 generator

Скажем, вы пишете приложение, которое генерирует код C++. Если вы когда-нибудь делали это, вы знаете, что это нехорошо. Встраивать большие части кода C++ в строковые буквы очень неудобно. Гораздо лучшим решением является:

  1. Напишите шаблон генерируемого кода, оставив заполнителей в точках, которые будут меняться.
  2. Доступ к шаблону в вашем приложении и замена заполнителей соответствующим текстом.
  3. Напишите результат.

Это довольно легко достичь. Вы пишете специальные дословные файлы, которые являются только C++, за исключением того, что первая строка файла содержит имя переменной, которая должна быть сгенерирована. Создается простой инструмент, который берет дословный файл и создает cpp-файл с одной<char*>переменной, имя которой взято из первой строки дословного файла и значение которой является правильно цитируемым содержимым файла.

Посмотрим, что поднимется. Строить можно.

Во-первых, Буст. Build не имеет понятия о «дословных файлах». Вы должны зарегистрировать новый тип цели. Это делает следующий код:

import type ;
type.register VERBATIM : verbatim ;

Первый параметрtype.registerдает название заявленного типа. По соглашению, это верхний регистр. Второй параметр — суффикс для файлов этого типа. Так что, если увеличить.<code.verbatim>в списке источников, он знает, что это тип<VERBATIM>.

Далее, скажите Буту. Постройте, что дословные файлы могут быть преобразованы в файлы C++ за один шаг сборки. Генераторявляется шаблоном для этапа сборки, который преобразует цели одного типа (или набора типов) в другой. Наш генератор будет называться<verbatim.inline-file>; он преобразует<VERBATIM>файлов в<CPP>файлы:

import generators ;
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;

Наконец, вы должны проинформировать Бута. Создайте команды оболочки, используемые для этой трансформации. Об этом говорится в декларации<actions>.

actions inline-file
{
    "./inline-file.py" $(<) $(>)
}

Теперь мы готовы связать все это вместе. Поместите весь код выше в файл<verbatim.jam>, добавьте<import verbatim ;>к<Jamroot.jam>, и вы можете написать следующее в своем Jamfile:

exe codegen : codegen.cpp class_template.verbatim usage.verbatim ;

Перечисленные дословные файлы будут автоматически преобразованы в исходные файлы C++, скомпилированы и затем связаны с исполняемым кодегеном.

В последующих разделах мы рассмотрим этот пример и подробно рассмотрим все механизмы. Полный код доступен в каталоге<example/customization>.

Target types

Первое, что мы сделали во введении, это объявили новый тип цели:

import type ;
type.register VERBATIM : verbatim ;

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

Первые два параметра правила<type.register>— название нового типа и список связанных с ним расширений. Файл с расширением из списка будет иметь заданный целевой тип. В случае, когда цель заявленного типа генерируется из других источников, будет использоваться первое указанное расширение.

Иногда вы хотите изменить суффикс, используемый для генерируемых целей, в зависимости от свойств сборки, таких как набор инструментов. Например, некоторые компиляторы используют расширение<elf>для исполняемых файлов. Вы можете использовать правило 113:

type.set-generated-target-suffix EXE : <toolset>elf : elf ;

Новый тип цели может быть унаследован от существующего.

type.register PLUGIN : : SHARED_LIB ;

Приведенный выше код определяет новый тип, полученный из<SHARED_LIB>. Первоначально новый тип наследует все свойства базового типа — в частности генераторы и суффикс. Как правило, вы каким-то образом измените новый тип. Например, с помощью<type.set-generated-target-suffix>можно установить суффикс для нового типа. Или можно написать специальный генератор для нового типа. Например, он может генерировать дополнительную метаинформацию для плагина. В любом случае, тип<PLUGIN>может использоваться всякий раз, когда<SHARED_LIB>может. Например, вы можете напрямую связать плагины с приложением.

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

Scanners

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

Для начала нужно объявить новый класс сканера:

class verbatim-scanner : common-scanner
{
    rule pattern ( )
    {
        return "//###include[ ]*\"([^\"]*)\"" ;
    }
}

Вся сложная логика находится в классе<common-scanner>, и вам нужно только переопределить метод, который возвращает регулярное выражение, используемое для сканирования. Скобки в обычном выражении указывают, какая часть строки является именем включенного файла. Будет распознана только первая скобчатая группа в регулярном выражении; если вы не можете выразить все, что хотите таким образом, вы можете вернуть несколько регулярных выражений, каждое из которых содержит скобчатую группу, подходящую.

После этого необходимо зарегистрировать класс сканера:

scanner.register verbatim-scanner : include ;

Значение второго параметра, в данном случае<include>, определяет свойства, которые содержат список путей, которые следует искать по включенным файлам.

Наконец, новый сканер присваивается целевому типу<VERBATIM>:

type.set-scanner VERBATIM : verbatim-scanner ;

Этого достаточно для сканирования зависимостей.

Tools and generators

В этом разделе будет описано, как увеличить. Строительство может быть расширено для поддержки новых инструментов.

Для каждого дополнительного инструмента – буст. Необходимо создать объект, называемый генератором. Этот объект имеет определенные типы целей, которые он принимает и производит. Используя эту информацию, Boost. Build может автоматически вызывать генератор. Например, если вы объявите генератор, который принимает цель типа<D>и производит цель типа<OBJ>, при размещении файла с протяженностью<.d>в списке источников вызовет Boost. Создайте, чтобы вызвать генератор, а затем связать полученный объектный файл с приложением. (Конечно, это требует, чтобы вы указали, что расширение<.d>соответствует типу<D>.)

Каждый генератор должен быть экземпляром класса, полученного из класса<generator>. В простейшем случае вам не нужно создавать производный класс, а просто создать экземпляр класса<generator>. Давайте рассмотрим пример, который мы видели во введении.

import generators ;
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
actions inline-file
{
    "./inline-file.py" $(<) $(>)
}

Объявляем стандартный генератор, указывая его идентификатор, тип источника и тип цели. При вызове генератор создаст цель типа<CPP>с целью источника типа<VERBATIM>в качестве единственного источника. Но какая команда будет использоваться для создания файла? В Boost.Build действия уточняются с помощью названных блоков «действий», а при создании целей следует указывать название блока действий. По соглашению, генераторы используют то же название блока действия, что и их собственный идентификатор. Так, в приведенном выше примере блок действий «встроенного файла» будет использоваться для преобразования источника в цель.

Существует два основных типа генераторов: стандартные и составные, которые зарегистрированы по<generators.register-standard>и<generators.register-composing>правилам соответственно. Например:

generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
generators.register-composing mex.mex : CPP LIB : MEX ;

Первый (стандартный) генератор беретодиночныйисточник типа<VERBATIM>и производит результат. Второй (составляющий) генератор принимает любое количество источников, которые могут иметь тип<CPP>или<LIB>. Составные генераторы обычно используются для генерации целевого типа верхнего уровня. Например, первый генератор, используемый при создании<exe>цели, представляет собой составной генератор, соответствующий соответствующему линкеру.

Вы также должны знать о двух конкретных функциях регистрации генераторов:<generators.register-c-compiler>и<generators.register-linker>. Первый настраивает сканирование зависимостей заголовка для файлов C, а секунды обрабатывают различные сложности, такие как библиотеки поиска. По этой причине вы всегда должны использовать эти функции при добавлении поддержки компиляторов и линкеров.

(Нужна записка о UNIX)

Custom generator classes

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

class custom-generator : generator
{
    rule __init__ ( * : * )
    {
        generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
    }
}
generators.register
  [ new custom-generator verbatim.inline-file : VERBATIM : CPP ] ;

Этот генератор будет работать точно так же, как генератор<verbatim.inline-file>, который мы определили выше, но можно настроить поведение с помощью основных методов класса<generator>.

Есть два способа заинтересовать. Метод<run>отвечает за общий процесс — он принимает ряд исходных целей, преобразует их в правильные типы и создает результат. Метод<generated-targets>называется, когда все источники преобразуются в правильные типы для фактического создания результата.

Метод<generated-targets>может быть переопределен, если вы хотите добавить дополнительные свойства к генерируемым целям или использовать дополнительные источники. Например, предположим, что у вас есть инструмент анализа программы, который должен иметь имя исполняемого файла и список всех источников. Естественно, вы не хотите перечислять все исходные файлы вручную. Вот как метод 150 может автоматически найти список источников:

class itrace-generator : generator {
....
    rule generated-targets ( sources + : property-set : project name ? )
    {
        local leaves ;
        local temp = [ virtual-target.traverse $(sources[1]) : : include-sources ] ;
        for local t in $(temp)
        {
            if ! [ $(t).action ]
            {
                leaves += $(t) ;
            }
        }
        return [ generator.generated-targets $(sources) $(leafs)
          : $(property-set) : $(project) $(name) ] ;
    }
}
generators.register [ new itrace-generator nm.itrace : EXE : ITRACE ] ;

Метод<generated-targets>будет называться с одной исходной мишенью типа<EXE>. Призыв к<virtual-target.traverse>возвращает все цели, от которых зависит исполняемый файл, и мы также находим файлы, которые не производятся из чего-либо. Найденные цели добавляются к источникам.

Метод<run>может быть перегружен, чтобы полностью настроить работу генератора. В частности, преобразование источников в желаемые типы может быть полностью настроено. Вот еще один реальный пример. Тесты для библиотеки Boost Python обычно состоят из двух частей: программы Python и файла C++. Файл C++ компилируется с расширением Python, которое загружается программой Python. Но в вероятном случае, если оба файла имеют одинаковое имя, созданное расширение Python должно быть переименовано. В противном случае программа Python будет импортировать себя, а не расширение. Вот как это можно сделать:

rule run ( project name ? : property-set : sources * )
{
    local python ;
    for local s in $(sources)
    {
        if [ $(s).type ] = PY
        {
            python = $(s) ;
        }
    }
    
    local libs ;
    for local s in $(sources)
    {
        if [ type.is-derived [ $(s).type ] LIB ]
        {
            libs += $(s) ;
        }
    }
    local new-sources ;
    for local s in $(sources)
    {
        if [ type.is-derived [ $(s).type ] CPP ]
        {
            local name = [ $(s).name ] ;    # get the target's basename
            if $(name) = [ $(python).name ]
            {
                name = $(name)_ext ;        # rename the target
            }
            new-sources += [ generators.construct $(project) $(name) :
              PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ;
        }
    }
    result = [ construct-result $(python) $(new-sources) : $(project) $(name)
                 : $(property-set) ] ;
}

Во-первых, мы разделяем все исходные тексты на файлы Python, библиотеки и C++. Для каждого источника C++ мы создаем отдельное расширение Python, вызывая<generators.construct>и передавая источник C++ и библиотеки. При необходимости мы также меняем название расширения.

Features

Часто нам нужно контролировать опции, переданные назвавшимися инструментами. Это делается с помощью особенностей. Рассмотрим пример:

# Declare a new free feature
import feature : feature ;
feature verbatim-options : : free ;
# Cause the value of the 'verbatim-options' feature to be
# available as 'OPTIONS' variable inside verbatim.inline-file
import toolset : flags ;
flags verbatim.inline-file OPTIONS <verbatim-options> ;
# Use the "OPTIONS" variable
actions inline-file
{
    "./inline-file.py" $(OPTIONS) $(<) $(>)
}

Сначала определим новую особенность. В 157-м стихе говорится, что всякий раз, когда Действия inline-file выполняются, значение функции<verbatim-options>добавляется к переменной<OPTIONS>и может использоваться внутри тела действия. Вам нужно будет обратиться за онлайн-помощью (--помощью), чтобы найти все особенности правила<toolset.flags>.

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

Большинство функций должны иметь фиксированный набор значений, который является портативным (инструментно-нейтральным) по всему классу инструментов, с которыми они предназначены для работы. Пользователь не должен корректировать значения для точного инструмента. Например,<<optimization>speed>имеет одинаковое значение для всех компиляторов C++, и пользователю не нужно беспокоиться о точных опциях, переданных командной строке компилятора.

Помимо таких переносных функций, существуют специальные «сырые» функции, которые позволяют пользователю передавать любое значение параметрам командной строки для конкретного инструмента, если это необходимо. Например, функция<<cxxflags>>позволяет передавать любые параметры командной строки компилятору C++. Функция<<include>>позволяет пропускать любую строку, предшествующую<-I>, и интерпретация зависит от инструмента. (См.раздел под названием “ Могу ли я получить вывод внешней программы с помощью Boost). Переменная джема? & #8221;для примера очень умного использования этой функции. Конечно, всегда следует стремиться использовать портативные функции, но они по-прежнему предоставляются в качестве бэкдора, чтобы убедиться, что они улучшаются. Построение не отнимает у пользователя никакого контроля.

Использование портативных функций является хорошей идеей, потому что:

  • Когда портативной функции присваивается фиксированный набор значений, вы можете построить свой проект с двумя различными настройками функции и Boost. Build будет автоматически использовать два разных каталога для генерируемых файлов. Повышаю. Строить не пытаются отдельные цели, построенные с разными сырыми вариантами.

  • В отличие от функций “raw”, вам не нужно использовать определенные флаги командной строки в вашем Jamfile, и он с большей вероятностью будет работать с другими инструментами.

Steps for adding a feauture

Добавление функции требует трех шагов:

  1. Объявление признака. Для этого используется правило «feature.feature». Вы должны выбрать набор атрибутов:

    • , если вы хотите, чтобы набор значений функции для одной цели автоматически распространялся на ее зависимые цели, тогда сделайте его “распространенный”.

    • если функция не имеет фиксированного списка значений, она должна быть “free.” Например, функция<include >является бесплатной функцией.

    • если функция используется для обозначения пути относительно Jamfile, она должна быть функцией “path”. Такие функции также автоматически преобразуют свои значения в Boost. Создайте внутреннее представление пути. Например,<include>является признаком пути.

    • если функция используется для обозначения некоторой цели, она должна быть функцией “зависимость”.

  2. Представление значения признака в целевой переменной. Действия построения — это шаблоны команд, модифицированные Boost. Переменные расширения Jam. Правило<toolset.flags>устанавливает целевую переменную для значения признака.

  3. Использование переменной. Переменная, установленная на шаге 2, может использоваться в действии сборки для формирования параметров команды или файлов.

Another example

Вот еще один пример. Давайте посмотрим, как мы можем создать функцию, которая относится к цели. Например, при связывании динамических библиотек в Windows иногда требуется указать «файл DEF», сообщив, какие функции следует экспортировать. Было бы неплохо использовать этот файл так:

        lib a : a.cpp : <def-file>a.def ;

Собственно, эта функция уже поддерживается, но в любом случае...

  1. Поскольку функция относится к цели, она должна быть «зависимостью».

    <
    feature def-file : : free dependency ;
    
    >

  2. Одним из инструментов, который заботится о файлах DEF, является msvc. К нему следует добавить следующую строку.

    <
    flags msvc.link DEF_FILE <def-file> ;
    
    >

  3. Поскольку переменная DEF_FILE не используется действием msvc.link, нам необходимо изменить ее следующим образом:

    <
    actions link bind DEF_FILE
    {
        $(.LD) .... /DEF:$(DEF_FILE) ....
    }
    
    >

    Заметьте часть<bind DEF_FILE>. Это говорит Бусту. Создайте для перевода внутреннего целевого имени в<DEF_FILE>соответствующее имя файла в<link>действии. Без него расширение<$(DEF_FILE)>было бы странным символом, который вряд ли имеет смысл для линкера.

    Мы почти закончили, за исключением добавления кода фоллвинга к<msvc.jam>:

    <
    rule link
    {
        DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ;
    }
    
    >

    Это обходной путь для жука в Бусте. Постройте двигатель, который, как мы надеемся, будет исправлен в один прекрасный день.

Variants and composite features.

Иногда вы хотите создать ярлык для некоторых функций. Например,<release>является значением<<variant>>и является ярлыком для набора признаков.

Можно определить собственные варианты сборки. Например:

variant crazy : <optimization>speed <inlining>off
                <debug-symbols>on <profiling>on ;

Определит новый вариант с заданным набором свойств. Вы также можете расширить существующий вариант:

variant super_release : release : <define>USE_ASM ;

В этом случае<super_release>будет расширяться на все свойства, указанные<release>, и дополнительные, которые вы указали.

Вы не можете использовать только функцию<variant>. Вот пример, который определяет совершенно новую функцию:

feature parallelism : mpi fake none : composite link-incompatible ;
feature.compose <parallelism>mpi : <library>/mpi//mpi/<parallelism>none ;
feature.compose <parallelism>fake : <library>/mpi//fake/<parallelism>none ;

Это позволит указать значение функции<parallelism>, которая расширится до ссылки на необходимую библиотеку.

Main target rules

Основное правило цели (например, & #8220;exe& #8221; или & #8220;lib& #8221;) создает цель верхнего уровня. Вполне вероятно, что вы захотите заявить о себе, и есть два способа сделать это.

Первый способ применяется, когда ваше целевое правило должно просто создавать цель определенного типа. В этом случае для вас уже определено правило! Когда вы определяете новый тип, увеличивайте. Построение автоматически определяет соответствующее правило. Название правила получено из названия типа, путем сокращения всех букв и замены подчеркиваний тире. Например, если вы создаете модуль<obfuscate.jam>, содержащий:

import type ;
type.register OBFUSCATED_CPP  : ocpp ;
import generators ;
generators.register-standard obfuscate.file : CPP : OBFUSCATED_CPP ;

и импортировать этот модуль, вы сможете использовать правило «запутанный-cpp» в Jamfiles, который преобразует источник в тип OBFUSCATED_CPP.

Второй способ — написать правило обёртки, которое называет любое из существующих правил. Например, предположим, что у вас есть только одна библиотека в каталоге, и вы хотите, чтобы все файлы cpp в каталоге были скомпилированы в эту библиотеку. Вы можете достичь этого эффекта, используя:

lib codegen : [ glob *.cpp ] ;

Если вы хотите сделать его еще проще, вы можете добавить следующее определение в файл<Jamroot.jam>:

rule glib ( name : extra-sources * : requirements * )
{
    lib $(name) : [ glob *.cpp ] $(extra-sources) : $(requirements) ;
}

Позволяет уменьшить Jamfile, чтобы просто

glib codegen ;

Обратите внимание, что поскольку вы можете связать пользовательский генератор с целевым типом, логика построения может быть довольно сложной. Например, модуль<boostbook>объявляет целевой тип<BOOSTBOOK_MAIN>и пользовательский генератор для этого типа. Вы можете использовать это в качестве примера, если ваше основное правило цели нетривиально.

Toolset modules

Если ваши расширения будут использоваться только в одном проекте, они могут быть помещены в отдельный файл<.jam>и импортированы вашим<Jamroot.jam>. Если расширения будут использоваться во многих проектах, пользователи будут благодарны вам за завершающий штрих.

Правило<using>обеспечивает стандартный механизм загрузки и настройки расширений. Чтобы это работало, ваш модуль должен обеспечить правило<init>. Правило будет называться с теми же параметрами, которые были переданы правилу<using>. Набор допустимых параметров определяется вами. Например, вы можете позволить пользователю указать пути, версии инструментов и другие параметры.

Вот некоторые рекомендации, которые помогают повысить. Построить более последовательно:

  • Правило<init>никогда не должно нарушаться. Даже если пользователь указал неправильный путь, вы должны выпустить предупреждение и продолжить. Конфигурация может быть разделена между различными машинами, а неправильные значения на одной машине могут быть нормальными на другой.

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

    <
    /usr/bin/g++-snapshot
    time g++
    
    >

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

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

    <
    using yfc ;
    using yfc : 3.3 ;
    using yfc : 3.4 ;
    
    >

    Тогда неясно, соответствует ли первая инициализация версии 3.3 инструмента, версии 3.4 инструмента или какой-либо другой версии. Это может привести к созданию двух одинаковых версий.

  • Если возможно,<init>должен быть вызывающим без параметров. В этом случае он должен попытаться автоматически обнаружить всю необходимую информацию, например, путем поиска инструмента в<PATH>или в общих местах установки. Часто это возможно и позволяет пользователю просто написать:

    <
    using yfc ;
    
    >

  • Рассмотрите возможность использования объектов в модуле<tools/common>. Вы можете посмотреть, как<tools/gcc.jam>использует этот модуль в правиле<init>.



Это название является историческим и в конечном итоге будет изменено на<metatarget>.

Этот шаблон создания-затем-регистрации вызван ограничениями Boost. Язык джема. Порт Python, скорее всего, никогда не будет создавать дубликаты целей.


PrevUpHomeNext

Статья Extender Manual раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 45. Boost.Build User Manual может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Chapter 45. Boost.Build User Manual ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-05 01:28:31/0.014770984649658/0