Subsystem Manifests

Each AURA subsystem can optionally include a special library unit called the manifest. This is an Ada package that is the first child of the subsystem root package itself, with a name “AURA”. For example, for a the CLI subsystem in our quick start example, the AURA manifest would be declared like this:

package CLI.AURA is
  ..

If included, manifests are copied to the project root as-is (spec and body), except that they are renamed as direct children of the AURA package with the subsystem name. As with the root AURA package, any AURA subsystem can then with this unit to access the configuration properties.

Note

Manifests, as well as all special nested packages described in this documentation, are completely optional.

In the example above, the CLI manifest would be converted into a unit in the project root that would be declared as follows:

package AURA.CLI is
  ..

Manifests should contain sufficient comments to allow the user to make their own modifications to the configuration of the subsystem once checked-out.

Note

Since the AURA package is Pure, manifest packages must also be Pure. This is by design, and limits what manifest packages can do during autoconfiguration. This is both a safety/security feature, and encouragement to create AURA packages that build more efficiently and reliably.

Role in Autoconfiguration

Once a subsystem manifest gets copied to the root project during the checkout of an AURA subsystem, it becomes what is known as the configuration package. Configuration packages are a powerful feature of the auto configuration process.

Note

If a subsystem does not have a manfiest, an empty configuration package is generated automatically. In other words, manifests are implicitly empty packages.

During the build process, each subsystem is configured, which involves evaluating each available subsystem configuration package, and using some of the explicitly defined components to influence the build environment with code paths, external libraries, compiler settings, and C language definitions.

Manifest Contents

An AURA manifest package may contain any legal Ada declarations, and may also have a body. The only restriction is that an AURA manifest cannot have any dependencies except the Ada standard library, and my not be generic.

There are three special nested packages that the AURA implementation recognizes and processes specially (Build Codepaths Information), and one recommended neutral nested package (Configuration).

All of the specially recognized nested packages can contain any number of constant String declarations (often initialized with non-static expressions). These declarations are used by the AURA implementation to affect different capabilities, features, and configuration parameters for the subsystem and build environment.

Example of a Manifest

The ASAP INET subsystem provides a great example of a package manifest that contains both platform-based auto configuration, as well as user-configured options. The INET manifest also uses most of the important AURA-specific configuration facilities.

Here is the complete INET manifest.

Note

Remember that the manifest will become a configuration package with the name aura.inet after checkout. The actual manifest itself will never be directly compiled by AURA, and should not be withed directly by any unit of the subsystem.

-- INET AURA Configuration Manifest

package INET.AURA is
   
   package Configuration is
      Enable_TLS: constant Boolean := False;
   end Configuration;
   
   package Build is
      package External_Libraries is
         LibreSSL_libtls: constant String 
           := (if Configuration.Enable_TLS then
                  "tls"
               else
                  "");
         -- Users should also ensure that the libressl include directory is in
         -- C_INCLUDE_PATH, if not installed in the usual locations
      end External_libraries;
      
      package Ada is
         package Compiler_Options is
            Ignore_Unknown_Pragmas: constant String := "-gnatwG";
         end Compiler_Options;
      end Ada;
      
      package C is
         package Preprocessor_Definitions is
            BSD: constant String
              := (if Platform_Flavor 
                    in "freebsd" | "openbsd" | "netbsd"
                  then
                     "__INET_OS_BSD"
                  else
                     "");
            
            Darwin: constant String
              := (if Platform_Flavor = "darwin" then
                     "__INET_OS_DARWIN"
                  else
                     "");
            
            Linux: constant String
              := (if Platform_Flavor = "linux" then
                     "__INET_OS_LINUX"
                  else
                     "");
         end Preprocessor_Definitions;
      end C;
   end Build;
   
   package Codepaths is
      TLS: constant String
        := (if Configuration.Enable_TLS then "tls" else "");
      
      OS_Dependent: constant String
        := (if Platform_Family = "unix" then
               "unix"
            else
               "");
      
      IP_Lookup_Base: constant String := "ip_lookup/" & OS_Dependent;
      
      IP_Lookup_Addrinfo: constant String 
        := (if Platform_Flavor in "linux" | "openbsd" | "darwin" then
               IP_Lookup_Base & "/addrinfo_posix"
            elsif Platform_Flavor in 
              "freebsd" | "netbsd" | "solaris" | "illumos"
            then
               IP_Lookup_Base & "/addrinfo_bsd"
            else
               "");
           
   end Codepaths;
