Porting Guide
=============

This guide documents how to port software to the Sortix operating system and how
to release your port to the Sortix community. Before you get started, you should
get the system source code and the current core set of existing ports and be
comfortable with building the system (with ports) entirely from scratch.

Overview
--------

### `$SORTIX_TOPLEVEL` ###

This is the top-level source directory containing the system source code. This
is actually not a real environmental variable, but just something used in this
document to denote the top-level system source code directory. This is where you
have cloned the Sortix source repository.

### Source Tix ###

A source tix is simply a directory, whose name is the name of a package, and
which contains the source code for the package and a special `tixbuildinfo`
file, which contains instructions for building the source tix into a binary tix.

### `$SORTIX_PORTS_DIR` (default `$SORTIX_TOPLEVEL/ports`) ###

This directory is where the build system will search for source tixes that
will automatically be built along with the rest of the system. Any other files
and sub-directories will be ignored. You can use symbolic links to source tixes
if you wish. You integrate new packages into the build simply by simply putting
the source tix inside this directory as a sub-directory. It will automatically
be built along with the rest of the system during the next system build.

### `.srctix.tar.xz` (Archived Source Tix) ###

Since you cannot publish raw directories, published source tixes are put into
a compressed archive and given the extension `.srctix.tar.xz`. You can easily
install an archived source tix by extracting it using standard tools:

    cd $SORTIX_PORTS_DIR &&
    tar --extract --file libfoo.srctix.tar.xz

This will install the source tix into the source repository and it will
automatically be built the next time you compile the system and ports.

The archived source tix is simply a tarball that contains a single directory,
which is a source tix as described above.

### `.porttix.tar.xz` (Archived Port Tix) ###

Upstream releases of software cannot be expected to contain a `tixbuildinfo`
file and sometimes they need to be patched. When maintaining a port, it is often
useful to have a copy of the upstream release and the patches applied to it.
The `.porttix.tar.xz` archives contain a copy of the upstream release (as a
compressed archive) and all the patches applied to it. This can automatically be
converted into an archived source tix using the `srctix-create` program. Users
will normally not use this format unless they wish to inspect how a package was
ported or confirm that no malicious alternations were made to the upstream
release.

Can the package be ported?
--------------------------

It's a very good idea to examine the package closely before attempting to port
it, or you might end up wasting a lot of time on something that isn't even
possible in the first place. There's a number of red flags that can prevent the
successful porting of a package, at least not without enhancements to the tix
package management system or to Sortix itself.

The first thing to verify is whether we want such a package on Sortix. Not all
packages are good, some have bad licenses, some have security problems, or bad
code quality -- and so on. Perhaps there is a similar package already ported,
which is technically superior, and it's better to have just one such package on
the system for the sake of purity. Perhaps it's just better to leave this
package behind and focus on the pure Sortix future? Does the philosophy of the
package developers contradict that of the Sortix project?

The second thing to verify is the build system. If it uses GNU autoconf or a
compatible ./configure script things are looking good. If the package has a
custom hand-written ./configure script you will likely have trouble, as the
authors of these tend to be ignorant about many useful ./configure options and
likely don't properly support-cross-compilation. If the package uses some exotic
build system, you might need to write wrapper scripts or teach Tix how to deal
with such build systems. If the package just has a makefile, you will likely run
into trouble if it doesn't follow good makefile conventions and you might end up
heavily patching it or rolling your own makefile. In these bad cases, consider
whether we even want to port such a package to Sortix.

The third thing to check is whether it cross-compiles. A lot of software have
considerable problems with this, and while we are able to work around some of
these problems, sometimes it's just not feasible. It's probably worth searching
around the net to see if other people have cross-compiled it. Perhaps they ran
into similar problems - as you are about to have - but have a fix?

The fourth thing to verify is whether Sortix is ready. Perhaps it uses some API
that Sortix doesn't have yet or depend on kernel features that are missing?
Perhaps the package has dependencies that are not yet satisfies, because nobody
has ported all the dependencies yet. You can often check guides such as "Beyond
Linux from Scratch" which contains a lot of building instructions and
dependency information. Try ask around in the operating system development
community if anyone has ported it before and what their experience was.

Authoring a Source Tix
----------------------

The first step in porting software to Sortix is creating a working source tix
and building it along with the rest of the system. You will need to get a copy
of the software you wish to install. You will need to save a copy of this
original compressed archive in a safe place, as you will need it later when you
create an archived port tix for publishing.

