any-build – file with shell functions and variables to build the package.

Description

Anybuild is the shell file with methods, unique for the package. Together with the rest of the generic engine anybuild allows to build and maintain it.

To create anybuild for package hello-world-1.0, one should create directory ports/packages/hello-world-1.0/ and create file ports/packages/hello-world-1.0/hello-world-1.0.build. There one should put methods and variables, describing the build process.

The base minimum to get package buildable is:

-
src_config(3) method
-
src_compile(3) method
-
src_install(3) method
-
DEPEND variable with build time dependencies
-
patches for package sources in directory build/patch/package-name-1.2.3/

The simple illustration of anybuild with described set:

#!/bin/sh

DEPEND="
>zlib-1.2
>=gmp-4.3.2
"

src_config()
{
    ./configure \
        --enable-shared \
        --enable-static \
        --enable-utf
}

src_compile()
{
    make
}

src_install()
{
    make install DESTDIR=$D
}

The first part of build script contains auxiliary information, such as shell interpreter name, copyright, comments from revision control system and so on. The example above contains just string #!/bin/sh
The second part contains global variables, defined by the anybuild. Global variables are assigned at the beginning of the script before all methods. In that case they will be correctly initialised for all interfaces.
The third part contains methods, defined by the build. The methods are part of any-map(7) scheme, which contains overall sequence of build methods. It can be viewed in any/corelib/map.sh.

The content of given methods is very close to the generic build scheme, used by vast quantity of programs, distributed for POSIX operating systems. The difference is grouping commands by methods with dedicated semantic purpose.

src_config
does the configure stage: all the set up before the compiling is done here.
The most well-known variant is classic ./configure call. If there are any modifications of input data before the compilation stage, they should be done here as well. For example, it can be some edit of the generated header with sed(1). If there are variable assignments, which cannot be calculated out of method call (and placed at the top of build file), this method is best place for them.
src_compile
does the actual compiling, linking, generating documentation and everything else to produce resulting content of a package.
The wide-spread code to do this is plain call of make(1) program. It is used when compiling of the package is based on makefiles – files with content-generating directives, written in their own language. These files are integral part of package sources and are not provided by build engines of higher level, such as any(1). When package uses another inner building scheme, there will be another commands to invoke the process.
src_install
does the copying of the ready files to separate installation place.
The same classic scheme does the installation with make install call. DESTDIR argument of make points to the root installation directory, where the subtree of installed package begins. The value $D contains the empty directory, dedicated for installation of current package. That variable, among many others, is provided by engine.

Put together in single file, methods above provide spectacular picture of package building.
Though it is possible to omit each method with typical repeated implementation and not put it in the anybuild (they will be taken from the engine then), the methods above are recommended to be present explicitly.

Variable DEPEND holds build time dependencies of current package.
Dependencies are the list of entries, separated by spaces or new lines. Single entry is the range of allowed package versions and can be one of the forms:

>=used-program-1.2.3
Package used-program must be of version 1.2.3 or higher.
>used-program-1.2.3
Package used-program must be of version strictly higher then 1.2.3.
=used-program-1.2.3
Package used-program must be strictly of version 1.2.3.
used-program-1.2.3
Package used-program must be of version 1.2.3 or higher (same as >=).
<used-program-1.2.3
Package used-program must be of version strictly lower then 1.2.3.
<=used-program-1.2.3
Package used-program must be of version 1.2.3 or lower.

Versions are compared as version strings by algorithm of sort -V.
See any-api(7) for all details, which concern the work of DEPEND and its neighbours.

The anybuild example, given above, has two entries:

DEPEND="
>zlib-1.2
>=gmp-4.3.2
"
The first entry >zlib-1.2 means package zlib with any version which is higher then 1.2. This condition matches any version, beginning with 1.2.0, continuing with 1.2.1, 1.2.N, 1.3.0 and so on.
The second entry >=gmp-4.3.2 means package gmp of exact version 4.3.2 or higher, like 4.3.3 or 4.4.

