Thu Apr 13 11:25:42 2023 UTC

How to build a first package

Why should you contribute?

Today NuTyX has about 1,700 applications available in binary form. In one sense, that's a lot. But it really isn't when you consider that new programs are released on the net every day, many under open source licences.

It's clear that you will never see all these programs ready for installation on NuTyX. Just using the github web site, we can find more than 52,000,000 repositories for all platforms.

If you find the application of your dreams, you will have to find all the dependencies, compile it yourself, install it and make it work on NuTyX. That is why we are asking you to contribute a little bit. It is you who will build the package... if you want to of course.

Building an application is often something like:

  • Find an application not available in NuTyX.
  • Download the source code of the application.
  • Check the MD5SUM (strongly recommended but not always possible).
  • Extract sources into a working directory.
  • Enter this working directory.
  • Check that all the dependencies of the package are installed in NuTyX (otherwise the build will not succeed).
  • Type the three magic commands (in most cases; there are exceptions):
./configure --prefix=/usr
make
make install

The "make install" step must be carried out as root.

That's it! Your new program is built and installed on your NuTyX system.

That's one way to do it... but as you can see, there are a few disadvantages:

  • You do not know what files have been installed.
  • You cannot easily get information about the installed package such as its name, version, description, etc...
  • You cannot share the application with another NuTyX installation.
  • This method is tedious because it needs to be repeated whenever there is an update available.
  • Your NuTyX system is no longer really clean, since you have added files that are not tracked by the package manager.

NuTyX has nice tools to help you with the integration of new programs. You will be then able to track them and maintain them. The package manager will treat them like any other NuTyX packages.

I suggest you to have a look at the FAQ to get an idea about the compilation modus under NuTyX.

What do you need to know to build a package?

Introduction

You don't need to be an experienced programmer to contribute to this project. A rudimentary knowledge of the bash scripting language is good enough. If you have no knowledge of bash, this article will explain the basic principles for creating a recipe without going crazy.

The bash langage is used to edit a recipe and to generate a package from it. The fact that bash is a programming language might seem frightening to some potential users, who might think: "I don't know how to program". There are three reasons why you should not feel scared:

  • Bash is an interpreted language, not a compiled one, which means that the commands you type in at the keyboard (or read in from a file) are directly executed without the need to construct a complete program.
  • The syntax of bash is very simple and intuitive.
  • Finally the recipe and the creation of the binary package from it seldom use the more complex options of the bash language.

So let's start Wink our little course on bash programming.

Declaring a variable

All you need to do to declare a variable is this:

name=mypackage

You declare the variable name by using the equals sign, with the name of the variable to the left of the sign and the value mypackage to the right.

Using a variable

So you know how to declare a variable; that's very nice, but how do you get at its content in order to use it?

To extract the content of a variable you use the dollar sign ($) with brackets if required. Both forms are valid:

${name}
$name

It is better to use the first form, otherwise if the variable name contains an underscore, it will not be interpreted correctly by bash:

name=xorg-libx11
_name=libX11
$name # this works ${_name} # so does this $_name # wrongly interpreted and won't work

In order to see the result of this command, I suggest you create a little file containing this code. You can call the file DeclareVar:

name=kernel
echo "The variable name contains the value: ${name}"

Now give the command:

sh DeclareVar
The variable name contains the value: kernel

Variables manipulations

In a recipe, it's often need to create/modify a variable by using an existing one.