In this example we will pretend to port a fictitious piece of software called
`libfoo`. The latest release of libfoo is released as a compressed tarball with
the filename `libfoo-0.42.tar.xz`. You will save a copy of this original tarball
in a safe location and then proceed to extract it into the `$SORTIX_PORTS_DIR`
directory. This will usually create a `libfoo-0.42` sub-directory, but you need
to rename it to simply `libfoo`.

The next step is to author a `libfoo/tixbuildinfo` file, if libfoo does not ship
with support for Sortix. You will need to examine the package and deduce what
it's build system is and write the file accordingly. As a minimum, you will need
to put this in the file (with no leading spaces):

    tix.version=1
    tix.class=srctix
    pkg.name=libfoo
    pkg.build-libraries=libbar libbaz libqux

This is a simple key-value format. You will need to set the `pkg.name` variable
to the exact name of the source tix. The `pkg.build-libraries` variable contains
the build-time dependencies of the package as a space-delimited set. This is the
common key-values that all `tixbuildinfo` files *must* contain. In addition,
they must also contain the `pkg.build-system` variable.

GNU configure scripts
---------------------

The Tix package management system is designed such that it is normally easy to
port a package using autoconf generated `./configure` scripts (or compatible).
In the best case, it will simply suffice to write:

    pkg.build-system=configure

### config.sub ###

Most packages contain a copy of the config.sub shell script that recognizes the
platform triplets. While Sortix has been added to the upstream config.sub, some
packages still use an obsolete copy without it. In such cases we will need to
add it ourselves. You can simply search the line that contains the `-aos*`
operating system entry and append `-sortix*` to it:

    -	      | -aos* | -aros* \
    +	      | -aos* | -aros* | -sortix* \

If you don't do this, you will receive mysterious errors about that the package
doesn't know what Sortix is.

### Passing options and variables to configure and make ###

However, some packages are known to be silly and requires further tricks to be
ported. Fortunately, the `tixbuildinfo` file provides control of what arguments
and environmental variables are given to `./configure` and to make:

    pkg.configure.args=--enable-bar --without-x
    pkg.configure.vars=gt_cv_locale_fr=false gt_cv_locale_ja=false
    pkg.make.vars=V=1

It is generally recommended to set V=1 when running Make, if the package is
silly and doesn't actually report the exact executed commands to the terminal
during the compilation. This often happens with packages that use libtool. The
exact used commands are valuable for debugging purposes, if you run into a
compile warning or a compile error.

### Building Out-Of-Directory ###

If the package requires being built outside of the main source directory, you
can easily enable this behaviour:

    pkg.configure.use-build-directory=true

### Changing Make Targets ###

By default the make command will be run twice as `make all` and `make install`.
However, in some cases you would want to customize which targets are invoked:

    pkg.make.build-target=all-libfoo
    pkg.make.install-target=install-libfoo

### Post-Install Command ###

After the make install target has been run, you have the option of running a
custom command, with the working directory being the temporary staging
directory, before the package is fully packaged up. If you need to run multiple
commands at this point, you will need to wrap them in a shell script:

    pkg.post-install.cmd=./srctix-post-install.sh

### `.la` files ###

Packages that use libtool have a nasty habit of installing `.la` files into the
system library directory. However, this format doesn't properly support
cross-compilation and have a number of other problems. The better choice is
simply to eradicate such files using a post build command:

    pkg.post-install.cmd=tix-eradicate-libtool-la

You should check whether your port produces such `.la` files and use this post
build command if you see any.

### `--with-sysroot`, `--with-build-sysroot` ###

If the package supports the --with-sysroot configure option and it works
correctly for cross-compilation, then you can take advantage of it:

    pkg.configure.with-sysroot=true

