Name

theory - package API used by Mutiny

Synopsis

This document aims to explain the API available for writing packages for Mutiny. This should be considered the definitive document to consult when writing packages, or when developing package managers.

The key words "must", "must not", "required", "shall", "shall not", "should", "should not", "recommended", "may", and "optional" in this document are to be interpreted as described in RFC 2119.

Repositories

Package managers must support multiple repositories. The internal prioritization of these repositories is dictated by the Priority. User-facing sort (ex. when listing repositories installed on the system) is not defined.

Repository names are alphanumeric plus _ and -.

The file and directory layout of a valid repository is as follows, italicized items representing optional items, anything else required:

  • <repository name>/

    • metadata/

      • dependencies

      • summary

      • priority

    • libraries/

      • library1

    • packages/

      • openssh#1.2.3/ - This directory’s path is exported as ${PKGDIR}.

        • dependencies

        • libraries - Libraries to be used for constructing the package’s theory.

        • sha256sum

        • sources

        • theory0

        • metadata/ - All files under this directory are exported as environment variables after importing all libraries.

          • HOMEPAGE

          • LICENSES

          • SUMMARY

        • files/ - This directory’s path is exported as ${FILES}.

          • patches/

            • openssh-1.2.3-backport.patch

Repository metadata

Repositories must have a directory in their root named metadata.

Dependencies (optional)

Repositories may contain a file in metadata, named dependencies. The dependencies file must contain a single repository on each line. Repositories listed in this file are repositories which the package manager must have installed prior to the installation of the repository depending on them. The order in which they are listed is unspecified.

Any repository listed will have its libraries directory searched when parsing package files; the order in which they are searched is determined by the priority value of the repository.

Do not add repositories to dependencies simply because a package in your repository depends on something in another repository. The package manager shall deal with determining what repository needs to be installed to satisfy a dependency through usage of the universe meta-repository.

Summary

Repositories should have a summary. The summary should be a short, one-line blurb describing the repository’s contents or objective.

Priority

Repositories must have a file within metadata named priority. The priority file must contain a non-negative integer.

The intended scheme goes like so:

  1. First-party primary repository, over which nothing has a greater priority There should only be one repository with this level. (ex. Base packages for a system.)

  2. First-party secondary repository (such as a supplementary repository). (ex. Repositories containing software categories or projects; GNOME, KDE…​)

  3. First-party tertiary repository. (ex. A distribution developer’s repository.)

  4. Third-party repository. (ex. A distribution user’s repository.)

Packages

Repositories should contain a directory named packages; if they do not, package managers may ignore them entirely, as there’s not much use to a repository with no package.

Package specifications

Package specifications (informally referred to as "specs") are strings which describe a package.

Specifications take on multiple permutations, because they are made up of four different parts, of which only the name is required.

Given the fully-qualified spec package#1.0::repository…​

  • Package name (package)

  • Package version (1.0)

  • Package repository (repository)