Anybuild is allowed to use variables and to call methods, described by any-api(7), and is restricted to the rest. That is due to a necessity to keep every construction of anybuild stable working: names, conventions, call results. When inner engine method changes, appropriate change of all its calls is not a problem. When method is used in lots of builds, distributed asynchronously, such change is not affordable.

Directory build/patch/package-name-1.2.3/ is used for patch applying. Every file there is given to patch(1) command in the order of ls(1) output. It is possible that patches are constantly kept in some other place, and herewith copied automatically to the dir above. The main place to keep patches of package-name-1.2.3 permanently is ports/patch/package-name-1.2.3/. Everything found here is copied to build/patch/package-name-1.2.3/ automatically by engine methods. There is also possibility to retrieve patches automatically from the history of revision control systems, if there is required module of any-engine for that. In any case the directory build/patch/package-name-1.2.3/ is the single final place for all variants of patch retrieving.

The example of used patches for package package-1.2.3 may be the following:

build/patch/package-1.2.3/00-essential.patch
build/patch/package-1.2.3/01-path-change.patch
build/patch/package-1.2.3/04-memory-leak-fix.patch
Each patch file starts with a number to determine the order to apply. Besides number, file name contains its summarised aim. By default all patches in according directory are applied.

Naming and version

The name of the build is recognised as:
ports/packages/hello-world-1.0/hello-world.build
or:
ports/packages/hello-world-1.0/hello-world-1.0.build
In both cases first directory in ports/packages/ contains full name and version. The name of the build file itself may be shorter and contain only package name.

Packages hello-world-1.1 and hello-world-1.2 are fully independent, not just the same entity with different attributes 1.1 or 1.2. That attitude allows to keep multiple package sets in single structure inside working directory. Content of release is defined explicitly by text lists in ports/list/. Thus user is not forced to choose which version of the two neighbour packages to install into his ports.

Possible mistakes

In some cases complex calculations are needed to define variables at the beginning of the build file. That procedures may be put in methods before the variables initialisation. It is also possible to call existing shell methods to initialise variables, but these existing methods must not be redefined by the anybuilds (neither current one nor its neighbours, which use the current). Otherwise variables initialisation will be incorrect during dependencies handling.

Aplain usage in anybuild

Aplain is engine module, shipped with any(1) itself. It provides useful interfaces for build actions above. They add set of options to the launch of build tools, so they automatically recieve much data, prepared inside the engine. The user does not need to put large repeated set of options manually. To use aplain, add data word aplain somewhere in config file:
    [any]
    ...
    rdd_prf_id = core_any, aplain
An example of anybuild with aplain interfaces:
#!/bin/sh

DEPEND="
>zlib-1.2
>=gmp-4.3.2
"

src_config()
{
    aconf \
        --enable-shared \
        --enable-static \
        --enable-utf
}

src_compile()
{
    amake
}

src_install()
{
    amake install DESTDIR=$D
}

The visible difference with primary example is quit slight. Direct calls to build tools configure and make are replaced with interfaces aconf and amake. Each of them calls the same appropriate tool inside, but with additional parameters. These parameters do the routine of transaction of global parameters and settings: toolchain, architecture, path prefix, build flags.
For example, aconf is expanded to something like:

    ./configure \
        --prefix="${PREFIX}" \
        --build="${APLAIN_HOST}" \
        --host="${APLAIN_TARGET}" \
        CC="${CC}" \
        CXX="${CXX}" \
        LD="${LD}" \
        LXX="${LXX}" \
        CFLAGS="${CFLAGS}" \
        CXXFLAGS="${CXXFLAGS}" \
        LDFLAGS="${LDFLAGS}" \
        RANLIB="${RANLIB}" \
        STRIP="${STRIP}" \
        OBJDUMP="${OBJDUMP}" \
        AR="${AR}" \
        "${@}"
