Параметры вывода
При желании сохранить содержиме регистра eax в какой-либо переменной, первым желанием будет написать что-нибудь подобное следующему:
procedure Getflags is
Eax : Unsigned_32; begin Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, Eax") -- сохранить значение флагов в переменной Put_Line ("Flags register:" & Eax'Img); end Getflags; |
с попыткой сразу сохранить содержимое регистра процессора eax в переменной Eax.
Увы, также просто как это может выглядеть, это не будет работать, поскольку нельзя точно сказать чем будет являться переменная Eax.
Несколько подумав, можно сказать: Eax - это локальная переменная, которая, в обычной ситуации, будет размещена в стеке.
Однако, в результате оптимизации, компилятор, для хранения этой переменной во время выполнения подпрограммы, может использовать не пространство в стеке, а обычный регистр процессора.
Таким образом, возникает законный вопрос: как необходимо специфицировать переменную, чтобы обеспечить корректную работу для обоих случаев?
Ответ на этот вопрос заключается в том, чтобы не сохранять результат в переменной Eax
самостоятельно, а предоставить компилятору возможность самостоятельно выбирать правильный операнд для использования.
Для этой цели применяется понятие параметра вывода.
Программа, использующая инструкцию вывода, будет выглядеть так как она была написана ранее:
Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0") -- сохранить значение флагов в переменной |
Следует обратить внимание на то, что мы заменили обращение к переменной Eax
на обращение к операнду %0.
Однако, компилятору необходимо указать, чем является %0:
Outputs => Unsigned_32'Asm_Output ("=g", Eax)); |
Здесь, часть "Outputs =>" укажет, что это именованный параметр инструкции ассемблера (полный синтаксис инструкций ассемблера будет рассмотрен позже в "Синтаксис GNAT").
Напомним также, что ранее мы описали переменную Eax, как переменную типа Interfaces.Unsigned_32.
Мы описали ее таким образом, поскольку 32-битный беззнаковый целый тип удачнее всего подходит для представления регистра процессора.
Не трудно заметить, что вывод назначен как атрибут типа, который мы реально хотим использовать для нашего вывода.
Unsigned_32'Asm_Output ("=g", Eax); |
Общая форма такого описания имеет следующий вид:
Type'Asm_Output (строка_ограничений, имя_переменной) |
Смысл имени переменной и атрибута 'Asm_Output достаточно понятны.
Остается понять, что означает и для чего используется строка_ограничений.
Перекомпиляция библиотеки времени выполнения GNAT
Бывают случаи когда возникает необходимость в самостоятельной перекомпиляции библиотеки времени выполнения GNAT.
Для таких случаев предусматривается специальный файл Makefile.adalib
для управления сборкой проекта с помощью утилиты GNU make.
Этот файл находится в каталоге, который содержит библиотеку времени выполнения GNAT.
Расположение этого каталога в системе зависит от способа установки окружения ссистемы компилятора GNAT, что можно узнать с помощью команды:
$ gnatls -v |
Последняя строка, которая показывает путь поиска объектных файлов (Object Search Path), как правило, содержит путь к библиотеке времени выполнения GNAT.
Файл Makefile.adalib содержит в себе документацию с инструкциями, которые необходимы для самостоятельной сборки и использования новой библиотеки.
Copyright (C) А.Гавва | V-0.4w май 2004 |
Переменные
Переменная может быть описана на уровне файла проекта или внутри пакета файла проекта. Примерами описания переменных может служить следующее:
This_OS : OS := external ("OS"); -- описание типированной переменной That_OS := "Linux"; -- описание нетипированной переменной |
Любое описание типированной переменной начинается с имени переменной за которым следует символ двоеточия, затем следует имя строкового типа, сопровождаемое ":=" и, далее, простое строковое выражение.
Любое описание нетипированной переменной начинается с имени переменной за которым следует ":=", сопровождаемое выражением.
Следует заметить, что несмотря на терминологию, такая форма "описания" больше похоже на присваивание чем на описание в языке Ада. Такая форма является описанием в нескольких смыслах:
Имя переменной не нуждается в предварительном описании. Описание основывает разновидность (строка или список строк) переменной, и последующие описания той же переменной должны быть согласованы
Описание строковой переменной (типированной или нетипированной) описывает переменную значением которой является строка. Эта переменная может быть использована как строковое выражение. Например:
File_Name := "readme.txt"; Saved_File_Name := File_Name & ".saved"; |
Описание переменной списка строк описывает переменную значением которой является список строк. Такой список может содержать любое число (нуль и более) строк.
Empty_List := (); List_With_One_Element := ("-gnaty"); List_With_Two_Elements := List_With_One_Element & "-gnatg"; Long_List := ("main.ada", "pack1_.ada", "pack1.ada", "pack2_.ada" "pack2.ada", "util_.ada", "util.ada"); |
Одна и та же типированная переменная не может быть описана более одного раза на уровне проекта и она не может быть описана более одного раза в любом пакете файла проекта. Типированная переменная подобна константе или переменной, которая доступна только для чтения.
Одна и та же нетипированная переменная может быть описана более одного раза. В таком случае, новое значение переменной будет заменять ее старое значение, и последующие ссылки (обращения) к этой переменной будут использовать новое значение.
Однако, как отмечалось ранее, если переменная была описана как строковая, то все последующие описания должны предоставлять строковое значение.
Подобным образом, если переменная была описана как список строк, все последующие описания переменной должны предоставлять значение в виде списка строк.
Любая ссылка к переменной (или обращение к переменной) может иметь несколько форм:
Имя переменной, для переменных расположенных в текущем пакете (если есть) или в текущем проекте Имя контекста, сопровождаемое символом точки и, далее, имя переменной.
В качестве контекста переменной может служить:
Имя существующего пакета в текущем проекте. Имя импортированного проекта в текущем проекте. Имя проекта-предка (например, какой-либо проект, прямо или косвенно, расширяется текущим проектом). Имя проекта-предка/импортируемого проекта, сопровождаемое символом точки и именем имени пакета.
Ссылка (обращение) к переменной может быть использована в выражении.
Переменные импортируемых проектов
Какой-либо атрибут или переменная, которые были описаны в импортируемом проекте или проекте-предке, могут быть использованы в выражениях, которые используются в импортирующем или расширяющем проекте.
В этом случае, для обращения к атрибуту или переменной используется префикс состоящий из имени проекта и, при необходимости, имени пакета, где атрибут или переменная были описаны.
with "imported"; project Main extends "base" is
Var1 := Imported.Var; Var2 := Base.Var & ".new"; package Builder is for Default_Switches ("Ada") use Imported.Builder.Ada_Switches & "-gnatg" & "-v"; end Builder; package Compiler is for Default_Switches ("Ada") use Base.Compiler.Ada_Switches; end Compiler; end Main; |
В показанном выше примере:
Переменная Var1 является копией переменной Var, которая описана в файле проекта "imported.gpr".
Значение переменной Var2 является конкатенацией копии значения переменной Var, которая описана в файле проекта "base.gpr", со строкой ".new".
Атрибут Default_Switches ("Ada"), в пакете Builder, является списком строк, который включает в свое значение копию переменной Ada_Switches, описанную в пакете Builder в файле проекта "imported.gpr", плюс два новых элемента: "-gnatg" и "-v".
Атрибут Default_Switches ("Ada"), в пакете Compiler, является копией переменной Ada_Switches, которая описана в пакете Compiler в файле расширяемого проекта "base.gpr".
Переносимость в UNIX, пакеты GNU Automake и GNU Autoconf
Если вы используете систему Linux и желаете создать проект, который будет выполняться на различных платформах UNIX, а не только в Linux, то вам необходимо использовать пакеты GNU Autoconf и GNU Automake.
Следует заметить, что свободно распространяемое программное обеспечение GNU использует средства этих пакетов очень интенсивно.
Поставляемый в системе Linux пакет GNU Autoconf
позволяет создать скрипт командного интерпретатора с именем configure, который настроен под конкретный проект.
Когда этот скрипт выполняется, он осуществляет сканирование средств той системы UNIX на которой он выполняется.
В результате, этот скрипт осуществляет необходимую подстройку файлов Makefile, которые управляют сборкой проекта.
В качестве необязательного дополнения он может генерировать C-файл config.h, который содержит информацию об обнаруженных средствах.
Пакет GNU Automake является дополнением к пакету GNU Autoconf.
С помощью утилиты automake
он позволяет создавать файлы Makefile.in из шаблонных файлов Makefile.am.
Как только работа утилиты automake завершена, для получения окончательных версий файлов Makefile
необходимо запустить скрипт configure.
После этого остается только выполнить команду make, которая построит проект для любой версии UNIX.
Существует возможность использования средств пакетов GNU Autoconf и GNU Automake
с файлами Makefile, которые используются для управления сборкой проектов программ на языке Ада.
Для получения подробных сведений об использовании этих пакетов следует обратиться к соответствующей документации или использовать команды "info autoconf" и "info automake".
Copyright (C) А.Гавва | V-0.4w май 2004 |
Поиск ошибок в коде ассемблера
Часто, при совершении ошибки в ассемблерном коде (подобной использованию не правильного модификатора размера или оператора для какой-либо инструкции) GNAT сообщает о такой ошибке во временном файле, который уничтожается после завершения процесса компиляции.
В подобных ситуациях, генерация ассемблерного файла, показанная в примере выше, может оказаться не заменимым отладочным средством, поскольку полученный ассемблерный файл может быть самостоятельно ассемблирован с помощью ассемблера as
(используемый в системе или поставляемый вместе с GNAT).
Так, для ассемблирования файла nothing.s, полученного ранее с помощью GNAT, можно использовать следующую команду:
as nothing.s |
Сообщения об ошибках ассемблирования будут указывать строки в соответствующем ассемблерном файле.
В результате, такая информация предоставляет возможность достаточно легко обнаружить и исправить ошибки, которые были допущены в первоначальном исходном тексте.
Получение расширенной информации компилятора
Опция командной строки компилятора -gnatG
позволяет получить информацию о том как GNAT интерпретирует исходный текст после его первоначального анализа.
Если при этом одновременно использована опция -gnatD, то GNAT сохранит эту информацию в файле с расширением имени .dg
(для отладки "debug").
Для демонстрации использования опции -gnatG
рассмотрим пример простой программы Pointers:
with System.Address_To_Access_Conversions; with Ada.Text_IO; use Ada.Text_IO;
procedure Pointers is package Int_Ptrs is new System.Address_To_Access_Conversions( Integer ); -- Конкретизация настраиваемого пакета для обеспечения возможности -- выполнения преобразования между ссылочными типами и адресами. -- Это также дополнительно создает ссылочный тип Object_Pointer, -- позволяющий ссылаться на значения целочисленного типа Integer. Five : aliased Integer := 5; -- Переменная Five описана как косвенно адресуемая, -- поскольку мы будем обращаться к ней с помощью -- значений ссылочного типа Int_Pointer : Int_Ptrs.Object_Pointer; -- Обобщенный ссылочный тип Ады Int_Address : System.Address; -- Адрес в памяти, подобный указателю языка C begin Int_Pointer := Five'Unchecked_Access; -- Использование атрибута 'Unchecked_Access необходимо, -- поскольку переменная Five локальна для головной программы. -- Если бы она была глобальной, то мы могли бы использовать -- атрибут 'Access вместо атрибута 'Unchecked_Access. Int_Address := Five'Address; -- Адреса моут быть определены с помощью атрибута 'Address. -- Это эквивалентно указателям языка C. Int_Pointer := Int_Ptrs.To_Pointer( Int_Address ); Int_Address := Int_Ptrs.To_Address( Int_Pointer ); -- Преобразование типов указателей Ады и C. end Pointers; |
Используя опцию командной строки компилятора -gnatG, для показанной выше программы Pointers, можно получить от GNAT информацию о результате анализа этой программы.
В данном случае, это отображает результаты конкретизации настраиваемого пакета:
Source recreated from tree for Pointers (body) ----------------------------------------------
with ada; with system; with system.system__address_to_access_conversions; with ada.ada__text_io; use ada.ada__text_io; with system; with system; with unchecked_conversion; procedure pointers is package int_ptrs is subtype int_ptrs__object is integer; package int_ptrs__address_to_access_conversions renames int_ptrs; type int_ptrs__object_pointer is access all int_ptrs__object; for int_ptrs__object_pointer'size use 32; function pointers__int_ptrs__to_pointer (value : system__address) return int_ptrs__object_pointer; function pointers__int_ptrs__to_address (value : int_ptrs__object_pointer) return system__address; pragma convention (intrinsic, pointers__int_ptrs__to_pointer); pragma convention (intrinsic, pointers__int_ptrs__to_address); end int_ptrs; package body int_ptrs is function pointers__int_ptrs__to_address (value : int_ptrs__object_pointer) return system__address is begin if value = null then return system__null_address; else return value.all'address; end if; end pointers__int_ptrs__to_address; function pointers__int_ptrs__to_pointer (value : system__address) return int_ptrs__object_pointer is package a_to_pGP3183 is subtype a_to_pGP3183__source is system__address; subtype a_to_pGP3183__target is int_ptrs__object_pointer; function pointers__int_ptrs__to_pointer__a_to_pGP3183__a_to_pR (s : a_to_pGP3183__source) return a_to_pGP3183__target; end a_to_pGP3183; function a_to_p is new unchecked_conversion (system__address, int_ptrs__object_pointer); begin return a_to_pGP3183__target!(a_to_pGP3183__source(value)); end pointers__int_ptrs__to_pointer; end int_ptrs; package int_ptrs is new system.system__address_to_access_conversions (integer); five : aliased integer := 5; int_pointer : int_ptrs.int_ptrs__object_pointer := null; int_address : system.system__address; begin int_pointer := five'unchecked_access; int_address := five'address; int_pointer := int_ptrs.pointers__int_ptrs__to_pointer (int_address); int_address := int_ptrs.pointers__int_ptrs__to_address (int_pointer); return; end pointers; |
Получение внутренней отладочной информации
Большинство компиляторов обладают внутренними опциями и режимами отладки. В этом смысле GNAT не является исключением, более того, все внутренние опции и режимы отладки GNAT не являются секретом. Общее и полное описание всех опций отладки компилятора и редактора связей можно найти в файле "debug.adb", который присутствует в комплекте исходных файлов GNAT.
Опции позволяющие получить и распечатать исходный текст программы, который построен из дерева внутреннего представления, представляют большой интерес для пользователей программ. Не менее интересны опции, которые позволяют распечатывать полное дерево внутреннего представления и таблицу сущностей (иначе, таблица с информацией о символах).
Восстановленный из внутреннего представления исходный текст предоставляет читаемую версию программы, которая получена из внутреннего представления после того как препроцессор (front-end) завершил анализ и расширение. Такой исходный текст может быть полезен при изучении эффективности характерных конструкций.
Например, в получаемом таким образом исходном тексте: индицированы проверки ограничений, сложные агрегаты заменены циклами и присваиваниями, примитивы задач заменены обращениями к библиотеке времени выполнения.
Порядок следования операндов источника и приемника
В действительности, приведенный выше пример не будет успешно ассемблироваться с GNAT.
В то время как соглашения Intel устанавливают порядок следования операндов источника и приемника как:
mov приемник, источник |
синтаксис AT&T использует обратный порядок:
mov источник, приемник |
Таким образом пример должен быть переписан:
mov 4, %eax |
Построение больших проектов
Утилита gnatmake, которая входит в состав системы компилятора GNAT, является прекрасным инструментом для построения небольших проектов.
Однако при построении больших проектов или в случае каких-либо особенностей разрабатываемого проекта (например, при использовании большого количества внешних функций языка C) может возникнуть желание использовать для управления построением проекта каких-либо дополнительных средств.
Данная глава предоставляет некоторую информацию по совместному использованию с GNAT достаточно широко распространенной утилиты управления сборкой проектов GNU make, а также пакетов GNU Autoconf и GNU Automake.
Следует заметить, что здесь не преследуется цель описать все возможности и варианты использования этих программных инструментов GNU.
Поэтому для получения более подробных сведений необходимо обратиться к соответствующей документации GNU.
Кроме того, следует учитывать, что утилита GNU make не предназначена для замены утилиты gnatmake, которая входит в систему компилятора GNAT.
Правила поиска файлов для gnatbind
Программа редактора связей gnatbind принимает имя ALI-файла как аргумент и нуждается в указании файлов с исходными текстами и других ALI-файлов для проверки целостности и согласованности всех объектов проекта.
При поиске файлов с исходными текстами используются те же правила, которые используются для gcc (gnatgcc).
Для ALI-файлов используются следующие правила поиска:
Каталоги с ALI-файлами, указанные в командной строке, если не указана опция -I-.
Все каталоги с ALI-файлами, указанные в командной строке с помощью опции -I, в соответствии с порядком указания каталогов.
В каждом каталоге, который перечислен в значении переменной окружения ADA_OBJECTS_PATH. Значение этой переменной окружения строится также как значение переменной окружения PATH: список каталогов разделенных двоеточием.
Содержимое файла ada_object_path, который является составной частью инсталляции GNAT и используется для хранения информации о стандартных библиотеках, таких как GNAT Run Time Library (RTL) пока в командной строке не указана опция -nostdlib.
Опция -I редактора связей gnatbind используется для указания пути поиска как для файлов с исходными текстами, так и для библиотечных файлов.
Чтобы указать путь поиска только для файлов с исходными текстами необходимо использовать опцию -aI, а при необходимости указания пути поиска только для библиотечных файлов необходимо использовать опцию -aO.
Это подразумевает, что для редактора связей gnatbind, указание опции -Idir будет эквивалентно одновременному указанию опций -aIdir и -aOdir.
Редактор связей gnatbind генерирует файл связывания (файл с исходным текстом на языке C) в текущем рабочем каталоге.
Пакеты Ada, System и Interfaces совместно со своими дочерними модулями, а также пакет GNAT совместно со своими дочерними модулями, которые содержат множество дополнительных библиотечных функций, формируют окружение времени выполнения реализации системы компилятора GNAT
(GNAT Run-Time Library).
Исходные тексты для этих модулей необходимы компилятору и храняться в одном каталоге.
ALI-файлы и объектные файлы, которые сгенерированы при компиляции библиотеки времени выполнения (RTL), необходимы редактору связей gnatbind
и компоновщику gnatlink.
Эти файлы сохраняются с одном каталоге, как правило отличном от каталога в котором храняться файлы с исходными текстами.
При обычной инсталляции, нет необходимости в спецификации этих каталогов при выполнении компиляции или связывания (binding).
Обнаружение этих файлов возможно как при использовании переменных окружения, так и при использовании встроенных соглашений по умолчанию.
В дополнение к упрощению доступа к библиотеке времени выполнения (RTL), основным предназначением использования путей поиска является компиляция исходных текстов из различных каталогов.
Это позволяет сделать окружение разработки более гибким.
Преодоление ограничения на длину командной строки
Многие операционные системы ограничивают длину командной строки.
Это может вызвать некоторые трудности при работе над большим проектом, поскольку затрудняет передачу списков каталогов, которые содержат файлы с исходными текстами и объектные файлы, в командной строке утилиты gnatmake.
Показанный ниже пример демонстрирует то, как можно установить переменные окружения, которые позволят утилите gnatmake выполнять обработку также как и в случае указания списков каталогов в командной строке запуска gnatmake.
При этом ограничения операционной системы позволяют использовать более длинные списки каталогов или, как в большинстве систем, длина списка будет не ограничена.
Подразумевается, что для создания списка каталогов в файле Makefile
используются ранее рассмотренные способы.
С целью полноты примера предполагается, что путь к объектным файлам (где также располагаются ALI файлы) не соответствует пути к файлам с исходными текстами.
Следует обратить внимание на небольшую хитрость, которая использована в файле Makefile: для повышения эффективности создаются две временные переменные (SOURCE_LIST и OBJECT_LIST), которые немедленно расширяются утилитой GNU make.
Примечательно, что этот способ перекрывает стандартное поведение утилиты make, когда переменные расширяются только в момент их фактического использования.
# В этом примере создаются две переменные окружения # ADA_INCLUDE_PATH и ADA_OBJECT_PATH. # Они обладают таким же эффектом как и опции -I в командной строке. # Эквивалентом использования -aI в командной строке будет # только указание переменной окружения ADA_INCLUDE_PATH, # а эквивалентом использования -aO является ADA_OBJECT_PATH. # Естественно, что для этих переменных необходимо использование # двух различных значений. # # Примечательно также, что необходимо сохранение предыдущих значений # этих переменных, поскольку они могут быть определены перед запуском # 'make' с целью указания установленных библиотек для GNAT.
# см. "Автоматическое создание списка каталогов" # для создания этих переменных SOURCE_DIRS= OBJECT_DIRS= empty:= space:=${empty} ${empty} SOURCE_LIST := ${subst ${space},:,${SOURCE_DIRS}} OBJECT_LIST := ${subst ${space},:,${OBJECT_DIRS}} ADA_INCLUDE_PATH += ${SOURCE_LIST} ADA_OBJECT_PATH += ${OBJECT_LIST} export ADA_INCLUDE_PATH export ADA_OBJECT_PATH all: gnatmake main_unit |
Примечания для командной строки gnatmake
Здесь предоставляются некоторые дополнтельные сведения, которые могут быть полезны при работе с командной строкой для утилиты gnatmake.
Если утилита gnatmake не находит ALI-файлов, то она выполняет перекомпиляцию главного файла программы и всех модулей, которые необходимы для главной программы.
Это подразумевает, что утилита gnatmake может быть использована как для выполнения начальной компиляции, так и на последующих этапах цикла разработки.
Если будет введена команда:
gnatmake file.adb
где file.adb является субмодулем или телом настраиваемого модуля, то утилита gnatmake выполнит перекомпиляцию file.adb
(поскольку она не обнаружит ALI-файла) и остановиться, выдав предупреждающее сообщение.
При использовании утилиты gnatmake с опцией -I определяются пути поиска как для файлов с исходными текстами, так и для библиотечных файлов.
Если необходимо указать пути поиска только для файлов с исходными текстами, то вместо опции -I необходимо использовать опцию -aI, а если необходимо указать пути поиска только для библиотечных файлов, то необходимо использовать опцию -aO.
Утилита gnatmake выполняет проверку согласованности ALI-файлов и соответствующих объектных файлов.
Если обнаруживается, что ALI-файл новее соответствующего объектного файла, или объектный файл отсутствует, то выполняется компиляция соответствующего файла с исходным текстом.
Примечательно, что утилита gnatmake, при выполнении проверки ALI-файлов и соответствующих объектных файлов, ожидает их наличие в одном каталоге.
Утилита gnatmake игнорирует любые файлы для которых соответствующие ALI-файлы заблокированы (запрещены по записи).
Это может быть использовано для того, чтобы исключить из проверки на согласованность файлы стандартной библиотеки, и это также подразумевает, что использование опции -f
не приведет к перекомпиляции таких файлов пока одновременно с опцией -f
не будет указана опция -a.
Утилита gnatmake была разработана с учетом согласованного использования самостоятельных Ада-библиотек.
которая организована следующим образом: каталог
Предположим, что имеется Ада-библиотека, которая организована следующим образом: каталог obj-dir содержит объектные файлы и ALI-файлы компилируемых модулей Ады, в то время как каталог include-dir содержит файлы спецификаций этих модулей, но не содержит тела (реализацию) этих модулей.
Тогда, для компиляции модуля который находится в файле main.adb и использует эту Ада-библиотеку необходимо будет использовать следующую команду:
$ gnatmake -aIinclude-dir -aLobj-dir main
Использование утилиты gnatmake с опцией -m (minimal recompilation)
является очень удобным средством, которое позволяет свободно обновлять комментарии и форматирование исходных текстов без необходимости выполнения полной перекомпиляции.
Примечательно однако, что добавление строк к файлам с исходными текстами может повлиять на отладку на уровне исходного текста, ввиду устаревания отладочной информации.
Если такая модификация касается файла со спецификацией, то влияние будет менее значительным, поскольку затронутая отладочная информация будет полезна только во время фазы элаборации программы.
Для файлов тел, такое влияние будет более значительным.
Во всех случаях, отладчик выдаст предупреждение о том, что файл с исходными текстами более новый по сравнению с файлом который содержит объектный код, и, таким образом, устаревшая отладочная информация пройдет не замеченной.
Пример элементарной программы
Пожалуй, более элементарную программу придумать не возможно:
with System.Machine_Code; use System.Machine_Code;
procedure Nothing is begin Asm ("nop"); end Nothing; |
Не составит труда догадаться, что эта программа состоит из единственной инструкции ассемблера nop, которая действительно ничего не делает.
Примеры файлов проектов
Рассмотрим несколько простых примеров, которые демонстрируют основные свойства средств управления проектами GNAT и базовую структуру файлов проектов.
Проекты библиотек
Проект библиотеки являются проектом объектный код которого помещается в какую-либо библиотеку (следует отметить, что в настоящий момент такая возможность не поддерживается для всех платформ).
Для создания проекта библиотеки, в файле проекта необходимо описать двухуровневые атрибуты: Library_Name и Library_Dir.
Дополнительно, можно описать атрибуты, которые относятся к библиотекам: Library_Kind, Library_Version и Library_Elaboration.
Атрибут Library_Name имеет строчное значение, которое должно начинаться с буквы и должно включать в себе только буквы и цифры.
Атрибут Library_Dir имеет строчное значение, которое обозначает путь к каталогу (абсолютный или относительный) где будет располагаться библиотека. Он должен обозначать уже существующий каталог, и этот каталог должен отличаться от каталога в котором будут сохраняться объектные файлы проекта. Кроме того, этот каталог должен быть доступен для записи.
В случае, когда оба атрибута, Library_Name и Library_Dir, указаны и являются допустимыми, предполагается, что файл проекта описывает проект библиотеки. Только для таких файлов проектов будут осуществляться проверка дополнительных, необязательных атрибутов, которые относятся к библиотекам.
Атрибут Library_Kind является строкой, которая должна иметь одно из следующих значений (указанные значения не зависят от регистра символов): "static", "dynamic" или "relocatable".
При отсутствии описания этого атрибута, предполагается, что библиотека является статической (то есть, значение по умолчанию - "static").
В противном случае, библиотека может быть динамической ("dynamic") или перемещаемой ("relocatable").
Следует заметить, что в зависимости от используемой операционной системы, различие между динамической и перемещаемой библиотекой может отсутствовать. Например, в UNIX такое различие отсутствует.
Атрибут Library_Version является строкой, интерпретация смысла которой зависит от платформы.
В UNIX, это значение используется только для динамических/перемещаемых библиотек, как внутреннее имя библиотеки (известное как "soname").
Если имя файла библиотеки (построенное из Library_Name) отличается от Library_Version, то файл библиотеки будет символической ссылкой на фактический файл библиотеки, именем которого будет Library_Version.
Пример файла проекта библиотеки (для UNIX):
project Plib is Version := "1"; for Library_Dir use "lib_dir"; for Library_Name use "dummy"; for Library_Kind use "relocatable"; for Library_Version use "libdummy.so." & Version; end Plib; |
Когда утилита gnatmake обнаруживает, что файл проекта (не главный файл проекта) является файлом проекта библиотеки, она проверяет все непосредственные исходные файлы проекта и осуществляет пересборку библиотеки если любой из исходных файлов был перекомпилирован.
Кроме того, все файлы "ALI" будут скопированы из каталога объектных файлов в каталог библиотеки.
При этом, для сборки исполняемых файлов утилита gnatmake
будет использовать библиотеку, а не индивидуальные объектные файлы.
Произвольное именование файлов, утилита gnatname
Как уже указывалось, компилятор должен обладать возможностью определить имя файла с исходным текстом компилируемого модуля. При использовании принимаемых по умолчанию, стандартных для GNAT соглашений именования (".ads" - для спецификаций, ".adb" - для тел), компилятор GNAT не нуждается в какой-либо дополнительной информации.
Когда именование файлов с исходными текстами не соответствет принимаемым по умолчанию, стандартным для GNAT соглашениям именования, компилятор GNAT должен получать дополнительно необходимую информацию с помощью рассмотренных ранее файлов с директивами конфигурации или с помощью файлов проектов, которые будут рассмотрены позже.
Ранее, при рассмотрении альтернативных схем именования, указывалось, что в случае хорошо организованной нестандартной схемы именования, для указания правил именования файлов с исходными текстами необходимо незначительное количество директив Source_File_Name.
Однако, когда используемая схема именования не регулярна или произвольна, для указания имен файлов с исходными текстами может потребоваться значительное число директив Source_File_Name.
Для облегчения поддержки взаимосвязи между именами компилируемых модулей и именами файлов с исходными текстами, система компилятора GNAT (версия 3.15 и более новые версии) предусматривает утилиту gnatname, которая предназначена для генерации необходимых директив для набора файлов.
Обычно запуск утилиты gnatname осуществляется с помощью команды, которая имеет следующий вид:
$ gnatname [опции] [шаблоны_имен] |
Следует заметить, что все аргументы командной строки запуска gnatname
не являются обязательными.
При использовании без аргументов, gnatname создаст в текущем каталоге файл gnat.adc, который будет содержать директивы конфигурации для всех компилируемых модулей расположенных в текущем каталоге.
Для обнаружения всех компилируемых модулей, gnatname, для всех обычных файлов расположенных в текущем каталоге, использует запуск компилятора GNAT в режиме проверки синтаксиса.
В результате, для файлов, которые содержат компилируемые модули Ады, будет осуществляться генерация директивы Source_File_Name.
В качестве аргументов gnatname могут быть указаны один или более шаблонов имен. Каждый шаблон имен должен быть заключен в двойные кавычки. Шаблон имен является регулярным выражением, которое используется для указания шаблона имен командными интерпретаторами UNIX (shell) или DOS. Ниже показаны примеры указания шаблонов имен:
"*.[12].ada" "*.ad[sb]*" "body_*" "spec_*" |
(регулярные выражения "Glob").
Отсутствие в командной строке gnatname аргументов, которые указывают шаблоны имен, является эквивалентным указанию единственного шаблона имен "*".
Все опции, которые указываются в командной строке запуска gnatname, должны предшествовать любым аргументам, которые указывают шаблоны имен.
В командной строке запуска gnatname могут быть указаны следующие опции:
-cfile | Создать файл директив конфигурации "file" (вместо создаваемого по умолчанию файла gnat.adc). Между "-c" и "file" допускается как отсутствие, так и присутствие пробелов (один и более). Указание файла "file" может содержать информацию о каталоге. Пользователь должен обладать правами записи в указываемый файл "file". В командной строке может присутствовать только одна опция "-c". При указании опции "-c" не допускается указание опции "-P". | |
-ddir |
Осуществлять поиск файлов с исходными текстами в каталоге "dir". Между "-d" и "dir" допускается как отсутствие, так и присутствие пробелов (один и более). При указании опции "-d", поиск исходных файлов в текущем рабочем каталоге не осуществляется за исключением случаев когда текущий каталог явно указан в опции "-d" или "-D". В командной строке допускается указание нескольких опций "-d". Когда каталог "dir" указывается как относительный путь, то его расположение выбирается относительно каталога в котором располагается файл с директивами конфигурации указываемый с помощью опции "-c", или относительно каталога содержащего файл проекта указываемый с помощью опции "-P", или, при отсутствии опций "-c" и "-P", относительно текущего рабочего каталога. Каталог, указываемый опцией "-d", должен существовать и должен быть доступен для чтения. |
|
-Dfile | Осуществлять поиск файлов с исходными текстами в каталогах, которые перечислены в текстовом файле "file". Между "-D" и "file" допускается как отсутствие, так и присутствие пробелов (один и более). Текстовый файл "file" должен существовать и должен быть доступен для чтения. В файле "file", каждая непустая строка должна указывать каталог. Указание опции "-D" является эквивалентом указания в командной строке такого же количества опций "-d", сколько непустых строк содержится в файле "file". | |
-h | Выводит информацию подсказки (help) об использовании. Вывод направляется в стандартное устройство вывода stdout. | |
-Pproj | Создать или обновить файл проекта "proj". Между "-P" и "proj" допускается как отсутствие, так и присутствие пробелов (один и более). Указание файла проекта "proj" может содержать информацию о каталоге. Файл "proj" должен быть доступен по записи. В командной строке может присутствовать только одна опция "-P". При указании опции "-P" не допускается указание опции "-c". | |
-v | "Многословный" (verbose) режим. Вывод детального объяснения поведения на устройство стандартного вывода stdout. Это включает: имя записонного файла; имена каталогов поиска и имена каждого файла, в этих каталогах поиска, которые соответствуют хотябы одному из указанных шаблонов имен; индикацию - является ли файл компилируемым модулем, и, если да, то вывод имени модуля. | |
-v -v | Очень "многословный" (verbose) режим. В дополнение к выводу информации генерируемой в многословном режиме, для каждого файла, который обнаружен в указанных каталогах поиска, имя которого не совпадает ни с одним из заданных шаблонов имен, выводится индикация о несовпадении имени файла. |
В заключение рассмотрим несколько простых примеров команд запуска gnatname.
Команда запуска gnatname без параметров
$ gnatname |
$ gnatname -d. "*" |
$ gnatname -c /home/me/names.adc -d sources "[a-z]*.ada*" |
Еще один пример:
$ gnatname -P/home/me/proj -dsources -dsources/plus -Dcommon_dirs.txt "body_*" "spec_*" |
Copyright (C) А.Гавва | V-0.4w май 2004 |
Проверка ошибок в исходном тексте без компиляции
Если вы используете версию GNAT 3.10p, то для проверки ошибок в исходном тексте программы, без выполнения фактической компиляции исходных текстов, можно использовать утилиту gnatf.
Для более новых версий GNAT, начиная с версии 3.11p, для проверки ошибок в исходном тексте программы без компиляции, можно использовать команду gcc с опцией -gnatc.
Проверка ошибок во время выполнения программы
Ада предоставляет обширные возможности для проверки ошибок во время выполнения программы.
По умолчанию, GNAT отключает некоторые проверки для повышения скорости выполнения программы.
Для включения всех проверок, необходимо использовать опции -gnato -gnatE.
Для того чтобы отключить все проверки, необходимо использовать опцию -gnatp.
Проверка примера элементарной программы
Для исследования показанного примера элементарной программы, который демонстрирует использование встроенного ассемблера, можно воспользоваться способностью GNAT генерировать ассемблерный листинг программы.
Это позволяет убедиться в том, что мы реально получаем то, что и ожидаем получить.
При последующем рассмотрении более сложных примеров использования встроенного ассемблера и Ады, а также при самостоятельном использовании встроенного ассемблера такая способность GNAT будет оценена по достоинству.
Следует также заметить, что анализ генерируемого ассемблерного листинга может оказаться более еффективным способом отладки программы чем компиляция и запуск отладчика.
Для того, чтобы получить ассемблерный листинг программы необходимо выполнить следующую команду:
gcc -c -S -fomit-frame-pointer -gnatp 'nothing.adb' |
Поясним значение используемых в этой команде опций:
-c | - | выполнить только компиляцию исходного текста |
-S | - | сгенерировать листинг ассемблера |
-fomit-frame-pointer | - | не устанавливать отдельные кадры стека |
-gnatp | - | не добавлять код проверки времени выполнения |
Выполнение команды предоставляет хорошо читаемую версию ассемблерного кода.
Результирующий файл будет иметь такое же базовое имя, а расширение имени файла будет .s.
В случае файла с исходным текстом nothing.adb, результирующий файл с ассемблерным листингом будет nothing.s.
Содержимое этого файла будет иметь следующий вид:
.file "nothing.adb" gcc2_compiled.: ___gnu_compiled_ada: .text .align 4 .globl __ada_nothing __ada_nothing: #APP nop #NO_APP jmp L1 .align 2,0x90 L1: ret |
Примечательно, что ассемблерный код, который непосредственно был введен в исходный текст примера программы, помещен между метками #APP и #NO_APP.
Расширение проекта
Иногда, при разработке большой программной системы, необходимо использование модифицированных версий некоторых исходных файлов без изменения оригинальных версий исходных файлов.
Такое поведение может быть достигнуто с помощью возможности расширения проекта:
project Modified_Utilities extends "/baseline/utilities.gpr" is ... |
Файл проекта для расширяемого проекта (проект-предок) указывается строковым литералом, указываемым вслед за зарезервированным словом extends, которое следует за именем расширяющего проекта (проект-потомок).
По умолчанию, проект-потомок наследует все исходные файлы проекта-предка. Однако, унаследованные исходные файлы могут быть переопределены: какой-либо модуль с таким же именем как модуль проекта-предка будет "скрывать" оригинальный модуль проекта-предка. Наследуемые исходные файлы рассматриваются как исходные файлы (но не как непосредственные исходные файлы) проекта-потомка.
Примечательно, что какой-либо унаследованный исходный файл сохраняет любые опции, которые указаны в проекте-предке.
Например, если проект Utilities содержит спецификацию и тело Ада-пакета Util_IO, то проект Modified_Utilities может содержать новое тело для пакета Util_IO.
Оригинальное тело Util_IO не будет рассматриваться при сборке программы. Однако, при этом будет использоваться спецификация этого пакета, которая расположена в проекте Utilities.
Следует заметить, что проект-потомок может иметь только одного предка, но он может импортировать любое число других проектов.
Кроме того, для какого-либо проекта не допускается, чтобы он одновременно импортировал (прямо или косвенно) проект-потомок и любой из его предков.
Расширение существующего проекта
Общим случаем для больших программных систем является наличие множества реализаций общего интерфейса. В терминах Ады, это представляется как множество версий тела пакета для одной и той же спецификации этого пакета.
Например, одна реализация может быть безопасна при использовании в многозадачных/многопоточных программах, а другая может быть более эффективно использована в последовательных/однопоточных приложениях
В среде GNAT это может быть смоделировано с помощью использования концепции расширения проекта (project extension).
Если один проект ("проект-потомок") расширяет другой проект ("проект-предок"), то по умолчанию, все исходные файлы проекта-предка наследуются проектом-потомком, но проект-потомок может заменить версию любого исходного файла проекта-предка новой версией, а также способен добавить новые файлы.
Для проектов, такая способность аналогична использованию расширения в объектно-ориентированном программировании.
В среде GNAT допускаются иерархии проектов: проект-потомок может служить проектом-предком для другого проекта, и проект, который наследует один проект, может также импортировать другие проекты.
В качестве примера предположим, что каталог "/seq" содержит файл проекта "seq_proj.gpr" и исходные файлы "pack.ads", "pack.adb" и "proc.adb":
/seq pack.ads pack.adb proc.adb seq_proj.gpr |
Следует заметить, что файл проекта может быть просто пуст (то есть, он не содержит каких-либо описаний атрибутов и/или пакетов):
project Seq_Proj is
end Seq_Proj; |
допуская, что все его исходные файлы являются исходными файлами Ады и все они расположены в каталоге проекта.
Предположим, что необходимо предоставить какую-либо альтернативную версию файла "pack.adb" в каталоге "/tasking", но, при этом, сохранить использование существующих версий файлов "pack.ads" и "proc.adb".
С этой целью можно описать проект Tasking_Proj, который наследует проект Seq_Proj.
Схематически это будет иметь следующий вид:
/tasking pack.adb tasking_proj.gpr |
project Tasking_Proj extends "/seq/seq_proj" is end Tasking_Proj; |
Следует заметить, что для решения рассматриваемой задачи можно вместо наследования проекта использовать импорт проекта.
Базовый проект base будет содержать исходные файлы "pack.ads" и "proc.adb", последовательный/однопоточный проект будет импортировать проект base и добавлять "pack.adb", а многозадачный/многопоточный проект будет, подобным образом, импортировать проект base и добавлять свою версию "pack.adb",
Фактический выбор решения зависит от необходимости замены других исходных файлов оригинального проекта. При отсутствии такой необходимости достаточно использовать импорт проекта. В противном случае, необходимо использовать расширение проекта.
Расширенный пример
Предположим, что у нас есть две программы prog1 и prog2, исходные файлы которых расположены в соответствующих каталогах.
Предположим также, что нам необходимо осуществлять сборку этих программ с помощью одной команды запуска утилиты gnatmake, и мы хотим сохранять объектные файлы этих программ в подкаталогах ".build" соответствующих каталогов с исходными файлами.
Кроме того, нам необходимо иметь внутри каждого подкаталога ".build" два отдельных подкаталога "release" и "debug", которые будут содержать объектные файлы скомпилированные с различными наборами опций компиляции.
Более наглядно, мы имеем следующую структуру каталогов:
main |- prog1 | |- .build | | debug | | release |- prog2 |- .build | debug | release |
Рассмотрим файлы проектов, которые нам необходимо создать в каталоге "main", для сопровождения этой структуры:
Создадим проект Common с пакетом Compiler, который указывает опции компиляции:
File "common.gpr":
project Common is for Source_Dirs use (); -- нет исходных файлов type Build_Type is ("release", "debug"); Build : Build_Type := External ("BUILD", "debug"); package Compiler is case Build is when "release" => for Default_Switches ("Ada") use ("-O2"); when "debug" => for Default_Switches ("Ada") use ("-g"); end case; end Compiler; end Common; |
Создадим отдельные проекты для двух программ:
File "prog1.gpr":
with "common"; project Prog1 is for Source_Dirs use ("prog1"); for Object_Dir use "prog1/.build/" & Common.Build; package Compiler renames Common.Compiler; end Prog1; |
File "prog2.gpr":
with "common"; project Prog2 is for Source_Dirs use ("prog2"); for Object_Dir use "prog2/.build/" & Common.Build; package Compiler renames Common.Compiler; end Prog2; |
Создадим проект-обертку Main:
File "main.gpr": with "common"; with "prog1"; with "prog2"; project Main is package Compiler renames Common.Compiler; end Main; |
Теперь, с помощью команды
gnatmake -Pmain dummy |
gnatmake -Pmain -XBUILD=release |
Различия в использовании внешнего и встроенного ассемблера
Справедливо заметить, что всегда существует возможность скомпоновать программу на языке Ада с внешней подпрограммой на ассемблере.
Однако, для небольших фрагментов ассемблерного кода, использование встроенного ассемблера обладает несколькими отличительными преимуществами:
нет необходимости в использовании дополнительных не Ада-средств автоматическое использование соответствующих соглашений по вызову подпрограмм более простой доступ к описанным в Аде константам и переменным возможность написания внутренних (intrinsic) подпрограмм возможность использования кода встроенного ассемблера для встроенной подстановки (inline) оптимизатор кода может учитывать использование кода встроенного ассемблера
Таким образом, в случаях когда нет необходимости писать большое количество ассемблерного кода, удобнее использовать средства встроенного ассемблера.
Различные опции сборки и каталоги выходных результатов для общих исходных файлов
Предположим, что файлами с исходными текстами на Аде являются следующие файлы: "pack.ads", "pack.adb" и "proc.adb". Предположим также, что все они располагаются в каталоге "/common", и файл "proc.adb" содержит главную подпрограмму Proc, которая указывает в спецификаторе with пакет Pack.
Необходимо осуществлять компиляцию этих исходных файлов используя два набора опций:
При отладке, необходимо утилите gnatmake передать опцию "-g", а компилятору опции "-gnata", "-gnato" и "-gnatE"; при этом вывод результатов компиляции должен осуществляться в каталог "/common/debug".
При подготовке версии реализации, компилятору необходимо передать опцию "-O2", а вывод результата компиляции должен быть осуществлен в каталог "/common/release".
Показанные ниже файлы проектов GNAT, которые позволяют решить поставленную задачу, имеют соответствующие имена "debug.gpr" и "release.gpr", и располагаются в каталоге "/common".
Для наглядности, представим рассматриваемый пример схематически:
/common debug.gpr release.gpr pack.ads pack.adb proc.adb /common/debug {-g, -gnata, -gnato, -gnatE} proc.ali, proc.o pack.ali, pack.o /common/release {-O2} proc.ali, proc.o pack.ali, pack.o |
Файла проекта "debug.gpr" имеет следующий вид:
project Debug is
for Object_Dir use "debug"; for Main use ("proc"); package Builder is for Default_Switches ("Ada") use ("-g"); end Builder; package Compiler is for Default_Switches ("Ada") use ("-fstack-check", "-gnata", "-gnato", "-gnatE"); end Compiler; end Debug; |
Файла проекта "release.gpr" имеет следующий вид:
project Release is
for Object_Dir use "release"; for Exec_Dir use "."; for Main use ("proc"); package Compiler is for Default_Switches ("Ada") use ("-O2"); end Compiler; end Release; |
Именем проекта, который описан в файле "debug.gpr", является "Debug" (регистр символов значения не имеет). Аналогично, проект, который описан в файле "release.gpr", имеет имя "Release". Для согласованности, файл проекта должен иметь такое же имя как и проект, а расширением имени файла проекта должно быть расширение ".gpr". Такое соглашение не является жестким требованием, однако, при его несоблюдении будет выдаваться предупреждающее сообщение.
Предположим, что текущим каталогом является каталог "/temp". Тогда, согласно установок в файле проекта "debug.gpr", команда
gnatmake -P/common/debug.gpr |
Подобным образом, согласно установок в файле проекта "release.gpr", команда
gnatmake -P/common/release.gpr |
В случаях, когда файл проекта явно не указывает набор каталогов, в которых храняться файлы с исходными текстами, или непосредственный набор исходных файлов, по умолчанию предполагается, что исходными файлами проекта являются файлы с исходными текстами Ады, которые расположены в том же каталоге, в котором находится файл проекта.
Таким образом, файлы "pack.ads", "pack.adb" и "proc.adb" являются исходными файлами для обоих проектов.
Различные свойства проекта выражаются в виде атрибутов в стиле языка Ада.
Подобным свойством проекта является каталог для сохранения объектных файлов (и файлов ALI), которому соответствует атрибут Object_Dir.
Значением атрибута Object_Dir может быть строка или строковое выражение.
Каталог для сохранения объектных файлов может быть указан как абсолютный или как относительный путь к каталогу.
В последнем случае, указывается относительный путь к каталогу в котором содержится файл проекта.
Таким образом, в показанных выше примерах, вывод компилятора направляется в каталог "/common/debug" (для проекта Debug), и в каталог "/common/release" (для проекта Release).
В случае, когда значение Object_Dir не указано, значением по умолчанию является каталог в котором содержится файл проекта.
Другим свойством проекта является каталог для сохранения исполняемых файлов (файлов с исполняемыми модулями), которому соответствует атрибут Exec_Dir.
Значением атрибута Exec_Dir также может быть строка или строковое выражение, которые указывают абсолютный или относительный путь к каталогу.
Когда значение Exec_Dir не указано, значением по умолчанию является каталог указанный для Object_Dir
(который, в свою очередь, может быть каталогом в котором содержится файл проекта, при отсутствии указания значения для Object_Dir).
Таким образом, в показанных выше примерах, исполняемый файл будет помещен в каталог "/common/debug" для проекта Debug
(атрибут Exec_Dir не указан), и в каталог "/common" для проекта Release.
Инструментальные средства системы компилятора GNAT, которые интегрированы с менеджером проектов GNAT, моделируются внутри файла проекта как соответствующие пакеты.
В показанных ранее примерах, проект Debug описывает пакеты Builder (соответствует команде gnatmake) и Compiler (соответствует компилятору, команда gcc или gnatgcc), а проект Release описывает только пакет Compiler.
Используемый в файлах проектов синтаксис Ада-пакетов не следует рассматривать буквально.
Хотя пакеты файлов проектов обладают внешним сходством с пакетами исходного текста Ады, их нотация является только способом передачи группы свойств именованной сущности.
Кроме того, следует заметить, что допустимые к использованию в файлах проектов имена пакетов ограничены предопределенным перечнем имен, находящихся в строгом соответствии с существующими инструментальными средствами системы компилятора GNAT, которые интегрированы с менеджером проектов GNAT, а содержимое таких пакетов ограничено небольшим набором конструкций (в показанных выше примерах пакеты содержат описания атрибутов).
Указание опций для инструментальных средств системы компилятора GNAT, которые интегрированы с менеджером проектов, может быть осуществлено с помощью установки значений соответствующих атрибутов в пакетах файлов проектов, которые соответствуют инструментальным средствам.
В показанные выше примеры демонстрируется использование атрибута Default_Switches, который описан в пакетах обоих файлов проектов и является одним из таких атрибутов.
В отличие от простых атрибутов, таких как Source_Dirs, атрибут Default_Switches
является ассоциативным массивом.
При описании такого атрибута необходимо обеспечить какой-либо "индекс" (литеральная строка), а результатом описания атрибута является установка значения "массива" для указанного "индекса".
Для атрибута Default_Switches, индексом является язык программирования (в данном случае - Ada), а указанное (после use) значение должно быть списком строковых выражений.
В файлах проектов допускается использование предопределенного набора атрибутов. При этом, одни атрибуты могут быть указаны на уровне проекта, а другие - на уровне пакета проекта.
Для любого атрибута, который является ассоциативным массивом, индекс должен быть представлен как литеральная строка, причем, налагаемые на эту строку ограничения (например, имя файла или имя языка программирования) зависят от индивидуального атрибута.
Кроме того, в зависимости от атрибута, указываемое значение атрибута должно быть строкой или списком строк.
В показанном ранее проекте Debug, осуществлялась установка опций для двух инструментальных средств: команды gnatmake и компилятора.
Таким образом, в файл проекта были включены соответствующие пакеты, и каждый пакет описывал значение атрибута Default_Switches
для индекса "Ada".
Следует заметить, что пакет соответствующий команде gnatmake именуется Builder.
Проект Release подобен проекту Debug, но он содержит только пакет Compiler.
Отметим, что в проекте Debug, все опции, которые начинаются с "-gnat" и указаны в пакете Compiler, могут быть перемещены в пакет Builder, поскольку команда gnatmake передает все подобные опции компилятору.
Одним из свойств проекта является список головных подпрограмм (фактически, список имен файлов с исходными текстами содержащими головные подпрограммы, причем, указание расширений имен файлов - не обязательно).
Это свойство указывается атрибутом Main, значением которого является список строк.
Когда проект описывает атрибут Main, при запуске команды gnatmake, отсутствует необходимость в указании головной подпрограммы (или головных подпрограмм).
Когда файл проекта не определяет использование какой-либо схемы именования файлов, используется стандартная для GNAT схема именования файлов принимаемая по умолчанию.
Механизм, который используется в файлах проектов для определения соглашений именования файлов с исходными текстами, обеспечивается пакетом Naming
и будет подробно рассмотрен несколько позже.
Когда файл проекта не определяет значение атрибута Languages
(указывает используемые в проекте языки программирования), инструментальные средства GNAT по умолчанию подразумевают, что языком программирования является Ada.
В общем случае предполагается, что проект может состоять из файлов с исходными текстами, которые написаны на языках программирования: Ada, C и/или других языках программирования.
Сборка первой программы
Используя любой текстовый редактор вашей системы создайте файл hello.adb, который содержит следующую программу на языке Ада:
with Ada.Text_IO; use Ada.Text_IO; procedure Hello is
begin Put_Line( "Hello World!" ); end Hello; |
Эта программа будет печатать на экране простое сообщение приветствия.
После сохранения файла, для построения выполняемого файла программы необходимо выполнить команду gnatmake:
gnatmake hello.adb |
Если при написании файла с исходным текстом были допущены ошибки, gnatmake покажет вам номер строки, номер символа в строке и сообщение об ошибке, описывающее суть возникшей проблемы.
Если текст программы был написан без ошибок, то вы получите исполняемый файл hello.exe, если вы используете Windows, или hello, если вы используете Linux.
Запустить полученную программу на выполнение можно командой:
hello |
или, в некоторых дистрибутивах Linux:
./hello |
В результате, на экране дисплея появится сообщение:
Hello World! |
После построения этого проекта, каталог будет содержать следующие файлы:
hello.adb | - | файл с исходным текстом программы на языке Ада |
hello.o | - | двоичный файл созданный компилятором, который содержит объектный код |
hello.ali | - | дополнительный информационный файл созданный компилятором (Ada Library Information file) |
hello.exe или hello | - | исполнимый файл программы |
Мы можем спокойно удалить файлы hello.o и hello.ali, которые в данном случае являются дополнительными информационными файлами.
Однако, при разработке больших проектов, содержащих большое количество файлов, сохранение подобных файлов в последствии может ускорить процесс построения проекта.
Схемы именования файлов
Бывают случаи когда необходимо осуществить перенос программной системы, которая была разработана в среде какого-либо Ада-компилятора, в среду компилятора GNAT.
При этом, имена файлов, которые использовались в среде другого Ада-компилятора, могут не соответствовать соглашениям по именованию файлов, которые стандартны и используются по умолчанию в среде GNAT.
В такой ситуации, вместо переименования всех файлов, - что может быть практически неосуществимо по целому ряду причин, - в файле проекта, внутри пакета Naming, можно описать необходимую схему именования файлов.
Например, показанное ниже описание пакета Naming
моделирует соглашения по наименованию, которые традиционны для системы компилятора Apex:
package Naming is
for Casing use "lowercase"; for Dot_Replacement use "."; for Specification_Suffix ("Ada") use ".1.ada"; for Implementation_Suffix ("Ada") use ".2.ada"; end Naming; |
Внутри пакета Naming могут быть описаны следующие атрибуты:
Casing | Этот атрибут может принимать одно из трех значений: "lowercase", "uppercase" или "mixedcase" (все значения не зависят от используемого регистра символов). Когда значение этого атрибута не указано, по умолчанию, предполагается значение "lowercase". | ||
Dot_Replacement | Значением этого атрибута может быть строка, которая удовлетворяет следующие условия:
строка не должна быть пустой строка не может начинаться или заканчиваться буквой или цифрой строка не может состоять из одиночного символа подчеркивания строка не может начинаться одиночным символом подчеркивания сопровождаемым одиночной буквой или цифрой строка не может содержать символ точки ('.'), за исключением случая когда вся эта строка состоит из одного символа точки (".") Когда значение этого атрибута не указано, по умолчанию, предполагается строка "-". |
||
Specification_Suffix |
Атрибут является ассоциативным массивом ( индексируется именем языка программирования; не зависит от регистра символов), значением которого может быть строка, удовлетворяющая следующие условия: строка не должна быть пустой строка не может начинаться с символа буквы или цифры строка не может начинаться одиночным символом подчеркивания сопровождаемым одиночной буквой или цифрой Когда значение атрибута "Specification_Suffix ("Ada")" не указано, по умолчанию, предполагается строка ".ads". |
||
Implementation_Suffix |
Атрибут является ассоциативным массивом (индексируется именем языка программирования; не зависит от регистра символов), значением которого может быть строка, удовлетворяющая следующие условия: строка не должна быть пустой строка не может начинаться с символа буквы или цифры строка не может начинаться одиночным символом подчеркивания сопровождаемым одиночной буквой или цифрой строка не может иметь такое же значение как Specification_Suffix Когда значение атрибута "Implementation_Suffix ("Ada")" не указано, по умолчанию, предполагается строка ".adb". |
||
Separate_Suffix |
Значение этого атрибута должно удовлетворять те же условия, что и значение для атрибута Implementation_Suffix. Когда значение атрибута "Separate_Suffix ("Ada")" не указано, по умолчанию, предполагается значение атрибута "Implementation_Suffix ("Ada")". |
||
Specification |
Этот атрибут является ассоциативным массивом и может быть использован для описания имен исходных файлов содержащих индивидуальные спецификации компилируемых модулей Ады. Индекс массива должен быть строковым литералом, который указывает модуль Ады (не зависит от регистра символов). Значением атрибута должна быть строка, которая указывает файл содержащий спецификацию компилируемого модуля (зависимость от регистра символов определяется используемой операционной системой).
|
||
Implementation |
Этот атрибут является ассоциативным массивом и может быть использован для описания имен исходных файлов содержащих индивидуальные тела (возможно субмодули) компилируемых модулей Ады. Индекс массива должен быть строковым литералом, который указывает модуль Ады (не зависит от регистра символов). Значением атрибута должна быть строка, которая указывает файл содержащий тело компилируемого модуля (зависимость от регистра символов определяется используемой операционной системой).
|
Шестнадцатеричные значения
При использовании соглашений Intel, для загрузки в регистр шестнадцатеричного значения можно было написать:
mov eax, 1EAh |
Увы, в этом случае синтаксис AT&T также отличается!
Для загрузки в регистр шестнадцатеричного значения необходимо использовать соглашения языка C, то есть, шестнадцатеричное значение должно иметь префикс 0x, причем, даже при использовании встроенного ассемблера Ады.
Таким образом, показанный выше пример должен быть переписан следующим образом:
mov $0x1EA, %eax |
где сама константа не зависит от используемого регистра символов.
Синтаксис файлов проектов
Рассмотрим структуру и синтаксис файлов проектов.
Проект может быть независимым проектом, который полностью описывается в одном единственным файле проекта.
В независимом проекте, любой исходный файл Ады может зависить только от предопределенной библиотеки и/или от других исходных файлов Ады этого же проекта.
Проект может также зависеть от других проектов в одном или обоих следующих случаях:
проект может импортировать любое количество других проектов проект может расширять не более одного другого проекта
Отношения зависимости проектов могут быть представлены как ориентированный граф без петель (подграф отображает в дереве отношение "расширения").
Непосредственными исходными файлами проекта являются исходные файлы, которые прямо определяются проектом. При этом, исходные файлы проекта могут определяться неявно, когда они располагаются в том же каталоге в котором расположен файл проекта, или исходные файлы проекта могут определяться явно, с помощью любого рассматриваемого ниже, относящегося к исходным файлам атрибута.
В общем смысле, исходные файлы проекта proj
являются непосредственными исходными файлами проекта proj
одновременно со всеми непосредственными исходными файлами проектов от которых проект proj прямо или косвенно зависит (кроме тех исходных файлов, которые заменяются при расширении, в случае наследования проекта).
Синтаксис GNAT
В заключение обсуждения использования средств встроенного ассемблера представим полный синтаксис инструкции встроенного ассемблера GNAT, который непосредственно позаимствован из документации GNAT:
ASM_CALL ::= Asm ( [Template =>] static_string_EXPRESSION [,[Outputs =>] OUTPUT_OPERAND_LIST ] [,[Inputs =>] INPUT_OPERAND_LIST ] [,[Clobber =>] static_string_EXPRESSION ] [,[Volatile =>] static_boolean_EXPRESSION] )
OUTPUT_OPERAND_LIST ::= No_Output_Operands | OUTPUT_OPERAND_ATTRIBUTE | (OUTPUT_OPERAND_ATTRIBUTE {,OUTPUT_OPERAND_ATTRIBUTE}) OUTPUT_OPERAND_ATTRIBUTE ::= SUBTYPE_MARK'Asm_Output (static_string_EXPRESSION, NAME) INPUT_OPERAND_LIST ::= No_Input_Operands | INPUT_OPERAND_ATTRIBUTE | (INPUT_OPERAND_ATTRIBUTE {,INPUT_OPERAND_ATTRIBUTE}) INPUT_OPERAND_ATTRIBUTE ::= SUBTYPE_MARK'Asm_Input (static_string_EXPRESSION, EXPRESSION) |
Copyright (C) А.Гавва | V-0.4w май 2004 |
Система Ада-компилятора GNAT
Система Ада-компилятора GNAT (сокращение от GNU New York University Ada Translator) одновременно является компилятором и системой времени выполнения для Ada95, которая использует многоплатформенный кодогенератор GCC, благодаря которому обеспечивается поддержка большого количества аппаратных платформ.
Таким образом, GNAT является частью программного обеспечения GNU.
Система Ада-компилятора GNAT была разработана в процессе тесного сотрудничества двух коллективов разработчиков:
GNAT Development Team - из Нью-Йоркского университета, под руководством профессоров Edmond Schonberg и Robert B. K. Dewar. Эта группа разрабатывала препроцессор и компилятор.
Project PART (POSIX Ada Real-Time) Team - из университета штата Флорида, под руководством профессора Theodore P. Baker. Эта группа разрабатывала библиотеку времени выполнения Ады.
Изначально этот проект финансировался правительством USA (с 1991 по 1994).
В августе 1994 года основные авторы проекта создали компанию Ada Core Technologies Inc. (ACT), которая предоставляет техническую поддержку по использованию системы GNAT в промышленных и коммерческих разработках.
В настоящее время, Ada Core Technologies продолжает расширять количество платформ на которых можно использовать Ада-компилятор GNAT, а также предусматривает различные средства для разработки и отладки Ада-программ.
Компания предусматривает свободное обновления компилятора для сообщества пользователей языка программирования Ада.
Copyright (C) А.Гавва | V-0.4w май 2004 |
Соглашения по наименованию исходных файлов GNAT
При анализе внутреннего устройства системы GNAT, может оказаться полезным следующее краткое описание соглашений по именованию исходных файлов системы:
Файлы, которые начинаются префиксом "sc", содержат лексический сканер. Все файлы, которые начинаются префиксом "par", являются компонентами синтаксического анализатора (parser).
Число в имени соответствует главе в "Руководстве по языку программирования Ада 95" ("Ada 95 Reference Manual").
Например, синтаксический разбор инструкции select
может быть обнаружен в файле "par-ch9.adb".
Все файлы, которые начинаются префиксом "sem", осуществляют семантический анализ.
Число в имени соответствует главе в "Руководстве по языку программирования Ада 95" ("Ada 95 Reference Manual").
Например, все случаи использования спецификаторов контекста могут быть обнаружены в файле "sem_ch10.adb".
Дополнительно, некоторые свойства языка требуют значительной специальной обработки. Для поддержки этого служат свои собственные семантические файлы: "sem_aggr" - для агрегатов, "sem_disp" - для динамической диспетчеризации, и т.д.
Все файлы, которые начинаются префиксом "exp", осуществляют нормализацию и расширение внутреннего представления (абстрактного синтаксического дерева - abstract syntax tree или AST).
Эти файлы используют такую же схему нумерации, которую используют файлы синтаксического и семантического анализа.
Например, конструкция процедур инициализации записи выполнена в файле "exp_ch3.adb".
Все файлы, которые начинаются префиксом "bind", реализуют редактор связей (binder), который осуществляет проверку согласованности компиляции, определяет порядок элаборации и генерирует файл редактора связей (bind file).
Файлы "atree.ads" и "atree.adb" описывают низкоуровневые структуры данных, которые используются препроцессором (front-end).
Файлы "sinfo.ads" и "sinfo.adb" описывают подробности структуры абстрактного синтаксического дерева, которое генерируется синтаксическим анализатором (parser).
Файлы "einfo.ads" и "einfo.adb" описывают подробности атрибутов всех сущностей, которые вычисляются в процессе семантического анализа.
Управление библиотекой осуществляется в файлах, которые начинаются префиксом "lib".
Ада-файлы, которые начинаются префиксом "a-", являются дочерними модулями пакета Ada, как это определено в Дополнении A (Annex A).
Все файлы, которые начинаются префиксом "i-", являются дочерними модулями пакета Interfaces, как это определено в Дополнении B (Annex B).
Все файлы, которые начинаются префиксом "s-", являются дочерними модулями пакета System. Они включают дочерние модули, которые определены в стандарте языка, и подпрограммы библиотеки времени выполнения GNAT.
Все файлы, которые начинаются префиксом "g-", являются дочерними модулями пакета GNAT. Здесь присутствует множество пакетов общего назначения, которые полностью документированы в своих файлах спецификаций.
Все остальные файлы ".c" являются модификацией общих файлов gnatgcc.
Создание Ада-библиотеки
В среде системы компилятора GNAT любая библиотека состоиз из двух компонентов:
Файлы с исходными текстами.
Скомпилированный код и ALI-файлы (Ada Library Information files)
Согласно требований системы компилятора GNAT, при использовании других пакетов необходимо, чтобы некоторые исходные тексты были доступны компилятору.
Таким образом, минимально необходимым множеством файлов с исходными текстами являются файлы с исходными текстами спецификаций всех пакетов, которые формируют видимую часть библиотеки, а также файлы с исходными текстами от которых зависят файлы спецификаций пакетов библиотеки.
Кроме того, должны быть доступны исходные тексты тел всех видимых настраиваемых модулей.
Хотя это не является строго необходимым требованием, рекомендуется, чтобы пользователю были доступны все исходные тексты, которые необходимы для перекомпиляции библиотеки.
В результате этого пользователю предоставляются исчерпывающие возможности в использовании межмодульных встроенных вставок (inline) и в осуществлении полноценной отладки на уровне исходных текстов.
Это также может облегчить ситуацию для тех пользователей, которые нуждаются в обновлении инструментальных средств при котором может возникнуть необходимость перекомпиляции библиотек из исходных текстов.
Обеспечение скомпилированного кода библиотеки может предусматриваться различными способами.
Самый простой способ предусматривает непосредственное множество объектных файлов сгенерированных в процессе компиляции библиотеки.
Кроме того, возможно объединение всех объектных файлов в едином библиотечном архиве с помощью какой-либо команды, которая предусматривается в используемой операционной системе.
И, наконец, возможно создание динамически загружаемой библиотеки (см. использование опции -shared в справочном руководстве по GCC).
Существует множество способов компиляции модулей, совокупность которых формирует библиотеку.
Например, это можно выполнить с помощью написания файла управления сборкой проекта Makefile
a
и последующего использования утилиты GNU make, или путем создания скрипта для командного интерпретатора.
В случае построения простой библиотеки, можно создать фиктивную головную программу, которая зависит от всех интерфейсных пакетов библиотеки.
В последствии, такая фиктивная головная программа может быть представлена утилите gnatmake, в результате чего gnatmake выполнит построение всех объектных файлов библиотеки.
Ниже показан пример подобной фиктивной головной программы и общий вид команд, которые могут быть использованы для построения единого библиотечного архива объектных файлов или динамически загружаемой библиотеки.
with My_Lib.Service1; with My_Lib.Service2; with My_Lib.Service3; procedure My_Lib_Dummy is begin null; end; # компиляция библиотеки $ gnatmake -c my_lib_dummy.adb # нам не нужен объектный код фиктивной головной программы $ rm my_lib_dummy.o my_lib_dummy.ali # создание единого библиотечного архива $ ar rc libmy_lib.a *.o # some systems may require "ranlib" to be run as well # или создание динамически загружаемой библиотеки $ gnatgcc -shared -o libmy_lib.so *.o # некоторые системы могут требовать, чтобы компиляция выполнялась с опцией -fPIC |
в одном из файлов с исходными текстами:
pragma Linker_Options ("-lmy_lib"); |
Средства GNATDebug_Pools
Использование Unchecked_Deallocation и/или Unchecked_Conversion
может легко привести к некорректным ссылкам памяти.
От проблем, порождаемых подобными ссылками, достаточно сложно избавиться поскольку проявляемые симптомы, как правило, далеки от реального источника проблемы.
В подобных случаях, очень важно обнаружить наличие подобной проблемы как можно раньше.
Именно такая задача возлагается на Storage_Pool предусмотренный в GNAT.Debug_Pools.
Чтобы воспользоваться средствами отладочного пула динамической памяти GNAT, пользователь должен ассоциировать объект отладочного пула с каждым ссылочным типом, который потенциально способен быть источником проблем.
type Ptr is access Some_Type; Pool : GNAT.Debug_Pools.Debug_Pool; for Ptr'Storage_Pool use Pool; |
GNAT.Debug_Pool является производным от Checked_Pool, который является характерным для GNAT пулом динамической памяти.
Такие пулы динамической памяти, подобно стандартным пулам динамической памяти Ады, позволяют пользователю переопределять стратегию распределения (аллокацию) и освобождения (деаллокацию) динамической памяти.
Они также предусматривают контрольные точки, для каждого разыменования (расшифровки) ссылок посредством операции Dereference, которая неявно вызывается в процессе разыменования каждого значения ссылочного типа.
После того как ссылочный тип ассоциирован с отладочным пулом, операции над значениями этого типа способны возбудить четыре различных исключения, которые соответствуют четырем потенциальным видам разрушения памяти:
GNAT.Debug_Pools.Accessing_Not_Allocated_Storage
GNAT.Debug_Pools.Accessing_Deallocated_Storage
GNAT.Debug_Pools.Freeing_Not_Allocated_Storage
GNAT.Debug_Pools.Freeing_Deallocated_Storage
Для типов, ассоциированных с отладочным пулом Debug_Pool
динамическое распределение выполняется с помощью использования стандартных подпрограмм размещения динамической памяти GNAT.
Ссылки ко всем фрагментам распределенной памяти сохраняются во внутреннем каталоге.
Стратегия освобождения состоит не в освобождении памяти используемой системы, а в заполнении памяти шаблоном, который может быть легко распознан в процессе отладочной сессии.
Шаблон соответствует старому двоично десятичному соглашению IBM и имеет значение 16#DEADBEEF#.
При каждом разыменовании, осуществляется проверка того, что ссылочное значение указывает на правильно распределенное место памяти.
Ниже показан полный пример использования отладочного пула Debug_Pool, который содержит типичный образец разрушения памяти:
@leftskip=0cm with Gnat.Io; use Gnat.Io; with Unchecked_Deallocation; with Unchecked_Conversion; with GNAT.Debug_Pools; with System.Storage_Elements; with Ada.Exceptions; use Ada.Exceptions; procedure Debug_Pool_Test is type T is access Integer; type U is access all T; P : GNAT.Debug_Pools.Debug_Pool; for T'Storage_Pool use P; procedure Free is new Unchecked_Deallocation (Integer, T); function UC is new Unchecked_Conversion (U, T); A, B : aliased T; procedure Info is new GNAT.Debug_Pools.Print_Info(Put_Line); begin Info (P); A := new Integer; B := new Integer; B := A; Info (P); Free (A); begin Put_Line (Integer'Image(B.all)); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; begin Free (B); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; B := UC(A'Access); begin Put_Line (Integer'Image(B.all)); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; begin Free (B); exception when E : others => Put_Line ("raised: " & Exception_Name (E)); end; Info (P); end Debug_Pool_Test; |
Debug Pool info: Total allocated bytes : 0 Total deallocated bytes : 0 Current Water Mark: 0 High Water Mark: 0 Debug Pool info: Total allocated bytes : 8 Total deallocated bytes : 0 Current Water Mark: 8 High Water Mark: 8 raised: GNAT.DEBUG_POOLS.ACCESSING_DEALLOCATED_STORAGE raised: GNAT.DEBUG_POOLS.FREEING_DEALLOCATED_STORAGE raised: GNAT.DEBUG_POOLS.ACCESSING_NOT_ALLOCATED_STORAGE raised: GNAT.DEBUG_POOLS.FREEING_NOT_ALLOCATED_STORAGE Debug Pool info: Total allocated bytes : 8 Total deallocated bytes : 4 Current Water Mark: 4 High Water Mark: 8 |
Средства оптимизации GNAT, используемые в исходном тексте
Существует несколько директив компилятора, которые позволяют изменять размер и скорость выполнения программы:
pragma Pack( Aggregate ); | - | Использовать минимальный размер пространства для агрегата. | |
pragma Optimize( Space / Time / Off ); | - | Выбор типа оптимизации инструкций. | |
pragma Inline( Subprogram ); pragma Inline_Always( Subprogram ); | - | Указывают на необходимость выполнения встроенной подстановки (inline) подпрограммы Subprogram. | |
pragma Discard_Names( type ); | - | Не помещать ASCII-идентификаторы в результирующий исполняемый файл. |
Директива Pack позволяет упаковывать массивы, записи и тэговые записи, что позволяет им, в результате, занимать меньшее пространство.
Например, упакованный массив переменных логического типа Boolean
приводит к тому, что каждая переменная занимает всего один бит.
Директива Pack позволяет упаковывать только структуры данных.
Следует также учесть, что не каждый самостоятельный элемент структуры данных может быть упакован.
Например, если имеется массив записей, то для того чтобы при распределении пространства использовался минимально возможный размер, понадобиться выполнить как упаковку массива, так и упаковку записи.
Также следует учитывать, что упаковка структур данных, как правило, ухудшают скорость выполнения программы.
Примером использования этой директивы компилятора для упаковки записи может служить следующее:
type CustomerProfile is
record Preferred : Boolean; Preorders_Allowed : Boolean; Sales_To_Date : Float; end record; pragma Pack( CustomerProfile ); |
Следует заметить, что GNAT способен выполнять достаточно плотную упаковку, упаковывая отдельные компоненты структур вплоть до индивидуальных битов.
Директива компилятора Optimize позволяет указать компилятору требуемый тип оптимизации инструкций: для максимально возможной скорости выполнения инструкций (Time), для использования инструкциями минимально возможного размера (Space) или без выполнения какой-либо оптимизации вообще (Off).
Эта директива никак не воздействует на структуры данных.
pragma Optimize ( Space ); package body AccountsPayable is |
Это значит, что в местах вызова указанной подпрограммы вместо генерации вызова подпрограммы выполняется непосредственная вставка машинного кода подпрограммы, что позволяет несколько повысить скорость выполнения подпрограммы.
При этом следует учитывать, что использование этой директивы может привести к увеличению общего размера результирующего исполняемого файла, поэтому ее следует использовать для тех подпрограмм, которые имеют маленький размер.
procedure Increment( X : in out Integer ) is begin X := X + 1; end Increment; pragma Inline( Increment ); |
будет проигнорирована, если при компиляции программы не используется опция командной строки компилятора -O3.
Также следует учесть, что опция -O3
будет автоматически осуществлять встроенную вставку коротких подпрограмм.
Директива компилятора Inline_Always вызывает принудительную встроенную вставку подпрограмм, описанных в разных пакетах (подобно опции -gnatn) не зависимо от указания в командной строке компилятора опций -gnatn или -gnatN.
Директива компилятора Discard_Names позволяет освободить пространство занимаемое ASCII-строками имен идентификаторов.
Например, при наличии большого перечислимого типа, Ада обычно сохраняет строки имен для каждого идентификатора значения перечислимого типа.
Это выполняется для поддержки использования атрибута 'Img.
Если использование атрибута 'Img не планируется, то можно указать компилятору на необходимость очистки этих имен.
type Dog_Breed is (Unknown, Boxer, Shepherd, Mixed_Breed); pragma Discard_Names( Dog_Breed ); |
В этом случае, вместо возвращения строкового представления имени идентификатора, атрибут 'Img будет возвращать позицию значения в списке перечисления значений перечислимого типа (например, 0, 1, 2 и так далее).
Средства управления проектами в системе GNAT
Эта глава описывает средства управления проектами, которые присутствуют в системе компилятора GNAT (GNAT Project Manager).
Основой этих средств является Менеджер Проектов GNAT (GNAT Project Manager).
Следует заметить, что менеджер проектов появился в системе GNAT версии 3.15 и отсутствует в более ранних версиях.
Менеджер проектов позволяет конфигурировать различные свойства для групп файлов с исходными текстами.
В частности, он позволяет определять:
Каталог или набор каталогов, где располагаются файлы с исходными текстами, и/или имена определенных самостоятельных файлов с исходными текстами.
Каталог в котором будут сохраняться файлы являющиеся результатом работы компилятора (файлы ALI, объектные файлы, файлы деревьев).
Каталог в котором будут сохраняться исполняемые файлы программ (иначе - исполняемые программные модули).
Установки опций для любых средств обработки проектов (gnatmake, компилятор, редактор связей, компоновщик, gnatls, gnatxref, gnatfind); такие установки могут быть заданы глобально и/или указаны для индивидуальных программных модулей.
Исходные файл (или файлы), который содержит главную подпрограмму
Используемый исходным текстом язык программирования (в настоящий момент Ада и/или C/C++)
Соглашения по именованию файлов с исходными текстами; допускается глобальная установка и/или установка для индивидуальных модулей.
Строковые типы
Значение строковой переменной может быть ограничено списком строковых литералов. Ограниченный список строковых литералов представляется как описание строкового типа.
Примером описания строкового типа может служить следующее:
type OS is ("NT, "nt", "Unix", "Linux", "other OS"); |
Переменные строкового типа называют типированными переменными, а все остальные переменные называют нетипированными переменными. Типированные переменные удобно использовать в конструкциях case.
Любое описание строкового типа начинается с зарезервированного слова type, за которым следует имя строкового типа (не зависит от регистра символов), затем следует зарезервированное слово is, далее - помещенный в скобки список из одного или более строковых литералов, которые отделяются друг от друга запятыми, и, в завершение, следует символ точки с запятой.
Строковые литералы, которые помещаются в список, являются зависимыми от регистра символов и должны отличаться друг от друга. Они могут содержать в себе любые допустимые для языка Ада символы, включая пробелы.
Строковый тип может быть описан только на уровне файла проекта, и не может быть описан внутри пакета файла проекта.
Ссылку (обращение) к строковому типу можно осуществить с помощью имени строкового типа, когда он описан в том же самом файле проекта, или путем использования точечной нотации: имя проекта, символ точки, имя строкового типа.
Суффиксы размера
Ассемблер Intel пытается определить размер инструкции пересылки анализируя размеры операндов. Таким образом, код:
mov ax, 10 |
будет пересылать 16-битное слово в регистр ax.
Ассемблер as, как правило, отказывается "играть в догадки".
Вместо этого, при написании имени инструкции необходимо использовать суффикс явной символьной индикации размеров операндов:
movw $10, %ax |
где допустимыми символами являются b, w и l:
b - byte (8-бит) [movb $10, %al]
w - word (16-бит) [movw $10, %ax]
l - long (32-бит) [movl $10, %eax]
Можно встретить и другие случаи использования модификаторов размера, подобно тому как pushfd становиться pushfl.
Чаще всего использование модификаторов размера очевидно, а в случае ошибки - ассемблер обязательно подскажет.
Связывание Ада-программы
При использовании таких языков программирования как C и C++, как только выполнена компиляция файлов с исходными текстами остается только один шаг до построения результирующей исполняемой программы, которым является компоновка в единое целое полученных в результате компиляции объектных файлов.
Это подразумевает, что существует возможность компоновки (иначе - сборки) результирующей исполняемой программы из несогласованных версий объектных файлов, что означает попадание в результирующую программу модулей которые включают различные версии одного и тот же заголовочного файла.
Правила Ады не позволяют скомпоновать подобную программу.
Например, если два каких-либо клиентских модуля используют различные версии одного и того же пакета, то выполнить построение программы, которая одновременно включает в себя оба этих модуля, не возможно.
Такие правила принудительно навязываются редактором связей GNAT (gnatbind), который также осуществляет определение последовательности элаборации в соответствии с требованиями языка Ада.
Запуск редактора связей GNAT выполняется после того как созданы все объектные файлы, которые необходимы для компоновки результирующей программы.
При запуске, редактор связей gnatbind получает имя головного модуля программы и путем чтения соответствующих ALI-файлов определяет перечень требуемых для компоновки этой программы модулей.
При обнаружении попытки скомпоновать программу из несогласованных версий объектных файлов или при отсутствии допустимой последовательности элаборации редактор связей gnatbind выдает соответствующие сообщения об ошибках.
В случае отсутствия ошибок, редактор связей gnatbind осуществляет генерацию главного файла программы (по умолчанию, генерируется исходный текст на языке Ада), который содержит вызовы процедур элаборации для компилируемых модулей, которые нуждаются в элаборации, с последующим вызовом головной программы.
В результате компиляции этого файла с исходным текстом генерируется объектный файл главной программы.
Имя главного файла программы, который генерируется редактором связей gnatbind, имеет следующий вид: b~xxx.adb, а соответствующий ему файл спецификации - b~xxx.ads, - где xxx является именем головного модуля программы.
В заключение, для компоновки результирующей исполняемой программы, используется компоновщик gnatlink, которому предоставляется объектный файл главной программы, полученный на этапе связывания, а также все объектные файлы модулей Ады, которые необходимы для компоновки результирующей исполняемой программы.
Связывание и компоновка, утилита gnatbl
Бывают случаи, когда необходимо выполнить сборку проекта части которого написаны на других языках программирования.
В таких ситуациях, компиляция выполняется с помощью различных компиляторов, а этапы связывания и компоновки могут быть выполнены автоматически за один шаг.
Для этого можно использовать утилиту gnatbl, которая автоматически вызывает редактор связей gnatbind и компоновщик gnatlink, соответственно выполняя связывание и компоновку проекта.
Три этапа сборки проекта
Весь процесс построения проекта осуществляется в три этапа, которые утилита gnatmake выполняет автоматически:
Компиляция (compiling): утилита gnatmake
осуществляет проверку вашего файла на наличие в нем каких-либо ошибок.
Если ошибки не обнаружены, то выполняется создание объектного файла, содержащего двоичную версию программы.
При обнаружении ошибок, работа утилиты gnatmake завершается.
Связывание (binding): утилита gnatmake
проверяет согласованность обновления версий всех файлов проекта.
Если при этом обнаруживаются файлы которые нуждаются в перекомпиляции, gnatmake осуществит компиляцию таких файлов.
Компоновка (linking): утилита gnatmake осуществляет комбинирование всех объектных файлов проекта для создания результирующего исполняемого файла программы.
Любой из этих этапов построения проекта может быть выполнен "вручную".
Например, для полностью "ручной" сборки программы hello.adb
можно выполнить следующее:
Компиляция программы с помощью команды:
gcc -c hello.adb |
Примечание: при использовании в операционной системе Linux дистрибутива GNAT от ALT
вместо команды gcc применяется команда gnatgcc.
Связывание с помощью команды:
gnatbind hello.ali |
Компоновка программы с помощью команды:
gnatlink hello.ali |
В случаях простых проектов проще и удобнее использовать автоматическое управление сборкой проекта.
Необходимость сборки "вручную" возникает в случаях когда какой-либо этап сборки нуждается в настройках, которые должны соответствовать определенным требованиям или условиям сборки проекта.
Указание путей поиска файлов для gnatmake
Кроме показанных ранее опций, утилита gnatmake поддерживает опции указания путей поиска для файлов с исходными текстами и библиотечных файлов:
-aIdir | - |
При поиске файлов с исходными текстами, поиск файлов должен также осуществляться в каталоге dir. |
-aLdir | - |
Предположим, что в каталоге dir расположена дополнительная внешняя Ада-библиотека. Эта опция указывает утилите gnatmake на то, что нет нужды выполнять перекомпиляцию модулей чьи .ali файлы расположены в каталоге dir. Это позволяет не иметь тела (реализацию) для модулей в каталоге dir. Однако, при этом остается необходимость в указании места расположения файлов со спецификациями этих модулей с помощью опций -aIdir или -Idir. Примечание: эта опция предусмотрена для совместимости с предыдущими версиями утилиты gnatmake. Наиболее простым способом, который приводит к исключению анализа стандартной библиотеки, является блокировка ALI-файлов этой библиотеки (запрещение записи в ALI-файлы). |
-aOdir | - |
При поиске библиотечных и объектных файлов, поиск должен также осуществляться в каталоге dir. |
-Adir | - |
Эквивалентно -aLdir -aIdir. |
-Idir | - |
Эквивалентно -aOdir -aIdir. |
-I- | - |
Не выполнять поиск файлов с исходными текстами в каталогах с исходными текстами, которые указаны в командной строке. Не выполнять поиск объектных и ALI-файлов в каталоге из которого утилита gnatmake была запущена. |
-Ldir | - |
Добавить каталог dir к списку каталогов в которых компоновщик будет осуществлять поиск библиотечных файлов. Это эквивалентно использованию -largs -Ldir. |
-nostdinc | - |
Не осуществлять поиск файлов с исходными текстами в системном каталоге по умолчанию. |
-nostdlib | - |
Не осуществлять поиск библиотечных файлов в системном каталоге по умолчанию. |
Уменьшение затрат времени с помощью утилиты gnatstub
Начиная с версии 3.11p, система компилятора GNAT предусматривает утилиту gnatstub.
Утилита gnatstub может быть использована для построения файла прототипа тела пакета в соответствии с предоставленным файлом спецификации пакета.
В результате обработки файла спецификации пакета, в котором указаны спецификации подпрограмм, утилита gnatstub создает файл тела пакета, в котором располагаются "пустые" тела подпрограмм, соответствующие их спецификациям.
Такие "пустые" подпрограммы часто называют "заглушками" ("stubs").
Использование утилиты gnatstub может оказаться полезным при разработке больших программных проектов, когда программист разрабатывает целый набор файлов спецификаций пакетов, а затем проверяет правильность общего дизайна проекта.
В таких случаях, как только закончена разработка файлов спецификаций, утилита gnatstub может быть использована для создания базовых шаблонов тел, сохраняя тем самым программисту время, затрачиваемое на копирование и модификацию спецификаций вручную.
Предположим, что у нас есть файл с исходным текстом спецификации пакета tiny.ads, содержимое которого имеет следующий вид:
package Tiny is
procedure Simple_Procedure; function Simple_Function return Boolean; end Tiny; |
Шаблон тела пакета может быть создан с помощью команды:
gnatstub tiny.ads |
В результате, утилита gnatstub генерирует файл tiny.adb, который имеет следующий вид:
package body Tiny is
--------------------- -- Simple_Function -- --------------------- function Simple_Function return Boolean is begin return Simple_Function; end Simple_Function; ---------------------- -- Simple_Procedure -- ---------------------- procedure Simple_Procedure is begin null; end Simple_Procedure; end Tiny; |
Такое тело пакета имеет соответствующий формат и готово к компиляции.
Естественно, что такое тело пакета, реально, ничего полезного не выполняет.
Ответственность за описание соответствующих деталей реализации полностью возлагается на программиста.
Управление режимами gnatmake
Опции управления режимами gnatmake позволяют использовать опции командной строки, которые будет непосредственно переданы компилятору, редактору связей или компоновщику.
Эффект использования опций управления режимом приводит к тому, что последующий список опций, вплоть до завершения всего списка или до следующей опции управления режимом, будет интерпретироваться как опции, которые необходимо передать определенному компоненту системы GNAT (компилятору, редактору связей или компоновщику).
-cargs switches | - |
Опции компилятора. Здесь, switches представляет список опций которые допустимо использовать с gcc (gnatgcc). Этот список опций будет передаваться компилятору каждый раз когда утилита gnatmake проходит этап компиляции. |
-bargs switches | - |
Опции редактора связей. Здесь, switches представляет список опций которые допустимо использовать с gcc (gnatgcc). Этот список опций будет передаваться редактору связей каждый раз когда утилита gnatmake проходит этап связывания. |
-largs switches | - |
Опции компоновщика. Здесь, switches представляет список опций которые допустимо использовать с gcc (gnatgcc). Этот список опций будет передаваться компоновщику каждый раз когда утилита gnatmake проходит этап компоновки. |
Управляющая программа gnat и файлы проектов
Кроме утилиты gnatmake, существуют другие инструментальные средства системы GNAT, которые ориентированы на обработку проектов.
К таким инструментальным средствам относятся: gnatbind, gnatfind, gnatlink, gnatls и gnatxref.
Следует однако заметить, что ни один из этих инструментов не может непосредственно использовать опцию указания файла проекта ("-P").
Таким образом, необходимо осуществлять запуск этих инструментов с помощью управляющей программы gnat (или gnatcmd).
Управляющая программа gnat обеспечивает внешний интерфейс инструментальных средств. Она принимает целый перечень команд и осуществляет вызов соответствующих инструментальных средств.
Изначально, управляющая программа gnat была разработана для VMS, с целью преобразования квалификаторов стиля VMS в опции стиля UNIX.
Однако, в настоящий момент эта программа доступна для всех платформ, которые поддерживает система компилятора GNAT.
На платформах отличных от VMS, управляющая программа gnat принимает следующие команды (вне зависимости от регистра символов):
BIND для запуска gnatbind
CHOP для запуска gnatchop
COMP или COMPILE для запуска компилятора ELIM для запуска gnatelim
FIND для запуска gnatfind
KR или KRUNCH для запуска gnatkr
LINK для запуска gnatlink
LS или LIST для запуска gnatls
MAKE для запуска gnatmake
NAME для запуска gnatname
PREP или PREPROCESS для запуска gnatprep
PSTA или STANDARD для запуска gnatpsta
STUB для запуска gnatstub
XREF для запуска gnatxref
Следует заметить, что запуск компилятора осуществляется с помощью команды "gnatmake -f -u".
Вслед за командой можно указать опции и аргументы, которые будут переданы соответствующему инструментальному средству:
gnat bind -C main.ali gnat ls -a main gnat chop foo.txt |
Для команд: BIND, FIND, LS или LIST, LINK и XREF, - в дополнение к опциям, которые применимы для непосредственно вызываемого инструментального средства, могут быть использованы опции, которые характерны для файлов проектов (-P, -X and -vPx).
Для каждой из этих команд, в главном проекте возможно существование пакета, который соответствует запускаемому инструментальному средству:
"package Binder" - для команды BIND (запускает gnatbind) "package Finder" - для команды FIND (запускает gnatfind) "package Gnatls" - для команды LS или LIST (запускает gnatls) "package Linker" - для команды LINK (запускает gnatlink) "package Cross_Reference" - для команды XREF (запускает gnatlink)
Пакет Gnatls обладает уникальным атрибутом Switches, простая переменная со значением в виде списка строк. Атрибут содержит опции используемые при запуске gnatls.
project Proj1 is package gnatls is for Switches use ("-a", "-v"); end gnatls; end Proj1; |
Атрибут "Default_Switches ("Ada")" содержит опции, которые используются при запуске инструментального средства соответствующего пакету.
project Proj is for Source_Dirs use ("./**"); package gnatls is for Switches use ("-a", "-v"); end gnatls; package Binder is for Default_Switches ("Ada") use ("-C", "-e"); end Binder; package Linker is for Default_Switches ("Ada") use ("-C"); end Linker; package Finder is for Default_Switches ("Ada") use ("-a", "-f"); end Finder; package Cross_Reference is for Default_Switches ("Ada") use ("-a", "-f", "-d", "-u"); end Cross_Reference; end Proj; |
gnat ls -Pproj main gnat xref -Pproj main gnat bind -Pproj main.ali |
Условная компиляция с помощью препроцессора gnatprep
Хотя разработчики Ады решили не включать в язык директивы поддержки препроцессора, подобные используемым в языке программирования C, GNAT предусматривает препроцессор gnatprep, который позволяет осуществлять условную компиляцию Ада-программ.
Препроцессор gnatprep (GNAT PREProcessor) принимает файл содержащий директивы условной компиляции и генерирует, в результате своей работы, файл с исходным текстом в котором удалены все инструкции, которые не соответствуют условиям указанным в директивах условной компиляции.
Следует обратить внимание на то, что директивы условной компиляции не допускают использования выражений (в отличие от препроцессора языка C).
Таким образом, в директивах условной компиляции могут использоваться только переменные, которые могут принимать значения "истина" (True) или "ложь" (False).
Предположим, что у нас есть файл prepvalues
со следующими определениями для препроцессора:
ALPHAVERSION := True BETAVERSION := False RELEASEVERSION := False TRANSLATION := English |
Предположим также, что у нас есть небольшая программа, текст которой содержит директивы препроцессора gnatprep:
with Text_IO; use Text_IO;
procedure Preptest is -- включить только ту часть кода, которая уместна для этой версии #if ALPHAVERSION S_Version : String := "Alpha"; #elsif BETAVERSION S_Version : String := "Beta"; #elsif RELEASEVERSION S_Version : String := "Release"; #else S_Version : String := "Unknown"; #end if; -- строковой переменной S_Translation будет присваиваться -- значение переменной препроцессора $TRANSLATION S_Translation : String := "$TRANSLATION"; begin Put_Line( "This is the " & S_Version & " edition" ); Put_Line( "This is the " & S_Translation & " translation" ); end Preptest; |
В результате обработки показанной выше программы препроцессором gnatprep, с учетом значений указанных в файле prepvalues, будет получен следующий исходный текст программы:
with Text_IO; use Text_IO; procedure Preptest is -- включить только ту часть кода, которая уместна для этой версии S_Version : String := "Beta"; -- строковой переменной S_Translation будет присваиваться -- значение переменной препроцессора $TRANSLATION S_Translation : String := "English"; begin Put_Line( "This is the " & S_Version & " edition" ); Put_Line( "This is the " & S_Translation & " translation" ); end Preptest; |
-Dsymbol=value | - |
Позволяет объявить значение value для символа symbol в командной строке запуска препроцессора gnatprep, а не в файле (аналогично опции -D для препроцессора языка C). Например, -DMacintosh=FALSE. |
-b | - |
Заменить в выходном файле с исходным текстом команды препроцессора gnatprep пустыми строками (вместо -c). |
-c | - |
Закомментировать в выходном файле с исходным текстом команды препроцессора gnatprep вместо -b). |
-r | - | Сгенерировать в выходном файле с исходным текстом директиву компилятора Source_Reference. |
-s | - | Распечатать отсортированный список символов и их значений. |
-u | - | Трактовать необъявленные символы как символы имеющие значение FALSE. |
или __LINE__ (number of current line), которые поддерживаются препроцессором языка C.
Установка Ада-библиотеки
Установка какой-либо библиотеки в системе компилятора GNAT осуществляется копированием файлов, которые составляют библиотеку, в какое-либо определенное место на диске.
Существует возможность установки файлов с исходными текстами библиотеки в самостоятельный каталог, отдельно от остальных файлов библиотеки (ALI, объектных файлов, библиотечных архивов).
Это возможно благодаря тому, что пути поиска для файлов с исходными текстами и для объектных файлов определяются отдельно.
Системный администратор может установить библиотеки общего назначения в место, которое будет указано для компилятора в пути поиска по умолчанию.
Для осуществления этого он должен указать расположение таких библиотек в файлах конфигурации "ada_source_path" и "ada_object_path", которые должны располагаться там же где и файл specs, который определяет конфигурацию gcc, в дереве установки системы компилятора GNAT.
Расположение файла specs, определяющего конфигурацию gcc, можно узнать следующим образом:
$ gnatgcc -v |
Упомянутые файлы конфигурации имеют простой формат: каждая строка должна содержать одно уникальное имя каталога.
Такие имена добавляются к соответствующему пути поиска в порядке их появления внутри файла.
Имена каталогов могут быть абсолютными или относительными, в последнем случае они указываются относительно расположения этих файлов.
Файлы "ada_source_path" и "ada_object_path" могут отсутствовать после установки системы компилятора GNAT.
В этом случае GNAT будет осуществлять поиск своей библиотеки времени выполнения в каталогах: "adainclude" - файлы с исходными текстами, и "adalib" - объектные и ALI файлы.
При наличии файлов "ada_source_path" и "ada_object_path" компилятор не осуществляет поиска в каталогах "adainclude" и "adalib".
Таким образом, файл "ada_source_path" должен указывать расположение файлов с исходными текстами библиотеки времени выполнения GNAT (которые могут быть расположены в "adainclude").
Подобным образом, файл "ada_object_path" должен указывать расположение объектных файлов библиотеки времени выполнения GNAT (которые могут быть расположены в "adalib").
Библиотека может быть установлена до или после стандартной библиотеки GNAT путем изменения порядка следования строк в конфигурационных файлах.
В общем случае, бииблиотека должна быть установлена перед стандартной библиотеки GNAT в случае, когда она переопределяет какую-либо часть стандартной библиотеки.
Установка бинарных файлов от ACT
При установке бинарных файлов от ACT следует учитывать, что они построены с учетом определенной версии библиотеки языка C.
Для того, чтобы определить какая версия библиотеки libc используется в вашем дистрибутиве Linux, следует проверить на какой файл указывает ссылка /lib/libc.so.
Например, если ссылка /lib/libc.so указывает на файл libc5, то вам необходима версия GNAT которая использует libc5.
По умолчанию, GNAT от ACT устанавливается в каталог /usr/gnat.
Если у вас отсутствует GCC версии 2.8.1, то вы можете указать другой каталог для установки GNAT и его собственной копии gcc версии 2.8.1.
Используя такой метод, вам необходимо выполнить дополнительный шаг.
Программа установки автоматически создает скрипт командного интерпретатора shell, в котором содержатся установки некоторых переменных окружения необходимых GCC для поиска файлов GNAT.
В результате, вы можете скопировать эти установки в ваши скрипты настройки среды (для bash, как правило, это файл .profile в вашем домашнем каталоге).
Вам также необходимо указать каталог установки файлов GNAT в начале переменной окружения PATH, для того чтобы GNAT не использовал версию gcc
которая поставляется вместе с вашим дистрибутивом Linux.
Например, это может быть выполнено с помощь команды:
export PATH="/usr/gnat/bin:$PATH" |
Эту команду следует применять только тогда когда вы используете GNAT, поскольку она эффективно "прячет" копию gcc которая поставляется вместе с вашим дистрибутивом Linux.
В случаях когда окружение среды, соответствующее требованиям использования GNAT, не желательно устанавливать как окружение по умолчанию можно написать короткий скрипт командного интерпретатора shell, который будет выполнять необходимые установки и запускать новый экземпляр командного интерпретатора.
Установка GNAT
Как уже говорилось, GNAT является частью проекта GCC.
Команда gcc, сама по себе, не является компилятором.
Это программа которая определяет тип исходного текста и затем осуществляет запуск соответствующего компилятора.
Например, компилятором языка Ада является программа называемая gnat1, а компилятором языка C является программа называемая cc1.
Когда программа gcc определяет, что предоставленный ей исходный текст написан на языке Ада она просто запускает компилятор gnat1
для компиляции этого исходного текста.
Поскольку GNAT и GCC должны работать совместно, то определенная версия GNAT создается в рассчете на то, что она будет взаимодействовать с определенной версией GCC.
Так, версии GNAT: 3.11p, 3.12p, 3.13p, 3.14p и 3.15p собраны с GCC версии 2.8.1 (чтобы узнать какая версия GCC используется в системе нужно запустить gcc с опцией командной строки -v).
Стандартный дистрибутив GNAT от ACT
поставляется со своей собственной копией GCC, имеющей корректную версию gcc, и позволяет установить GNAT и GCC в различные каталоги.
Дистрибутив GNAT от ACT содержит двоичные исполняемые файлы у которых отключена поддержка языка C++.
Таким образом, если необходима поддержка языка C++ одновременно с языком Ада, то необходимо пересобрать GNAT и GCC из исходных текстов.
Доступ к свободно распространяемой версии компилятора GNAT от ACT
предоставляется через сеть Internet с сервера Нью-Йоркского университета по адресу "ftp://ftp.cs.nyu.edu/pub/gnat".
В момент написания этого текста на этом сервере доступны две версии компилятора: 3.14p и 3.15p (последняя версия 3.15p), которые соответственно расположены в подкаталогах "ftp://ftp.cs.nyu.edu/pub/gnat/3.14p" и "ftp://ftp.cs.nyu.edu/pub/gnat/3.15p".
Следует заметить, что на различных зеркалах этого сервера (которых в Internet немало) можно обнаружить и более ранние версии компилятора.
Каждый подкаталог в котором храниться соответствующая версия компилятора содержит архивы бинарных файлов, файлов документации и файлов с исходными текстами.
Архивы бинарных файлов упакованы с учетом использования компилятора на различных целевых платформах.
Тип целевой платформы, как правило, можно определить по имени архивного файла.
Например, архив gnat-3.15p-i686-pc-redhat71-gnu-bin.tar.gz
содержат компилятор GNAT версии 3.15p, который может использоваться на процессоре i686 и операционной системе Linux (желательно дистрибутив Red Hat 7.1), а архив gnat-3.15p-sparc-sun-solaris2.5.1-bin.tar.gz
содержат компилятор GNAT версии 3.15p, который может использоваться на процессоре SPARK и операционной системе Solaris-2.5.1.
Вне зависимости от того какую версию компилятора вы выберете, перед началом установки настоятельно рекомендуется ознакомиться с содержимым соответствующих файлов README.
Установка GNAT на Linux
При установке GNAT на Linux следует учитывать, что в системе возможна совместная одновременная установка нескольких различных версий gcc.
Для выбора определенной версии gcc, необходимой для компиляции исходных текстов, можно использовать опцию командной строки -V.
Однако, для выполнения этого, необходимо пересобрать GCC из исходных текстов так, чтобы можно было использовать различные версии gcc.
Установка GNAT на Windows
Компилятор GNAT, который собран для работы на аппаратной платформе Intel x86 под управлением операционной системой Windows находится в подкаталоге "winnt".
Как говорится в документации, версия 3.14p может быть использована в среде Windows 95/98 или Windows NT 4.0/2000, а версия 3.15p - в среде Windows 95/98 или Windows NT 4.0/2000/XP.
Компилятор GNAT для Windows разбит на две части.
Файл gnat-3.14p-nt.exe (или gnat-3.15p-nt.exe) содержит базовый комплект инструментов Ада-компилятора, а файл gnatwin-3.14p.exe (или gnatwin-3.15p.exe) содержит компоненты Ада-интерфейса Win32.
Для установки компилятора необходимо просто запустить на выполнение файл gnat-3.14p-nt.exe (или gnat-3.15p-nt.exe) и следовать инструкциям интерактивной установки.
После успешного завершения установки базового комплекта инструментов, аналогичным образом, устанавливаются компоненты Ада-интерфейса Win32.
Кроме этого, специально для пользователей Windows, существует свободно распространяема интегрированная среда разработки AdaGide, которая ориентированна на компилятор GNAT.
Дистрибутив AdaGide свободно доступен с WEB-странички проекта: "
http://www.usafa.af.mil/dfcs/bios/mcc_html/adagide.html".
Установка RPM-пакетов ALT
Кроме стандартного дистрибутива GNAT от ACT, существует альтернативный дистрибутив GNAT от ALT
(Ada for Linux Team, WEB-страничка проекта доступна по адресу: "http://www.gnuada.org/alt.html") и альтернативный дистрибутив ALR
(Ada for Linux RU, WEB-страничка проекта доступна по адресу: "http://www.prz.rzeszow.pl/ada/").
Версия дистрибутива GNAT от ALT построена с учетом использования GNAT в среде дисрибутивов системы Linux от Red Hat, S.u.S.E. или Debian.
GNAT от ALT может быть также использован с дистрибутивами Mandrake и Caldera.
В настоящий момент дистрибутив от ALT содержит компилятор GNAT версии 3.13p.
Дистрибутив ALT содержит поддержку для ASIS, GLADE
и использует "родные нити" Linux (native Linux threads).
Пакеты дистрибутива содержат gnatgcc (версия gcc с поддержкой GNAT и C++), отладчик gnatgdb (версия gdb, которая поддерживает исходные тексты на языке Ада), препроцессор gnatprep и другие утилиты разработки.
Кроме того, дистрибутив от ALT включает множество дополнительных пакетов в которых находятся различные библиотеки и программы, которые могут быть полезны при разработке программного обеспечения на языке программирования Ада в среде операционной системы Linux.
Дистрибутив ALR построен в среде системы Red Hat Linux. Он наследует основные особенности дистрибутива ALT, но построен на основе более новой версии компилятора GNAT - 3.15p, и включает более новые версии пактов дополнительного программного обеспечения. Кроме того, в этой сборке компилятор позволяет использовать в идентификаторах кириллицу в кодировках KOI8-R/KOI8-U. Необходимо заметить, что при дальнейшем упоминании в тексте дистрибутива ALT равноправно подразумевается дистрибутив ALR.
Следует учесть, что все пакеты rpm
были построены с учетом сред дистрибутивов Red Hat и S.u.S.E..
Поэтому, для того чтобы проигнорировать предупреждающие сообщения о зависимости пакетов, которые могут возникнуть при попытке установить rpm-пакеты от ALT
в среде других дистрибутивов Linux, может потребоваться использование опции командной строки --nodep в команде rpm.
Для установки rpm- пакетов дистрибутива от ALT необходимо выполнить следующее:
Загрузить и прочитать файл readme.
Загрузить файл rpm-пакета gnat-3.xxp-runtime*
(здесь, xx обозначает текущую версию GNAT, а * обозначает остаток имени файла).
Для старых версий rpm-пакетов, содержимое этого пакета находится в пакете gnat-3.xxp*.
Загрузить файл rpm-пакета gnat-3.xxp.
Для старых версий rpm-пакетов, содержимое этого пакета находится в пакете gnat-3.xxp-devel*.
Выполнить команду: rpm -i gnat-3.xxp-runtime*
Выполнить команду: rpm -i gnat-3.xxp*
При необходимости, загрузить и установить любые дополнительные пакеты дистрибутива GNAT от ALT.
Файлы всех rpm-пакетов дистрибутива GNAT от ALT сконфигурированы для совместной работы с версией GNAT от ALT.
Для их установки необходимо просто загрузить их с интернет-сайта ALT
и выполнить команду установки rpm -i ... для соответствующего пакета.
С помощью CVS, система построения пакетов дистрибутива GNAT от ALT
доступна для тех кто желает ознакомиться с деталями построения rpm-пакетов от ALT:
Утилита gnatmake
Как уже было показано ранее, утилита gnatmake используется для компиляции Ада-программ.
Например:
gnatmake main.adb |
Она осуществляет проверку всех пакетов от которых зависит программа main.adb
и автоматически компилирует любой пакет, который нуждается в компиляции или перекомпиляции.
После завершения процесса компиляции утилита gnatmake автоматически выполняет процесс связывания и компоновки, для получения, в результате, исполняемого файла программы.
Бывают случаи, когда необходимо выполнить только компиляцию проекта и не выполнять связывание и компоновку.
Это можно сделать используя опцию -n:
gnatmake -n main.adb |
Утилита gnatmake и файлы проектов
Рассмотрим два вопроса, которые касаются утилиты gnatmake и файлов проектов: описание опций для утилиты gnatmake и всех инструментальных средств, запуск которых она осуществляет; использование атрибута Main.
Для каждого пакета: Builder, Compiler, Binder и Linker, - можно указать атрибуты Default_Switches и Switches.
Имена этих атрибутов подразумевают, что эти атрибуты определяют какие опции (switches - переключатели) и для каких файлов необходимо использовать как аргументы командной строки запуска утилиты gnatmake.
Ниже будет показано, что эти ориентированные на пакеты опции предшествуют опциям, которые передаются в командную строку запуска утилиты gnatmake.
Атрибут Default_Switches является ассоциативным массивом, который индексируется именем языка программирования (не зависимо от регистра символов) и возвращает значение в виде списка строк. Например:
package Compiler is
for Default_Switches ("Ada") use ("-gnaty", "-v"); end Compiler; |
Атрибут Switches также вляется ассоциативным массивом. Он индексируется именем файла (которое зависит или не зависит от регистра символов, в зависимости от используемой операционной системы) и возвращает значение в виде списка строк. Например:
package Builder is
for Switches ("main1.adb") use ("-O2"); for Switches ("main2.adb") use ("-g"); end Builder; |
Для пакета Builder, имена файлов должны обозначать исходные файлы головных подпрограмм. Для пакетов Binder и Linker, имена файлов должны обозначать файлы "ALI" или исходные файлы головных подпрограмм. В каждом случае применимо только самостоятельное имя файла (без явного указания расширения имени файла).
Для каждого используемого в процессе сборки программы инструментального средства (утилита gnatmake, компилятор, редактор связей и компоновщик), соответствующий ему пакет указывает набор опций, которые необходимо использовать при обработке этим инструментальным средством каждого файла.
Такой набор используемых опций определяется с помощью описания внутри пакета соответствующих, ориентированных на указание опций атрибутов.
В частности, опции, которые каждый такой пакет определяет для указанного файла F, включают:
значение атрибута "Switches (F)", когда он указан в пакете для данного файла
в противном случае, значение атрибута "Default_Switches ("Ada")", когда он указан в пакете
Когда внутри пакета ни один их этих атрибутов не описан. пакет не указывает никаких опций для данного файла.
При запуске утилиты gnatmake для обработки заданного файла, весь набор опций командной строки запуска утилиты gnatmake состоит из двух множеств, располагаемых в следующем порядке:
набор опций, которые определены для этого файла в пакете Builder
набор опций, которые были переданы в командной строке
Когда, для обработки файла, утилита gnatmake осуществляет запуск какого-либо инструментального средства (компилятор, редактор связей, компоновщик) набор опций, которые передаютсяя инструментальному средству, состоит из трех множеств, располагаемых в следующем порядке:
набор опций, которые определены для этого файла в пакете Builder
файла проекта, заданного в командной строке набор опций, которые определены для этого файла внутри пакета соответствующего инструментального средства (в соответствующем файле проекта) набор опций, которые были переданы в командной строке
Следует заметить, что опции, заданные в командной строке запуска утилиты gnatmake, могут передаваться или не передаваться индивидуальному инструментальному средству, в зависимости от конкретной опции.
Утилита gnatmake может осуществлять запуск компилятора для обработки исходных файлов различных проектов. Менеджер проектов GNAT будет использовать соответствующий файл проекта, чтобы определить пакет Compiler
для каждого исходного файла, который нуждается в компиляции. То же самое справедливо для пакетов Binder (редактор связей) и Linker (компоновщик).
В качестве примера, рассмотрим пакет Compiler
в показанном ниже файле проекта:
project Proj1 is package Compiler is for Default_Switches ("Ada") use ("-g"); for Switches ("a.adb") use ("-O1"); for Switches ("b.adb") use ("-O2", "-gnaty"); end Compiler; end Proj1; |
Следующий пример демонстрирует упорядочивание опций, которые предоставлены различными пакетами:
project Proj2 is package Builder is for Switches ("main.adb") use ("-g", "-O1", "-f"); end Builder; packager Compiler is for Switches ("main.adb") use ("-O2"); end Compiler; end Proj2; |
gnatmake -PProj2 -O0 main |
-g -O1 -O2 -O0 |
В рассматриваемом примере, опции "-g" и "-O1" обеспечиваются пакетом Builder, опция "-O2" обеспечена пакетом Compiler, а опция "-O0" была указана в командной строке.
Следует заметить, что опция "-g" будет также передаваться при запуске gnatlink.
Заключительный пример демонстрирует предоставление опций из пакетов, которые описаны в различных файлах проектов:
project Proj3 is for Source_Files use ("pack.ads", "pack.adb"); package Compiler is for Default_Switches ("Ada") use ("-gnata"); end Compiler; end Proj3; with "Proj3"; project Proj4 is for Source_Files use ("foo_main.adb", "bar_main.adb"); package Builder is for Switches ("foo_main.adb") use ("-s", "-g"); end Builder; end Proj4; -- Ada source file: with Pack; procedure Foo_Main is ... end Foo_Main; |
При использовании команды:
gnatmake -PProj4 foo_main.adb -cargs -gnato |
При компиляции импортируемого Ада-пакета Pack, будут использоваться опция "-g" (из пакета Proj4.Builder), опция "-gnata" (из пакета Proj3.Compiler) и опция "-gnato" (передается из командной строки).
При использовании файла проекта, в командной строке запуска утилиты gnatmake можно указать несколько головных подпрограмм. Исходный файл каждой указываемой таким образом головной подпрограммы должен являться непосредственным исходным файлом проекта.
gnatmake -Pprj main1 main2 main3 |
В этом случае, полученный результат будет зависесть от наличия описания атрибута Main.
Значением этого атрибута является список строк, каждый элемент которого является именем исходного файла (указание расширения имени файла является не обязательным), который содержит головную подпрограмму.
Когда атрибут Main описан в файле проекта как не пустой список строк и в командной строке запуска утилиты gnatmake не указана опция "-u", запуск gnatmake, - с указанием такого файла проекта, но без указания в командной строке какой-либо головной подпрограммы, - является эквивалентным запуску gnatmake с указанием в командной строке всех имен файлов, которые указываются атрибутом Main.
Например:
project Prj is for Main use ("main1", "main2", "main3"); end Prj; |
Когда файл проекта не содержит описание атрибута Main, или этот атрибут описан как пустой список строк, или в командной строке указана опция "-u", и запуск утилиты gnatmake осуществляется без указания в командной строке какой-либо головной подпрограммы, будет осуществлена проверка всех непосредственных исходных файлов проекта и, потенциально, они будут перекомпилированы.
В зависимости от присутствия в командной строке опции "-u", исходные файлы других проектов, от которых зависят непосредственные исходные файлы главного проекта, будут также проверены и, потенциально, перекомпилированы.
Другими словами, опция "-u" применяется ко всем непосредственным исходным файлам главного файла проекта.
Утилита gnatmem
Для мониторинга программы, которая выполняется под управлением отладчика gdb (поставляемого вместе с GNAT), может быть использована утилита gnatmem
(начиная с версии 3.12 GNAT).
После того как выполнение программы завершено, утилита gnatmem отображает общую информацию о динамическом распределении памяти, в процессе работы программы.
Эта информация может быть использована для поиска "утечек" памяти, то есть, мест где программа осуществляет динамическое распределение памяти и не возвращает распределенное пространство памяти системе.
Поскольку утилита gnatmem использует отладчик gdb, программа должна быть скомпилирована с подключением поддержки отладки под управлением gdb, что выполняется указанием опции -g при компиляции программы.
Для запуска программы program под управлением утилиты gnatmem
можно сделать следующее:
gnatmem program |
Утилита gnatmem может принимать следующие опции командной строки:
-q | - | Активирует "молчаливый" режим - выводится не вся статистика, а только информация о потенциально возможных утечках памяти. |
n | - | Число в интервале от 1 до 10 указывающее глубину вложенности информации обратной трассировки. |
-o file | - | Сохранить вывод отладчика gdb в указанный файл file. Скрипт отладчика gdb сохраняется как файл gnatmem.tmp. |
-i file | - | Осуществить мониторинг используя первоначально сохраненный с помощью опции -o
файл file. Удобно использовать для проверки программы выполнение которой под управлением утилиты gnatmem было неожиданно прервано в результате возникновения каких-либо ошибок. |
Утилита перекрестных ссылок gnatxref
Утилита gnatxref (или gnatf для GNAT 3.10) - это утилита которая генерирует индексы для каждого появления идентификатора в программе, включая все идентификаторы использованные в пакетах от которых данная программа зависит.
Опция -v позволяет генерировать листинг в формате tag-файла редактора vi.
Для программы hello.adb, показанной ранее, утилита gnatxref
генерирует следующее:
Text_IO U a-textio.ads:51:13 {} {hello.adb:1:10 4:7 } Put_Line U a-textio.ads:260:14 {} {hello.adb:4:15 } Ada U ada.ads:18:9 {} {hello.adb:1:6 4:3 } hello U hello.adb:2:11 {} {} |
Каждая строка начинается с указания имени индексируемого идентификатора.
Далее следует имя файла в котором данный идентификатор объявлен с указанием расположения этого объявления в файле.
В заключение, следует секция в которой перечислены все появления идентификатора в программе.
В данном примере, идентификатор Text_IO
появляется в первой строке (с учетом спецификатора with) и в четвертой строке (Put_Line).
Утилиты gnatpsys и gnatpsta
Множество описаний стандартных пакетов System и Standard
зависят от конкретной реализации системы и используемой платформы.
Исходный текст пакета Standard не доступен, а доступный исходный текст пакета System
использует множество специальных атрибутов для параметризации различных критических значений и в некоторых случаях может оказаться не достаточно информативным.
Для подобных случаев предусматриваются специальные утилиты gnatpsys и gnatpsta
(начиная с версии 3.15, утилита gnatpsys - не поставляется).
Эти утилиты осуществляют динамическое определение значений параметров, которые зависят от конкретной реализации системы и используются пакетами System и Standard, после чего, отображают их в виде листингов с исходными текстами этих пакетов, показывая все фактические значения, которые могут быть интересны программисту.
Обе утилиты осуществляют вывод на стандартное устройство вывода используемой системы и не требуют указания каких-либо параметров в командной строке.
Утилита gnatpsys предназначена для отображения листинга пакета System, а утилита gnatpsta отображает исходный текст пакета Standard.
Встроенная подстановка (inline) для кода на встроенном ассемблере
При использовании маленьких функций, затраты на выполнение вызова могут оказаться больше чем затраты на выполнение непосредственного кода функции, которая написана на встроенном ассемблере.
Это хорошо демонстрирует пример функции Increment.
Решением такой проблемы может быть использование встроенной подстановки (inline), которая позволяет в месте вызова подпрограммы (процедуры или функции), написанной на встроенном ассемблере, осуществить непосредственную вставку машинного кода, который соответствует этой подпрограмме.
Таким образом, для выполнения встроенной подстановки показанной ранее функции Increment, необходимо просто добавить соответствующую директиву компилятора:
with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code;
procedure Inc_It2 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; pragma Inline (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_It2; |
После компиляции этой программы с указанием выполнения полной оптимизации (опция -O2) и разрешением осуществления встроенной подстановки (опция -gnatpn) функция Increment будет откомпилирована как обычно, но в том месте, где раньше указывался вызов функции:
pushl %edi call _inc_it__increment.1 |
теперь будет расположен непосредственный код этой функции:
movl %esi,%eax #APP incl %eax #NO_APP movl %eax,%edx |
В конечном итоге, это повысит общую эффективность выполнения кода программы.
Выражения
Какое-либо выражение является или строковым выражением, или выражением списка строк.
Какое-либо строковое выражение является или простым строковым выражением, или составным строковым выражением.
Какое-либо простое строковое выражение является:
Строковым литералом (например, "comm/my_proj.gpr") Обращеие к переменной обладающей строковым значением (см. "Переменные") Обращение к атрибуту обладающему строковым значением (см. "Атрибуты") Внешняя ссылка (см. "Внешние ссылки в Файлах Проектов")
Какое-либо составное строковое выражение является конкатенацией строковых выражений с помощью символа '&'. Например:
Path & "/" & File_Name & ".ads" |
Какое-либо выражение списка строк является простым выражением списка строк или составным выражением списка строк.
Каким-либо простым выражением списка строк является:
Заключенный в скобки список, состоящий из нуль или более строковых выражений, разделенных запятыми:
File_Names := (File_Name, "gnat.adc", File_Name & ".orig"); Empty_List := (); |
Обращение к переменной обладающей значением списка строк Обращение к атрибуту обладающему значением списка строк
Каким-либо составным выражением списка строк является конкатенация простого выражения списка строк и какого-либо выражения с помощью символа '&'.
Примечательно, что каждая лексема составного выражения списка строк, за исключением первой, может быть как строковым выражением, так и выражением списка строк. Например:
File_Name_List := () & File_Name; -- в этом списке одна строка
Extended_File_Name_List := File_Name_List & (File_Name & ".orig"); -- две строки Big_List := File_Name_List & Extended_File_Name_List; -- Конкатенация двух списков строк: три строки Illegal_List := "gnat.adc" & Extended_File_Name_List; -- не допустимо: конкатенация должна начинаться со списка строк |
Вызов подпрограмм определяемых пользователем
Одной из важных особенностей отладчика GDB является его способность вызывать в процессе отладки подпрограммы, определяемые пользователем.
Это можно выполнить путем простого ввода команды вызова подпрограммы, которая имеет следующий вид:
call subprogram-name (parameters) |
Ключевое слово call может быть опущено в случае, когда имя подпрограммы subprogram-name
не совпадает с какой-либо предопределенной командой GDB.
В результате выполнения этой команды осуществляется вызов указанной подпрограммы и передача ей списка параметров, которые заданы в команде.
В качестве параметров могут быть указаны выражения и они могут содержать переменные отлаживаемой программы.
Подпрограмма должна быть определена в программе на уровне библиотеки, поскольку GDB вызывает подпрограмму в окружении, которое устанавливается для выполнения отлаживаемой программы (это также подразумевает, что подпрограмма обладает возможностью доступа и даже может модифицировать значения переменных внутри программы).
Важность данного средства заключается в том, что оно позволяет использовать в программе различные отладочные подпрограммы, которые приспособлены к каким-либо специфическим структурам данных отлаживаемой программы.
Подобные подпрограммы отладки могут быть написаны с целью получения высокоуровневого отображения внутреннего состояния какого-либо абстрактного типа данных, что намного удобнее чем использование распечатки содержимого памяти (иначе - дамп памяти), которая заната структурой данных, на физическом уровне.
Следует учитывать, что стандартная команда отладчика GDB print
имеет представление только о физическом расположении типов в памяти, а не об их смысловом значении и абстрактном представлении.
Отладочные подпрограммы могут предусматривать отображение информации с учетом требуемого семантического уровня и, таким образом, их использование при отладке программы может быть очень полезно.
Например, при непосредственной отладке GNAT, ключевым моментом является наличие доступа к содержимому узлов семантического дерева, которое содержит внутреннее представление программы.
Однако, узлы семантического дерева представляются в виде целочисленных значений, которые являются индексами в таблицу узлов.
Использование встроенной команды print, для узлов семантического дерева, обеспечит простое отображение целочисленных значений, что не обладает желаемой информативностью.
Таким образом, намного удобнее использовать подпрограмму PN
(описана в файле treepr.adb дистрибутива исходных текстов GNAT), которая принимает на входе узел семантического дерева и отображает его высокоуровневое представление, включающее синтаксическую категорию данного узла, его место в исходном тексте, целочисленные значения, соответствующие родительскому узлу и узлам потомкам, а также множество дополнительной семантической информации.
Для более детального изучения этого примера можно обратиться к телу этой процедуры в указанном файле.
Задачи Ады
При работе с задачами Ады, GDB позволяет использовать следующие команды:
info tasks
Эта команда отображает список текущих задач Ады, который имеет вид подобный следующему:
@leftskip=0cm (gdb) info tasks ID TID P-ID Thread Pri State Name 1 8088000 0 807e000 15 Child Activation Wait main_task 2 80a4000 1 80ae000 15 Accept/Select Wait b 3 809a800 1 80a4800 15 Child Activation Wait a * 4 80ae800 3 80b8000 15 Running c |
При отображении списка, символ звездочки указывает на выполнение задачи в текущий момент времени.
Первый столбец отображает идентификатор задачи (task ID), который используется для обращения к задачам в следующих командах.
break linespec task taskid
break linespec task taskid if ...
Эти команды подобны командам break ... thread ..., а linespec указывает строки исходного текста.
Чтобы указать GDB на необходимость приостановки выполнения программы, когда определенная задача Ады достигнет точки прерывания, следует использовать квалификатор "task taskid".
При этом, taskid является численным идентификатором задачи, который назначается отладчиком GDB (он отображается в первом столбце, при отображении списка задач с помощью команды "info tasks").
Если при установке точки прерывания квалификатор "task taskid" не указан, то точки прерывания будут установлены для всех задач программы.
Квалификатор task может быть также использован при установке условных точек прерывания.
В этом случае квалификатор "task taskid" указывается перед указанием условия точки прерывания (перед if).
task taskno
Эта команда позволяет переключиться на задачу, которая указывается с помощью параметра taskno.
В частности, это позволяет просматривать и анализировать обратную трассировку стека указанной задачи.
Необходимо заметить, что перед продолжением выполнения программы следует выполнить переключение на оригинальную задачу, в которой произошла приостановка выполнения программы, иначе возможен сбой работы планировщика задач программы.
Более подробные сведения о поддержке задач содержатся в руководстве "Отладка с помощью GDB" (Debugging with GDB).
Загрузка содержимого памяти
Ранее мы рассмотрели инструкцию вида:
movl $my_var, %eax |
которая позволяет загрузить регистр eax значением адреса переменной my_var.
А как можно загрузить в регистр eax значение содержимого переменной my_var?
Используя привычный синтаксис Intel можно написать:
mov eax, [my_var] |
Синтаксис AT&T потребует изменить это на:
movl my_var, %eax |
"Затирание" содержимого регистров
До настоящего момента времени, мы явно указывали компилятору (посредством параметров ввода и вывода) какие регистры процессора мы используем, и генератор кода компилятора вынужден был это учитывать.
Однако, что произойдет в том случае когда мы используем какой-либо регистр процессора, а компилятор об этом не знает?
Можно догадаться, что в таком случае компилятор тоже будет использовать этот регистр процессора, а результатом этого будет какой-нибудь "сюрприз" при работе программы.
Следовательно, использование в коде ассемблера какого-либо регистра процессора, о котором компилятору ничего не известно, может привести к каким-нибудь побочным эффектам в инструкциях (например, mull, сохраняющая результат своего действия в двух регистрах eax и edx) или в действиях, описанных в коде ассемблера, подобно следующему:
Asm ("movl %0, %%ebx" & "movl %%ebx, %1", Inputs => Unsigned_32'Asm_Input ("=g", Input), Outputs => Unsigned_32'Asm_Output ("g", Output)); |
Здесь, компилятор не подозревает о том, что регистр ebx уже используется. В подобных случаях, необходимо использовать параметр Clobber, который дополнительно укажет компилятору регистры, используемые в коде ассемблера:
Asm ("movl %0, %%ebx" & "movl %%ebx, %1", Inputs => Unsigned_32'Asm_Input ("=g", Input), Outputs => Unsigned_32'Asm_Output ("g", Output), Clobber => "ebx"); |
Как видно из этого примера, параметр Clobber является простой строкой, которая содержит список используемых регистров процессора.
Для случаев использования параметра Clobber
необходимо сделать два дополнительных замечания:
именам регистров не должен предшествовать символ процента имена регистров в списке разделяются символом запятой: "eax, ebx"
Кроме перечисления используемых регистров в параметре Clobber, существует еще два именованых параметра которые удобно использовать в некоторых случаях:
использование регистра cc указывает, что могут быть изменены флаги
использование регистра memory указывает, что может быть изменено расположение в памяти
Значения констант
Это может показаться странным, но попытка ассемблирования показанного ранее кода также окажется безуспешной.
Причина в том, что синтаксис AT&T подразумевает, что перед непосредственными статическими константными значениями необходимо помещать символ доллара '$':
mov $4, %eax |
В результате, этот код выполнит загрузку регистра eax значением 4.
Такая же нотация используется когда необходимо загрузить в регистр какое-либо адресное значение.
Например, можно написать следующее:
mov $my_var, %eax |
для загрузки адреса переменной my_var в регистр eax.
Знакомство с командами GDB
Отладчик GDB поддерживает обширный набор команд.
Руководство "Отладка с помощтью GDB" (Debugging with GDB) содержит полное описание всех команд и предоставляет большое количество примеров их использования.
Более того, GDB имеет встроенную команду help, которая выдает краткую справку о доступных командах и их опциях.
Следует заметить, что здесь, для демонстрации принципов использования отладчика GDB, рассматриваются только несколько наиболее часто употребляемых команд.
При чтении данного материала следует создать какую-нибудь простую программу с отладочной информацией и поэкспериментировать с указанными командами.
set args arguments
С помощью данной команды можно указать список аргументов arguments, которые будут переданы программе при выполнении последующей команды run.
В использовании команды set args нет необходимости, когда программа не нуждается в аргументах.
run
Команда run осуществляет запуск программы на выполнение со стартовой точки.
Если программа была запущена ранее, а в текущий момент ее выполнение приостановлено в какой-либо точке прерывания, то отладчик запросит подтверждение на прекращение текущего выполнения программы и перезапуск выполнения программы со стартовой точки.
breakpoint location
С помощью данной команды можно осуществлять установку точек прерывания, в которых GDB приостановит выполнение программы и будет ожидать последующие команды.
В качестве location можно использовать номер строки в файле, который указывается в виде файл:номер_строки, или имя подерограммы.
При указании совмещенного имени, которое используют несколько подпрограмм, отладчик потребует уточнение на какой из этих подпрограмм необходима установка точки прерывания.
В этом случае можно также указать, что точки прерывания должны быть установлены на всех подпрограммах.
Когда выполняемая программа достигает точки прерывания, ее выполнение приостанавливается, а GDB сигнализирует об этом печатью строки кода, перед которой произошла остановка выполнения программы.
breakpoint exception name
Это специальная форма команды установки точки прерывания, которая позволяет прервать выполнение программы при возбуждении исключения с именем name.
Если имя name не указано, то приостановка выполнения программы осуществляется при возбуждении любого исключения.
print expression
Эта команда печатает значение выражения expression.
GDB нормально обрабатывает основные простые выражения Ады, благодаря чему, выражение expression может содержать вызовы функций, переменные, знаки операций и обращения к атрибутам.
continue
Эта команда позволяет продолжить выполнение программы, после приостановки в какой-либо точке прерывания, вплоть до завершения работы программы или до достижения какой-либо другой точки прерывания.
step
Выполнить одну строку кода после точки прерывания.
Если следующая инструкция является вызовом подпрограммы, то последующее выполнение будет продолжено внутри (с первой инструкции) вызываемой подпрограммы
next
Выполнить одну строку кода.
Если следующая инструкция является вызовом подпрограммы, то выполнение вызова подпрограммы и возврат из нее осуществляется без остановок.
list
Отобразить несколько строк вокруг текущего положения в исходном тексте.
Практически, более удобно иметь отдельное окно редактирования, в котором открыт соответствующий файл с исходным текстом.
Последующее исполнение этой команды отобразит несколько последующих строк исходного текста.
Эта команда может принимать номер строки в качестве аргумента.
В этом случае она отобразит несколько строк исходного текста вокруг указанной строки.
backtrace
Отобразить обратную трассировку цепочки вызовов.
Обычно эта команда используется для отображения последовательности вызовов, которые привели в текущую точку прерывания.
Отображение содержит по одной строке для каждой записи активации (кадр стека) соответствующей активной подпрограмме.
up
При остановке выполнения программы в точке прерывания, GDB способен отобразить значения переменных, которые локальны для текущего кадра стека (иначе - уровень вложения вызовов).
Команда up может быть использована для анализа содержимого других активных кадров стека, перемещаясь на один кадр стека вверх (от кадра стека вызванной подпрограммы к кадру стека вызвавшей подпрограммы).
down
Выполняет перемещение на один кадр стека вниз.
Действие этой команды противоположно действию команды up.
frame n
Переместиться в кадр стека с номером n.
Значение 0 указывает на кадр стека, который соответствует положению текущей точки прерывания, то есть, вершине стека вызовов.
Показанный выше список команд GDB является ознакомительным, и он очень краток.
О дополнительных возможностях, которыми являются условные точки прерывания, способность исполнения последовательности команд в точке прерывания, способность выполнять отладку программы на уровне команд процессора, и многих других полезных свойствах отладчика GDB можно узнать из руководства "Отладка с помощью GDB" (Debugging with GDB).
Примечательно, что многие основные команды имеют сокращенные аббривиатуры.
Например, c - для continue
bt - для backtrace.