Ада-95. Компилятор GNAT

         

Альтернативные схемы именования


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

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

Таким образом, в случае разработки большой системы, это ведет к значительному увеличению размеров файла gnat.adc

и, как следствие, может осложнить сопровождение проекта.

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

Для этого используются показанные ниже формы директивы компилятора Source_File_Name:

pragma Source_File_Name ( Spec_File_Name => FILE_NAME_PATTERN [,Casing => CASING_SPEC] [,Dot_Replacement => STRING_LITERAL]);

pragma Source_File_Name ( Body_File_Name => FILE_NAME_PATTERN [,Casing => CASING_SPEC] [,Dot_Replacement => STRING_LITERAL]);

pragma Source_File_Name ( Subunit_File_Name => FILE_NAME_PATTERN [,Casing => CASING_SPEC] [,Dot_Replacement => STRING_LITERAL]);

FILE_NAME_PATTERN ::= STRING_LITERAL CASING_SPEC ::= Lowercase | Uppercase | Mixedcase

Строка FILE_NAME_PATTERN является шаблоном имен файлов.

Она содержит один символ звездочки, вместо которого подставляется имя модуля.

Необязательный параметр Casing указывает используемый в имени файла регистр символов: Lowercase - нижний регистр (маленькие буквы), Uppercase - верхний регистр (большие буквы), Mixedcase - смешанное использование регистра символов.

При отсутствии параметра Casing, по умолчанию, используется Lowercase.

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

Когда строка Dot_Replacement не указывается, разделяющие точки неизменно присутствуют в имени файла.

Хотя показанный выше синтаксис демонстрирует, что параметр Casing

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






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

Достаточно часто для именования субмодулей желательно использовать такое же правило как и для тел модулей.

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

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

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

Трансляция имен файлов включает следующие шаги:

Если для данного модуля существует соответствующая директива Source_File_Name, то всегда используется информация указанная в этой директиве, а любые другие шаблонные правила игнорируются.

Если указана директива Source_File_Name

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

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

Если директива Source_File_Name задающая тип шаблона применимого для модуля, для которого существует соответствующий файл, отсутствует, то используется правило именования, которое применяется по умолчанию и является стандартным для GNAT.

Как пример использования этого механизма рассмотрим распространенную схему именования, когда для имен файлов используется нижний регистр, разделительная точка неизменно копируется в результирующее имя файла, имена файлов спецификаций заканчиваются как ".1.ada", а имена файлов тел заканчиваются как ".2.ada".

GNAT будет следовать этой схеме при наличии двух директив:



pragma Source_File_Name (Spec_File_Name => "*.1.ada"); pragma Source_File_Name (Body_File_Name => "*.2.ada");

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

pragma Source_File_Name (Spec_File_Name => "*.ads", Dot_Replacement => "-"); pragma Source_File_Name (Body_File_Name => "*.adb", Dot_Replacement => "-");

В качестве заключительного примера, рассмотрим реализацию схемы именования, которую использует один из компиляторов Ada83: как символ-разделитель для субмодулей используется "__" (два символа подчеркивания); файлы спецификаций идентифицируются добавлением "_.ADA"; файлы тел идентифицируются добавлением ".ADA"; файлы субмодулей идентифицируются добавлением ".SEP".

Для всех имен файлов используется верхний регистр символов.

Естественно, что дочерние модули - не представлены, поскольку это компилятор Ada83, однако, будет логично расширить эту схему указанием использовать двойное подчеркивание в качестве разделителя для дочерних модулей.

pragma Source_File_Name (Spec_File_Name => "*_.ADA", Dot_Replacement => "__", Casing = Uppercase); pragma Source_File_Name (Body_File_Name => "*.ADA", Dot_Replacement => "__", Casing = Uppercase); pragma Source_File_Name (Subunit_File_Name => "*.SEP", Dot_Replacement => "__", Casing = Uppercase);


Атрибуты


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

Существуют две категории атрибутов: простые атрибуты и ассоциативные массивы.

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

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

Имя атрибута

Значение

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, для какого-либо атрибута текущего проекта имя существующего в текущем проекте пакета имя какого-либо импортируемого проекта имя какого-либо проекта-предка (расширяемого текущим проектом) имя какого-либо импортируемого/проекта-предка, сопровождаемое точкой и, затем, именем пакета

Например:

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");


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


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

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

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

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



Автоматическое создание списка каталогов


Для большинства файлов Makefile, возникает необходимость перечисления списка каталогов и сохранения этого списка в какой-либо переменной.

Для небольших проектов чаще всего такое перечисление проще выполнять вручную, поскольку после этого сохраняется полное управление над правильностью порядка перечисления этих каталогов.

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

Показанный ниже пример файла Makefile

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

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

Он использует шаблонные символы, которые будут автоматически расширены утилитой GNU make.

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

Второй способ - более общий.

Он требует использования внешней программы find, которая стандартна для большинства систем UNIX.

Все подкаталоги, которые расположены внутри корневого каталога проекта, будут добавлены в список каталогов проекта.

# Показанный ниже пример основывается на следующей иерархии каталогов, # причем, все каталоги могут содержать произвольное количество файлов: # ROOT_DIRECTORY -> a -> aa -> aaa # -> ab # -> ac # -> b -> ba -> baa # -> bb # -> bc # Этот файл Makefile создает переменную с именем DIRS, которая может # быть использована в любой момент, когда необходим список каталогов # (см. другие примеры)

# Корневой каталог иерархии каталогов проекта ROOT_DIRECTORY=.

#### # Первый способ: явно определяет список каталогов. # Он позволяет определять любое подмножество необходимых каталогов. ####

DIRS := a/aa/ a/ab/ b/ba/

#### # Второй способ: использует шаблоны # Примечательно, что аргументы показанных ниже шаблонов # должны звканчиваться символом '/'. # Поскольку шаблоны также возвращают имена файлов, их необходимо # отфильтровывать, чтобы избежать дублирование имен каталогов. # Для этого используются встроенные функции make dir и sort. # Это устанавливает переменную DIRS в следующее значение (примечательно, что # каталоги aaa и baa не будут указаны до тех пор, пока не будут # изменены аргументы шаблона). # DIRS= ./a/a/ ./b/ ./a/aa/ ./a/ab/ ./a/ac/ ./b/ba/ ./b/bb/ ./b/bc/ ####

DIRS := ${sort ${dir ${wildcard ${ROOT_DIRECTORY}/*/ ${ROOT_DIRECTORY}/*/*/}}}

#### # Третий способ: использует внешнюю программу # Эта команда будет более быстро выполняться на локальных дисках. # В результате выполнения этой команды переменная DIIRS будет # установлена в следующее значение: # DIRS= ./a ./a/aa ./a/aa/aaa ./a/ab ./a/ac ./b ./b/ba ./b/ba/baa ./b/bb ./b/bc ####

DIRS := ${shell find ${ROOT_DIRECTORY} -type d -print}



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


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

project Empty is

end Empty;

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

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

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

case others   use
end   package   when
for   renames   with
is   type    

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



Более реальный пример


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

with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code; with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;

procedure Getflags is

Eax : Unsigned_32; begin

Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0", -- сохранить значение флагов в переменной Outputs => Unsigned_32'Asm_Output ("=g", Eax)); Put_Line ("Flags register:" & Eax'Img); end Getflags;

Текст этого примера может неожиданно показаться несколько сложным.

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

Следует заметить, что понимание текста этого примера позволит использовать всю мощь встроенного ассемблера GNAT.

Первое, на что необходимо обратить внимание - это способ записи множества инструкций ассемблера.

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

Asm ("pushfl popl %%eax movl %%eax, %0")

В генерируемом ассемблерном файле этот фрагмент будет отображен следующим образом:

#APP pushfl popl %eax movl %eax, -40(%ebp) #NO_APP

Не трудно заметить, что такой результат не очень легко и удобно читать.

Таким образом, удобнее записывать различные инструкции ассемблера в отдельных строчках исходного текста, заключая их в двойные кавычки, а с помощью стандартного пакета Ada.Characters.Latin_1

выполнять "ручную" вставку символов перевода строки(LineFeed / LF) и горизонтальной табуляции (Horizontal Tab / HT).

Это позволяет сделать более читабельным как исходный текст Ады:

Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0") -- сохранить значение флагов в переменной

<


так и генерируемый файл ассемблера:

#APP pushfl popl %eax movl %eax, -40(%ebp) #NO_APP

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

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

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

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

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

Asm ("movw %%ax, %%bx");

Это будет отображено в сгенерированном ассемблерном файле как:

#APP movw %ax, %bx #NO_APP

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

Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0", -- сохранить значение флагов в переменной Outputs => Unsigned_32'Asm_Output ("=g", Eax));