With all verbatim values hidden, build files stay clear and readable, while powerful config set is recieved. The reader sees the options, which belong to the configure of concrete package, and not such parameters, as compiler, which are described globally for all packages at the project level.

Besides readability, usage of aplain interfaces provides the possibility to change some build setting for many packages at once without manual edit of each anybuild.
For example, amake interface adds the MAKEOPTS variable as option to the call of make(1). That variable contains arbitrary make options, set up in config files. When a user decides to start package building in parallel, he writes in config file:

    any_make_opts = -j 5
The shell variable MAKEOPTS is initialised with the value "-j 5" and is automatically passed to make in all amake calls. As the result, all packages are built with parallelised make without single change in their anybuild files.
Other build parameters and tunings can be changed in similar manner.

Compilation paths and flags

The engine sets up several essential variables with paths, programs and resources for actual compilation, linkage and installation of binaries. All build interfaces already use the variables below, so typical cases may have no need them at all.

PREFIX contains main configuration prefix. Such prefix is used as the basic directory for files of a package. There can be several prefixes, used by distro project, but each package uses one of them. All possible prefixes are kept in PREFIX_LIST.
When single prefix is used in the project, variable may be ommitted and direct value is used instead, for example, /usr/include instead of $PREFIX/include.

D variable contains the path to install the ready binaries. It is used for make install call, as has been shown above. After src_install(3) ready files of a package can be accessed as ${D}${PREFIX}/bin/execfile, ${D}${PREFIX}/lib/libfile and so on.
For widespread /usr prefix that would be files $D/usr/bin/execfile or $D/usr/lib/libfile.

S variable contains the path to directory with sources, prepared for the build.

src_install()
{
    amake install DESTDIR=$D
    install -m 555 ${S}/obj/inner_bin ${D}/usr/bin/
}
In the example above additional file from source directory is installed, as it has not been handled by the rule of makefile.

ROOT variable keeps the root directory for all files, accessible to current package.
To show the location of include files in subdirectory:

src_config()
{
    CFLAGS="${CFLAGS} -I${ROOT}/usr/include/subdir" \
    aconf
}
Note the preserving of the previous value in assignment of CFLAGS. This construction helps to add new values to the variable several times correctly.

To search for libraries in dedicated directory in addition to compilation flags:

src_config()
{
    CFLAGS="${CFLAGS} -I${ROOT}/usr/include/subdir" \
    LDFLAGS="${LDFLAGS} -L${ROOT}/usr/lib/subdir" \
    aconf
}

The variables CC, CXX, CCLD, CXXLD keep the values of toolchain elements: C compiler, C++ compiler, generic linker and C++ linker. They are needed often to replace the default or hardcoded compiler name inside makefiles:

    sed \
        -e "/LDSHARED/ s%gcc%${CCLD}%" \
        Makefile > Makefile.edit
    mv Makefile.edit Makefile
In the example above file Makefile keeps the hardcoded name of the linker gcc. It cannot be changed with any outer options. So a user needs to edit the makefile and replace gcc with the actual string, used for linkage, which is kept in CCLD.

ENGINE_CFLAGS, ENGINE_LDFLAGS, ENGINE_CXXFLAGS keep the flags for compiler and linker, passed to resulting build string by default. These flags contain strings to search include and library files inside all used prefixes, and all platform-dependent flags.

P variable keeps the name of a current package, as it is called in the build list.
PN contains single name of program, PV only package version.

Conditional code

Group of ause* methods provide powerful tool to create flexible builds. Their behaviour is based on content of AUSE variable, which contains all used data words of current launch, and thus it is fingerprint of global distro project properties. Shell code with these methods is referenced as conditional code through manuals.

Method ause word returns logical true, if the given data word word is present in AUSE, and false otherwise. Method does not do any output. So that is the universal way to do something with dependence on some property, reflected in configuration:

    if ause sparc ; then
        dopatch big-endian-fix.patch
    fi
Here the additional patch big-endian-fix.patch will be applied in case the configuration somehow deals with sparc.
    if ! ause legacy ; then
        dopatch hardened.patch
    fi
