Disclaimer :
This document is a work in progress and should not be considered official yet.
|
Developer reference: Internal structure and organization of Ada packages.
1.
Overview
Introduction
Before you start on the internals of the Ada packages you may want to
go through the user guide in case you are not familiar with
how to activate the chosen gnat profile and where to look for the important files.
Ada related packages can be divided into three important categories:
- Compilers and packages that directly extend them.
-
Currently two closely related "brands" are supported:
gnat-gcc released by FSF and gnat-gpl, the
AdaCore version. The primary example of the "extending" package would
be asis, as it is closely tied to a particular version of compiler
and installs directly to the same locations where the specs and libs
of the corresponding gnat go. The packages in this category should use the
gnatbuild.eclass.
- Ada libraries
-
These are built for every installed gnat and their profile-dependent files are
installed to profile specific dirs, similarly to those of gnat, except that they
go in a "library place". This is handled automatically by gnat.eclass, inner
workings of which are discussed below.
- Executables/other programs
-
The stuff that is to be executed directly or otherwise is not supposed to be linked
against. The prominent examples would be gps, c2ada, etc.
These require no special
treatment and can be built in a regular way with any active compiler or can
depend on a particular variant. One particular issue should be observed however.
If the execuables link against any of the
profile-specific Ada libraries, when user switches gnat profile the particular
binary versions of these libs will become unavailable. In fact, the linker will
attempt to select the library by name and will try to link against the binary
incompatible variant, resulting in execution failure. To resolve this, such binaries
should be compiled with LD_RUN_PATH defined and containing the locations of the
needed variants of the libs.
The profiles are switched via eselect-gnat module, the usual way. Its internal workings
are also discussed in the chapter describing
gnatbuild.eclass.
2.
gnatbuild.eclass and eselect-gnat
General notes.
The gnatbuild.eclass has been modelled after the toolchain.eclass,
similarly providing multiple SLOTs tracking the gcc backend variations. One additional
"complication" that we have in Ada case is that there are two related, however different
compilers available, as mentioned above. These are provided as separate packages,
dev-lang/gnat-gcc for FSF's Ada and dev-lang/gnat-gpl for the one by
AdaCore.
Warning: Beware! The last one has changed the license from GMGPL to pure GPL no
so long ago. |
It is possible they change it again to GPL-3 and FSF will likely want to do so as well.
Therefore attention needs to be paid to the licenses when these packages are updated.
gnat (both versions) can be considered a "yet another gcc frontend", therefore
it is built similarly to other gcc based languages. There is, however, a significant
distinction. It may be argued, that Ada is a "real language", in the sense that it
requires an Ada-enabled compiler to build itself. This makes the build procedure
significantly different from, e.g., gpc or gdc in that we first
need to provide a bootstrap compiler and then setup a bootstrap environment.
In practice, the bootstraps need to be created only once, as gcc (and gnat)
internally
build itself twice (stage1 and stage2) and then build the final binary and libs with
stage2. Plus, so far, all the new versions of gcc could be built with the oldest at that time
backend of gnat - 3.4. If, however, a version of gcc is released that cannot
be built with an old bootstrap (for example, the transition from 2.8 to the later versions
was problemmatic), a new one may need to be issued.
If you take a look at the src_compile, you will notice that all the code dealing
with running configure and make is preceded by the block setting many
env vars. Such as (here and everywhere you can refere to the appropriate eclass or ebuild
in portage to see all of the code):
Code Listing 2.1: Setting up bootstrap environment in gnatbuild.eclass |
export PATH="${GNATBOOT}/bin:${PATH}"
GNATLIB="${GNATBOOT}/lib/gnatgcc/${BOOT_TARGET}/${BOOT_SLOT}"
export CC="${GNATBOOT}/bin/gnatgcc"
export INCLUDE_DIR="${GNATLIB}/include"
export LIB_DIR="${GNATLIB}"
export LDFLAGS="-L${GNATLIB}"
... |
These settings serve the purpose of letting the gnat build scripts find the bootstrap
compiler, so that we do not have to depend on having some version of Ada-enabled
gcc already installed on the system. While pretty plain, this part may get somewhat
tricky. What vars you need to set or avoid depends on the version of toolchain
the build host has active. The most "abusive" package in the toolchain was traditionally
binutils. In fact there were many bugs reporting build failures with configure
complaining that CC is
unable to find collect1 or some other part of the bootstrap compiler. The most
common cause of these bugs was related to having an old version of binutils
installed on user's computer. Correspondingly, it was necessary to force gnat to depend
on a appropriately recent version of binutils. Fortunately, it seems that toolschain
has largerly stabilized in the last year or so, as this has not been necessary for
quite a while.
Partitioning of the src_* functions.
Lets take a look at some other gnatbuild.eclass internals. One can notice,
that all the src_* functions are partitioned in semi-independent sections,
as per the eclass guide. For example the src_unpack
has the following form:
Code Listing 2.2: gnatbuild_src_unpack structure |
gnatbuild_src_unpack() {
debug-print-function ${FUNCNAME} $@
[ -z "$1" ] && gnatbuild_src_unpack all
while [ "$1" ]; do
case $1 in
base_unpack)
unpack ${A}
pax-mark E $(find ${GNATBOOT} -name gnat1)
cd ${S}
EPATCH_MULTI_MSG="Applying Gentoo patches ..." \
epatch "${FILESDIR}"/patches/*.patch
...
;;
common_prep)
cd "${S}/gcc"
touch cstamp-h.in
...
;;
all)
gnatbuild_src_unpack base_unpack common_prep
;;
esac
shift
done
}
|
This allows the subsections to be called independently from within overriding
function, such as would be an ebuild's src_unpack in this case. For example
gnat-gpl-3.4.5.2005.ebuild has the following in its src_unpack:
Code Listing 2.3: gnat-gpl-3.4.5.2005.ebuild's src_unpack |
src_unpack() {
gnatbuild_src_unpack base_unpack
mv "${GNATSOURCE}/src/ada" "${S}/gcc"
cd "${S}"
epatch ${WORKDIR}/${PN}-gcc-${SLOT}.diff
gnatbuild_src_unpack common_prep
sed -i -e 's:(Last3 = "gnatgcc"):(Last3 = "gcc"):' "${S}/gcc/ada/makegpr.adb"
} |
gnat_src_compile and gnat_src_install are partitioned in a similar
way, allowing easy modifications to be performed at every step. Although,
as compilers from both FSF and ACT are becoming more unified,
this is rarely necessary in later versions.
SLOTs and virtuals.
Gentoo has long had support for parallel istallation of different major package
versions. Yes, I am talking about the famous SLOT mechanism. As here we are dealing with
multiple compiler variants that are supposed to be code-compatible, it only makes sense
to make a good use of this mechanism. It only needed to be modified to accept multiple
package names in our case. As all the SLOT "inner workings" are done right in the
eclass/ebuild code, there is nothing special about it. All what was necessary to do,
was to extend SLOT logic to accept proper package names.
The important part of getting SLOTs right is to use suitable naming conventon. After
much discussion in bug #.. the following naming scheme was adopted:
${PN}-${GCCVER}.${ACTVer} (may be followed by the usual -rX for Gentoo specific
revisions). Here
| ${PN} |
Stands for the package name. Right now we have gnat-gcc for FSF's variant
and gnat-gpl for the one by AdaCore. More may be added, should we try to add
some other Ada compiler to the tree.
|
| ${GCCVer} |
The gcc backend version. Something like 3.4.6 or 4.2.0. This part is
split in a way similar to how it is done in toolchain.eclass to obtain
GCCMAJOR .. GCCRELEASE vars. The "in-package" part of qualifier
(denoted as the SLOT, to keep it compatible with the "usial way") is determined
solely by this var and ecuals (as in toolchain) ${GCCBRANCH}.
While ACT omits this qualifier in its gnat versions, it is necessary to
have it supplied for consistency and proper SLOT calculation.
|
| ${ACTVer} |
The Ada-specific part of the version. The name got "inspired" by ACT variants
always (so far) coming with some ACT specific lable. For example
gnat-2006. In the gnat-gcc case this part is empty.
|
Let's consider two possible examples of fully qualified package names.
- gnat-gcc-4.2.0
-
gnat-gcc - an FSF version of gnat compiler, released
along with the 4.2.0 version of gcc.
- gnat-gpl-3.4.6.2006-r1
-
An AdaCore's (gnat-gpl) version of gnat based on gcc-3.4.6
backend with the Ada specific code as in gnat-gpl-2006, Gentoo specific
revision -r1.
As with gcc, the code produced by compiler is only binary compatible
within the same major version (SLOT). While theoretically one can try combining
object files produced by gnat-gpl to those produced by gnat-gcc
having identical backend version, such combinations are not supported. One must
also be aware of potential differences in the produced ali files.
As such, both ${PN}-${SLOT} components are defining the "operational SLOT"
or profile specification. Moreover, a fully qualified profile name will contain
an additional component - ${ARCH} to allow for the possibility of crosscompilation.
However the description of this is left to the section dealing with eselect-gnat
internals.
As is often the case with packages providing similar functionality, we provide a virtual
that tracks various gnat versions: virtual.gnat. This is a "new style"
(that is, resembpling a regular package) virtual that tracks the gcc backend
versions, the 3.4, 4.1 and 4.2 are provided as of now.
Also, as Ada-2005 standard has been recently approved, some packages are starting to
require and Ada-2005 capable compiler (of which only gnat-gpl-2007 can be considered
to be providing a reasonably complete subset of Ada-2005 functionality at this moment).
It becomes necessary to provide another vortual: virtual/ada that may be populated
with ada-1983, ada-1995 and ada-2005, provdiding dependencies on
appropriate versions of gnat.
Install locations
The installation procedure mimics (again) that of gcc. The only principal
difference (at the time of this writing) is that gnat compilers have
been already transitioned
to make use of $(get_libdir) where proper, while toolchain has not
done so yet. The following global vars are defined to manage the install locations:
Code Listing 2.4: gnat install locations |
PREFIX=${GNATBUILD_PREFIX:-/usr}
LIBPATH=${PREFIX}/$(get_libdir)/${PN}/${CTARGET}/${SLOT}
LIBEXECPATH=${PREFIX}/libexec/${PN}/${CTARGET}/${SLOT}
INCLUDEPATH=${LIBPATH}/include
BINPATH=${PREFIX}/${CTARGET}/${PN}-bin/${SLOT}
DATAPATH=${PREFIX}/share/${PN}-data/${CTARGET}/${SLOT}
CONFIG_PATH="/usr/share/gnat/eselect"
gnat_config_file="${D}/${CONFIG_PATH}/${CTARGET}-${PN}-${SLOT}" |
Lets go over these locations in more detail.
| Path/variable |
Description |
| BINPATH |
This is where the libraries are installed. The .so files and such.
The variable itself also serves as a top location for INCLUDEPATH
|
| LIBEXECPATH |
As per FHS, the location of "other executables". Needs to be on the PATH
as well. Like gcc, gnat keeps here the compiler driver related
files: cc1, collect2 and gnat1.
|
| LIBPATH |
This is where the libraries are installed. The .so files and such.
The variable itself also serves as a top location for INCLUDEPATH
|
| INCLUDEPATH |
Specs go here. Following toolchain this resides under LIBPATH.
The main reason is that, unlike with the libs, we want to keep specs for every
compiler variant separate.
|
| DATAPATH |
Man, info, locale files. Again, these may differ between gnat variants,
so keep them separate.
|
| CONFIG_PATH |
Where data describing these install location is stored for the gnat.eselect
module. The gnat_config_file variable points to the file containing
the profile-specific data.
|
eselect-gnat workings
eselect-gnat was modelled after the eselect-compiler module, that was
supposed to supersede the gcc-config script at the time of development. Of
course that got shot down and now we are "stuck" with gnat using the "more
modern" tool, while gcc is still handled by legacy gcc-config script.
Nonetheless, eselect-gnat works well with the way Ada support is setup in
Gentoo, and below I describe its inner workings.
Inheriting all the general features of gcc, the run-time behavior of
gnat can be extensively regulated by env vars. As such, the approach that was
adopted consists of the compiler producing the "specs" file tat contains all the
principal locations during its build, and eselect-gnat using this generated
file to create an appropriate entry under the /etc/env.d. There are no
additional "hidden entries", everything rotates around the way env settings are
managed in Gentoo. Thus, eselect gnat set, update and unset
actions directly operate on the env entry (re)creating a new or deleting an existing
one. This env file has the name of the form ${MARKER}${gnat_profile} with
MARKER currently set to MARKER="55gnat-" and gnat_profile
having a usial form of ${ARCH}-${compiler_name}-${SLOT}, such as
x86_64-pc-linux-gnu-gnat-gcc-4.2 for example. The generation of the original
specs file for each compiler is performed by the create_eselect_conf function
in the prep-env part of the gnatbuild_src_install function in
gnatbuild.eclass. And the specs file location is defined near the op of
gnatbuild.eclass as:
Code Listing 2.5: gnat profile specs location |
CONFIG_PATH="/usr/share/gnat/eselect"
gnat_config_file="${D}/${CONFIG_PATH}/${CTARGET}-${PN}-${SLOT}" |
The ${CONFIG_PATH} serves as the top directory where all the information
necessary for the gnat.eselect is stored. Every gnat installs a single file
that has a name/SLOT specific name, thus overwriting the one for older version
within the same grouping but avoiding collision with a different compiler or SLOT.
Every installed library created a subdir under ${CONFIG_PATH} that, in turn,
contains library spec files for every gnat profile it was compiled with. More on the
libs below, in the corresponding section. The location for ${CONFIG_PATH} has
been purposedly chosen outside normally config-protected locations, so that spec
files are removed when the corresponding version of compiler is unmerged. The same
goes for the libs.
Therefore, figuring out what variants of gnat are installed is a simple matter of
scanning ${CONFIG_PATH} for the specs files and then splitting them into the
${ARCH}, ${compiler_name} and SLOT components. Right now every
lib is supposed to create a separate directory for itself and every regular file
under ${CONFIG_PATH} is expected to be a specs file for some compiler
variant.
eselect-gnat provides common actions, such as show and list as
well as set and update. As all the relevant information for all
gnat profiles is concentrated under ${CONFIG_PATH, determination of
The set accepts the name of the gnat
profile to activate as an argument and update simply rege
3.
gnat.eclass and libraries
Overview
As was described above, in Gentoo we provide multiple SLOTted versions of gnat
compilers that users can have installed in parallel. Unlike with many other languages,
Ada compilers tend to follows the standard rather tightly. Therefore most, if not all,
the common libs are expected to compile cleanly with any compiler, provided it implements
the necessary version of Ada standard. Therefore it was decided to provide users with the
ability to have libs compiled for all the installed gnat variants and to make eselect
to switch to an appropriate lib image when a certain gnat profile is activated.
The libs are managed by gnat.eclass, which automates their handling. The principal
action happens in the src_compile function. All the installed gnat profiles are
geting activated in turn and the lib gets compiled multiple times for every profile.
The src_install function then collects the compiled parts and installs them
in appropriate locations. The detailed workings of the eclass will be considered
below. Here I will just note again, that gnat.eclass is designed to be used
with the "common Ada libs" and thus should be used only where appropriate. It makes
no sense to use it to build some directly executable application for example.
Detailed sequence of multi-build.
As was already mentioned, the principal "magic" happens in src_compile. Here
we have to:
- copy the source directory, so that the build does not poison the original,
- activate the next compiler profile,
-
call the lib_compile callback, which now holds stuff that normally
happens in src_compile of a normal ebuild.
- call the lib_install callback. This function is supposed to be similar to
the normal src_install except that it only needs to concern itself with
installing the gnat-profile specific stuff. The compiled .a or .so
files or config scrits are the most common "ingredients" that are processed by it.
and cycle through these steps untill we go through all the installed gnat
compilers. The following code fragment is responsible for doing just this.
Code Listing 3.1: fragment of gnat_src_compile function |
gnat_src_compile() {
compilers=( $(find_compilers ) )
if [[ -n ${compilers[@]} ]] ; then
local i
for (( i = 0 ; i < ${#compilers[@]} ; i = i + 1 )) ; do
mkdir ${DL}
cp -dpR "${S}" ${SL}
generate_envFile ${compilers[${i}]} ${BuildEnv} && \
expand_BuildEnv ${BuildEnv} && \
. ${BuildEnv} || die "failed to switch to ${compilers[${i}]}"
...
cd ${SL}
gnat_filter_flags ${compilers[${i}]}
lib_compile ${compilers[${i}]} || die "failed compiling for ${compilers[${i}]}"
cd ${SL}
lib_install ${compilers[${i}]} || die "failed installing profile-specific part for ${compiler
mv ${DL} ${DL}-${compilers[${i}]}
rm -rf ${SL}
done
else
die "please make sure you have at least one gnat compiler installed!"
fi
} |
Next, the src_install function.
Here, we need to
|