Getting Started

Installation

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

You will also need a GNAT compiler toolchain. On Linux you can usually get it from your distribution. Otherwise, and for Windows and macOS you can download and install GNAT Community.

alr on Linux and macOS

For Linux and 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

You will also have to add your GNAT toolchain in the environmentPATH.

alr on Windows

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

Inside the PowerShell you will also have to add your GNAT toolchain in the environmentPATH. For instance with GNAT Community at the default location:

PS> $env:Path += ";C:\GNAT\2020\bin\;C:\GNAT\2020-arm-elf\bin\"

On Windows, the first time you run alr the program will ask if you want to install msys2. 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.

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 project

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.

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

Creating a new project

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

  1. Issue alr init --bin myproj (you can use --lib for a library project)
  2. Enter the folder: cd myproj
  3. Check that it builds: alr build
  4. Run it: alr run

Dependencies and upgrading

Alire keeps track of a project dependencies in the file ./alire.toml of your project. You may check the one just created in the previous example.

This file can be managed through alr:

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

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.

Alternatively you can edit the file (example in the works) to add dependencies and then issue:

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

Finding available projects

For quick listing of crates and their descriptions you can use the list command:

  • alr list [substring]

There’s also a search command which provides more details about specific releases:

  • alr search <substring> will look for substring in crate names.
  • alr search --list will list the whole 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

By default alr is quite terse and will hide the output of subprocesses, mostly reporting in case of failure. 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.

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 


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.

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.x branches are used by stable versions of alr.
  • devel-x.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, you will be provided with an upload link for the branch your alr is using.

However, when submitting releases manually, you must 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.

Note that, as of this writing, only development branches exist, until the first stable release of alr is made.

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.

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 an 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] (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.
    • This is a temporary measure until more sophisticated publishing automation is supported. See the 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. 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 (for now) 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 will need to prepare individual online source archives (or repositories) and proceed from there.

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. 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


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

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) array of strings. Flat list of licenses for the software that is packaged. The following licenses are allowed:

    • AFL 3.0, AGPL 3.0, Apache 2.0, Artistic 2.0, BSD 2-Clauses, BSD 3-Clauses Clear, BSD 3-Clauses, BSL 1.0, CC0 1.0, CC BY 4.0, CC BY-SA 4.0, ECL 2.0, EPL 1.0, EPL 2.0, EUPL 1.1, EUPL 1.2, GPL 2.0, GPL 3.0, ISC, LGPL 2.1, LGPL 3.0, LPPL 1.3c, MIT, MPL 2.0, MS PL, MS RL, NCSA, OFL 1.1, OSL 3.0, PostgreSQL, Unlicense, WTFPL, zlib, GMGPL 2.0, GMGPL 3.0, Public Domain.
    • Custom, can be used for project specific licenses.

    If the license is unknown or not in the list above, leave an empty array.

    licenses = []
    

    For a double license:

    licenses = ["GPL 3.0", "MIT"]
    
  • 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). Logical operators for and (&), or (|) are accepted; see the Semantic_Versioning project documentation on extended version sets.

  • 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"
    

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

    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 installing this package. The general action syntax is:

    [[actions]]
    type = <kind>
    command = <command>
    

    <command> is a an array of strings for a shell command to run in the source directory. <kind> can be either:

    • post-fetch: the command is to be run right after getting the package sources

    • pre-build: the command is to be run right before GPRbuild is run

    • post-build: the command is to be run right after GPRbuild has been run

    • test: the command is run on demand for crate testing within the Alire ecosystem (using alr test).

    Actions accept dynamic expressions. For example:

    [[general.actions.'case(os)'.linux]]
    type = "post-fetch"
    command = ["make"]
    
    [[general.actions.'case(os)'.windows]]
    type = "post-fetch"
    command = ["cmd", "build"]
    
    [[general.actions.'case(os)'.'...']]
    # An explicit empty case alternative, which is not mandatory
    
  • 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).

    Examples of origin tables:

    # Clone a git repository
    [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"]
    

    If the package only maps a package from the system package manager, (for instance make), use:

    origin = "native:make"
    

    Have the expression evaluate to an empty string to indicate that the package is not available, or just leave the alternative out. For instance, to state that make is available on Debian/Ubuntu and not on the other platforms:

    [origin.'case(distribution)']
    'debian|ubuntu' = "native:make"
    
  • 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"
    

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.

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 (at this time, Debian & Ubuntu. Do not hesitate to contact us if you would like to maintain other distributions) during resolution.

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: linux, macos and windows.

  • distribution: name of the Linux distribution, or none if running on a different OS. Currently supported values are: debian, ubuntu.

  • 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

Further reading

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


Configuration

alr provides a generic mechanism to list, get, set or unset configuration 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 config options:

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

Examples:

  • alr config --global --set my_option option_value

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

  • alr config --get my_option

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

Custom configuration options

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

Built-in configuration 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 a configuration file. For instance:

  • alr config --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

Here is the list of Alire built-in configuration options. You can also get this from alr with alr help config.

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

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

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

  • msys2.do_not_install [Boolean]: If true, Alire will not try to automatically install msys2 system package manager. (Windows only)

  • msys2.install_dir [Absolute path]: Directory where Alire will detect and/or install msys2 system package manager. (Windows only)

  • auto-gpr-with [Boolean]: If true, Alire will automatically add/edit a list of ‘with’ statements in the root GPR project file based on the dependencies of the crate.


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.

  • 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.


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.

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.md] 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.