gcc
gnatbind
gnatlink
gnatmake
gnatchop
gnatf
gnatkr
gnatprep
gnatls
(C) Copyright 1995-1997, Ada Core Technologies, Inc.
GNAT is free software; you can redistribute it and/or modify it under terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNAT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNAT; see file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
This guide describes the use of GNAT, the full language compiler for the Ada 95 programming language. It describes the features of the compiler, and describes how you can use it to build Ada 95 applications.
This guide contains the following chapters:
gcc
, describes how to compile
Ada programs with gcc
, the Ada compiler.
gnatbind
, describes how to
perform binding in Ada programs with gnatbind
, the GNAT binding
utility.
gnatlink
,
describes gnatlink
, a
program that provides for linking using the GNAT run-time library to
construct a program. It can also incorporate foreign language object
units.
gnatmake
, describes gnatmake
, a
utility that automatically determines and compiles the set of sources
needed by an Ada compilation unit.
gnatchop
, describes
gnatchop
, a utility that allows you to preprocess a file and
split it into several other files, one for each compilation unit.
gnatf
, discusses
gnatf
, a modified version of the GNAT compiler used to
provide cross-referencing capabilities.
gnatkr
, describes the gnatkr
file name krunching utility, used to deal with handling shortened
file names on systems with a limit on the length of names.
gnatprep
, describes gnatprep
, a
preprocessor utility that allows a single source file to be used to
generate multiple or parametrized source files using macro substitution.
gnatls
, describes gnatls
, a
utility that displays information about automatically determines and
compiles the set of sources needed by an Ada compilation unit.
gnatpsta
and gnatpsys
.
This user's guide assumes that you are familiar with Ada 95 language, as described in the International Standard ANSI/ISO/IEC-8652:1995, Jan 1995.
For further information about related tools, refer to the following documents:
Following are examples of the typographical and graphic conventions used in this guide:
Functions
, utility program names
, standard names
,
and classes
.
and then shown this way.
Commands that are entered by the user are preceded in this manual by the characters "$ " (dollar sign followed by space). If your system uses this sequence as a prompt, then the commands will appear exactly as you see them in the manual. If your system uses some other prompt, then the command will appear with the $ replaced by whatever prompt character you are using.
This chapter describes the usual ways of using GNAT to compile Ada programs.
Three steps are needed to create an executable file from an Ada source file:
All three steps are most commonly handled by using the gnatmake
utility program that, given the name of the main program, automatically
performs the necessary compilation, binding and linking steps.
Any editor may be used to prepare an Ada program. If emacs
is
used, the optional Ada mode may be helpful in laying out the program. The
program text is a normal text file. We will suppose in our initial
example that you have used your editor to prepare the following
standard format text file:
with Text_IO; use Text_IO; procedure Hello is begin Put_Line ("Hello WORLD!"); end Hello;
This file should be named `hello.adb'. Using the normal default file naming conventions, GNAT requires that each file contain a single unit whose file name corresponds to the unit name with periods replaced by hyphens and whose extension is `.ads' for a spec and `.adb' for a body. You can compile the program using the following command:
$ gcc -c hello.adb
gcc
is the command used to access the compiler. This compiler is
capable of compiling programs in several languages including Ada 95 and
C. It determines you have given it an Ada program by the extension
(`.ads' or `.adb'), and will call the GNAT compiler to compile
the specified file.
The -c
switch is required. It tells gcc
to do a
compilation. (For C programs, gcc
can also do linking, but this
capability is not used directly for Ada programs, so the -c
switch must always be present.)
This compile command generates a file
`hello.o' which is the object
file corresponding to your Ada program. It also generates a file
`hello.ali'
which contains additional information used to check
that an Ada program is consistent. To get an executable file,
we then use gnatbind
to bind the program
and gnatlink
to link the program.
$ gnatbind hello.ali $ gnatlink hello.ali
A simpler method of carrying out these steps is to use
gnatmake
, which
is a master program which invokes all of the required
compilation, binding and linking tools in the correct order. In particular,
it automatically recompiles any modified sources, or sources that depend
on modified sources, so that a consistent compilation is ensured.
$ gnatmake hello
The result is an executable program called `hello', which can be run using:
./hello
and, if all has gone well, you will see
Hello WORLD!
appear in response to this command.
Consider a slightly more complicated example that has three files, a main program, and the spec and body of a package:
package Greetings is procedure Hello; procedure Goodbye; end Greetings; with Text_IO; use Text_IO; package body Greetings is procedure Hello is begin Put_Line ("Hello WORLD!"); end Hello; procedure Goodbye is begin Put_Line ("Goodbye WORLD!"); end Goodbye; end Greetings; with Greetings; procedure Gmain is begin Greetings.Hello; Greetings.Goodbye; end Gmain;
Following the one-unit-per-file rule, prepare this program in the following three separate files:
To prepare an executable version of this program, we could use four separate steps to compile bind and link the program as follows:
$ gcc -c gmain.adb $ gcc -c greetings.adb $ gnatbind gmain.ali $ gnatlink gmain.ali
Note that there is no required order of compilation when using GNAT.
In particular it is perfectly fine to compile the main program first.
Also, it is not necessary to compile package specs in the case where
there is a separate body, only the body need be compiled. If you want
to submit these programs to the compiler for semantic checking purposes,
then you use the
-gnatc
switch:
$ gcc -c greetings.ads -gnatc
Although the compilation can be done in separate steps as in the
above example, in practice it is almost always more convenient
to use the gnatmake
capability. All you need to know in this case
is the main program name, and the effect of the above four commands
can be achieved in a single command:
$ gnatmake Gmain
In the next section we discuss the advantages of using gnatmake
in
more detail.
gnatmake
UtilityAs you work on a program, you keep track of which units you modify and make sure you not only recompile these units, but also any units that depend on units you have modified. For example, in the preceding case, if you edit `gmain.adb', you only need recompile that file. But if you edit `greetings.ads', you must recompile both `greetings.adb' and `gmain.adb', because both files contain units that depend on `greetings.ads'.
gnatbind
will warn you if you forget one of these compilation
steps, so it is never possible to generate an inconsistent program as a
result of forgetting to do a compilation, but it can be annoying to keep
track of the dependencies. One approach would be to use a
make file, but the trouble with make files is that the
dependencies may change as you change the program, and you must make
sure that the make file is kept up to date manually, an error-prone
process.
The gnatmake
utility takes care of these details automatically.
Invoke it using one of the following forms:
$ gnatmake gmain.adb $ gnatmake Gmain
The argument is the file containing the main program or alternatively
the name of the main unit. gnatmake
examines the environment, automatically recompiles any files that need
recompiling, and binds and links the resulting set of object files,
generating the executable file, `gmain'.
In a large program, it
can be extremely helpful to use gnatmake
, because working out by hand
what needs to be recompiled can be difficult.
Note that gnatmake
takes into account all the intricate rules in Ada 95
for determining dependencies. These include paying attention to inlining
dependencies and generic instantiation dependencies. Unlike some other
Ada make tools, gnatmake
does not rely on the dependencies that were
found by the compiler on a previous compilation, which may possibly
be wrong due to source changes. It works out the exact set of
dependencies from scratch each time it is run.
This chapter describes the compilation model used by GNAT. Although similar to that used by other languages, such as C and C++, this model is substantially different from the traditional Ada compilation models, which are based on a library. The model is initially described without reference to this traditional model. If you have not previously used an Ada compiler, you need only read the first part of this chapter. The last section describes and discusses the differences between the GNAT model and the traditional Ada compiler models. If you have used other Ada compilers, you may find this section helps you to understand those differences.
Ada source programs are represented in standard text files, using Latin-1 coding. Latin-1 is ASCII with the additional characters used for representing foreign languages (see section Foreign Language Representation for support of non-USA character sets). The format effector characters are represented using their standard ASCII encodings, as follows:
VT
16#0B#
HT
16#09#
CR
16#0D#
LF
16#0A#
FF
16#0C#
The end of physical lines is marked by any of the following sequences:
LF
, CR
, CR-LF
, or LF-CR
. Standard UNIX-format
files simply use LF
to terminate physical lines. The other
combinations are recognized to provide convenient processing for files
imported from other operating systems.
The end of a source file is normally represented by the physical end of
file. However the control character 16#1A#
(SUB
) is also
represented as signalling the end of the source file. Again, this is
provided for compatibility with other operating systems where this
code is used to represent the end of file.
Each file contains a single Ada compilation unit, including any pragmas associated with the unit. For example, this means you must place a package declaration (a package spec) and the corresponding body in separate files. An Ada compilation (which is a sequence of compilation units) is represented using a sequence of files. Similarly, you will place each subunit or child unit in a separate file.
GNAT supports the standard character sets defined in Ada 95 as well as several other non-standard character sets for use in localized versions of the compiler.
The basic character set is Latin-1. This character set is defined by ISO
standard 8859, part 1. The lower half (character codes 16#00#
... 16#7F#)
is identical to standard ASCII coding, but the upper half is
used to represent additional characters. This includes extended letters
used by European languages, such as the vowels with umlauts used in German,
and the extra letter A-ring used in Swedish.
For a complete list of Latin-1 codes and their encodings, see the source
file of library unit Ada.Characters.Latin_1
in file
`a-chlat1.ads'.
You may use any of these extended characters freely in character or
string literals. In addition, the extended characters that represent
letters can be used in identifiers.
GNAT also supports several other 8-bit coding schemes:
For precise data on the encodings permitted, and the uppercase and lower case equivalences that are recognized, see the file `csets.adb' in the GNAT compiler sources. You will need to obtain a full source release of GNAT to obtain this file.
GNAT allows wide character codes to appear in character and string literals, and also optionally in identifiers, using the following possible encoding schemes:
[ " a b c d " ]Where
a
, b
, c
, d
are the four hexadecimal
characters (using uppercase letters) of the wide character code. For
example, ["A345"] is used to represent the wide character with code
16#A345#
.
This scheme is compatible with use of the full Wide_Character set,
and is also the method used for wide character encoding in the standard
ACVC (Ada Compiler Validation Capability) test suite distributions.
ESC a b c dWhere
a
, b
, c
, d
are the four hexadecimal
characters (using uppercase letters) of the wide character code. For
example, ESC A345 is used to represent the wide character with code
16#A345#
.
This scheme is compatible with use of the full Wide_Character set.
16#abcd#
where the upper bit is on (in
other words, "a" is in the range 8-F) is represented as two bytes,
16#ab#
and 16#cd#
. The second byte may never be a format control
character, but is not required to be in the upper half. This method can
be also used for shift-JIS or EUC, where the internal coding matches the
external coding.
16#ab#
and
16#cd#
, with the restrictions described for upper-half encoding as
described above. The internal character code is the corresponding JIS
character according to the standard algorithm for Shift-JIS
conversion. Only characters defined in the JIS code set table can be
used with this encoding method.
16#ab#
and
16#cd#
, with both characters being in the upper half. The internal
character code is the corresponding JIS character according to the EUC
encoding algorithm. Only characters defined in the JIS code set table
can be used with this encoding method.
Note: Some of these coding schemes do not permit the full use of the Ada 95 character set. For example, neither Shift JIS, nor EUC allow the use of the upper half of the Latin-1 set.
The default file name is determined by the name of the unit the file contains. The name is formed by taking the full expanded name of the unit and replacing the separating dots with hyphens and using lowercase for all letters.
A special exception arises if the file name according to the above rules has one of the characters a,g,i, or s and the second character is a minus. In this case, the character /tilde/dollar sign/ is used in place of the The reason for this special exception is to avoid clashes with the standard names for children of System, Ada, Interfaces, and GNAT, which use the prefixes -s -a -i and -g respectively.
The extension is `.ads' for a spec and `.adb' for a body. The following table shows some examples of these rules.
Following these rules can result in excessively long
file names if corresponding
unit names are long (for example, if child units or subunits are
heavily nested). An option is available to shorten such long file names
(called file name "krunching"). This may be particularly useful when
programs being developed with GNAT are to be used on operating systems
with limited file name lengths. See section Using gnatkr
.
Of course, no file shortening algorithm can guarantee uniqueness over all possible unit names; if file name krunching is used it is your responsibility to ensure no name clashes occur, or alternatively you can specify the exact file names that you want to be used, as described in the next section.
In the previous section, we have described the default rules used by GNAT to determine the file name in which a given unit resides. It is often convenient to follow these default rules, and if you do then the compiler knows without being explicitly told where to find all the files it needs.
However, in some cases, particularly when a program is imported from another Ada compiler environment, it may be more convenient for the programmer to specify which file names are used. GNAT allows arbitrary file names to be used via the Source_File_Name pragma. The form of this pragma is as shown in the following examples:
pragma Source_File_Name (My_Utilities.Stacks, Spec_File_Name => "myutilst_a.ada"); pragma Source_File_name (My_Utilities.Stacks, Body_File_Name => "myutilst.ada");
As shown in this example, the first argument for the pragma is the unit name (in this example a child unit). The second argument must have an identifier which indicates if the file name is for the spec or the body, and the file name itself is given as a static string constant.
The source file name pragma is a configuration pragma, which means that normally it will be placed in the `gnat.adc' file used to hold configuration pragmas that apply to a complete compilation environment. See section Handling of Configuration Pragmas for more details on how the `gnat.adc' file is created and used.
GNAT allows completely arbitrary file names to be specified using the source file name pragma. However, if the file name specified has an extension other than `.ads' `.adb' or `.ada' it is necesary to use a special syntax when compiling the file. The name in this case must be preceded by the special sequence -x followed by a space, as in:
$ gcc -c -x peculiar_file_name.sim
If gnatmake
is used, then it handles non standard file names automatically.
One special case arises if the main unit has a non-standard file name, in
this case, the gnatmake
argument must be this non-standard file name. It
is not possible to use the normal unit name form of the gnatmake
command in this case.
If you want to examine the workings of the GNAT system, the following brief description of its organization may be helpful:
Ada
, as
defined in Annex A.
Interfaces
, as
defined in Annex B.
System
. This includes
both language-defined children and GNAT run-time routines).
GNAT
. These are useful
general-purpose packages, fully documented in their specifications. All
the other `.c' files are modifications of common gcc
files.
An Ada program consists of a set of source files, and the first step in compiling the program is to generate the corresponding object files. These are generated by compiling a subset of these source files. The files you need to compile are the following:
The preceding rules describe the set of files that must be compiled to generate the object files for a program. Each object file has the same name as the corresponding source file, except that the extension is `.o' as usual.
You may wish to compile other files for the purpose of checking syntactic and semantic correctness. For example, in the case where a package has a separate spec and body, you would not normally compile the spec. However, it is convenient in practice to compile the spec to make sure it is correct before compiling clients of this spec, because such compilations will fail if there is an error in the spec.
GNAT provides the option for compiling such files purely for the
purposes of checking correctness; such compilations are not required as
part of the process of building a program. To compile a file in this
checking mode, use the -gnatc
switch.
A given object file clearly depends on the source file which is compiled
to produce it. Here we are using depends in the sense of a typical
make
utility; in other words, an object file depends on a source
file if changes to the source file require the object file to be
recompiled.
In addition to this basic dependency, a given object may depend on
additional source files as follows:
with
's a unit X, the object file
depends on the file containing the spec of unit X. This includes
files that are with
'ed implicitly either because they are parents
of with
'ed child units or they are run-time units required by the
language constructs used in a particular unit.
Inline
applies and inlining is activated with the
-gnatn
switch, the object file depends on the file containing the
body of this subprogram as well as on the file containing the spec.
Similarly if the -gnatN
switch is used, then the unit is
dependent on all body files.
These rules are applied transitively: if unit A
with
's
unit B
, whose elaboration calls an inlined procedure in package
C
, the object file for unit A
will depend on the body of
C
, in file `c.adb'.
The set of dependent files described by these rules includes all the files on which the unit is semantically dependent, as described in the Ada 95 Language Reference Manual. However it is larger because of the inclusion of generic, inline, and subunit dependencies.
An object file must be recreated by recompiling the corresponding source
file if any of the source files on which it depends are modified. For
example, if the make
utility is used to control compilation,
the rule for an Ada object file must mention all the source files on
which the object file depends. The determination of the necessary
recompilations may be done automatically using gnatmake
.
Each compilation actually generates two output files. The first of these is the normal object file with a `.o' extension. The second is a text file containing the dependency information file. It has the same name but with an `.ali' extension. This file is known as the Ada Library Information (ALI) file.
You normally need not be concerned with the contents of this file, but this section is included in case you want to understand how these files are being used. Each ALI file consists of a series of lines of the form:
Key_Character parameter parameter ...
The first two lines in the file identify the library output version and
Standard
version. These are required to be consistent across the
entire set of compilation units in your program.
V "xxxxxxxxxxxxxxxx"
This line indicates the library output version, as defined in `gnatvsn.ads'. It ensures that separate object modules of a program are consistent. It must be changed if anything changes that would affect successful binding of modules compiled separately. Examples of such changes are modifications in the format of the library information described in this package, modifications to calling sequences, or to the way data is represented.
S "xxxxxxxxxxxxxxxx"
This line contains information regarding types declared in packages
Standard
as stored in Gnatvsn.Standard_Version
.
The purpose (on systems where, for example, the size of Integer
can be set by command line switches) is to ensure that all units in a
program are compiled with a consistent set of options.
M type [priority]
This line is present only for a unit that can be a main program.
type is either P
for a parameterless procedure or F
for a function returning a value of integral type. The latter is for
writing a main program that returns an exit status. priority is
present only if there was a valid pragma Priority
in the
corresponding unit to set the main task priority. It is an unsigned
decimal integer.
F x
This line is present if a pragma Float_Representation or Long_Float is used to specify other than the default floating-point format. This option applies only to implementations of GNAT for the Digital Alpha Systems. The character x is 'I' for IEEE_Float, 'G' for VAX_Float with Long_Float using G_Float, and 'D' for VAX_Float for Long_Float with D_Float.
P L=x Q=x T=x
This line is present if the unit uses tasking directly or indirectly, and has one or more valid xxx_Policy pragmas that apply to the unit. The arguments are as follows
L=x (locking policy)
This is present if a valid Locking_Policy pragma applies to the unit. The single character indicates the policy in effect (e.g. `C' for Ceiling_Locking).
Q=x (queuing policy)
This is present if a valid Queuing_Policy pragma applies to the unit. The single character indicates the policy in effect (e.g. `P' for Priority_Queuing).
T=x (task_dispatching policy)
This is present if a valid Task_Dispatching_Policy pragma applies to the unit. The single character indicates the policy in effect (e.g. `F' for FIFO_Within_Priorities).
Following these header lines, a set of information lines appears for each compilation unit that appears in the corresponding object file. In particular, when a package body or subprogram body is compiled there will be two sets of information, one for the spec and one for the body, with the entry for the body appearing first. This is the only case in which a single ALI file contains more than one unit. Note that subunits do not count as compilation units for this purpose, and generate no library information, because they are inlined. The lines for each compilation unit have the following form:
U unit-name source-name version [attributes]
This line identifies the unit to which this section of the library
information file applies. unit-name is the unit name in internal
format, as described in package Uname
, and source-file is
the name of the source file containing the unit.
version is the version given as eight hexadecimal characters with lowercase letters. This value is a hash code that includes contributions from the time stamps of this unit and all its semantically dependent units.
The optional attributes are a series of two-letter codes indicating information about the unit. They give the nature of the unit, and also the information provided by categorization pragmas.
EB
NE
NE
set, depending on whether or not elaboration code is required.
PK
PU
Pure
.
PR
Preelaborate
.
RC
Remote_Call_Interface
.
RT
Remote_Types
.
SP
Shared_Passive
.
SU
The attributes may appear in any order, separated by spaces. Another line in the ALI file has the following form:
W unit-name [source-name lib-name [E] [EA] [ED]]
One of these lines is present for each unit mentioned in an explicit
with
clause by the current unit. unit-name is the unit name
in internal format. source-name is the file name of the file that
must be compiled to compile that unit (usually the file for the body,
except for packages that have no body). lib-name is the file name
of the library information file that contains the results of compiling
the unit. The E
and EA
parameters are present if
pragma Elaborate
or pragma Elaborate_All
, respectively,
apply to this unit. ED
is used to indicate that the compiler
has determined that a pragma Elaborate_All
for this unit would be
desirable. See section Elaboration Order Handling in GNAT for
details on the use of the ED parameter.
Following the unit information is an optional series of lines that
indicate the usage of pragma Linker_Options
. For each appearance of
pragma Linker_Options
in any of the units for which unit lines are
present, a line of the form
L string
appears where string is the string from the pragma enclosed in quotes. Within the quotes, the following can occur:
For further details, see Stringt.Write_String_Table_Entry
in the
file `stringt.ads'. Note that wide characters in the form {hhhh}
cannot be produced, because pragma Linker_Option
accepts only
String
, not Wide_String
.
Finally, at the end of the ALI file is a series of lines that indicate the source files on which the compiled units depend. This is used by the binder for consistency checking and look like:
D source-name time-stamp [comments]
where comments, if present, must be separated from the time stamp by at least one blank. Currently this field is unused.
Blank lines are ignored when the library information is read, and separate sections of the file are separated by blank lines to ease readability. Extra blanks between fields are also ignored.
All compiled units are marked with a time stamp, which is derived from The source file. The binder uses these time stamps to ensure consistency of the set of units that constitutes a single program. Time stamps are twelve-character strings of the form YYMMDDHHMMSS. Each two-character field has the following meaning:
YY
MM
DD
HH
MM
SS
Time stamps may be compared lexicographically (in other words, the order of Ada comparison operations on strings) to determine which is later or earlier. However, in normal mode, only equality comparisons have any effect on the semantics of the library. Later/earlier comparisons are used only for determining the most informative error messages to be issued by the binder. Note that this means that despite the fact that only two digits are used for the year, there are no "year 2000" problems with this representation choice.
The time stamp is the actual stamp stored with the file without any
adjustment resulting from time zone comparisons. This avoids problems in
using libraries across networks with clients spread across multiple time
zones, but may mean the time stamp will differ from that displayed in a
directory listing. For example, in UNIX systems,
file time stamps are stored in Greenwich Mean Time (GMT), but the
ls
command displays local times.
When using languages such as C and C++, the only remaining step in building an executable program once the source files have been compiled is linking the object modules together. This means it is possible to link an inconsistent version of a program in which two units have included different versions of the same header.
The rules in Ada do not permit such an inconsistent program to be built. For example, if two clients have different versions of the same package, it is not possible to build a program containing these two clients. These rules are enforced by the GNAT binder, which also determines an elaboration order consistent with the Ada rules.
The GNAT binder is run after all the object files for a program have been compiled. It is given the name of the main program unit, and from this it determines the set of units required by the program, reading the corresponding ALI files. It generates error messages if the program is inconsistent or if no valid order of elaboration exists.
If no errors are detected, the binder produces a main program, in C,
that contains calls to the required elaboration procedures, followed by
a call to the main program. This C program is compiled using the C
compiler to generate the object file for the main program. The name of
the C file is b_xxx.c
where xxx is the name of the
main program unit.
Finally, the linker is used to build the resulting executable program, using the object from the main program from the bind step as well as the object files for the Ada units of the program.
You build a program that contains some Ada files and some other language files in one of two ways, depending on whether the main program is in Ada or not. If the main program is in Ada, you proceed as follows:
If the main program is in some language other than Ada, you use a special option of the binder to generate callable routines to initialize and finalize the Ada units. You must insert calls to these routines in the main program, or some other appropriate point. The call to initialize the Ada units must occur before the first Ada subprogram is called, and the call to finalize the Ada units must occur after the last Ada subprogram returns. You use the same procedure for building the program as described previously. In this case, however, the binder places the initialization and finalization subprograms into file `b_xxx.c' instead of the main program.
The GNAT model of compilation is close to the C and C++ models. You can
think of Ada specs as corresponding to header files in C. As in C, you
don't need to compile specs; they are compiled when they are used. The
Ada with
is similar in effect to the #include
of a C
header.
One notable difference is that, in Ada, you may compile specs separately to check them for semantic and syntactic accuracy. This is not always possible with C headers because they are fragments of programs that have no specific syntactic or semantic rules.
The other major difference is the requirement for running the binder, which performs two important functions. First, it checks for consistency. In C or C++, the only defense against putting together inconsistent programs is outside the compiler, in a make file, for example. The binder satisfies the Ada requirement that it be impossible to construct an inconsistent program when the compiler is used in normal mode.
The other important function of the binder is to deal with elaboration
issues. There are also elaboration issues in C++ that are handled
automatically. This automatic handling has the advantage of being
simpler to use, but the C++ programmer has no control over elaboration.
Where gnatbind
might complain there was no valid order of
elaboration, a C++ compiler would simply construct a program that
malfunctioned at run time.
This section is intended to be useful to Ada programmers who have previously used an Ada compiler implementing the traditional Ada library model, as described in the Ada 95 Languages Reference Manual. If you have not used such a system, please go on to the next section.
In GNAT, there no library in the normal sense. Instead, the set of source files themselves acts as the library. Compiling Ada programs does not generate any centralized information, but rather an object file and a ALI file, which are of interest only to the binder and linker. In a traditional system, the compiler reads information not only from the source file being compiled, but also from the centralized library. This means that the effect of a compilation depends on what has been previously compiled. In particular:
with
'ed, the unit seen by the compiler corresponds
to the version of the unit most recently compiled into the library.
In GNAT, compiling one unit never affects the compilation of any other units because the compiler reads only source files. Only changes to source files can affect the results of a compilation. In particular:
with
'ed, the unit seen by the compiler corresponds
to the source version of the unit that is currently accessible to the
compiler.
The important result of these differences are that order of compilation is never significant in GNAT. There is no situation in which you are required to do one compilation before another. What shows up as order of compilation requirements in the traditional Ada library becomes, in GNAT, simple source dependencies; in other words, it shows up as a set of rules saying what source files must be present when a file is compiled.
gcc
This chapter discusses how to compile Ada programs and the set of switches that can be used to control the behavior of the GNAT compiler. to the compiler.
The first step in creating an executable program is to compile the units
of the program using the gcc
command. You must compile the
following files:
You need not compile the following files
because they are compiled as part of compiling related units. GNAT compiles generic units when a client instantiates the generic, specs when the corresponding body is compiled, and subunits when the parent is compiled. If you attempt to compile any of these files, you will get this error message:
No code generated for unit xxx in file yyy
The basic command for compiling a file containing an Ada unit is
$ gcc -c [switches] `file name'
where file name is the name of the Ada file (usually
having an extension
`.ads' for a spec or `.adb' for a body).
You specify the
-c
switch to tell gcc
to compile, but not link, the file.
The result of a successful compilation is an object file, which has the
same name as the source file but an extension of `.o' and an Ada
Library Information (ALI) file, which also has the same name as the
source file, but with `.ali' as the extension. GNAT creates these
two output files in the current directory, but you may specify a source
file in any directory using an absolute or relative path specification
containing the directory information.
gcc
is actually a driver program that looks at the extensions of
the file arguments and loads the appropriate compiler. For example, the
GNU C compiler is `cc1', and the Ada compiler is `gnat1'.
These programs are in directories known to the driver program (in some
configurations via environment variables you set), but need not be in
your path. The gcc
driver also calls the assembler and any other
utilities needed to complete the generation of the required object
files.
It is possible to supply several file names on the same gcc
command. This causes gcc
to call the appropriate compiler for
each file. For example, the following command lists three separate
files to be compiled:
$ gcc -c x.adb y.adb z.c
calls gnat1
(the Ada compiler) twice to compile `x.adb' and
`y.adb', and cc1
(the C compiler) once to compile `z.c'.
The compiler generates three object files `x.o', `y.o' and
`z.o' and the two ALI files `x.ali' and `y.ali' from the
Ada compilations. Any switches apply to all the files listed,
except for
-gnatx
switches, which apply only to Ada compilations.
gcc
The gcc
command accepts numerous switches to control the
compilation process, which are fully described in this section.
-b target
-Bdir
gnat1
, the Ada compiler)
from dir instead of the default location. Only use this switch
when multiple versions of the GNAT compiler are available. See the
gcc
manual page for further details. You would normally use the
-b
or -V
switch instead.
-c
gcc
without a -c
switch to
compile and link in one step. This is because the binder must be run,
and currently gcc
cannot be used to run the GNAT binder.
-g
-g
switch
if you plan on using the debugger.
-Idir
-I-
-o file
gcc
to redirect the generated object file
and its associated ALI file. Beware of this switch with GNAT, because it may
cause the object file and ALI file to have different names which in turn
may confuse the binder and the linker.
-O[n]
-O
appears
-O
without
an operand.
-S
-c
to
cause the assembler source file to be
generated, using `.s' as the extension,
instead of the object file.
This may be useful if you need to examine the generated assembly code.
-v
gcc
driver. Normally used only for
debugging purposes or if you need to be sure what version of the
compiler you are executing.
-V ver
gcc
version, not the GNAT version.
-Wuninitialized
-O
switch (in other words, This switch works only if
optimization is turned on).
-gnata
Pragma Assert
and pragma Debug
to be
activated.
-gnatb
stderr
even if verbose mode set.
-gnatc
-gnate
-gnatE
-gnatf
-gnatg
-gnatic
-gnatje
-gnatkn
k
= krunch).
-gnatl
-gnatmn
-gnatn
inline
is specified.
-gnatN
inline
is specified. This is equivalent
to using -gnatn
and adding a pragma inline
for every
subprogram in the program.
-fno-inline
-gnato
-gnatp
-gnatq
-gnatr
-gnats
-gnatt
-gnatu
-gnatv
stdout
.
-gnatwm
s,e,l
for suppress, treat as error, elaboration
warnings).
-gnatzm
-gnat83
-gnat95
You may combine a sequence of GNAT switches into a single switch. For example, the specifying the switch
-gnatcfi3
is equivalent to specifying the following sequence of switches:
-gnatc -gnatf -gnati3
The standard default format for error messages is called "brief format."
Brief format messages are written to stdout
(the standard output
file) and have the following form:
e.adb:3:04: Incorrect spelling of keyword "function" e.adb:4:20: ";" should be "is"
The first integer after the file name is the line number and the second
integer is the column number. emacs
can parse the error messages
and point to the referenced character.
The following switches allow control over the error message
format:
-gnatv
stdout
. The same program compiled with the
-gnatv
switch would generate:
3. funcion X (Q : Integer) | >>> Incorrect spelling of keyword "function" 4. return Integer; | >>> ";" should be "is"The vertical bar indicates the location of the error, and the `>>>' prefix can be used to search for error messages. When this switch is used the only source lines output are those with errors.
-gnatl
l
stands for list.
This switch causes a full listing of
the file to be generated. The output is as follows:
1. procedure E is 2. V : Integer; 3. function X (Q : Integer) | >>> Incorrect spelling of keyword "function" 4. return Integer; | >>> ";" should be "is" 5. begin 6. return Q + Q; 7. end; 8. begin 9. V := X + X; 10.end E;When you specify the
-gnatv
or -gnatl
switches and
standard output is redirected, a brief summary is written to
stderr
(standard error) giving the number of error messages and
warning messages generated.
-gnatb
b
stands for brief.
This switch causes GNAT to generate the
brief format error messages to stdout
as well as the verbose
format message or full listing.
-gnatmn
m
stands for maximum.
n is a decimal integer in the
range of 1 to 999 and limits the number of error messages to be
generated. For example, using -gnatm2
might yield
e.adb:3:04: Incorrect spelling of keyword "function" e.adb:5:35: missing ".." fatal error: maximum errors reached compilation abandoned
-gnatf
f
stands for full.
Normally, the compiler suppresses error messages that are likely to be
redundant. This switch causes all error
messages to be generated. One particular effect is for the case of
references to undefined variables. If a given variable is referenced
several times, the normal format of messages is
e.adb:7:07: "V" is undefined (more references follow)where the parenthetical comment warns that there are additional references to the variable
V
. Compiling the same program with the
-gnatf
switch yields
e.adb:7:07: "V" is undefined e.adb:8:07: "V" is undefined e.adb:8:12: "V" is undefined e.adb:8:16: "V" is undefined e.adb:9:07: "V" is undefined e.adb:9:12: "V" is undefined
-gnatq
q
stands for quit (really "don't quit").
In normal operation mode the compiler first parses the program and
determines if there are any syntax errors. If there are, appropriate
error messages are generated and compilation is immediately terminated.
This switch tells
GNAT to continue with semantic analysis even if syntax errors have been
found. This may enable the detection of more errors in a single run. On
the other hand, the semantic analyzer is more likely to encounter some
internal fatal error when given a syntactically invalid tree.
-gnate
-gnate
usually causes error messages to be
generated out of sequence. Use it when the compiler blows up due to an
internal error. In this case, the error messages may be lost. Sometimes
blowups are the result of mishandled error messages, so you may want to
run with the -gnate
switch to determine whether any error
messages were generated (see section GNAT Crashes).
In addition to error messages, corresponding to illegalities as defined in the reference manual, the compiler detects two kinds of warning situations.
First, the compiler considers some constructs suspicious and generates a warning message to alert you to a possible error. Second, if the compiler detects a situation that is sure to raise an exception at run time, it generates a warning message. The following shows an example of warning messages:
e.adb:4:24: warning: creation of object may raise Storage_Error e.adb:10:17: warning: static value out of range e.adb:10:17: warning: "Constraint_Error" will be raised at run time
GNAT detects a large number of situations which it considers appropriate
for the generation of warning messages. As always, warnings are not
definite indications of errors. For example, if you do an out of range
assignment with the deliberate intention of raising a
Constraint_Error
exception, then the warning that may be
issued does not indicate an error. Some of the situations that GNAT
issues warnings for (at least some of the time) are:
Three switches are available to control the handling of warning messages:
-gnatwe
w
stands for warning and the e
stands for error.
The
This switch causes warning messages to be treated as errors.
The warning string still appears, but the warning messages are counted
as errors, and prevent the generation of an object file.
-gnatws
-gnatwl
-gnata
Assert
and Debug
normally have no effect and
are ignored. This switch, where `a' stands for assert, causes
Assert
and Debug
pragmas to be activated.
The pragmas have the form:
pragma Assert (Boolean-expression [, static-string-expression]) pragma Debug (procedure call)The
Assert
pragma causes Boolean-expression to be tested.
If the result is True
, the pragma has no effect (other than
possible side effects from evaluating the expression). If the result is
False
, the exception Assert_Error
declared in the package
System.Assertions
is
raised (passing static-string-expression, if present, as the
message associated with the exception). If no string expression is
given the default is a string giving the file name and line number
of the pragma.
The Debug
pragma causes procedure to be called. Note that
pragma Debug
may appear within a declaration sequence, allowing
debugging procedures to be called between declarations.
If you compile with the default options, GNAT will insert many run-time
checks into the compiled code, including code that performs range
checking against constraints, but not arithmetic overflow checking for
integer operations (including division by zero) or checks for access
before elaboration on subprogram calls. All other run-time checks, as
required by the Ada 95 Reference Manual, are generated by default.
The following gcc
switches refine this default behavior:
-gnatp
pragma Suppress
(all_checks
) in your source. Use this switch to improve the performance
of the code at the expense of safety in the presence of invalid data or
program bugs.
-gnato
Constraint_Error
as required by Ada
semantics).
Note that the -gnato
switch does not affect the code generated
for any floating-point operations; it applies only to integer
operations. For floating-point, GNAT has the Machine_Overflows
attribute set to False
and the normal mode of operation is to
generate IEEE NaN and infinite values on overflow or invalid operations
(such as dividing 0.0 by 0.0).
-gnatE
gcc
.
The setting of these switches only controls the default setting of the
checks. You may modify them using either Suppress
(to remove
checks) or Unsuppress
(to add back suppressed checks) pragmas in
the program source.
gcc
for Syntax Checking-gnats
s
stands for syntax.
Run GNAT in syntax checking only mode. For
example, the command
$ gcc -c -gnats x.adbcompiles file `x.adb' in syntax-check-only mode. You can check a series of files in a single command, and can use wild cards to specify such a group of files. Note that you must specify the
-c
(compile
only) flag in addition to the -gnats
flag.
You may use other switches in conjunction with -gnats
. In
particular, -gnatl
and -gnatv
are useful to control the
format of any generated error messages.
The output is simply the error messages, if any. No object file or ALI
file is generated by a syntax-only compilation. Also, no units other
than the one specified are accessed. For example, if a unit X
with
's a unit Y
, compiling unit X
in syntax
check only mode does not access the source file containing unit
Y
.
Normally, GNAT allows only a single unit in a source file. However, this
restriction does not apply in syntax-check-only mode, and it is possible
to check a file containing multiple compilation units concatenated
together. This is primarily used by the gnatchop
utility
(see section Renaming Files Using gnatchop
).
gcc
for Semantic Checking-gnatc
c
stands for check.
Cause the compiler to operate in semantic check mode,
with full checking for all illegalities specified in the
reference manual, but without generation of any source code (no object
or ALI file generated).
Because dependent files must be accessed, you must follow the GNAT
semantic restrictions on file structuring to operate in this mode:
-gnat83
-gnat83
switch can be ported reasonably easily to an Ada 83
compiler. This is the main use of the switch.
With few exceptions (most notably the need to use <>
on
unconstrained generic formal parameters), it is not necessary to use the
-gnat83
switch when compiling Ada 83 programs, because, with rare
and obscure exceptions, Ada 95 is upwardly compatible with Ada 83. This
means that a correct Ada 83 program is usually also a correct Ada 95
program.
-gnat95
-gnatr
else
must line up with an if
and code in the then
and
else
parts must be indented. The compile considers violations of
the layout rules a syntax error if you specify this switch.
-gnatg
-gnatg
switch specified.
You can find the full documentation for the style conventions imposed by
-gnatg
in the body of the package Style
in the
compiler sources (in the file `style.adb').
You should not normally use the -gnatg
switch. However, you
must use -gnatg
for compiling any language-defined unit,
or for adding children to any language-defined unit other than
Standard
.
-gnatic
1
2
3
4
p
8
f
n
w
-gnatje
n
h
u
s
e
-gnatkn
-gnatn
n
here is intended to suggest the first syllable of the
word "inline".
GNAT recognizes and processes Inline
pragmas. However, for the
inlining to actually occur, optimization must be enabled. To enable
inlining across unit boundaries, this is, inlining a call in one unit of
a subprogram declared in a with
'ed unit, you must also specify
this switch.
In the absence of this switch, GNAT does not attempt
inlining across units and does not need to access the bodies of
subprograms for which pragma Inline
is specified if they are not
in the current unit.
If you specify this the compiler will access these bodies,
creating an extra source dependency for the resulting object file, and
where possible, the call will be inlined. See section Inlining of Subprograms for further details on when inlining is possible.
-gnatN
Inline
for every subprogram referenced by the compiled
unit.
-gnatt
-gnatu
stdout
.
The listing includes all units on which the unit being compiled depends
either directly or indirectly.
-gnatdx
Debug
unit in the compiler source
file `debug.adb'.
One switch you may wish to use is -gnatdg
, which causes a listing
of the generated code in Ada source form. For example, all tasking
constructs are reduced to appropriate run-time library calls. The syntax
of this listing is close to normal Ada with the following additions:
new xxx [storage_pool = yyy]
at end procedure-name;
(if expr then expr else expr)
x?y:z
construction in C.
target^(source)
target?(source)
target?^(source)
x #/ y
x #mod y
x #* y
x #rem y
free expr [storage_pool = xxx]
free
statement.
freeze typename [actions]
reference itype
function-name! (arg, arg, arg)
labelname : label
expr && expr && expr ... && expr
[constraint_error]
Constraint_Error
exception.
expression'reference
target-type!(source-expression)
[numerator/denominator]
With the GNAT source-based library system, the compiler must be able to find source files for units that are needed by the unit being compiled. Search paths are used to guide this process.
The compiler compiles one source file whose name must be given explicitly on the command line. In other words, no searching is done for this file. To find all other source files that are needed (the most common being the specs of units), the compiler looks in the following directories, in the following order:
-I
switch given on the gcc
command line, in the order given.
ADA_INCLUDE_PATH
environment variable. Construct this value
exactly as the PATH
environment variable: a list of directory
names separated by colons.
Specifying the switch -I-
inhibits the use of the directory
containing the source file named in the command line. You can still
have this directory on your search path, but in this case it must be
explicitely requested with a -I
switch.
The compiler outputs its object files and ALI files in the current
working directory.
Caution: The object file can be redirected with the -o
switch;
however, gcc
and gnat1
have not been coordinated on this
so the ALI file will not go to the right place. Therefore, you should
avoid using the -o
switch.
The packages Ada
, System
, and Interfaces
and their
children make up the GNAT RTL, together with the simple System.IO
package used in the "Hello World" example. The sources for these units
are needed by the compiler and are kept together in one directory. Not
all of the bodies are needed, but all of the sources are kept together
anyway. In a normal installation, you need not specify these directory
names when compiling or binding. Either the environment variables or
the built-in defaults cause these files to be found.
In addition to the language-defined hierarchies (System, Ada and Interfaces), the GNAT distribution provides a fourth hierarchy, consisting of child units of GNAT. This is a collection of generally useful routines. See the GNAT reference manual for further details.
Besides the assistance in using the RTL, a major use of search paths is in compiling sources from multiple directories. This can make development environments much more flexible.
If, in our earlier example, there was a spec for the hello
procedure, it would be contained in the file `hello.ads'; yet this
file would not need to be explicitly compiled. This is the result of the
model we chose to implement library management. Some of the consequences
of this model are as follows:
with
's, all its subunits, and the bodies of any generics it
instantiates must be available (findable by the search-paths mechanism
described above), or you will receive a fatal error message.
The following are some typical Ada compilation command line examples:
$ gcc -c xyz.adb
$ gcc -c -O2 -gnata xyz-def.adb
Assert
/Debug
statements
enabled.
$ gcc -c -gnatc abc-def.adb
gnatbind
This chapter describes the GNAT binder, gnatbind
, which is used
to bind compiled GNAT objects. The gnatbind
program performs
four separate functions:
gnatlink
utility used to link the Ada application.
gnatbind
The form of the gnatbind
command is
$ gnatbind [switches] mainprog.ali [switches]
where mainprog.adb is the Ada file containing the main program
unit body. If no switches are specified, gnatbind
constructs a C
file whose name is `b_mainprog.c'. For example, if given the
parameter `hello.ali', for a main program contained in file
`hello.adb', the binder output file would be `b_hello.c'.
When doing consistency checking, the binder takes any source files it
can locate into consideration. For example, if the binder determines
that the given main program requires the package Pack
, whose ALI
file is `pack.ali' and whose corresponding source spec file is
`pack.ads', it attempts to locate the source file `pack.ads'
(using the same search path conventions as previously described for the
gcc
command). If it can located this source file, the time stamps
or source checksums
must match. In other words, any ALI files mentioning this spec must have
resulted from compiling this version of the source file (or in the case
where the source checksums match, a version close enough that the
difference does not matter).
The effect of this consistency checking, which includes source files, is that the binder ensures that the program is consistent with the latest version of the source files that can be located at bind time. Editing a source file without compiling files that depend on the source file cause error messages to be generated from the binder.
For example, suppose you have a main program `hello.adb' and a
package P
, from file `p.ads' and you perform the following
steps:
gcc -c hello.adb
to compile the main program.
gcc -c p.ads
to compile package P
.
gnatbind hello.ali
.
At this point, the file `p.ali' contains an out-of-date time stamp because the file `p.ads' has been edited. The attempt at binding fails, and the binder generates the following error messages:
error: "hello.adb" must be recompiled ("p.ads" has been modified) error: "p.ads" has been modified and must be recompiled
Now both files must be recompiled as indicated, and then the bind can succeed, generating a main program. You need not normally be concerned with the contents of this file, but it is similar to the following:
extern int gnat_argc; extern char **gnat_argv; extern char **gnat_envp; extern int gnat_exit_status; void adafinal (); void adainit () { __gnat_set_globals ( -1, /* Main_Priority */ -1, /* Time_Slice_Value */ ' ', /* Locking_Policy */ ' ', /* Queuing_Policy */ ' ', /* Tasking_Dispatching_Policy */ adafinal); system___elabs (); /* system__standard_library___elabs (); */ /* system__task_specific_data___elabs (); */ /* system__tasking_soft_links___elabs (); */ system__tasking_soft_links___elabb (); /* system__task_specific_data___elabb (); */ /* system__standard_library___elabb (); */ /* m___elabb (); */ } void adafinal () { } int main (argc, argv, envp) int argc; char **argv; char **envp; { gnat_argc = argc; gnat_argv = argv; gnat_envp = envp; __gnat_initialize(); adainit(); _ada_m (); adafinal(); __gnat_finalize(); exit (gnat_exit_status); } unsigned mB = 0x2B0EB17F; unsigned system__standard_libraryB = 0x0122ED49; unsigned system__standard_libraryS = 0x79B018CE; unsigned systemS = 0x08FBDA7E; unsigned system__task_specific_dataB = 0x6CC7367B; unsigned system__task_specific_dataS = 0x47178527; unsigned system__tasking_soft_linksB = 0x5A75A73C; unsigned system__tasking_soft_linksS = 0x3012AFCB; /* BEGIN Object file/option list ./system.o ./s-tasoli.o ./s-taspda.o ./s-stalib.o ./m.o END Object file/option list */
The call to __gnat_set_globals
establishes program parameters,
including the priority of the main task, and parameters for tasking
control. It also passes the address of the finalization routine so
that it can be called at the end of the program.
Next there is code to save the argc
and argv
values for
later access by the Ada.Command_Line
package. The variable
gnat_exit_status
saves the exit status set by calls to
Ada.Command_Line.Set_Exit_Status
and is used to return an exit
status to the system.
The call to __gnat_initialize
and the corresponding call at the
end of execution to __gnat_finalize
allow any specialized
initialization and finalization code to be hooked in. The default
versions of these routines do nothing.
The calls to xxx___elabb
and
xxx___elabs
perform necessary elaboration of the bodies and
specs respectively of units in the program. These calls are commented
out if the unit in question has no elaboration code.
The call to
m
is the call to the main program.
The list of unsigned constants gives the version number information.
Version numbers are computed by combining time stamps of a unit and all
units on which it depends. These values are used for implementation of
the Version
and Body_Version
attributes.
Finally, a set of comments gives full names of all the object files required to be linked for the Ada component of the program. As seen in the previous example, this list includes the files explicitly supplied and referenced by the user as well as implicitly referenced run-time unit files. The latter are omitted if the corresponding units reside in shared libraries. The directory names for the run-time units depend on the system configuration.
As described in the previous section, by default gnatbind
checks
that object files are consistent with one another and are consistent
with any source files it can locate. The following switches to control
access to sources.
-s
-x
gnatmake
because in this
case the checking against sources has already been performed by
gnatmake
.
The following switches provide control over the generation of error messages from the binder:
-v
stderr
. If this switch is present, a header is written
to stdout
and any error messages are directed to stdout
.
All that is written to stderr
is a brief summary message.
-b
stderr
even if verbose mode is
specified. This is relevant only when used with the
-v
switch.
-mn
-r
main
to gnat_main
.
This is useful in the case of some cross-building environments, where
the actual main program is separate from the one generated
by gnatbind
.
-ws
-we
-t
-t
should be used only in unusual situations, with extreme care.
The following switches provide additional control over the elaboration order. See section Elaboration Order Handling in GNAT for full details.
-f
Elaborate_All
pragmas, and to use full reference
manual semantics in an attempt to find a legal elaboration order,
even if it seems likely that this order will cause an elaboration
exception.
-h
Program_Error
exception. This switch reverses the
action of the binder, and requests that it deliberately choose an order
that is likely to maximize the likeihood of an elaboration errror
The following switches allow additional control over the output generated by the binder.
-e
stdout
.
-l
stdout
.
-o file
-c
In our description in this chapter so far we have assumed the main
program is in Ada and the task of the binder is to generate a
corresponding function main
to pass control to this Ada main
program. GNAT also supports the building of executable programs where
the main program is not in Ada, but some of the called routines are
written in Ada and compiled using GNAT. The following switch is used in
this situation:
-n
In this case, most of the functions of the binder are still required, but instead of generating a main program, the binder generates a file containing the following callable routines:
adainit
adainit
is
required before the first call to an Ada subprogram.
adafinal
adafinal
is required
after the last call to an Ada subprogram, and before the program
terminates.
If the -n
switch
is given, more than one ALI file may appear on
the command line for gnatbind
. The normal closure
calculation is performed for each of the specified units. Calculating
the closure means finding out the set of units involved by tracing
with
references. The reason it is necessary to be able to
specify more than one ALI file is that a given program may invoke two or
more quite separate groups of Ada subprograms.
The binder takes the name of its output file from the first specified
ALI file, unless overridden by the use of the
-o file
, The output file is
a C source file, which must be compiled using the C compiler.
This compilation occurs automatically as part of the gnatmake
processing.
The following are the switches available with gnatbind
:
-b
stderr
even if verbose mode set.
-c
-e
-aI
-aO
-I
-I-
gnatbind
was
invoked, and do not look for ALI files in the directory containing the
ALI file named in the gnatbind
command line.
-l
-mn
-n
-o file
-s
-t
-v
stdout
.
-wx
-x
You may obtain this listing by running the program gnatbind
with
no arguments.
The package Ada.Command_Line
provides access to the command-line
arguments and program name. In order for this interface to operate
correctly, the two variables
int gnat_argc; char **gnat_argv;
are declared in one of the GNAT library routines. These variables must
be set from the actual argc
and argv
values passed to the
main program. With no n
present, gnatbind
generates the C main program to automatically set these variables.
If the n
switch is used, there is no automatic way to
set these variables. If they are not set, the procedures in
Ada.Command_Line
will not be available, and any attempt to use
them will raise Constraint_Error
. If command line access is
required, your main program must set gnat_argc
and
gnat_argv
from the argc
and argv
values passed to
it.
gnatbind
The binder takes the name of an ALI file as its argument and needs to locate source files as well as other ALI files to verify object consistency.
For source files, it follows exactly the same search rules as gcc
(see section Search Paths and the Run-Time Library (RTL)). For ALI files the
directories searched are:
-I-
is specified.
-I
switches on the gnatbind
command line, in the order given.
ADA_OBJECTS_PATH
environment variable. Construct this value the
same as the PATH
environment variable: a list of directory names
separated by colons.
In the binder the switch -I
is used to specify both source and
library file paths. Use -aI
instead if you just want to specify
source paths only and -aO
if you want to specify libary paths
only. This means that for the binder
-I
dir is equivalent to
-aI
dir
\-aO\/OBJECT_SEARCH=
dir.
The binder generates the bind file (a C language source file) in the
current working directory.
The packages Ada
, System
, and Interfaces
and their
children make up the GNAT Run-Time Library, together with the package
GNAT and its children which contain a set of useful additional
library functions provided by GNAT. The sources for these units are
needed by the compiler and are kept together in one directory. The ALI
files and object files generated by compiling the RTL are needed by the
binder and the linker and are kept together in one directory, typically
different from the directory containing the sources. In a normal
installation, you need not specify these directory names when compiling
or binding. Either the environment variables or the built-in defaults
cause these files to be found.
Besides the assistance in using the RTL, a major use of search paths is in compiling sources from multiple directories. This can make development environments much more flexible.
gnatbind
Usage
This section contains a number of examples of using the GNAT binding
utility gnatbind
.
gnatbind hello.ali
Hello
(source program in `hello.adb') is
bound using the standard switch settings. The generated main program is
`b_hello.c'. This is the normal, default use of the binder.
gnatbind main.ali -o mainprog.c -x -e
Main
(source program in
`main.adb') is bound, excluding source files from the
consistency checking, generating
the file `mainprog.c'.
gnatbind -x main_program.ali -o mainprog.c
gnatbind -n math.ali dbase.ali -o ada-control.c
Math
and Dbase
appear. This call
to gnatbind
generates the file `control.c' containing
the adainit
and adafinal
routines to be called before and
after accessing the Ada subprograms.
gnatlink
This chapter discusses gnatlink
, a utility program used to link
Ada programs and build an executable file. This program is basically a
simple process which invokes the UNIX linker (via the gcc
command) with a correct list of object files and library references.
gnatlink
automatically determines the list of files and
references for the Ada part of a program. It uses the binder file
generated by the binder to determine this list.
gnatlink
The form of the gnatlink
command is
gnatlink [switches] mainprog[.ali] [non-Ada objects] [linker options]
`mainprog.ali' references the ALI file of the main program.
The `.ali' extension of this file can be omitted. From this
reference, gnatlink
locates the corresponding binder file
`b_mainprog.c' and, using the information in this file along
with the list of non-Ada objects and linker options, constructs a UNIX
linker command file to create the executable.
The arguments following `mainprog.ali' are passed to the
linker uninterpreted. They typically include the names of object files
for units written in other languages than Ada and any library references
required to resolve references in any of these foreign language units,
or in pragma Import
statements in any Ada units. This list may
also include linker switches.
gnatlink
determines the list of objects required by the Ada
program and prepends them to the list of objects passed to the linker.
gnatlink
also gathers any arguments set by the use of
pragma Linker_Options
and adds them to the list of arguments
presented to the linker.
gnatlink
The following switches are available with the gnatlink
utility:
-o exec-name
gnatlink try.ali
creates
an executable called `try'.
-v
-g
-g
.
In addition, the binder does not delete the `b_mainprog.c'
and `b_mainprog.o' files. Without -g
, the binder
removes these files by default.
-gnatlink name
gnatmake
A typical development cycle when working on an Ada program consists of the following steps:
The third step can be tricky, because not only do the modified files
have to be compiled, but any files depending on these files must also be
recompiled. The dependency rules in Ada can be quite complex, especially
in the presence of overloading, use
clauses, generics and inlined
subprograms.
gnatmake
automatically takes care of the third and fourth steps
of this process. It determines which sources need to be compiled,
compiles them, and binds and links the resulting object files.
Unlike some other Ada make programs, the dependencies are always
accurately recomputed from the new sources. The source based approach of
the GNAT compilation model makes this possible. This means that if
changes to the source program cause corresponding changes in
dependencies, they will always be tracked exactly correctly by
gnatmake
.
gnatmake
The gnatmake
command has the form
$ gnatmake switches unit_or_file_name
The only required argument is unit_or_file_name, which specifies the compilation unit that is the main program. There are two ways to specify this:
gnatmake
unit
). In this case gnatmake
will use the switches
{-aIdir}
and
{-Idir}
to locate the appropriate file.
gnatmake
[dir/]file.adb
). If no relative or absolute directory dir
is specified, the input source file will be searched for in the directory
where gnatmake
was invoked. gnatmake
will not use the switches
{-aIdir}
and {-Idir} to locate the source file.
All gnatmake
output (except when you specifiy
-M
) is to
stderr
. The output produced by the
-M
switch is send to
stdout
.
gnatmake
You may specify any of the following switches to gnatmake
:
-a
gnatmake
does not check these files,
because the assumption is that the GNAT internal files are properly up
to date, and also that any write protected ALI files have been properly
installed. Note that if there is an installation problem, such that one
of these files is not up to date, it will be properly caught by the
binder.
You may have to specify this switch if you are working on GNAT
itself. \-f\/ALL_FILES
is also useful in conjunction with
\-f\/FORCE_COMPILE
if you need to recompile an entire application,
including run-time files, using special configuration pragma settings,
such as a non-standard Float_Representation
pragma.
By default
gnatmake -a
compiles all GNAT
internal files with
gcc -c -gnatg
rather than gcc -c
.
-c
gnatmake
will attempt binding and linking
unless all objects are up to date and the executable is more recent than
the objects.
-f
-a
switch is also specified.
-jn
gnatmake
will give you the full ordered
list of failing compiles at the end). If this is problematic, rerun
the make process with n set to 1 to get a clean list of messages.
-k
gnatmake
terminates.
-M
stdout
in a form that can be directly exploited in
a `Makefile'. By default, each source file is prefixed with its
(relative or absolute) directory name. This name is whatever you
specified in the various -aI
and -I
switches. If you use
gnatmake -M -q
(see -q
below), only the source file names,
without relative paths, are output. If you just specify the
-M
switch, dependencies of the GNAT internal system files are omitted. This
is typically what you want. If you also specify
the -a
switch,
dependencies of the GNAT internal files are also listed. Note that
dependencies of the objects in external Ada libraries (see switch
-aL
dir in the following list) are never reported.
-i
gnatmake
compiles all object files and ALI files
into the current directory. If the -i
switch is used,
then instead object files and ALI files that already exist are overwritten
in place. This means that once a large project is organized into separate
directories in the desired manner, then gnatmake
will automatically
maintain and update this organization. If no ALI files are found on the
Ada object path (section Search Paths and the Run-Time Library (RTL)),
the new object and ALI files are created in the
directory containing the source being compiled. If another organization
is desired, where objects and sources are kept in different directories,
a useful technique is to create dummy ALI files in the desired directories.
These are dummy files, so gnatmake
will be forced to recompile
the corresponding source files, but it will be put the resulting object
and ALI files in the location where it found the dummy file.
-m
-n
-o exec_name
-o
switch is omitted the default name for
the executable will be the name of the input file in appropriate form
for an executable file.
-q
gnatmake
are displayed.
-v
gnatmake
decides are necessary.
gcc
switches
-g
or any uppercase switch (other than -A
, or
-L
) or any switch that is more than one character is passed to
gcc
(e.g. -O
, -gnato,
etc.)
Source and library search path switches:
-aIdir
-aLdir
gnatmake
to skip compilation units whose `.ali'
files have been located in directory dir. This allows you to have
missing bodies for the units in dir. You still need to specify
the location of the specs for these units by using the switches
-aIdir
or -Idir
.
Note: this switch is provided for compatibility with previous versions
of gnatmake
. The easier method of causing standard libraries
to be excluded from consideration is to write protect the corresponding
ALI files.
-aOdir
gnatbind
.
-Adir
-aLdir
-aIdir
.
-Idir
-aOdir
-aIdir
.
-I-
gnatmake
was invoked.
-Ldir
-largs -L
dir.
gnatmake
The mode switches allow the inclusion of switches to be passed on to the compiler, binder or linker. The effect of a mode switch is to cause all subsequent switches up to the end of the switch list, or up to the next mode switch, to be interpreted as switches to be passed on to the designated component.
-cargs switches
gcc
. They will be passed on to
all compile steps performed by gnatmake
.
-bargs switches
gcc
. They will be passed on to
all bind steps performed by gnatmake
.
-largs switches
gcc
. They will be passed on to
all link steps performed by gnatmake
.
This section contains some additional useful notes on the operation
of the gnatmake
command.
gnatmake
finds no ALI files, it recompiles the main program
and all other units required by the main program.
This means that gnatmake
can be used for the initial compile, as well as during the re-edit
development cycle.
gnatmake file.adb
, where `file.adb'
is a subunit or body of a generic unit, gnatmake
recompiles
`file.adb' (because it finds no ALI) and stops, issuing a
warning.
gnatmake
the switch -I
is used to specify both source and
library file paths. Use -aI
instead if you just want to specify
source paths only and -aO
if you want to specify libary paths
only.
gnatmake
examines both an ALI file and its corresponding object file
for consistency. If an ALI is more recent than its corresponding object,
or the object is missing, the corresponding source will be recompiled.
Note that gnatmake
expects an ALI and the corresponding object file
to be in the same directory.
gnatmake
will ignore any files whose ALI file is write protected.
This may conveniently be used to exclude standard libraries from
consideration and in particular it means that the use of the
-f
switch will not recompile these files
unless -a
is also specified.
gnatmake
has been designed to make the use of Ada libraries
particularly convenient. Assume you have an Ada library organized
as follows: obj-dir contains the objects and ALI files for
of your Ada compilation units,
whereas include-dir contains the
specs of these units, but no bodies. Then to compile a unit
stored in main.adb
, which uses this Ada library you would just type
$ gnatmake -aIinclude-dir -aLobj-dir main
gnatmake
along with the
-s (minimal recompilation)
switch provides an
extremely powerful tool: you can freely update the comments/format of your
source files without having to recompile everything. Note, however, that
adding or deleting lines in a source files may render its debugging
info obsolete. If the file in question is a spec, the impact is rather
limited, as that debugging info will only be useful during the
elaboration phase of your program. For bodies the impact can be more
significant. In all events, your debugger will warn you if a source file
is more recent than the corresponding object, and so obsolescence of
debugging information cannot go unnoticed.
gnatmake
Works
Generally gnatmake
automatically performs all necessary
recompilations and you don't need to worry about how it works. However,
it may be useful to have some basic understanding of the gnatmake
approach and in particular to understand how it uses the results of
previous compilations without incorrectly depending on them.
First a definition: an object file is considered up to date if the corresponding ALI file exists and its time stamp predates that of the object file and if all the source files listed in the dependency section of this ALI file have time stamps matching those in the ALI file. This means that neither the source file itself nor any files that it depends on have been modified, and hence there is no need to recompile this file.
gnatmake
works by first checking if the specified main unit is up
to date. If so, no compilations is required for the main unit. If not,
gnatmake
compiles the main program to build a new ALI file that
reflects the latest sources. Then the ALI file of the main unit is
examined to find all the source files on which the main program depends,
and recursively applies the above procedure test on all these files.
This process ensures that gnatmake
only trusts the dependencies
in an existing ALI file if they are known to be correct. Otherwise it
always recompiles to determine a new, guaranteed accurate set of
dependencies. As a result the program is compiled "upside down" from what may
be more familiar as the required order of compilation in some other Ada
systems. In particular, clients are compiled before the units on which
they depend. The ability of GNAT to compile in any order is critical in
allowing an order of compilation to be chosen that guarantees that
gnatmake
will recompute a correct set of new dependencies if
necessary.
gnatmake
Usagegnatmake hello.adb
Hello
) and bind and link the
resulting object files to generate an executable file `hello'.
gnatmake -q Main_Unit -cargs -O2 -bargs -l
Main_Unit
(from file `main_unit.adb'). All compilations will
be done with optimization level 2 and the order of elaboration will be
listed by the binder. gnatmake
will operate in quiet mode, not
displaying commands it is executing.
gnatchop
This chapter discusses how to handle files with multiple units by using
the gnatchop
utility. This utility is also useful in renaming
files to meet the standard GNAT default file naming conventions.
The basic compilation model of GNAT requires a file submitted to the compiler have only one unit and there must be a strict correspondence between the file name and the unit name.
The gnatchop
utility allows both of these rules to be relaxed,
allowing GNAT to process files which contain multiple compilation units
and files with arbitrary file names. The approach used by gnatchop
is to read the specified file and generate one or more output files,
containing one unit per file and with proper file names as required by
GNAT.
If you want to permanently restructure a set of "foreign" files so that
they match the GNAT rules and do the remaining development using the
GNAT structure, you can simply use gnatchop
once, generate the
new set of files and work with them from that point on.
Alternatively, if you want to keep your files in the "foreign" format,
perhaps to maintain compatibility with some other Ada compilation
system, you can set up a procedure where you use gnatchop
each
time you compile, regarding the source files that it writes as temporary
files that you throw away.
gnatchop
The gnatchop
command has the form:
$ gnatchop switches file name [directory]
The only required argument is the file name of the file to be chopped. There are no restrictions on the form of this file name. The file itself contains one or more Ada files, in normal GNAT format, concatenated together.
When run in default mode, gnatchop
generates one output file in
the current directory for each unit in the file. For example, given a
file called `hellofiles' containing
procedure hello; with Text_IO; use Text_IO; procedure hello is begin Put_Line ("Hello"); end hello;
the command
$ gnatchop hellofiles
generates two files in the current directory, one called `hello.ads' containing the single line that is the procedure spec, and the other called `hello.adb' containing the remaining text. The original file is not affected. The generated files can be compiled in the normal manner.
directory, if specified, gives the name of the directory to which the output files will be written. If it is not specified, all files are written to the current directory.
gnatchop
gnatchop
recognizes the following switches:
-kmm
-r
Source_Reference
pragmas. Use this switch if the output
files are regarded as temporary and development is to be done in terms
of the original unchopped file. This switch causes
Source_Reference
pragmas to be inserted into each of the
generated files to refers back to the original file name and line number.
The result is that all error messages refer back to the original
unchopped file.
In addition, the debugging information placed into the object file (when
the -g
switch of gcc
or gnatmake
is specified) also
refers back to this original file so that tools like profilers and
debuggers will give information in terms of the original unchopped file.
-s
gcc
commands to compile the generated files.
-w
gnatchop
regards it as a
fatal error situation if there is already a file with the same name as a
file it would otherwise output. This switch bypasses this
check, and any such existing files will be silently overwritten.
gnatchop
Usagegnatchop -w hello_s.ada ichbiah/files
gnatchop -s -r collect
Source_Reference
pragmas, so error messages will refer back to
the file `collect' with proper line numbers.
gnatchop archive
gnatchop
is in sending sets of sources
around, for example in email messages. The required sources are simply
concatenated (for example, using a UNIX cat
command), and then
gnatchop
is used at the other end to reconstitute the original
file names.
In Ada 95, configuration pragmas include those pragmas described as
being configuration pragmas in the reference manual, as well as
implementation dependent pragmas that are configuration pragmas. See the
individual descriptions of pragmas in the GNAT Reference Manual for
details on these additional GNAT-specific configuration pragmas. Most
notably, the pragma Source_File_Reference
, which allows
specifying non-default names for source files, is a configuration
pragma.
Configuration pragmas may either appear at the start of a compilation unit, in which case they apply only to that unit, or they may apply to all compilations performed in a given compialtion environment.
gnat.adc
fileIn the case of GNAT, a compilation environment is defined by the current directory at the time that a compile command is given. This current directory is searched for a file whose name is `gnat.adc', and if this file is present, then it is expected to contain one or more configuration pragmas that will be applied to the current compilation.
Configuration pragmas may be entered into the `gnat.adc' file
either by running gnatchop
on a source file that consists only of
configuration pragmas, or, usually more convenient in practice, by
direct editing of the `gnat.adc' file, which is a standard format
source file.
This chapter describes the handling of elaboration code in Ada 95, and in GNAT, and in particular discusses how the order of elaboration can be controlled, automatically or as specified explicitly by the program, when using GNAT.
Ada 95 provides rather general mechanisms for executing code at elaboration time, i.e. before the main program starts executing. Such code arises in three contexts:
Sqrt_Half : Float := Sqrt (0.5);
BEGIN-END
section at the outer level of a package body is
executed as part of the package body elaboration code.
Subprogram calls are possible in any of these contexts, which means that any arbitrary part of the program may be executed as part of the elaboration code. It is even possible to write a program which does all its work at elaboration time, with a null main program, although stylistically this would probably usually be considered an inappropriate way to structure a program.
An important concern arises in the context of this code, which is that
we have to be sure that it is elaborated in an appropriate order. What we
have is lots of little sections of elaboration code, potentially one section
of code for each unit in the program. It is important that these execute
in the correct order. Correctness here means that, taking the above
example of the declaration of Sqrt_Half
,
that if some other piece of
elaboration code references Sqrt_Half
,
then it must run after the
section of elaboration code that contains the declaration of
Sqrt_Half
.
Now we would never have any elaboration order problems if we made a rule
that whenever you with
a unit, you must elaborate both the spec and body
of that unit before elaborating the unit doing the with
'ing:
with Unit_1; package Unit_2 is ...
would require that both the body and spec of Unit_1 be elaborated before the spec of Unit_2. However, a rule like that would be far too restrictive. In particular, it would make it impossible to have routines in separate packages that were mutually recursive.
One might think that a clever enough compiler could look at the actual elaboration code and determine an appropriate correct order of elaboration, but in the general case, this is not possible. Consider the following example.
In the body of Unit_1, we have a procedure Func_1 that references the variable Sqrt_1, which is declared in the elaboration code of the body of Unit_1:
Sqrt_1 : Float := Sqrt (0.1);
The elaboration code of the body of Unit_1 also contains:
if expression_1 = 1 then Q := Unit_2.Func_2; end if;
Unit_2 is exactly parallel, it has a procedure Func_2 that references the variable Sqrt_2, which is declared in the elaboration code of the body Unit_2:
Sqrt_2 : Float := Sqrt (0.1);
The elaboration code of the body of Unit_2 also contains:
if expression_2 = 2 then Q := Unit_1.Func_1; end if;
Now the question is, which of the following orders of elaboration is acceptable:
Spec of Unit_1 Spec of Unit_2 Body of Unit_1 Body of Unit_2
or
Spec of Unit_2 Spec of Unit_1 Body of Unit_2 Body of Unit_1
If you carefully analyze the flow here, you will see that you cannot tell
at compile time the answer to this question.
If expression_1
is not equal to 1,
and expression_2
is not equal to 2,
then either order is acceptable, because neither of the function calls is
executed. If both tests evaluate to true, then neither order is acceptable
and in fact there is no correct order.
If one of the two expressions is true, and the other is false, then one
of the above orders is correct, and the other is incorrect. For example,
if expression_1
= 1 and expression_2
/= 2,
then the call to Func_2
will occur, but not the call to Func_1.
This means that it is essential
to elaborate the body of Unit_1
before
the body of Unit_2,
so the first
order of elaboration is correct and the second is wrong.
By making expression_1
and expression_2
depend on input data, or perhaps
the time of day, we can make it impossible for the compiler or binder
to figure out which of these expressions will be true, and hence it
is impossible to guarantee a safe order of elaboration at run time.
In some languages that involve the same kind of elaboration problems, e.g. Java and C++, the programmer is expected to worry about these kind of ordering problems themselves, and it is quite possible to write a program in which an incorrect elaboration order can give surprising results as a result of referencing variables before they are initialized as intended. Ada 95 is designed to be a safe language, so this approach is clearly not acceptable. Consequently, the language provides three lines of defense:
with
a unit, then its spec is always
elaborated before the unit doing the with
. Similarly, a parent
spec is always elaborated before the child spec, and finally
a spec is always elaborated before its corresponding body.
Program_Error
) is raised.
Let's look at these facilities in more detail. First, the rules for dynamic checking. One possible rule would be simply to say that the exception is raised if you access a variable which has not yet been elaborated. The trouble with this approach is that it could require expensive checks on every variable reference. Instead Ada 95 has two rules which are a little more restrictive, but easier to check, and easier to state:
Program_Error
is raised.
Program_Error
is raised.
The idea here is that if the body has been elaborated, then any variables it references must have been elaborated, so by checking for the body being elaborated, we are guaranteed that none of its references causes any trouble. As we noted above, this is a little too restrictive, because a subprogram that has no non-local references in its body is in fact safe to call. However, it really would not be right to rely on this, because it would mean that the caller was relying on details of the implementation in the body, which is something we always try to avoid in Ada.
To get an idea of how this might be implemented, consider the following
model implementation. A Boolean variable is associated with each subprogram
and generic unit. This variable is initially set to False, and is set to
True when the body is elaborated. Every call or instantiation checks the
variable, and raises Program_Error
if the variable is False.
In the previous section we discussed the rules in Ada 95 which ensure
that Program_Error
is raised if an incorrect elaboration order is
chosen. However, this is not sufficient. Although we certainly prefer
an exception to getting the wrong results, we need ways of avoiding
the exception.
To achieve this, Ada 95 provides a number of features for controlling
the order of elaboration, and we discuss these features in this section.
First, there are several ways of indicating to the compiler that a given unit has no elaboration problems:
package Definitions is generic type m is new integer; package Subp is type a is array (1 .. 10) of m; type b is array (1 .. 20) of m; end mm; end x;A package that
with
's Definitions
may safely instantiate
Definitions.Subp
because the compiler can determine that there
definitely is no package body to worry about in this case
A
has such a pragma,
and unit B
does
a with
of unit A
. Now the standard rules require
the spec of unit A
to be elaborated before the with
'ing unit, and given the pragma in
A
, we also know that the body of A
will be elaborated before B
, so
calls to A
are safe and do not need a check.
Note that,
unlike pragma Pure
and pragma Preelaborate
,
the use of
Elaborate_Body
does not guarantee that the program is
free of elaboration problems, because it may not be possible
to satisfy the requested elaboration order.
Let's go back to the example with Unit_1
and Unit_2
.
If a programmer
marks Unit_1
as Elaborate_Body
,
and not Unit_2,
then the order of
elaboration will be:
Spec of Unit_2 Spec of Unit_1 Body of Unit_1 Body of Unit_2
Now that means that the call to Func_1
in Unit_2
need not be checked,
it must be safe. But the call to Func_2
in
Unit_1
may still fail if
Expression_1
is equal to 1,
and the programmer must still take
responsibility for this not being the case.
If all units have pragma Elaborate_Body
, then all problems are
eliminated, except for calls entirely within a body, which are
in any case fully under programmer control. However, this is
not always possible.
In particular, for our Unit_1/Unit_2
example, if
we marked both of them as having pragma Elaborate_Body
, then
clearly no elaboration order is possible.
The above pragmas allow a server to guarantee safe use by clients, and
clearly this is the preferable approach. Consequently a good rule in
Ada 95 is to mark units as Pure
or Preelaborate
if possible,
and if this is not possible,
mark them as Elaborate_Body
if possible.
But, as we have discussed, it is not always possible to use one of these
three pragmas. So we also provide methods for clients to control the
order of elaboration:
with
statement,
and it requires that the body of the named unit be elaborated before
the unit in which the pragma occurs. The idea is to use this pragma
if you know that you will be making calls, directly or indirectly,
at elaboration time to subprograms in a given unit.
Unit ANow if we put a pragmawith
's unit B and calls B.Func in elaboration code Unit Bwith
's unit C, and B.Func calls C.Func
Elaborate (B)
in unit A
, this ensures that the
body of B
is elaborated before the call, but not the
body of C
, so
the call to C.Func
could still cause Program_Error
to
be raised.
But the effect of a pragma Elaborate_All
is stronger, it requires
not only that the body of the named unit be elaborated before the
unit doing the with
, but also the bodies of all units that the
named unit uses, following with
links transitively. For example,
if we put a pragma Elaborate_All (B)
in unit A
,
then it requires
not only that the body of B
be elaborated before A
,
but also the
body of C
, because B
with
's C
.
We are now in a position to give a usage rule in Ada 95 for avoiding elaboration problems, at least if dynamic dispatching and access to procedure values are not used. We will handle these cases separately later.
The rule is simple. If a unit has elaboration code that can directly or
indirectly make a call to a subprogram in a with
'ed unit, or instantiate
a generic unit in a with
'ed unit,
then if the with
'ed unit does not have
pragma Pure, Preelaborate, or Elaborate_Body, then the client should have
an Elaborate_All for the with
'ed unit. By following this rule a client is
assured that calls can be made without risk of an exception.
If this rule is not followed, then a program may be in one of four
states:
Note that one additional advantage of following our Elaborate_All rule is that the program continues to stay in the ideal (all orders OK) state even if maintenance changes some bodies of some subprograms. Even if a program that does not follow this rule happens to be safe, this state of affairs may deteriorate silently as a result of maintenance changes.
In the case of internal calls, i.e. calls within a single package, the programmer has full control over the order of elaboration, and it is up to the programmer to elaborate declarations in an appropriate order. For example writing:
function One return Float; Q : Float := One; function One return Float is begin return 1.0; end One;
will obviously raise Program_Error at run time, and indeed GNAT will generate a warning that the call will raise Program_Error:
1. procedure y is 2. function One return Float; 3. 4. Q : Float := One; | >>> warning: cannot call "One" before body is elaborated >>> warning: Program_Error will be raised at run time 5. 6. function One return Float is 7. begin 8. return 1.0; 9. end One; 0. 11. begin 12. null; 13. end;
Note that in this particular case, it is probably the case that, because
One
does not access any global variables, the call really would be safe,
but in Ada 95, we do not want the validity of the check to depend on the
contents of the body (think about the separate compilation case), so this
is still wrong, as we discussed in the previous sections.
The error is easily corrected by rearranging the declarations so that the body of One appears before the elaboration call (note that in Ada 95, declarations can appear in any order, so there is no restriction that would prevent this reordering, and if we write:
function One return Float; function One return Float is begin return 1.0; end One; Q : Float := One;
then all is well, and no warning is generated, and no
Program_Error
exception
will be raised.
Things get a bit more complicated when a chain of subprograms is executed:
function A return Integer; function B return Integer; function C return Integer; function B return Integer is begin return A; end; function C return Integer is begin return B; end; X : Integer := C; function A return Integer is begin return 1; end;
Now the call to C
at elaboration time in the declaration of X
is correct, because
the body of C
is already elaborated,
and the call to B
within the body of
C
is correct, but the call
to A
within the body of B
is incorrect, because the body
of A
has not been elaborated, so Program_Error
will be raised on the call to A
.
In this case GNAT will generate a
warning that Program_Error
may be
raised at the point of the call. Let's look at the warning:
1. procedure x is 2. function A return Integer; 3. function B return Integer; 4. function C return Integer; 5. 6. function B return Integer is begin return A; end; | >>> warning: call to "A" before body is elaborated may raise Program_Error >>> warning: "B" called at line 7 >>> warning: "C" called at line 9 7. function C return Integer is begin return B; end; 8. 9. X : Integer := C; 10. 11. function A return Integer is begin return 1; end; 12. 13. begin 14. null; 15. end;
Note that the message here says "may raise", instead of the direct case,
where the message says "will be raised". That's because whether
A
is
actually called depends on run-time flow of control in the general case.
For example, if the body of B
said
function B return Integer is begin if some-condition-depending-on-input-data then return A; else return 1; end if; end B;
then we could not know till run time whether the incorrect call to A would
actually occur, so Program_Error
might
or might not be raised. If GNAT
felt more ambitious, it could do a better job of analyzing bodies, to
determine whether or not Program_Error
might be raised, but it certainly
couldn't do a perfect job (that would require solving the halting problem
and is provably impossible), and because this is a warning anyway, it does
not seem worth the effort to do the analysis. Cases in which it
would be relevant are rare.
In practice, warnings of either of the types given above will usually correspond to real errors, and should be examined carefully, and typically eliminated. In the rare case that a warning is bogus, it can be suppressed by any of the following methods:
-gnatws
switch set
Elaboration_Checks
for the called subprogram
Warnings_Off
to turn warnings off for the call
For the internal elaboration check case,
GNAT by default generates the
necessary run-time checks to ensure
that Program_Error
is raised if any
call fails an elaboration check. Of course this can only happen if a
warning has been issued as described above. The use of pragma
Suppress (Elaboration_Checks)
may (but is not guaranteed) to suppress
some of these checks, meaning that it may be possible (but is not
guaranteed) for a program to be able to call a subprogram whose body
is not yet elaborated, without raising a Program_Error
exception.
The previous section discussed the case in which the execution of a particular thread of elaboration code occurred entirely within a single unit. This is the easy case to handle, because a programmer has direct and total control over the order of elaboration, and furthermore, checks need only be generated in cases which are rare and which the compiler can easily detect. The siutation is more complex when separate compilation is taken into account. Consider the following:
package Math is function Sqrt (Arg : Float) return Float; end Math; package body Math is function Sqrt (Arg : Float) return Float is begin ... end Sqrt; end Math; with Math; package Stuff is X : Float := Math.Sqrt (0.5); end Stuff; with Stuff; procedure Main is begin ... end Main;
where Main
is the main program. When this program is executed, the
elaboration code must first be executed, and one of the jobs of the
binder is to determine the order in which the units of a program are
to be elaborated. In this case we have four units the spec and body
of Math
,
the spec of Stuff
and the body of Main
), and the question
is in what order should the four separate sections of elaboration code
be executed?
There are some restrictions in the order of elaboration that the binder
can choose. In particular, if you have a with
for a package X
, then you
are assured that the spec of X
is elaborated before you are, but you are
not assured that the body of X
is elaborated before you are.
This means that in the above case, the binder is allowed to choose the
order:
spec of Math spec of Stuff body of Math body of Main
but that's not good, because now the call to Math.Sqrt
that happens during
the elaboration of the Stuff
spec happens before the body of Math.Sqrt
is
elaborated, and hence causes Program_Error
exception to be raised.
At first glance, one might react that the binder is being silly, because
obviously you want to elaborate the body of something you with
first, but
that is not a general rule that can be followed in all cases. Consider
package X is ... package Y is ... with X; package body Y is ... with Y; package body X is ...
This is a common arrangement, and, apart from the order of elaboration
problems that arise only in connection with elaboration code, works fine.
A rule that says that you must elaborate the body first of anything you
with
cannot work in this case
(the body of X
with
's Y
,
which means you
want to elaborate the body of Y
first, but that with
's X
,
which means
you want to elaborate the body of X
first, but ... and we have a
loop that cannot be broken.
It is true that the binder can in many cases guess an order of elaboration
that is unlikely to cause a Program_Error
exception to be raised, and it tries to do so (in the
above example of Math/Stuff/Spec
, the GNAT binder will
in fact always
elaborate the body of Math
right after its spec, so all will be well).
However, a program that blindly relies on the binder to be kind can get into trouble, as we discussed in the previous sections, so GNAT provides a number of facilities for assisting the programmer in developing programs that are robust with respect to elaboration order.
The default behavior in GNAT ensures elaboration safety. What GNAT does in its default mode is to automatically and implicitly implement the rule we previously suggested as the right approach. Let's restate the rule:
If a unit has elaboration code that can directly or indirectly make a
call to a subprogram in a with
'ed unit, or instantiate a generic unit
in a with
'ed unit, then if the with
'ed unit
does not have pragma Pure
,
Preelaborate
, or Elaborate_Body
,
then the client should have an
Elaborate_All
for the with
'ed unit. By following this rule a client
is assured that calls can be made without risk of an exception.
What GNAT does is to trace all calls that are potentially made from
elaboration code, and put in any missing implicit Elaborate_All
pragmas.
The advantage of this approach is that no elaboration problems
are possible if the binder can find an elaboration order that is
consistent with these implicit Elaborate_All
pragmas. The
disadvantage of this approach is that no such order may exist.
If the binder does not generate any diagnostics, then it means that it
has found an elaboration order that is guaranteed to be safe. However,
the binder may still be relying on implicitly generated
Elaborate_All
pragmas so portability to other compilers than
GNAT is not guaranteed.
If it is important to guarantee portability, then the compilations should
use the
-gnatwl
(warn on elaboration problems) switch. This will cause warning messages
to be generated indicating the missing Elaborate_All
pragmas.
Consider the following source program:
with k; package j is m : integer := k.r; end;
where it is clear that there really
should be a pragma Elaborate_All
for unit k
. An implicit pragma will be generated, and it is
likely that the binder will be able to honor this implicit pragma. However
it is safer to include the pragma explicitly in the source. If this
unit is compiled with the
-gnatwl
switch, then the compiler outputs a warning:
1. with k; 2. package j is 3. m : integer := k.r; | >>> warning: call to "r" may raise Program_Error >>> warning: missing pragma Elaborate_All for "k" 4. end;
and these warnings can be used as a guide for supplying the missing pragmas.
If the binder cannot find an acceptable order, it outputs quite detailed diagnostics. For example:
error: elaboration circularity detected info: "proc (body)" must be elaborated before "pack (body)" info: reason: Elaborate_All probably needed in unit "pack (body)" info: recompile "pack (body)" with -gnatwl info: for full details info: "proc (body)" info: is needed by its spec: info: "proc (spec)" info: which is withed by: info: "pack (body)" info: "pack (body)" must be elaborated before "proc (body)" info: reason: pragma Elaborate in unit "proc (body)"
In this case we have a cycle that the binder cannot break. On the one
hand, there is an explicit pragma Elaborate in proc
for
pack
. This means that the body of pack
must be elaborated
before the body of proc
. On the other hand, there is elaboration
code in pack
that calls a subprogram in proc
. This means
that for maximum safety, there should really be a pragma
Elaborate_All in pack
for proc
which would require that
the body of proc
be elaborated before the body of
pack
. Clearly both requirements cannot be satisfied.
Faced with a circularity of this kind, you have three different options.
-gnatE
(dynamic elaboration check) switch, then GNAT behaves in
a quite different manner. Dynamic checks are generated for all calls
that could possibly result in raising an exception. With this switch,
the compiler does not generate implicit Elaborate_All
pragmas.
The behavior then is exactly as specified in the reference manual.
The binder will generate an executable program that may or may not
raise Program_Error, and then it is the programmer's job to ensure
that it does not raise an exception. Note that it is important to
compile all units with the switch, it cannot be used selectively.
-f
switch for the
gnatbind
step, or
-bargs -f
if you are using
gnatmake
.
This switch tells the binder to ignore any implicit Elaborate_All
pragmas that were generated by the compiler, and suppresses any
circularity messages that they cause. The resulting executable will work
fine if there are no elaboration problems, but if there are some order of
elaboration problems, then they will not be detected, and unexpected
results may occur.
It is hard to generalize on which of these three approaches should be
taken. Obviously if it is possible to fix the program so that the default
treatment works, this is preferable, but this may not always be practical.
It is certainly simple enough to use
-gnatE
or
-f
but the danger in either case is that, even if the GNAT binder
finds a correct elaboration error free order, it may not always do so,
and certainly a binder from another Ada compiler may not do so. A
combination of testing and analysis (for which the warnings generated
with the
-gnatwl
switch can be useful) must be used to ensure that the program is free
of errors. One switch that is useful in this testing is the
-h (horrible elaboration order)
switch for
gnatbind
.
Normally the binder tries to find an order that has the best chance of
succeeding in avoiding elaboration problems. With this switch, the binder
plays a kind of devil's advocate role, and tries to choose the order that
has the best chance of failing. If your program works even with this
switch, then it has a better chance of being error free, but this is still
not a guarantee.
For an example of this approach in action, consider the C-tests (executable tests) from the ACVC suite. If these are compiled and run with the default treatment, then all but one of them succeeds without generating any error dianostics from the binder. However, there is one test that fails, and this is not surprising, because the whole point of this test is to ensure that the compiler can handle cases where it is impossible to determine a correct order statically, and it checks that an exception is indeed raised at run time.
This one test must be compiled and run using the
-gnatE
switch, and then passes fine. Alternatively, the entire suite can
be run using this switch. It is never wrong to run with the dynamic
elaboration switch if your code is correct, and we assume that the
C-tests are indeed correct. It is less efficient, but efficiency is
not a factor in running the ACVC tests.
The introduction of accesst-to-subprogram types in Ada 95 complicates the handling of elaboration. The trouble is that we now have a situation where it is impossible at compile time to tell exactly which procedure is being called. This means that it is not possible to analyze the elaboration rqeuirements statically at compile time in this case.
If at the time the access value is created, the body of the subprogram is
known to have been elaborated, then the access value is safe, and its use
does not require a check. This may be achieved by appropriate arrangement
of the order of declarations if the subprogram is in the current unit,
or, if the subprogram is in another unit, then by using pragma
Pure
, Preelaborate
, or Elaborate_Body
on the referenced unit.
If the referenced body is not known to have been elaborated at the time
the access value is created, then any use of the access value must do a
dynamic check, and this dynamic check will fail, raising a
Program_Error
exception if the body still has not been elaborated.
GNAT will generate the necessary checks, and in addition, if the
-gnatwl
switch is set, will generate warnings that such checks are required.
The use of dynamic dispatching for tagged types similarly generates
a requirement for dynamic checks, and premature calls to any primitive
operation of a tagged type, before the body has been elaborated, will
also result in the raising of a Program_Error
exception
First, compile your program with the default options, using none of
the special elaboration control switches. If the binder successfully
binds your program, then you can be confident that, apart from issues
raised by the use of access-to-subprogram types and dynamic dispatching,
the program is free of elaboration errors. If it is important that the
program be portable, then use the
-gnatwl
switch to generate warnings about missing Elaborate_All
pragmas, and supply the missing pragmas.
If the program fails to bind using the default static elaboration
handling, then you can fix the program to eliminate the binder
message, or recompile the entire program with the
-gnatE
switch to generate dynamic elaboration checks,
or, if you are sure there really are no elaboratoin problems,
use the
-f
switch for the binder to cause it to ignore implicit Elaborate_All
pragmas generated by the compiler.
gnatf
This chapter discusses gnatf
, a stripped-down version of the GNAT
compiler containing only the front end. gnatf
can perform full
syntax and semantic checking and also has a cross-reference analyzer
built in that can perform a variety of functions.
gnatf
The GNAT system provides a stand-alone tool, gnatf
, which allows
for syntax and semantics checking without any code generation. This is
somewhat faster than using gcc -gnatc
.
The standard GNAT switches that do not concern code generation are still
available in gnatf
. However, they should not be proceeded by
-gnat
, so to do syntax only checking with gnatf
, use
gnatf -s ile.adb
, not gnatf -gnats file.adb
.
The important function of gnatf
is that it contains a cross reference
tool, whose goals are:
emacs
mode contained in the GNAT distribution.
with
clause is unnecessary, misplaced or redundant.
gnatf
The gnatf
command line is of the following form:
$ gnatf [switches] files
The effect is similar to a gcc
command specifying the
-gnatc
(no code generation) switch, although it is somewhat
faster, especially if several files are processed at the same type
gnatf
The compilation switches for gnatf are identical to the corresponding
switches for gcc
, except that the names are not preceded by
-gnat
. For example, the gnatf
switch for syntax-only
checking is -s
instead of -gnats
. For full details on
these switches, See section Switches for gcc
.
The following list contains the descriptions of the cross-referencing
flags available with gnatf
:
-x1
with
clauses. Specifically, a warning message is generated in the following
cases:
with
'ed but never used (this works
with child library units as well).
with
'ed in a body (responsible
subunit) if the same with
clause already appears in the
specification (responsible specification or body for subunits).
with
'ed within a specification but is
used only by the body or a subunit.
-x2
for i in Color'First .. Color'Last
for I in 1 .. 80 loop Put ('x'); end loop;
-x3
-x4
-x5
-x3
switch gives the
most succinct cross-referencing information, -x5
the most
comprehensive. The -x4
gives an intermediate level
of output.
The -x5
switch lists all
entities defined or used in the analyzed compilation units. It gives the
source location of their definition and all their uses in the analyzed
units.
-x6
-x5
, except that
all cross-reference information is stored in the single
file `X.ref', and the entity kind of each cross-referenced entity
is also given.
The cross reference information gathered by the -x3
and
-x4
switches is a subset of the information specified by the
-x5
switch. When
-x3
or -x4
are specified, the cross-referencing tool
gnatf
produces the following information for each compilation
unit analyzed. We refer to that unit as unit in the following list.
with
'ed unit graph rooted at unit and also
other units automatically loaded by GNAT during code generation (generic
bodies, subunits, and bodies of inlined subprograms).
-x3
and -x4
switches is that
-x3
omits all generic bodies or inlined subprograms from the
exported entities, while -x4
includes them. Both -x3
and
-x4
consider subunits as exported entities.
gnatf
considers only outermost visible entities to be exported.
That is, a record or enumeration type may be exported, but its inner
fields or enumeration literals are never considered exported entities.
It is the same for subprogram parameters and discriminants.
The cross-referencing file is divided into various sections. There is one section for each compilation unit explicitly requested. We call these units, RUs, for "requested units." There is also one section for each AU, (auxiliary unit); that is, those compilation units that are implicitly loaded by the compiler, but whose compilation has not been explicitly requested by the user. Specs of withed packages are typical AUs.
All entities exported by RUs (the -x3
and -x4
switches) or
all entities belonging to RUs (the -x5
and -x6
switches)
appear in the cross-referencing file(s).
However, only the entities defined in AUs that are imported in RUs
appear in the cross-referencing file. Their order is the order of
declaration in the source files.
The sections in the cross reference referring to RUs and AUs are respectively denoted:
%% unit.ad[sb]
-- unit.ad[sb]
Note: An entity defined inside a generic and used through a generic instantiation is listed under the cross-referencing section of the generic unit.
gnatf
Usage01 with Part1; -- unused 02 with Part2; use Part2; 03 procedure Test is 04 05 Thing : Number; 06 type Client is record 07 Number : Integer; 08 State : Boolean; 09 end record; 10 type Color is (Red, Green); -- unused 11 My_Client : Client; 12 13 begin 14 My_Client.Number := 1; 15 My_Client.State := True; 16 Thing := 20; 17 Thing := Thing + Thing; 18 end;
01 package Part1 is 02 type Useless is new Integer; 03 end;
01 package Part2 is 02 type Number is new Integer range 1 .. 1000; 03 The_Number : constant := 42; 04 end;
The result of invoking gnatf -x5 test.adb
is:
stderr
(the screen)
test.adb:1:06: warning: "Part1" withed but unused. test.adb:3:11: warning: "Test" unused test.adb:10:09: warning: "Color" unused
01 V "SGNAT v1.0 " 02 test.adb 941012154746 2 3 03 part1.ads 941012154531 04 part2.ads 941012154620 05 06 %% test.adb 07 test 3:11 08 thing 5:4 09 {16:4 17:4 17:13 17:21} 10 client 6:9 11 {11:16} 12 client.number 7:7 13 {14:14} 14 client.state 8:7 15 {15:14} 16 color 10:9 17 red 10:19 18 green 10:24 19 my_client 11:4 20 {14:4 15:4} 21 22 -- part1.ads 23 part1 1:9 24 {1:6} 25 26 -- part2.ads 27 part2 1:9 28 {2:6 2:17} 29 number 2:9 30 {5:14}
The unit Test
is the only RU (requested unit). AUs (auxiliary
units) are packages Part1
and Part2
. First, the graph of
the loaded units with their time stamps is given:
02 test.adb 941012154746 2 3 03 part1.ads 941012154531 04 part2.ads 941012154620
Unit Test
requires the loading of units Part1
and
Part2
(the second and third units listed in the inclusion graph).
The entry
06 %% test.adb 07 [...] 08 thing 5:4 09 {16:4 17:4 17:13 17:21}
means Thing
is an entity (a variable) defined in line 5 column 4;
used in line 16 column 4; and in line 17 columns 4, 13, and 21; in file
`test.adb'.
The entity Useless
may be used in units other than Test
,
but that information is not contained in the `test.xrb' file
because Test
does not use Useless
.
gnatkr
This chapter discusses the method used by the compiler to shorten
the default file names chosen for Ada units so that they do not
exceed the maximum length permitted, and also describes the
gnatkr
utility that can be used to determine the result of
applying this shortening.
gnatkr
The normal rule in using GNAT if default file names are used is that the file name must be derived from the unit name. The exact default rule is: Take the unit name and replace all dots by hyphens, except that if such a replacement occurs in the second character position of a name and the first character is one of a/g/s/i then replace the dot by the character ~ (tilde) instead of a minus. The reason for this special exception is to avoid clashes with the standard names for children of System, Ada, Interfaces, and GNAT, which use the prefixes -s -a -i and -g respectively.
The -gnatknn
switch of the compiler activates a "krunching"
circuit that limits file names to nn characters (where nn is a decimal
integer). For example, using OpenVMS,
where the maximum file name length is
63, the value of nn is usually set to 63, but if you want to generate
a set of files that would be usable if ported to a system with some
different maximum file length, then a different value might be
appropriate.
The default value of 63 for OpenVMS need not be specified.
The gnatkr
utility can be used to determine the krunched name for
a given file, when krunched to a specified maximum length.
gnatkr
The gnatkr
command has the form
$ gnatkr name [length]
name can be an Ada name with dots or the GNAT name of the unit
where the dots representing child units or subunit are replaced by
hyphens. The only confusion arises if a name ends in .ads
or
.adb
. gnatkr
takes this to be an extension if there are
no other dots in the name and the whole name is in lowercase.
length represents the length of the krunched name. The default without any argument given is 8 characters. A length of zero stands for unlimited, in other words no chop except for system files which are always 8.
The output is the kruched name. The output has an extension only if the original argument was a file name with an extension.
The initial file name is determined by the name of the unit that the file
contains. The name is formed by taking the full expanded name of the
unit and replacing the separating dots with hyphens and using lowercase
for all letter, except that a hyphen in the second character position is
replaced by a tilde if the first character is a, i, g, or s.
The extension is .ads
for a
specification and .adb
for a body.
Krunching does not affect the extension, but the file name is shorted to
the specified length by following these rules:
our-strings-wide_fixed 22 our strings wide fixed 19 our string wide fixed 18 our strin wide fixed 17 our stri wide fixed 16 our stri wide fixe 14 our str wide fixe 14 our str wid fixe 13 our str wid fix 12 ou str wid fix 11 ou st wid fix 10 ou st wi fix 9 ou st wi fi 8 Final file name: oustwifi.adb
ada-strings-wide_fixed 22 a- strings wide fixed 18 a- string wide fixed 17 a- strin wide fixed 16 a- stri wide fixed 15 a- stri wide fixe 14 a- str wide fixe 13 a- str wid fixe 12 a- str wid fix 11 a- st wid fix 10 a- st wi fix 9 a- st wi fi 8 Final file name: a-stwifi.adb
Of course no file shortening algorithm can guarantee uniqueness over all
possible unit names, and if file name krunching is used then it is your
responsibility to ensure that no name clashes occur. The utility
program gnatkr
is supplied for conveniently determining the
krunched name of a file.
gnatkr
Usage$ gnatkr very_long_unit_name.ads --> velounna.ads $ gnatkr very_long_unit_name.ads 6 --> vlunna.ads $ gnatkr very_long_unit_name.ads 0 --> very_long_unit_name.ads $ gnatkr grandparent-parent-child.ads --> grparchi.ads $ gnatkr Grandparent.Parent.Child --> grparchi
gnatprep
The gnatprep
utility provides
a simple preprocessing capability for Ada programs.
It is designed for use with GNAT, but is not dependent on any special
features of GNAT.
gnatprep
To call gnatprep
use
$ gnatprep infile outfile deffile switches
where
infile
outfile
deffile
switches
gnatprep
-c
-b
-r
-b
if -c
is not set, so
that source line numbers are not modified.
-s
-v
Note: if neither -b
nor -c
is present,
then preprocessor lines and
deleted lines are completely removed from the output, unless -r is
specified, in which case -b is assumed.
The definitions file contains lines of the form
symbol := value
where symbol is an identifier, following normal Ada (case-insensitive) rules for its syntax, and value is one of the following:
Comment lines may also appear in the definitions file, starting with the usual --, and comments may be added to the definitions lines.
gnatprep
The input text may contain preprocessor conditional inclusion lines, and also general symbol substitution sequences. The preprocessor conditional inclusion commands have the form
#if [not] symbol [then] lines #elsif [not] symbol [then] lines #elsif [not] symbol [then] lines ... #else lines #end if;
For these Boolean tests, the symbol must have either the value True or False. If the value is True, then the corresponding lines are included, and if the value is False, they are excluded. It is an error to reference a symbol not defined in the symbol definitions file, or to reference a symbol that has a value other than True or False. The use of the not operator inverts the sense of this logical test, so that the lines are included only if the symbol is not defined. The THEN keyword is optional as shown
The # must be in column one, but otherwise the format is free form. Spaces or tabs may appear between the # and the keyword. The keywords and the symbols are case insensitive as in normal Ada code. Comments may be used on a preprocessor line, but other than that, no other tokens may appear on a preprocessor line. Any number of #elsif clauses can be present, including none at all. The #else is optional, as in Ada.
The # marking the start of a preprocessor line must be the first non-blank character on the line, i.e. it must be preceded only by spaces or horizontal tabs.
Symbol substitution is obtained by using the sequence
$symbol
anywhere within a source line, except in a comment. The identifier following the $ must match one of the symbols defined in the symbol definition file, and the result is to substitute the value of the symbol in place of $symbol in the output file.
gnatls
gnatls
is a tool that outputs information about compiled
units. It gives the relationship between objects, unit names and source
files. It can also be used to check the source dependencies of a unit
as well as various characteristics.
gnatls
The gnatls
command has the form
$ gnatls switches object_or_ali_file
The main argument is the list of object or `ali' files (see section The Ada Library Information Files) for which information is requested.
In normal mode, without additional option, gnatls
produces a
four-column listing. Each line represents information for a specific
object. The first column gives the full path of the object, the second
column gives the name of the principal unit in this object, the third
column gives the status of the source and the fourth column gives the
full path of the source representing this unit.
Here is a simple example of use:
$ gnatls *.o ./demo1.o demo1 DIF demo1.adb ./demo2.o demo2 OK demo2.adb ./hello.o h1 OK hello.adb ./instr-child.o instr.child MOK instr-child.adb ./instr.o instr OK instr.adb ./tef.o tef DIF tef.adb ./text_io_example.o text_io_example OK text_io_example.adb ./tgef.o tgef DIF tgef.adb
The first line can be interpreted as follows: the main unit which is contained in object file `demo1.o' is demo1, whose main source is in `demo1.adb'. Furthermore, the version of the source used for the compilation of demo1 has been modified (DIF). Each source file has a status qualifier which can be OK, MOK, DIF or NFP:
OK (unchanged)
MOK (slightly modified)
gnatmake
but may be in some future version of the system).
DIF (modified)
??? (file not found)
HID (hidden, unchanged version not first on PATH)
gnatls
gnatls
recognizes the following switches:
-a
-d
-h
-o
-s
-u
-aOdir
-aIdir
-Idir
-I-
gnatmake
-v
Preelaborable
No_Elab_Code
Pure
Elaborate_Body
Remote_Types
Shared_Passive
Predefined
Remote_Call_Interface
gnatls
UsageExample of using the verbose switch. Note how the source and object paths are affected by the -I switch.
$ gnatls -v -I.. demo1.o GNATLS 3.10w (970212) Copyright 1997 Free Software Foundation, Inc. Source Search Path: <Current_Directory> ../ /home/comar/local/adainclude/ Object Search Path: <Current_Directory> ../ /home/comar/local/lib/gcc-lib/mips-sni-sysv4/2.7.2/adalib/ ./demo1.o Unit => Name => demo1 Kind => subprogram body Flags => No_Elab_Code Source => demo1.adb modified
Examples of use of the dependency list. Note the use of the -s switch which gives a straight list of source file. This can be useful for building specialized scripts.
$ gnatls -d demo2.o ./demo2.o demo2 OK demo2.adb OK gen_list.ads OK gen_list.adb OK instr.ads OK instr-child.ads $ gnatls -d -s -a demo1.o demo1.adb /home/comar/local/adainclude/ada.ads /home/comar/local/adainclude/a-finali.ads /home/comar/local/adainclude/a-filico.ads /home/comar/local/adainclude/a-stream.ads /home/comar/local/adainclude/a-tags.ads gen_list.ads gen_list.adb /home/comar/local/adainclude/gnat.ads /home/comar/local/adainclude/g-io.ads instr.ads /home/comar/local/adainclude/system.ads /home/comar/local/adainclude/s-exctab.ads /home/comar/local/adainclude/s-finimp.ads /home/comar/local/adainclude/s-finroo.ads /home/comar/local/adainclude/s-secsta.ads /home/comar/local/adainclude/s-stalib.ads /home/comar/local/adainclude/s-stoele.ads /home/comar/local/adainclude/s-stratt.ads /home/comar/local/adainclude/s-tasoli.ads /home/comar/local/adainclude/s-unstyp.ads /home/comar/local/adainclude/unchconv.ads
This chapter discusses some other utility programs available in the Ada environment.
The object files generated by GNAT are in standard system format and in particular the debugging information uses this format. This means programs generated by GNAT can be used with existing utilities that depend on these formats.
In general, any utility program that works with C will also often work with Ada programs generated by GNAT. This includes software utilities such as gprof (a profiling program), gdb (the FSF debugger), and utilities such as Purify.
gnatpsys
Utility ProgramMany of the definitions in package System are implementation dependent. Furthermore, although the source of the package System is available for inspection, it uses special attributes for parametrizing many of the critical values, so the source is not informative.
The gnatpsys
utility is designed to deal with this situation.
It is an Ada program that when it runs, dynamically determines the
values of all the relevant parameters in System, and prints them
out in the form of an Ada source listing for System that shows all
the values that are of interest. This output is generated to
`stdout'.
To determine the value of any parameter in package System, simply
run gnatpsys
with no qualifiers or arguments, and examine
the output. This is preferable to consulting documentation, because
you know that the values you are getting are the actual ones provided
by the system.
gnatpsta
Utility ProgramMany of the definitions in package Standard are implementation dependent. However, the source of this package does not exist as an Ada source file, so these values cannot be determined by inspecting the source. They can be determined by examinining in detail the coding of `cstand.adb' which creates the image of Standard in the compiler, but this is awkward and requires a great deal of internal knowlegde about the system.
The gnatpsta
utility is designed to deal with this situation.
It is an Ada program that when it runs, dynamically determines the
values of all the relevant parameters in Standard, and prints them
out in the form of an Ada source listing for Standard that shows all
the values that are of interest. This output is generated to
`stdout'.
To determine the value of any parameter in package Standard, simply
run gnatpsta
with no qualifiers or arguments, and examine
the output. This is preferable to consulting documentation, because
you know that the values you are getting are the actual ones provided
by the system.
In order to interpret the output from GNAT, when using tools that are originally intended for use with other languages, it is useful to understand the conventions used to generate link names from the Ada entity names.
All names are in all lower-case letters. With the exception of library procedure names, the mechanism used is simply to use the full expanded Ada name with dots replaced by double underscores. For example, suppose we have the following package spec:
package QRS is MN : Integer; end QRS;
The variable MN
has a full expanded Ada name of QRS.MN
, so
the corresponding link name is qrs__mn
.
Of course if a pragma Export
is used this may be overridden:
package Exports is Var1 : Integer; pragma Export (Var1, C, External_Name => "var1_name"); Var2 : Integer; pragma Export (Var2, C, Link_Name => "var2_link_name"); end Exports;
In this case, the link name for Var1 is var1_name, and the link name for Var2 is var2_link_name.
One exception occurs for library level procedures. A potential ambiguity
arises between the required name _main
for the C main program,
and the name we would otherwise assign to an Ada library level procedure
called Main
(which might well not be the main program).
To avoid this ambiguity, we attach the prefix _ada_
to such
names. So if we have a library level procedure such as
procedure Hello (S : String);
the external name of this procedure will be _ada_hello.
emacs
In the subdirectory `emacs-ada-mode' you will find a set of files
implementing an Ada mode under GNU emacs
.
For instance, the Ada mode has the same indenting friendliness that C
programmers get with the c-mode, you can toggle between specification
and body with a few keystrokes, etc. This mode also uses `gnatf' to
be able to point to an entity with the mouse, click it and open a window
with its definition.
This chapter discusses how to run and debug Ada programs.
Most compilers have secret internal debugging switches and modes. GNAT does also, except GNAT internal debugging switches and modes are not secret. A summary and full description of all the compiler and binder debug flags are in the file `debug.adb'. You must obtain the sources of the compiler to see the full detailed effects of these flags.
The switches that print the source of the program (reconstructed from the internal tree) are of general interest, as are the options to print the full internal tree, and the entity table (that is the symbol table information).
GNAT may experience problems in operation, such as aborting with a segmentation fault or illegal memory access, raising an internal exception, or terminating abnormally. If such problems occur, try the solutions described in this section.
The following strategies are presented in increasing order of difficulty, corresponding to the user's skill level and curiosity about the functioning of the compiler.
gcc
with the -gnatf
and -gnate
switches. The first
switch causes all errors on a given line to be reported. In its absence,
only the first error on a line is displayed.
The -gnate
switch causes errors to be displayed as soon as they
are encountered, rather than after compilation is terminated.
Often this is enough to identify the construct that produced the crash.
gcc
with the -v
(verbose) switch. In this mode,
gcc
produces ongoing information about the progress of the
compilation and provides the name of each procedure as code is
generated. This switch allows you to find which Ada procedure was being
compiled when it encountered a code generation problem.
gcc
with the -gnatdc
switch. This is a GNAT specific
switch that does for the frontend what -v
does for the backend.
The system prints the name of each unit, either a compilation unit or
nested unit, as it is being analyzed.
gdb
available (most UNIX systems), start
gdb
directly on the gnat1
executable. gnat1
is the
front-end of GNAT, and can be run independently (normally it is just
called from gcc
). You can use gdb
on gnat1
as you
would on a C program (but see section Using gdb
for caveats). The
where
command is the first line of attack; the variable
lineno
(seen by print lineno
), used by the second phase of
gnat1
and by the gcc
backend, indicates the source line at
which the execution stopped, and input_file name
indicates the name of
the source file.
gdb
The GNU debugger gdb
is a powerful general purpose debugger that
has been ported to many environments
including most Unix and PC based systems.
A complete manual for gdb
is available from the Free Software
Foundation, and this manual can also be downloaded from standard
FSF sites. However, this manual is oriented towards C programmers.
There is already provision in gdb
for handling multiple source
languages, and
as part of the GNAT development effort, gdb
has been enhanced
to recognize Ada as one of the languages that it recognizes. This
section describes the enhancements that provide this Ada capability.
gdb
The philosophy of gdb
Ada mode is pragmatic.
It does not reproduce the
entire semantics of Ada, but rather a sufficient subset to cover typical
debugging needs. This subset includes function calls, so the
programmer can link in a package of auxiliary debugging routines and
call them from gdb
to produce fancier effects.
This capability of calling functions is a particularly important one. You can include in your programs dumping and diagnostic routines that output your data structures to the standard output file at any level of abstraction that you choose. Then during your debugging session, you can simply call one of these routines with an argument that comes from the contents of some variable in the context that you are debugging.
gdb
selects a syntax for
expressions depending on its "current language",
which by default is selected automatically based on the name of the
source file containing the function attached to the currently-selected
frame. Also by default, when the program is not executing, the
current language is that of the main program. Programmers may also
use the set language
command to set the current language by hand.
The Ada mode for gdb
adds "ada"
as a possible value of the current language, and
selects it automatically for files with extensions `.ads' or
`.adb'. By means of a heuristic check, it can usually
determine when the main program is written in Ada, and start in
Ada mode when appropriate.
gdb
When the current language is "ada",
gdb
accepts expressions
written in an "extended subset: of Ada95 syntax.
Currently, this subset includes:
gdb
can
distinguish array indexing from function calls. At the
moment, named parameters are recognized syntactically, but the
names are ignored, and gdb
treats all calls as positional.
.all
.
'First
, 'Last
,
'Access
, and 'Range
(the latter is useful only in membership tests).
&
is not
supported yet.
gdb
gdb
understands both GNAT arrays with static constraints and GNAT
arrays that are represented using array descriptors. It can index
either, as well as printing them. This also applies to strings.
The syntax for printing out arrays, is C syntax at the
moment.
GNAT has a convention for "mangling" the expanded names of subprograms and objects declared within packages and nested packages, converting the dots into sequences of two underscores, suffixing the names with an additional integer to get unique names for overloaded subprograms declared in the same scope, and distinguishing the name of the main program with a special prefix See section The External Symbol Naming Scheme of GNAT.
gdb
converts the names of symbols from files whose language is Ada
into the standard Ada syntax for expanded names. In addition, when
matching against Ada symbols, gdb
ignores case.
In addition, gdb
provides, in effect, with
and
use
clauses for all packages in a program, so that the programmer is free
to call subprograms or to access package variables at the library
level at any point, using only as much of the suffix of the
expanded name as is needed. For example, a function
Utilities.Math.Sin
may also be referred to as sin
or
Math.Sin
if no ambiguities arise
While this is quite different like Ada semantics,
we hypothesize that it is convenient from the point of view of the
gdb
-user. In particular, it is essentially the convention used by GDB
for C programs.
gdb
gdb
will detect and attempt to resolve calls on overloaded
subprograms. Specifically, it employs a simple heuristic in which
the types and number of the actual parameters of a call are used to
select one of several candidates. Unlike full Ada, gdb
does not
use return types, and because its visibility rules differ, it
generally sees more candidate resolutions. If its heuristics don't
work, it will prompt the user to make a choice.
User-defined functions may be calledeither with prefix syntax, as in
C := "+"(A,B); C := Math."+"(A,B);
or with infix syntax, as in
C := A+B;
There are limitations: one cannot refer to built-in operators with prefix syntax and one cannot refer to user-defined operators with infix syntax if there is a conflict with a built-in operator. Overloading resolution is the same as for ordinary functions.
Note the existing support in gdb
for C++
only provides overloading of class
member functions. In contrast, gdb
for Ada is able to
handle all overloaded subprograms, not just primitive subprograms (the
analogues of C++ member functions).
Somewhat experimentally, gdb
includes a modification of GDB that,
when in Ada mode, causes all unrecognized commands to be treated as
subprogram calls. For example, the command
(gdb) printTree(T)
would normally cause an error. In gdb
, it is treated as
(gdb) call printTree(T)
The utility of this feature is not certain. It was inspired
by the observation that interactions between gdb
and complex Ada
features--notably tasking and exceptions--will generally take the
form of calls on special run-time library procedures. These calls
function, in the user's mind, as additional GDB commands, so it makes
sense to have the syntax encourage that notion.
gdb
The first example shows the use of the Ada version of gdb
in
accessing Ada arrays. We first give the contents of the source
files, and then the transcript.
The file `arrays1.ads' contains:
1. package arrays1 is 2. 3. type Vector is array (INTEGER range <>) of INTEGER; 4. subtype Vector10 is Vector(1..10); 5. type Array2D is array 6. (INTEGER range <>, INTEGER range <>) of INTEGER; 7. subtype Array2D3_4 is Array2D(2..4, 3..6); 8. 9. procedure main(N: INTEGER); 10. procedure f(X0: in out Vector; Y0: in out Vector10; 11. X1: Vector; Y1: Vector10); 12. procedure g(A0: in out Array2D; B0: in out Array2D3_4; S: STRING); 13. 14. end arrays1;
The file `arrays1.adb' contains:
1. package body arrays1 is 2. 3. procedure main(N: INTEGER) is 4. A,B: Vector10; 5. C,D: Vector(1..N); 6. E: Array2D(1..N, 3..N); 7. H: Array2D3_4; 8. 9. begin 10. for i in A'RANGE loop 11. A(i) := -i; B(i) := -2 * i; 12. end loop; 13. 14. for i in C'RANGE loop 15. C(i) := 2*i+1; D(i) := 2*i; 16. end loop; 17. f(C, A, D, B); 18. 19. for i in E'RANGE(1) loop 20. for j in E'RANGE(2) loop 21. E(i,j) := 5*i + 7*j; 22. end loop; 23. end loop; 24. 25. for i in H'RANGE(1) loop 26. for j in H'RANGE(2) loop 27. H(i,j) := 2*i + 3*j; 28. end loop; 29. end loop; 30. 31. g(E, H, "Hello, world"); 32. 33. end; 34. 35. procedure f(X0: in out Vector; Y0: in out Vector10; 36. X1: Vector; Y1: Vector10) is 37. begin 38. null; 39. end; 40. 41. procedure g(A0: in out Array2D; B0: in out Array2D3_4; S: STRING) is 42. begin 43. null; 44. end; 45. 46. end arrays1;
The file `main1.adb' contains:
1. with arrays1; 2. procedure main1 is 3. begin 4. arrays1.main(8); 5. end;
The following is a terminal session with this
program. The program was compiled using GNAT
Comments are preceded by gdb #
. When re-creating this
demonstration, do not expect identical addresses.
(gdb) break main1 Breakpoint 1 at 0x2ebc: file main1.adb, line 4. (gdb) run ... Breakpoint 1, main1 () at main1.adb:4 4 arrays1.main(8); (gdb) step arrays1.main (n=8) at arrays1.adb:2 2 procedure main(N: INTEGER) is (gdb) break f Breakpoint 2 at 0x2d00: file arrays1.adb, line 30. (gdb) # Just 'f' works .... (gdb) # But we are allowed to qualify: (gdb) break arrays1.g Breakpoint 3 at 0x2db8: file arrays1.adb, line 36. (gdb) cont Continuing. Breakpoint 2, arrays1.f (x0=0xf7fff5e8, y0=0xf7fff750, x1=0xf7fff560, y1=0xf7fff728) at arrays1.adb:30 30 procedure f(X0: in out Vector; Y0: in out Vector10; (gdb) # All array parameters are printed as pointers. (gdb) # AdaGDB allows them to be dereferenced. (gdb) p x0.all $1 = {3, 5, 7, 9, 11, 13, 15, 17} (gdb) p y0.all $2 = {-1, -2, -3, -4, -5, -6, -7, -8, -9, -10} (gdb) p x1.all $3 = {2, 4, 6, 8, 10, 12, 14, 16} (gdb) p y1.all $4 = {-2, -4, -6, -8, -10, -12, -14, -16, -18, -20} (gdb) # Parameters x0 and x1 are represented by fat pointers. (gdb) # We'll change temporarily to ordinary GDB and look at what they (gdb) # really are. (gdb) set language c Warning: the current language does not match this frame. (gdb) p x0 $5 = {P_ARRAY = 0xf7fff5e8, P_BOUNDS = 0xf7fff6e0} (gdb) p x1 $6 = {P_ARRAY = 0xf7fff560, P_BOUNDS = 0xf7fff6d0} (gdb) set language auto (gdb) # Attributes of arrays (gdb) p x0'first $7 = 1 (gdb) p x0'last $8 = 8 (gdb) # Indexing (gdb) p x0(1) $9 = 3 (gdb) cont Continuing. Breakpoint 3, arrays1.g (a0=0xf7fff438, b0=0xf7fff6f8, s=0x2608) at arrays1.adb:36 36 procedure g(A0: in out Array2D; B0: in out Array2D3_4; S: STRING) is (gdb) p a0'first(1) $10 = 1 (gdb) p a0'first(2) $11 = 3 (gdb) # 2-D indexing (gdb) p a0(1,3) $12 = 26 (gdb) # A more complex expression (at the moment, no special treatment (gdb) # for the Ada BOOLEAN type, so it prints as 1). (gdb) p a0(a0'first(1), a0'first(2)) < 42 and then s(2) = 'e' $13 = 1 (gdb) # Ranges (gdb) p a0'last(1) $14 = 8 (gdb) p 4 in a0'range(1) $15 = 1 (gdb) p 4 not in a0'range(1) $16 = 0 (gdb) p 0 in a0'range(1) $17 = 0 (gdb) p 4 in a0'first .. a0'last $18 = 1 (gdb)
Our second example of the use of gdb
involves the use of
user-defined operators.
It is adapted from a program by Charles W. Kann. We first give
the transcript and then the source file listings.
(gdb) # Look up the function "/" in Rationals (gdb) # (just its address; don't call it). (gdb) p Rationals."/"'access Multiple matches for Rationals."/" [0] cancel [1] rationals."/" at rationals.adb:45 [2] rationals."/" at rationals.adb:141 > 0 cancelled (gdb) break testrat1 Breakpoint 1 at 0xbd14: file testrat1.adb, line 8. (gdb) # File input0 contains 7/8 and 5/6 on two lines. (gdb) run < input0 ... Breakpoint 1, testrat1 () at testrat1.adb:8 8 A: Rationals.Rational; (gdb) next 9 B: Rationals.Rational; (gdb) next 10 C: Rationals.Rational; (gdb) next 11 D: Rationals.Rational; (gdb) next 12 E: Rationals.Rational; (gdb) next 13 F: Rationals.Rational; (gdb) next 17 A := Rationals."/"(1,3); (gdb) next 18 B := Rationals."/"(2,-4); (gdb) next 19 Text_IO.Put(Item => "A = "); (gdb) p A $1 = {numerator = 1, denominator = 3} (gdb) p B $2 = {numerator = -1, denominator = 2} (gdb) p A+B -- User-defined operator $3 = {numerator = -1, denominator = 6} (gdb) p A*B $5 = {numerator = -1, denominator = 6} (gdb) p a/B $6 = {numerator = -2, denominator = 3} (gdb) p abs(a*b) $7 = {numerator = 1, denominator = 6} (gdb) p "/"(5,10) -- This always calls a user-defined function $8 = {numerator = 1, denominator = 2} (gdb) p a<b $9 = false (gdb) p a>b $10 = true (gdb) p a<= b $11 = false (gdb) next 20 Rational_IO.Put(Item => A); (gdb) next 21 Text_IO.New_Line; (gdb) next A = 1/3 22 Text_IO.Put(Item => "B = "); (gdb) break 43 Breakpoint 2 at 0xbf70: file testrat1.adb, line 43. (gdb) cont Continuing. B = -1/2 Enter rational number C > Enter rational number D > E = A + B is -1/6 F = C * D is 35/48 Breakpoint 2, testrat1 () at testrat1.adb:43 43 Text_IO.Put(Item => "A + E * F is "); (gdb) next 44 Rational_IO.Put(Item => Rationals."+"(A, Rationals."*"(E,F))); (gdb) step rationals."*" (r1=0xf7fff7b0, r2=0xf7fff7a8) at rationals.adb:129 129 N := Numer(R1) * Numer(R2); (gdb) # Here, record parameters show up as pointers, because (gdb) # this is the way they are passed internally. (gdb) # The user must dereference them. (gdb) p r1.all $12 = {numerator = -1, denominator = 6} (gdb) p r2.all $13 = {numerator = 35, denominator = 48} (gdb) cont Continuing. A + E * F is 61/288 Program exited normally.
The file `rationals.ads'
1. PACKAGE Rationals IS 2. 3. -- Specification of the abstract data type for representing 4. -- and manipulating rational numbers. 5. -- All rational quantities in this package are initialized 6. -- to 0/1. 7. 8. TYPE Rational IS PRIVATE; 9. 10. --Operators 11. 12. FUNCTION "/" (X : Integer; Y : Integer) RETURN Rational; 13. -- constructor: returns a rational number in lowest terms 14. -- Pre : X and Y are defined 15. -- Post: returns a rational number 16. -- If Y > 0, returns Reduce(X,Y) 17. -- If Y < 0, returns Reduce(-X,-Y) 18. -- Raises: ZeroDenominator if Y = 0 19. 20. ZeroDenominator: EXCEPTION; 21. 22. FUNCTION Numer (R : Rational) RETURN Integer; 23. FUNCTION Denom (R : Rational) RETURN Positive; 24. -- selectors: 25. -- Pre: R is defined 26. -- Post: Numer (Denom) returns the numerator (denominator) of R; 27. 28. FUNCTION "<" (R1 : Rational; R2 : Rational) RETURN Boolean; 29. FUNCTION "<="(R1 : Rational; R2 : Rational) RETURN Boolean; 30. FUNCTION ">" (R1 : Rational; R2 : Rational) RETURN Boolean; 31. FUNCTION ">="(R1 : Rational; R2 : Rational) RETURN Boolean; 32. -- inquiry operators: comparison of two rational numbers 33. -- Pre : R1 and R2 are defined 34. 35. FUNCTION "+"(R: Rational) RETURN Rational; 36. FUNCTION "-"(R: Rational) RETURN Rational; 37. FUNCTION "ABS"(R: Rational) RETURN Rational; 38. -- monadic arithmetic constructors: 39. -- Pre: R is defined 40. 41. FUNCTION "+"(R1 : Rational; R2 : Rational) RETURN Rational; 42. FUNCTION "-"(R1 : Rational; R2 : Rational) RETURN Rational; 43. FUNCTION "*"(R1 : Rational; R2 : Rational) RETURN Rational; 44. FUNCTION "/"(R1 : Rational; R2 : Rational) RETURN Rational; 45. -- dyadic arithmetic constructors: 46. -- Pre : R1 and R2 are defined 47. 48. PRIVATE 49. -- A record of type Rational consists of a pair of Integer values 50. -- such that the 1st number represents the numerator of a rational 51. -- number and the 2nd number represents the denominator. 52. 53. TYPE Rational IS RECORD 54. Numerator : Integer := 0; 55. Denominator: Positive := 1; 56. END RECORD; 57. END Rationals;
The file `rationals.adb'
1. PACKAGE BODY Rationals IS 2. 3. -- Body of the abstract data type for representing 4. -- and manipulating rational numbers. 5. 6. -- local function GCD, not provided to clients 7. 8. FUNCTION GCD(M: Positive; N: Positive) RETURN Positive IS 9. 10. -- finds the greatest common divisor of M and N 11. -- Pre: M and N are defined 12. -- Post: returns the GCD of M and N, by Euclid's Algorithm 13. 14. TempM: Natural; 15. TempN: Natural; 16. 17. BEGIN -- GCD 18. 19. TempM := M; 20. TempN := N; 21. 22. WHILE TempM /= 0 and TempN /= 0 LOOP 23. IF (TempM > TempN) THEN 24. TempM := TempM REM TempN; 25. ELSE 26. TempN := TempN REM TempM; 27. END IF; 28. END LOOP; 29. 30. RETURN TempN+TempM; 31. 32. END GCD; 33. 34. -- exported operators 35. 36. -- constructor 37. 38. FUNCTION "/" (X : Integer; Y : Integer) RETURN Rational IS 39. 40. G: Positive; 41. 42. BEGIN -- "/" 43. 44. IF Y = 0 THEN 45. RAISE ZeroDenominator; 46. END IF; 47. 48. IF X = 0 THEN 49. RETURN (Numerator => 0, Denominator => 1); 50. END IF; 51. 52. G := GCD(ABS X, ABS Y); 53. IF Y > 0 THEN 54. RETURN (Numerator => X/G, Denominator => Y/G); 55. ELSE 56. RETURN (Numerator => (-X)/G, Denominator => (-Y)/G); 57. END IF; 58. 59. END "/"; 60. 61. -- selectors 62. 63. FUNCTION Numer (R : Rational) RETURN Integer IS 64. BEGIN -- Numer 65. RETURN R.Numerator; 66. END Numer; 67. 68. FUNCTION Denom (R : Rational) RETURN Positive IS 69. BEGIN -- Denom 70. RETURN R.Denominator; 71. END Denom; 72. 73. -- inquiry operators for comparison of Rationals 74. 75. FUNCTION "<" (R1 : Rational; R2 : Rational) RETURN Boolean IS 76. BEGIN 77. RETURN Numer(R1) * Denom(R2) < Numer(R2) * Denom(R1); 78. END "<"; 79. 80. FUNCTION ">" (R1 : Rational; R2 : Rational) RETURN Boolean IS 81. BEGIN 82. RETURN R2 < R1; 83. END ">"; 84. 85. FUNCTION "<=" (R1 : Rational; R2 : Rational) RETURN Boolean IS 86. BEGIN 87. RETURN not (R2 < R1); 88. END "<="; 89. 90. FUNCTION ">=" (R1 : Rational; R2 : Rational) RETURN Boolean IS 91. BEGIN 92. RETURN not (R1 < R2); 93. END ">="; 94. 95. -- monadic arithmetic operators 96. 97. FUNCTION "+"(R : Rational) RETURN Rational IS 98. BEGIN -- "+" 99. RETURN R; 100. END "+"; 101. 102. FUNCTION "-"(R : Rational) RETURN Rational IS 103. BEGIN -- "-" 104. RETURN (-Numer(R)) / Denom(R); 105. END "-"; 106. 107. FUNCTION "ABS"(R : Rational) RETURN Rational IS 108. BEGIN -- "ABS" 109. RETURN (ABS Numer(R)) / Denom(R); 110. END "ABS"; 111. 112. -- dyadic arithmetic operators 113. 114. FUNCTION "+"(R1 : Rational; R2 : Rational) RETURN Rational IS 115. N: Integer; 116. D: Positive; 117. BEGIN -- "+" 118. N := Numer(R1) * Denom(R2) + Numer(R2) * Denom(R1); 119. D := Denom(R1) * Denom(R2); 120. RETURN N/D; -- compiler will use fraction constructor here! 121. END "+"; 122. 123. FUNCTION "*"(R1 : Rational; R2 : Rational) RETURN Rational IS 124. N: Integer; 125. D: Positive; 126. BEGIN 127. N := Numer(R1) * Numer(R2); 128. D := Denom(R1) * Denom(R2); 129. RETURN N/D; -- compiler will use fraction constructor here! 130. END "*"; 131. 132. FUNCTION "-"(R1 : Rational; R2 : Rational) RETURN Rational IS 133. BEGIN 134. RETURN R1 + (-R2); 135. END "-"; 136. 137. FUNCTION "/"(R1 : Rational; R2 : Rational) RETURN Rational IS 138. BEGIN -- stub 139. RETURN (Numer(R1)*Denom(R2)) / (Numer(R2)*Denom(R1)); 140. END "/"; 141. 142. END Rationals;
The file `rational_io.ads'
1. WITH Rationals; 2. USE Rationals; 3. WITH Text_IO; 4. PACKAGE Rational_IO IS 5. 6. -- Specification of the input/output package for Rationals 7. 8. -- input operations to read a Rational from terminal or file 9. 10. PROCEDURE Get (Item : OUT Rational); 11. PROCEDURE Get (File: IN Text_IO.File_Type; Item : OUT Rational); 12. -- Pre : File is open 13. -- Post: The first integer number read is the numerator of Item; 14. -- the second integer number is the denominator of Item. 15. -- A "/" between the two numbers is optional. 16. -- The Rational constructor "/" is called 17. -- to produce a rational in reduced form. 18. 19. 20. -- output operations to display a Rational on terminal or 21. -- write it to an external file 22. 23. PROCEDURE Put (Item : IN Rational); 24. PROCEDURE Put (File: IN Text_IO.File_Type; Item : IN Rational); 25. -- Pre : Item is defined; File is open 26. -- Post: displays or writes the numerator 27. and denominator of Item. 28. END Rational_IO;
The file `rational_io.adb'
1. WITH Text_IO; 2. WITH My_Int_IO; 3. WITH Rationals; USE Rationals; 4. PACKAGE BODY Rational_IO IS 5. 6. -- Body of the input/output package for Rationals 7. 8. -- input procedures 9. 10. PROCEDURE Get (File: IN Text_IO.File_Type; Item : OUT Rational) IS 11. 12. N: Integer; 13. D: Integer; 14. Dummy: Character; -- dummy character to hold the "/" 15. 16. BEGIN -- Get 17. 18. My_Int_IO.Get(File => File, Item => N); 19. Text_IO.Get (File => File, Item => Dummy); 20. My_Int_IO.Get(File => File, Item => D); 21. Item := N/D; 22. 23. END Get; 24. 25. PROCEDURE Get (Item : OUT Rational) IS 26. 27. BEGIN -- Get 28. 29. Get(File => Text_IO.Standard_Input, Item => Item); 30. 31. END Get; 32. 33. -- output procedures 34. 35. PROCEDURE Put (File : IN Text_IO.File_Type; Item : IN Rational) IS 36. 37. BEGIN -- Put 38. 39. My_Int_IO.Put(File => File, Item => Numer(Item), Width => 1); 40. Text_IO.Put(File => File, Item => '/'); 41. My_Int_IO.Put(File => File, Item => Denom(Item), Width => 1); 42. 43. END Put; 44. 45. PROCEDURE Put (Item : IN Rational) IS 46. 47. BEGIN -- Put 48. 49. Put(File => Text_IO.Standard_Output, Item => Item); 50. 51. END Put; 52. 53. END Rational_IO;
The file `testrat1.adb':
1. WITH Text_IO; 2. WITH Rationals; 3. WITH Rational_IO; 4. PROCEDURE TestRat1 IS 5. 6. -- Very rudimentary test of package Rationals 7. 8. A: Rationals.Rational; 9. B: Rationals.Rational; 10. C: Rationals.Rational; 11. D: Rationals.Rational; 12. E: Rationals.Rational; 13. F: Rationals.Rational; 14. 15. BEGIN -- TestRat1 16. 17. A := Rationals."/"(1,3); 18. B := Rationals."/"(2,-4); 19. Text_IO.Put(Item => "A = "); 20. Rational_IO.Put(Item => A); 21. Text_IO.New_Line; 22. Text_IO.Put(Item => "B = "); 23. Rational_IO.Put(Item => B); 24. Text_IO.New_Line; 25. 26. -- Read in rational numbers C and D. 27. Text_IO.Put(Item => "Enter rational number C > "); 28. Rational_IO.Get(Item => C); 29. Text_IO.Put(Item => "Enter rational number D > "); 30. Rational_IO.Get(Item => D); 31. Text_IO.New_Line; 32. 33. E := Rationals."+"(A,B); -- form the sum 34. Text_IO.Put(Item => "E = A + B is "); 35. Rational_IO.Put(Item => E); 36. Text_IO.New_Line; 37. 38. F := Rationals."*"(C,D); -- form the product 39. Text_IO.Put(Item => "F = C * D is "); 40. Rational_IO.Put(Item => F); 41. Text_IO.New_Line; 42. 43. Text_IO.Put(Item => "A + E * F is "); 44. Rational_IO.Put(Item => Rationals."+"(A, Rationals."*"(E,F))); 45. Text_IO.New_Line; 46. 47. END TestRat1;
It is possible to use gdb's C mode for Ada programs when the Ada mode is
not available or not behaving as expected, but C syntax must be used
instead of Ada syntax for expressions. Use set language c
to
switch to C mode, and set language ada
to switch back to Ada
mode. In C mode the following naming conventions allow you to find the
Ada entities defined in your program:
package_name__subprogram_name
. Note the double underscores
separating package name from subprogram name.
See section The External Symbol Naming Scheme of GNAT for more information.
gdb
Exceptions can be caught by breaking in the __gnat_raise
function and
then entering a bt
or where
command. It is even possible to
use conditional breakpoints to trap specific exceptions. However, there is
no simple way to trap only on an unhandled exception at the current time.
gdb
The Ada aware version of gdb
has some facilities for handling
Ada tasks. To use these facilities you need to include the file
System.Task_Debug in your program (by including a with
statement that mentions this file), and of course you must compile
your program with the -g
switch.
You can then use the info tasks
command in gdb
to
display the current status of tasks in the system.
The format of the display is shown by this example:
(gdb) info tasks TASK NAME ID STATE THREAD * 1 t3 698a8 Active 7 2 t2 66af8 Active 8 3 t1 63d48 Active 9 4 main_task 5a1d8 Await_Dependents 1
In the table that is displayed
TASK
gdb
tables
NAME
ID
Task_Id
value
STATE
THREAD
thread_id
value (from the C thread library)
For example, given the program:
1. with System.Task_Debug; 2. with Ada.Text_IO; 3. 4. procedure Test_Task_Debug is 5. 6. use Ada.Text_IO; 7. 8. task type T (Index : Integer); 9. task body T is 10. begin 11. loop 12. Put_Line ("task #" & Integer'Image (Index) & " running"); 13. flush; 14. delay 2.0; 15. end loop; 16. end T; 17. 18. procedure Start is 19. begin 20. null; 21. end Start; 22. 23. T1 : T (1); 24. T2 : T (2); 25. T3 : T (3); 26. 27. begin 28. Start; 29. end Test_Task_Debug;
Compiling the above program with the -g
switch and
running it under gdb
, we have the following sample session
showing the use of the info threads command:
(gdb) b test_task_debug Breakpoint 1 at 0x3fb3c: file test_task_debug.adb, line 30. (gdb) run Starting program: /nile.d/charlet/gdb/debug/test_task_debug Breakpoint 1, test_task_debug () at test_task_debug.adb:30 30 Start; (gdb) info tasks TASK NAME ID STATE THREAD * 1 main_task 5a0e0 Active 1 (gdb) b 27 Breakpoint 2 at 0x3fd08: file test_task_debug.adb, line 27. (gdb) cont Continuing. Breakpoint 2, test_task_debug () at test_task_debug.adb:27 27 T3 : T (3); (gdb) info tasks TASK NAME ID STATE THREAD 1 main_task 5a0e0 Active 1 * 2 t2 66a70 Created 0 3 t1 63cc0 Created 0 (gdb) b 15 Breakpoint 3 at 0x3f968: file test_task_debug.adb, line 15. (gdb) cont Continuing. task # 3 running Breakpoint 3, test_task_debug.tB (_task=0xeffff7b0) at test_task_debug.adb:15 15 flush; (gdb) info tasks TASK NAME ID STATE THREAD 1 main_task 5a0e0 Active 1 2 t2 66a70 Can_Activate 8 3 t1 63cc0 Can_Activate 9 * 4 t3 69d78 Active 7 (gdb)
The GNAT system provides a number of options that allow a trade off between
The defaults if no options are selected are aimed at improving the speed of compilation and minimizing dependences at the expense of performance of the generated code:
These options are suitable for most program development purposes. This chapter describes how you can modify these choises.
By default, GNAT produces all run-time checks except arithmetic overflow checking for integer operations (including division by zero) and checks for access before elaboration on subprogram calls.
Two gnat switches, -gnatp
and -gnato
allow this default to
be modified. See section Run-time Checks.
Our experience is that the default is suitable for most development purposes.
We treat integer overflow and elaboration checks specially because these are quite expensive and in our experience are not as important as other run-time checks in the development process.
Note that the setting of the switches controls the default setting of
the checks. They may be modified using either pragma Suppress
(to
remove checks) or pragma Unsuppress
(to add back suppressed
checks) in the program source.
The default is optimization off. This results in the fastest compile
times, but GNAT makes absolutely no attempt to optimize, and the
generated programs are considerably larger and slower. You can use the
-On
switch, where n is an integer from 0 to 3, on the
gcc
command to control the optimization level:
-O0
-O1
-O2
-O3
The penalty in compilation time, and the improvement in execution time, both depend on the particular application and the hardware environment. You should experiment to find the best level for your application.
Note: Unlike the case with some other compiler systems, gcc
has
been tested extensively at all optimization levels. There are some bugs
which appear only with optimization turned on, but there have also been
bugs which show up only in unoptimized code. Selecting a lower
level of optimization does not improve the reliability of the code
generator, which in practice is highly reliable at all optimization
levels.
A call to a subprogram in the current unit is inlined if all the following conditions are met:
-O1
.
gcc
cannot support in inlined subprograms.
pragma Inline
applies to the subprogram or it is
small and automatic inlining (optimization level -O3
) is
specified.
Calls to subprograms in with
'ed units are normally not inlined.
To achieve this level of inlining, the following conditions must all be
true:
-O1
.
gcc
cannot
support in inlined subprograms.
pragma Inline
for the subprogram.
-gnatn
switch
is used in the gcc
command line @end itemize
Note that specifying the -gnatn
switch causes additional
compilation dependencies. Consider the following:
package R is procedure Q; pragma Inline Q; end R; package body R is ... end R; with R; procedure Main is begin ... R.Q; end Main;With the default behavior (no
-gnatn
switch specified), the
compilation of the Main
procedure depends only on its own source,
`main.adb', and the spec of the package in file `r.ads'. This
means that editing the body of R
does not require recompiling
Main
.
On the other hand, the call R.Q
is not inlined under these
circumstances. If the -gnatn
switch is present when Main
is compiled, the call will be inlined if the body of Q
is small
enough, but now Main
depends on the body of R
in
`r.adb' as well as the spec. This means that if the body is edited,
the main program must be recompiled. Note that this extra dependency
occurs whether or not the call is in fact inlined by gcc
.
Note: The -fno-inline
switch
can be used to prevent
all inlining. This switch overrides all other conditions and ensures
that no inlining occurs. The extra dependencies resulting from
-gnatn
will still be active, even if
this switch is used to suppress the resulting inlining actions.
-a
(gnatls
)
-A
(gnatmake
)
-a
(gnatmake
)
-aI
(gnatmake
)
-aL
(gnatmake
)
-aO
(gnatmake
)
-B
(gcc
)
-b
(gcc
)
-b
(gnatbind
)
-bargs
(gnatmake
)
-c
(gcc
)
-c
(gnatbind
)
-c
(gnatmake
)
-cargs
(gnatmake
)
-d
(gnatls
)
-e
(gnatbind
)
-f
(gnatbind
)
-f
(gnatmake
)
-fno-inline
(gcc
)
-g
(gcc
)
-g
(gnatlink
)
-gnat83
(gcc
)
-gnat95
(gcc
)
-gnata
(gcc
)
-gnatb
(gcc
)
-gnatc
(gcc
)
-gnatdc
switch
-gnate
(gcc
)
-gnatE
(gcc
)
-gnatf
(gcc
)
-gnatg
(gcc
)
-gnati
(gcc
)
-gnatj
(gcc
)
-gnatk
(gcc
)
-gnatl
(gcc
)
-gnatlink
(gnatlink
)
-gnatm
(gcc
)
-gnatn
switch
-gnatn
(gcc
), -gnatn
(gcc
)
-gnato
(gcc
), -gnato
(gcc
)
-gnatp
(gcc
), -gnatp
(gcc
)
-gnatq
(gcc
)
-gnatr
(gcc
)
-gnats
(gcc
)
-gnatt
(gcc
)
-gnatu
(gcc
)
-gnatv
(gcc
)
-gnatwe
(gcc
)
-gnatwl
(gcc
)
-gnatws
(gcc
)
-h
(gnatbind
)
-h
(gnatls
)
-I-
(gcc
)
-I-
(gnatmake
)
-I
(gcc
)
-i
(gnatmake
)
-I
(gnatmake
)
-j
(gnatmake
)
-k
(gnatchop
)
-k
(gnatmake
)
-l
(gnatbind
)
-L
(gnatmake
)
-largs
(gnatmake
)
-m
(gnatbind
)
-m
(gnatmake
)
-M
(gnatmake
)
-n
(gnatbind
)
-n
(gnatmake
)
-o
(gcc
)
-O
(gcc
), -O
(gcc
)
-o
(gnatbind
)
-o
(gnatlink
)
-o
(gnatls
)
-o
(gnatmake
)
-q
(gnatmake
)
-r
(gnatbind
)
-r
(gnatchop
)
-S
(gcc
)
-s
(gnatbind
)
-s
(gnatchop
)
-s
(gnatls
), -s
(gnatls
)
-t
(gnatbind
)
-u
(gnatls
)
-v
(gcc
)
-V
(gcc
)
-v
(gnatbind
)
-v
(gnatlink
)
-v
(gnatmake
)
-w
(gnatchop
)
-we
(gnatbind
)
-ws
(gnatbind
)
-Wuninitialized
(gcc
)
-x2
(gnatf
)
-x3
(gnatf
)
-x4
(gnatf
)
-x5
(gnatf
)
-x6
(gnatf
)
-x
(gnatbind
)
gnat.adc
gnatmake
gnatmake
This document was generated on 27 August 1997 using the texi2html translator version 1.51.