Exheres for Smarties

Table of Contents

Overview

exheres-0 is an experimental, fluid EAPI and tree layout (in theory, the two concepts are independent, but the only place using one without the other is the Paludis test suite). When we stop breaking things we'll make an EAPI exheres-1 and carry on using exheres-0 for experiments.

Tree Layout

packages/
    cat-foo/
        per-category.exlib
        pkg-foo/
            pkg-foo-1.23.exheres-0
                per-package.exlib

    exlibs/
        foo.exlib

Concept Renames

What Gentoo calls use flags we call options. We may support non-boolean options at some point if there're good use cases demonstrated. USE_EXPAND is called SUBOPTIONS, and uses a colon (e.g. video_cards:intel).

Keywords has become platforms. We're still using arch and ~arch, for now. We don't automatically inject PLATFORMS into MYOPTIONS; platforms are regular SUBOPTIONS and should be managed as such.

General Strictness

Various things that were merely QA notices for ebuilds are strict errors.

Metadata Variables

Environment Variables

Dependencies

Not fully implemented on the package manager side.

DEPENDENCIES="
    build+run:
        foo/bar
    run:
        foo/baz
    post:
        foo/monkey
    "

Labels affect all following atoms inside the current ( ) block, but not any higher level blocks. The default is build+run. So:

DEPENDENCIES="
    blah? ( cat/build-and-run )
    foo? (
        cat/build-and-run
        build:
            cat/build-only
        run:
            cat/run-only
            bar? (
                cat/run-only
                post:
                    cat/post-only
            )
            cat/run-only-again
    )
    cat/build-and-run"

Labels aren't just for *DEPEND replacement. We'll also have them for host vs target deps:

DEPENDENCIES="
    build,host: foo/bar
    run,target: foo/other
    "

Note the use of a comma rather than plus. Plus is for labels of the same group, comma is for different groups.

There're also suggested, recommended and required labels.

When specifying labels, if no value is specified for a given group, its current value is propagated. So:

DEPENDENCIES="
    suggested:
        cat/suggested-build-and-run
    run:
        cat/suggested-run
    "

The full list of labels divided by group at present is:

Package Dependencies

Slot deps:

Square brackets (these go after slot deps, so foo/bar:slot[bracket]):

Operators:

~> means the same as it does for Gems.

SRC_URI

SRC_URI has two new things. First, labels. Just one type:

SRC_URI="
      like-primaryuri.tar.bz2
      manual: fetch-restricted-stuff.tar.bz2
      listed-only: will-not-look-on-mirrors.tar.bz2
      local-only: only-on-local-mirrors.tar.bz2
    "

Supported values are:

Second, arrows:

SRC_URI="http://sucky-upstream/download/foo/1.2/foo.tar.bz2 -> foo-1.2.tar.bz2"