Here the user wants to apply hardened patch, but not for the legacy case, which needs to keep things untouched.

Method ause_enable word string prints the string, if property of word is present. It is used to embed conditional text:

    aconf \
        --generic-option \
        $( ause_enable 'sparc' '--endian=big' )
When sparc is present in AUSE, the code above will be expanded into:
    aconf \
        --generic-option \
        --endian=big
When sparc is absent, resulting code will be:
    aconf \
        --generic-option \

The method helps to write large strings without duplication, such as calls of aconf.
ause_notenable does the same as ause_enable but with reversed logic.

Symmetric group of methods use, use_enable, use_notenable exists. They work exactly as ause series, but based on USE variable. They have another semantic purpose. AUSE and ause methods handle global configuration, such as binary architecture, OS, whole project properties. USE and use work with properties at package level: whether it is compiled together with some other package, which flavour of protocol implementation it includes, and so on.

The use_enable, use_notenable methods are applicated widely in dependencies:

    DEPEND="
    >=zlib-1.2.8
    >=ncurses-5.9
    $(use_enable 'ssl' '>=libressl-2.2.8')
    "

More dependencies

DEPEND contains build-time dependencies, which are handled by the build engine. The package manager at the target machine handles its own dependencies, which are called run-time. These are independent types of data, belonging to different program systems. To make package manager see dependencies it wants, build engine must create meta-information for the package in format, recognisable for manager, and put there records about deps. Build-time dependencies are seen by build engine directly.

In simple cases build-time and run-time dependencies are identical. Then no need to assign the same data twice. DEPEND is primary, as it is required before creation of installable package. So in this case DEPEND is used as the source for run-time dependencies as well. When these types differ, RDEPEND comes to a game.

RDEPEND variable holds run-time dependencies independently from DEPEND. It has the same syntax for entries and it's declared at the top of anybuild in the same way like this:

RDEPEND="
>=zlib-1.2.11
>=gmp-4.3.2
"

The value of DEPEND often can be subset of RDEPEND and vice-versa. Build-time requires everything from run-time plus some additional libraries, or run-time requires more in addition to build-time (frequent case for modules of perl(1) or python(1), which are simply installed at build stage, but need lots of another modules to actually work). The general technique to describe that situation is something like:

RDEPEND="
>zlib-1.2
>=gmp-4.3.2
"
DEPEND="
${RDEPEND}
>=flex-2.6.0
>=bison-3.0.0
"

DEPEND="
Compress-Zlib-1.42
"
RDEPEND="
${DEPEND}
>perl-5.16
>=mod_perl-2.0.10
"
Mind the order of related variables: the less containing one is declared first, so the second one uses the initialised variable inside.

The variable RDEPEND is not used by any engine directly, it is only written to package meta-information, generated by pack_gen_meta(3). Thus the actual format and names can be arbitrary to match the rules of packaging system:

DEPEND="
>zlib-1.2
>=gmp-4.3.2
"
RDEPEND="
>>zlib1g-1.2
>=libgmp10-4.3.2
>=libgmp3-dev-4.3.2
"

If RDEPEND follows the same entry format, as DEPEND, any takes into account the actual versions of packages, which were used during the build of the current one. For all entries from RDEPEND, which were used during the build, engine adds the lower minimal boundary in the form of >= to run-time dependencies. So resulting binary archive cannot be used with packages of lower versions, then ones it was built with.
In the case RDEPEND contains entries with non-DEPEND scheme, these entries are not limited from below by the packages, participated in current build.

One more type of dependencies is subset of build-time ones, needed to be installed in host. The entries of this type are kept in HDEPEND variable.
Build-time dependencies may provide not only include, library and another readable files, but also executable ones. These executables are used for build tasks: tools like make(1), documentation generators, parsers of different data formats.
Cross-compilation enlarges the list of host dependencies. If some binary file is generated during the build and then process tries to execute it, it cannot be done with different target architecture of that binary. Such files should be compiled for host architecture beforehand and installed additionally.