end INET.AURA;

The Configuration Nested Package

-- INET AURA Configuration Manifest

package INET.AURA is
   
   package Configuration is
      Enable_TLS: constant Boolean := False;
   end Configuration;
   
   package Build is
      
      -- ...
      
   end Build;
   
   package Codepaths is
      
      -- ...
      
   end Codepaths;
   
end INET.AURA;

The Configuration nested package is a recommended convention, but is not specially recognized by the AURA implementation. Its recommended role is to contain all user-configurable options for the subsystem.

In this example, the Configuration package contains the option for enabling TLS support for the INET subsystem. The manifest should contain the default configuration.

If the user of the INET package wished to enable TLS support, they would edit the subsystem configuration package to enable that feature (in this case, aura.inet in the project root).

By following this convention, the AURA subsystem users can more easily configure their checkouts of the subsystem.

Note

This package, if present, should be the first declaration of the manifest.

The Build Nested Package

The Build nested package is used to control the building of subsystems, as well as the linking of projects that depend on the subsystem.

The Build nested package is composed of a further number of specific, AURA-recognized nested packages.

Note

The Build nested package, as well as all of its nested children, is optional.

The External_Libraries Package

-- INET AURA Configuration Manifest

package INET.AURA is
   
   package Configuration is
      -- ..
   end Configuration;
   
   package Build is
      package External_Libraries is
         LibreSSL_libtls: constant String 
           := (if Configuration.Enable_TLS then
                  "tls"
               else
                  "");
         -- Users should also ensure that the libressl include directory is in
         -- C_INCLUDE_PATH, if not installed in the usual locations
      end External_libraries;
      
      -- ..
   end Build;
   
   -- ..
   
end INET.AURA;

The Build.External_Libraries package can contain any number of constant String declarations which declare the linker/compiler-recognized library name.

Note

The AURA implementation is advised to generally follow the UNIX convention of using the library name, not including ‘lib’. So, for example “iberty” for “libiberty”, which for the uix cc and ld convention would imply a command line option of -Liberty

The reference implementation follows this convention.

For an AURA subsystem that relies on libiberty, it should have a manifest with a declaration for Build.External_Libraries that contains a constant string with the value “iberty”.

Note

The names of the constant String objects are ignored by the AURA implementation, but should typically be descriptive enough to maximize readability.

Note

Empty strings are ignored, which is useful when the requirement of an external library is conditional, depending on the configuration.

Note

In the above example that the value of LibreSSL_libtls is declared with a conditional expression that is based on the value of Configuration.Enable_TLS. This is the recommended application of user configuration, using the Configuration package.

The Ada Package

package INET.AURA is
   
   package Configuration is
      -- ..
   end Configuration;
   
   package Build is
      -- ..
      
      package Ada is
         package Compiler_Options is
            Ignore_Unknown_Pragmas: constant String := "-gnatwG";
         end Compiler_Options;
      end Ada;
      
      -- ..
   end Build;
   
   -- ..
   
end INET.AURA;

The Build.Ada package supplies subsystem-specific configuration for the building of Ada sources.

Currently AURA only specifies a further nested package Compiler_Options.

The Compiler_Options nested package should contain a number of constant String declarations, where each one represents a specific option to pass to the Ada compiler.

Note

In the above example, the GNAT-specific option -gnatwG is included. Notice how the object is given the descriptive name Ignore_Unknown_Pragmas. This is included because the AURA specification includes a new pragma External_With used by AURA subsystems to include non-Ada units in their codebase.

See also

See this section for more discussion on the new pragma External_With, and how to include non-Ada sources in an AURA subsystem.

The C Package

