Copyright (C) А.Гавва V-0.4w май 2004

7. Средства управления проектами в системе GNAT

Эта глава описывает средства управления проектами, которые присутствуют в системе компилятора GNAT (GNAT Project Manager). Основой этих средств является Менеджер Проектов GNAT (GNAT Project Manager). Следует заметить, что менеджер проектов появился в системе GNAT версии 3.15 и отсутствует в более ранних версиях. Менеджер проектов позволяет конфигурировать различные свойства для групп файлов с исходными текстами. В частности, он позволяет определять:

7.1 Файлы проектов GNAT

Проект является определенным набором значений для перечисленных выше свойств. Установки этих значений могут быть определены в файле проекта, который является обычным текстовым файлом с Ада-подобным синтаксисом. В общем случае, значением какого-либо свойства может служить строка или список строк. Свойствам, значения которых не определены явно, назначаются значения по умолчанию. Файл проекта может запрашивать значения внешних переменных (определяемых пользователем опций командной строки или переменных окружения), кроме того, для свойств допускается условная установка значений, которая основывается на значениях внешних переменных.

В простейших случаях, исходные файлы проектов зависят только от других исходных файлов одного и того же проекта, или от предопределенных библиотек. Следует заметить, что "зависимость", в данном случае, рассматривается в техническом смысле, например, один Ада-модуль указывает в спецификаторе with какой-либо другой Ада-модуль. Однако, менеджер проектов позволяет также осуществлять более естественное упорядочивание, когда исходные тексты одного проекта зависят от исходных текстов другого проекта (или проектов):

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

Активация менеджера проектов GNAT осуществляется с помощью опции "-Pprojectfile" утилиты gnatmake или управляющей программы gnat (или gnatcmd) При необходимости определения в командной строке значения внешней переменной, установка которой опрашивается в файле проекта, дополнительно можно использовать опцию командной строки "-Xvbl=value". В результате, менеджер проектов GNAT осуществляет анализ и интерпретацию файла проекта, и управляет запуском инструментальных средств системы GNAT основываясь на установках/настройках свойств проекта.

Менеджер проектов поддерживает обширный диапазон стратегий разработки для систем различного размера. Некоторыми легко обрабатываемыми типичными ситуациями являются:

Расположение получаемого в результате сборки проекта исполняемого модуля может быть указано внутри файла проекта или в командной строке путем использования опции "-o". При отсутствии этой опции в файле проекта или командной строке, любой исполняемый файл, генерируемый командой gnatmake, будет помещен в каталог Exec_Dir, который указывается в файле проекта. Если в файле проекта каталог Exec_Dir не указан, то исполняемый модуль будет помещен в каталог в котором сохраняются объектные файлы проекта.

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

Далее, с помощью демонстрируемых примеров, будут рассмотрены основные свойства средств управления проектами GNAT, а также более детально будут рассмотрены синтаксис и семантика файлов проектов.

7.2 Примеры файлов проектов

Рассмотрим несколько простых примеров, которые демонстрируют основные свойства средств управления проектами GNAT и базовую структуру файлов проектов.

7.2.1 Различные опции сборки и каталоги выходных результатов
        для общих исходных файлов

Предположим, что файлами с исходными текстами на Аде являются следующие файлы: "pack.ads", "pack.adb" и "proc.adb". Предположим также, что все они располагаются в каталоге "/common", и файл "proc.adb" содержит главную подпрограмму Proc, которая указывает в спецификаторе with пакет Pack. Необходимо осуществлять компиляцию этих исходных файлов используя два набора опций:

Показанные ниже файлы проектов GNAT, которые позволяют решить поставленную задачу, имеют соответствующие имена "debug.gpr" и "release.gpr", и располагаются в каталоге "/common".

Для наглядности, представим рассматриваемый пример схематически:


/common
  debug.gpr
  release.gpr
  pack.ads
  pack.adb
  proc.adb
/common/debug {-g, -gnata, -gnato, -gnatE}
  proc.ali, proc.o
  pack.ali, pack.o
/common/release {-O2}
  proc.ali, proc.o
  pack.ali, pack.o

Файла проекта "debug.gpr" имеет следующий вид:


project Debug is
  for Object_Dir use "debug";
  for Main use ("proc");

  package Builder is
    for Default_Switches ("Ada") use ("-g");
  end Builder;

  package Compiler is
    for Default_Switches ("Ada")
       use ("-fstack-check", "-gnata", "-gnato", "-gnatE");
  end Compiler;
end Debug;

Файла проекта "release.gpr" имеет следующий вид:


project Release is
  for Object_Dir use "release";
  for Exec_Dir use ".";
  for Main use ("proc");

  package Compiler is
    for Default_Switches ("Ada") use ("-O2");
  end Compiler;
end Release;

Именем проекта, который описан в файле "debug.gpr", является "Debug" (регистр символов значения не имеет). Аналогично, проект, который описан в файле "release.gpr", имеет имя "Release". Для согласованности, файл проекта должен иметь такое же имя как и проект, а расширением имени файла проекта должно быть расширение ".gpr". Такое соглашение не является жестким требованием, однако, при его несоблюдении будет выдаваться предупреждающее сообщение.

Предположим, что текущим каталогом является каталог "/temp". Тогда, согласно установок в файле проекта "debug.gpr", команда


gnatmake -P/common/debug.gpr

будет генерировать вывод объектных файлов и файлов ALI в каталог "/common/debug", и исполняемый файл "proc" (в системе Windows "proc.exe") также будет помещен в каталог "/common/debug".

Подобным образом, согласно установок в файле проекта "release.gpr", команда


gnatmake -P/common/release.gpr

будет генерировать вывод объектных файлов и файлов ALI в каталог "/common/release", а исполняемый файл "proc" (в системе Windows "proc.exe") будет помещен в каталог "/common".

В случаях, когда файл проекта явно не указывает набор каталогов, в которых храняться файлы с исходными текстами, или непосредственный набор исходных файлов, по умолчанию предполагается, что исходными файлами проекта являются файлы с исходными текстами Ады, которые расположены в том же каталоге, в котором находится файл проекта. Таким образом, файлы "pack.ads", "pack.adb" и "proc.adb" являются исходными файлами для обоих проектов.

Различные свойства проекта выражаются в виде атрибутов в стиле языка Ада. Подобным свойством проекта является каталог для сохранения объектных файлов (и файлов ALI), которому соответствует атрибут Object_Dir. Значением атрибута Object_Dir может быть строка или строковое выражение. Каталог для сохранения объектных файлов может быть указан как абсолютный или как относительный путь к каталогу. В последнем случае, указывается относительный путь к каталогу в котором содержится файл проекта. Таким образом, в показанных выше примерах, вывод компилятора направляется в каталог "/common/debug" (для проекта Debug), и в каталог "/common/release" (для проекта Release). В случае, когда значение Object_Dir не указано, значением по умолчанию является каталог в котором содержится файл проекта.

Другим свойством проекта является каталог для сохранения исполняемых файлов (файлов с исполняемыми модулями), которому соответствует атрибут Exec_Dir. Значением атрибута Exec_Dir также может быть строка или строковое выражение, которые указывают абсолютный или относительный путь к каталогу. Когда значение Exec_Dir не указано, значением по умолчанию является каталог указанный для Object_Dir (который, в свою очередь, может быть каталогом в котором содержится файл проекта, при отсутствии указания значения для Object_Dir). Таким образом, в показанных выше примерах, исполняемый файл будет помещен в каталог "/common/debug" для проекта Debug (атрибут Exec_Dir не указан), и в каталог "/common" для проекта Release.

Инструментальные средства системы компилятора GNAT, которые интегрированы с менеджером проектов GNAT, моделируются внутри файла проекта как соответствующие пакеты. В показанных ранее примерах, проект Debug описывает пакеты Builder (соответствует команде gnatmake) и Compiler (соответствует компилятору, команда gcc или gnatgcc), а проект Release описывает только пакет Compiler.

Используемый в файлах проектов синтаксис Ада-пакетов не следует рассматривать буквально. Хотя пакеты файлов проектов обладают внешним сходством с пакетами исходного текста Ады, их нотация является только способом передачи группы свойств именованной сущности. Кроме того, следует заметить, что допустимые к использованию в файлах проектов имена пакетов ограничены предопределенным перечнем имен, находящихся в строгом соответствии с существующими инструментальными средствами системы компилятора GNAT, которые интегрированы с менеджером проектов GNAT, а содержимое таких пакетов ограничено небольшим набором конструкций (в показанных выше примерах пакеты содержат описания атрибутов).