The syntax of the variable is the same as for DEPEND.

HDEPEND="
>=flex-2.6.0
>=bison-3.0.0
"
DEPEND="
>zlib-1.2
>=gmp-4.3.2
"
The example above declares the need in libraries of packages zlib and gmp, used at build time and residing in IMGDIR, and also the need in programs of packages flex and bison, which should be runnable at build time and residing in host binary environment at standard paths like /usr/bin/.

The build of a package may create binaries inside own build dir and then use them for further steps. Cross-compilation requires that files available for host run-time environment. When such binary is simple, it can be compiled twice for host and target right during the generic process. When such binary is complex and depends on large set of libraries, the entire package should be prepared for host and only after that the target variant is reachable. The expression for the case can be:

HDEPEND="
$(ause_notenable 'host' "${P}")
"
The example above declares the dependency on the same package in the host, excluding the case of build for host itself. It is assumed, that build for host is defined by dataword host. There is no such reserved word in engine, that assumption should be set up explicitly by config files.

Less dependencies

When build is done in staticdeproot mode, automatic control on build dependencies is not performed. The value of DEPEND is used only to initialise RDEPEND, when it is not declared explicitly. The latter one is still actually used to generate control information for package manager.
Work with HDEPEND is performed also.

More methods in anybuild

In typical and easy case the methods src_config(3), src_compile(3), src_install(3) are perfectly enough to describe the build process. All the routine is done by the rest of any-map(7) methods, sitting in the engine, so there is no need to reflect them in the anybuild. To see all methods doing the whole build sequence, one can view the map head method inside any/corelib/map.sh.
That scheme runs well when hidden methods do suit the situation. When they don't, a user is forced to add his code for them somehow. For example, the package must provide some directory for context of other packages, which is not caught by generic pkg_root(3).

In general case anybuild may contain redefenitions of any other method from any-map(7). While mentioned above src_* methods always need their implementation right inside anybuild, the rest already contain some behaviour, most likely non-trivial. If a user redefines them, he will need to repeat that behaviour manually, which is not desirable at all.

The pre* and post* methods solve that task. They allow to avoid the hand-made reimplementation of entire map method and at the same time give the ability to enrich it locally in anybuild. Each method of any-map(7) (besides src_config(3), src_compile(3), src_install(3)) has the call of empty stub premethod at the beginning and postmethod at the end. For example, pkg_install(3) has pkg_preinstall and pkg_postinstall methods. These stubs are always empty in the engine, as they are reserved for anybuilds.
When anybuild implements pkg_preinstall, that code will be called inside standard pkg_install(3) before its generic realisation. The summary effect is the execution of anybuild-defined code and untouched engine code of method itself one after another. The same applies to pkg_postinstall part.

#!/bin/sh

DEPEND="
>zlib-1.2
>=gmp-4.3.2
"

src_config()
{
    aconf \
        --enable-shared \
        --enable-static \
        --enable-utf
}

src_compile()
{
    amake
}

src_install()
{
    amake install DESTDIR=$D
}

pkg_postroot()
{
    linkdir ${D}/usr/share/db ${ROOT}/usr/share/db
}

pkg_prerminstall()
{
    mv ${D} "${D}.backup_$(date +%s)"
}
The example above contains two local described methods: pkg_postroot and pkg_prerminstall. pkg_postroot handles the need of directory usr/share/db, which will be used by other packages inside their ROOT. pkg_prerminstall creates backup from old binary directory instead of deleting it (unique name of backup directory is created with date).

Handling the sources

The cases arise when name of sources, kept at the author's place, and name of a package inside build system are completely different. The bright example is qt-everywhere-opensource-src name for simple qt4. To keep direct access to original sources variable MY_P exists. It contains the name for source archive (without extension), fetched by src_fetch(3). Set it up to use altered origin source name:
    MY_P="another-name-1.2.3"