For a package spec to be valid, it must match the regex ((^[A-Za-z](?:[A-Za-z0-9_+-]*)?)(#[a-z0-9\._-]+)?(::[A-Za-z0-9_-]+)?|\*).

Breaking it down:

  • Package name is alphanumeric, plus _, +, and -. It must start with an alphanumeric.

  • Package version is numeric plus ., _, -, and lowercase alpha characters. (for r1, etc.)

  • Repository is alphanumeric plus _, and -. It must start with an alphanumeric.

All parts of a package spec are case-sensitive.

Disambiguation

The only strictly required part of a spec is the inclusion of the package name. If any other part other than the name is omitted, it will be disambiguated in order to determine what packages can satisfy the specification given.

If more than one package matches a specification, the package manager may prompt the user in some fashion to be more specific.

Format

theory0 files and lib0 files are effectively just shell scripts. Package managers should avoid locking the user into any particular Bourne-style shell implementation; as such, the format of these files should adhere to shell syntax as defined in POSIX 2016.

That said, a few exceptions are made for functionality that is deemed too useful to be given up, and isn’t yet specified in POSIX…​

  • Declaring arrays (array=( "foo" "bar" "baz" ))

  • Iterating through arrays (for item in "${array[@]}";do …​ done)

  • Appending to arrays (array+=( "xyzzy" ))

  • Local-scope variables (local var="value")

Build environment

When referring to a "build environment", this document is referring to the literal shell environment which the shell process is running in. This means it consists of things such as variables, functions, and current working directory.

Phases

Everything in this section is required of any package manager implementation.

"By default" refers to a package which does not define any phases or import any libraries which define phases.

Note the difference between "not defined" and "does nothing". Packages must have each phase defined, regardless of if they have any function; if a phase listed here is not defined by either the package manager, or the package (or a library used by the package), the package manager must error out and fail, because that is an invalid package.

"Does nothing" would mean something like pkg_init() { true; }. "Not defined" would mean no definition of the function. (ex. Attempting to run function that is not defined would give an unknown command error)

pkg_init()

This phase is ran when a build environment is created for a package building session. Normally nothing is done, and this is a dummy function.

Examples of other defintions could be creating a custom PATH and script wrappers to be used for build systems that are stubborn to cooperate with cross-compilation.

src_fetch()

Only ran during installation.

This phase’s purpose is to get any sources needed to make the package being built. By default it downloads any unretrieved files specified in the DOWNLOADS metadata, and then verifies the files match the checksums specified in the sha256sum metadata.

Usually you will not need to change this.

Examples of other definitions could include retrieval of git sources, hg, cvs, etc.

src_unpack()

Only ran during installation.

This phase’s purpose is to unpack any files retrived during src_fetch(). By default this means it will extract any archives downloaded into the [Build directory], and then change into the [Work directory].

Examples of other definitions could include checking out git sources into WORK, or similar.

src_prepare()

Only ran during installation.

This phase’s purpose is to prepare the package for the real build process; so, things which are normally done before building, like applying patches, generating Autotools scripts, etc. are to be done here.

By default it does nothing.

src_configure()

Only ran during installation.

This phase’s purpose is to run package configuration-related steps of the build process. Things like ./configure, cmake, or writing build configuration files would be done here.

By default it is not defined.

The rationale behind not providing a default definition is that it allows for more flexibility and less package manager dependent functionality. Rather than putting a default definition that, say, expects an Autotools like package, and putting that functionality in the package manager, it can be done with libraries.

src_compile()

Only ran during installation.

This phase’s purpose is to run the compilation process for the package. Things like make, ninja, etc. would be done here.

By default it is not defined.

src_test()

Only ran during installation.

This phase’s purpose is to run tests for the package being built. Things like make check, ctest, ./setup.py test, etc. are done here.

By default it is not defined. This phase is special in that the package manager should make note of this phase not being defined. The package manager should not fail when src_test() is undefined; at least, not by default. Failing may be useful for quality assurance purposes.

src_install()

Only ran during installation.

This phase’s purpose is to run the installation for the package; so, commands like make install DESTDIR="${IMAGE}".

Under no circumstances should anything in this phase touch something outside the build environment. The package manager will merge files from the package to the system, and the build process may not even have access to the system anyway, instead being built in a chroot or a sandbox of some sort.

By default it is not defined.

pkg_premerge()

Only ran during installation.

This phase’s purpose is to run any commands that are required to be ran on the system itself before the package is merged.

By default it does nothing.

pkg_postmerge()

Only ran during installation.

This phase’s purpose is to run any commands that are required to be ran on the system itself after the package is merged.

(ex. Updating icon caches, displaying important information after a major package upgrade, …​)

By default it does nothing.

Directories

The only requirements of the directory in which a package build is executed is that it is read-write accessable by the package manager, and that the work directory be entirely empty before any phases are ran.

The directories in which a build is executed are undefined. However, it is a good idea to use something located in a good location for temporary work. A directory in /var/tmp is a good idea; /tmp, not so much, as build directories should be allowed to persist for long periods of time.