When consulting the listed URI (which can be a mirror://) the filename is used. When consulting mirrors and for the saved download location, the value on the right of the arrow is used.

Annotations

Annotations are another new dep-like toy. They don't convey any critical information (as in, a compliant package manager need do nothing more than parse the contents, and an annotation-using package manager can ignore any particular key it wants). They look like this:

DEPENDENCIES="
    build+run:
        zoo/monkey [[
            description = [ it needs a monkey ]
            url = [ http://explain.exherbo.org/?zoo-monkey ]
        ]]

        !zoo/clown [[
            description = [ clowns will murder us in our sleep ]
            url = [ http://explain.exherbo.org/?clowns-are-evil ]
            resolution = uninstall-blocked-after
        ]]

    post,suggested:
        zoo/snake [[
            description = [ otherwise we can't strangle things ]
        ]]
    "

Annotations start with [[ and end with ]] . Inside, they're key = value or key = [ value that can contain spaces ] . Note that whitespace is mandatory everywhere. Also note that currently the only 'quote' we allow is [ ] and you can't use things that look like use? flags, ( dependencies ) etc, but if this proves too restrictive we might change it.

Annotation keys we recognise currently are:

Annotations are not, in principle, limited to package and block dep specs. You can put them after other things too:

DEPENDENCIES="
    build,run: [[ here = [ would apply to the labels ] ]]
        foo? (
            || (
                cat/one
                cat/two
            ) [[ here = [ would apply to the || block ] ]]
        ) [[ here = [ would apply to the foo? block ] ]]
    "
SRC_URI="
    mirror://foo/${P}.tar.bz2 [[ here = [ would apply to the URI ] ]]
    "

but we don't do anything with any of those keys.

Don't overdo annotations. Blockers almost always benefit from having them. So do suggestions. Normal deps, not so much, although sometimes a little [[ note = [ configure.ac says 2.0, but we get runtime terminal corruption unless we use at least 2.3 ] ]] might not go amiss.

Phases

The default phase functions are named default_src_blah, and you're allowed to (and encouraged to) call them if you're just adding functionality. But rather than calling default_src_blah, you can just call the special 'default' function that'll look at what phase we're in and act accordingly.

pkg_pretend

Run in the sandbox and with userpriv. Called for every package after a 'paludis --pretend --install' or 'paludis --install' (but not currently for binaries -- not sure how we'll handle those yet). You can die in here to tell the user to change config things etc (but use option deps and the like if possible instead).

default_pkg_pretend()
{
    :
}

pkg_setup

Run in the sandbox.

default_pkg_setup()
{
    :
}

src_unpack

Run in the sandbox and with userpriv.

default_src_unpack()
{
    [[ -n "${A}" ]] && unpack --if-compressed ${A}
}

src_prepare

Run in the sandbox and with userpriv. The DEFAULT_SRC_PREPARE_PATCHES array, if set, should be set in global scope and mustn't be set dynamically. If you're doing anything complicated, write your own src_prepare. The variable is just for the easy cases.

default_src_prepare()
{
    if [[ -n "${DEFAULT_SRC_PREPARE_PATCHES[@]}" ]]; then
        expatch "${DEFAULT_SRC_PREPARE_PATCHES[@]}"
    fi
}

src_configure

Run in the sandbox and with userpriv. Again, DEFAULT_SRC_CONFIGURE_* is just for the easy cases.

default_src_configure()
{
    if [[ -x ${ECONF_SOURCE:-.}/configure ]] ; then
        econf \
            ${DEFAULT_SRC_CONFIGURE_PARAMS} \
            $(for s in ${DEFAULT_SRC_CONFIGURE_OPTION_ENABLES} ; do \
                option_enable "${s}" ; \
            done ) \
            $(for s in ${DEFAULT_SRC_CONFIGURE_OPTION_WITHS} ; do \
                option_with "${s}" ; \
            done )
    fi
}

src_compile

Run in the sandbox and with userpriv. DEFAULT_SRC_COMPILE_PARAMS is passed on to emake.

default_src_compile()
{
    if [[ -f Makefile ]] || [[ -f makefile ]] || [[ -f GNUmakefile ]] ; then
        emake ${DEFAULT_SRC_COMPILE_PARAMS[@]} || die "emake failed"
    fi
}

src_test

Run in the sandbox and with userpriv. Should be considered mandatory.

default_src_test()
{
    if [[ -f Makefile ]] || [[ -f GNUmakefile ]] || [[ -f makefile ]] ; then
        echo "Makefile found, looking for potential test targets"
        if make -j1 -n check ; then
            echo "Found check target"
            emake -j1 check || die "make check failed"
        elif make -j1 -n test ; then
            echo "Found test target"
            emake -j1 test || die "make test failed"
        else
            echo "No check or test target, skipping tests"
        fi
    else
        echo "No Makefile, skipping tests"
    fi
}

src_install

Run in the sandbox.

This default function is fairly complex so it deserves a longer explanation. The first part simply looks for a Makefile and, if one is found, uses the install target with DESTDIR="${D}". It dies if an existing makefile is missing an install target. The remaining part tries to identify (case-insensitively) documents that are common in lots of packages and installs them by means of dodoc. default_src_install supports one variable for adding extra parameters for emake install, three varialbes for adding extra docs and one variable for excluding undesired docs.

default_src_install()
{
    local done_docs old_set f d p doc e
    if [[ -f Makefile ]] || [[ -f makefile ]] || [[ -f GNUmakefile ]] ; then
        if make -j1 -n ${DEFAULT_SRC_INSTALL_PARAMS[@]} install ; then
            echo "Found a makefile, using the install target" 
            emake -j1 DESTDIR="${D}" ${DEFAULT_SRC_INSTALL_PARAMS[@]} install || \
                die "default emake install failed";
        else
            die "default emake install located a makefile but no install target"
        fi
    else
        echo "No makefile found, not using emake install"
    fi
    done_docs=
    old_set=$(shopt | grep 'nocaseglob[[:space:]]*on')
    shopt -s nocaseglob
    for d in '' ${DEFAULT_SRC_INSTALL_EXTRA_SUBDIRS[@]} ; do
        if [[ -n ${d} ]]; then
            [[ -d ${d} ]] || die "${d} is not a dir"
            pushd "${d}" > /dev/null || die "Failed to enter ${d}"
            local docdesttree="${DOCDESTTREE}"
            docinto "${d}"
        fi
        for f in README Change{,s,Log} AUTHORS NEWS TODO ABOUT THANKS {KNOWN_,}BUGS SUBMITTING \
            HACKING FAQ CREDITS PKG-INFO HISTORY PACKAGING MAINTAINER{,S} CONTRIBUT{E,OR,ORS} \
            RELEASE ANNOUNCE PORTING NOTES PROBLEMS NOTICE ${DEFAULT_SRC_INSTALL_EXTRA_DOCS[@]}; do
            for p in ${DEFAULT_SRC_INSTALL_EXTRA_PREFIXES[@]} '' ; do
                for doc in *([[:digit:]])${p}${f}{,+([._-])*} ; do
                    if [[ -s "${doc}" ]] ; then
                        for e in ${DEFAULT_SRC_INSTALL_EXCLUDE[@]} ; do
                            [[ ${doc} == ${e} ]] && continue 2
                        done
                        done_docs="${done_docs} ${d%/}${d:+/}${doc}"
                        dodoc "${doc}" || die "dodoc ${d%/}${d:+/}${doc} failed"
                    fi
                done
            done
        done
        if [[ -n ${d} ]]; then
            docinto "${docdesttree}"
            popd > /dev/null || die "Failed to leave ${d}"
        fi
    done
    if [[ -n "${done_docs}" ]] ; then
        echo "Installed docs ${done_docs# }"
    else
        echo "Didn't find any docs to install"
    fi
    [[ -n ${old_set} ]] || shopt -u nocaseglob
}

pkg_preinst

Run in the sandbox.

default_pkg_preinst()
{
    :
}

pkg_postinst

Run in the sandbox.

default_pkg_postinst()
{
    :
}

pkg_prerm

Run in the sandbox.

default_pkg_prerm()
{
    :
}

pkg_postrm

Run in the sandbox.

default_pkg_postrm()
{
    :
}

pkg_nofetch

Run in the sandbox and with userpriv.

default_pkg_nofetch()
{
    [[ -z "${A}" ]] && return

    local f g=
    for f in ${A} ; do
        [[ -f "${FETCHEDDIR}/${A}" ]] && continue
        if [[ -z "${g}" ]] ; then
            echo "The following files could not be fetched automatically for ${PN}:"
            g=no
        fi
        echo "* ${f}"
    done
}

pkg_config

Run in the sandbox.

default_pkg_config()
{
    eerror "No configuration function is defined"
}

Helper Functions

Organised by where they're implemented in Paludis, to make it easy to keep this up to date.

We're probably going to make most of these a lot stricter, and make forced die calls for things not existing and the like. Where it makes sense, we might also add a non-die version called try_blah.

Exlibs

Not that different to eclasses yet. Can be per-category and per-package rather than global (this doesn't work nicely with master repositories yet though). Loaded using 'require'. Functions are exported using 'export_exlib_phases'.

Formatting

Four space indents, no tabs.

Copyright goes to you, but you need to GPL v2 it if it goes in the tree. For stuff you wrote from scratch:

# Copyright 2008 Ciaran McCreesh
# Distributed under the terms of the GNU General Public License v2

If you make large changes to an exheres:

# Copyright 2007, 2008 Ciaran McCreesh
# Copyright 2008 Ivan Toby Rich
# Distributed under the terms of the GNU General Public License v2

If you rip off another non-trivial exheres:

# Copyright 2008 Ivan Toby Rich
# Distributed under the terms of the GNU General Public License v2
# Based in part upon 'foo-1.23.exheres-0', which is:
#     Copyright 2008 Ciaran McCreesh

Basing your work upon ebuilds is fine, but if you did, make sure you include the Gentoo copyright notice. This applies even if you read the ebuilds for inspiration - unless you have a lawyer handy to tell you exactly what does or does not count as a derived work, it's easiest to assume that if you looked at the ebuilds then it's derived (not because it necessarily is, but because doing so imposes no additional restrictions, whereas not doing so gives certain Gentoo people with nothing better to do an opportunity to make nuisances of themselves). So then you need do this:

# Copyright 2008 Ciaran McCreesh
# Distributed under the terms of the GNU General Public License v2
# Based in part upon 'foo-1.23.ebuild' from Gentoo, which is:
#     Copyright 1999-2007 Gentoo Foundation

Applying Patches

All attempts should be made to keep the package as close to the upstream version as possible. In general, patches for compilation and runtime errors are okay. However, patches that introduce new features are discouraged, unless they are part of the upstream release cycle. Patches that alter external interfaces are particularly problematic. It is unacceptable to believe that it is sufficient to patch packages in our repository for such a change as this makes life harder for users who want to use packages that are not in our repositories. In the case that a patch is needed it must contain a header with the following information:

Source: written by, from distribution, etc
Upstream: yes, no, bug number, etc
Reason: reason for the patch

This allows for others (or yourself), who may work with the package in the future, to be able to determine why a specific patch was applied. In the case that a patch no longer applies, the header aids in determining what to do with the patch (port it, discard it, etc).

If there is active upstream development, it is highly recommended that patches be passed along to them so that they may incorporate the fix in the next release. This is beneficial for both parties. Bugs in the upstream project are caught and fixed, and it reduces the effort required downstream to track and fix the bug with every release.