PowerAda Setting Up the PowerAda Project

From OC Systems Wiki!
Jump to: navigation, search

There are many factors that determine how software files are organized on disk. Perhaps the most common determining factor is history: the software was already organized in a certain way when the effort started, because you are porting from another compiler or doing maintenance or enhancement to existing software.

In any case, the organization certainly consists of one or more UNIX directories containing one or more Ada files, as well as files written in other languages, documentation, PDL, data, test cases, shell scripts, and third-party tools, libraries, and bindings. This section discusses how each of these are handled in PowerAda.

Before reading this, you may want to try the "Multiple Directories Demo" for an introduction to some of the issues involved. For a higher-level overview of the PowerAda projects, refer to Chapter 3, "Basic PowerAda Concepts".

Defining the Directory Hierarchy

Generally all the files in the development environment should be placed in a single PowerAda project. Since a project is represented by a single directory tree, this means that you may need to create an additional level in the directory hierarchy to bring different parts of the code together. All work within a PowerAda project is done relative to the project root, which is the parent directory of the code actually under development. A typical organization might consist of top-level directories like

common/  vehicles/ aircraft/

with many levels of subdirectories underneath.

The use of symbolically linked directories in a PowerAda project is not recommended as different applications and shells treat the paths to such directories differently, and the path is the unique identification of the directory used by PowerAda.

You must also be sure that compilation unit names are unique throughout the project unless you are sure that they will never be part of the same library. In PowerAda, a source file's name need have no relation to the Ada compilation unit(s) within it, so unique filenames do not guarantee unique unit names. If the same compilation unit appears in two different sublibraries, the wrong version may be included in your program, and the powerada build tools may attempt to recompile the wrong source file when attempting to bring your program up-to-date.

Associating Ada Files and Sublibraries

Ada source must be compiled into a sublibrary to become part of a program and to be analyzed by many of the PowerAda development tools. All Ada files in a given directory must be associated with a single sublibrary, which is generally located in the same directory as the files themselves. The source that goes into a sublibrary is identified by a special sublibrary files file, called adalib.files.

It is very likely that the files for a program are distributed across several directories and hence (assuming one sublibrary per directory) across several sublibraries. You have to explicitly define the relationship between the source in these directories. If a source file compiled into one sublibrary depends on, or imports, source in another sublibrary, there must be a corresponding import relationship between the sublibraries themselves. This ordered relationship between the sublibraries is determined by the sublibrary imports file, called adalib.imports.

Both adalib.imports and adalib.files are text files that must be created and maintained by the user along with the Ada source files and should be included in the same configuration management system used for the source code. The following sections discuss creating these files for your existing directory tree

Creating Initial adalib.files Files

It is usually fairly simple to record the Ada files that will be compiled into each sublibrary. Assume all your Ada files end in ".ada". Then for each directory, create a file called adalib.files, which contains the project-relative name of all the files in that directory:

cd /project_root
for i in . $(find * -type d -print)
  ls $i/*.ada > $i/adalib.files
  if [ ! -s $i/adalib.files ] ; then
     # there were no Ada files
     rm $i/adalib.files

Creating Initial adalib.imports Files

The adalib.imports files identify the relationships between the different directories into which Ada source will be compiled. In most cases, the source in each directory is compiled into a sublibrary in that same directory. Therefore, for each directory D, create an adalib.imports indicating what other sublibraries (directories) contain Ada units WITH'd by the source in D. The dependencies between the sublibraries correspond to the with or import dependencies between the Ada compilation units that will be compiled into them. If the dependencies are not fully known, each adalib.imports initially can list all the other sublibraries it could possibly depend upon--compilation order is independent of sublibrary. They can be "minimized" later in a fairly automated fashion by individual users, or in a separate pass. ("Sloppy" adalib.imports will lead to less efficient recompilation and searching, as some sublibraries may be analyzed or searched more often than necessary.)

In the simplest case, where all the code in a directory tree is related, one could simply create the same file for all sublibraries as follows

cd /project_root
for i in . $(find * -type d -print)
  if [ -f $i/adalib.files ] ; then
    # there are ada files here
    echo $i/adalib
done > adalib.imports-template

One may then go through, edit or split this adalib.imports-template as appropriate, and then use it to create the initial adalib.imports in all the directories it names, removing the name of the sublibrary itself:

for i in $(cat adalib.imports-template)
  D=$(dirname $i)
  grep -v "$i" adalib.imports-template > $D/adalib.imports

When creating and editing these files, always use project-relative name for the sublibraries within the project. That is, a sublibrary name should not start with a '/' unless it refers to some special third-party library that is not going to be changed during development.

Separating Source from Sublibrary

While it is the default behavior, there need not be one sublibrary for each directory; source files from several different directories may be compiled into the same sublibrary. The adalib.files file is always in the same directory as the sublibrary and contains the project-relative names of all files compiled into that sublibrary.

The adalib.imports file in the directory containing the adalib (sublibrary) itself must identify other imported sublibraries as described above. If the source in a directory D will be compiled into a sublibrary in some other directory (say S), then D/adalib.imports must contain the single line

use S/adalib

This mechanism suggests an alternative organization for small projects: have a single sublibrary at the root of the project and compile all the code into it. This would be done by having a single empty adalib.imports in root directory, an adalib.files which lists all the files in the entire project, and in each of the directories an identical adalib.imports that simply contains

use adalib

The drawback to this approach is that the sublibrary file may get very big (although there is no limit other than system resources), and that the Visit Sublibrary operation will always show all units in the whole project no matter what directory is being viewed.

Checking adalib.imports Validity

If the adalib.imports files are invalid almost nothing in PowerAda will work. So it is useful to verify that they will produce a valid result. The tool aprojls, when using the -x option and given an adalib.imports file as an argument, expands it into the full list of sublibraries it represents, and will report any errors encountered when doing so. A good way to check all the files in a directory tree is:

find * -name adalib.imports -print |\
   xargs -i -t aprojls -x "{}" > /dev/null

This will display the name of each imports file and any errors that occur.

Incorporating Non-Ada Source Code

In addition to Ada code, most applications contain some amount of C or other non-Ada code. This code must be compiled to object code separately by tools other than PowerAda, but the resulting object or archive files must be included when programs are linked. The mechanism provided to do this is a "linker" directive in the adalib.imports file. This is described fully in Chapter 7, "The linker Imports Directive".

Referencing External Libraries

Bindings and other libraries which are not within the current PowerAda project must be referenced by identifying the absolute pathname of the external sublibrary(s) in an adalib.imports file. If an external sublibrary is itself in a project, then the adalib.imports associated with the referenced sublibrary will be expanded.

A recommended way to implement such interfaces within a project is to have a directory for each product or service that has external sublibraries. This directory would contain only an adalib.imports file, perhaps accompanied by text file describing the purpose and origin of the interface. The adalib.imports would contain the appropriate fully qualified names. All other imports files in the project would reference this sublibrary without any indication that it represents an interface to external sublibraries.

For example, consider an X Windows binding consisting of xlib/adalib, xt/adalib, and xm/adalib. These have been installed in directory /usr/lpp/AdaX. Rather than naming /usr/lpp/AdaX/xlib/adalib, etc in each adalib.imports that might require it, make a directory AdaX/ in the project containing AdaX/adalib.imports which names the three sublibraries, and then those that import need only import the project-relative path AdaX/adalib. If the bindings are moved or re-arranged, only AdaX/adalib.imports needs to be changed.

Portability of fully-qualified names can also be handed by using environment variables, or using

   include pathname

in your adalib.imports, where pathname is a file containing the names of the imported external sublibraries.

Note that you should not use fully qualified names or include directives to reference sublibraries within the project--those should always be specified as project-relative names.