The following examples are taken directly from existing recipes.

  • We want to keep the left part of the variable before a given character here '-'
    name=qt6-tools
    basename=${name%-*}
        
    The basename variable contains "qt6"

  • We want to keep the right part of the variable after a character given here '-'
    name=qt6-tools
    basename=${name##*-}
        
    The basename variable contains "tools"

  • We want to keep the first two letters of the variable
    name=qt6-tools
    basename=${name:0:2}
        
    The basename variable contains "qt"

  • We want to delete part of the variable, here the string '6-'
    name=qt6-tools
    basename=${name/6-/}
        
    The basename variable contains "qttools"

Using predefined variables in a recipe

When you construct a recipe, you will have access to a whole series of predefined variables.

PKG: Certainly the most often used, this defines the destination folder for the compiled files. This folder then contains the file structure before it is packaged up
SRC: This variable defines the folder where all the source files are extracted. It is therefore very useful when you need to loop through subfolders and then return to the end of the loop to where you started. This is also the variable which defines the current working directory for the main function of the recipe.
MAKEFLAGS: This variable is not used so often. It is initialized in the pkgmk configuration file. Its use is detailed in another article. For the time being, all you need to know is that it allows you to specify the extent of parallel compilation.
There are others, but personally I see no need for them when constructing recipes.

Declaring an array

To declare an array of strings (or anything else), you add parentheses:

source=(firefox.png firefox.desktop)

This array source contains 2 variables: firefox.png and firefox.desktop. The source array from the recipe for the kernel provides a particularly good example:

source=(http://www.kernel.org/pub/linux/kernel/v3.0/linux-${_BaseVersion}.tar.bz2 \
	 config_64 config \
	 http://www.kernel.org/pub/linux/kernel/v3.0/patch-$version.bz2 \
	 http://downloads.nutyx.org/files/patchs/$name/broadcom-wl-kernel3-01.patch \
	 http://downloads.nutyx.org/files/patchs/$name/3.2.8.patch \
	 http://downloads.nutyx.org/files/patchs/$name/3.4.patch \
	 http://downloads.nutyx.org/files/patchs/$name/makefile_compat.patch \
	 http://downloads.nutyx.org/files/patchs/$name/bc_wl_abiupdate.patch)

Each line is a variable in its own right and can be accessed using the syntax:

echo "$(source[1])"

You will notice that each variable can in turn contain other variables provided that they have been defined earlier.

Declaring a function

To declare a function is almost as simple. You use parentheses when giving the name of the function; the function code is always enclosed in brackets { etc }. A little example:

build() {
cd $name-$version
./configure --prefix=/usr
make
make DESTDIR=$PKG install
}

As you can see, it's very pretty and also contains a minimal recipe (without the variable declarations). Here you can see the essential use of the PKG variable.

What's a port?

Introduction

A port is a directory containing the files needed for building a package. The command pkgmk will be used to "build" the package, which can be installed afterwards.

This means that this directory must contain as a minimum a file named Pkgfile. The building of the package will be controlled by the contents of this file. It contains all the information necessary to compile and install the package from source.

The name port comes from the BSD world where a port refers to software "ported" to another operating system or platform. In our case, the port is simply the definitions for building a package.

Minimal syntax of a port

The minimum information required for producing a binary are:

  • name: the name of the build package.
  • version: the version of the sources of the package.
  • release: the nth attempt at building the package. Its value will always start at 1
  • source: the address of the sources of the package we want to build.
  • build(): the function which contains the recipe.

A little example with comments:

# Name of the package
name=gnome-terminal

# Version of the package
version=2.6.4

# Release of the package
release=1

# Location of the sources of the package to be built
source=(http://ftp.gnome.org/pub/gnome/sources/$name/${version:0.3}/$name-$version.tar.xz)

# Recipe for building the package (often uses three standard commands)
build() {
cd $name-$version
./configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info
make
make DESTDIR=$PKG install
}

If you compare this example with an official NuTyX port, you can see that the official one contains more information. We will see this in the next paragraph.

Practical example of a port

The libxfce4util/Pkgfile file

description="libxfce4util, base library for Xfce"
url="http://docs.xfce.org/"

maintainer="git.xfce.org/xfce/xfce4-libutils"
packager="tnut "

makedepends=(glib intltool)

name=libxfce4util
version=4.8.2
release=1

source=(http://archive.xfce.org/src/xfce/${name}/${version:0.3}/$name-$version.tar.xz)

build() {

  cd ${name}-$version

  ./configure --prefix=/usr \
           --disable-static

  make

  make DESTDIR=$PKG install
}

If you want to make sure that the build bot is able to compile the package without any problems, it's important to specify all the dependencies needed for the compilation of the package. Dependencies starting from the NuTyX base are always included. Indeed the bot will always build the package by starting from the NuTyX base.

The line makedepends=(...) is used for this purpose. It defines all the needed dependencies for the compilation of the package. Dependencies are separated a spaceLaughing.

Build your first package

[Important] For this package, we won't use an isolated environment named chroot. We will see in another tutorial how to build in isolated environments: It's the only working method if we need to build a package having dependencies.

We have provided the required Know-how, the minimum syntax of instructions and a list of available packages for you to choose from.

Come and join the many participants of all ages and gender, from all over the world, and venture along this exciting road we call NuTyX. We can promise you a rewarding experience as you build your very first NuTyX system, following our (NuTyX) rules Laughing.

We will show you how to build and install a first package in a very straightforward way.

This tutorial explains to you in a very simple way the construction of your first binary package.

They are no prerequisites. This exercise can be done regardless of the state of your NuTyX.

The chosen package is a little text editor which doesn't need any dependencies.

A few explainations

All the available packages under NuTyX use a recipe.

This recipe (Pkgfile) is located in a folder call as the package name which is stored in a collection.

By default, all the collections are under a folder you choose.

The recipe

We create first the folder: the "perso" collection and the folder of the package.

sudo mkdir -p /usr/ports/perso/nano
echo '
description="Text Editor"
maintainer="Nano team"
packager="François Perrin francois dot perrin at myworld dot com"
url="http://www.nano-editor.org/"

name=nano
version=2.7.2
release=1

source=(${url}/dist/v${version:0:3}/$name-$version.tar.xz)

build() {
cd $name-$version
./configure --prefix=/usr \
--sysconfdir=/etc \
--enable-utf8 \
--enable-color \
--enable-multibuffer \
--enable-nanorc \
--docdir=/usr/share/doc/$name-$version
make
make DESTDIR=$PKG install
rm $PKG/usr/share/info/dir
}' > /usr/ports/perso/nano/Pkgfile

Install the development packages

get cards.devel

Compile the package

cd /usr/ports/perso/nano
sudo pkgmk -d

Install the package

sudo pkgadd nano1*

The configuration files

The /etc/cards.conf file

This file is always present on a NuTyX installation.

It's a bit like "the spine" of your NuTyX.

The /etc/cards.conf file will reflect the way you use NuTyX.

All the collections that you want to use in your NuTyX can be specified in two ways:

  1. configuration example of cli collection in binaries from a remote server
    dir /var/lib/pkg/depot/cli|http://downloads.nutyx.org
  2. configuration example of cli in local ports
    dir /usr/ports/cli

This file is also responsible for the list of packages you want to keep when you execute the command:

cards base -r
And finally, it specifies which location should be installed.

You can find more information by reading the cards.conf manual

man cards.conf

The /etc/pkgmk.conf file

This file is used when compiling news ports. It specifies all the variables used by the pkgmk command.

Please take the time to read the main page of pkgmk.conf

man pkgmk.conf
Example of /etc/pkgmk.conf file
export CFLAGS="-O2 -pipe"
export CXXFLAGS="${CFLAGS}"

case ${PKGMK_ARCH} in
        "x86_64"|"")
		export MAKEFLAGS="-j$(getconf _NPROCESSORS_ONLN)"
                ;;
        "i686")
                export CFLAGS="${CFLAGS} -m32"
                export CXXFLAGS="${CXXFLAGS} -m32"
                export LDFLAGS="${LDFLAGS} -m32"
                ;;
        *)
                echo "Unknown architecture selected! Exiting."
                exit 1
                ;;
esac
# Those settings are typically set for packaging
# without sub packages and only French locations
# They are also the default ones
#
# PKGMK_GROUPS=()
# PKGMK_LOCALES=(fr)
# PKGMK_COMPRESS_PACKAGE="no"
# PKGMK_CLEAN="yes"
# PKGMK_IGNORE_FOOTPRINT="yes"
# PKGMK_KEEP_SOURCES="no"
# PKGMK_SOURCE_DIR="$PWD"
# PKGMK_WORK_DIR="$PWD/work"
# PKGMK_IGNORE_REPO="yes"
# PKGMK_IGNORE_COLLECTION="yes"
# PKGMK_IGNORE_RUNTIMEDEPS="yes"

Conclusion

We are now ready to build our packages. Lets see now what's a collection.