any-build – guide for writing build, file with shell script to build the package.
Description
To create anybuild for package hello-world-1.0,
one should create directory
ports/packages/hello-world-1.0/
and file
ports/packages/hello-world-1.0/hello-world.build.
There one should put methods and variables, describing the build process.
The typical stuff to get package buildable looks like this:
- -
- 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 illustration of simple 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
out of all methods, usually at the beginning of a script for better readability.
In that case they will be correctly initialised for all that methods.
- -
- 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.
Given methods perform very closely 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 global 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 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. Installation to empty separated directory allows to create the archive later only with files, belonging to that package.
Put together in single file, methods above provide spectacular picture of package building.
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 package name with range of allowed 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. Each version matches this condition, 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.
- Important rule:
- Global variables and methods are allowed in anybuilds only if they are described in any-api(7) or in documented extensions of that API. There are no limitations on usage of local variables and methods, declared in anybuild itself.
Patches
These directories are used to keep patches:
- ports/packages/package-name-1.2.3/patch/
- ports/patch/package-name-1.2.3/
During building inside method src_fetch(3), patches are copied into build/patch/package-name-1.2.3/, and during patch applying inside src_prepare(3) files are taken from that directory. So disregarding the found location of patches, variable PATCHDIR keeps the same value and contains build/patch/.
The example of patch set for package package-1.2.3:
- 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
By default with PATCHLIST unset, all patches are applied directly from PATCHDIR, but not from nested subdirs.
Naming and version
Anybuild name is detected in following variants:
- ports/packages/hello-world-1.0/hello-world.build
- ports/packages/hello-world-1.0/hello-world-1.0.build
Packages hello-world-1.1 and hello-world-1.2 are fully independent, they are 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 from ports/list/, for example, ports/list/distro.src. Thus user is not forced to choose which version of the two neighbour packages to keep in his project: he may keep both at the same time and use any of them.
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.
Typical interfaces
The engine has interfaces-wrappers over typical build tools like configure and make. They aim additional set of functions with centralised control during the configuration and compiling. Meanwhile these functions are handled inside interface, not cluttering anybuild.
The example of anybuild with typical 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="${CONFIG_HOST}" \ --host="${CONFIG_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 options hidden, build files stay clear and readable, while powerful config set is transmitted to configure. 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 typical 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 user-defined 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 5The 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.
The alternative way for flexible settings:
src_config() { eval \ ./configure \ $(configdefault) \ --enable-shared \ --enable-static \ --enable-utf }
Interface configdefault is used instead of wrapper function. It prints typical options for configure. The effect is similar to aconf, but in this case the call of configure is seen plainly. That may have advantages from the point of more simple perception.
eval construction is needed to handle the spaces correctly in output of configdefault.
Compilation paths and flags
The engine sets up several essential variables with paths, programs and resources for actual compilation, linkage and installation of binaries.
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 just 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 for installation of the ready binaries.
It is used for make install
call, as has been shown above.
After src_install(3)
prepared 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() { make 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 during the build process. The variable is not actual for plaincc mode, as it provides all files from root, exactly as with manual build. The variable is needed for the work with dedicated file system, through which build dependencies are controlled. ROOT serves as the root of such filesystem.
To show the location of include files in subdirectory:
src_config() { CFLAGS="${CFLAGS} -I${ROOT}/usr/include/subdir" \ ./configure }Note the preserving of the previous value in assignment of CFLAGS. The construction helps to add correctly new values to the variable several times.
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" \ ./configure }
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 MakefileIn 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 placed in the build list.
PN
contains single name of a package, PV
only package version.
Conditional code
Group of ause* methods provide powerful tool to create flexible builds. Their behaviour is based on AUSE variable, which contains flags of current configuration (data words), and thus the variable is the fingerprint of global properties in your distro. Shell code with ause* 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 the property, reflected in configuration:
if ause legacy ; then # perform nice support for outdated trash fiHere we check the property legacy as condition and perform something nice for it.
if ! ause sparc ; then # no big endian support here fiHere we check the absence of sparc in configuration, as we do something inside the block, which notoriously does not support that architecture.
Method ause_enable word string prints the string, if property of word is present in configuration. It is used to embed conditional text:
./configure \ --generic-option \ $( ause_enable 'sparc' '--endian=big' )
When sparc is present in AUSE, the code above will be expanded into:
./configure \ --generic-option \ --endian=bigWhen sparc is absent, resulting code will be:
./configure \ --generic-option \
The method helps to write large strings without duplication, such as calls of configure.
ause_notenable does the same as ause_enable but with reversed logic: print out the string, if there is no property; print nothing, if property is present.
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 utilizes.
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 any system. 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 exploited by build system 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 the dependencies at build-time and run-time 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 material. For example, it can be packages with static libraries, needed at compiling, but not used during execution.
- Run-time requires more then build-time. It's frequent case for modules of perl(1) or python(1), which are simply installed at build stage, requiring nothing; but these modules need lots of another modules in the system 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 system 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 package manager:
DEPEND=" >zlib-1.2 >=gmp-4.3.2 " RDEPEND=" >>zlib1g-1.2 >=libgmp10-4.3.2 >=libgmp3-dev-4.3.2 "
Binary interface tracking
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, the 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 left as is, and limitation from low border of the packages, participated in current build, is not applied.
Additional limitation on range of dependencies is applied for binary compatibility control. If a package packA is built against package packB-1.2.3, at run-time in pessimistic case it will require package packB with version not lower then 1.2.3. In spite of theoretical ability of packA to work together with packB with lower versions, its binary interface in pessimistic case is set up strictly to packB-1.2.3, as it was built with that.
For distinct control over the version of binary interface independently from package version the BPI exists.
Take a look at example with packages packlib-1.2.3.4 and app.
- In simple case app, built against packlib-1.2.3.4, will have the run-time dependency >=packlib-1.2.3.4.
- If one sets up BPI=1.1 in the anybuild of the package packlib-1.2.3.4 and builds app against it, the run-time dependency in app will be >=packlib-1.1. That means packages packlib-1.1, packlib-1.2, packlib-1.2.3 are binary compatible between each other, and app must work with any of them, although it has been actually built against packlib-1.2.3.4.
There is mode for automatic setting of BPI: autobpi. It works by following principle: first two numbers inside version set up compatibility, all following numbers in version represent fixes, keeping compatibility. For version 1.2.3.4 from the example above the named numbers are 1.2.
To turn on autobpi, it is needed to set up option any_bool_autobpi=1 in config file rdd.def or directory rdd.conf.d/. This mode still allows to set up BPI explicitly into arbitrary value.
- The package app, built against packlib-1.2.3.4 with working autobpi, will have run-time dependency >=packlib-1.2.
- If one sets up BPI=1.1 in anybuild of the package packlib-1.2.3.4 and builds app against it with working autobpi, the run-time dependency in app will be >=packlib-1.1.
Host dependencies
One more type of dependencies is subset of build-time ones. The packages of this category must be installed into host environment for successfull build of a current package. The entries of this type are kept in HDEPEND variable.
Build-time dependencies may provide not only includes, libraries and another readable files, which is possible to reflect inside ROOT. Dependencies may be required for executable files. They are build tools like make(1) and its numerous analogues (cmake, qmake), documentation generators, parsers of different data formats. Such applications could not be reflected into separate directory, like it happens for packages from DEPEND. They need proper installation into main filesystem.
Cross-compilation enlarges the list of host dependencies. If some binary file is generated during the build and then process tries to execute it, the scheme cannot be done because of different target architecture of the 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. The packages reside in IMGDIR and provide their files through ROOT.
Also there is the need in programs of packages flex and bison, which should be runnable at build time and residing in host environment at standard paths like /usr/bin/.
A package can compile its own files to launch them later on further stages of build. That means for cross-compilation the neccessity to create such file additionally, so it would work in host envirionment. When such binary is simple, it can be compiled twice just during general package building without exceptional troubles: one time for target platform, one time for host. But when binary is complex, its build requires additional libraries as well, it is needed to compile the whole package separately and install it into host. The case can be expressed like this:
HDEPEND=" $(ause_notenable 'host' "${P}") "The example above declares the dependency on the same package in the host, excluding the case of building for host itself. So while building for host the package will not depend on itself. It is assumed, that build for host is defined by dataword host.
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 in this mode.
More methods in anybuild
In typical and easy case the methods src_config(3),
src_compile(3),
src_install(3)
are perfectly enough to define the build process.
All the routine is done by the rest of any-map(7)
methods, sitting in the engine.
No need to change them neither to reflect them in the anybuild.
To see all methods doing the complete build sequence, one can view the map
head method inside
any/corelib/map.sh.
The scheme runs well when hidden methods do work as wanted. When they don't, a user is forced to add his code for them somehow. For example, the package must provide additional directory for other packages, and that directory is not caught by generic pkg_root(3).
One may write his own pkg_root. In general, anybuild may contain redefenitions of any methods from any-map(7). The problem is methods like pkg_root already have some functional, nontrivial the most often. If a user creates his own implementation, he will need to repeat all that functions in his build. And the duplication is needed in each build with non-standard map function.
The pre* and post* methods solve that task. They allow to add more actions to standard functions without replacing them.
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 functions. The summary effect is the execution of anybuild-defined code and untouched 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() { eval \ ./configure \ $(configdefault) \ --enable-shared \ --enable-static \ --enable-utf } src_compile() { make } src_install() { make 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 locally declared methods: pkg_postroot and pkg_prerminstall. pkg_postroot handles the need in 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
Names change
The cases arise when name of package sources from its authors completely differs from a name inside your project. The bright example is qt-everywhere-opensource-src name for simple qt5. The variable MY_P exists to keep access to sources from authors. 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 ones for local development. To change the name of repository with development sources for the package the variable MY_PD exists. Its usage is the same as 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 build system always works with the package by the list name.
Search for sources in several places
The need may arise to access the sources from non-standard place. For example, that can be third-party repository out of infrastructure of your project. The simple case is done with access from postfetch:
src_postfetch() { search_sources_somewhere_else }
The situation hardens, if it is needed to extract sources from both standard location (general server or directory) and some side place. 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 configure is absent, we call some additional code to search for sources in altered ways.
Patchworks
The essential action at source preparation stage is patches applying. By default, all files from PATCHDIR directory are applied, but not from its subdirectories. 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.
Default patch handling does not apply materials from nested subdirectories. That allows to keep there specialized patches, which are needed not always, but in particular cases. To check that cases conditional code can be used.
Examine extended example, listed above:
- ports/patch/package-1.2.3/00-essential.patch
- ports/patch/package-1.2.3/01-path-change.patch
- ports/patch/package-1.2.3/04-memory-leak-fix.patch
- ports/patch/package-1.2.3/special/00-hardening-checks.patch
src_postprepare() { if ause 'hardened' ; then dopatch special/00-hardening-checks.patch fi }In the above example we add code into procedure src_prepare(3) after its primary content. Additional patch is applied manually by condition hardened from nested subdir special/.
Interface dopatch serves for manual patch applying in anybuilds. If the path to a file is relative, interface will try to search for it in several places, including PATCHDIR. If the path is absolute, interface checks only that. See description of dopatch in any-api(7) for details.
dopatch can handle entire directories:
src_postprepare() { if ause 'hardened' ; then dopatch special/ fi }In the example above all patches from directory special/ will be applied. The order is the same as for generic patch scheme: sorted by alphabet.
Cache for configure
CONFIGCACHE variable contains file with cache for configure script. Its main use is cross-compilation, when the values for configure cannot be defined directly. The file is not created by default.
The subconfig module contains methods to create the CONFIGCACHE file and install it into 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. Depending on package format the fields may vary in form and order.
To alter binary package name, BPN is used:
BPN=${PN}-binExample 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) has not been changed: D, S and all of the rest essential variables still contain the old P.
To alter binary package version, BPV is used:
BPV=1.0.0Example above sets hardcode run-time version 1.0.0 for an executable 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. Along with that the search for files for these packages will use template:
- IMGDIR/packname/
BPN="e2fsprogs-bin e2fsprogs-lib e2fsprogs-doc" PV="1.44.1"the directories will be required inside IMGDIR:
- IMGDIR/e2fsprogs-bin-1.44.1/.
- IMGDIR/e2fsprogs-lib-1.44.1/.
- IMGDIR/e2fsprogs-doc-1.44.1/.
The dividing of files to subpackages from single D is performed in method image_split_pack(3), which is called inside pkg_config(3). The engine without additional modules does not implement it.
To produce several binary packages with different versions, BP can be used. It contains names of subpackages together with versions.
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 as well to the main one, contained in BP.
Entries inside BP must follow naming conventions of packages inside any(1): name and version delimited by simple dash - symbol. If the package manager needs another naming scheme, the name will be translated during generation of package control information.
Inner versions
Inner version contains additional information according to a package in context of the project. That could be fixes from distro or building context.
Inner version is defined inside any in following ways:
- explicitly with variables inside anybuild;
- inner version can be saved inside file. The following files are checked in priority order:
- MINTNEUTRALDIR/inner_version
- DISTDIR/inner_version (directory with a build);
- T/inner_version
- inner version is calculated dynamically, if some module is involved for that. For example, the module fetchdevgit(7) defines inner version by the quantity of commits in git history for materials, concerning a package.
The inner version must be integer positive number.
To change the inner version explicitly, set up INNER_VERSION.
INNER_VERSION="1"The example above turns off the dynamic detection of inner version and assigns it statically with 1.
To turn off the usage of inner version, write space into INNER_VERSION:
INNER_VERSION=" "Mind that writing empty value into INNER_VERSION is not enogh, as the variable is empty by default. With empty INNER_VERSION the inner version will still be defined dynamically, basing on a file or calculations inside module.
It is possible to assign prefix to inner version without changing its essential number value. That is done to reflect the building context of a package.
For example, the same package gzip-3.4 with 8 fixes is built twice: inside context of distro with version 2 and version 3. It has the same quantity of inner fixes (8 in the example). But binary packages from different contexts vary. To show that difference the global identificator is used:
RELEASE="r2."After definition above the full inner version becomes r2.8, and binary package becomes gzip-3.4-r2.8. For the build inside global version 3:
RELEASE="r3."According inner version becomes r3.8, and complete binary package gets gzip-3.4-r3.8.
It is possible also to set up the minimal value for inner version, which is calculated automatically. In that case the minimal version would be not one, but given value. Along with that the version will still be calculated, and not statically initialized. Assign the option any_base_inner_version for that in configuration file (not in a build).
[id] any_base_inner_version = 3
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 function of rdd(1).
If AUSE contains some word, and KEYWORDS contains the entry ~word, further 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 other cases.
When restrictive words (without ~) are given, the following variables are checked besides AUSE: ANYARCH, ANYARCHMODE, ANYOS, ANYARCHCAP, ANYARCHENDIAN. If one of them contains value from KEYWORDS, the build is permitted. Masking words (with ~ ) are checked only inside 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 it 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 words can be inside AUSE or additional variables, ANYOS for this example.
There is condition for successfull check with KEYWORDS in despite of restrictive words: if any of variables ANYARCH, ANYARCHMODE, ANYOS contains the value all. That is the reserved word to mark architecture-independent mode, which is supposed to be universal. The example is work with sources.
If KEYWORDS contains masking words, the word all does not concern them, and the check still is active.
Flavours
In consideration of multiple global configuration (reflected in AUSE) and multiple functional flags (reflected in USE), the package may be built with lots of alternatives. To connect these alternatives with binary package, variable FLAVOUR is used.
FLAVOUR="planb"
When FLAVOUR is set, its contents is added to the name of binary file and to package meta-information. But the essential package name is not modified, and two builds with different flavours remain the same package from the point of view of package manager. If package manager supports flavours, it will explicitly use this possibility. Details of the handling depend on a module with package format.
Consider the package package-1.2.3 and its default installable archive package-1.2.3-amd64-1.txz. If the user 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 input data at build stage, FLAVOUR is represented at installation to the user at run-time stage. 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 a flavour describes several independent properties, it may include more then one word:
if ause 'unusual' || ause 'strange' || ause 'panic' ; then FLAVOUR="planb" fi if use 'ssl' && ause 'hardened' ; then FLAVOUR="${FLAVOUR} hardened" fi
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 a single build, which are able to work together with each other, BPN should be used.
Altered build files
Some conditions in configuration may require such behaviour from the build, which is considerably different or totally altered from default implementation.
Additional behaviour can be inserted into main build as conditional code. When such conditional code is well-known and used relatively often, it is the preferred variant. But when additional code feebly concerns the main one, handles very specific cases, bloats the main build and brings obscurity instead of commonality, it is better to place the code to the separate file. The scheme is implemented with modular code loading.
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 libraries from engine. That allows to keep there arbitrary code, which will be in use only for word and only for current package (as DISTDIR is separate directory for each package 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() { eval \ ./configure \ $(configdefault) \ --enable-shared \ --enable-utf \ --enable-gui } src_compile() { make } src_install() { make 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 eval \ ./configure \ $(configdefault) \ $(ause_enable 'minimal' '--enable-static') \ $(ause_notenable 'minimal' '--enable-shared') \ --enable-utf \ $(ause_enable 'minimal' '--disable-python') \ $(ause_notenable 'minimal' '--enable-gui') } src_compile() { make } src_install() { make install DESTDIR=$D }Although the build above completely handles both variants, it has become messy and poorly readable in comparance with the original one. The package is used in its main variant at the majority of cases, but maintainers each time will have to read and investigate the set of rarely used conditions.
Now everything, concerning the minimal build, is placed to separate file
- DISTDIR/minimal.sh.
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() { eval \ ./configure \ $(configdefault) \ --enable-shared \ --enable-utf \ --enable-gui } src_compile() { make } src_install() { make install DESTDIR=$D }
DISTDIR/minimal.sh
#!/bin/sh DEPEND=" >zlib-1.2 >=gmp-4.3.2 " FLAVOUR="min" src_config() { eval \ LDFLAGS="${LDFLAGS} -static" \ ./configure \ $(configdefault) \ --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: they are already declared in main file, and no need to repeat them in the alternate one.
Maintenance of two and more variants of code in separate files has its cost.
Since the build is splitted into 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 to make sure the change in additional files is not needed.
The matters become more tough, when several people watch out the changes in the build, and each of them
rechecks the diff: was it uncommited to the alternative file on purpose or was simply forgotten?
Alternative files are recommended for the code, which deviates significantly from the main build. When some dataword changes just several options or 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 functions in that areas. Needed module may also be created by a user himself.