Introduction

Alire is a source-based package manager for the Ada and SPARK programming languages.

It is a way for developers to easily build upon projects (libraries or programs) shared by the community, but also to easily share their projects for others to build upon.

In the Alire vocabulary, sources of projects/libraries/programs are provided by what is called a crate. A crate can depend on crates, and other crates can depend on it. For instance, the libgpr crate depends on the xmlada crate.

Crates can have multiple dependencies, themselves having multiple dependencies. This forms a dependency graph. Alire’s main task is to automatically fetch, build and upgrade the crates of the dependency graph so you don’t have to do it by hand.

The main interface into the Alire ecosystem is a command line tool called alr.


Getting Started

Installation

You can download the last release of Alire at the GitHub repository.

alr on Linux

On Linux, Alire is simply provided in an archive.

Once the archive is extracted you have to add alr in the environment PATH . This may be done for the duration of a terminal session by running the command below:

$ export PATH="<PATH_TO_EXTRACTED>/bin/:$PATH"

Those wanting to keep this path permanently in their PATH environment may do so by pasting the above command into the .profile file of their user’s account.

Alire provides GNAT toolchains hosted on x86-64 for Linux. If those toolchains do not work for you, or if you are on another host architecture like ARM, you have the option to look at the GNAT toolchains from the Linux distribution.

alr on Windows

On Windows an installer is provided. The installer will create a shortcut to start PowerShell with Alire in the environment PATH.

The first time you run alr, the program will ask if you want to install msys2 (except in the cases listed below). This is recommended as alr will use msys2 to automatically install required tools such as git or make that you would otherwise have to install manually. msys2 will also provide external libraries required by some projects in the Alire index, allowing you to build more projects out of the box.

msys2 will not be installed

  • when running alr settings, to allow uninterrupted configuration, and setting of msys2 location (see alr help settings), or
  • when you already have a msys2 installation in your PATH (more precisely, if pacman is found in your PATH.)
    • In this case, alr will reuse your existing installation.

Alire provides GNAT toolchains hosted on x86-64 for Windows. Those toolchains should work for all cases; if not, let us know.

alr on macOS

On macOS, Alire is simply provided in an archive.

Once the archive is extracted you have to add alr in the environment PATH:

$ export PATH="<PATH_TO_EXTRACTED>/bin/:$PATH"

If you try to run it on recent versions of macOS, you will get a popup saying “alr” cannot be opened because the developer cannot be verified. and inviting you to move it to the bin. The way round this is to remove the quarantine attribute,

$ xattr -d com.apple.quarantine bin/alr

Alire provides GNAT toolchains hosted on x86-64 for macOS. If those toolchains do not work for you, or if you are on another host architecture like the Apple M1, you have the option to look at the GNAT toolchains from the community.

alr for other platforms

If alr is not available on your platform, you can try to build it from sources. Go to the GitHub repository for more information.

First steps

The following miniguide shows how to obtain and build already packaged projects, and create your own. First, create or enter into some folder where you don’t mind that new project folders are created by the alr tool

Run alr without arguments to get a summary of available commands.

Run alr --help for global options about verbosity.

Run alr help <command> for more details about a command.

Downloading, compiling and running an executable crate

Obtaining an executable project already cataloged in Alire is straightforward. We’ll demonstrate it with the hello project which is a plain “Hello, world!” application (or you can use the hangman or eagle_lander projects as funnier alternatives).

Follow these steps:

  1. Issue alr get hello
  2. Enter the new folder you’ll find under your current directory: cd hello*
  3. Build and run the project with alr run. This will build and then launch the resulting executable.

    The first time you run this command, the toolchain selection assistant will ask you to select your preferred default toolchains (GNAT compiler and GPRbuild). For this getting started example, we recommend to just press enter twice to select the defaults.

As a shorthand, you can use alr get --build hello to get and build the program in one step.

Creating a new crate

Alire allows you to initialize an empty binary or library crate with ease:

  1. Issue alr init --bin myproj (you can use --lib for a library project)

    The first time you run this command, alr will ask a couple of questions to automatically fill-in information about the crate:

    • GitHub login: is used to identify the maintainer of the crate when contributed to the community index.
    • Full name: Name of the author of the crate
    • Email address: Point of contact to author of the crate

    All the questions are optional, you can just press enter to use the default values.

    The alr init command will create a basic crate structure in the myproj directory.

  2. Enter the folder: cd myproj
  3. Build the crate: alr build
  4. Run the program: alr run

We can now edit the sources of this executable in the src/ directory. For instance, add a “Hello world” to src/myproj.adb:

with Ada.Text_IO;
procedure Myproj is
begin
   Ada.Text_IO.Put_Line ("Hello, world!");
end Myproj;

Use alr run to build and run the program again:

$ alr run
# Building myproj/myproj.gpr...
Compile
   [Ada]          myproj.adb
Bind
   [gprbind]      myproj.bexch
   [Ada]          myproj.ali
Link
   [link]         myproj.adb
Build finished successfully in 0.35 seconds.
Hello, world!

The Alire manifest

Besides the alr command, the main interface with Alire is the manifest.

The manifest is a text file named alire.toml in the root directory of the crate. It contains all sorts of information about the crate, some mandatory such as the name and version, others optional like the licenses. Alire manifests are written in TOML format.

You can have a look at the manifest to get a idea of its content, but nothing has to be edited by hand so far.

Dependencies and upgrading

Alire keeps track of dependencies in the manifest (alire.toml) of your crate.

Adding dependencies can be done with the alr with command:

  • alr with crate_name adds a dependency. You can immediately ‘with’ its packages in your code.
  • alr with --del crate_name removes a dependency.

Alternatively you can edit the file to add dependencies and then issue:

  • alr update, which will fetch any modified dependencies in your project.

Using alr with without arguments will show the current dependencies of your project. Using one of --solve, --tree, --versions, --graph will show different details about the complete solution needed to fulfill dependencies.

Add a dependency

Let’s add a dependency to the libhello crate.

$ alr with libhello
Requested changes:

   # libhello ^1.0.0 (add)

Changes to dependency solution:

   + libhello 1.0.0 (new)

Do you want to proceed?
[Y] Yes  [N] No  (default is Yes)

alr is showing the new dependency solution, i.e. all the crates in the dependency graph and their version.

Press enter to accept the new solution.

alr will then download the sources of the libhello crate.

Use the dependency

You can now edit the src/myproj.adb source file again, and write this piece of code to call a function from the libhello crate:

with Libhello;
procedure Myproj is
begin
   libhello.Hello_World;
end Myproj;

Run alr run to build and run the new executable:

$ alr run
# Building myproj/myproj.gpr...
Setup
   [mkdir]        object directory for project Libhello
   [mkdir]        library directory for project Libhello
Compile
   [Ada]          myproj.adb
   [Ada]          libhello.adb
Build Libraries
   [gprlib]       hello.lexch
   [archive]      libhello.a
   [index]        libhello.a
Bind
   [gprbind]      myproj.bexch
   [Ada]          myproj.ali
Link
   [link]         myproj.adb
Build finished successfully in 0.34 seconds.
Hello, world!

As you can see, the libhello library sources are automatically built and linked in your program.

Finding available projects

For quick listing of crates and their descriptions you can use the search command with the --crates switch:

  • alr search --crates [substring]

Otherwise, search will look into releases, providing more details about specific releases:

  • alr search <substring> will look for substring in crate names.
  • alr search --list will list the latest release of every crate.
  • alr search --list --full will list all releases in the catalog.

Even more details are obtained with:

  • alr show <crate>

This last command will show generic information. To see the one that specifically applies to your platform:

  • alr show --system <crate>

The list of projects and their descriptions are also available on the Alire website:

Build environment

To create a build environment, alr sets environment variables such as GPR_PROJECT_PATH before running gprbuild. If you want to run gprbuild yourself or inside an editor (GNAT Studio), you can use the printenv command to print the build environment:

  • alr printenv

Troubleshooting

If you hit any problem, increasing verbosity (-v or even -vv) is usually enough to get an idea of the root of the problem. Additionally, -d will show tracebacks of exceptions.

Subprocess output is shown by default (you can silence it, and anything else not an error) with -q, which enables quiet mode. Any subprocess that exits abnormally will be reported, including its invocation arguments.

If you suspect your settings may be the source of some problem, please check our section on Settings, and in particular how to use a default pristine settings

Running tests

alr comes with a test suite for self-checks. See the instructions in the README of the testsuite folder.

Additionally, you can test in batch the building of crates in your platform with the alr test command. (See alr test --help output for instructions.)

Migration of an existing Ada/SPARK project to Alire

First you have to decide on a crate name for your project, this name will have to follow the naming rules of Alire. You can find those rules using the command:

$ alr help identifiers

Avoid using ada as a prefix for your crate name, this will make the project harder to find in a list. ada suffix is ok when the project is a binding for an existing library (e.g. sdlada, gtkada).

We will use the name my_crate as an example, and consider that the repository uses the same name.

Clone your project repository and enter the directory:

$ git clone https://github.com/github_login/my_crate.git
$ cd my_crate

At this point you have a choice:

  1. Let Alire generate a new GPR project file for you. This is recommended for most projects, and in particular if your project has simple code organization and GPR project file. One of the advantages is that Alire will create a GPR project file “standardized” for best integration in the ecosystem.

  2. Keep your existing GPR project file. This is recommended for projects with complex GPR project file(s).

1: Using Alire GPR project file

If you want Alire to generate a project you first have to delete the existing GPR project file:

$ rm *.gpr

And then use alr init command to create a skeleton for your crate:

For a library:

$ alr init --in-place --lib my_crate

For an application:

$ alr init --in-place --bin my_crate

If this is your first time using alr init, you will have to provide some information like your name and GitHub login.

You can ignore the warnings such as Cannot create '[...]/my_crate/src/my_crate.ads', Alire is trying to create a root package for your crate but you probably already have one.

From this point you can edit the GPR project file to change the source dir or compilation flags, if needed. And then try to compile your crate with:

$ alr build

2: Using your own GPR project file(s)

If you want to keep the existing GPR project file, use alr init with the --no-skel option to skip the project skeleton creation:

For a library:

$ alr init --in-place --no-skel --lib my_crate

For an application:

$ alr init --in-place --no-skel --bin my_crate

If this is your first time using alr init, you will have to provide some information like your name and GitHub login.

If your GPR project file does not match the crate name (i.e. my_crate.gpr), you have to add a project-files field in your alire.toml manifest. For instance:

project-files = ["project_file.gpr"]

Although this is not recommended (see best practices), you can have multiple GPR project files:

project-files = ["project_file_1.gpr", "project_file_2.gpr"]

You can now compile your crate with:

$ alr build


Toolchain management

Toolchains are comprised by a GNAT compiler and a gprbuild project file processor. Alire strives to simplify the availability of GNAT releases, which are packaged to be retrieved and used with ease.

The user can now select a preferred compiler via alr toolchain --select. Releases may still override this selection (for example to use a cross-compiler). Several compilers can be installed simultaneously, but only the last one --selected will be used by default. The rest will be used in preference to uninstalled compilers, if the default one does not match some crate’s dependencies.

There are two kinds of dependencies on GNAT compilers: generic dependencies on the gnat crate, which apply to every compiler, and dependencies on a precise native or cross-compiler.

The native compiler for each platform is always gnat_native, whereas cross-compilers follow the naming convention gnat_<target> (e.g., gnat_riscv_elf). Alire will only offer to install valid cross-compilers for the host platform.

Identifying available compilers

Compilers available for download can be listed with alr search --full --external-detect gnat_. They will also be shown by the selection assistant, alr toolchain --select.

