Cross compiling for Windows under Linux
Introduction - knowing, what you are doing
This is a short introduction for newbies. The following sections describe how to set up a Linux system to cross compile, creating Win32 executables (or FreeBSD or Darwin/OSX, or ...). Why cross compiling? Free Pascal is a compiler and basically converts source into binaries (machine language). These binaries also contain information about how the operating system should start the executable. Therefore these binaries are platform specific.
Free Pascal itself does not need much setup. It can create binaries for many platforms. Just tell it to do so. But the compiler is only one part.
There is also the assembler and the linker. And these tools are not able to create crossplatform code. That's why we have to create a special linker 'ld' and assembler 'as' for every target platform. These are the binutils.
After creating the cross tools, all the FPC Pascal units will be cross compiled. For example, there will then be one system.ppu file for every target platform. Next, your FPC config file (fpc.cfg) will be set up, so that cross compilation becomes so easy, that you can forget all the boring details. The same will be done for the LCL - the Lazarus Component Library. And after this you can cross compile Pascal programs for Win32. Either start them with wine or copy them to a Windows machine and test them there.
Why *nix to Windows and not the other way around
The main reason for this is that generating Linux/Unix binaries on a foreign platform (even another Unix or Linux system) is more complicated. Static linking is already complicated, let alone shared.
You would need the used libraries from the target platform (gtk, glib, libc etc), and a lot of additional configuring for ld (library paths, dynlinker path etc).
This has been partially done (for the static case), but it is hard since it needs manual postediting of linker files and linker commandline, and a deep understanding about what makes Unix binaries tick.
Newer FPCs - 2.1.1 and newer
If you are compiling a 2.1.1 or newer version of FPC you can just do:
$ make all OS_TARGET=win32 CPU_TARGET=i386
$ su -c "make crossinstall OS_TARGET=win32 CPU_TARGET=i386"
The reason for this simplicity is the internal linker included in this version of fpc.
FPC older than 2.1.1
For FPC versions older than 2.1.1, please see the History link (version before 19 August 2014) to see the steps needed
Cross compiling the LCL and Lazarus components
The IDE automatically cross compiles all used packages when you change the target of your project and build it.
Cross compiling a project
In Project->Compiler Options->Code, set the Target OS to 'win32' and in Paths the 'LCL Widget Type' to win32. That's all. The next time you build, you will create a win32 executable.
The IDE will rescan for win32 units, so that 'Find declaration' and code completion features will now work with the win32 rtl instead of the linux rtl. When you open another project or reopen this project the IDE will automatically switch.
Hints for Cross compiling and Lazarus
If you create an application/package for multiple targets, you will often do the following: Fix a bug, compile and test it under Linux, then compile and test it under win32, .. . Because normally you overwrite your .ppu files, you have to recompile everything, everytime you switch. This is not necessary. The Lazarus IDE supports macros.
Example 1: Cross compiling a project for linux and win32.
Set Project -> Compiler Options -> Paths -> Unit Output directory to $(TargetOS). This macro will be replaced by the value in Code -> TargetOS in lowercase (i.e. "linux" for Linux and "win32" for Win32). The output directory is relative to your project directory (the directory where your .lpi is). Create a linux and win32 directory in your project directory.
When you click on the "Show Options" button at the bottom of the compiler options, you will see a -FElinux/ or -FEwin32/. This option tells the compiler where to write the output (e.g. .ppu/.o files).
Example 2: Cross compiling a project for various platforms and widget sets.
Set the Unit output directory to $(TargetCPU)/$(TargetOS)/$(LCLWidgetType) and create the sub directories for all targets. This path construction is also used by the LCL.
The same can be done for packages.
Cross compiling and Lazarus Packages
Lazarus packages are not limited to libraries. They can be used to compile nearly everything. And the IDE automatically recompiles them if needed.
Packages can inherit compiler options. For example: A project that uses a package inherits the output directory of the package. In other words: the output directory of the package is added to unit search path of the project. See in the IDE: Project -> Compiler options -> Inherited.
Inheritance normally works only one way, but there are exceptions: The target platform (OS and CPU) of the project overrides the target for all used packages. That means, if you set the Target OS of the project to "win32" and compile the project, the IDE will check if the used packages need to be recompiled for this Target OS.
Package A has as output directory: lib/$(TargetOS) Project uses A.
- The project is built for linux. The IDE compiles A for linux in <PackageDirOfA>/lib/linux/, then it compiles the project for linux.
- The project is built for win32. The IDE compiles A for win32 in <PackageDirOfA>/lib/win32/, then it compiles the project for win32.
- The project is built again for linux. The IDE checks A for linux and does not recompile it. Then it compiles the project for linux.
So, using the macros saves a lot of time.