Project:Ada/DevReference
This is a guide to internal workings of the gnat and gnatbuild eclasses and eselect-gnat module, as well as an authoritative reference to packaging principles of Ada libs and other related packages.
Overview
Introduction
Before you start on the internals of the Ada packages you may want to go through the user guide (yet to be written) 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
.
gnatbuild.eclass and eselect-gnat
General notes
The gnatbuild.eclass
has been modeled 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.
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 problematic), 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 refer to the appropriate eclass or ebuild in portage to see all of the code):
# Set some paths to our bootstrap compiler.
export PATH="${GNATBOOT}/bin:${PATH}"
# !ATTN! the *installed* compilers have ${PN} as part of their
# LIBPATH, while the *bootstrap* uses hardset "gnatgcc" in theirs
# (which is referenced as GNATLIB below)
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 toolchain has largely 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. For example the src_unpack
has the following form:
# common unpack stuff
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}
# patching gcc sources, following the toolchain
EPATCH_MULTI_MSG="Applying Gentoo patches ..." \
epatch "${FILESDIR}"/patches/*.patch
...
;;
common_prep)
# Prepare the gcc source directory
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
:
src_unpack() {
gnatbuild_src_unpack base_unpack
# prep gcc sources for Ada
mv "${GNATSOURCE}/src/ada" "${S}/gcc"
cd "${S}"
epatch ${WORKDIR}/${PN}-gcc-${SLOT}.diff
gnatbuild_src_unpack common_prep
# one of the converted gcc->gnatgcc in common_prep needs to stay gcc in
# fact in this version
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 installation 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 convention. After much discussion in some long-forgotten 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 "usual 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, resembling 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 virtual: virtual/ada
that may be populated with ada-1983
, ada-1995
and ada-2005
, providing 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:
# set our install locations
PREFIX=${GNATBUILD_PREFIX:-/usr} # not sure we need this hook, but may be..
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}
# ATTN! the one below should match the path defined in eselect-gnat module
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 modeled 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:
# ATTN! the one below should match the path defined in eselect-gnat module
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 purposely 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
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 getting 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 insrc_compile
of a normal ebuild. - call the
lib_install
callback. This function is supposed to be similar to the normalsrc_install
except that it only needs to concern itself with installing the gnat-profile specific stuff. The compiled.a
or.so
files or config scripts are the most common "ingredients" that are processed by it.
and cycle through these steps until we go through all the installed gnat
compilers. The following code fragment is responsible for doing just this.
gnat_src_compile() {
...
compilers=( $(find_compilers ) )
if [[ -n ${compilers[@]} ]] ; then
local i
for (( i = 0 ; i < ${#compilers[@]} ; i = i + 1 )) ; do
# copy sources
mkdir ${DL}
cp -dpR "${S}" ${SL}
# setup environment
generate_envFile ${compilers[${i}]} ${BuildEnv} && \
expand_BuildEnv ${BuildEnv} && \
. ${BuildEnv} || die "failed to switch to ${compilers[${i}]}"
...
# call compilation callback
cd ${SL}
gnat_filter_flags ${compilers[${i}]}
lib_compile ${compilers[${i}]} || die "failed compiling for ${compilers[${i}]}"
# call install callback
cd ${SL}
lib_install ${compilers[${i}]} || die "failed installing profile-specific part for ${compiler
# move installed and cleanup
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
Acknowledgements
We would like to thank the following authors and editors for their contributions to this guide:
- George Shapovalov