Running alr toolchain without arguments will show the installed compilers and the preferred compiler, if any.

Automatic compiler installation

If a crate requires a target-specific compiler via its dependencies, alr will attempt to use first a matching installed compiler. Otherwise, a matching compiler will be automatically downloaded.

Automatic compiler selection by Alire

When a build is launched, or alr printenv is run to obtain a build environment, alr will use the available compilers as follows:

  1. A target-specific compiler needed due to dependencies will be selected.
  2. Otherwise, if the user has defined a preferred compiler, it will be selected.
  3. If no preferred compiler has been defined, Alire will rely on the existing user environment.

Within these conditions, a compiler already downloaded will be preferred. Downloading a new compiler will be attempted only if no matching compiler is already available.

Specifying dependencies on GNAT compilers for crate publishing

From the point of view of a user preparing a release for publication, these are the considerations to bear in mind:

  • Do not use any dependency on a compiler if you do not have a reason to do so.
  • Use dependencies on gnat to specify known compiler version restrictions.
  • Use dependencies on a precise gnat cross-compiler (e.g., gnat_riscv_elf) when your crate is purposely only available for such a target.
  • It is normally not necessary to specify a dependency on the native compiler (gnat_native) as that would unnecessarily limit the availability of a library crate that might be useful to dependent cross-target crates.
  • It may be useful to depend on gnat_native in cases where a crate builds a tool to be used always in the host platform, for example to be used in some action during the build process.

Note about cross-compilation

Independently of the compiler made available by alr in the environment, the crate project file still must define an appropriate Target attribute for the desired compiler. At the moment, Alire does not examine project file contents to identify necessary compilers, and relies only on regular depends-on dependencies.


Publishing your projects in Alire