Otherwise the build system will simply communicate the correct system root to
the compiler through hidden environmental variables and other tricks (such as
the cross-compiler's default system root). If the package supports also the
--with-build-sysroot to support having one system-root at runtime and another
at build-time, then you should enable both options:

    pkg.configure.with-sysroot=true
    pkg.configure.with-build-sysroot=true

If you are in doubt whether this works, simply disable these options (they are
disabled by default) and the default system-root tricks will do just fine.

### Wrapping configure and make ###

If you are unlucky, the configure script is custom and doesn't support common
options or have other flaws. In that case you best yell a lot at the developers
and question whether we want to port such a package in the first place, if the
developers can't get the build system right. However, you can often work-around
such problems by wrapping the configure and make commands in custom shell
scripts:

    pkg.configure.cmd=./srctix-configure.sh
    pkg.make.cmd=./srctix-make.sh

You'll need to set the executable bit on these files and these scripts should
emulate the standard configure and make tools and do whatever magic to work
around the broken build system. This should be considered the last resort - you
should check if there are other key-values you can set that works around the
issue or whether patching the software solves the issue.

### CC, CXX ###

If the package somehow fails to detect the correct compiler from the `--build`,
`--host`, and `--target` configure options, but it honours the CC and CXX
environmental variables, then you can set these keys and they will be set:

    pkg.make.needed-vars.CC=true
    pkg.make.needed-vars.CXX=true

### DESTDIR ###

During the install phase, the DESTDIR environmental variable points to a
temporary staging directory, which the port *must* use as an additional prefix.
If the package does not honour this, please yell a lot at the developers and
question whether we want to port such a package in the first place. You can
possibly work around this by wrapping make and configure.

### make distclean ###

The source tix will automatically be cleaned whenever it is built. If you are
using the configure build system, then the `make distclean` target will be used.
There currently is no way to override this, but you can wrap the make command to
work-around this or add support to `tix-build` for configuration.

Simple Makefile
---------------

Tix can use a conventional Makefile with a default target and an install target.

    pkg.build-system=makefile

The appropriate variables such as CC, CFLAGS, PREFIX, DESTDIR and such will be
set and must be honored from the environment.

Testing the Port
----------------

You can now proceed to attempt to compile your port. The simplest solution is
simply to rebuild the system along with all its ports, as usual. Keep in mind
that you will likely run into compile warnings or errors. It might be useful
to pass the `-O` or '--output-sync` option to Make to ensure the output is
consistent, even in the face of parallel execution - if you are using the -j
make option.

If everything goes well, congratulations, you just ported a piece of software to
Sortix. You should test it out and see if it works as expected and whether the
port can be improved -- be sure to follow the rest of the guide and publish it
to the Sortix community.

If things didn't work, it's time to troubleshoot and examine whether the port is
actually possible and what modifications will have to be made, as described in
the next section.

Porting the Software
--------------------

Alright, so things didn't work in the first try. Tough luck. A lot of packages
do the same silly things that makes it harder to port them. You'll have to be
creative to determine the correct strategy for completing the port, sometimes
you will have to implement features in Sortix or add some compatibility. Other
times, you can work around the build system or patch the software.

Try ask around and examine other archived port tixes and see their patches
reveal any useful advice on how to resolve the situation:

Here is a list of common problems:

### Configure fails ###

Try and examine the config.log file inside the build directory. At the bottom,
there is a dump of environmental variables, but just above it is information
about what particular test program failed.

If you examine the ./configure script closely, you will find that most checks
have an associated cache environmental variable. If a particular check fails
erroneously or isn't cross-compilation compatible, you can work around this
issue by modifying the `tixbuildinfo` file such that it declares these
environmental variables.

### Running cross-compiled programs ###

Some packages are silly and think they are always able to execute compiled test
programs, but this is inherently untrue for cross-compilation. Fortunately most
packages know better as cross-compilation is somewhat common. Still -- some
packages still do this, sometimes even as part of the check whether the compiler
works. The result is often that the process hangs, as Sortix executables are
Linux speak for "just hang and do nothing" in many cases. You'll have to fix the
package or somehow work around the problem. Don't forget to yell at the
developers of the package and question whether we really want it.

### Broken Locale Check ###

Some packages (like GNU tar) suffers from a defect where they scan for many
locales by running cross-compiled programs (see above). This can be worked
around by declaring the appropriate cache variables.

### Gnulib ###

Yuck! There's a lot of dirty hacks here and everybody has partial copies of it
integrated into the build system, but people rarely update such copies. As such,
it'll continue to be a pain to repeatedly work around. You should really check
existing ports and see if their patches solve this particular gnulib problem.
The problems here are often nasty, as gnulib occasionally wants to access libc
internals it has no business dealing with to work-around some obscure bug on
long-forgotten systems. You can probably safely `#if defined(__sortix__)` at the
relevant places where there seem to be no better solution.

You may even run into gnulib adding a warning to the gets(3) prototype telling
people to never use it. This ironically fails to compile on Sortix, which has no
gets(3) function in the first place.

### PATH_MAX ###

Sortix has no such limit and programs shouldn't rely on such a limit existing.
Nonetheless, sometimes it's easier to work-around the problem rather than really
fixing the problem:

    #if defined(__sortix__) && !defined(PATH_MAX)
    #define PATH_MAX 32768
    #endif

### Other problems ###

There's lots of problems that can arise when porting packages. While Sortix
itself is at fault for many of them, as it is still young and much remains to be
implemented, a lot of problems is the direct result of poor packages with
cross-compilation problems or not adhering strictly enough to standards. It can
be useful to maintain a branch of the main system, which has had a number of
hacks applied for compatibility. Ideally, this allows the Sortix developers to
know what compatibility problems needs to be addressed. Indeed, many features
originally started out as compatibility hacks.

Be also sure to consult the `obsolete-stuff` document, as it enumerates a list
of problematic APIs that we'd like to remove or refuse to implement - and what
the modern replacement APIs are.

Publishing the Port
-------------------

Now that your port works, it's time to publish it and let the community enjoy
your work.

### Cleaning the Source Directory ###

Your source tix likely have a lot of left-over temporary files as the result of
the testing phase. We'll need to clean the source directory before proceeding:

    cd $SORTIX_PORTS_DIR/libfoo &&
    ./configure &&
    make distclean

Or perhaps you'll need to do something else. The important thing is that no
object files and other problematic binary files are left behind. If the source
tree is already configured, you can skip the ./configure step.

### Creating the Normalized Tree ###

Unfortunately, some upstream releases are not actually distcleaned. This is a
problem because the diff between the upstream release and your port will contain
a lot of garbage. We'll then proceed to create a normalized tree that you can
diff cleanly against.

    cd $SORTIX_PORTS_DIR &&
    tar --extract --file $THAT_SAFE_LOCATION/libfoo-0.42.tar.xz &&
    mv libfoo-0.42 libfoo.normalized &
    (cd libfoo.original && ./configure && colormake distclean)

This should hopefully ensure that the two trees should be identical, except the
few changes you had to make to port the package. Be mindful if the upstream
developers are silly and don't put a single directory in their tarballs. You can
then proceed to analyse the difference between the two trees:

    diff -Naur libfoo.normalized libfoo

If everything went well, you should only see your changes. If there are other
changes, you should resolve the situation by deleting files from the normalized
tree, or by copying files from the normalized tree into the patched tree. Be
mindful that diff(1) and patch(1) doesn't preserve the executable bit. Don't put
a `tixbuildinfo` into the normalized tree, or it might unexpectedly become a
source tix.

### Creating the Archived Port Tix ###

The next step is to create the archived port tix, which contains the upstream
tarball and the patches done to it (including the normalization step). This file
allows others to easily review your port and ensure you have not made any
malicious changes. It will also allow others to recreate the normalized tree
and continue further development of the port. To create the archived port tix,
you simply have to invoke this command:

    cd $SORTIX_PORTS_DIR &&
    porttix-create --tarball $THAT_SAFE_LOCATION/libfoo-0.42.tar.xz \
                   --normalized libfoo.normalized \
                   libfoo

This will create a `libfoo.porttix.tar.xz` file, which is publishable. It is
not, however, ideal for simple extraction as a source tix.

### Creating the Archived Source Tix ###

In concept, it's easy to create an archived source tix: You simply tar up the
source tix you previously created. However, while it is easy to convert an
archived port tix into an archived source tix, it is not possible to go in the
other direction. It is recommended to create a port tix as it forces you to
carefully consider all the applied patches and is respectful to the community.
As mentioned, it is easy to convert the `.porttix.tar.xz` file into the desired
archived source tix:

    srctix-create libfoo.porttix.tar.xz

This will create an suitable `libfoo.srctix.tar.xz` in the current directory,
which is an archive containing the normalized tree with all patches applied.
When reviewing, be sure to verify the included tarball in the archived port tix
is entirely identical to the upstream release byte-for-byte to avoid security
problems.

### Publishing ###

Now that you have completed your port and built the archived port tix, it is
time to share your work with the Sortix community. The simplest solution is
sending the archived port tix (not the archived source tix) to the Sortix
developers. They will then review your work and graciously publish your port
through the appropriate channels. Alternatively, you can upload the port tix and
the source tix to your own site and maintain it as a third party port.

Conclusion
----------

This should be a basic walk-through the process of porting a piece of software
to the Sortix operating system. Note how the porting facilities are experimental
and much is subject to change in the near future. Porting software is an
advanced topic and this documentation merely touches the more common situations
and surely important advice is missing from this documentation. It's always a
good idea to consult the Sortix developers for advice.