Commonly names differ only with some part, for example, source archive contains -src in it. The more convenient way for customized source name in that case will be something like:

    MY_P="${PN}-src-${PV}"
The build in the form above is more universal and will work at any version of original package P.

The usage of MY_P influences only the original sources, not development ones. To change the name of repository with development sources for the package the variable MY_PD exists. Its usage is pretty like MY_P:

    MY_PD="side-development-1.2.3"

If the current package is a clone of another existing one under changed name, one will like to switch both sources:

    MY_P="${PN}-another-${PV}"
    MY_PD="${MY_P}"

Both MY_P and MY_PD do not affect the local directories, where the sources are finally extraced. S, D, PATCHDIR and all other essential variables still contain value of original P inside. So the engine always works with the package by the list name.

The general technique to combine generic and custom methods to fetch sources looks like:

src_postfetch()
{
    if [ ! -x "${S}/configure" ] ; then
        search_sources_somewhere_else
    fi
}
The example above assumes, that if correct file $S/configure exists, the generic src_fetch(3) has worked out correct, and postfetch part is not needed. If file is absent, we call some additional code to search for sources in altered ways.

The main action at source preparation stage is applying patches. By default, all files in PATCHDIR directory are applied. PATCHDIR points to build/patch/$P/ directory.

If the variable PATCHLIST is set up, list of needed patches is taken from there. Patches are taken in order of their listing in the variable.

PATCHLIST="
generic.patch
more-generic.patch
"
The example above declares explicitly two patches to be used: generic.patch and more-generic.patch.

CONFIGCACHE variable contains file with gathered cache for configure script. Its main use is cross-compilation, when the values for configure cannot be defined directly. When aplain module is used, that file is created automatically from cache values, preserved at APLAIN_SRCDIR directory. Then the file is given by default to configure scripts inside source directory.

Binary packages

By default each source package inside any(1) produces single binary package or installable archive. It is placed in PACKDIR directory. The name and version for package manager are taken from PN and PV respectively. Name of a file with installable archive usually also contains another fields, such as binary architecture and inner version, which is package subversion, given by distro project. However, each package format defines all fields of a file on its own, and some of the fields may be omitted and some others may be present.

To alter binary package name, BPN is used:

    BPN=${PN}-bin
Example above changes the run-time name of a package from original packname to packname-bin. The version remained the same. The change means that new name will appear both in generated control information and in the name of a file with installable archive. The name of a package inside directories of any(1) is not changed: D, S and all the rest still contain the old P.

To alter binary package version, BPV is used:

    BPV=1.0.0
Example above sets hardcode run-time version 1.0.0 for a package. Again the change does not concern the variables with inner paths of build engine.

If build of current package should produce several binary packages, their space-separated names should be listed in BPN:

    BPN="${PN}-bin ${PN}-lib ${PN}-doc"
In case of several values inside BPN binary package is created for each of them. Actual installable files of each packname must be placed in IMGDIR/packname/ for that. Splitting of files can be done automatically by some method inside pkg_config(3) or manually. Each resulting binary package will recieve version BPV and postfix with inner version of P, as defined by package format.

To produce several binary packages with different versions, BP can be used.

    BP="${PN}-bin-${PV} ${PN}-lib-${PV} ${PN}-doc-1.0"
In example above packages name-bin and name-lib get the same version, as original package, and name-doc gets its own version 1.0. Inner version will be attached to the string with version inside binary package as well.
Entries inside BP must follow naming conventions of packages inside any(1): name and version delimited by simple dash - symbol. If package manager needs another naming scheme, name will be translated when package control information is generated.

To change the version, including inner part, set up INNER_VERSION.

        INNER_VERSION="-1"
Example above turns off the automatic calculation of inner version and sets it statically to 1 (with separator from the main version).
To turn off the inner version completely, one should turn off the method fetch_inner_version:
    fetch_inner_version()
    {
        return
    }

Keywords

