Win-builds

(Free) Software Building and Packaging For Windows

This is an old revision of the document!


Creating new packages

Environment

If you haven’t already, build the packages that already exist. This will ensure you have everything needed and working; you can check the corresponding documentation.

Be ready to ask questions

Packaging is usually an experience with many issues and dirty work-arounds. Some of these are quite common and easy to spot for someone who has already seen similar issues so don’t hesitate to hit one of the support channels and ask questions.

The questions will help build documentation. For issues that have already been solved elsewhere in the project the answers will be easy; the difficult part is in making the list of possible issues. Again, don’t hesitate to ask.

Check whether a Slackware Linux build script already exists

There is a fairly large number of Slackware build scripts which are split in two:

  • base Slackware packages with a partial git repository in the slackware directory;
  • the slackbuilds.org (community repository of Slackware build scripts) which full git repository is in the slackbuilds.org directory.

If such a build script exist, read Section 4, “Port an existing build script”. Otherwise, read Section 5, “Or, start from a similar package”.

Even though this relies on build scripts for Slackware, this is in no way tied to Slackware: these build scripts have been chosen for their quality, simplicity and portability.

Port an existing build script

Locate the existing script

For slackbuilds.org all the files are already there. For slackware64-current, you need to copy them first. For example, libgcrypt is inside slackware64-current/n so you’d run a command similar to this one:

cd slackware64-current/n
lftp -c 'connect http://mirrors.slackware.com/slackware/slackware64-current/source/n; mirror libgcrypt'

Do the porting (mostly search-replace)

  • Add a ${PACKAGE}.yypkg.script file to the package directory; if you have nothing specific to set for the packaging (by far the usual case), you only have to cp another one; any will do except gcc’s; don’t forget to name it after the package however. For instance:
cp ../../l/libjpeg.yypkg.script newpackage.yypkg.script
  • Update the values for packager_email and packager_name in the ${PACKAGE}.yypkg.script file.
  • Replace all occurrences of usr with ${PREFIX} (verbatim: don’t expand the variable). This means /usr/ becomes /${PREFIX}/.
  • Delete the block of code that sets SLKCFLAGS and LIBDIRSUFFIX; it starts like this:
if [ "$ARCH" = "i486" ]; then
  SLKCFLAGS="-O2 -march=i486 -mtune=i686"
  LIBDIRSUFFIX=""
elif [ "$ARCH" = "i686" ]; then
  • Replace occurrences of $SLKCFLAGS with -O2 * Add –host=${HOST_TRIPLET} to the configure invocation. If this involves adding a newline in a configure call, don’t forget to escape it with \ right before it.
  • Locate the “strip” invocation; it looks like:
find $PKG | xargs file | grep -e "executable" -e "shared object" | grep ELF \
  | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true

Replace grep ELF with grep ${HOST_EXE_FORMAT} and xargs strip with xargs ${HOST_STRIP}.

  • Go near the end of the file and replace:
mkdir -p $PKG/install
cat $CWD/slack-desc > $PKG/install/slack-desc

cd $PKG
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.${PKGTYPE:-tgz}

with

cat ${CWD}/${PKGNAM}.yypkg.script | sed \
  -e "s/%{PKG}/${PKGNAM}/" \
  -e "s/%{HST}/${HOST_TRIPLET}/" \
  -e "s/%{TGT}//" \
  -e "s/%{VER}/${VERSION}/" \
  -e "s/%{BUILD}/${BUILD}/" \
  -e "s/%{DESCR}/${DESCR:-"No description"}/" \
  | yypkg --makepkg -o ${YYOUTPUT} --script - --directory "${PKG}/${PREFIX}"

Some packages don’t set the PKGNAM variable but instead hardcode the package name throughout the script; in that case, use the package name instead of ${PKGNAM}.

  • If there are many source files, replace the find + chmod commands which are used to reset file permissions to sane default with:
chmod -R u+w,go+r-w,a-s .

Or, start from a similar package

Create a new directory for your package inside mingw/ and copy an existing build script that is similar:

  • for autotools-based systems, copy slackware64-current/n/libgcrypt.SlackBuild and slackware64-current/n/libgcrypt.yypkg.script
  • for cmake-based systems, copy slackbuilds.org/libraries/openjpeg.SlackBuild and slackbuilds.org/libraries/openjpeg.yypkg.script

CMake-based build systems are much less friendly for cross-compilation and especially to Windows compared to autotools ones and openjpeg.SlackBuild has more kludges but they can’t be easily avoided for cmake-based systems.

You will probably have to add package-specific bits. This is why having references in the form of native linux build scripts can help a lot.

Download the sources

You will have to download the sources in the same directory as your build script.

Also note that the extension of the sources can matter: slackbuilds unfortunately tend to assume one such as in the following snippet (assumes .bz2) and you’ll need to ensure the build script matches the tarball extension.

rm -rf $PKGNAM-$VERSION
tar xvf $CWD/$PKGNAM-$VERSION.tar.bz2 || exit 1
cd $PKGNAM-$VERSION

Add your package to the list

Before building you need to let the builer know about your package. In the win-builds git repository, edit build/windows.ml“. You need to add an entry after all the package dependencies. The list is written in OCaml but you only need to understand the basic syntax.

For instance, libgcrypt depends on libgpg-error and needs to be added below it so that it can reference it.

(* The code below defines a new variable: libgcrypt
 * it is the result of a call to the "add" function with 6 arguments:
 * - ("libgcrypt", None): the package name is "libgcrypt" and there no
 *   so-called "variant" for it
 * - ~dir: the directory containing the package; the full path will be
 *   "slackware64-current/n/libgcrypt"
 * - ~dependencies: the list of dependencies, separated by ';' and enclosed
 *   between '[' and ']'.
 * - ~version: the package version as a string
 * - ~build: the package build number, as an integer
 * - ~sources: the list of sources which are not tracked in git (i.e. large
 *   binaries); each entry is made of the filename followed by the SHA1 of the
 *   file; the filename can contain ${PACKAGE} and ${VERSION} to not repeat
 *   the values given as ~version and in the first argument to the "add"
 *   function.
*)
let libgcrypt = add ("libgcrypt", None)
  ~dir:"slackware64-current/n"
  ~dependencies:[ libgpg_error ]
  ~version:"1.6.1"
  ~build:1
  ~sources:[
    "${PACKAGE}-${VERSION}.tar.bz2", "d9b63ac3b17a6972fc11150d136925b702f02";
  ]
in

Note the trailing in: the OCaml construct is let … in and the syntax requires that in after each new variable creation.

Test your package

You can now build your package like any other package, i.e. with a command similar to:

make -C win-builds WINDOWS=your_package

This will build your package for windows for both 32b and 64b. The logs will be stored inside @@VERSION@@/logs/windows_??/your_package.

You will most likely have to build, check the logs and change your build script several times.

Remember you can build for only one architecture at once. This will save CPU time and allow iterating faster. The syntax is:

make -C win-builds WINDOWS_64=your_package

devshell feature

While developing you might want to have a shell. To do so, simply append :devshell to your target:

make -C win-builds WINDOWS_64=your_package:devshell

It will give you a shell configured just like the shell used to run the SlackBuild except that it will try to use your own shell and not necessarily bash. You can then play the build script by hand to see what is going wrong.