Указание опций для инструментальных средств системы компилятора GNAT, которые интегрированы с менеджером проектов, может быть осуществлено с помощью установки значений соответствующих атрибутов в пакетах файлов проектов, которые соответствуют инструментальным средствам. В показанные выше примеры демонстрируется использование атрибута Default_Switches, который описан в пакетах обоих файлов проектов и является одним из таких атрибутов. В отличие от простых атрибутов, таких как Source_Dirs, атрибут Default_Switches является ассоциативным массивом. При описании такого атрибута необходимо обеспечить какой-либо "индекс" (литеральная строка), а результатом описания атрибута является установка значения "массива" для указанного "индекса". Для атрибута Default_Switches, индексом является язык программирования (в данном случае - Ada), а указанное (после use) значение должно быть списком строковых выражений.

В файлах проектов допускается использование предопределенного набора атрибутов. При этом, одни атрибуты могут быть указаны на уровне проекта, а другие - на уровне пакета проекта. Для любого атрибута, который является ассоциативным массивом, индекс должен быть представлен как литеральная строка, причем, налагаемые на эту строку ограничения (например, имя файла или имя языка программирования) зависят от индивидуального атрибута. Кроме того, в зависимости от атрибута, указываемое значение атрибута должно быть строкой или списком строк.

В показанном ранее проекте Debug, осуществлялась установка опций для двух инструментальных средств: команды gnatmake и компилятора. Таким образом, в файл проекта были включены соответствующие пакеты, и каждый пакет описывал значение атрибута Default_Switches для индекса "Ada". Следует заметить, что пакет соответствующий команде gnatmake именуется Builder. Проект Release подобен проекту Debug, но он содержит только пакет Compiler.

Отметим, что в проекте Debug, все опции, которые начинаются с "-gnat" и указаны в пакете Compiler, могут быть перемещены в пакет Builder, поскольку команда gnatmake передает все подобные опции компилятору.

Одним из свойств проекта является список головных подпрограмм (фактически, список имен файлов с исходными текстами содержащими головные подпрограммы, причем, указание расширений имен файлов - не обязательно). Это свойство указывается атрибутом Main, значением которого является список строк. Когда проект описывает атрибут Main, при запуске команды gnatmake, отсутствует необходимость в указании головной подпрограммы (или головных подпрограмм).

Когда файл проекта не определяет использование какой-либо схемы именования файлов, используется стандартная для GNAT схема именования файлов принимаемая по умолчанию. Механизм, который используется в файлах проектов для определения соглашений именования файлов с исходными текстами, обеспечивается пакетом Naming и будет подробно рассмотрен несколько позже.

Когда файл проекта не определяет значение атрибута Languages (указывает используемые в проекте языки программирования), инструментальные средства GNAT по умолчанию подразумевают, что языком программирования является Ada. В общем случае предполагается, что проект может состоять из файлов с исходными текстами, которые написаны на языках программирования: Ada, C и/или других языках программирования.

7.2.2 Использование внешних переменных

Вместо написания различных самостоятельных файлов проектов, для получения отладочной версии и версии реализации, можно написать единственный файл проекта, который опрашивает состояние внешних переменных (могут быть установлены как переменные окружения или переданы в командной строке), и осуществляет условную подстановку соответствующих значений. Предположим, что исходные тексты "pack.ads", "pack.adb" и "proc.adb" расположены в каталоге "/common". Показанный ниже файл проекта "build.gpr", осуществляет опрос состояния внешней переменной с именем "STYLE", и определяет, таким образом, расположение каталога для сохранения объектных модулей и используемые опции, в зависимости от значения этой переменной. При этом, когда значением переменной "STYLE" является "deb" (debug) - осуществляется сборка отладочной версии, а когда значением переменной является "rel" (release) - версия реализации. По умолчанию, значением переменной "STYLE" является "deb".


project Build is
  for Main use ("proc");

  type Style_Type is ("deb", "rel");
  Style : Style_Type := external ("STYLE", "deb");

  case Style is
    when "deb" =>
      for Object_Dir use "debug";

    when "rel" =>
      for Object_Dir use "release";
      for Exec_Dir use ".";
  end case;


  package Builder is
    case Style is
      when "deb" =>
        for Default_Switches ("Ada") use ("-g");
    end case;
  end Builder;


  package Compiler is
    case Style is
      when "deb" =>
        for Default_Switches ("Ada") use ("-gnata", "-gnato", "-gnatE");

      when "rel" =>
        for Default_Switches ("Ada") use ("-O2");
    end case;
  end Compiler;

end Build;

Тип Style_Type является примером строкового типа (string type), который в файлах проектов является своеобразным аналогом перечислимого типа Ады, и вместо идентификаторов содержит строковые литералы. Переменная Style описана как переменная этого типа.

Форма "external ("STYLE", "deb")" является внешним обращением (или внешней ссылкой - external reference). Первый аргумент такого внешнего обращения является именем внешней переменной (external variable), а второй аргумент - определяет значение, которое будет использоваться как значение по умолчанию в случае отсутствия указанной внешней переменной. Внешняя переменная может быть определена с помощью опции командной строки -X, или, в качестве внешней переменной, может быть использована переменная окружения.

Каждая конструкция case расширяется менеджером проектов согласно значения переменной Style. Таким образом, команда


gnatmake -P/common/build.gpr -XSTYLE=deb

эквивалентна запуску команды gnatmake, которая использует файл проекта "debug.gpr" из ранее рассмотренного примера. Кроме того, для данного примера, аналогичным образом будет обработана команда


gnatmake -P/common/build.gpr

Поскольку значение "deb" является значением по умолчанию для переменной STYLE.

Аналогичным образом, команда


gnatmake -P/common/build.gpr -XSTYLE=rel

является эквивалентом запуска команды gnatmake, которая использует файл проекта "release.gpr" из ранее рассмотренного примера.

7.2.3 Импорт других проектов

Какой-либо компилируемый модуль расположенный в файле с исходным текстом, который принадлежит одному проекту, может зависить от компилируемых модулей расположенных в файлах с исходными текстами, которые принадлежат другим проектам. Для получения такого поведения, зависимый проект должен импортировать проекты, которые содержат необходимые файлы с исходными текстами. Достижение такого эффекта заключено в синтаксисе, который подобен использованию спецификатора with в языке Ада. При этом указываемыми в with сущностями являются строки, которые обозначают файлы проектов.

В качестве простого примера предположим, что два проекта GUI_Proj и Comm_Proj описаны в файлах проектов "gui_proj.gpr" и "comm_proj.gpr", которые расположены в каталогах "/gui" и "/comm" соответственно. Предположим также, что исходными файлами проекта GUI_Proj являются файлы "gui.ads" и "gui.adb", а исходными файлами проекта Comm_Proj являются файлы "comm.ads" и "comm.adb", и файлы каждого проекта размещаются в каталоге соответствующего проекта. Для наглядности, представим схематическую диаграмму:


/gui
  gui_proj.gpr
  gui.ads
  gui.adb

/comm
  comm_proj.gpr
  comm.ads
  comm.adb

Предположим, что в каталоге "/app" необходимо разработать приложение, которое будет указывать в спецификаторе with пакеты GUI и Comm, используя свойства соответствующих файлов проекта (например, установки опций и указание каталога сохранения объектных файлов). Скелет кода для головной процедуры приложения может иметь следующий вид:


with GUI, Comm;
procedure App_Main is
   ...
begin
   ...
end App_Main;

Файл проекта приложения "app_proj.gpr", который позволяет добиться желаемого эффекта, может выглядеть следующим образом:


with "/gui/gui_proj", "/comm/comm_proj";
project App_Proj is
   for Main use ("app_main");
end App_Proj;

Сборка исполняемого файла приложения может быть получена с помощью команды:


gnatmake -P/app/app_proj

При этом, генерация исполняемого модуля app_main осуществляется в том же каталоге где располагается файл проекта "app_proj.gpr".

Когда импортируемый файл проекта использует стандартное расширение имени файла (gpr) указание расширение имени файла в предложении with файла проекта может быть опущено (как показано в примере выше).

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

Следовательно, при наличии переменной окружения ADA_PROJECT_PATH, которая включает каталоги "/gui" и "/comm", файл проекта приложения "app_proj.gpr", может быть написан следующим образом:


with "gui_proj", "comm_proj";
project App_Proj is
   for Main use ("app_main");
end App_Proj;

Следует учитывать, что способность импорта других проектов обладает потенциальной двусмысленностью. Например, один и тот же модуль может присутствовать в различных импортируемых проектах, или такой модуль может присутствовать как в импортируемом проекте, так и в импортирующем проекте. Оба случая являются условием возникновения ошибки. Следует заметить, что для текущей версии менеджера проектов GNAT (версия 3.15) наличие двусмысленного модуля является недопустимым даже тогда, когда импортирующий проект не содержит обращений к этому модулю. Это строгое правило может быть ослаблено в последующих реализациях менеджера проектов.

7.2.4 Расширение существующего проекта

Общим случаем для больших программных систем является наличие множества реализаций общего интерфейса. В терминах Ады, это представляется как множество версий тела пакета для одной и той же спецификации этого пакета. Например, одна реализация может быть безопасна при использовании в многозадачных/многопоточных программах, а другая может быть более эффективно использована в последовательных/однопоточных приложениях В среде GNAT это может быть смоделировано с помощью использования концепции расширения проекта (project extension). Если один проект ("проект-потомок") расширяет другой проект ("проект-предок"), то по умолчанию, все исходные файлы проекта-предка наследуются проектом-потомком, но проект-потомок может заменить версию любого исходного файла проекта-предка новой версией, а также способен добавить новые файлы. Для проектов, такая способность аналогична использованию расширения в объектно-ориентированном программировании. В среде GNAT допускаются иерархии проектов: проект-потомок может служить проектом-предком для другого проекта, и проект, который наследует один проект, может также импортировать другие проекты.

В качестве примера предположим, что каталог "/seq" содержит файл проекта "seq_proj.gpr" и исходные файлы "pack.ads", "pack.adb" и "proc.adb":


/seq
  pack.ads
  pack.adb
  proc.adb
  seq_proj.gpr

Следует заметить, что файл проекта может быть просто пуст (то есть, он не содержит каких-либо описаний атрибутов и/или пакетов):


project Seq_Proj is
end Seq_Proj;

допуская, что все его исходные файлы являются исходными файлами Ады и все они расположены в каталоге проекта.

Предположим, что необходимо предоставить какую-либо альтернативную версию файла "pack.adb" в каталоге "/tasking", но, при этом, сохранить использование существующих версий файлов "pack.ads" и "proc.adb". С этой целью можно описать проект Tasking_Proj, который наследует проект Seq_Proj. Схематически это будет иметь следующий вид:


/tasking
  pack.adb
  tasking_proj.gpr

При этом, файл проекта "tasking_proj.gpr" может быть следующим:


project Tasking_Proj extends "/seq/seq_proj" is
end Tasking_Proj;

Таким образом, используемая в процессе сборки версия файла "pack.adb", будет зависеть от указываемого файла проекта.

Следует заметить, что для решения рассматриваемой задачи можно вместо наследования проекта использовать импорт проекта. Базовый проект base будет содержать исходные файлы "pack.ads" и "proc.adb", последовательный/однопоточный проект будет импортировать проект base и добавлять "pack.adb", а многозадачный/многопоточный проект будет, подобным образом, импортировать проект base и добавлять свою версию "pack.adb", Фактический выбор решения зависит от необходимости замены других исходных файлов оригинального проекта. При отсутствии такой необходимости достаточно использовать импорт проекта. В противном случае, необходимо использовать расширение проекта.

7.3 Синтаксис файлов проектов

Рассмотрим структуру и синтаксис файлов проектов. Проект может быть независимым проектом, который полностью описывается в одном единственным файле проекта. В независимом проекте, любой исходный файл Ады может зависить только от предопределенной библиотеки и/или от других исходных файлов Ады этого же проекта.

Проект может также зависеть от других проектов в одном или обоих следующих случаях:

Отношения зависимости проектов могут быть представлены как ориентированный граф без петель (подграф отображает в дереве отношение "расширения").

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

7.3.1 Базовый синтаксис

Как видно в показанных ранее примерах, файлы проектов обладают Ада-подобным синтаксисом. Минимальный файл проекта имеет следующий вид:


project Empty is

end Empty;

Здесь, идентификатор Empty является именем проекта. Это имя проекта должно присутствовать после зарезервированного слова end, в конце файла проекта, и должно сопровождаться символом точки с запятой (';').

Любое имя в файле проекта, такое как имя проекта или имя переменной, обладает таким же синтаксисом как идентификатор Ады.

Файлы проектов предполагают использование таких же зарезервированных слов, которые используются в языке Ада, а также дополнительные зарезервированные слова: extends, external и project. Следует заметить, что в настоящее время синтаксис файлов проектов реально использует только следующие зарезервированные слова Ады:

case   others   use
end   package   when
for   renames   with
is   type    

Файлы проектов используют такой же синтаксис комментариев, который используется в языке программирования Ада: комментарий начинается с двух следующих подряд символов дефиса ("--") и распространяется вплоть до завершения строки.

7.3.2 Пакеты

Файл проекта может содержать пакеты. Именем пакета должен быть один из предопределенных идентификаторов (не зависит от регистра символов), кроме того, пакет с указанным именем может упоминаться в файле проекта только однократно. Список предопределенных идентификаторов имен для пакетов файлов проектов следующий:

Naming   Binder   Cross_Reference
Builder   Linker   gnatls
Compiler   Finder    

Следует заметитб, что полный список имен пакетов и их атрибуты указываются в файле "prj-attr.adb" (из комплекта файлов с исходными текстами компилятора GNAT).

В простейшем случае, пакет файла проекта может быть пустым:


project Simple is
  package Builder is
  end Builder;
end Simple;

Как будет показано далее, пакет файла проекта может содержать описания атрибутов, описания переменных и конструкции case.

При наличии двусмысленности между именем проекта и именем пакета (в файле проекта), имя всегда обозначает проект. Для предотвращения таких коллизий, рекомендуется избегать именования проектов с помощью имен, которые предназначены для именования пакетов файлов проектов, или использовать имена, которые начинаются с "gnat".

7.3.3 Выражения

Какое-либо выражение является или строковым выражением, или выражением списка строк.

Какое-либо строковое выражение является или простым строковым выражением, или составным строковым выражением.

Какое-либо простое строковое выражение является:

Какое-либо составное строковое выражение является конкатенацией строковых выражений с помощью символа '&'. Например:


       Path & "/" & File_Name & ".ads"

Какое-либо выражение списка строк является простым выражением списка строк или составным выражением списка строк.

Каким-либо простым выражением списка строк является:

Каким-либо составным выражением списка строк является конкатенация простого выражения списка строк и какого-либо выражения с помощью символа '&'. Примечательно, что каждая лексема составного выражения списка строк, за исключением первой, может быть как строковым выражением, так и выражением списка строк. Например:


   File_Name_List := () & File_Name;
   --  в этом списке одна строка

   Extended_File_Name_List := File_Name_List & (File_Name & ".orig");
   --  две строки

   Big_List := File_Name_List & Extended_File_Name_List;
   --  Конкатенация двух списков строк: три строки

   Illegal_List := "gnat.adc" & Extended_File_Name_List;
   --  не допустимо: конкатенация должна начинаться со списка строк

7.3.4 Строковые типы

Значение строковой переменной может быть ограничено списком строковых литералов. Ограниченный список строковых литералов представляется как описание строкового типа.