The variable KEYWORDS is used to control the support of some configuration by the current package. That control is done inside keywords_check method, which can be called from pkg_pretend(3) or map_autopre option of rdd(1).

If AUSE contains some word, and KEYWORDS contain the entry ~word, the whole build is skipped. Such word is called masking.

    KEYWORDS="~arm ~sparc"
The example above will skip the build and leave SKIP status, if AUSE contains arm or sparc.

If it is needed to mark only the working configuration and skip all the rest, entries inside KEYWORDS are given without ~:

    KEYWORDS="amd64 linux"
The example above claims the build works only if configuration contains amd64 OR linux words, and skips all the other cases.

When restrictive words (without ~) are given, they are checked inside AUSE variable and as a value of the following variables: ANYARCH, ANYARCHMODE, ANYOS, ANYARCHCAP, ANYARCHENDIAN. Masking words are checked only in AUSE.

Masking and restrictive words can be given at the same time:

    KEYWORDS="linux bsd ~arm"
The example above says the package will work only for linux or bsd and in any case will not work with arm dataword, even if the user has linux or bsd in configuration. arm word is checked inside AUSE, and linux or bsd word can be inside AUSE or additional variables, ANYOS for this example.

Disregarding all restricting words the check will be done as passed, if any of variables ANYARCH, ANYARCHMODE, ANYOS contain the value all. That value does not bypass masking datawords, if they are given.

Flavours

With datawords and representing AUSE variable, build flags and representing USE variable, each package may be built in many variations without changes in build script. To reflect that variations in the package, variable FLAVOUR is used.
    FLAVOUR="planb"
When FLAVOUR is set, its contents is added to the name of resulting binary package. It can be added to package meta-information as well, but not to the essential package name, so two builds with different flavours remain the same for package manager (at least for the one without direct support of flavour choice). Details of the handling remain in module with support of package format.

Consider the package package-1.2.3 and its default installable archive package-1.2.3-amd64-1.txz. If the builder has decided to apply plan B for it as in example above, according archive will be named package-planb-1.2.3-amd64-1.txz.

While AUSE and USE are options for package builder, FLAVOUR is represented at install time to the package consumer. FLAVOUR allows to create reasonably limited quantity of variants from all possible combinations of datawords and build flags.

    if ause 'unusual' || ause 'strange' || ause 'panic' ; then
        FLAVOUR="planb"
    fi
    if use 'ssl' && ause 'hardened' ; then
        FLAVOUR="hardened"
    fi

When flavour describe several independent properties, it may include more of the one word:

    if ause 'unusual' || ause 'strange' || ause 'panic' ; then
        FLAVOUR="planb"
    fi
    if use 'ssl' && ause 'hardened' ; then
        separator=
        if test -n "${FLAVOUR}" ; then
            separator='-'
        fi
        FLAVOUR="${FLAVOUR}${separator}hardened"
    fi
Additional check on non-empty FLAVOUR in the example above is used to put separator between two words only when the previous word actually exists.

Packages with different flavours are normally considered as mutually exclusive, and cannot be installed at the same time. When it is needed to create several subpackages from single build, able to work all at once with each other, BPN should be used.

Altered build files

Some conditions and configurations may require such behaviour from the build, which is considerably different or totally altered from default implementation. That behaviour can be inserted into main build as conditional code. When such additional code is well-known and used relatively often, it is the preferred variant. But when such code feebly concerns the main code, handles just specific case, bloats the main build and brings obscurity instead of commonality, it is better to place it to the separate file. Modular code loading implements that.

When dataword word is loaded and is present in AUSE, shell file word.sh is checked in DISTDIR directory (the main build lies there as well). If DISTDIR/word.sh is present, it is loaded as additional library after engine-level libraries. That allows to keep there arbitrary code, which will be in use only for word and only for current package (DISTDIR contains P).

Consider the simplified build for a package package-1.2.3:

#!/bin/sh

