Getting Lazarus to develop Windows applications on Void Linux (musl)
Published at:
This is a guide to prepare a development environment on Void Linux to develop native GUI applications for Windows using Free Pascal and the Lazarus IDE.
The process will have the following characteristics:
- No dependency on proprietary software.
- No mutation of system directories (except via the package manager).
- We’ll get the latest version of the Free Pascal Compiler (FPC) and Lazarus.
Note that Void Linux does not include Lazarus in their repository, so we have to build it from source.
Moreover, Void Linux includes FPC 3.2.0, which is not the latest version (and is unable to compile the latest version of Lazarus), so we have to build FPC from source as well.
This means that the process requires a bit of patience.
Questions ¶
Q: Why Free Pascal and not Delphi?
A: Because Delphi is proprietary.
Q: Why don’t you just use Lazarus on Windows (the target platform)?
A: Because Windows is proprietary and garbage.
Q: Why Pascal and not a popular programming language?
A: Because I love Pascal.
Q: Why Void and not a popular distro that includes Lazarus?
A: Because I love Void.
Q: Why build from source instead of using the official builds?
A: Because they’re not compatible with musl.
Q: Why use musl in the first place instead of glibc?
A: Because I love challenges.
Q: Why Qt5 and not the default GTK?
A: Because GTK2 is outdated and the GTK3 LCL interface is not stable yet.
Q: Why Qt5 and not Qt6 (the latest)?
A: Because the Qt5 LCL interface is more stable.
Targets ¶
What we’re going to build:
- Free Pascal Compiler (FPC) 3.2.2, both native and cross-compiler for Windows.
- Qt5Pas.
- Lazarus 4.4.0.
Assumptions ¶
- This guide assumes you’re on the
x86_64architecture. - This guide assumes you don’t have Free Pascal or Lazarus installed.
This guide assumes you have native and cross-compiler GCC toolchains:
sudo xbps-install base-devel cross-i686-w64-mingw32 cross-x86_64-w64-mingw32
Note: FPC depends on GNU tools for assembling and linking, so you have to keep these packages (or at least a subset of them) as long as you’re using FPC to build Windows applications.
Note: Wine and GDB ¶
To run Windows applications in the development environment, we’re going to use use Wine:
sudo xbps-install wine
To debug Windows binaries on Linux, we’re going to use gdbserver.exe inside
Wine, and use the native gdb to connect to it.
sudo xbps-install gdb
To get gdbserver.exe, you can save your time and download pre-built mingw-w64
toolchain packages for Windows.
I chose w64devkit because it’s simple and portable. But you’re free to
choose another provider (or build gdbserver.exe yourself).
# We're gonna need two versions, one for 32-bit binaries and one for 64-bit
# binaries
sudo wget https://github.com/skeeto/w64devkit/releases/download/v2.4.0/w64devkit-x64-2.4.0.7z.exe
sudo wget https://github.com/skeeto/w64devkit/releases/download/v2.4.0/w64devkit-x86-2.4.0.7z.exe
# Run and install somewhere suitable
wine w64devkit-x64-2.4.0.7z.exe
wine w64devkit-x86-2.4.0.7z.exe
Note: Qt5 ¶
Void provides an outdated libQt5Pas package. I tried to compile Lazarus
against it and I failed.
This is why we’re going to compile our own. We need the Qt5 toolchain:
sudo xbps-install qt5-x11extras-devel
# This will pull important dependencies like `qt5-devel` and `qt5-qmake`
Note: While some pulled dependencies will only be used when building
libQt5Pas, other dependencies will be runtime dependencies (needed by Lazarus
at runtime). Be careful when cleaning build dependencies.
Getting FPC 3.2.2 ¶
In order to build FPC, we need FPC. As per the wiki, an FPC release is only guaranteed to be compiled successfully using its preceding release.
Void includes FPC 3.2.0, which is two releases older than the current version. This means we have to compile FPC 3.2.1 using Void’s FPC 3.2.0, and then compile FPC 3.2.2 using our FPC 3.2.1.
However, and by looking at the void-package build recipe for FPC, it seems
that FPC won’t normally compile under musl without some modifications in its
source code. (I already tried and fpmake will fail to link or something.)
This means that we need to patch FPC 3.2.1 and FPC 3.2.2, which is not an easy task (because these patches are not official).
Luckily, Alpine Linux (also uses musl) have FPC 3.2.2 in their testing repository. We are going to use it to build our own FPC (because their FPC build does not support GDB).
Getting Alpine’s FPC ¶
# Make a new directory for Alpine's FPC
mkdir alpine_fpc
cd alpine_fpc
# Download Alpine's FPC package
wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/fpc-3.2.2-r4.apk
# Extract it
tar -xf fpc-3.2.2-r4.apk
# (Optional) Delete the package
rm fpc-3.2.2-r4.apk
Now we have two directories, etc and usr.
# Temporarily prepend Alpine's FPC to the PATH
export PATH="$PWD/usr/bin:$PATH"
Alpine’s FPC now resides in usr/bin/fpc but it won’t work because it can’t
find ppcx64. The reason is that the Alpine build script creates an absolute
symlink for ppcx64 that assumes the package is installed in / (which is
fair). We have to fix the link:
ln -sf ../lib/fpc/3.2.2/ppcx64 usr/bin
Now we have a working FPC.
Preparing the FPC build ¶
# Get out of alpine_fpc
cd ..
# Download the FPC source code from the official mirror
wget https://downloads.sourceforge.net/sourceforge/freepascal/fpcbuild-3.2.2.tar.gz
# Extract it
tar -xf fpcbuild-3.2.2.tar.gz
# (Optional) Delete the tarball
rm fpcbuild-3.2.2.tar.gz
Now we have an fpcbuild-3.2.2 directory, containing the source code of FPC.
cd fpcbuild-3.2.2
Looking at Alpine’s build recipe at git.alpinelinux.org, there are two
modifications that must be done before building FPC on musl systems.
# Use correct linker path for produced binaries
sed -i \
-e "s,/lib64/ld-linux-x86-64\.so\..,/lib/ld-musl-x86_64.so.1," \
-e "s,/lib/ld-linux\.so\..,/lib/ld-musl-i386.so.1," \
-e "s,/lib/ld-linux-aarch64\.so\..,/lib/ld-musl-aarch64.so.1," \
-e "s,/lib/ld-linux-armhf\.so\..,/lib/ld-musl-armhf.so.1," \
-e "s,/lib64/ld64\.so\..,/lib/ld-musl-powerpc64le.so.1," \
fpcsrc/compiler/systems/t_linux.pas
# Strip out any unsupported instructions
find fpcsrc/rtl/linux -type f -print0 | xargs -0 sed -i '/libc_csu/d'
Building and installing FPC ¶
As per the recipe (with some modifications):
# Build
cd fpcsrc/compiler
fpcmake -Tall
cd ../..
make build
# Assign and prepare the installation prefix
P=~/.fpc # or whatever you want
mkdir -p $P
# Install
make install PREFIX=$P/usr
# Create a symlink for ppcx64
ln -s ../lib/fpc/3.2.2/ppcx64 $P/usr/bin
# Generate default configuration
$P/usr/lib/fpc/3.2.2/samplecfg $P/usr/lib/fpc/3.2.2 $P/usr/lib/fpc/etc
# Move the source directory to the prefix (will be needed)
mv fpcsrc $P/source
# Get out of fpcbuild-3.2.2
cd ..
# (Optional) Delete fpcbuild-3.2.2 as we no longer need it
rm -r fpcbuild-3.2.2
# (Optional) Delete alpine_fpc as we no longer need it
rm -r alpine_fpc
Now permanently prepend $P/usr/bin in your user’s $PATH.
The guide will assume the PATH is properly prepended from here on.
Getting Lazarus 4.4.0 ¶
Now that we have a working FPC, compiling Lazarus is such a trivial task.
# Assign and create a permanent directory for Lazarus
L=~/.lazarus
mkdir $L
cd $L
# Download the Lazarus source code from the official mirror
wget https://downloads.sourceforge.net/project/lazarus/Lazarus%20Zip%20_%20GZip/Lazarus%204.4/lazarus-4.4-0.tar.gz
# Extract it
tar -xf lazarus-4.4.0.tar.gz
# (Optional) Delete the tarball
rm lazarus-4.4.0.tar.gz
Now we have a lazarus directory inside $L.
Building qt5pas
¶
cd lazarus/lcl/interfaces/qt5/cbindings
# Generate the Makefile
qmake
# Build
make -j$(nproc)
# This is going to take a while (C++ compilers suck, I know)
After make finishes, we’ll have libQt5Pas.so.1.2.16 and symlinks to it. As
we won’t touch the system directories, we’re going to copy them to somewhere
reachable by FPC at compile time.
# Create directory for shared libraries
mkdir -p $P/usr/lib/fpc/3.2.2/lib/x86_64-linux
# Copy the library and its symlinks
cp libQt5Pas.so* $P/usr/lib/fpc/3.2.2/lib/x86_64-linux
# (Optional) Clean the source tree
make distclean
Now we have to append $P/usr/lib/fpc/3.2.2/lib/x86_64-linux to the user’s
LD_LIBRARY_PATH so Lazarus can find the library at runtime.
The guide will assume the LD_LIBRARY_PATH is already appended from here on.
Building Lazarus ¶
cd $L/lazarus
# Build Lazarus
make bigide LCL_PLATFORM=qt5
# Run for the first time
./startlazarus
Running make clean is not recommended, as rebuilding Lazarus is a common
thing (unless you’re never going to use non-default Lazarus packages).
Now you can either add $L/lazarus to your user’s PATH or symlink
startlazarus to somewhere in your user’s PATH.
Getting the cross-compilers ¶
# Go to the FPC source tree
cd $P/source
# Clean the source tree
make clean
# Build and then install the cross-compiler for "Windows x64"
make all OS_TARGET=win64 CPU_TARGET=x86_64
make crossinstall OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=$P/usr
# Do the same thing for "Windows x86"
make clean
make all OS_TARGET=win32 CPU_TARGET=i386
make crossinstall OS_TARGET=win32 CPU_TARGET=i386 INSTALL_PREFIX=$P/usr
# (Optional) clean the source free for the last time
make distclean
# Create a symlinks for ppcrossx64 and ppcross386
ln -s ../lib/fpc/3.2.2/ppcrossx64 $P/usr/bin
ln -s ../lib/fpc/3.2.2/ppcross386 $P/usr/bin
Keeping the $P/source directory is recommended as Lazarus needs it for code
inspection purposes.
Getting the debugger to work ¶
This is by-far the hardest part. Lazarus is not really well-equipped for a cross-debugging environment. All we currently have is an experiment feature-incomplete debugger backend: GNU remote debugger (gdbserver).
You can change the current debugger backend inside Lazarus in: Tools -> Options -> Debugger -> Debugger backend.
You’ll see that there is only two backends available:
- FpDebug internal Dwarf-debugger
- GNU debugger (gdb)
As per the Lazarus forums, you have to manually edit
$L/lazarus/ide/debugmanager.pas, adding gdbmidebugger to the uses
statement in the interface section of the unit. Insertion order was not
specified so I simply appended.
Rebuild Lazarus from inside Lazarus in: Tools -> Configure “Build Lazarus” …. Change the Profile to build value to Optimized IDE then click Save Settings. Now click Tools -> Build Lazarus with Profile: Optimized IDE.
After Lazarus finishes the build, it will restart itself. Now go to the debugger backend settings and you’ll see that our much-needed backend is there.
Now, to target Windows, go to Project -> Project Options … -> Compiler options -> Config and target and set Target OS to Win32 or Win64.
If you build and run the project, you’ll get a debugger error. That’s because
Lazarus does not (currently) manage the gdbserver. You have to run it
yourself:
.../w64devkit/bin/gdbserver :2345 $YOUR_EXE
You have to match the architecture of the target with that of the debugger.
Now try to rerun the project, debugging should work. That said, there are many essential features missing; like the ability to pause/stop execution.
What now? ¶
It appear the DX of remote debugging in Lazarus sucks so bad; but there is
always some space for improvement. I’m no expert in gdbserver, but it seems
it supports the following paradigm:
- Lazarus starts the
gdbserver.exeprocess (must be set somewhere) - On debugging, Lazarus sends the binary to the process
gdbserver.exedoes its job
Unfortunately, the current implementation of the gdbserver debugging backend
does not support all of that. You have to start the gdbserver manually after
building but before running the project. All of that to get a
feature-incomplete debugger (as opposed to normal GDB-powered debugger or the
internal debugger).
Compromise ¶
If you’re like me and you’re not invested nor experienced enough to contribute a better GDB remote debugger backend for Lazarus, you can go with the obvious solution: Build, run, debug and test for Linux, release for Windows.
The RTL, the FCL and the LCL are great libraries to write cross-platform software. As long as you do not introduce platform-specific code, you’re good to go. And you can always give a final, comprehensive user testing within Wine (or ReactOS or MS Windows).