package INET.AURA is
   
   package Configuration is
      -- ..
   end Configuration;
   
   package Build is
      -- ..
      
      package C is
         package Preprocessor_Definitions is
            BSD: constant String
              := (if Platform_Flavor 
                    in "freebsd" | "openbsd" | "netbsd"
                  then
                     "__INET_OS_BSD"
                  else
                     "");
            
            Darwin: constant String
              := (if Platform_Flavor = "darwin" then
                     "__INET_OS_DARWIN"
                  else
                     "");
            
            Linux: constant String
              := (if Platform_Flavor = "linux" then
                     "__INET_OS_LINUX"
                  else
                     "");
         end Preprocessor_Definitions;
      end C;
   end Build;
   
   -- ..
   
end INET.AURA;

The Build.C package supplies subsystem-specific configuration for the building of C sources, and is a particularly powerful tool in the integration of C sources in AURA subsystems.

Currently, AURA specifies two further nested packages: Compiler_Options and Preprocessor_Definitions.

Build.C.Compiler_Options

This functions identically as it does for Build.Ada.Compiler_Options, as discussed above, except that it applies only to the C compiler.

Build.C.Preprocessor_Definitions

This package is the most interesting and powerful tool for the integration of C sources.

Like most of the other nested packages, the Preprocessor_Definitions package should contain a series of constant String declarations. Each String causes the content of that string to be “defined” for any C sources in the subsystem’s codepaths.

For example, if the INET AURA subsystem is built on MacOS, AURA.Platform_Flavor will have a value of “darwin”, which will cause Build.C.Darwin to evaluate to “__INET_OS_DARWIN”. This will cause all C sources that are part of the INET subsystem to be (in effect) compiled with #define __INET_OS_DARWIN.

Note

If any of the constant String declarations of Build.C.Preprocessor_Definitions evaluate to a null string, AURA will ignore that declaration. Therefore (as in the above example), if a preprocessor definition should not be made in some cases, it should be set to a null string.

The Codepaths Nested Package

package INET.AURA is
   
   -- ..
   
   package Codepaths is
      TLS: constant String
        := (if Configuration.Enable_TLS then "tls" else "");
      
      OS_Dependent: constant String
        := (if Platform_Family = "unix" then
               "unix"
            else
               "");
      
      IP_Lookup_Base: constant String := "ip_lookup/" & OS_Dependent;
      
      IP_Lookup_Addrinfo: constant String 
        := (if Platform_Flavor in "linux" | "openbsd" | "darwin" then
               IP_Lookup_Base & "/addrinfo_posix"
            elsif Platform_Flavor in 
              "freebsd" | "netbsd" | "solaris" | "illumos"
            then
               IP_Lookup_Base & "/addrinfo_bsd"
            else
               "");
           
   end Codepaths;
   
   -- ..
   
end INET.AURA;

The Codepaths nested package is perhaps the most powerful single feature of AURA subsystem auto configuration. Codepaths allow the active content of the subsystem codebase to be selected through the auto configuration process. Among other things, this allows platform-specific code to be selected automatically for the target platform. In the above INET example, it is also used to include the additional TLS code when that option is enabled.

While the Codebase mechanics are implementation-defined, the required behavior strongly hints at the typical implementation, and the recommended implementation is that of AURA CLI.

The Codepaths package consists of any number of static String declarations. Each declaration that evaluates to a non-null string refers to some collection of library units, and/or other further collections. For AURA CLI, these declarations should evaluate to UNIX-style paths, without leading ‘/’. These paths should index subdirectories of the subsystem’s own subdirectory.

For the INET example, we see that if TLS is enabled (Configuration.Enable_TLS = True), the immediate source code of the tls subdirectory should be included.

Note

Any subdirectories in a selected Codepath are not automatically included. These must be explicitly selected if they are to be included as well.

Codepaths can be of any depth. In the INET example, we see that all platforms have a base ip_lookup codepath (subdirectory), which further contains addrinfo_posix and addrinfo_bsd. These second-level subdirectories are selected based on the platform. It is important to note that the content of these second-level subdirectories would not be entered if not explicitly given as a codepath. That is to say, if only IP_Lookup_Base was declared, the contents of addrinfo_posix and addrinfo_bsd would not be included.