Примером описания строкового типа может служить следующее:


   type OS is ("NT, "nt", "Unix", "Linux", "other OS");

Переменные строкового типа называют типированными переменными, а все остальные переменные называют нетипированными переменными. Типированные переменные удобно использовать в конструкциях case.

Любое описание строкового типа начинается с зарезервированного слова type, за которым следует имя строкового типа (не зависит от регистра символов), затем следует зарезервированное слово is, далее - помещенный в скобки список из одного или более строковых литералов, которые отделяются друг от друга запятыми, и, в завершение, следует символ точки с запятой.

Строковые литералы, которые помещаются в список, являются зависимыми от регистра символов и должны отличаться друг от друга. Они могут содержать в себе любые допустимые для языка Ада символы, включая пробелы.

Строковый тип может быть описан только на уровне файла проекта, и не может быть описан внутри пакета файла проекта.

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

7.3.5 Переменные

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


   This_OS : OS := external ("OS"); --  описание типированной переменной
   That_OS := "Linux";              --  описание нетипированной переменной

Любое описание типированной переменной начинается с имени переменной за которым следует символ двоеточия, затем следует имя строкового типа, сопровождаемое ":=" и, далее, простое строковое выражение.

Любое описание нетипированной переменной начинается с имени переменной за которым следует ":=", сопровождаемое выражением. Следует заметить, что несмотря на терминологию, такая форма "описания" больше похоже на присваивание чем на описание в языке Ада. Такая форма является описанием в нескольких смыслах:

Описание строковой переменной (типированной или нетипированной) описывает переменную значением которой является строка. Эта переменная может быть использована как строковое выражение. Например:


   File_Name       := "readme.txt";
   Saved_File_Name := File_Name & ".saved";

Описание переменной списка строк описывает переменную значением которой является список строк. Такой список может содержать любое число (нуль и более) строк.


   Empty_List := ();
   List_With_One_Element := ("-gnaty");
   List_With_Two_Elements := List_With_One_Element & "-gnatg";
   Long_List := ("main.ada", "pack1_.ada", "pack1.ada", "pack2_.ada"
                 "pack2.ada", "util_.ada", "util.ada");

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

Одна и та же нетипированная переменная может быть описана более одного раза. В таком случае, новое значение переменной будет заменять ее старое значение, и последующие ссылки (обращения) к этой переменной будут использовать новое значение. Однако, как отмечалось ранее, если переменная была описана как строковая, то все последующие описания должны предоставлять строковое значение. Подобным образом, если переменная была описана как список строк, все последующие описания переменной должны предоставлять значение в виде списка строк.

Любая ссылка к переменной (или обращение к переменной) может иметь несколько форм:

В качестве контекста переменной может служить:

Ссылка (обращение) к переменной может быть использована в выражении.

7.3.6 Атрибуты

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

Используемые имена атрибутов строго ограничены - все имена атрибутов предопределены. Существуют атрибуты проектов и атрибуты пакетов (для каждого пакета). Имена атрибутов не зависят от регистра символов.

Ниже перечислены атрибуты проектов (все они являются простыми атрибутами):

Имя атрибута Значение
 Source_Files   список строк 
 Source_Dirs   список строк 
 Source_List_File   строка
 Object_Dir   строка
 Exec_Dir   строка
 Main   список строк 
 Languages   список строк 
 Library_Dir   строка
 Library_Name   строка
 Library_Kind   строка
 Library_Elaboration   строка
 Library_Version   строка

Ниже перечислены атрибуты пакета Naming:

Имя атрибута Категория Индекс Значение
 Specification_Suffix   ассоциативный массив   имя языка   строка 
 Implementation_Suffix   ассоциативный массив   имя языка   строка 
 Separate_Suffix   простой атрибут   -   строка 
 Casing   простой атрибут   -   строка 
 Dot_Replacement   простой атрибут   -   строка 
 Specification   ассоциативный массив   имя модуля Ады   строка 
 Implementation   ассоциативный массив   имя модуля Ады   строка 
 Specification_Exceptions   ассоциативный массив   имя языка   список строк 
 Implementation_Exceptions   ассоциативный массив   имя языка   список строк 

Ниже перечислены атрибуты пакетов Builder, Compiler, Binder, Linker, Cross_Reference и Finder (см. также "Опции и Файлы проектов")

Имя атрибута Категория Индекс Значение
 Default_Switches   ассоциативный массив   имя языка   список строк 
 Switches   ассоциативный массив   имя файла   список строк 

Дополнительно, пакет Builder обладает однострочными атрибутами Local_Configuration_Pragmas и Global_Configuration_Pragmas; атрибуты пакета Glide не документированы и предназначены для внутреннего использования.

Каждый простой атрибут обладает значением по умолчанию: пустая строка для атрибутов обладающих строковым значением, и пустой список для атрибутов обладающих значением списка строк.

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

Ниже показаны примеры простых описаний атрибутов:


   for Object_Dir use "objects";
   for Source_Dirs use ("units", "test/drivers");

Любое простое описание атрибута начинается с зарезервированного слова for, после которого следует имя атрибута, сопровождаемое зарезервированным словом use, за которым следует выражение (разновидность выражения зависит от атрибута), и, в завершение, следует символ точки с запятой.

Ссылки (обращения) к атрибутам могут быть использованы в выражениях. Общая форма такого обращения имеет вид:


   entity'attribute

Где "entity" является сущностью для которой определен атрибут "attribute". Для атрибутов, которые принадлежат к категории ассоциативных массивов, после имени атрибута необходимо в скобках указать строковый литерал, который используется в качестве индекса. Для наглядности, продемонстрируем несколько примеров:


  project'Object_Dir
  Naming'Dot_Replacement
  Imported_Project'Source_Dirs
  Imported_Project.Naming'Casing
  Builder'Default_Switches ("Ada")

Сущностью "entity" может являться:

Например:


project Prj is
   for Source_Dirs use project'Source_Dirs & "units";
   for Source_Dirs use project'Source_Dirs & "test/drivers"
end Prj;

В показанном выше примере, при первом описании атрибута Source_Dirs, его начальным значением является значение по умолчанию, то есть, пустой список строк. После первого описания, атрибут Source_Dirs является списком строк, который содержит один элемент ("units"), а после второго описания - два элемента ("units" и "test/drivers").

Следует заметить, что показанный выше пример приведен только в качестве демонстрации. На приктике, файл проекта, как правило, будет содержать только одно описание атрибута:


   for Source_Dirs use ("units", "test/drivers");

7.3.7 Атрибуты как ассоциативные массивы

Некоторые атрибуты описываются как ассоциативные массивы. Ассоциативный массив можно рассматривать как функцию, которая принимает в качестве параметра строку, и возвращает строку или список строк как значение результата.

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


   for Implementation ("main") use "Main.ada";
   for Switches ("main.ada") use ("-v", "-gnatv");
   for Switches ("main.ada") use Builder'Switches ("main.ada") & "-g";

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

7.3.8 Конструкция "case"

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


project MyProj is
   type OS_Type is ("Linux", "Unix", "NT", "VMS");

   OS : OS_Type := external ("OS", "Linux");

   package Compiler is
     case OS is
       when "Linux" | "Unix" =>
         for Default_Switches ("Ada") use ("-gnath");
       when "NT" =>
         for Default_Switches ("Ada") use ("-gnatP");
       when others =>
     end case;
   end Compiler;
end MyProj;

Синтаксис конструкции case основан на синтаксисе инструкции выбора case языка Ада (хотя, в данном случае, не существует конструкции null для пустых альтернатив).

Следом за зарезервированным словом case указывается переменная выбора (типированная строковая переменная), далее - зарезервированное слово is, а затем последовательность из одной и/или более альтернатив выбора. Каждая альтернатива выбора состоит из зарезервированного слова when в сопровождении списка строковых литералов (разделяемых символом '|'), или зарезервированного слова others; далее следует лексема "=>". Каждый строковый литерал должен принадлежать к строковому типу переменной выбора. При наличии альтернативы others, она должна быть последней в перечне альтернатив. Завершителем конструкции case служит последовательность "end case;".

После каждой лексемы "=>" присутствует нуль или более конструкций. Внутри конструкции case допускается использовать только другие конструкции case и описания атрибутов. Описания строковых типов, описания переменных и пакетов являются недопустимыми.

Достаточно часто значение переменной выбора указывается как значение внешней ссылки (внешней переменной).

7.4 Исходные, объектные и исполняемые файлы проекта

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

7.4.1 Каталог объектных файлов

Каталог для сохранения объектных файлов проекта является каталогом в котором будут сохраняться результаты работы компилятора (файлы "ALI" и файлы объектных модулей), которые получены в результате обработки непосредственных исходных файлов проекта. Следует заметить, что для унаследованных исходных файлов (при расширении проекта-предка) будет использоваться каталог для сохранения объектных файлов проекта-предка.

Каталог для сохранения объектных файлов указывается внутри файла проекта как значение атрибута Object_Dir.


   for Object_Dir use "objects";

Атрибут Object_Dir содержит строковое значение - путь к каталогу для сохранения объектных файлов проекта. Путь к каталогу может быть указан как абсолютный или как относительный (задан относительно каталога в котором расположен файл проекта). Указываемый каталог должен существовать и должен быть доступен для чтения и записи.

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

7.4.2 Каталог исполняемых файлов

Каталогом для сохранения исполняемых файлов проекта является каталог, который содержит исполняемые файлы головных подпрограмм проекта.

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


   for Exec_Dir use "executables";

Атрибут Exec_Dir содержит строковое значение - путь к каталогу для сохранения исполняемых файлов проекта. Путь к каталогу может быть указан как абсолютный или как относительный (задан относительно каталога в котором расположен файл проекта). Указываемый каталог должен существовать и должен быть доступен для записи.

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

7.4.3 Каталоги исходных файлов

Каталоги с исходными файлами проекта могут быть указаны с помощью атрибута файла проекта Source_Dirs.

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

В случае, когда явно указывается, что значением атрибута Source_Dirs является пустой список:


    for Source_Dirs use ();

предполагается, что проект не содержит исходных файлов.

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


    for Source_Dirs use ("sources", "test/drivers");

Если строка в списке заканчивается на "/**", то каталог, имя которого предшествует двум звездочкам, а также все его подкаталоги (рекурсивно) являются каталогами с исходными файлами проекта:


    for Source_Dirs use ("/system/sources/**");

В показанном выше примере, каталог "/system/sources" и все его подкаталоги (рекурсивно) являются каталогами с исходными файлами проекта.

Чтобы указать, что каталогами с исходными файлами проекта являются каталог содержащий файл проекта и все его подкаталоги, можно описать атрибут Source_Dirs следующим образом:


    for Source_Dirs use ("./**");

Каждый каталог с исходными файлами должен существовать и должен быть доступен по чтению.

7.4.4 Имена исходных файлов

В проекте, который содержит исходные файлы, имена исходных файлов могут быть указаны с помощью атрибутов Source_Files (список строк) и/или Source_List_File (строка). Имена исходных файлов никогда не содержат в себе информацию о каталоге.

Если для атрибута Source_Files дано явное значение, то каждый элемент списка является именем исходного файла проекта.


   for Source_Files use ("main.adb");
   for Source_Files use ("main.adb", "pack1.ads", "pack2.adb");

Если для атрибута Source_Files явное значение не дано, но для атрибута Source_List_File указано строчное значение, то имена исходных файлов содержатся в текстовом файле, полное имя которого является значением атрибута Source_List_File (при этом, путь к каталогу, который содержится в полном имени файла, может быть указан как абсолютный или как заданный относительно каталога в котором располагается файл проекта).

В указанном файле, именем исходного файла является каждая непустая и не являющаяся комментарием строка. Строки комментариев начинаются с двух дефисов.


   for Source_List_File use "source_list.txt";

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

В случае одновременной установки явных значений для атрибутов Source_Files и Source_List_File осуществляется генерация предупреждающего сообщения. При этом, более приоритетным является значение атрибута Source_Files.

Каждое имя исходного файла должно быть именем единственного исходного файла, который существует в одном из каталогов с исходными файлами проекта.

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

Любой проект должен содержать по крайней мере один непосредственный исходный файл, за исключением проектов, для которых точно указано отсутствие исходных файлов Ады (атрибут Source_Dirs или атрибут Source_Files описан как пустой список, или атрибут Languages описан без указания в списке языка "Ada"):


   for Source_Dirs use ();
   for Source_Files use ();
   for Languages use ("C", "C++");

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

7.5 Импорт проектов

Какой-либо непосредственный исходный файл проекта P может зависеть от исходных файлов, которые не являются непосредственными исходными файлами проекта P и не содержатся в предопределенной библиотеке. Для получения такого эффекта проект P должен импортировать проекты, которые содержат необходимые исходные файлы:


  with "project1", "utilities.gpr";
  with "/namings/apex.gpr";
  project Main is
    ...

В показанном выше примере можно увидеть, что синтаксис, который используется для импортирования проектов, подобен синтаксису Ады, который используется для импортирования компилируемых моделей. Однако, в файлах проектов вместо имен используются строковые литералы, и в спецификаторе with указываются файлы проектов а не пакеты.

Каждый строковый литерал является именем файла проекта или полным именем файла проекта (абсолютным или относительным). Если строка является простым именем файла, без указания пути к каталогу, то расположение файла определяется с помощью пути к каталогу проекта (project path):

Если используется относительный путь к каталогу проекта


  with "tests/proj";

то путь к каталогу проекта определяется относительно каталога в котором расположен импортирующий (текущий) файл проекта. Полная расшифровка любых символьных ссылок осуществляется в каталоге импортирующего файла проекта перед обнаружением и выборкой импортируемого файла проекта.

Когда имя файла проекта, которое указано в спецификаторе with, не содержит расширения имени файла, по умолчанию, подразумевается расширение имени файла ".gpr". Если указанное имя файла с таким расширением не обнаружено, то будет использовано имя файла без расширения (как непосредственно указано в спецификаторе with). Таким образом, согласно показанного выше примера: если обнаружен файл "project1.gpr", то он будет использован; в противном случае, если обнаружен файл "project1", то он будет использован; если не найден ни один из этих файлов, то это будет ошибкой.

При несовпадении имени проекта с именем файла проекта, генерируется предупреждающее сообщение (эта проверка не зависит от регистра символов).

Любой исходный файл, который является непосредственным исходным файлом импортируемого проекта, может быть использован непосредственным исходным файлом импортирующего проекта, и рекурсивно. Таким образом, если A импортирует B, а B импортирует C, то непосредственные исходные файлы A могут зависеть от непосредственных исходных файлов C, даже если A не импортирует C явным образом. Однако, использование такого подхода не рекомендуется, поскольку возможна ситуация, когда B прекратит импортировать C; после чего некоторые исходные файлы в A перестанут компилироваться.

Побочным эффектом такой способности является то, что циклические зависимости - не допустимы: если A импортирует B (прямо или косвенно), то для B не разрешается импортировать A.

7.6 Расширение проекта

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


   project Modified_Utilities extends "/baseline/utilities.gpr" is ...

Файл проекта для расширяемого проекта (проект-предок) указывается строковым литералом, указываемым вслед за зарезервированным словом extends, которое следует за именем расширяющего проекта (проект-потомок).

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

Например, если проект Utilities содержит спецификацию и тело Ада-пакета Util_IO, то проект Modified_Utilities может содержать новое тело для пакета Util_IO. Оригинальное тело Util_IO не будет рассматриваться при сборке программы. Однако, при этом будет использоваться спецификация этого пакета, которая расположена в проекте Utilities.

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

7.7 Обращение к внешним переменным в файлах проектов

Файл проекта может содержать обращения к внешним переменным. Такие обращения называют внешними ссылками.

Внешняя переменная может быть определена как часть среды окружения (например, какая-либо переменная окружения UNIX), или указана в командной строке с помощью опции "-Xvbl=value". При одновременном наличии переменной окружения и значения заданного в командной строке, будет использовано значение из командной строки.

Внешняя ссылка указывается с помощью встроенной функции external, которая возвращает строковое значение. Эта функция имеет две формы:

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


   external ("USER")
   external ("OS", "Linux")

В форме с одним параметром, функция возвращает значение внешней переменной, которая указана как параметр. Если в среде окружения такое имя отсутствует, возвращается пустая строка.

В форме с двумя аргументами, второй параметр является значением, которое возвращается когда переменная указанная как первый параметр отсутствует в среде окружения. В показанном выше примере, если "OS" не является именем переменной окружения и не указано в командной строке, то возвращаемым значением будет "Linux".

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


   type Mode_Type is ("Debug", "Release");
   Mode : Mode_Type := external ("MODE");
   case Mode is
     when "Debug" =>
        ...

7.8 Пакеты файлов проектов

Пакет является свойством файла проекта с помощью которого обеспечивается возможность описания установок для индивидуальных инструментов, которые интегрированы со средствами управления проектами системы компилятора GNAT. Внутри файла проекта, для каждого такого инструмента можно описать соответствующий пакет (напомним, что имена пакетов строго предопределены). Пакет может содержать в себе описания переменных, атрибутов и конструкции выбора case.


   project Proj is
      package Builder is  -- используется утилитой gnatmake
         for Default_Switches ("Ada") use ("-v", "-g");
      end Builder;
   end Proj;

Описание пакета начинается с зарезервированного слова package, далее следует имя пакета (не зависит от регистра символов) сопровождаемое зарезервированным словом is. Описание пакета завершается зарезервированным словом end, которое сопровождается именем пакета и символом точки с запятой.

Большинство описываемых пакетов содержит атрибут Default_Switches. Этот атрибут является ассоциативным массивом, а его значением является список строк. Индексом ассоциативного массива является имя языка программирования (не зависит от регистра символов). Этот атрибут указывает опцию или набор опций, которые будут использоваться соответствующим инструментальным средством.

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

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


  with "/global/apex.gpr";
  project Example is
    package Naming renames Apex.Naming;
    ...
  end Example;

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

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

7.9 Переменные импортируемых проектов

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


  with "imported";
  project Main extends "base" is
     Var1 := Imported.Var;
     Var2 := Base.Var & ".new";

     package Builder is
        for Default_Switches ("Ada") use Imported.Builder.Ada_Switches &
                         "-gnatg" & "-v";
     end Builder;

     package Compiler is
        for Default_Switches ("Ada") use Base.Compiler.Ada_Switches;
     end Compiler;
  end Main;

В показанном выше примере:

7.10 Схемы именования файлов

Бывают случаи когда необходимо осуществить перенос программной системы, которая была разработана в среде какого-либо Ада-компилятора, в среду компилятора GNAT. При этом, имена файлов, которые использовались в среде другого Ада-компилятора, могут не соответствовать соглашениям по именованию файлов, которые стандартны и используются по умолчанию в среде GNAT. В такой ситуации, вместо переименования всех файлов, - что может быть практически неосуществимо по целому ряду причин, - в файле проекта, внутри пакета Naming, можно описать необходимую схему именования файлов. Например, показанное ниже описание пакета Naming моделирует соглашения по наименованию, которые традиционны для системы компилятора Apex:


  package Naming is
    for Casing                        use "lowercase";
    for Dot_Replacement               use ".";
    for Specification_Suffix ("Ada")  use ".1.ada";
    for Implementation_Suffix ("Ada") use ".2.ada";
  end Naming;

Внутри пакета Naming могут быть описаны следующие атрибуты:

Имя атрибута   Описание
   
 Casing    Этот атрибут может принимать одно из трех значений: "lowercase", "uppercase" или "mixedcase" (все значения не зависят от используемого регистра символов). Когда значение этого атрибута не указано, по умолчанию, предполагается значение "lowercase".
   
 Dot_Replacement    Значением этого атрибута может быть строка, которая удовлетворяет следующие условия:
  • строка не должна быть пустой
  • строка не может начинаться или заканчиваться буквой или цифрой
  • строка не может состоять из одиночного символа подчеркивания
  • строка не может начинаться одиночным символом подчеркивания сопровождаемым одиночной буквой или цифрой
  • строка не может содержать символ точки ('.'), за исключением случая когда вся эта строка состоит из одного символа точки (".")

Когда значение этого атрибута не указано, по умолчанию, предполагается строка "-".

   
 Specification_Suffix    Атрибут является ассоциативным массивом (индексируется именем языка программирования; не зависит от регистра символов), значением которого может быть строка, удовлетворяющая следующие условия:
  • строка не должна быть пустой
  • строка не может начинаться с символа буквы или цифры
  • строка не может начинаться одиночным символом подчеркивания сопровождаемым одиночной буквой или цифрой

Когда значение атрибута "Specification_Suffix ("Ada")" не указано, по умолчанию, предполагается строка ".ads".

   
 Implementation_Suffix    Атрибут является ассоциативным массивом (индексируется именем языка программирования; не зависит от регистра символов), значением которого может быть строка, удовлетворяющая следующие условия:
  • строка не должна быть пустой
  • строка не может начинаться с символа буквы или цифры
  • строка не может начинаться одиночным символом подчеркивания сопровождаемым одиночной буквой или цифрой
  • строка не может иметь такое же значение как Specification_Suffix

Когда значение атрибута "Implementation_Suffix ("Ada")" не указано, по умолчанию, предполагается строка ".adb".

   
 Separate_Suffix    Значение этого атрибута должно удовлетворять те же условия, что и значение для атрибута Implementation_Suffix. Когда значение атрибута "Separate_Suffix ("Ada")" не указано, по умолчанию, предполагается значение атрибута "Implementation_Suffix ("Ada")".
   
 Specification    Этот атрибут является ассоциативным массивом и может быть использован для описания имен исходных файлов содержащих индивидуальные спецификации компилируемых модулей Ады. Индекс массива должен быть строковым литералом, который указывает модуль Ады (не зависит от регистра символов). Значением атрибута должна быть строка, которая указывает файл содержащий спецификацию компилируемого модуля (зависимость от регистра символов определяется используемой операционной системой).


   for Specification ("MyPack.MyChild")
      use "mypack.mychild.spec";

   
 Implementation    Этот атрибут является ассоциативным массивом и может быть использован для описания имен исходных файлов содержащих индивидуальные тела (возможно субмодули) компилируемых модулей Ады. Индекс массива должен быть строковым литералом, который указывает модуль Ады (не зависит от регистра символов). Значением атрибута должна быть строка, которая указывает файл содержащий тело компилируемого модуля (зависимость от регистра символов определяется используемой операционной системой).


   for Implementation ("MyPack.MyChild")
      use "mypack.mychild.body";

7.11 Проекты библиотек

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

Для создания проекта библиотеки, в файле проекта необходимо описать двухуровневые атрибуты: Library_Name и Library_Dir. Дополнительно, можно описать атрибуты, которые относятся к библиотекам: Library_Kind, Library_Version и Library_Elaboration.

Атрибут Library_Name имеет строчное значение, которое должно начинаться с буквы и должно включать в себе только буквы и цифры.

Атрибут Library_Dir имеет строчное значение, которое обозначает путь к каталогу (абсолютный или относительный) где будет располагаться библиотека. Он должен обозначать уже существующий каталог, и этот каталог должен отличаться от каталога в котором будут сохраняться объектные файлы проекта. Кроме того, этот каталог должен быть доступен для записи.

В случае, когда оба атрибута, Library_Name и Library_Dir, указаны и являются допустимыми, предполагается, что файл проекта описывает проект библиотеки. Только для таких файлов проектов будут осуществляться проверка дополнительных, необязательных атрибутов, которые относятся к библиотекам.

Атрибут Library_Kind является строкой, которая должна иметь одно из следующих значений (указанные значения не зависят от регистра символов): "static", "dynamic" или "relocatable". При отсутствии описания этого атрибута, предполагается, что библиотека является статической (то есть, значение по умолчанию - "static"). В противном случае, библиотека может быть динамической ("dynamic") или перемещаемой ("relocatable"). Следует заметить, что в зависимости от используемой операционной системы, различие между динамической и перемещаемой библиотекой может отсутствовать. Например, в UNIX такое различие отсутствует.

Атрибут Library_Version является строкой, интерпретация смысла которой зависит от платформы. В UNIX, это значение используется только для динамических/перемещаемых библиотек, как внутреннее имя библиотеки (известное как "soname"). Если имя файла библиотеки (построенное из Library_Name) отличается от Library_Version, то файл библиотеки будет символической ссылкой на фактический файл библиотеки, именем которого будет Library_Version.

Пример файла проекта библиотеки (для UNIX):


project Plib is

   Version := "1";

   for Library_Dir      use "lib_dir";
   for Library_Name     use "dummy";
   for Library_Kind     use "relocatable";
   for Library_Version  use "libdummy.so." & Version;

end Plib;

Каталог "lib_dir" будет содержать внутренний файл библиотеки именем которого будет "libdummy.so.1", а "libdummy.so" будет символической ссылкой на "libdummy.so.1".

Когда утилита gnatmake обнаруживает, что файл проекта (не главный файл проекта) является файлом проекта библиотеки, она проверяет все непосредственные исходные файлы проекта и осуществляет пересборку библиотеки если любой из исходных файлов был перекомпилирован. Кроме того, все файлы "ALI" будут скопированы из каталога объектных файлов в каталог библиотеки. При этом, для сборки исполняемых файлов утилита gnatmake будет использовать библиотеку, а не индивидуальные объектные файлы.

7.12 Опции командной строки, относящиеся к файлам проектов

В системе компилятора GNAT, инструментальные средства, которые поддерживают файлы проектов, могут использовать в командной строке следующие опции:

Опция   Описание
   
 -Pproject    Указывает имя файла проекта (project). Этот файл проекта будет анализироваться с учетом степени "многословности", которая может быть указана опцией "-vPx", и используя внешние ссылки, которые могут быть указаны опциями "-X". В командной строке может присутствовать только одна опция "-P". Поскольку менеджер проектов GNAT начинает анализировать файла проекта только после проверки всех опций командной строки, порядок указания опций "-P", "-vPx" и/или "-X" значения не имеет.
   
 -Xname=value    Эта опция позволяет указать в командной строке, что переменная "name" имеет значение "value". Менеджер проектов GNAT будет использовать это значение в процессе анализа файла проекта, при обнаружении внешней ссылки "external (name)". Если "name" или "value" содержит пробелы, то в командной строке "name=value" должно быть помещено между символами кавычек:


  -XOS=NT
  -X"user=John Doe"

Одновременно, в командной строке может быть указано несколько опций "-X". Если в командной строке одновременно несколько опций "-X" указывают значения для одной и той же переменной "name", то будет использовано последнее указанное значение. Значение внешней переменной, которое указано с помощью опции "-X", более приоритетно по сравнению со значением переменной окружения с таким же именем "name".

   
 -vPx    Устанавливает уровень/степень "многословности" при анализе файла проекта. Указание "-vP0" предполагается по умолчанию и подразумевает низкий уровень "многословности" (отсутствует вывод для синтаксически корректных файлов проектов); "-vP1" - подразумевает средний уровень "многословности"; "-vP2" - подразумевает высокий уровень "многословности". Если в командной строке одновременно указаны несколько опций "-vPx", то будет использовано последнее указанное значение.

7.13 Инструментальные средства поддерживающие файлы проектов

7.13.1 Утилита gnatmake и файлы проектов

Рассмотрим два вопроса, которые касаются утилиты gnatmake и файлов проектов: описание опций для утилиты gnatmake и всех инструментальных средств, запуск которых она осуществляет; использование атрибута Main.

Для каждого пакета: Builder, Compiler, Binder и Linker, - можно указать атрибуты Default_Switches и Switches. Имена этих атрибутов подразумевают, что эти атрибуты определяют какие опции (switches - переключатели) и для каких файлов необходимо использовать как аргументы командной строки запуска утилиты gnatmake. Ниже будет показано, что эти ориентированные на пакеты опции предшествуют опциям, которые передаются в командную строку запуска утилиты gnatmake.

Атрибут Default_Switches является ассоциативным массивом, который индексируется именем языка программирования (не зависимо от регистра символов) и возвращает значение в виде списка строк. Например:


package Compiler is
  for Default_Switches ("Ada") use ("-gnaty", "-v");
end Compiler;

Атрибут Switches также вляется ассоциативным массивом. Он индексируется именем файла (которое зависит или не зависит от регистра символов, в зависимости от используемой операционной системы) и возвращает значение в виде списка строк. Например:


package Builder is
   for Switches ("main1.adb") use ("-O2");
   for Switches ("main2.adb") use ("-g");
end Builder;

Для пакета Builder, имена файлов должны обозначать исходные файлы головных подпрограмм. Для пакетов Binder и Linker, имена файлов должны обозначать файлы "ALI" или исходные файлы головных подпрограмм. В каждом случае применимо только самостоятельное имя файла (без явного указания расширения имени файла).

Для каждого используемого в процессе сборки программы инструментального средства (утилита gnatmake, компилятор, редактор связей и компоновщик), соответствующий ему пакет указывает набор опций, которые необходимо использовать при обработке этим инструментальным средством каждого файла. Такой набор используемых опций определяется с помощью описания внутри пакета соответствующих, ориентированных на указание опций атрибутов. В частности, опции, которые каждый такой пакет определяет для указанного файла F, включают:

Когда внутри пакета ни один их этих атрибутов не описан. пакет не указывает никаких опций для данного файла.

При запуске утилиты gnatmake для обработки заданного файла, весь набор опций командной строки запуска утилиты gnatmake состоит из двух множеств, располагаемых в следующем порядке:

  1. набор опций, которые определены для этого файла в пакете Builder
  2. набор опций, которые были переданы в командной строке

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

  1. набор опций, которые определены для этого файла в пакете Builder файла проекта, заданного в командной строке
  2. набор опций, которые определены для этого файла внутри пакета соответствующего инструментального средства (в соответствующем файле проекта)
  3. набор опций, которые были переданы в командной строке

Следует заметить, что опции, заданные в командной строке запуска утилиты gnatmake, могут передаваться или не передаваться индивидуальному инструментальному средству, в зависимости от конкретной опции.

Утилита gnatmake может осуществлять запуск компилятора для обработки исходных файлов различных проектов. Менеджер проектов GNAT будет использовать соответствующий файл проекта, чтобы определить пакет Compiler для каждого исходного файла, который нуждается в компиляции. То же самое справедливо для пакетов Binder (редактор связей) и Linker (компоновщик).

В качестве примера, рассмотрим пакет Compiler в показанном ниже файле проекта:


project Proj1 is
   package Compiler is
      for Default_Switches ("Ada") use ("-g");
      for Switches ("a.adb") use ("-O1");
      for Switches ("b.adb") use ("-O2", "-gnaty");
   end Compiler;
end Proj1;

В этом случае, при запуске утилиты gnatmake с указанием этого файла проекта, когда необходимо осуществить компиляцию файлов "a.adb", "b.adb" и "c.adb", файл "a.adb" будет компилироваться с опцией "-O1", файл "b.adb" будет компилироваться с опциями "-O2" и "-gnaty", а файл "c.adb" будет компилироваться с опцией "-g".

Следующий пример демонстрирует упорядочивание опций, которые предоставлены различными пакетами:


project Proj2 is
   package Builder is
      for Switches ("main.adb") use ("-g", "-O1", "-f");
   end Builder;

   packager Compiler is
      for Switches ("main.adb") use ("-O2");
   end Compiler;
end Proj2;

При использовании команды:


    gnatmake -PProj2 -O0 main

Компилятор будет запущен для компиляции "main.adb" с указанием следующей последовательности опций командной строки:


   -g -O1 -O2 -O0

В этом случае, последняя опция "-O" будет иметь приоритет перед всеми предыдущими опциями "-O". Некоторые другие опции (например, такие как "-c") будут добавлены неявно.

В рассматриваемом примере, опции "-g" и "-O1" обеспечиваются пакетом Builder, опция "-O2" обеспечена пакетом Compiler, а опция "-O0" была указана в командной строке.

Следует заметить, что опция "-g" будет также передаваться при запуске gnatlink.

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


project Proj3 is
   for Source_Files use ("pack.ads", "pack.adb");
   package Compiler is
      for Default_Switches ("Ada") use ("-gnata");
   end Compiler;
end Proj3;

with "Proj3";
project Proj4 is
   for Source_Files use ("foo_main.adb", "bar_main.adb");
   package Builder is
      for Switches ("foo_main.adb") use ("-s", "-g");
   end Builder;
end Proj4;

-- Ada source file:
with Pack;
procedure Foo_Main is
   ...
end Foo_Main;

При использовании команды:


gnatmake -PProj4 foo_main.adb -cargs -gnato

Передаваемыми компилятору опциями, для компиляции "foo_main.adb", являются опция "-g" (обеспечивается пакетом Proj4.Builder) и опция "-gnato" (передается из командной строки). При компиляции импортируемого Ада-пакета Pack, будут использоваться опция "-g" (из пакета Proj4.Builder), опция "-gnata" (из пакета Proj3.Compiler) и опция "-gnato" (передается из командной строки).

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


    gnatmake -Pprj main1 main2 main3

Кроме того, при использовании файла проекта, запуск утилиты gnatmake может быть осуществлен без явного указания какой-либо головной подпрограммы. В этом случае, полученный результат будет зависесть от наличия описания атрибута Main. Значением этого атрибута является список строк, каждый элемент которого является именем исходного файла (указание расширения имени файла является не обязательным), который содержит головную подпрограмму.

Когда атрибут Main описан в файле проекта как не пустой список строк и в командной строке запуска утилиты gnatmake не указана опция "-u", запуск gnatmake, - с указанием такого файла проекта, но без указания в командной строке какой-либо головной подпрограммы, - является эквивалентным запуску gnatmake с указанием в командной строке всех имен файлов, которые указываются атрибутом Main. Например:


   project Prj is
      for Main use ("main1", "main2", "main3");
   end Prj;

Для показанного выше примера, команда "gnatmake -Pprj" будет эквивалентна команде "gnatmake -Pprj main1 main2 main3".

Когда файл проекта не содержит описание атрибута Main, или этот атрибут описан как пустой список строк, или в командной строке указана опция "-u", и запуск утилиты gnatmake осуществляется без указания в командной строке какой-либо головной подпрограммы, будет осуществлена проверка всех непосредственных исходных файлов проекта и, потенциально, они будут перекомпилированы. В зависимости от присутствия в командной строке опции "-u", исходные файлы других проектов, от которых зависят непосредственные исходные файлы главного проекта, будут также проверены и, потенциально, перекомпилированы. Другими словами, опция "-u" применяется ко всем непосредственным исходным файлам главного файла проекта.

7.13.2 Управляющая программа gnat и файлы проектов

Кроме утилиты gnatmake, существуют другие инструментальные средства системы GNAT, которые ориентированы на обработку проектов. К таким инструментальным средствам относятся: gnatbind, gnatfind, gnatlink, gnatls и gnatxref. Следует однако заметить, что ни один из этих инструментов не может непосредственно использовать опцию указания файла проекта ("-P"). Таким образом, необходимо осуществлять запуск этих инструментов с помощью управляющей программы gnat (или gnatcmd).

Управляющая программа gnat обеспечивает внешний интерфейс инструментальных средств. Она принимает целый перечень команд и осуществляет вызов соответствующих инструментальных средств. Изначально, управляющая программа gnat была разработана для VMS, с целью преобразования квалификаторов стиля VMS в опции стиля UNIX. Однако, в настоящий момент эта программа доступна для всех платформ, которые поддерживает система компилятора GNAT.

На платформах отличных от VMS, управляющая программа gnat принимает следующие команды (вне зависимости от регистра символов):

Следует заметить, что запуск компилятора осуществляется с помощью команды "gnatmake -f -u".

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


  gnat bind -C main.ali
  gnat ls -a main
  gnat chop foo.txt

Для команд: BIND, FIND, LS или LIST, LINK и XREF, - в дополнение к опциям, которые применимы для непосредственно вызываемого инструментального средства, могут быть использованы опции, которые характерны для файлов проектов (-P, -X and -vPx).

Для каждой из этих команд, в главном проекте возможно существование пакета, который соответствует запускаемому инструментальному средству:

Пакет Gnatls обладает уникальным атрибутом Switches, простая переменная со значением в виде списка строк. Атрибут содержит опции используемые при запуске gnatls.


project Proj1 is
   package gnatls is
      for Switches use ("-a", "-v");
   end gnatls;
end Proj1;

Все остальные пакеты обладают атрибутом Default_Switches, ассоциативный массив, который индексируется именем языка программирования (не зависит от регистра символов) и имеет значение в виде списка строк. Атрибут "Default_Switches ("Ada")" содержит опции, которые используются при запуске инструментального средства соответствующего пакету.


project Proj is

   for Source_Dirs use ("./**");

   package gnatls is
      for Switches use ("-a", "-v");
   end gnatls;

   package Binder is
      for Default_Switches ("Ada") use ("-C", "-e");
   end Binder;

   package Linker is
      for Default_Switches ("Ada") use ("-C");
   end Linker;

   package Finder is
      for Default_Switches ("Ada") use ("-a", "-f");
   end Finder;

   package Cross_Reference is
      for Default_Switches ("Ada") use ("-a", "-f", "-d", "-u");
   end Cross_Reference;
end Proj;

Для показанного выше файла проекта, команды:


   gnat ls -Pproj main
   gnat xref -Pproj main
   gnat bind -Pproj main.ali

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

7.14 Расширенный пример

Предположим, что у нас есть две программы prog1 и prog2, исходные файлы которых расположены в соответствующих каталогах. Предположим также, что нам необходимо осуществлять сборку этих программ с помощью одной команды запуска утилиты gnatmake, и мы хотим сохранять объектные файлы этих программ в подкаталогах ".build" соответствующих каталогов с исходными файлами. Кроме того, нам необходимо иметь внутри каждого подкаталога ".build" два отдельных подкаталога "release" и "debug", которые будут содержать объектные файлы скомпилированные с различными наборами опций компиляции.

Более наглядно, мы имеем следующую структуру каталогов:


   main
     |- prog1
     |    |- .build
     |         | debug
     |         | release
     |- prog2
          |- .build
               | debug
               | release

Рассмотрим файлы проектов, которые нам необходимо создать в каталоге "main", для сопровождения этой структуры:

  1. Создадим проект Common с пакетом Compiler, который указывает опции компиляции:

    
    File "common.gpr":
    
    project Common is
    
       for Source_Dirs use (); -- нет исходных файлов
    
       type Build_Type is ("release", "debug");
       Build : Build_Type := External ("BUILD", "debug");
       package Compiler is
          case Build is
             when "release" =>
               for Default_Switches ("Ada") use ("-O2");
             when "debug"   =>
               for Default_Switches ("Ada") use ("-g");
          end case;
       end Compiler;
    
    end Common;
    

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

    
    File "prog1.gpr":
    
    with "common";
    project Prog1 is
    
        for Source_Dirs use ("prog1");
        for Object_Dir  use "prog1/.build/" & Common.Build;
    
        package Compiler renames Common.Compiler;
    
    end Prog1;
    

    
    File "prog2.gpr":
    
    with "common";
    project Prog2 is
    
        for Source_Dirs use ("prog2");
        for Object_Dir  use "prog2/.build/" & Common.Build;
    
        package Compiler renames Common.Compiler;
    
    end Prog2;
    

  3. Создадим проект-обертку Main:

    
    File "main.gpr":
    
    with "common";
    with "prog1";
    with "prog2";
    project Main is
    
       package Compiler renames Common.Compiler;
    
    end Main;
    

  4. В заключение, необходимо создать процедуру-заглушку, которая указывает в своем спецификаторе with (прямо или косвенно) все файлы с исходными текстами двух программ.

Теперь, с помощью команды


   gnatmake -Pmain dummy

можно осуществить сборку программ для режима отладки (Debug), а с помощью команды


   gnatmake -Pmain -XBUILD=release

можно осуществить сборку программ для режима реализации (Release).

7.15 Диаграмма полного синтаксиса файлов проектов

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


project ::=
  context_clause project_declaration

context_clause ::=
  {with_clause}

with_clause ::=
  with literal_string { , literal_string } ;

project_declaration ::=
  project <project_>simple_name [ extends literal_string ] is
    {declarative_item}
  end <project_>simple_name;

declarative_item ::=
  package_declaration |
  typed_string_declaration |
  other_declarative_item

package_declaration ::=
  package <package_>simple_name package_completion

package_completion ::=
  package_body | package_renaming

package body ::=
  is
    {other_declarative_item}
  end <package_>simple_name ;

package_renaming ::==
  renames <project_>simple_name.<package_>simple_name ;

typed_string_declaration ::=
  type <typed_string_>_simple_name is
   ( literal_string {, literal_string} );

other_declarative_item ::=
  attribute_declaration |
  typed_variable_declaration |
  variable_declaration |
  case_construction

attribute_declaration ::=
  for attribute use expression ;

attribute ::=
  <simple_attribute_>simple_name |
  <associative_array_attribute_>simple_name ( literal_string )

typed_variable_declaration ::=
  <typed_variable_>simple_name : <typed_string_>name :=  string_expression ;

variable_declaration ::=
  <variable_>simple_name := expression;

expression ::=
  term {& term}

term ::=
  literal_string |
  string_list |
  <variable_>name |
  external_value |
  attribute_reference

literal_string ::=
  (same as Ada)

string_list ::=
  ( <string_>expression { , <string_>expression } )

external_value ::=
  external ( literal_string [, literal_string] )

attribute_reference ::=
  attribute_parent ' <simple_attribute_>simple_name [ ( literal_string ) ]

attribute_parent ::=
  project |
  <project_or_package>simple_name |
  <project_>simple_name . <package_>simple_name

case_construction ::=
  case <typed_variable_>name is
    {case_item}
  end case ;

case_item ::=
  when discrete_choice_list => {case_construction | attribute_declaration}

discrete_choice_list ::=
  literal_string {| literal_string}

name ::=
  simple_name {. simple_name}

simple_name ::=
  identifier (same as Ada)


Copyright (C) А.Гавва V-0.4w май 2004