DEPEND="
>zlib-1.2
>=gmp-4.3.2
>gtk-2
>pixman-0.30
>cairo-1.14
>libX11-1.6
"
RDEPEND="
${DEPEND}
>font-ttf-1.0
>python-3.4
"

src_config()
{
    aconf \
        --enable-shared \
        --enable-utf \
        --enable-gui
}

src_compile()
{
    amake
}

src_install()
{
    amake install DESTDIR=$D
}
The package uses graphical interface and thus needs stack of graphical libraries to build it. It requires additional components at run-time for windows (fonts) and for python bindings. The minimal build of the same package exists. It includes reduced set of dependencies and leaves out the graphics. Also it must be built statically. Together with existing build it may look like:
#!/bin/sh

DEPEND="
>zlib-1.2
>=gmp-4.3.2
"
if ! ause 'minimal' ; then
    DEPEND="
${DEPEND}
>gtk-2
>pixman-0.30
>cairo-1.14
>libX11-1.6
"
fi
RDEPEND="
${DEPEND}
$(ause_notenable 'minimal' '>font-ttf-1.0')
$(ause_notenable 'minimal' '>python-3.4')
"
if use 'minimal' ; then
    FLAVOUR="min"
fi

src_config()
{
    if use 'minimal' ; then
        LDFLAGS="${LDFLAGS} -static"
    fi
    aconf \
        $(ause_enable 'minimal' '--enable-static') \
        $(ause_notenable 'minimal' '--enable-shared') \
        --enable-utf \
        $(ause_enable 'minimal' '--disable-python') \
        $(ause_notenable 'minimal' '--enable-gui')
}

src_compile()
{
    amake
}

src_install()
{
    amake install DESTDIR=$D
}
Although build above completely handles both variants of the package, it became messy and poorly readable in comparance with the original one. When package is used as the main one at majority of cases, maintainers will need each time to read set of unused conditions.
Now everything, concerning the minimal build, is placed to separate file DISTDIR/minimal.sh. The total code will be as follows:

DISTDIR/package-1.2.3.build

#!/bin/sh

DEPEND="
>zlib-1.2
>=gmp-4.3.2
>gtk-2
>pixman-0.30
>cairo-1.14
>libX11-1.6
"
RDEPEND="
${DEPEND}
>font-ttf-1.0
>python-3.4
"

src_config()
{
    aconf \
        --enable-shared \
        --enable-utf \
        --enable-gui
}

src_compile()
{
    amake
}

src_install()
{
    amake install DESTDIR=$D
}

DISTDIR/minimal.sh

#!/bin/sh

DEPEND="
>zlib-1.2
>=gmp-4.3.2
"

FLAVOUR="min"

src_config()
{
    LDFLAGS="${LDFLAGS} -static"
    aconf \
        --enable-static \
        --enable-utf \
        --disable-python
}
Now both variants are neat and readable. Note the absence of src_compile and src_install in minimal variant: as these methods are unchanged, no need to redefine them in alternate file.

Keeping two and more variants of code in separate files has its cost. Once the build owns several separate alternatives, it is needed to keep them all in sync. After each change of main build the maintainer needs to bring it to all alternatives as well, or make sure the change in additional files is not needed. The matters become more tough, when several people watch out the changes of the build, and each of them rechecks the diff: is it uncommited to alternative file on purpose or simply forgotten?

Alternative files are recommended for significant deviations from the main build. When some dataword changes just several options and variables, it is much better handled with ause interfaces or plain if ; else code.

Package maintenance

Besides anybuild, several other things may be needed to get the package working properly:
-
scripts for package manager, such as postinstall
-
meta-information for package manager, such as description
-
etalon data for sanity and regression checkers
-
meta-information for any other utils, such as tracking of vulnerabilites
Format of such data depends on used package manager, checkers and quality assurance tools. anymods(7) lists available modules with support of that areas. Needed module with lacking functions may also be created by a user himself.

See also

any(1), any-api(7), any-map(7), anymods(7)