Здесь, %0, %1, %2... индицируют операнды которые позже описывают использование параметров ввода (Input) и вывода (Output).


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


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

project ::= context_clause project_declaration

context_clause ::= {with_clause}

with_clause ::= with literal_string { , literal_string } ;

project_declaration ::= project simple_name [ extends literal_string ] is

{declarative_item} end simple_name;

declarative_item ::= package_declaration | typed_string_declaration | other_declarative_item

package_declaration ::= package simple_name package_completion

package_completion ::= package_body | package_renaming

package body ::= is

{other_declarative_item} end simple_name ;

package_renaming ::== renames simple_name.simple_name ;

typed_string_declaration ::= type _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_name | simple_name ( literal_string )

typed_variable_declaration ::= simple_name : name := string_expression ;

variable_declaration ::= simple_name := expression;

expression ::= term {& term}

term ::= literal_string | string_list | name | external_value | attribute_reference

literal_string ::= (same as Ada)

string_list ::= ( expression { , expression } )

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

attribute_reference ::= attribute_parent ' simple_name [ ( literal_string ) ]

attribute_parent ::= project | simple_name | simple_name . simple_name

case_construction ::= case 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



Директивы компилятора для отладки


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

Директива

Описание

pragma Assert (условие);  -  Вставить условие контрольной проверки
pragma Debug (имя_процедуры);  -  Указание необходимости выполнения вызова отладочной процедуры

Следует заметить, что для активации использования этих директив, необходимо указать опцию командной строки компилятора -gnata

при компиляции исходных текстов.

В тех случаях, когда при компиляции исходных текстов опция -gnata не указана, GNAT будет просто игнорировать присутствие этих директив.

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

Директива компилятора Assert

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

Если указанные условия ложны, то осуществляется возбуждение исключения.

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

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

pragma Assert (Screen_Height = 24);

Здесь, если значение переменной Screen_Height не будет равно 24, то будет возбуждено исключение ASSERT_ERROR.

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

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

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

Например:

X := 5; pragma Debug (Print_To_Log_File( "X is now" & X'Img ));




В данном случае, если предположить, что процедура Print_To_Log_File используется для сохранения сообщения в каком-либо файле протокола (log-файле), то этот пример сохранит в этом файле протокола сообщение "X is now 5".

Следует также заметить, что действие директивы компилятора Debug

может быть подавлено с помощью директивы Suppress_Debug_Info.

При выполнении отладки программ могут быть полезными еще две директивы компилятора.

Директива компилятора No_Return

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

Это позволяет подавить соответствующие предупреждающие сообщения компилятора.

Директива компилятора Normalize_Scalars

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

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

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

Обычно Ада не осуществляет установку начальных значений, пока это не указано явно.

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

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

Следует заметить, что директива Normalize_Scalars

будет работать лучше если при компиляции исходных текстов использована опция компилятора -gnatVf.


Директивы конфигурации


Согласно "Руководства по языку программирования Ада 95" существует набор директив компилятора, которые предназначены для управления конфигурацией среды компиляции Ада-системы. Такие директивы называют директивами конфигурации.

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

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

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

Ada_83 Normalize_Scalars
Ada_95   Polling
C_Pass_By_Copy   Propagate_Exceptions
Component_Alignment   Queuing_Policy
Discard_Names   Ravenscar
Elaboration_Checks   Restricted_Run_Time
Eliminate   Restrictions
Extend_System   Reviewable
Extensions_Allowed   Source_File_Name
External_Name_Casing   Style_Checks
Float_Representation   Suppress
Initialize_Scalars   Task_Dispatching_Policy
License   Unsuppress
Locking_Policy   Use_VADS_Size
Long_Float   Warnings
No_Run_Time   Validity_Checks



Доступность средств разработки


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

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

Примерами таких фирм могут служить:

Ada Core Technologies

Aonix

AverCom

DDC-I

Green Hills Software

Irvine Compiler Corp.

OC Systems

Rational Software Corp.

RR Software

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

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

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

Компилятор ObjectAda от Aonix

(http://www.aonix.com/), который позволяет вести разработку под операционными системами Windows и Solaris. Компилятор GNAT от Ada Core Technologies

(www.gnat.com), который поставляется со всеми исходными текстами под лицензией GPL

и поддерживает большое число платформ.

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

Исходя из этого, значительно больший интерес представляет свободно распространяемая версия компилятора GNAT от Ada Core Technologies, для которой подобные ограничения отсутствуют.

Справедливо будет заметить, что Ada Core Technologies строго предупреждает об отсутствии каких либо гарантий на использование данной версии компилятора, и не рекомендует использовать эту версию компилятора при разработке программного обеспечения от которого требуется высокая надежность.



Файлы директив конфигурации


В системе компилятора GNAT, среда компиляции определяется текущим каталогом на момент запуска команды компиляции.

Этот текущий каталог используется для поиска файла с именем gnat.adc.

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

Однако, если в командной строке команды компиляции присутствует опция -gnatA, то содержимое файла gnat.adc не рассматривается..

Директивы конфигурации могут быть введены в файл gnat.adc

как путем запуска утилиты gnatchop

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

В дополнение к gnat.adc

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

Для указания этого, в командной строке необходимо использовать опцию -gnatecpath.

В этом случае, параметр опции "path" должен указывать существующий файл, который состоит только из директив конфигурации.

Директивы конфигурации указанного файла будут служить как дополнение к директивам конфигурации, которые расположены в файле gnat.adc

(предполагается, что файл gnat.adc присутствует, и не используется опция -gnatA).

Следует заметить, что в командной строке может присутствовать несколько опций -gnatec. Однако, фактическое значение будет иметь только последняя указанная опция.

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



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


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

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

Файл проекта может запрашивать значения внешних переменных

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

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

Следует заметить, что "зависимость", в данном случае, рассматривается в техническом смысле, например, один Ада-модуль указывает в спецификаторе with

какой-либо другой Ада-модуль.

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

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

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

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

Активация менеджера проектов GNAT осуществляется с помощью опции "-Pprojectfile" утилиты gnatmake или управляющей программы gnat

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


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

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

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

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

Расположение получаемого в результате сборки проекта исполняемого модуля может быть указано внутри файла проекта или в командной строке путем использования опции "-o".

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

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

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

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


Генерация опций командной строки для gnatmake


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

С целью полноты этот пример подразумевает, что путь к исходным текстам не соответствует пути к объектным файлам.

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

# см. "Автоматическое создание списка каталогов" # для создания этих переменных SOURCE_DIRS= OBJECT_DIRS=

GNATMAKE_SWITCHES := ${patsubst %,-aI%,${SOURCE_DIRS}} GNATMAKE_SWITCHES += ${patsubst %,-aO%,${OBJECT_DIRS}}

all: gnatmake ${GNATMAKE_SWITCHES} main_unit



Ада-95. Компилятор GNAT


Эта глава предоставляет информацию, которая может оказаться полезной при построении и использовании библиотек с системой компилятора GNAT.

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



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


В проекте, который содержит исходные файлы, имена исходных файлов могут быть указаны с помощью атрибутов 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.



Именование регистров процессора


При использовании соглашений Intel, именование регистров процессора осуществляется следующим образом:

mov eax, 4

В случае синтаксиса AT&T, перед именем регистра процессора должен располагаться символ процента '%':

mov %eax, 4



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


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

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

Достижение такого эффекта заключено в синтаксисе, который подобен использованию спецификатора 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, которая включает каталог содержащий необходимый файл проекта.

Следовательно, при наличии переменной окружения 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) наличие двусмысленного модуля является недопустимым даже тогда, когда импортирующий проект не содержит обращений к этому модулю.

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


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


Какой-либо непосредственный исходный файл проекта P

может зависеть от исходных файлов, которые не являются непосредственными исходными файлами проекта P

и не содержатся в предопределенной библиотеке.

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

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

...

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

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

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

Если переменная окружения ADA_PROJECT_PATH существует, то путь к каталогу проекта содержит все каталоги, которые указаны в этой переменной окружения, плюс каталог в котором расположен текущий файл проекта.

Если переменная окружения ADA_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.


Инструкции повторения


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

rep stos

В случае синтаксиса AT&T, эти инструкции должны быть записаны в отдельных строчках ассемблерного кода:

rep stosl



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


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

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



Исключения и точки прерывания


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

break exception

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

break exception name

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

break exception unhandled

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

info exceptions

info exceptions regexp

Команда info exceptions позволяет пользователю анализировать все исключения, которые определены внутри Ада-программы.

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



Использование Ада-библиотеки


При использовании Ада-библиотеки необходимо указывать ее как в пути поиска файлов с исходными текстами, так и в пути поиска объектных файлов.

Например, вы можете использовать библиотеку "mylib", которая установлена в каталогах "/dir/my_lib_src" и "/dir/my_lib_obj" следующим образом:

$ gnatmake -aI/dir/my_lib_src -aO/dir/my_lib_obj my_appl \ -largs -lmy_lib

Такая команда может быть упрощена до команды вида:

$ gnatmake my_appl

когда соблюдаются следующие условия:

каталог "/dir/my_lib_src" добавлен пользователем в переменную окружения "ADA_INCLUDE_PATH", или указан администратором системы в файле "ada_source_path"

каталог "/dir/my_lib_obj" добавлен пользователем в переменную окружения "ADA_OBJECTS_PATH", или указан администратором системы в файле "ada_object_path"

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



Использование других имен файлов


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

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

GNAT позволяет указывать "случайные" имена файлов с помощью директивы компилятора Source_File_Name, которая имеет следующий вид:

pragma Source_File_Name (My_Utilities.Stacks, Spec_File_Name => "myutilst_a.ada"); pragma Source_File_name (My_Utilities.Stacks, Body_File_Name => "myutilst.ada");

Первый аргумент директивы указывает имя модуля (в случае показанного примера - имя дочернего модуля).

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

Непосредственное имя файла указывается строковым литералом.

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

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

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

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

Такой опцией командной строки gcc является опция -x.

Для указания языка программирования она должна сопровождаться пробелом и именем используемого языка программирования, в данном случае - ada:

$ gnatgcc -c -x ada peculiar_file_name.sim

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

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



Использование отладчика GNU GDB


Начиная с версии 3.11, компилятор GNAT поставляется вместе с работающим в режиме командной строки отладчиком GNU GDB.

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

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

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

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



Использование пакета SystemMachine_Code


После рассмотрения общих правил написания инструкций ассемблера, можно сконцентрировать внимание на интеграции кода ассемблера с Ада-программой GNAT.



Использование самостоятельно описываемых типов


Параметры вывода позволяют использовать не только предопределенные типы.

Они также позволяют использовать типы которые самостоятельно описаны программистом.

В качестве примера, рассмотрим ситуацию в которой необходимо прочитать содержимое регистра флагов процессора и проверить состояние флага переноса (carry flag).

Известно, что флаг переноса находится в бите 0 регистра флагов процессора.

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

if (Eax and 16#00000001#) = 1 then

Put_Line ("Carry flag set"); end if;

Однако, может возникнуть необходимость в установке или чтении других битов.

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

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

with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code; with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;

procedure Bits is

subtype Num_Bits is Natural range 0 .. 31;

type Flags_Register is array (Num_Bits) of Boolean; pragma Pack (Flags_Register); for Flags_Register'Size use 32;

Carry_Flag : constant Num_Bits := 0;

Register : Flags_Register;

begin

Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "pop %0", -- загрузить флаги из стека в переменную Register Outputs => Flags_Register'Asm_Output ("=g", Register));

if Register(Carry_Flag) = True then

Put_Line ("Carry flag set"); end if;

end Bits;

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

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

#APP pushfl pop %eax #NO_APP testb $1,%al

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



Использование утилиты gnatmake в файлах Makefile


Управление построением сложного проекта удобно осуществлять с помощью комбинированного использования утилит GNU make и gnatmake.

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

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

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

## Этот Makefile предназначен для использования при следующей структуре ## каталогов: ## - Исходные тексты разделены на серию компонентов программного обеспечения ## csc (computer software components) ## Каждый из этих csc помещается в своем собственном каталоге. ## Их имена соответствуют названиям каталогов. ## Они будут компилироваться в динамически загружаемые библиотеки ## (хотя они могут работать и при статической компоновке) ## - Головная программа (и возможно другие пакеты, которые не являются ## частями каких-либо csc, располагаются в каталоге верхнего уровня ## (там где располагается Makefile). ## toplevel_dir __ first_csc (sources) __ lib (will contain the library) ## \_ second_csc (sources) __ lib (will contain the library) ## \_ ... ## ## Хотя этот Makefile предназначен для построения динамически загружаемых ## библиотек, его достаточно легко модифицировать для построения ## частично скомпонованых объектов (необходимо модифицировать показанные ниже ## строки с -shared и gnatlink) ## ## При использовании этого Makefile, можно изменить любой существующий файл ## системы или добавить какой-либо новый файл, и все будет корректно ## перекомпилировано (фактически, будут перекомпилированы только объекты, ## которые нуждаются в перекомпиляции и будет осуществлена перекомпоновка ## головной программы). ##

# Список компонентов программного обеспечения csc проекта. # Он может быть сгенерирован автоматически. CSC_LIST=aa bb cc

# Имя головной программы (без расширения) MAIN=main

# Для построения объектов с опцией -fPIC # необходимо раскомментировать следующую строку #NEED_FPIC=-fPIC

# Следующая переменная должна указывать каталог # в котором расположена библиотека libgnat.so # Узнать расположение libgnat.so можно с помощью команды # 'gnatls -v'. Обычно, это последний каталог в Object_Path. GLIB=...

# Каталоги для библиотек. Этот макрос расширяется в список CSC, # который перечисляет динамически загружаемые библиотеки. # Возможно использование простого перечисления: # LIB_DIR=aa/lib/libaa.so bb/lib/libbb.so cc/lib/libcc.so LIB_DIR=${foreach dir,${CSC_LIST},${dir}/lib/lib${dir}.so}

${MAIN}: objects ${LIB_DIR} gnatbind ${MAIN} ${CSC_LIST:%=-aO%/lib} -shared gnatlink ${MAIN} ${CSC_LIST:%=-l%}

objects:: # перекомпиляция исходных текстов gnatmake -c -i ${MAIN}.adb ${NEED_FPIC} ${CSC_LIST:%=-I%}

# Примечание: в будущих версиях GNAT следующие команды будут упрощены # с помощью использования нового инструментального средства # gnatmlib ${LIB_DIR}: mkdir -p ${dir $ } cd ${dir $ }; gnatgcc -shared -o ${notdir $ } ../*.o -L${GLIB} -lgnat cd ${dir $ }; cp -f ../*.ali .

# Зависимости для модулей aa/lib/libaa.so: aa/*.o bb/lib/libbb.so: bb/*.o bb/lib/libcc.so: cc/*.o

# Перед запуском программы необходимо убедиться в том, # что динамически загружаемые библиотеки указаны в пути поиска run:: LD_LIBRARY_PATH=pwd/aa/lib:pwd/bb/lib:pwd/cc/lib ./${MAIN}

clean:: ${RM} -rf ${CSC_LIST:%=%/lib} ${RM} ${CSC_LIST:%=%/*.ali} ${RM} ${CSC_LIST:%=%/*.o} ${RM} *.o *.ali ${MAIN}



Использование утилиты GNU make


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

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



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


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

Предположим, что исходные тексты "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" из ранее рассмотренного примера.


Использование встроенного ассемблера


Использование встроенного ассемблера вместе с компилятором GNAT в реальности не является сложностью и позволяет генерировать очень эффективный код.

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

Прежде чем продолжить обсуждение этой темы, необходимо сделать несколько замечаний:

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

Основное внимание уделяется использованию встроенного ассемблера для процессоров семейства Intel x86, однако рассмотренные принципы могут пригодиться для использования встроенного ассемблера с другими типами процессоров.



Использование выражений Ады


Отладчик GDB обеспечивает поддержку для достаточно обширного подмножества синтаксиса выражений Ады.

Основой философии дизайна этого подмножества является следующее:

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

Надежность и сохранность типов, а также строгая приверженность к ограничениям языка программирования Ада не так важна для пользователя GDB.

Для пользователя GDB важна краткость.

Таким образом, для краткости, отладчик действует так, как будто спецификаторы with и use

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

В случае возникновения двусмысленности GDB запрашивает у пользователя соответствующее уточнение.

Более подпробно поддержка синтаксиса Ады рассматривается в руководстве "Отладка с помощью GDB" (Debugging with GDB).



Изменяемые инструкции


Иногда, оптимизатор компилятора может перехитрить самого себя.

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

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

Рассмотрим следующий пример:

Asm ("movl %0, %%ebx" & "movl %%ebx, %1", Inputs => Unsigned_32'Asm_Input ("=g", Input), Outputs => Unsigned_32'Asm_Output ("g", Output), Clobber => "ebx", Volatile => True");

Примечательно, что по умолчанию параметр Volatile

установлен в False пока не существует ни одного параметра вывода.

В результате демонстрации такого примера, может показаться, что установку параметра Volatile в True

стоит использовать повсеместно.

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

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



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


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

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

for Exec_Dir use "executables";

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

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

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

По умолчанию, когда отсутствует явное описание значения атрибута Exec_Dir

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



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


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

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

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

for Object_Dir use "objects";

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

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

Указываемый каталог должен существовать и должен быть доступен для чтения и записи.

По умолчанию, когда отсутствует явное описание значения атрибута Object_Dir

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



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


Каталоги с исходными файлами проекта могут быть указаны с помощью атрибута файла проекта 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 ("./**");

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



Компоновщик gnatlink


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

Эта простая программа вызывает компоновщик системы (через команду gcc/gnatgcc) и предоставляет ему корректный список ссылок на объектные и библиотечные файлы.

Программа gnatlink выполняет автоматическое определение списка необходимых файлов для сборки Ада-программы.

Для определения этого списка файлов gnatlink использует файл сгенерированный редактором связей gnatbind.

Команда запуска компоновщика gnatlink имеет следующий вид:

$ gnatlink [switches] mainprog[.ali] [non-Ada objects] [linker options]

В данном случае, файл mainprog.ali является ALI-файлом главной программы.

Расширение имени файла .ali может не указываться, поскольку такое расширение имени файла подразумевается по умолчанию.

Из этой команды компоновщик gnatlink определяет соответствующий файл b~mainprog.adb, который генерируется редактором связей gnatbind, и, используя информацию из этого файла, вместе со списком не Ада-объектов и опций компоновщика, конструирует команду для запуска компоновщика системы, в результате выполнения которой выполняется построение результирующего исполняемого файла.

Аргументы команды, которые следуют за mainprog.ali

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

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

Список linker options (опции компоновщика) - это не обязательный список опций, которые специфичны для компоновщика,

По умолчанию, в качестве компоновщика используется gcc (gnatgcc), который вызывает соответствующий системный компоновщик, обычно называемый ld.

При этом, как обычно, могут быть представлены стандартные опции компоновщика, такие как -lmy_lib или -Ldir.

Для опций, которые не распознаются gcc (gnatgcc) как опции компоновщика должны использоваться -Xlinker или -Wl,.

Для получения более детальной информации необходимо обратиться к документации на GCC.

Примером того как выполнить генерацию компоновщиком файла-карты (linker map file), подразумевая, что используется системный компоновщик ld, может служить следующее:

$ gnatlink my_prog -Wl,-Map,MAPFILE

Компоновщик gnatlink определяет список объектов, необходимых для Ада-программы, и предваряет их списком объектов передаваемых компоновщику.

Кроме того, gnatlink собирает любые аргументы установленные директивой компилятора pragma Linker_Options и добавляет их к списку аргументов передаваемых компоновщику системы.



Конструкция "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 и описания атрибутов.

Описания строковых типов, описания переменных и пакетов являются недопустимыми.

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



Косвенная адресация


Теперь нам известно как загрузить значение адреса и содержимое памяти.

Но как загрузить содержимое памяти на которое указывает адрес расположенный в памяти или регистре?

Синтаксис AT&T не использует средства подобные "BYTE PTR", которые традиционны для ассемблера Intel.

Для загрузки регистра ebx 32-битным содержимым памяти адрес которого храниться в регистре eax необходимо написать:

movl (%eax), %ebx

Для добавления смещения к адресу в регистре eax, необходимо использовать это смещение как префикс.

Например, для получения содержимого 4-мя байтами ниже адреса, указанного в регистре eax, следует написать:

movl -4(%eax), %ebx

или использовать содержимое памяти подобным образом:

movl my_var(%eax), %ebx

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



Модель компиляции GNAT и общая согласованная Ада-библиотека


Эта информация может оказаться полезной для тех программистов, которые ранее использовали какой-либо другой Ада-компилятор использующий традиционную модель общей согласованной Ада-библиотеки, как описано в "Руководстве по языку программирования Ада 95".

Система компилятора GNAT не использует традиционную модель общей согласованной Ада-библиотеки.

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

Компиляция Ада-программы не генерирует никакой централизованной информации. Вместо этого в результате компиляции генерируются объектные файлы и ALI-файлы, которые предназначены для обработки редактором связей gnatbind

и компоновщиком gnatlink.

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

В частности:

При указании какого-либо модуля в спецификаторе with

компилятору будет представлена самая свежая версия этого модуля скомпилированного в общую библиотеку.

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

Компиляция какого-либо нового модуля может сделать устаревшими версии других модулей в общей библиотеке.

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

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

В частности:

При указании какого-либо модуля в спецификаторе with

компилятору будет представлена версия этого модуля соответствующий исходный текст которой будет доступен компилятору в процессе текущей компиляции.

Встроенная подстановка (inline) требует наличия и доступности для компилятора файлов с соответствующими исходными текстами тел пакетов и/или подпрограмм.


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

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

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

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

То что в традиционной Ада-системе с общей библиотекой проявляется как требование строго определенной последовательности компиляции для GNAT проявляется как простая зависимость файлов с исходными текстами.

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


Модели компиляции GNAT и C/C++


Модель компиляции GNAT очень похожа на модель компиляции, которая традиционна для языков программирования C/C++.

Файлы спецификации могут рассматриваться как соответствующие файлы заголовков (*.h) C/C++.

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

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

Спецификатор with обладает эффектом, который подобен использованию директивы #include

в C/C++.

При этом существует одно примечательное отличие: в Аде можно выполнять раздельную компиляцию спецификации с целью осуществления ее семантической и синтаксической проверки.

Это не всегда возможно для файлов заголовков C/C++ поскольку они являются фрагментами программы, которая использует иные семантические и синтаксические правила.

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

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

В C/C++ такая проверка согласованности различных частей программы, как правило, возлагается на файл управляющий сборкой проекта (Makefile).

Таким образом, редактор связей gnatbind

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

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

Следует заметить, что обработка очередности элаборации присутствует также в C++. Считается, что в C++ она осуществляется автоматически и поэтому о ней, как правило, не упоминается. Однако фактическая очередность элаборации самостоятельных объектных модулей C++ определяется порядком компоновки объектных модулей компоновщиком. Преимуществом подхода C++ является простота, но при этом программист не имеет реального контроля над процессом элаборации.

В результате, в случаях когда редактор связей gnatbind

может указать на отсутствие корректного порядка элаборации и, следовательно, не возможность компоновки результирующей программы, компилятор C++ выполнит построение программы не работоспособность которой будет обнаружена только во время выполнения.



Некорректное завершение работы компилятора


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

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

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

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

Запуск gnatgcc с опциями "-gnatf" и "-gnate". Опция "-gnatf" указывает на необходимость вывода сообщений о всех ошибках, которые обнаружены в строке исходного текста. При ее отсутствии, выдается сообщение только о первой обнаруженной ошибке. Опция "-gnate" указывает на необходимость вывода сообщения об ошибке сразу после обнаружения ошибки, а не после завершения процесса компиляции. Когда GNAT завершает свою работу преждевременно, наиболее вероятным источником возникшей проблемы будет сообщение об ошибке, которое отображается последним.

Запуск gnatgcc с указанием опции "многословности" вывода "-v (verbose)". В этом режиме, gnatgcc генерирует непрерывную информацию о процессе компиляции и предусматривает имя каждой процедуры код которой сгенерирован. Эта опция позволяет осуществлять поиск Ада-процедуры при генерации кода которой возникают проблемы.

Запуск gnatgcc с указанием опции "-gnatdc". Эта опция специфична для GNAT и ее воздействие на препроцессор (front-end) аналогично воздействию опции "-v" на кодогенератор (back-end). Система распечатывает имя каждого модуля (как компилируемого модуля, так и вложенного модуля) в процессе его анализа.

В заключение, можно запустить отладчик gnatgdb

непосредственно для исполняемого файла gnat1.

Исполняемый файл gnat1 является препроцессором (front-end) GNAT, который может быть самостоятельно запущен на выполнение (в обычной ситуации его запуск осуществляется из gnatgcc/gcc).

Использование отладчика gnatgdb для gnat1 аналогично использованию gnatgdb для программ на языке C.

Команда where является первой строкой атаки; переменная lineno (которую можно увидеть с помощью print lineno), используется во второй фазе gnat1 и в кодогенераторе gnatgcc, индицирует строку исходного текста, в которой произошла остановка выполнения, а input_file name индицирует имя файла с исходным текстом.



Обнаружение большого количества ошибок при компиляции


В случае обнаружения очень большого количества ошибок компиляции в исходном тексте, может оказаться полезным перенаправление вывода сообщений об ошибках в отдельный файл (например, путем использования gcc ... 2> temp.out).

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



Обработка директив конфигурации


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

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

Система компилятора GNAT предусматривает утилиту gnatchop, которая обеспечивает способ автоматической обработки директив конфигурации в соответствии с семантикой компиляции (а именно, для файлов с множеством модулей) описанной в "Руководстве по языку программирования Ада 95".

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



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


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

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

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

external (имя_внешней_переменной)

external (имя_внешней_переменной, значение_по_умолчанию)

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

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

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

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

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

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

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

when "Debug" => ...



Общие правила наименования файлов


Не зависимо от используемой операционной системы (Microsoft Windows или Linux) компилятор GNAT достаточно активно использует суффиксы имен файлов (иначе, расширения имен файлов).

Ниже перечисляются общие соглашения GNAT по использованию суффиксов имен файлов:

Суффикс

Пояснения

.ads  -  файл с исходным текстом спецификации пакета Ады
.adb  -  файл с исходным текстом тела пакета Ады или Ада-программы
.adc  -  файл конфигурации GNAT
.adt  -  файл дерева зависимостей
.ali  - 

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

.xrb  -  файл перекрестных ссылок генерируемый утилитой gnatf

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

Имя файла формируется путем взятия полного расширенного имени модуля и замене разделительных точек символами дефиса ('-').

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

Исключение из этого правила касается файлов чьи имена начинаются символами a, g, i или s, а следующим символом является символ дефиса.

В этом случае вместо символа дефиса ('-') используется символ тильды ('~').

Смысл таких специальных правил заключается в том, что это позволяет избежать конфликта имен с файлами стандартной библиотеки, которые содержат дочерние модули пакетов System, Ada, Interfaces и GNAT, использующих для имен файлов префиксы s-, a-, i- или g-, соответственно.

Следующий список демонстрирует некоторые примеры использования этих правил именования файлов:

Имя файла

Пояснения

main.ads  -  Спецификация главной программы
main.adb  -  Тело главной программы
arith_functions.ads  -  Спецификация пакета Arith_Functions
arith_functions.adb  -  Тело пакета Arith_Functions
func-spec.ads  -  Спецификация дочернего пакета Func.Spec
func-spec.adb  -  Тело дочернего пакета Func.Spec
main-sub.adb  -  Тело Sub субмодуля Main
a~bad.adb  -  Тело дочернего пакета A.Bad
<


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

Для усечения длин имен файлов можно использовать "уплотнение" имен файлов.

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

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

Это означает, что при использовании "уплотнения" имен файлов окончательная ответственность за уникальность имен файлов возлагается на разработчика приложения.

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

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


Общие сведения о GNU make


В процессе своей работы утилита GNU make осуществляет интерпретацию набора правил, которые сохранены в файле управления сборкой проекта с именем Makefile.

Правила описывают зависимость одних файлов от других файлов.

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

Кроме того, файлы управления сборкой проекта (Makefile) могут содержать в себе комментарии, переменные и правила, которые ссылаются на параметры команды make.

Предположим, что Ада-программа называется dbase

и в ее состав входят следующие файлы с исходными текстами: common.adb, scanner.adb и parser.adb.

В этом случае файл Makefile может содержать в себе следующее правило:

dbase: common.o scanner.o parser.o gnatlink -o dbase

Это правило говорит о том, что исполняемый файл dbase

зависит от трех файлов с исходными текстами на языке Ада, и чтобы выполненить обновление файла dbase

программа make должна заново скомпоновать его из указанных объектных файлов с помощью команды gnatlink.

Следует заметить, что при написании Ада-программ, которые использует файлы с исходными текстами на языке C основополагающая стратегия использования утилиты GNU make с компилятором GNAT заключается в создании правил, которые гарантируют правильную компиляцию файлов с исходными текстами на языке C, а затем завершают построение всего проекта с помощью выполнения команды gnatmake.

Рассмотрим пример простого файла управления сборкой проекта (Makefile), который будет осуществлять компиляцию Ада-программы main.adb, а также любых Ада-пакетов, которые используются в программе main.adb.

Этот пример будет работать со всеми небольшими проектами.

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

Для использования этого файла управления сборкой проекта необходимо просто выполнить команду make, которая осуществит автоматическую сборку проекта, или выполнить команду make clean

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

# Пример простого файла управления сборкой проекта Makefile # # Предполагается, что головная программа имеет имя "main.adb" #

OBJS = main.o somepackage.o

# Правило компиляции файлов с исходными тектами на языке Ада .adb.o: gcc -c $<

.SUFFIXES: .adb .o

# Правило компоновки головной программы main: $(OBJS) gnatbind -xf main.ali; gnatlink main.ali

clean: rm *.o *.ali core



Общие сведения об отладчике GNU GDB


Отладчик GDB является платформенно независимым отладчиком общего назначения.

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

В частности, с его помощью можно отлаживать Ада-программы, которые скомпилированы с помощью компилятора GNAT.

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

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

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

Эта информация хранится в результирующем файле отдельно от сгенерированного объектного кода.

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

Генерация отладочной информации инициируется указанием опциии -g

в команде запуска компилятора gnatgcc или утилиты gnatmake.

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

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

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

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

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




дистрибутива исходных текстов GNAT.

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

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

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

В случае запуска программы под управлением GDB осуществляется активация отладчика.

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

При указании команды run

выполнение программы происходит также как и без GDB.

Это является ключевым моментом в философии дизайна GDB.

GDB не вмешивается в работу программы до тех пор пока не будет встречена точка прерывания работы программы.

Если процесс выполнения программы не встречает точек прерывания, то программа выполняется также как и при отсутствии отладчика.

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

Здесь рассматривается базовое использование GDB в текстовом режиме.

Команда запуска GDB имеет следующий вид:

$ gdb program

Здесь, program - это имя исполняемого файла программы.

Заметим, что в случае использования дистрибутива GNAT от ALT, вместо команды gdb

следует использовать команду gnatgdb:

$ gnatgdb program

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

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


Оценка "мертвого" кода с помощью утилиты gnatelim


Утилита gnatelim, основанная на ASIS (Ada Semantic Interface Specification), может быть использована для поиска неиспользуемых программой частей в объектных файлах и удаления их из финального исполняемого файла.

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

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

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

Предположим, что главной программой является файл main.adb, тогда мы можем выполнить следующее:

gnatmake -c main gnatbind main gnatmake -f -c -gnatc -gnatt main gnatelim main > gnat.adc gnatmake -f main

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



Ограничение возможностей языка


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

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

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

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

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

Ada_83  -  Запретить использование средств стандарта Ada 95
Ada_95  -  Разрешить использование средств стандарта Ada 95 (установлено по умолчанию)
Controlled  -  Отключить "сборку мусора" (garbage collection) для указанного типа данных. Реально, это не воздействует на GNAT, поскольку он не обеспечивает средств "сборки мусора".
Ravanscar  -  Принудительная установка ограничений реального времени, которые соответствуют требованиям Ravanscar.
Restricted_Run_Time  -  Подобна директиве Ravanscar.
Restrictions  -  Отключает некоторые средства языка.

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

Более подробная информация об использовании этих директив компилятора находится в документации компилятора GNAT.

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



Ограничения


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

Это значит, что мы указываем компилятору то, как мы интерпретируем смысл этой переменной.

Например:

Unsigned_32'Asm_Output ("=m", Eax);

Здесь, использование ограничения m (memory) указывает компилятору, что переменная Eax должна быть переменной которая размещается в памяти.

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

Рассмотрим еще один пример указания ограничения:

Unsigned_32'Asm_Output ("=r", Eax);

Здесь, использование ограничения r (register) указывает компилятору на использование регистровой переменной.

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

Если ограничению предшествует символ равенства ("="), то это указывает компилятору, что переменная будет использоваться для сохранения данных.

В показанном ранее примере, при указании ограничения, использовалось ограничение g (global), что позволяет оптимизатору использовать то, что он сочтет более эффективным.

Следует заметить, что существующее число различных ограничений достаточно обширно.

При этом, для процессоров архитектуры Intel x86 наиболее часто используемыми ограничениями являются:

= ограничение вывода
g  -  глобальная переменная (т.е. может быть чем угодно)
m  -  переменная в памяти
I  -  константа
a  -  использовать регистр eax
b  -  использовать регистр ebx
c  -  использовать регистр ecx
d  -  использовать регистр edx
S  -  использовать регистр esi
D  -  использовать регистр edi
r  -  использовать один из регистров eax, ebx, ecx или edx
q  -  использовать один из регистров eax, ebx, ecx, edx, esi или edi
<


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

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

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

Теперь необходимо рассмотреть то, как указать компилятору где эта переменная находится.

Для связывания параметра вывода с ассемблерным операндом используется %x-нотация, в которой x является числом.

Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0", -- сохранить значение флагов в переменной Outputs => Unsigned_32'Asm_Output ("=g", Eax));

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

Означает-ли это, что мы можем иметь только одну переменную вывода?

Нет, мы можем иметь их столько сколько нам необходимо.

Это работает достаточно просто:

можно описать множество параметров вывода разделяя их запятыми и завершая список символом точки с запятой

отсчет операндов ведется в последовательности %0, %1, %2

и т.д., начиная с первого параметра вывода

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

Asm ("movl %%eax, %0" & "movl %%ebx, %1" & "movl %%ecx, %2", Outputs => (Unsigned_32'Asm_Output ("=g", Eax"), -- %0 = Eax (Unsigned_32'Asm_Output ("=g", Ebx"), -- %1 = Ebx (Unsigned_32'Asm_Output ("=g", Ecx)); -- %2 = Ecx

Следует нпомнить с чего начиналось написание нашего примера:



Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0", -- сохранить значение флагов в переменной Outputs => Unsigned_32'Asm_Output ("=g", Eax));

Фактически, мы можем использовать регистровое ограничение для указания компилятору на необходимость сохранять содержимое регистра eax в переменной Eax

путем написания следующих инструкций встроенного ассемблера:

with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code; with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;

procedure Flags is

Eax : Unsigned_32; begin

Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax", -- загрузить флаги из стека в регистр eax Outputs => Unsigned_32'Asm_Output ("=a", Eax)); Put_Line ("Flags register:" & Eax'Img); end Flags;

Примечательно, что ограничение a, указывает компилятору, что переменная Eax будет располагаться в регистре eax.

Генерируемый компилятором, код ассемблера будет иметь следующий вид:

#APP pushfl popl %eax #NO_APP movl %eax,-40(%ebp)

Очевидно, что значение eax сохранено в Eax компилятором после выполнения кода ассемблера.

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

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

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

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

with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code; with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;

procedure Okflags is

Eax : Unsigned_32; begin

Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "pop %0", -- загрузить флаги из стека в переменную Eax Outputs => Unsigned_32'Asm_Output ("=g", Eax)); Put_Line ("Flags register:" & Eax'Img); end Okflags;

В результате, мы получим результат, который был показан ранее.


Опции gnatmake


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

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

--GCC=compiler_name

Указывает программу используемую для компиляции. По умолчанию используется gcc (gnatgcc).

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

Как пример, --GCC="foo -x -y" указывает утилите gnatmake, что в качестве команды запуска компилятора необходимо использовать команду foo -x -y.

Примечательно, что опция -c всегда вставляется после имени команды.

Таким образом, показанный выше пример команды компилятора, которая будет использована утилитой gnatmake, в результате, будет иметь вид foo -c -x -y.

--GNATBIND=binder_name  - 

Указывает программу используемую в качестве редактора связей. По умолчанию используется редактор связей gnatbind.

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

Как пример, --GNATBIND="bar -x -y" указывает утилите gnatmake, что в качестве команды запуска редактора связей необходимо использовать команду bar -x -y.

Опции редактора связей, которые обычно принимаются утилитой gnatmake

для передачи их редактору связей, будут добавлены после bar -x -y.

--GNATLINK=linker_name  - 

Указывает программу используемую в качестве компоновщика. По умолчанию используется компоновщик gnatlink.

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

Как пример, --GNATLINK="lan -x -y" указывает утилите gnatmake, что в качестве команды запуска компоновщика необходимо использовать команду lan -x -y.

Опции компоновщика, которые обычно принимаются утилитой gnatmake

для передачи их компоновщику, будут добавлены после lan -x -y.

-a  - 

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

Заблокированными файлами являются файлы у которых запись в соответствующие ALI-файлы запрещена.

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

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

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

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

совместно со специальными директивами управления конфигурацией компилятора, подобными не стандартной директиве компилятора Float_Representation.

По умолчанию, gnatmake -a выполняет компиляцию всех внутренних файлов GNAT путем использования gnatgcc -c -gnatg, а не gnatgcc -c.

-c  - 

Выполнить только компиляцию и не выполнять связывание и компоновку.

Выполняется по умолчанию, если корневой модуль, который специфицирован как file_name не является головным модулем.

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

-f  - 

Принудительная перекомпиляция.

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

-i  - 

В обычном режиме, утилита gnatmake выполняет компиляцию всех объектных файлов и ALI-файлов только в текущем каталоге.

При использовании опции -i

существующие в различных каталогах объектные и ALI-файлы будут соответственно переписаны.

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

При этом, если объектные и ALI-файлы не будут обнаружены в путях поиска объектных файлов Ады, то будут созданы новые объектные и ALI-файлы в каталоге, который содержит файлы с компилируемыми исходными текстами.

Если желательна другая организация, когда объектные файлы и файлы с исходными текстами храняться в отдельных каталогах, может быть использован удобный прием при котором в требуемых каталогах создаются ALI-файлы "заглушки".

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

-jn  - 

Использовать n процессов для выполнения перекомпиляции.

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

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

Если это вызывает проблемы, то следует перезапустить процесс сборки установив n в 1, для получения более чистого списка сообщений.

-k  - 

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

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

-m  - 

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

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

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

-M  - 

Проверка согласованности обновлений всех объектов.

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

в форме которая позволяет непосредственное использование этой информации в файле Makefile

для сборки проекта под управлением программы GNU make.

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

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

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

Если указана только опция -M, то вывод зависимостей от внутренних системных файлов GNAT не производится.

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

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

Примечательно, что зависимость от объектов которые расположены во внешних Ада-библиотеках (см. опции -aLdir) никогда не отображается.

-n  - 

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

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

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

-o exec_name  - 

Определение имени для генерируемого исполняемого файла приложения. При этом имя исполняемого файла будет exec_name.

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

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

-q  - 

Устанавливает "молчаливый" режим работы утилиты gnatmake.

При обычном режиме (без использования этой опции) утилита gnatmake

отображает все выполняемые ею команды.

-s  - 

Выполнить перекомпиляцию, если опции компилятора были изменены при последней компиляции.

Все опции компиляции, кроме -I и -o, рассматриваются в следующем порядке: порядок следования между различными опциями (которые заданы первыми буквами) игнорируется, но выполняется анализ порядка следования между одинаковыми опциями.

Например, указание опций -O -O2 будет отличаться от -O2 -O, но указание опций -g -O эквивалентно -O -g.

-v  - 

Устанавливает "многословный" режим при котором утилита gnatmake отображает смысл причины выполнения всех необходимых перекомпиляций.

-z  - 

Указывает на отсутствие главной подпрограммы.

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

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

Опции gcc
(gnatgcc)
 - 

Опция -g или любая другая опция заданная символом верхнего регистра (кроме -A или -L) или любая опция, которая описывается более чем одним символом будет передана gnatgcc (например, -O, -gnato и т.д.).



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


В системе компилятора 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".

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



Опции компилятора


Программа gcc (или gnatgcc, для дистрибутива ALT) принимает следующие опции командной строки, которые позволяют непосредственно управлять процессом компиляции:

-b target Компиляция программы которая предназначена для запуска на платформе target. Здесь target подразумевает имя конфигурации системы. Если target не является той же самой системой на которой выполняется компиляция, то система компилятора GNAT должна быть построена и сконфигурирована так, чтобы обеспечивать кросс-компиляцию для платформы target.
-Bdir  -  Загрузить компилятор (например, Ада-компилятор gnat1) из каталога dir

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

Использование этой опции имеет смысл только в случае доступности различных версий компилятора GNAT.

За более подробными сведениями следует обратиться к руководству по gcc.

Вместо этой опции обычно используются опции -b или -V.

-c  -  Выполнить компиляцию. Эта опция всегда используется при выполнении компиляции Ада-программы.

Примечательно, что можно использовать gcc (или gnatgcc) без опции -c

для выполнения компиляции и компоновки за один шаг.

Это вызвано необходимостью вызова редактора связей, но в текущий момент gnatgcc

не может быть использован для запуска редактора связей GNAT.

-g  -  Выполнить генерацию отладочной информации.

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

Опцию -g необходимо использовать если планируется последующее использование отладчика (как правило gdb).

-Idir  -  Указывает компилятору каталог dir для поиска файлов с исходными текстами необходимыми для текущей компиляции.
-I-  -  Указывает компилятору, что не нужно искать файлы с исходными текстами в каталогах указанных в командной строке.
-o file  -  Эта опция используется для перенаправления вывода генерируемого компилятором объектного файла и ассоциируемого с ним ALI-файла.




Данная опция нуждается в аккуратном использовании поскольку это может привести к тому, что объектный файл и ALI-файл будут иметь различные имена, что в результате может "запутать" редактор связей gnatbind и компоновщик gnatlink.
-O[n]  -  Здесь, значение n определяет уровень оптимизации:

n = 0  -  Оптимизация отсутствует, установлено по умолчанию если не указана опция -O

n = 1  -  Нормальная оптимизация, установлено по умолчанию если указана опция -O

без операнда
n = 2  -  Экстенсивная оптимизация
n = 3  -  Экстенсивная оптимизация с автоматической встроенной подстановкой (inline). Это применяется только для встроенной подстановки внутри модулей.
-S  -  Используется совместно с опцией -c для генерации файла с исходным текстом на языке ассемблера (расширение имени файла .s) вместо файла с объектным кодом.

Это может быть полезно когда необходима проверка генерируемого кода для ассемблера.
-v  -  Установить "многословный" режим, который показывает все команды, генерируемые gcc

(gnatgcc).

Обычно используется только в целях отладки или когда необходимо убедиться в правильности используемой/запускаемой версии компилятора.
-V ver  -  Запустить на выполнение версию компилятора ver. Подразумевается версия gcc (gnatgcc), а не версия GNAT.
-funwind-tables  -  Эта опция производит генерацию объектного файла с unwind table information.

Это требуется для обработки исключений с нулевыми затратами производительности, которая использует трассировочные способности библиотеки GNAT.
-gnata  -  Активирует использование контрольных инструкций (assertion) устанавливаемых директивами компилятора "pragma Assert" и "pragma Debug".
-gnatb  -  Генерировать краткие сообщения на stderr даже при установленном опцией -v "многословном" режиме.
-gnatc  -  Выполнить проверку синтаксиса и семантики (без генерации какого-либо выходного кода).
-gnatD  -  Выводить расширенные исходные тексты для отладки на уровне исходных текстов. Эта опция подавляет генерацию информации о перекрестных ссылках (см. опцию -gnatx).
-gnate  -  Принудительная генерация сообщений об ошибках (используется в случае разрушения компилятора при компиляции).
-gnatE  -  Выполнить полную проверку динамической элаборации.
-gnatf  -  Полные ошибки. Множественные ошибки в одной строке, все неописанные ссылки.
-gnatF  -  Все внешние имена приводить к верхнему регистру.
-gnatg  -  Активация проверки стиля написания исходного текста.
-gnatG  -  Отобразить генерируемый расширенный код в виде исходного текста.
-gnatic  -  Установить кодировку символов для идентификаторов в исходном тексте (c=1/2/3/4/8/p/f/n/w).
-gnath  -  Выдать информацию об использовании. Вывод осуществляется на stdout.
-gnatkn  -  Ограничить длину имен файлов длиной n (1-999) символов (k = krunch).
-gnatl  -  Выводить полный листинг исходных текстов, включая встроенные сообщения об ошибках.
-gnatmn  -  Ограничить число детектируемых ошибок величиной n (1-999).
-gnatn  -  Активировать встроенную подстановку (inline) в пределах модуля для подпрограмм, которые указаны в директивах компилятора Inline.
-fno-inline  -  Подавить любые встроенные подстановки (inline), даже если активированы другие опции оптимизации и встроенная подстановка.
-fstack-check  -  Активировать проверку состояния стека.
-gnato  -  Активация прочих проверок, которые обычно не используются по умолчанию, включая проверку численного переполнения и проверку доступа до проверки элаборации.
-gnatp  -  Подавить все проверки.
-gnatq  -  При обнаружении ошибки синтаксического разбора, попытаться осуществить семантическую проверку.
-gnatP  -  Включение опроса.

Это требуется на некоторых системах (в частности, для Windows NT) для осуществления асинхронного принудительного прекращения и способности асинхронной передачи управления.
-gnatR  -  Вывести информацию о представлении описанных типов массивов и записей.
-gnats  -  Выполнить синтаксическую проверку.
-gnatt  -  Сгенерировать выходной файл дерева.
-gnatT nnn  -  Установить квант времени (time slice) в указанное число миллисекунд.
-gnatu  -  Вывести список модулей текущей компиляции.
-gnatU  -  Обозначить все сообщения об ошибках уникальной строкой "error:".
-gnatv  -  Установить "многословный" режим. Выполняется полный вывод сообщений об ошибках с выводом строк исходного текста на stdout.
-gnatwm  -  Установить режим выдачи предупредительных сообщений. Где m=s,e,l соответственно означает: подавление сообщений (suppress), трактовать как ошибку (treat as error), предупреждения элаборации (elaboration warnings).
-gnatWe  -  Установить кодировку для wide character

(e=n/h/u/s/e/8).
-gnatx  -  Подавление генерации информации о перекрестных ссылках.
-gnatwm  -  Режим предупреждающих сообщений.
-gnaty  -  Включение встроенной проверки стиля.
-gnatzm  -  Генерация и компиляция распределенных "заглушек". Где, m=r/c "заглушка" приемника (receiver)/вызова (caller).
-gnat83  -  Установить принудительные ограничения Ada 83.
-gnat95  -  Установить стандартный режим Ada 95.

Опции компоновщика gnatlink


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

-A

Указывает gnatlink, что сгенерированный редактором связей gnatbind код является Ада-кодом. Это принимается по умолчанию.

-C  - 

Указывает gnatlink, что сгенерированный редактором связей gnatbind код является C-кодом.

-g  - 

Опция включения отладочной информации, использование которой приводит к тому, что Ада-файл редактора связей gnatbind (b~mainprog.adb) будет скомпилирован с опцией -g.

Дополнительно, при указании этой опции, редактор связей gnatbind не будет удалять файлы b~mainprog.adb, b~mainprog.o и b~mainprog.ali, которые удаляются в случае отсутствия опции -g.

Та же самая процедура выполняется при генерации редактором связей gnatbind C-файла, в результате указания для редактора связей опции -C.

В этом случае файлы, которые генерирует редактор связей gnatbind, будут иметь следующие имена: b_mainprog.c и b_mainprog.o.

-n  - 

Не выполнять компиляцию файла сгенерированного редактором связей gnatbind.

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

-v  - 

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

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

-v -v  - 

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

-o exec-name  - 

exec-name указывает альтернативное имя для генерируемого исполняемого файла программы.

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

Например, команда

gnatlink try.ali

будет создавать исполняемый файл с именем try.

-b target  - 

Компиляция программы для запуска на платформе target, при этом target определяет имя системной конфигурации.

Для выполнения этого, необходимо наличие построенного кросс-компилятора GNAT, если платформа target не является хост-системой.

-Bdir  - 

Загрузить исполняемый файл компилятора (например, gnat1 - Ада-компилятор) из каталога dir, вместо каталога по умолчанию.

Эта опция может быть использована только при наличии множества версий компилятора GNAT.

Вместо этой опции могут быть использованы опции -b или -V.

--GCC=compiler_name  - 

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

По умолчанию - используется программа gcc (gnatgcc).

Если указание compiler_name содержит пробелы или другие разделительные символы, то compiler_name

необходимо заключать в кавычки.

В качестве примера, --GCC="foo -x -y" указывает gnatlink, что в качестве компилятора необходимо использовать foo -x -y.

Примечательно, что опция -c всегда вставляется после имени команды.

Таким образом, показанный выше пример команды компилятора, которая будет использована gnatlink, в результате, будет иметь вид foo -c -x -y.

--LINK=name  - 

Данная опция указывает имя name используемого компоновщика системы.

Это удобно при работе с программами части которых написаны на разных языках программирования, например, c++ требует использования своего собственного компоновщика.

Когда эта опция не указана, то используется имя компоновщика по умолчанию - gcc (gnatgcc).



Опции оптимизации компилятора


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

-O0

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

-O или -O1  - 

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

-O2  - 

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

-O3  - 

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

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

При этом следует учитывать, что согласно замечаний в GCC FAQ округление вещественных чисел с плавающей точкой может вызвать проблемы при использовании опций командной строки компилятора -O2 и -O3

без одновременного использования опции -ffloat-store

(сохранение вещественных чисел с плавающей точкой вне регистров процессора, что замедляет выполнение программы).

На производительность результирующей программы оказывают влияние опции командной строки компилятора которые управляют встроенной подстановкой (inline):

-gnatn  - 

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

используется в спецификациях пакетов.

-gnatN  - 

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

отсутствие
-gnatn / -gnatN
 - 

встроенная подстановка между пакетами не осуществляется даже в случаях, когда директива компилятора Inline

используется в спецификациях пакетов.

<


Следует заметить, что использование этих опций требует одновременного использования опции -O, иначе эти опции не приведут к ожидаемому результату.

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

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

pragma Suppress (All_Checks);

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

(gnatgcc):

-ffast-math  -  GCC будет игнорировать некоторые требования математических правил ANSI и IEEE.

Например, в результате применения этой опции, перед вызовом функции sqrt

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

Следует однако учитывать, что хотя применения этой опции может повысить производительность выполнения математических действий, в результате могут быть обнаружены побочные эффекты при использовании библиотек ожидающих соответствие требованиям математических правил ANSI/IEEE.
-fomit-frame-pointer  -  GCC будет освобождать регистры, которые обычно предназначены для сохранения указателя на кадр стека.

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

Опции редактора связей gnatbind


Программа gnatbind является редактором связей (binder) системы компилятора GNAT.

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

-aO

Определяет каталог в котором будет производиться поиск *.ALI-файлов.

-aI  - 

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

-A  - 

Генерировать программу редактора связей на Аде (установлено по умолчанию).

-b  - 

Генерировать краткие сообщения в stderr даже когда установлен режим многословных (verbose) сообщений.

-c  - 

Только проверка, генерация выходного файла редактора связей отсутствует.

-C  - 

Генерировать программу редактора связей на C.

-e  - 

Выводить полный список зависимостей последовательности элаборации.

-E  - 

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

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

В настоящее время, опция поддерживается только для Solaris, Linux и Windows на платформе ix86.

Для Solaris и Linux необходимо явное использование флага -funwind-tables для gcc (gnatgcc), при компиляции каждого файла приложения.

Дополнительная информация находится в пакетах GNAT.Traceback и GNAT.Traceback.Symbolic.

Чтобы активировать действие этой опции для Windows

нет необходимости в использовании дополнительных опций, однако нельзя использовать флаг -fomit-frame-pointer для gnatgcc.

-f  - 

Полная семантика элаборации, в соответствии с требованиями стандарта.

-h  - 

Вывод справочного сообщения (help) об использовании.

-I  - 

Определяет каталог для поиска файлов с исходными текстами и *.ALI-файлов.

-I-  - 

Не производить поиск файлов с исходными текстами в текущем каталоге, откуда gnatbind был запущен, и не производить поиск *.ALI-файлов в каталогах указанных в командной строке.

-l  - 

Отобразить выбранный порядок элаборации.

-Mxyz  - 

Переименовать сгенерированную главную программу из main в xyz.

-mn  - 

Ограничить число обнаруженных ошибок до n (1-999).

-n  - 

Главная программа отсутствует.

-nostdinc  - 

Не производить поиск файлов с исходными текстами в системных каталогах по умолчанию.

-nostdlib  - 

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

-o file  - 

Указывает имя file для выходного файла (по умолчанию имя выходного файла задается как b~xxx.adb).

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

-O  - 

Вывод списка объектов.

-p  - 

Пессиместический (худший случай) порядок элаборации.

-s  - 

Требуется присутствие всех файлов с исходными текстами.

-static  - 

Выполнить компоновку приложения со статическими библиотеками времени выполнения GNAT.

-shared  - 

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

-t  - 

Допускать ошибку метки времени создания и другие ошибки целостности/согласованности.

-Tn  - 

Установить значение кванта времени (time slice) в n миллисекунд. Нулевое значение подразумевает отсутствие квантования по времени, а также указывает могозадачному окружению времени выполнения на необходимость максимального соответствия требованиям приложения D (Annex D) RM.

-v  - 

Режим многословных (verbose) сообщений. Осуществляет вывод сообщений об ошибках, заголовков и общий отчет на stdout.

-wx  - 

Установка режима предупредительных (warning) сообщений.

(x=s/e для подавления / для трактования как ошибки).

-x  - 

Исключить проверку целостности/согласованности для файлов с исходными текстами (проверка выполняется только для объектных файлов).

-z  - 

Главная подпрограмма отсутствует.

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



Оптимизация для специфического типа процессора


Для GCC версий 2.x существует две основные опции оптимизации, которые основаны на специфическом типе процессора. Это указано в

руководстве по GCC:

-mno-486  -  оптимизация для 80386.
-m486  -  оптимизация для 80486. Однако, такая программа сможет выполняться на процессоре 80386.

Следует заметить, что в настоящий момент для GCC версий 2.x, нет опций поддержки новых типов процессоров фирмы Intel (Pentium и более новые).

Однако предполагается, что будущие версии GNAT, которые будут собраны с использованием GCC версий 3.x и более новыми версиями GCC, возможно, будут полноценно поддерживать следующие опции:

-mpentium  -  оптимизация для Pentium / Intel 586.
-mcpu=i686  -  оптимизация для Pentium II/ Intel 686.
-mcpu=k6  -  оптимизация для AMD K6.

Для GCC 2.8.1, который используется совместно с GNAT, рекомендуется следующая комбинация опций компилятора для получения разумного выигрыша в производительности при использовании процессора Pentium:

-m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strength-reduce

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

Для получения более полной информации следует обратиться к gcc FAQ.

Рассмотрим совместное использование этих опций.

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

В процессе разработки программы, можно использовать утилиту gnatmake с опцией -O1.

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

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

-m486 -O3 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -fno-strength-reduce -gnatp

В результате, это позволяет обеспечить максимальную производительность при выполнении программы на процессоре Intel Pentium.

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



Оптимизация проекта


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

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

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

Например, алгоритм пузырьковой сортировки является легким способом сортировки данных относительно маленького объема, однако алгоритм квиксорт (quick sort) обладает производительностью, которая во много раз выше.

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

Как правило, подобные подпрограммы называют узкими местами.

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

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

Некоторая оптимизация может быть выполнена компилятором GNAT автоматически.

Для оптимизации программы можно использовать как опции командной строки, так и директивы компилятора.



Особенности используемого ассемблера


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

Причина в том, что ассемблер, который используется семейством компиляторов GCC, не использует привычный язык ассемблера Intel, а использует язык ассемблера, который происходит от ассемблера as системы AT&T Unix (его часто называют ассемблером с синтаксисом AT&T).

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



Особенности реализации компилятора GNAT


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

В следствие этого, реализация GNAT обладает следующими основными преимуществами:

базовый интерфейс хорошо согласован с множеством различных целевых платформ

обеспечено хорошее взаимодействие с оптимизатором компилятора, что позволяет генерировать очень эффективный код

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



Отладка настраиваемых модулей


При конкретизации настраиваемого модуля GNAT всегда использует расширение кода.

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

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

Рассмотрим следующий пример:

procedure g is

generic package k is

procedure kp (v1 : in out integer); end k;

package body k is

procedure kp (v1 : in out integer) is

begin

v1 := v1 + 1; end kp; end k;

package k1 is new k; package k2 is new k;

var : integer := 1;

begin

k1.kp (var); k2.kp (var); k1.kp (var); k2.kp (var); end;

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

(gdb) break g.k2.kp

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



Отслеживание состояния стека и обнаружение утечек памяти во время выполнения программы


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

Эти средства предоставляются пакетами Gnat.Traceback и Gnat.Traceback.Symbolic

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

Для того, чтобы активировать использование этих средств необходимо при компиляции программы указать опцию -funwind-tables, а при связывании программы - опцию -E.



Пакет SystemMachine_Code


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

Для таких случаев Ада предоставляет пакет System.Machine_Code

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

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

Следовательно, здесь основное внимание сосредоточено на реализации этого пакета для компилятора GNAT.



Пакеты


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

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".



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


Пакет является свойством файла проекта с помощью которого обеспечивается возможность описания установок для индивидуальных инструментов, которые интегрированы со средствами управления проектами системы компилятора 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, который позволяет установить необходимые соглашения по именованию исходных файлов.



Параметры ввода


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

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

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

with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code;

procedure Inc_It is

function Increment (Value : Unsigned_32) return Unsigned_32 is

Result : Unsigned_32; begin

Asm ("incl %0", Inputs => Unsigned_32'Asm_Input ("a", Value), Outputs => Unsigned_32'Asm_Output ("=a", Result)); return Result; end Increment;

Value : Unsigned_32;

begin

Value := 5; Put_Line ("Value before is" & Value'Img); Value := Increment (Value); Put_Line ("Value after is" & Value'Img); end Inc_It;

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

Asm ("incl %0", Inputs => Unsigned_32'Asm_Input ("a", Value), Outputs => Unsigned_32'Asm_Output ("=a", Result));

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

Описание параметра ввода во многом похоже на описание параметра вывода, но использует атрибут 'Asm_Input вместо атрибута 'Asm_Output.

Кроме того, при описании параметра ввода, отсутствует указание ограничения =, указывающего на вывод значения.

Также как и в случае параметров вывода, допускается наличие множества параметров ввода.

Отсчет параметров (%0, %1, ...) начинается с первого параметра ввода и продолжается при перечислении инструкций вывода.




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

Asm ("incl %0", Inputs => Unsigned_32'Asm_Input ("a", Value), Outputs => Unsigned_32'Asm_Output ("=a", Result));

указывает компилятору:
загрузить 32-битное значение переменной Value в регистр eax
выполнить инструкцию incl %eax
сохранить содержимое регистра eax в переменной Result
Если посмотреть на сгенерированный компилятором файл ассемблера (используя полную оптимизацию), то можно обнаружить следующее:

_inc_it__increment.1: subl $4,%esp movl 8(%esp),%eax #APP incl %eax #NO_APP movl %eax,%edx movl %ecx,(%esp) addl $4,%esp ret

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