Publishing a project in Alire is done with the help of the alr publish command. The steps to take are described after some introductory concepts (jump to these steps directly here; you can also ask for help on the gitter channel of the project.

Automated publishing (TL;DR.)

The simplest publishing experience, provided you have a GitHub account and Personal Access Token, consist on issuing

alr publish

at the root of your workspace, when said workspace is an up-to-date clone of a git repository.

The publishing assistant will review your submission, point out any necessary fixes or additional information required, and request a pull into the community index on GitHub on your behalf.

Read on for the details underlying these automated steps, or in case you need to perform further tweaking.

Creating a GitHub Personal Access Token

A Personal Access Token (PAT) allows Alire to act on your behalf to fork the community index, push the new release manifest to a new branch in your own fork, and finally open a pull-request against the community repository.

The PAT, once created, is a plain string. You can either export the environment variable GH_TOKEN set to this string, or provide it when Alire asks for it.

There are two kinds of PATs on GitHub: classic and fine-grained. The latter are in beta and not documented here yet. Follow these steps to create a classic PAT:

  1. On the main https://github.com page, after having logged in, click on your profile photo on the top-right corner.
  2. Click on “Settings” in the list of options in the profile menu.
  3. Click on “Developer settings” entry at the bottom in your Settings page.
  4. Click on “Personal access tokens” and then “Tokens (classic)”.
  5. Click on “Generate new token” and the select the classic variant.
  6. In the “Select scopes” section, under “repo”, check “public_repo” and “workflow”. Those are the only permissions needed for this PAT.
  7. Click on “Generate token” at the bottom.

You will get the PAT string after completing the generation.

General concepts

The community index is a collection of TOML files stored in the alire-index repository, under the index directory. Each file contains a release for a crate and is named after the crate and version it contains. A file contains the description of a release, with other metadata.

The complete specification of such TOML files is available in this document.

New crates and releases

Publishing a new crate is achieved through a pull-request against the index repository, in which the TOML file for the release must be provided.

Index branches

The community index is supported through two kinds of branches:

  • stable-x.x branches are used by stable versions of alr.
  • devel-x.x branches are used to introduce breaking changes in the index format, during the development of alr.

Your alr version knows which branch to use, so you do not need to manually select one. When using alr publish to assist on creating a release, alr will either create the pull request against the proper branch, or you will be provided with an upload link for the branch your alr is using.

However, when submitting releases manually, you can decide to which branch they will be added: selecting the latest stable branch results in the release becoming immediately available to the latest stable alr. Conversely, using the latest development branch will make the releases available for testing by unstable clients, and will become generally available with the next stable release of alr.

Checks on contributions

Each crate is “owned” by a list of maintainers, provided with the maintainers-logins property of the crate file. After the initial submission, which will be manually approved (see the policies for details), the maintainers of a crate are the only people allowed to submit new releases or metadata modifications to the corresponding crate.

Other checks your submission will go through are:

  • It contains all required metadata.
  • It builds on all of our CI configurations.
    • You can disable an unsupported target with the available property.

Best practices

See the section on best practices for crates before publishing your first release.

Detailed steps

Depending on how you develop your project, you can use one of the following methods to prepare your release submission:

Starting from a git repository that contains an Alire workspace

For this common use case, you need:

  • A git repository that is clean and up-to-date with its remote.
    • The repository already contains the release you want to publish.
    • The commit with the release must exist both locally and at the remote.
  • The repository must also be an Alire-enabled workspace:
    • It contains a top-level alire.toml manifest describing the release.
  • The remote host must be one of a few trusted major open-source sites.
    • This requirement is motivated by vulnerabilities identified with SHA1, whose migration to a stronger hash is not yet complete in git.
    • alr will inform you if your host is not supported. Please contact us if you think a site should be allowed. The complete list can be consulted by running alr publish --trusted-sites.
    • This is a temporary measure until more sophisticated publishing automation is supported. See the Starting with a remote source archive case for alternatives to this scenario (you are not forced to change your code hosting, or even have an online repository).

By default, the last commit is used for the release. You can alternatively provide another commit, tag, or branch. In any case, the git revision will be used to obtain a final commit. That is, a release cannot evolve with a branch, or be updated by moving a tag.

  • Within the repository, issue

alr publish

to use the last commit. You can, alternatively, issue:

alr publish . <commit|tag|branch>

Note the path between publish and your non-commit revision. Likewise, you can run this command from outside your repository, as long as you supply the proper path to it.

At this point, alr publish will carry out a few tests and, if everything checks out, it will create a ${repo_root}/alire/releases/crate-version.toml file. This file must be submitted to the community index via a PR. alr will offer to create the pull request for you, unless you specify --skip-submit. If so, a link for conveniently creating this PR will also be provided by alr:

  • Upload the generated index manifest file (crate-version.toml) to the supplied page link on GitHub and create a pull-request.

Starting with a remote repository, without local clone

This case is analogous to the previous one, but you don’t need the local repository. The same considerations about allowed hosts discussed in the previous scenario apply:

  • The repository already contains the commit with release you want to publish.
  • The repository must also be an Alire-enabled workspace:
    • It contains a top-level alire.toml manifest describing the release.
  • The remote host must be one of a few trusted major open-source sites.
    • This requirement is motivated by vulnerabilities identified with SHA1, whose migration to a stronger hash is [not yet complete] (https://git-scm.com/docs/hash-function-transition/) in git.
    • alr will inform you if your host is not supported. Please contact us if you think a site should be allowed. The complete list can be consulted by running alr publish --trusted-sites.

The only difference when invoking alr is that you must supply the remote URL and commit (not a tag or branch). The commit must exist in the repository:

alr publish <URL> <commit>

The checks will be carried out and the outcome will be the same as in the previous scenario.

Starting with a remote source archive

This case can be used when you use another VCS other than git, or do not work with an online repository.

In this use case, you start from an already prepared final remote tarball/zipball:

  • The archive must contain a single directory (name not important) containing, in turn, the sources. This is the kind of archives automatically generated by GitHub, GitLab, Sourceforge… or through git archive.
  • The alire.toml manifest must be placed at the top-level with the rest of your sources (inside the same single directory just described), containing all required information except for the [origin] table, which will be created by alr.
  • This archive must not contain the alire directory generated by alr in working directories. The alire directory is designed to be put in your VCS ignore list.

With the source archive already uploaded to the online host where it is going to be served (there are no restrictions on this host), you should issue

alr publish <URL>

and the publishing process will carry on as in the previous cases, performing the checks and providing you with a file to submit to the index, and an upload URL to do so.

Starting with a local source folder

Invoking alr publish --tar inside an Alire workspace will result in the creation of a source archive at ${CRATE_ROOT}/alire/archives/. This archive must be manually uploaded by the user to a publicly accessible hosting service.

After the upload, the user can supply the URL to fetch this archive to the publishing assistant (which will be waiting for this information), and the assistant will resume as if it had been invoked with alr publish <URL> (see Starting with a remote source archive).

Support for complex projects whose sources become multiple Alire crates

In case your project does not easily map to a single Alire crate (e.g., because you manage multiple project files with different dependencies, or there are other reasons to keep the sources together even if they generate several crates), you have several options.

The simplest one is to store each crate in a subdirectory within the repository, with its corresponding Alire manifest, sources and project files. With the repository up-to-date with the remote, and the local copy checked out at the desired commit, issuing alr publish in each subdirectory will properly recognize that the crate is nested below the repository root. Furthermore, when using this method, all nested crates will share the same storage when retrieved as dependencies.

A similar alternative would be to publish each crate relying on source archives In this case you can use alr publish --tar normally inside each subdirectory. Compared with the previous options, there is no disadvantage to this method if you favor source archives.

Another possibility would be to use a bit of scripting to create temporary subfolders with the described organization, and again using alr publish --tar normally.

Finally, the alr publish command provides a --manifest <file> switch to work in place with several crates. You can have different manifests at custom locations (other than the expected ./alire.toml) and provide each one in turn with the --manifest switch to create their respective crate. In this case, alr temporarily uses the given file as the root manifest, so all sources will be packaged for each crate. This is a bit wasteful, but as long as each crate’s project files are properly defined (no shared sources), this remains an option to split the sources into crates. With the current support for autodetection of crates in subdirectories, this option is not recommended for new repositories.

Starting from other configurations

If your case does not fit well into any of the situations above we definitely want to hear about it, to see how it can be brought into existing or new Alire workflows.

Creating the PR via cloning.

Instead of uploading the generated index manifest file via the GitHub upload link, you can follow the usual procedure to submit a PR to a GitHub repository:

  1. Fork the community index to your GitHub account.
  2. Clone your fork locally and place generated manifest at the intended folder.
  3. Commit and push the changes to your fork.
  4. Create the pull request from your fork against the community repository through the GitHub web interface (or the hub tool).
    1. The base branch you select for the pull request will determine where your changes will become available; see the section on index branches for details.

Publishing outcome

Once the pull request is verified and merged, the new release will become available for normal use after running alr index --update-all. The open source Ada ecosystem needs all the help it can get, so thank you for contributing!

ALR Badge

If you like, you can add a nice, shiny badge to your project page which links back to the Alire website. This can even serve as a reminder to republish your project once you published a new release, because the badge shows the latest version of your project that is known to Alire.

The Alire website is updated once a day, every day. Hence, after we accepted and merged your pull request, it might take up to a day for your changes to appear there, usually less.

To add the badge, all you need to do is add the line

[![Alire](https://img.shields.io/endpoint?url=https://alire.ada.dev/badges/YOUR_CRATE.json)](https://alire.ada.dev/crates/YOUR_CRATE.html)

to your README.md. Of course, you need to replace the string YOUR_CRATE with your actual project’s crate name.

Here’s an example:

[![Alire](https://img.shields.io/endpoint?url=https://alire.ada.dev/badges/hal.json)](https://alire.ada.dev/crates/hal.html)

This will be shown as:

Alire

Publishing to a local/private index

Having a local index may be useful sometimes, be it for local testing, or for private crates not intended for publication.

There is no practical difference between the community index that is cloned locally and a private local index stored on disk. Hence, after obtaining the manifest file with alr publish, it is a matter of placing it at the expected location within the index: /path/to/index/cr/crate_name/crate_name-x.x.x.toml

If the crate being published locally contains "provides" definitions, it is necessary to call alr index --update-all once to ensure it is properly used by the dependency solver. This is only necessary for the first release in a crate that uses the "provides" feature.


Catalog format specification

Big picture

Each release belonging to a crate is described as a TOML file. This file has minor differences depending on its location: a local manifest is found at the top-level directory of the sources of a project, in which case its named alire.toml, whereas a manifest found in an index (e.g., the community index), is named <name>-<version>.toml.

Other than that, contents follow the same conventions and there are only slight differences (some fields are intended only for an index manifest, and cannot appear, or are optional, in a local manifest). These differences are highlighted in the following descriptions, where necessary.

Each TOML description file contains exactly one release, except for the special external definitions that are described in their own section.

Information encoding

This section describes the various encodings used in this format to encode information.

First, there are two kinds of data: atomic and composite.

Atomic data designates values that cannot be decomposed. There are only two atomic data types:

  • mere strings ("Hello, world!");
  • booleans (true, false);

When a string denotes a relative path intended to be portable across operating systems, it must use forward slashes as directory separator: "path/to/my/resource".

We can then split composite data in two kinds: lists (TOML’s arrays) and mappings (JSON’s tables). Lists are just sequences of other values, for instance a list of strings:

["A", "B"]

Mappings are the traditional sets of associations from keys (here, always strings) to other values. For instance, the following represents a set of dependencies, with version constraints:

libfoo = "^1.2"
libbar = "^2.0 & /=2.1.3" # Excluding a known bad version

In some contexts, information can be dynamic: special encodings can be used to make data vary depending on the environment (OS, architecture, …). The environment is represented as a set of specific variables which can have a specific set of values: see the Parameters section below for a comprehensive list.

All properties that support dynamic expressions follow the same structure, in which the expression (case-like) is inserted between the key and its value. For example, given a static expression:

key = "value"

one of its cases would be expressed by the following inline TOML table:

key.'case(var)'.var_value = "value"

Several expressions can be inserted between a property key and its value, leading to a combinatorial explosion if all cases have specific values. The equivalent to an others Ada clause in this format is a '...' entry.

Here is an example of a conditional boolean value.

{'case(distribution)' = {
    'debian|ubuntu': true,
    '...': false
}}

# Or in a more idiomatic TOML syntax
['case(distribution)']
'debian|ubuntu' = true
'...' = false

Depending on the value of the distribution environment variable, this will return true (its value is debian or ubuntu) or false (for other values). Note that these and subsequent examples are not showing the left-hand-side property to which such a value would be assigned.

A little variation allows building environment-dependent composite data. For instance, to make the dependency on libbar above dynamic:

{
    "libfoo": "^1.2",
    "case(os)": {
        "linux": {"libbar": "^2.0"},
        "windows": {"libwinbar": "^3.0"},
        "...": {}
    }
}

# Or in a more idiomatic TOML syntax
libfoo = "^1.2"

['case(os)'.linux]
libbar = "^2.0"

['case(os)'.windows]
libwinbar = "^3.0"

['case(os)'.'...']

The case(os) part selects dependencies depending on the value of the os environment variable.

If the os environment variable contains linux, this will create the following dependencies:

libfoo = "^1.2"
libbar = "^2.0"

If the os environment variable contains windows, this will create the following dependencies:

libfoo = "^1.2"
libwinbar = "^3.0"

And finally for other os values:

libfoo = "^1.2"

Release Information

This section describes the actual properties that must or can appear in a manifest file to describe a release. Unless specified, all the entries must be static, i.e. they cannot depend on the context.

  • name: mandatory string. The name of the crate this release belongs to. Use alr help identifiers to see the rules such names must follow.

  • version: mandatory string. The semantic version of the release.

  • description: mandatory string. One-line description about the package. For instance:

    description = "Library to handle foobars"
    
  • long-description: optional free-form string to provide information about this package, in addition to description, without length restrictions.

  • authors: optional array of strings. Flat list of human-readable names for the authors, i.e. the people that wrote the software that is packaged. For instance:

    authors = ["Alice Example",
               "Bob For Instance <bob@example.com>"]
    
  • maintainers: mandatory (for indexing) array of strings. Flat list of human-readable names (optional) for the maintainers, with a contact email (mandatory); i.e. the people that maintain the crate metadata in Alire. For instance:

    maintainers = ["alice@example.com",
                   "Bob For Instance <bob@athome.com>"]
    
  • maintainers-logins: mandatory (for indexing) array of strings. Flat list of github login usernames used by the maintainers of the crate. This information is used to authorize crate modifications. For instance:

    maintainers-logins = ["alicehacks", "bobcoder"]
    
  • licenses: mandatory (for indexing) string. A valid SPDX expression. Custom license identifiers are accepted with the format: custom-[0-9a-zA-Z.-]+

    licenses = "MIT"
    

    For a double license:

    licenses = "GPL-3.0-only OR MIT"
    

    For a custom license:

    licenses = "custom-my-license-1.2"
    
  • website: optional string. URL to the original project’s website. For instance:

    website = "https://myproject.example.org/"
    
  • tags: optional array of strings. Flat list of topics covered by the crate. Tags will help users find crates related to their interests:

    tags = ["spark", "security"]
    
  • available: optional dynamic boolean expression. Determines whether the package is available for the current platform (true) or not (false). For instance:

    [available.'case(distribution)']
    'debian|ubuntu' = true
    '...' = false
    
  • depends-on: optional array of dynamic dependencies expressions. For instance:

    [[depends-on]]  # A static dependency
    libfoo = "^1.2"
    
    [[depends-on]]  # A dynamic dependency
    [depends-on.'case(os)'.linux]
    libbar = "^2.0"
    
    [depends-on.'case(os)'.windows]
    libwinbar = "^3.0"
    

    Available constraint operators are the usual Ada relationals (=, /=, >, >=, <, <=) plus caret (^, any upwards version within the same major point) and tilde (~, any upwards version within the same minor point).

    Note that caret and tilde do not have any special behavior for pre-1 versions. This means, for example, that ^0.2 will still mean any release below 1.0. The Semver specification does not make any promises about the compatibility of pre-1 versions, and there are differing interpretations of these operators out there for such versions. Bear in mind this when expressing your restrictions; for pre-1 versions you most likely want to use ~0.x constraints (compatibility within a minor version).

    Logical operators for and (&), or (|) are accepted; see the Semantic_Versioning project documentation on extended version sets.

    See also the section on compiler dependencies for more details on how to use the depends-on property for cross-compiling or compiler version selection.

  • project-files: optional list of strings. Each is a path, relative to the root of the source directory, to a .gpr project file to be made available. Expressions are accepted. For instance:

    project-files = ["my_project.gpr", "utils/utils_for_my_project.gpr"]
    
    [project-files.'case(word-size)']
    bits-64 = ["my_project.gpr"]
    bits-32 = ["my_project32.gpr"]
    
  • gpr-externals: optional table, giving a mapping from the name of external variables in the .gpr project files to sets of possible values (as array of strings), or an empty string if this set is infinite. For instance:

    [gpr-externals]
    BUILD_MODE = ["debug", "profile", "release"]
    TAG = ""
    
  • gpr-set-externals: optional dynamic table, setting values of project external variables when building the project. This should not be used to specify default values, the default values must be specified in the .gpr project file. Expressions are accepted before the mapping. For instance:

    [gpr-set-externals]
    BUILD_MODE = "release"
    
    [gpr-set-externals.'case(os)']
    linux   = { OS = "gnu-linux" } # Compact table syntax is convenient in this case
    windows = { OS = "ms-linux" }  # to see all enumeration values, one per row.
    
  • environment: optional dynamic table used to modify environment variables that will apply at build time. Variables and values are specified with the form VARIABLE.<action> = "value", where <action> is one of append, prepend, or set. For instance:

    [environment]
    C_INCLUDE_PATH.append = "/usr/include/something"
    MY_PROJECT_ASSETS.set= "${CRATE_ROOT}/assets"
    PATH.append = "${DISTRIB_ROOT}/usr/bin"
    

    Path fragments in this table must use portable format, that is, ‘/’ for path separation. Alire will take care of using the native separator when setting these variables.

    Predefined variables are provided by Alire and will be replaced in the value:

    • ${CRATE_ROOT} absolute path to the deployment directory of the crate.
    • ${DISTRIB_ROOT} absolute path to the root directory of the system distribution. On UNIX systems it will be /, on Windows msys2 it will be the msys2 installation directory (e.g. C:\Users\user_name\.cache\alire\msys2).

    The escaping "\$" can be used to prevent the expansion of a dollar-bracketed expression.

    Environment entries can use dynamic expressions:

    [environment.'case(distribution)']
    msys2 = { C_INCLUDE_PATH.append = "${DISTRIB_ROOT}/mingw64/include/SDL2" }
    
  • executables: optional dynamic list of strings. Each one is the simple name of an executable provided by the package. Executables are looked for by alr in the build tree and must not include a path. If only one executable is given, it is considered the default for alr run. For instance:

    executables = ["my_main"]
    
  • actions: optional dynamic list of actions to perform when certain events take place in a workspace. Actions are executed in the order they are defined in the manifest. The general action syntax is:

    [[actions]]
    type = <kind>
    command = <command>
    directory = <relative path>  # Optional
    

    <command> is an array of strings for a shell command to run in the source directory.

    For events that cause a workspace-wide triggering of actions (all pre_/post_ actions described next), the actions are invoked in a dependency-safe order, starting at the leaves of the dependency graph (releases with no dependencies) and moving up to the root release (the working release, or the release being obtained with alr get). In this context, the root release is considered part of the dependency solution, and so its actions are executed too, always in the last place.

    directory is an optional portable relative path (forward-slashed) from the crate root, in which the action will be executed. This directory must exist or the action will error. Actions are executed by default in the crate root.

    <kind> can be either:

    • post-fetch: the command is to be run whenever there are new sources deployed in the workspace, in any release in the solution. All releases post-fetch actions are run after the new deployment is complete. Initial retrieval, subsequent modification of dependencies, pinning a directory or repository is considered a deployment of new sources. A manual alr update, even if it results in no changes, will also trigger this action in every release in the solution.

    • pre-build: the command is to be run right before the build of the workspace starts. This kind of action is run for all releases in the solution.

    • post-build: the command is to be run right after a build has successfully completed. This kind of action is run for all releases in the solution.

    • test: the command is run on demand for crate testing within the Alire ecosystem (using alr test). This kind of action is run only for the root crate being tested. The crate is not built beforehand when a test action is defined so, if a build is necessary, it should be explicitly given as part of the action sequence.

    Since actions may end being run more than once they should take this into account and allow multiple runs with the expected results intended by the packager.

    Actions accept dynamic expressions. For example:

    [[actions.'case(os)'.linux]]
    type = "post-fetch"
    command = ["make"]
    
    [[actions.'case(os)'.windows]]
    type = "post-fetch"
    command = ["cmd", "build"]
    
    [[actions.'case(os)'.'...']]
    # An explicit empty case alternative, which is not mandatory
    

    The aforementioned TOML syntax is valid when there is only one conditional action. For multiple conditional actions, one can write:

    [[actions]]
    [actions.'case(os)'.linux]
    # Regular contents of an action, applying to the Linux case
    [actions.'case(os)'.macos]
    # macOS case
    
    [[actions]]
    # Another action, that needs not be also conditional (but could be).
    
  • auto-gpr-with: optional Boolean value that specifies if the project (gpr) files of a crate can be automatically depended upon (‘withed’) directly by the root project file. (The default is true.) This feature is meant to simplify the process of using dependencies in Alire. However, not all project files are supposed to be direct dependencies. Some are intended to be extended, for example, and in that case a crate can disable the feature by setting auto-gpr-with=false.

  • origin: dynamic table. Mandatory for index manifests and forbidden in workspace manifests. This table describes how sources are obtained, using the following fields:

    • url: mandatory string which points to a source file or repository.

    • hashes: mandatory string array for source archives. An array of “kind:digest” fields that specify a hash kind and its value. Kinds accepted are: sha512.

    • archive-name: optional string. If url points to a source archive, this can specify the name of the file to download, which is needed in order to properly extract the sources, in case the URL does not identify it.

    • commit: mandatory string for VCS origins that describes the VCS-specific revision to be checked out (a git/hg hash, a svn revision).

    • subdir: optional relative path, only valid for repository origins, that when provided indicates that the crate is not located at the repository root. This option enables the possibility of publishing several crates from the same repository (sometimes referred to as a monorepo).

    • binary: optional (defauts to false) boolean used to design the origin as binary. Binary origins are not compiled and can optionally use dynamic expressions to narrow down the platform to which they apply. An origin using a dynamic expression must be tagged as binary; see the example below.

    Examples of origin tables:

    # Clone a git repository with a crate at its root
    [origin]
    url = "git+https://github.com/example-user/example-project"
    commit = "ec8b267bb8b777c6887059059924d823e9443439"
    
    # Download and extract a source archive
    origin = "https://example.org/0123456789"
    archive-name = "archive.tar.gz"
    hashes = ["sha512:bf6082573dc537836ea8506a2c9a75dc7837440c35c5b02a52add52e38293640d99e90a9706690591f8899b8b4935824b195f230b3aa1c4da10911e3caf954c04ac"]
    
    # Clone a git repository with the crate in a subdirectory
    [origin]
    url = "git+https://github.com/example-user/example-project"
    commit = "ec8b267bb8b777c6887059059924d823e9443439"
    subdir = "examples"
    
    # A binary origin denoting a compiler
    [origin."case(os)".linux."case(host-arch)".x86-64]
    url = "https://github.com/alire-project/GNAT-FSF-builds/releases/download/gnat-12.1.0-1/gnat-x86_64-linux-12.1.0-1.tar.gz"
    hashes = ["sha256:df1f36b306359d528799b1de8629a793523347a90c9d4b72efd23c62a7279555"]
    binary = true
    
  • available: optional dynamic boolean expression. If it evaluates to false, the package is not available for the current platform.

  • notes: optional string. Provides miscellaneous information about this release. For instance:

    notes = "Experimental version"
    
  • configuration: optional table to control crate configuration code generators:

    For more information on crate configuration, see Using crate configuration.

    • disabled: Completely disable configuration code generation for the crate (default: false)

    • output_dir: Path to the directory where the configuration code will be generated, relative to the crate root (default: config).

    • generate_ada: Enable generation of Ada configuration (default: true).

    • generate_gpr: Enable generation of GPR file configuration (default: true).

    • generate_c: Enable generation of C configuration (default: true).

    • auto_gpr_with: Enabled generation of list of withed project in the GPR file configuration (default: true).

  • configuration.variables: optional table of crate configuration variable definitions.

    For more information on crate configuration, see Using crate configuration.

    The keys of the table are names of the variables. Variable definitions themselves are tables with the following entries:

    • type: mandatory string which defines the type of the variable, it can be:

      • String: any string

      • Boolean: either True or False

      • Enum: enumeration type

      • Integer: an integer value that can be encoded in 64-bit

      • Real: a real value that can be encoded in IEEE 754 binary64

    • default: optional default value for the variable. Will be used if no crates explicitly set a value for this variable. Must be a valid value for the type.

    • first: (optional) for Real and Integer types only. Defines the lower bound of valid values for the type (inclusive).

    • last: (optional) for Real and Integer types only. Defines the upper bound of valid values for the type (inclusive).

    • values: mandatory for Enum types. An array of strings containing all the possible values for the enumeration.

    Example:

    [configuration.variables]
    Device_Name = {type = "String", default = "no device name"}
    Print_Debug = {type = "Boolean", default = false}
    Debug_Level = {type = "Enum", values = ["Info", "Debug", "Warn", "Error"], default = "Warn"}
    Buffer_Size = {type = "Integer", first = 0, last = 1024, default = 256}
    Max_Power   = {type = "Real", first = 0.0, last = 100.0, default = 50.0}
    
  • configuration.values optional table of variables assignment:

    The keys of the table are crate names, and entries are sub-tables of variable_name and value. The type of the value has to match the definition of the variable type.

    Example:

    [configuration.values]
    crate_1.var1 = 42
    crate_1.var2 = true
    crate_2.var1 = "Debug"
    
  • build-profiles: optional table of strings that sets the build profile of crates in the solution.

    For more information on build profiles and switches, see Build profiles and switches.

    There are 3 build profiles available in Alire:

    • Development
    • Release
    • Validation

    Example:

    [build-profiles]
    depend1 = "validation" # Set depend1 build profile to validation
    depend2 = "development" # Set depend2 build profile to development
    my_crate = "release" # Set my_crate build profile to release
    

    A wildcard key can be used to set the build profile of all crates that are not otherwise specified:

    [build-profiles]
    "*" = "validation" # Set all build profiles to validation
    
  • build-switches: optional table of build profile switches definitions.

    For more information on build profiles and switches, see Build profiles and switches.

    The keys of the table are either build profiles or the wildcard "*":

    • Development
    • Release
    • Validation

    The values are profile definitions, themselves tables with switch categories as keys and switches selection as values. This list of switch categories and their corresponding selection is as follow:

    • Optimization
      • Performance
      • Size
      • Debug
    • Debug_Info
      • No
      • Yes
    • Runtime_Checks
      • None
      • Default
      • Overflow
      • Everything
    • Compile_Checks
      • None
      • Warnings
      • Errors
    • Contracts
      • No
      • Yes
    • Style_Checks
      • No
      • Yes
    • Ada_Version
      • Compiler_Default
      • Ada83
      • Ada95
      • Ada05
      • Ada12
      • Ada2022
      • GNAT_Extensions
    • Source_Encoding
      • Compiler_Default
      • UTF-8

    For example, to enable all run-time checks in the release profile:

    [build-switches]
    release.runtime_checks = "Everything"
    

    To disable style checks for all profiles:

    [build-switches]
    "*".style_checks = "No"
    

    All switch categories also accept a custom list of switches, for instance:

    [build-switches]
    release.optimization = ["-O1"]
    validation.style_checks = ["-gnatyg"]
    
  • provides: specifies a list of releases of another crate for which the current release is a drop-in replacement. I.e., the crate is either API-compatible or call-compatible, depending on how it is to be used (as a source library, or providing some command-line tool).

    Example:

    name = "foo"
    provides = ["bar=1.1"]
    # A crate depending on `bar^1` might find this `foo` release in its solution instead.
    
  • forbids: an array of tables containing dependency specifications, just as the depends-on property. Releases matching one of the forbidden dependencies are prevented from appearing in a solution with the release doing the forbidding.

    There are two use cases for this property:

    1. To codify known conflicts between releases for some reason (for example, sources with the same name).
    2. To provide drop-in replacements for another crate, in conjunction with a provides field. In this case the release must both provide and forbid the crate for which it is a replacement.

    Example:

    name = "bar"
    version = "1.0"
    provides = [ "foo=1.0" ]
    [[forbids]]
    baz = "*" # This crate cannot coexist with ours for some reason
    foo = "*" # No other crate that provides foo is needed/allowed at the same time
    

Work-in-progress dependency overrides

It is usual to develop several interdependent crates at the same time. In this scenario, it is often impractical to rely on indexed releases which are not intended to be modified. Instead, one would prefer to use a work-in-progress version of a crate to fulfill some dependency.

Alire provides pins to support this use case. Pins override dependencies, they are intended to be used locally, and to be fulfilled by proper dependencies once a crate is ready to be published. The use of pins is based on two ideas:

  • Dependencies are given, as normally, in the depends-on array of the manifest, even for those dependencies to be pinned. This way, once the release is ready, pins are simply removed and the actual dependencies are used in their place.
  • Dependency overrides, aka pins, are given under the [[pins]] array of the manifest.

Three kinds of pins are available, all of them with the syntax:

crate_name = { pin_attributes }

The specific pin kinds and their attributes are:

  • Pins to versions: used to force the use of a particular version of an indexed crate.

    • version: a string containing a single version to be used.
    • crate_name = { version = "1.2+hotfix-1" }
  • Pins to local crates: a local directory will fulfill the crate dependency, no matter what version is given in its local manifest. “Raw” Ada projects without an Alire manifest can be used too, as long as their project file matches the crate name and it is located in the directory given as override.

    • path: an absolute or relative path to the crate directory.
    • crate_name = { path = "../my/wip/crate" }

    For the common case of directories containing an Alire manifest, dependencies and pins will be included recursively in the build context.

  • Pins to git repositories: the repository will be cloned locally and its directory will be used as in the previous case. This pin may optionally include a commit to fix the checkout to be used, or a branch to track. Otherwise, the default branch will be used. Running alr update will refresh the checkout.

    • url: the URL of a git repository
    • commit (optional): a complete git commit hash.
    • crate_name = { url = "https://my/repo.git" } # Updatable pin to default branch
    • crate_name = { url = "https://my/repo.git", branch="feature" } # Updatable pin
    • crate_name = { url = "https://my/repo.git", commit="abcdef..." } # Fixed pin

Using pins for crate testing

Pins are also useful to have a separate test project that depends on your main crate. The recommended setup is as follows:

/path/to/my_crate
├── alire.toml
└── tests
    └── alire.toml

I.e., a tests crate is initialized within the main my_crate. In tests manifest, you have a dependency and local relative path pin for my_crate:

# tests/alire.toml
[[depends-on]]
my_crate = "*"              # Any version of the main crate
aunit = "*"                 # We can have dependencies for testing only
[[pins]]
my_crate = { path = ".." }  # Overridden by the latest sources

Then, my_crate is published normally, and tests can be used locally for any kind of testing needed on my_crate without polluting my_crate manifest with test specifics (like extra dependencies used by the test setup).

External releases

The above information applies to regular releases distributed from sources (that is, the Ada projects whose distribution is the main Alire goal). Some special supporting releases also exist that are described differently.

A release is considered “external” when it is not built from sources and, furthermore, its semantic version cannot be known until run time. Hence, the availability and version of these releases is detected by alr.

Several definitions for these external releases may exist so they are defined in a manifest as a vector with key external:

[[external]]
# Common entries to all externals
kind = "hint" # One of several predefined external kinds
hint = "Please install SDL in your platform from source or system packages"
# Specific external kind parameters might follow

All external kinds can define these regular properties:

  • available: when defined, it restricts the external detection to the given environment conditions.

  • hint: optional dynamic string containing an explanation for the user on how to make the external entity available. This explanation is shown on request with alr show --external, or after alr get, for any external dependency that could not be detected.

External kinds: hints

A plain undetectable external kind intended to simply serve as a hint. For crates that are known to be unavailable through Alire, it serves to provide a generic or customized hint to the user. It has no specific fields, other than the common ones just described. Its key is "hint":

[[external]]
kind = "hint" # Identifies this external kind
# Bare minimum external. Optionally, the hint/available fields can be used.

External kinds: command-line tools

This external kind is used to describe commands that can be run in the system, and that are able to provide their own version via some particular invocation. Their specific fields are (all mandatory):

kind = "version-output" # Identifies this external kind

version-command = ["gnat", "--version"]
# Invocation that will provide the version when the tool is available

version-regexp  = "^GNAT ([\\d\\.]+).*|^GNAT Community ([\\d]{4}).*"
# TOML-escaped GNAT.Regpat-compatible regular expression. Parenthesized
# matches will cause the matched expression to be parsed as the Semantic
# Version of the tool.

provides = "another_crate_name"
# This crate will be equivalent to `another_crate_name` for the solver. The
# version will be the same as detected for the current external. For example,
# all GNAT compilers provide the "gnat" crate, and so there cannot be two
# compilers in the same solution.

External kinds: system packages

Systems that have their own package manager (e.g. Linux) can readily provide many complex dependencies still unpackaged as source code in Alire. Alire can use these on supported platforms during resolution. At this time, the supported platforms are Arch, CentOS, Debian, Fedora, Homebrew, MacPorts, MSYS2, RHEL, SUSE/openSUSE, and Ubuntu; do not hesitate to contact us if you would like to maintain other distributions.

A system external gives a list of platform package names that supply the dependency natively. The platform package manager will be used to detect their availability and version. To that effect, the origin field is used (which can accept dynamic expressions in this context):

kind = "system" # Identifies this external kind
origin = ["libncursesada3", "libncursesada5"]
# As versions appear this list will grow. To speed up detection, dynamic
# expressions may become recommended for certain system packages.

For Ada pre-compiled system libraries that require the platform compiler for linking (e.g., in Debian/Ubuntu), and that cannot be used with other GNAT compilers, this should be expressed with the available property, e.g.:

available.'case(toolchain)'.user = false
# `available` defaults to true, so it is enough to flag the user toolchains

Parameters

  • os: name of the OS. Currently supported values are: freebsd, linux, macos, windows, and os-unknown.

  • distribution: name of the Linux distribution or name of the software distribution platform if running on a different OS. Currently supported values are: arch, centos, debian, fedora, homebrew, macports, msys2, rhel, suse, ubuntu, and distribution-unknown.

  • toolchain: takes system value in distributions with the system Ada compiler first in PATH (GNAT FSF in Debian/Ubuntu), user otherwise (GNAT Community editions, other cross-target toolchains).

  • word-size: architecture word size. Currently supported values are: bits-32, bits-64, bits-unknown

Using crate configuration

Alire provides a mechanism for crates to expose a list of variables that can be set by other crates depending on them. The configuration variables will then be converted to Ada, C and GPR source files that can be used to change the behavior or feature set of the code.

Let’s start with a simple example. A crate named test can print debug log on the console. However printing on the console has a performance impact, for an embedded project it can even have a significant code size impact. Therefore it would be best if this logging can be disabled/enabled at compile time.

To achieve this, a crate maintainer can define a configuration variable in the crate manifest alire.toml. The definition will be like so:

[configuration.variables]
Enable_Logs = {type = "Boolean", default = false}

A single variable of type Boolean with a default value of false.

From this definition, Alire will generate various source files, including an Ada package specification:

package Test_Config is
   Enable_Logs : constant Boolean := False;
end Test_Config;

In the crate source code, this configuration package can be used like so:

   if Test_Config.Enable_Logs then
      Print_Log ("This is a log message.");
   end if;

If one of the crates depending on test sets the configuration variable to true, e.g.:

[configuration.values]
test.Enable_Logs = true

The constant value will change in the generated configuration package:

package Test_Config is
   Enable_Logs : constant Boolean := True;
end Test_Config;

Which will enable logging in the test crate.

It is possible for multiple depending crates to set test.Enable_Logs to the same value, however if two depending crates set the variable to a different value then the configuration is invalid and Alire will print an error. If no depending crates set the test.Enable_Logs variable, then its default value is used.

When to use crate configuration?

Usually when something has to be static or known at compiler-time, either for performance or memory usage.

When not to use crate configuration?

When the Ada languages provides a better alternative. There are many ways to provide an Ada API that will result in compile time optimization or static memory usage.

For instance, discriminants are an effective way to let the user define the size of a buffer:

   type Buffered_Thing (Size : Positive) is private;
private
   type Buffer_Array is array (Positive range <>) of Unsigned_8;
   type Buffered_Thing (Size : Positive) is record
      Buf : Buffer_Array (1 .. Size);
   end record;

With this definition, users are then able to allocate either statically, on the stack or on the heap depending on their project.

   Thing : Buffered_Thing (Size => 256);

Use cases

Log levels

Enumerations variables in crate configuration can be used to set a level of log verbosity:

[configuration.variables]
Log_Level = {type = "Enum", values = ["Info", "Debug", "Warn", "Error"], default = "Warn"}

Buffer size

Integer variables can be used the define the size of a static buffer:

[configuration.variables]
Buffer_Size = {type = "Integer", first = 0, last = 1024, default = 256}

This is useful in particular for embedded projects where compile time memory usage is preferred over dynamic allocation.

Server URL

String variables can be used to define the URL of a website or service:

[configuration.variables]
URL_Name = {type = "String", default = "example.com"}

PID coefficients

Real variables can be used for PID coefficients:

[configuration.variables]
Proportional = {type = "Real"}
Integral = {type = "Real"}
Derivative = {type = "Real"}

Worst case allocation

Integer variable can be used to define The maximum length of file names in a file-system:

[configuration.variables]
Max_Filename_Length = {type = "Integer", first = 5, last = 128}

Select algorithm in GPR project file

Crate configuration also generates a GPR project file, therefore it can be used to control which units are compiled in the project.

[configuration.variables]
Sort_Algorithm = {type = "Enum", values = ["bubble", "quick", "merge"]}

The generated GPR will look something like this:

project Test_Config is
   type Sort_Algorith_Kind is ("bubble", "quick", "merge");
   Sort_Algorith : Sort_Algorith_Kind := "quick";
end Test_Config;

It can be used in the main GPR file like so:

   package Naming is
      for Body ("Test.Sort") use "test-sort__" & Test_Config.Sort_Algorith;
   end Naming;

With the files test-sort__bubble.adb, test-sort__quick.adb and test-sort__merge.adb each implementing a different algorithm.

Platform Specific Code

In the crate configuration Alire also generates a few built-in values to identify the host platform:

  • Alire_Host_OS
  • Alire_Host_Arch
  • Alire_Host_Distro

They can be used in the main GPR file to add a different source directory based on the OS. For instance:

   for Source_Dirs use ("src",
                        "src/" & Test_Config.Alire_Host_OS);

with the following directory tree:

+-- src
    +-- host_specific.ads
    +-- linux
    |   +-- host_specific.adb
    +-- macos
    |   +-- host_specific.adb
    +-- windows
        +-- host_specific.adb

Build Profiles and Switches

As part of crate configuration, Alire will generate a list of compiler switches in the configuration GPR file. The list of switches for a given crate is controlled from two features:

  • build-profiles
  • build-switches

There are 3 build profiles available in Alire:

  • Development
  • Release
  • Validation

By default, the root crate is in Development and the dependencies are in Release. The defaults can be overridden in two ways:

  • The build profile of the root crate can be changed with a switch to the alr build command:
    • $ alr build --release
    • $ alr build --validation
    • $ alr build --development (default)
  • In the root crate manifest, the build profile of each crate in the solution can be changed with the [build-profiles] table.

    This can be used, for instance, in a unit test crate to set the crate under test in validation profile, or to debug one of the dependencies.

    Example:

    [build-profiles]
    lib_under_test  = "validation"
    lib_to_debug    = "development"
    

Each crate can customize the compiler switches corresponding to its profiles using the [build-switches] table. In general, this should be avoided to preserve consistency in the ecosystem. However, there are cases where it makes sense for a crates to change its build switches. For instance, a SPARK crate that is proved to be safe from errors can disable run-time checks in all profiles:

[build-switches]
"*".runtime_checks = "none"

It is also possible to specify a custom list of compiler switches for a category:

[build-switches]
release.optimization = ["-O1", "-gnatn"]

Using switches in GPR file

Alire will generate a list of switches in the crate configuration GPR file. It will look something like this:

abstract project my_crate_Config is
   [...]
   Ada_Compiler_Switches := External_As_List ("ADAFLAGS", " ") &
          (
            "-Os" -- Optimize for code size
           ,"-gnatn" -- Enable inlining
          );
   [...]

In the main GPR file, “with” the crate config GPR and use the Ada_Compiler_Switches variable to define compiler switches:

with "config/my_crate_config.gpr";
project My_Crate is

   [...]

   package Compiler is
      for Default_Switches ("Ada") use My_Crate_Config.Ada_Compiler_Switches;
   end Compiler;

Compiler versions and cross-compilers

Dependencies in Alire are used also to deal with compiler versions and cross-compilers. Also related is the information on toolchains available in the Toolchain management document or via alr help toolchains.

Excluding compiler versions

One may know that a particular compiler version has a problem with some code. This may be expressed with dependencies on the generic gnat crate, which although is not found in the catalog, is a crate that all GNAT compilers provide. (Such a crate without actual releases, but provided by other crates, is called an abstract crate.) For example:

gnat = ">=7"   # We require a minimum compiler version
gnat = "/=7.3" # We know a precise version is incompatible

Since only one dependency on a same crate may appear, the relational operators & (and), | (or) can be used instead:

[[depends-on]]
gnat = "/=7.3 & >=7"

Requesting a compiler for a concrete target

The other use of compiler dependencies is to specify that a compiler for a particular target is needed. (Note that the project file also has to specify the proper target and runtime.) This way Alire can configure the appropriate environment for the build. For example:

gnat_arm_elf = "*" # Any compiler targeting ARM

Dependencies on cross-compilers should only be used in crates that actually require a concrete target (e.g., final binaries) to avoid preventing their use as general libraries with any compiler.

Further reading

You can inspect index files to get an idea of how projects are included into the catalog.


Settings

alr provides a generic mechanism to list, get, set or unset settings options, either in a local or global context.

Option names (keys) can use lowercase and uppercase alphanumeric characters from the Latin alphabet. Underscores and dashes can also be used except as the first or last character. Dot ‘.’ is used to specify sub-categories, e.g. ‘user.name’ or ‘user.email’.

Option values can be integers, floats, Booleans (true or false), or strings. The type detection is automatic, e.g. 10 is integer, 10.1 is float, true is Boolean. You can force a value to be a string by using double-quotes, e.g. “10.1” or “true”. Extra type checking is used for built-in options (see below).

Specific settings options:

  • --list List settings options
  • --show-origin Show origin of settings values in --list
  • --get Print value of a setting option
  • --set Set a setting option
  • --unset Unset a setting option
  • --global Set and Unset global settings instead of the local one
  • --builtins-doc Print Markdown list of built-in settings

Examples:

  • alr settings --global --set my_option option_value

    Will set a setting option with the key my_option and the string value option_value in the global settings file.

  • alr settings --get my_option

    Will print the value setting option my_option if it is defined, otherwise the command fails.

Custom settings options

The alr settings command allows you to set and get any combination of option key and value. You can use this feature to store your own project related settings, or implement tools that integrate in an Alire context. However, be careful when naming custom settings options because Alire may use the same key in the future. We recommend using a distinctive sub-category name, for instance: my_project.my_option.

Built-in settings options

The options used by Alire are pre-defined and documented. We call these options built-ins.

A built-in option has a pre-defined type that is checked when setting or loading. For instance:

  • alr settings --global --set user.email "This is not an email address"

will fail because the value tentatively assigned to user.email is not an email address.

The built-ins also have a short description to document their type and usage.

Built-ins list

You can get the list of options recognized by alr with alr help settings, including their default values and a short explanation of their effects.

Relocating your settings

By default, alr stores its global settings at <user home>/.config/alire. You can use any other location by setting in the environment the variable ALIRE_SETTINGS_DIR=</absolute/path/to/settings/folder>, or by using the global -s switch: alr -s </path/to/settings> <command>.

Using pristine default settings can be useful to isolate the source of errors by ensuring that a misconfiguration is not at play.

Inspecting your settings

These commands may help you in identifying Alire settings and environment:

  • alr settings --list will show all settings options in effect.
  • alr version will print many relevant bits of information about the current alr environment.
  • alr --version will just print the version number and exit.
  • cache.dir [Absolute path][Default:]: Directory where Alire will store its cache.

  • dependencies.git.keep_repository [Boolean][Default:FALSE]: When true, git origins are a proper git repository after deployment. Otherwise they are deployed as a plain directory.

  • dependencies.shared [Boolean][Default:TRUE]: When true, dependencies are downloaded and built in a shared location inside the global cache. When false, dependencies are sandboxed in each workspace.

  • distribution.disable_detection [Boolean][Default:FALSE]: If true, Alire will report an unknown distribution and will not attempt to use the system package manager. Takes precedence over distribution.override.

  • distribution.override [String][Default:]: Distribution name to be used instead of autodetection. No effect if distribution.disable_detection is True.

  • editor.cmd [String][Default:]: Editor command and arguments for editing crate code (alr edit). The executables and arguments are separated by a single space character. The token ${GPR_FILE} is replaced by a path to the project file to open.

  • index.auto_community [Boolean][Default:TRUE]: When unset or true, the community index will be added automatically when required if no other index is configured.

  • index.auto_update [Integer][Default:24]: Hours between automatic index refresh. Set to 0 to disable.

  • index.host [String][Default:https://github.com]: URL of the community index host

  • index.owner [String][Default:alire-project]: Owner of the index repository (GitHub user/org).

  • index.repository_name [String][Default:alire-index]: Name of the index repository.

  • solver.autonarrow [Boolean][Default:TRUE]: If true, alr with will replace ‘any’ dependencies with the appropriate caret/tilde dependency.

  • toolchain.assistant [Boolean][Default:TRUE]: If true, and assistant to select the default toolchain will run when first needed.

  • toolchain.dir [Absolute path][Default:]: Directory where Alire will store its toolchains.

  • update.manually_only [Boolean][Default:FALSE]: If true, Alire will not attempt to update dependencies even after the manifest is manually edited, or when no valid solution has been ever computed. All updates have to be manually requested through alr update

  • user.email [Email address][Default:]: User email address. Used for the authors and maintainers field of a new crate.

  • user.github_login [GitHub login][Default:]: User GitHub login/username. Used to for the maintainers-logins field of a new crate.

  • user.name [String][Default:]: User full name. Used for the authors and maintainers field of a new crate.

  • warning.caret [Boolean][Default:TRUE]: If true, Alire will warn about the use of caret (^) for pre-1 dependencies, for which tilde (~) is recommended instead.

  • warning.old_index [Boolean][Default:TRUE]: If unset or true, a warning will be emitted when using a compatible index with a lower version than the newest known.


Policies

Crate ownership

Because Alire comes late in the history of the Ada and SPARK languages we will not apply a strict “first come, first served” policy on crate names. At least for the first months or years, we allow ourselves a degree of control on the projects/crates published in the index, with the following goals:

  • Long term support: Owner and maintainers of a project are most likely in the best position to maintain the corresponding Alire crate.
  • Respect the ownership of projects: Owner and maintainers of a project deserve to be credited for their work.
  • Avoid user confusion on the names of crates: Crate names should be clear with regard to the project they contain. For instance, do not try to impersonate existing crates or projects.

To that end we will potentially reject a crate or transfer the ownership of a crate.

We count on the goodwill of the contributors to help us conduct this moderation in a kind and courteous way. Do not submit a crate to the Alire index if you are not willing to comply with this policy.

As the Alire project matures, we expect to do less moderating and potentially remove this policy in favor of a “first come, first served” policy.

Release immutability

A release (identified by a unique semantic version) is protected against changes by its integrity hashes. If errors are identified post-publication, a release could be withdrawn, or superseded by a new one (using the appropriate major/minor/patch/build version changes), but not modified.

Best practices

  • Avoid using ada as a prefix for your crate name, this will make the project harder to find in a list. ada suffix is ok when the project is a binding for an existing library (e.g. sdlada, gtkada).

  • Split big projects in multiple crates:

    • If your project is a collection of components (like GNATcoll for instance) and each component has different dependencies, you should consider splitting the collection into multiple Alire crates. The components can still be maintained in the same repository and use the same release archive/commit (e.g. gnatcoll_sqlite, gnatcoll_sql, gnatcoll_postgres).

    • If your project is an application/executable/tool, some parts of the application may be interesting on their own and could benefit the ecosystem. For instance a parser for a standard file format would be useful across projects.

  • Separate supporting projects into nested crates, in particular when developing libraries:

    • Tests, demos, examples, etc., can be provided in nested crates so they can be conveniently used locally when needed without causing extra build load on clients. See the documentation on local pins for details.

    • The manifests of these nested crates need not to be published to the community index if they do not provide an application of general interest.

  • GPR project file clashes: to prevent issues when combining the GPR project files of different crates, we recommend to follow the rules below:

    • Use a project file name that matches the name of the crate (e.g. my_crate.gpr for a crate named my_crate)

    • Avoid using multiple GPR project files for a single crate

    • Avoid using common names for GPR project files such as shared.gpr, common.gpr, config.gpr, etc.

    • Prefix GPR scenario variables with the name of your crate:
      Build_Mode := External ("MY_CRATE_BUILD_MODE", "release");
      
    • Avoid common names for GPR scenario variables such as OS, TARGET, BUILD_MODE, MODE, etc.

    • For library projects, do use the “standard” LIBRARY_TYPE external, but wrap it in a crate specific external:

      type Library_Type_Type is ("relocatable", "static", "static-pic");
      
      Library_Type : Library_Type_Type :=
        external ("MY_CRATE_LIBRARY_TYPE", external ("LIBRARY_TYPE", "static"));
      
      for Library_Kind use Library_Type;
      

      Having the MY_CRATE_LIBRARY_TYPE external will allow users to override the value LIBRARY_TYPE just for this crate, if need be.

Source code

  • If you use non-ASCII characters in the source code, then use UTF-8 encoding for the sources and add -gnatW8 to compiler options (as provided by alr init command). Other crates can use -gnatW8 and this means the compiler will read your crate sources as UTF-8 and the run-time library will use UTF-8 for text I/O, so make sure your crate is OK with this.

  • Consider to follow or at least familiarize yourself with the Ada Style Guide.


User-facing changes log

This document is a development diary summarizing changes in alr that notably affect the user experience. It is intended as a one-stop point for users to stay on top of alr new features.

Release 2.0

ALIRE_SETTINGS_DIR replaces ALR_CONFIG

PR 1625

This reflects the new nomenclature of Alire settings versus crate configuration. Also, it better reflects that the effect is on the whole library and not only the alr command-line tool.

alr settings replaces alr config

PR 1617

The alr settings command replaces the alr config command. This change is introduced to tackle the confusion between the configuration of the Alire commands and operations, and the configuration of crates.

alr config is still available and should work as before with the exception of a deprecation warning message.

Deprecation of toolchain --install/--uninstall/--install-dir

PR #1614

Toolchain selection for use by Alire is still done by using alr toolchain --select.

For the installation of toolchains outside of Alire management (i.e., for direct use with other tools, but not with Alire), the recommended method now is to use alr install, e.g.:

# Install to the default location, <user home>/.alire/bin
$ alr install gnat_native gprbuild

# Install elsewhere
$ alr install --prefix=/path/to/installation gnat_native gprbuild

Removal of managed toolchains can be done by simply removing their folders inside the toolchain cache (reported by alr version).

Cache and toolchain storage location overridding

PR #1593

The cache directory can now be set independently of the configuration directory, by setting the cache.dir config builtin to an absolute path. For example:

alr config --global --set cache.dir /path/to/my/global/cache

Since the cache by default also contains installed toolchains, which may not be needed to be moved elsewhere, the toolchain.dir can be used to dissociate toolchain location from cache location in a similar way:

alr config --global --set toolchain.dir /path/to/my/toolchains

New switch alr build --stop-after=<build stage>

PR #1573

From alr help build:

Build stages

Instead of always doing a full build, the process can be stopped early using --stop-after=<stage>, where <stage> is one of:

  • sync: sync pristine sources to build location
  • generation: generate configuration-dependent files
  • post-fetch: running of post-fetch actions
  • pre-build: running of pre-build actions
  • build: actual building of sources
  • post-build: running of post-build actions

Enable shared dependencies by default

PR #1449

Pre-2.0, Alire worked always in “sandboxed” mode; that is, all source dependencies were found under <workspace>/alire/cache. This behavior can be now enabled with alr config --set dependencies.shared false, locally or globally.

By default, post-2.0, Alire works in “shared” mode, where sources are downloaded once (to ~/.cache/alire/releases) and unique builds are created (under ~/.cache/alire/builds) for unique configurations. This should minimize rebuilds across crate configurations and workspaces, and eliminate risks of inconsistencies.

Disk use is decreased by unique source downloads, but might be increased by unique build configurations. Cache management and cleanup will be provided down the road. The build cache can always be deleted to retrieve disk space, at the cost of triggering rebuilds.

Unique builds are identified by a build hash which takes into account the following inputs for a given release:

  • Build profile
  • Environment variables modified in the manifest
  • GPR external variables declared or set
  • Configuration variables declared or set
  • Compiler version
  • Vaue of LIBRARY_TYPE and <CRATE>_LIBRARY_TYPE variables.
  • Hash of dependencies

Automatic index updates

PR #1447

A new configuration option, index.auto_update, allows setting the refresh period of indexes. It defaults to 24 hours and the user will be asked the first time to allow automatic updates. Setting this option to 0 will also disable automatic updates.

When enabled, updates may happen before executing commands that rely on indexes: get, search, with, etc.

Deprecation of dependencies.dir in favor of dependencies.shared

PR #1419

A new system of shared sources and builds is being implemented, which will ensure full consistency and reuse of builds.

In the new system (still disabled; enable it by setting alr config --set dependencies.shared true), dependencies will no longer be stored under <workspace>/alire/cache/dependencies. Instead, three new directories are introduced:

  • $HOME/.cache/alire/releases: contains sources for read-only purposes and binary releases (except toolchains, see below).
  • $HOME/.cache/alire/builds: contains source builds for a unique combination of build profile, GPR externals and environment variables.
  • $HOME/.cache/alire/toolchains: contains downloaded toolchains.

The previous $HOME/.cache/alire/dependencies that contained both toolchains and binary releases is no longer in use.

Users wanting to modify dependencies in tandem within a workspace are encouraged to use the pin system.

If these default locations for shared dependencies must be relocated, this can be achieved by using a new configuration path (ALR_CONFIG or -c global switch). In that case, the aforementioned paths will be found under /path/to/config/cache.

Set working directory with --chdir

PR #1479

A new switch --chdir (short form -C) is introduced which requires a target directory argument. alr then runs as though it were invoked in that directory.

Request review of an index submission with alr publish --request-review

PR #1409

When a submission has passed all server-side tests, for the time being it must be reviewed and merged manually. This can now be done with alr publish --request-review <num>.

Cancel an index submission with alr publish --cancel

PR #1406

A pending submission can be closed with alr publish --cancel <num> --reason <text>.

Track user’s index submissions with alr publish --status

PR #1400

The new alr publish --status switch will print a table with unmerged pull requests opened by the user against the community index repository.

Automatic release submission during alr publish

PR #1398

alr publish will now prompt to continue after manifest creation into a series of steps culminating on the creation of a draft pull request on the community index repository.

The new steps will perform all necessary actions: forking of the community repository into the user account, cloning, committing of the new manifest, and pull request creation.

For alr to be able to do these steps on the user’s behalf, the user has to provide a ‘Personal Access Token (PAT)’ with ‘repo’ permissions.

The old behavior, ending the assistant after manifest creation, can be achieved with the new --skip-submit flag.

Removal of alr test --docker

PR #1366

The option to test indexed releases with the local alr using a Docker image has been removed, as it never made too much sense for alr to invoke itself, and it introduced unwanted complexity into the alr test command.

Global sharing of dependencies via config setting

PR #1367

A new built-in configuration key can be used to define a directory where all dependencies will be stored:

alr config --set --global dependencies.dir /abs/path/to/existing/dir

Without --global, as usual, the setting will only affect the working crate.

The use of this feature entails a penalty in that crate configuration files will be regenerated before every build to ensure consistent build profiles.

Caveat emptor: dependencies built by several dependents with different configuration options or scenario variables might cause race conditions or other unexpected issues. Use this feature with caution.

Test of a working crate with alr test

PR #1356

This PR enables the use of alr test on local crates. Previously, it could only be used on indexed ones.

By default, running alr test will build the crate in release mode. This behavior can be overridden by defining one or more test actions.

Binary releases moved to system cache from system config directory

PR #1349

Alire was storing large binary releases like compilers in the config location, which is against best practices.

Users are advised to delete the old location to recover disk space, or to manually move the contents to avoid redownloading toolchains.

  • Old location: <user home>/.config/alire/cache
  • New location: <user home>/.cache/alire

Installation of indexed crates

PR #1335

It is now possible to install an indexed crate directly:

$ alr install hello

This is roughly equivalent to alr get hello && cd hello* && alr install

The main differences are:

  • Cleanup is automatic.
  • Several crates can be installed in one go, e.g.: alr install hello hangman.
  • alr get will always retrieve the latest version, whereas alr install will also require a complete solution to dependencies.

Installation of local crates

PR #1322

alr install without arguments performs the installation of the current crate. With --info, it shows the contents of an installation prefix. For example:

$ alr -n init --bin mycrate && cd mycrate
$ alr install
$ alr install --info
Installation prefix found at /home/user/.alire
Contents:
   mycrate=0.1.0-dev

Or, to install the hangman game:

$ alr get hangman && cd hangman*
$ alr install

New subcommand alr install

PR #1302

A new subcommand alr install allows the installation of binaries to a location intended to be added to the user’s PATH. The default install location is $HOME/.alire, with binaries going into $HOME/.alire/bin.

This is a experimental feature that will see improvements and tweaks in further PRs and as we gather feedback on its usage.

At present, only binary releases can be installed (e.g., compilers, gprbuild, gnatprove). There is no ability to uninstall releases either (but reinstallation can be forced).

Only one version per executable can be installed, meaning that, for example, only one toolchain version can exist in an installation prefix. So, this feature is intended to make the user’s preferred version of a crate generally available in the system for use outside of Alire, but not to replace e.g. the ability of Alire to use several compilers, or to reuse compiled libraries as dependencies in several workspaces.

Examples:

$ alr install gnatprove
ⓘ Installing gnatprove=12.1.1...
ⓘ Installation complete.

$ alr install
Installation prefix found at /home/jano/.alire
Contents:
   gnatprove=12.1.1

$ PATH+=:$HOME/.alire/bin gnatprove --version
Usage: gnatprove -Pproj [switches] [-cargs switches]
...

$ alr install gnatprove^11
error: Requested release gnatprove=11.2.3 has another version already
installed: gnatprove=12.1.1. (This error can be overridden with --force.)

$ alr --force install gnatprove^11  # Downgrade installation

Find dependents of a release with `alr show –dependents

PR #1170

A new switch for alr show lists the newest release that depends on another given release. For example, to find direct dependencies on libhello:

$ alr show libhello --dependents
CRATE  VERSION  DEPENDENCY
hello  1.0.1    ^1.0.0

To identify all dependents, both direct and indirect, use --dependents=shortest, which will also show the shortest dependency chain from (indirect) dependent to dependee:

$ alr show aws --dependents=shortest
CRATE                    VERSION  DEPENDENCY  CHAIN
adabots                  1.2.0    ^21.0.0     adabots=1.2.0»aws=21.0.0
awa                      2.4.0    ~21.0.0     awa=2.4.0»utilada_aws=2.5.0»aws=21.0.0
awa_unit                 2.4.0    ~21.0.0     awa_unit=2.4.0»awa=2.4.0»utilada_aws=2.5.0»aws=21.0.0
matreshka_spikedog_awsd  21.0.0   *           matreshka_spikedog_awsd=21.0.0»aws=21.0.0
servletada_aws           1.6.0    ~21.0.0     servletada_aws=1.6.0»utilada_aws=2.5.0»aws=21.0.0
utilada_aws              2.5.0    ~21.0.0     utilada_aws=2.5.0»aws=21.0.0
webdriver                1.0.0    *           webdriver=1.0.0»aws=21.0.0

Finally, to obtain all paths through which dependents reach a dependency, use the all value. In this case crates may appear more than once in the listing:

$ alr show --dependents=all cortex_m
CRATE               VERSION  DEPENDENCY  CHAIN
minisamd51_bsp      0.1.0    ^0.1.0      minisamd51_bsp=0.1.0»samd51_hal=0.2.0»cortex_m=0.5.0
minisamd51_example  0.1.1    ^0.1.0      minisamd51_example=0.1.1»minisamd51_bsp=0.1.0»samd51_hal=0.2.0»cortex_m=0.5.0
pico_bsp            2.0.0    ~0.5.0      pico_bsp=2.0.0»rp2040_hal=2.0.0»cortex_m=0.5.0
pico_examples       2.0.0    ~0.5.0      pico_examples=2.0.0»rp2040_hal=2.0.0»cortex_m=0.5.0
pico_examples       2.0.0    ~0.5.0      pico_examples=2.0.0»pico_bsp=2.0.0»rp2040_hal=2.0.0»cortex_m=0.5.0
pygamer_bsp         0.1.0    ^0.1.0      pygamer_bsp=0.1.0»cortex_m=0.5.0
pygamer_bsp         0.1.0    ^0.1.0      pygamer_bsp=0.1.0»samd51_hal=0.2.0»cortex_m=0.5.0
rp2040_hal          2.0.0    ~0.5.0      rp2040_hal=2.0.0»cortex_m=0.5.0
samd51_hal          0.2.0    ^0.1.0      samd51_hal=0.2.0»cortex_m=0.5.0

Finer control of build profiles in alr build

PR #1119

Build profiles can be now tweaked from the command-line with a new switch:

  • alr build --profiles '*=development' # Set all profiles to development
  • alr build --profiles '%=validation' # Set profiles without an override in a manifest to validation

Explicit crates can be given, intermixed with one of the wildcards, which apply to the rest of crates in the build:

  • alr build --profiles '*=development,libhello=release' # Set all profiles to development but for libhello

The existing switches --release, --validation, --development continue to control root crate profile and take the highest priority:

  • alr build --validation --profiles '*=development' # Set the working crate to validation and the rest to development

Reuse build profile of alr build when issuing alr run

PR #1080

alr run will trigger a build to have an up-to-date executable, and before this PR this was always a development build. Now, the last profile used during an alr build will be reused.

Release 1.2

New subcommand for listing and manual triggering of actions

PR #983

Actions defined in a working release can be listed now with alr action. A specific kind of action can be triggered by specifying its kind. Actions in the complete dependency tree can be listed and triggered with the --recursive switch.

$ alr action                # Display actions defined in the root release
$ alr action --recursive    # Display all actions in the root and dependencies
$ alr action post-build     # Run post-build actions in the root release
$ alr action post-build -r  # Run post-build actions in the root and dependencies

UTF-8 Source Encoding

PR #972

As part of the build profile feature, the GNAT switch -gnatW8 is unconditionally added to the list of compiler switches in the configuration GPR file. This switch enables the use of UTF-8 for source file encoding.

Support for crates in repository subfolders (monorepos)

PR #939

A crate can now be located nested within a repository and still be published as a repository origin. This enables simpler publishing of such crates, as alr publish will automatically recognize the situation. For example, let us say we have this structure:

my_repo
  +-- my_crate
  +-- my_crate_examples

Running alr publish at ./git_repo/my_crate or ./git_repo/my_crate_examples will detect that the crate is not at the root and adjust the origin metadata to take into account the extra path.

Other typical hierarchies that should likewise work are:

my_crate (also a repository)
  +-- examples

my_repo
  +-- crate1
        +-- examples
  +-- crate2
        +-- examples

At this time alr publish will not remove pins, so that is still a manual adjustment that the user may have to perform prior to publishing; that is, the manifest at each nested crate must be manually readied for publishing (just as for any other regular crate).

Root crate build profile

PR #896

The default build profile for the root crate is Development. This can be changed with the --release, --validation and --development switches for alr build.

$ alr build --release     # build with release profile
$ alr build --validation  # build with validation profile
$ alr build --development # build with development profile
$ alr build               # build with development profile

Build profiles and switches

PR #895

As part of the crate configuration feature, Alire will generate a list of compiler switches in the configuration GPR file. The list of switches is controlled from two features:

  • build-profiles
  • build-switches

User defined command aliases

PR #853

It is now possible to define in configuration (local or global) aliases for the alr commands.

$ alr config --set --global alias.graph 'show --graph'
$ alr graph

Will run the alr show command with the --graph switch.

New command alr exec -- <command line>

PR #853

This new command takes an executable and arguments and run them in the Alire environment/context of the current crate.

$ alr exec -- sh -c 'echo ${ALIRE}'
True

Pass alr clean switches to gprclean

PR #853

Using the -- delimiter the switches and arguments for alr clean can now be passed to the underlying gprclean execution.

For instance:

$ alr clean -- -XTEST=42

Pass alr build switches to gprbuild

PR #850

Using the -- delimiter, the switches and arguments for alr build are now passed to the underlying gprbuild execution.

For instance:

$ alr build -- -f

will force recompilation.

Global switches only allowed before sub-command

PR #850

Before this change the global switches (-f, -n, --config=, etc.) could be placed anywhere on the command line. For instance, the following two commands were equivalent:

$ alr -f show
$ alr show -f

Global switches are now only allowed before the sub-command name. Such that:

$ alr -f show # Is OK
$ alr show -f # Is not OK (unrecognized option '-f' for 'show')

Release 1.1

Lockfile moved to alire folder

PR #789

The lock file (alire.lock) is now a purely internal file, regenerated as needed from scratch, and needs not be put under version control. Since, furthermore, this file is not intended for user edition or inspection, it is now created inside the alire folder of a crate.

Existing lock files at the root of a crate will be automatically migrated to their new location the first time an alr command that uses the lock file is run inside a crate with the old situation.

This change obsoletes the recommendation that accompanied PR #501 about putting the lock file under version control.

Conflicting releases

PR #781

For releases that have known incompatibilities (duplicated source names, drop-in equivalent crates), it is now possible to express this information through a forbids table array, with the same syntax as dependencies. For example:

[[forbids]]
conflicting_crate = "^1"

Releases related by a forbids property will not appear simultaneously as dependencies in a solution, as the solver will discard these combinations.

Toolchain management

PR #775

A variety of GNAT compilers (native and cross-target) is now available through Alire. These compilers are managed with the alr toolchain new command. The available compilers can be listed with alr search --full gnat_.

Toolchain configuration is common to all crates in the active configuration prefix (which can be switched with the global -c option or by providing a path with the ALR_CONFIG environment variable).

The alr toolchain --select subcommand allows selecting the preferred default compiler (or none at all, to continue using the previous mode of operation) for crates that do not specify one.

Crates that require a particular cross-compiler may now specify it as a regular dependency on, e.g., gnat_riscv_elf.

In addition to a default compiler, the preferred version of a compiler for a target may be made available with alr toolchain --install <crate[=version]>. When launching a build, Alire will use preferably the default selected compiler or, if the default is for a different target, one of the other installed compilers. If no installed compiler is available for the crate target, Alire will offer to download the appropriate cross-target compiler.

Finally, running alr toolchain without arguments will list the currently installed compilers and gprbuild versions.

Pins to git branches

PR #754

A new option for remote pins exist to track branches:

[[pins]]
wip = { url = "https://gitrepo.com/wip.git" branch="feature" }

Running alr update will pull any changes from the branch.

Pins stored in the manifest

PR #743.

The options to modify pins through the command-line (with --use, alr pin [--unpin] crate have been disabled in favor of direct edition of the manifest. This way, pins are more robust against lockfile format changes. These kinds of pins exist:

[[pins]]
foo = { version = "1.3.2+bugfix" } # Require a specific version
bar = { path = "../my/bar" } # Use a local crate to override a dependency
baz = { url = "https://github.com/baz.git" } # No commit, will use HEAD, will update on `alr update`
gru = { url = "https://gitlab.com/gru.git" commit="123456890abcdef..." } # Explicit commit, won't update

Automatic GPR ‘with’ now in crate configuration

PR #740.

When adding or removing dependency with alr with, the list of with statement for each project files of the dependencies is now automatically added to the GPR crate configuration file instead of the root project file.

Git remotes for pinned releases

PR #715

The pinning commands (alr with --use, alr pin --use) now also accept a git repository URL, which will be downloaded and used to override a dependency, as previously could be done only with local directories. The pinning feature works recursively, so unpublished crates can now have complete dependencies prior to submission to the community index (which relies only on indexed dependencies).

Switch to help with publishing of multi-crate repositories

PR #635.

The alr publish command now supports a new --manifest <file> switch, to help with packaging sources that provide several crates. Maintainers can now prepare different manifest files for the corresponding crates, and select each one in turn for publishing, without the repository itself being an actual Alire crate. Source management must still be taken care of by maintainers; sources should not be shared by project files in different crates intended to be simultaneously included.

Configuration of crates

PR #699. PR #673.

Pre-compilation parameterization of source files can be now achieved by declaring variables and initial constant values for these variables in the Alire manifests. This allows customizing code in both the root crate and dependencies. For example:

[configuration.variables]
Device_Name = {type = "String", default = "no device name"}
Debug_Level = {type = "Enum", values = ["Info", "Debug", "Warn", "Error"], default = "Warn"}
Buffer_Size = {type = "Integer", first = 0, last = 1024, default = 256}

[configuration.values]
crate_1.var1 = 42
crate_1.var2 = true
crate_2.var1 = "Debug"

Check more examples and details in the catalog specification section Using configuration.

Release 1.0

Narrow down versions for dependencies given without restrictions

PR #675.

When a user requests a dependency without narrowing down its version set (e.g., alr with foo), the solved version will be used to instead add an “update-safe” dependency (e.g., foo^1.x, foo~0.x). To truly request any version, this can be explicitly entered as alr with 'foo>=0'.

This behavior can be disabled by setting the solver.autonarrow configuration option to false.

The command alr list has been renamed to alr search --crates

PR #671.

To consolidate search functionality under the single alr search command, the old behavior of alr list can now be achieved with alr search --crates. By default, alr search looks into releases, but now it can look too into crates with the new --crates switch.

Document caret/tilde use for pre-1.0 versions, and warn about it

PR #669.

Alire does not change the meaning of caret (^) and tilde (~) operators for pre/post-1.0 versions. This interpretation has been clarified in the catalog specification, and alr will warn about any suspicious usage. This warning may be disabled by the user with the new warning.caret configuration option.

Do not perform build relocations

PR #667.

GPRBuild machinery for build relocation is incompatible with some use cases, so now all builds are performed in place, using the locations given in project files. This should only have a user-visible impact for pinned dependencies, which will see changes in their build directory when Alire builds for dependent crates are run.

Switch to check for unknown enumeration values in the index

PR #656.

To allow backwards-compatible use of new supported environment configurations in the index, unknown values in dynamic case expressions are silently ignored when loading an index. In order to allow pinpointing these values (or truly wrong entries), a new switch alr index --check can be used that will reject an index containing unknown values.

This error, either in indexes or a local manifest, can be downgraded to a warning with --force.

Switch manifest licenses field to SPDX expressions

PR #629.

The licenses in crate manifests now expects a valid SPDX expression. Custom license identifiers are accepted with the format: custom-[0-9a-zA-Z.-]+.

Example:

licenses = "MIT OR custom-my-own-license"

For the 1.x release, usage of the previous licenses format is obsolete and will trigger a warning. In future major releases this format will not be accepted at all.

Custom editor command for alr edit

PR #611.

The code editor launched by alr edit can now be configured instead of using the hard-coded GNATstudio. Use alr config --set --global editor.cmd '<BINARY> <ARGS>' for custom editor and command line arguments. The token ${GPR_FILE} is replaced by a path to the project file to open.

For instance:

$ alr config --set --global editor.cmd 'emacs ${GPR_FILE}'

The default editor is still GNATstudio.

Release 0.7-beta

Assistance to generate and publish as tarball

PR #529.

By using alr publish --tar, the publishing assistant starts with the creation of a tarball of the sources in an Alire workspace. The user must upload this tarball to an online location, after which the assistant proceeds as if it had been invoked with alr publish http[s]://url/to/tarball.tgz.

First publishing assistant

PR #527.

A new publishing assistant can be invoked with alr publish [URL [commit]]. At this time, the assistant requires that all necessary metadata for a release, excepting the [origin] table, is informed in the alire.toml file.

The assistant has local and remote modes of operation. In the local mode, the user invokes the assistant from within a repository on its computer that is up-to-date with its remote, and that contains an Alire workspace. In this case, it is enough to run alr publish.

In the remote mode, the user must prepare a source file or repository in their final online locations, and use alr publish <URL> [<commit>], with the commit being mandatory for repositories and not needed for source archives.

In all cases, alr will fetch the sources, perform a few checks on the completeness of the information, and generate a final metadata file, intended to be submitted to the community index via pull request. An upload link is provided for convenience that can be used to create this pull request.

Complete information about this feature is available in the updated Publishing page.

Other features of the assistant are that, in the local mode, a branch or tag can be specified to pinpoint a commit, and that the test build of the crate can be skipped with --skip-build.

Move manifest and lock files to top-level folder

PR #501.

The metadata information about a crate/release has been reworked to simplify user workflows and internal operation. Metadata is stored in the manifest file, which as of this PR is always called alire.toml and located at the root directory of an Alire-enabled workspace. A companion lock file, alire.lock, stores information about the dependency solution and overrides.

These two files can be safely put under version control. The manifest, in particular, is intended to evolve with your Ada project, by being an up-to-date record of any necessary dependencies and other properties (version, project files, executables, maintainers, etc.).

The manifest internal format has been simplified by eliminating the possibility of multiple releases from its contents, which removes some nesting, and removing or making optional some fields that only make sense at the time of publishing a crate to some index. Check the catalog-format-spec file for details.

The alire directory continues to exist, and it is used to store the source code of dependencies, local configuration and backup files. It can be safely ignored for VCS, as its contents are either not critical or can be reconstructed from the manifest information.

New alr with --versions switch

PR #464.

A new alr with --versions switch is available to obtain version-focused information of dependencies. Namely, the combined dependencies on a crate are shown, with the release in the solution, and the last known version for the crate:

CRATE      DEPENDENCY      SOLVED  LATEST
a_project  (root)          0.0.0   unknown
hello      ^1              1.0.1   4.0.0
libhello   (^1.0) & (~1.0) 1.0.1   2.0.0
superhello *               1.0.0   1.0.0
unobtanium *               missing unknown
wip        *               /fake   unknown

New alr with --graph and alr with --tree switches

PR #465.

The ASCII art dependency graph generated with graph-easy, that was printed at the end of alr with --solve output, is moved to its own alr with --graph switch. A fallback tree visualization is generated when graph-easy is unavailable. This new tree visualization can also be obtained with alr with --tree:

my_project=0.0.0
├── hello=1.0.1 (^1)
│   └── libhello=1.0.1 (^1.0)
├── superhello=1.0.0 (*)
│   └── libhello=1.0.1 (~1.0)
├── unobtanium* (direct,missed) (*)
└── wip* (direct,linked,pin=/fake) (*)

Automatically ‘with’ GPR project files from dependencies

PR #458.

When adding or removing dependency with alr with, a list of with statement for each project files of the dependencies can be automatically added to the root project file:

-- begin auto-gpr-with --
--  This section was automatically added by Alire
with "libhello.gpr";
-- end auto-gpr-with --

project Test is
...

This feature can be permanently enabled or disabled with a local or global configuration option:

alr config --global --set auto-gpr-with false

Crates with project files not compatible with this feature can disable it using the auto-gpr-with entry:

auto-gpr-with=false

Show release-specific dependency sets in solutions

PR #453.

The dependency solution shown with the --solve switch now details for each release the particular version set with which a dependency is brought into the dependency closure. For example:

Dependencies (graph):
   hello=1.0.1      --> libhello=1.0.1 (^1.0)
   superhello=1.0.0 --> libhello=1.0.1 (~1.0)

Use crate metadata when pinning to a directory

PR #450.

When pinning a dependency to a directory (alr pin|with <crate> --use), detect if the target contains Alire metadata (as usual, in an alire subdir). In that case, use it to determine further dependencies and project file scopes. Also, the target dependency name is verified.

For such a target directory, a shortcut with command is available since the crate name can be determined from the metadata: alr with --use /path/to/target (note the absence of a crate name).

Allow working with incomplete solutions

PR #447.

Before this patch, any change in dependencies that resulted in an incomplete solution caused a final “invalid solution” error. Now, any incomplete solution will be presented to the user with details about the unfulfilled dependencies. This solution can be accepted and worked with normally, although the user is responsible to provide in the environment any missing project files.

This change affects all commands that compute a dependency solution, i.e., get, pin, update, with.

Use a directory to fulfill a dependency

PR #439

A local path can now be used to fulfill a dependency. The path can be supplied during initial dependency addition or afterwards during pinning, via the --use switch. Such a path will be added to the environment generated by alr setenv. Examples:

$ alr with some_crate --use /some/absolute/or/relative/path
# To simultaneously add a dependency and the directory to use for its GPR file.
# The dependency needs not to exist in the loaded indexes.

$ alr with indexed_crate
$ alr pin indexed_crate --use /path/to/gpr/containing/folder
# To pin a previously added dependency.