Project:Ada/DevReference

From Gentoo Wiki
Jump to:navigation Jump to:search
Warning, this page is a work in progress by George (talk | contribs). Treat its contents with caution.

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.

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 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):

CODE Setting up bootstrap environment in gnatbuild.eclass
	# 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:

CODE gnatbuild_src_unpack structure
# 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:

CODE gnat-gpl-3.4.5.2005.ebuild's 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:

CODE gnat 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:

CODE gnat profile specs location
# 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:

  1. copy the source directory, so that the build does not poison the original,
  2. activate the next compiler profile,
  3. call the lib_compile callback, which now holds stuff that normally happens in src_compile of a normal ebuild.
  4. 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 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.

CODE 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
		# 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