Difference between revisions of "Cross compiling for Windows under Linux"

From Free Pascal wiki
Jump to navigationJump to search
 
(Cleanup page and hide very old legacy section inside comment tags)
 
(71 intermediate revisions by 22 users not shown)
Line 1: Line 1:
== Hints to Cross Compile Win32 binaries under Linux ==
+
{{Cross compiling for Win32 under Linux}}
 +
{{Note|Rewrite in progress, original content is retained inside comment tags; will pop up here again as it's reviewed and updated as necessary.}}
  
== Lazarus and the Free Pascal Compiler ==
+
== General ==
 +
One of the FPC's features is its ability to cross-compile. It's very useful to be able to make Windows (32-bit and 64-bit) from your Linux workstation. Everything we do here relates to FPC, no changes are needed to Lazarus.
  
=== About Cross compiling - Knowing, what you are doing ===
+
It's very important to note that these instructions apply to a FPC that was installed from packages from SourceForge; if you are using packages from your Linux distribution then they will almost certainly not work. In fact, in many cases, Linux distribution repository packages may not support building cross-compiles. Please consider uninstalling the distribution's packages and replacing them with ones from the [https://sourceforge.net/projects/lazarus/files/Lazarus official FPC/Lazarus SourceForge repository].
  
This is a short introduction for newbies.
+
These instructions have been tested on Ubuntu Linux 21.04 "The Hirsute Hippo" and openSUSE Leap 15.3.
The following sections describe how to setup a system to cross compile, that means working under linux and creating win32 executables (or freebsd or dawin, or ...).
 
Why cross compiling: FreePascal is a compiler and basically converts source into binaries (machine language). These binaries also contains information, how the operating system starts the executable. Therefore these binaries are platform specific.
 
FreePascal 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 cross code. That's why we have to create 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, then there is one system.ppu file for every target platform.  
 
Then your fpc config file will be setup, 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. ===
+
=== Install FPC/Lazarus ===
 +
If you already have a working FPC/Lazarus install obtained from SourceForge, then skip this step. Install FPC and Lazarus using the SourceForge method as detailed in [[Installing Lazarus on Linux#Build Lazarus from Source|Installing Lazarus on Linux § Build Lazarus from Source]] It's a good idea to fire it up and make sure you can build and compile a simple app first.
  
The main reason for this is that generating unix binaries on a foreign platform (another unix or even Linux distro included) is more complicated. Static linking is already complicated, let alone shared.
+
=== Setting up x86_64 Linux to Windows Cross-compile ===
 +
Note, we assume the use of FPC 3.2.2, if you're using a different version, put the right numbers in yourself!
  
You would need the used libraries from the target platform's (gtk, glib,libc etc), and a lot of additional configuring for ld. (library paths, dynlinker path etc)
+
The process here is to build the pre-compiled FPC units to suit Windows (both 32-bit and 64-bit) and, of course, the Windows compilers, ppcross386 and ppcross64. We then put a symlink to the compilers in the right place and make sure your FPC config files know about the Windows pre-compiled units.
  
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.  
+
Now to make a cross-compiler, the following needs to be done as root. It's easier (but slightly riskier) to <code>sudo</code> (or <code>su</code>) and stay as root for the whole process, so please be careful. We will set a temporary environment variable containing the FPC version number to make copy and pasting easy (assuming you are using FPC 3.2.2).
 +
<syntaxhighlight lang="shell">
 +
sudo -i
 +
export FPCVER="3.2.2"
 +
cd "/usr/share/fpcsrc/${FPCVER}"
 +
make clean all OS_TARGET=win64 CPU_TARGET=x86_64
 +
make clean all OS_TARGET=win32 CPU_TARGET=i386
 +
</syntaxhighlight>               
  
=== Download the FPC Sources ===
+
Those steps take awhile each. Watch for errors as the compile report scrolls by. It builds Windows versions of the units in the FPC-SRC tree. The next lines are pretty quick; they copy the units to <code>/usr/lib/fpc/${FPCVER}/units</code> and the ones after that make symlinks to the cross-compilers.
 +
<syntaxhighlight lang="bash">
 +
make crossinstall OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=/usr
 +
make crossinstall OS_TARGET=win32 CPU_TARGET=i386 INSTALL_PREFIX=/usr
 +
ln -sf "/usr/lib/fpc/${FPCVER}/ppcrossx64" /usr/bin/ppcrossx64
 +
ln -sf "/usr/lib/fpc/${FPCVER}/ppcross386" /usr/bin/ppcross386
 +
</syntaxhighlight>
  
The binaries are not enough, you need the complete fpc sources.
+
Assuming you did not see any errors, does your <code>fpc.cfg</code> file need attention?
See www.freepascal.org. You can use the cvs or a daily snapshot.
+
<syntaxhighlight lang="bash">
For the following examples the fpc sources were downloaded to ~/sources/fpc.
+
grep 'Fu' /etc/fpc.cfg
 +
</syntaxhighlight>
  
=== Download the gnu binutils. ===
+
Does the result listed include <code>-Fu/usr/lib/fpc/${fpcversion}/units/${fpctarget}/*</code>? If not, you need to fire up your favorite editor and add it. Look for the first line in the sample below, and when you find it, add the second line as shown.
 +
<syntaxhighlight lang="text">
 +
# searchpath for units and other system dependent things
 +
-Fu/usr/lib/fpc/${fpcversion}/units/${fpctarget}/*
 +
</syntaxhighlight>
  
For example binutils-2.14.tar.gz downloaded to
+
That's it, press <kbd style="background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 3px; box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #fff; color: #333; display: inline-block; font-family: sans-serif; font-size: 0.75rem; line-height: 1.4; margin: 0px 0.1em; padding: 0.1em 0.6em; text-shadow: 0 1px 0 #fff; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset;">Ctrl</kbd>+<kbd style="background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 3px; box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #fff; color: #333; display: inline-block; font-family: sans-serif; font-size: 0.75rem; line-height: 1.4; margin: 0px 0.1em; padding: 0.1em 0.6em; text-shadow: 0 1px 0 #fff; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset;">D</kbd> to get out of the risky root mode, and test it out.
~/download/binutils-2.14.tar.gz.
 
  
=== Cross build binutils ===
+
=== Testing ===
 +
Fire up Lazarus and open a new project, then:
 +
* Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set Target OS (-T) to Win64
 +
* Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set LCLWidgetType to Win32
 +
* Run ⇒ Build
 +
* Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set Target OS (-T) to Win32
 +
* Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set Target CPU Family (-P) to i386
 +
* Project ⇒ ProjectOptions ⇒ AdditionsAndOverRides, and set LCLWidgetType to Win32
 +
* Run ⇒ Build
  
In the fpc source directory there is a script to build the binutils for all
+
Obviously, you cannot 'run' that binary, but if one is made, it's almost certainly okay.
cross platforms: install/cross/buildcrossbinutils
 
Create a copy of the script:
 
  
  []$ cd ~/sources/fpc/install/cross/
+
== Lazarus/LCL ==
  []$ cp buildcrossbinutils buildcrossbinutils.sh
+
=== 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.
  
Edit the variables at the start of the new script.
+
=== Cross-compiling a project ===
 +
Lazarus projects can have 'Modes', and it's convenient to set a mode for each compilation you want to perform. Here you can make Linux64 binaries and Windows 32-bit and 64-bit executables, so make a mode for each and apply the appropriate settings to each mode.  
  
The BASE variable points to a building and installation directory. So, it
+
In Project ⇒ ProjectOptions ⇒ ConfigAndTarget, set the Target OS to 'win64' and in "Additions and Overrides" click "Set LCL WidgetType" and select win32. That's all. The next time you build, you will create a win64 executable.
should be an empty directory. For example:
 
  BASE=~/cross_fpc
 
  
Now the downloaded binutils file. If for instance you downloaded ~/download/binutils-2.14.tar.gz then set
+
Similarly, to make a Win32 executable, do the above but also set Project ⇒ ProjectOptions ⇒ ConfigAndTarget and set Target CPU Family (-P) to i386.
  BINUTILSPATH=~/download/
 
  BINUTILSBASE=binutils
 
  BINUTILSVERSION=2.14
 
  BINUTILS_GZIP=yes
 
  
The script will automatically combine this to ~/download/binutils-2.14.tar.gz.
+
The IDE will rescan for the Windows units, so that 'Find declaration' and code completion features will now work with the win32 rtl instead of the Linux rtl.
The rest variables define what target platforms you have. The default is to
 
build quite a lot, so compilation will take some time (hours).
 
For cross compile to windows, you need only
 
  
  TARGETS_WIN="mingw32"
+
=== Hints for cross-compiling with Lazarus ===
 
+
If you create an application/package for multiple targets, you will often do the following:
and to comment all other:
+
* Fix a bug
 
+
* Compile and test it under Linux, then
  #BSD="freebsd netbsd openbsd"
+
* Compile and test it under Win32
  #TARGETS_WIN="cygwin mingw32 msdosdjgpp"
 
  #TARGETS_I386="${BSD} linux solaris darwin"
 
  #TARGETS_POWERPC="${BSD} linux darwin"
 
  #TARGETS_SPARC="${BSD} linux solaris"
 
  #TARGETS_M68k=
 
  
 +
Because normally you overwrite your .ppu files you have to recompile everything, every time you switch. However, this is not necessary; the Lazarus IDE supports macros.
  
Then run the script:
+
==== 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 file is). Create <code>linux</code> and <code>win32</code> directories in your project directory.
  
  []$ sh buildcrossbinutils.sh
+
When you click on the "Show Options" button at the bottom of the compiler options, you will see a <code>-FElinux/</code> or <code>-FEwin32/</code>. This option tells the compiler where to write the output (e.g. .ppu/.o files).
 
 
The script creates a subdirectory 'logs' full of log files. If something goes
 
wrong, start looking there.
 
  
Note that for several platforms (Linux,FreeBSD, win32) these are available in compiled
+
==== Example 2: Cross-compiling a project for various platforms and widget sets ====
form already. See ftp://freepascal.stack.nl/pub/fpc/contrib/cross/
+
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.
  
=== Cross build FPC ===
+
The same can be done for packages.
  
In the fpc source directory there is a script to build the fpc snapshot for
+
=== Cross-compiling and Lazarus packages ===
all cross platforms: install/cross/buildcrosssnapshot
+
Lazarus packages are not limited to libraries, they can be used to compile nearly everything and the IDE automatically recompiles them, if needed.
Create a copy of the script:
 
  
  []$ cd ~/sources/fpc/install/cross/
+
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 the unit search path of the project. See for yourself in IDE: Project ⇒ Compiler options ⇒ Inherited.
  []$ cp buildcrosssnapshot buildcrosssnapshot.sh
 
  
Edit the variables at the start of the new script.
+
Inheritance normally works only one way, but there are exceptions:
 +
* The target platform (OS and CPU) of the project override 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. For example:
 +
** 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.
  
Normally you will change at least CROSSTOOLSROOT, FPCCVS, DESTDIR, TARGETS_OS
+
So using the macros saves a lot of time.
and TARGETS_CPU. For example:
 
 
 
  CROSSTOOLSROOT=~/cross_fpc/cross
 
  FPCCVS=~/sources/fpc
 
  TARGETS_OS="win32"
 
  TARGETS_CPU="i386"
 
  DESTDIR=~/cross_fpc/
 
 
 
Then run the script:
 
  
  []$ sh buildcrosssnapshot.sh
+
<!--
 +
== Legacy (for now) ==
 +
{{Note|This is information that is possibly out of date and unnecessary. It might be deleted some time in the future, so if you find something still valuable, consider moving it up above the preceeding heading.}}
  
After this you got cross compiled units in ~/cross_fpc/
+
=== Introduction - Knowing what you are doing ===
 +
{{Note|If your FPC has come from your Linux distribution repository, it is likely it won't have everything needed to be turned into a cross-compiler. Please see [[Installing the Free Pascal Compiler#Linux|Installing the Free Pascal Compiler § Linux]].}}
  
=== Configure your fpc.cfg ===
+
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/macOS, or…). Why cross-compiling? Free Pascal is a compiler and basically converts source code into binaries (machine language). These binaries also contain information about how the operating system should start the executable. Therefore, these binaries are platform-specific.
  
Open as root your /etc/fpc.cfg or create copy /etc/fpc.cfg to ~/fpc.cfg and edit this file. Search in the config for the unit search paths
+
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.
-Fu/usr/lib/fpc/$version/units/$target
 
-Fu/usr/lib/fpc/$version/units/$target/*
 
-Fu/usr/lib/fpc/$version/units/$target/rtl
 
  
Replace them with special paths for other platforms. For example for normal linux and cross compiled win32:
+
There are also the assembler and the linker, and these tools are not able to create cross-platform code. That's why we have to create a special linker <code>ld</code> and assembler <code>as</code> for every target platform; these are the <code>binutils</code>.
#IFDEF win32
 
-Fu~/cross_fpc/lib/fpc/$version/cross/i386-win32/units/
 
-Fu~/cross_fpc/lib/fpc/$version/cross/i386-win32/units/*
 
-Fu~/cross_fpc/lib/fpc/$version/cross/i386-win32/units/rtl
 
-XPi686-mingw32-
 
-FD~/cross_fpc/cross/bin
 
#ELSE linux
 
-Fu/usr/lib/fpc/$version/units/$target
 
-Fu/usr/lib/fpc/$version/units/$target/*
 
-Fu/usr/lib/fpc/$version/units/$target/rtl
 
#-Fu~/fpc/packages/*;~/fpc/rtl/linux
 
#ENDIF
 
  
ToDo: Test and troubleshooting
+
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 (<code>fpc.cfg</code>) will be setup so that cross-compilation becomes so easy that you can forget all the boring details. The same will be done for the LCL&mdash;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.
  
=== Cross compiling the LCL and lazarus components ===
+
=== Free Pascal ===
 +
==== 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/dynamic.
  
At Command line:
+
You would need the used libraries from the target platform (GTK, Glib, libc, etc.), and a lot of additional configuring for <code>ld</code> (library paths, dynamic linker path, etc.).
cd lazarus/lcl; make clean all OS_TARGET=win32
 
cd lazarus/lcl; make clean all
 
  
This will first cross compile everything for win32 (including the IDE, which is unecessary, but so I have to write less doc).  
+
This has been partially done (for the static case), but it is hard since it needs manual editing of linker files and the linker command line invocations, plus a deep understanding about what makes Linux/Unix binaries tick.
  
Or in the IDE: Set LCL, Synedit, Codetools, Package Registration to Clean+Build, set LCL interface to win32 and set 'Target OS' to win32. Then 'build lazarus'.  
+
==== Newer FPCs - 2.1.1+ ====
These four parts have splitted output directories, so your linux .ppu/.o files are *not* overwritten and you don't need to recompile them.
+
If you are compiling a v2.1.1 or newer version of FPC you can just do…
 +
<syntaxhighlight lang="shell-session">
 +
$ make all OS_TARGET=win32 CPU_TARGET=i386
 +
</syntaxhighlight>
 +
…and then…
 +
<syntaxhighlight lang="shell-session">
 +
$ su -c "make crossinstall OS_TARGET=win32 CPU_TARGET=i386"
 +
</syntaxhighlight>
  
=== Cross compiling a project ===
+
{{Note|<code>make crossinstall</code> by default installs all files in the <code>/usr/local/lib/fpc/${fpcversion}/units<code> directory, that's why you will need to add it to your <code>/etc/fpc.cfg</code> file as a new search path:<br /><code>-Fu/usr/local/lib/fpc/${fpcversion}/units/${fpctarget}/*</code>.<br /><br />Another option is to use INSTALL_PREFIX&#61;<code>/usr</code> while performing crossinstall. In this case, you will not need to change anything in the <code>/etc/fpc.cfg</code> file because all will be installed in <code>/usr/lib/fpc/${fpcversion}/units</code>, which is already there.}}
  
Set in Run->Compiler Options->Code the Target OS to 'win32' and in Paths the
+
{{Note|To build for Win64, the make command is: <syntaxhighlight lang="shell" inline>make all OS_TARGET=win64 CPU_TARGET=x86_64</syntaxhighlight>}}
'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 ===
+
The reason for this simplicity is the internal linker included in this version of fpc.
  
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.
+
==== An example under Bunsen Labs (Debian 8) ====
The Lazarus IDE supports macros.
+
* Cross-compile to Win32 and Win64 using FPC 3.0.0 and Lazarus 1.6.2.
 +
* Open up your terminal and execute the following commands (many thanks to Handoko and Leledumbo for this, you are awesome.).
 +
<syntaxhighlight lang="shell">
 +
# Navigate to the fpc source folder.
 +
cd /usr/share/fpcsrc/3.0.0
  
Example 1: Cross compiling a project for linux and win32.
+
# Compile the cross-compiler.
 
+
sudo make clean all OS_TARGET=win32 CPU_TARGET=i386
Set Run -> 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 ===
+
# Install the cross-compiler.
 +
sudo make crossinstall OS_TARGET=win32 CPU_TARGET=i386 INSTALL_PREFIX=/usr
  
Lazarus packages are not limited to libraries. They can be used to compile nearly everything. And the IDE automatically recompiles them if needed.  
+
# Link the cross-compiler and place the link where Lazarus can see it.
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: Run -> Compiler options -> Inherited.
+
sudo ln -sf /usr/lib/fpc/3.0.0/ppcross386 /usr/bin/ppcross386
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.
 
  
For example:
+
# Do the same using x64 as target
 +
sudo make clean all OS_TARGET=win64 CPU_TARGET=x86_64
 +
sudo make crossinstall OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=/usr
 +
sudo ln -sf /usr/lib/fpc/3.0.0/ppcrossx64 /usr/bin/ppcrossx64
 +
</syntaxhighlight>
 +
* Make sure your cross-compilers are alive: (Note, the links to the cross-compiling binaries appear in <code>/usr/bin</code> rather than <code>/usr/lib</code>, as shown in the images below).
 +
[[File:000-cross-compilers-compiled.png|thumb|right]]
 +
* Make sure your cross-compilers were properly linked:
 +
[[File:000-cross-compilers-linked.png|thumb|right]]
 +
* Now open Lazarus.
 +
** Find the "Paths" item in the right list of the "Project Options" window. Press <kbd style="background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 3px; box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #fff; color: #333; display: inline-block; font-family: sans-serif; font-size: 0.75rem; line-height: 1.4; margin: 0px 0.1em; padding: 0.1em 0.6em; text-shadow: 0 1px 0 #fff; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset;">Ctrl</kbd>+<kbd style="background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 3px; box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #fff; color: #333; display: inline-block; font-family: sans-serif; font-size: 0.75rem; line-height: 1.4; margin: 0px 0.1em; padding: 0.1em 0.6em; text-shadow: 0 1px 0 #fff; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset;">Shift</kbd>+<kbd style="background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 3px; box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #fff; color: #333; display: inline-block; font-family: sans-serif; font-size: 0.75rem; line-height: 1.4; margin: 0px 0.1em; padding: 0.1em 0.6em; text-shadow: 0 1px 0 #fff; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset;">F11</kbd> or navigate through the Projects ⇒ Project Options ⇒ Paths menus.
 +
** For each build, configure your paths so that all necessary libraries are reachable and all output files can be generated by Lazarus/FPC with no overriding. I have chosen to use macros in the "Unit output path" and "Target file name".
 +
[[File:002-paths-win32.png|thumb|right]]
 +
** Create builds and edit build names in the "Build Mode: [BuildName]" window (click in the upper […] button to open it).
 +
[[File:001-build-modes.png|thumb|right]]
 +
** Click <kbd style="background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 3px; box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #fff; color: #333; display: inline-block; font-family: sans-serif; font-size: 0.75rem; line-height: 1.4; margin: 0px 0.1em; padding: 0.1em 0.6em; text-shadow: 0 1px 0 #fff; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset;">OK</kbd> when you're done.
 +
** Now go to Run ⇒ "Build Many Modes" and press <kbd style="background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 3px; box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #fff; color: #333; display: inline-block; font-family: sans-serif; font-size: 0.75rem; line-height: 1.4; margin: 0px 0.1em; padding: 0.1em 0.6em; text-shadow: 0 1px 0 #fff; -moz-border-radius: 3px; -moz-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset; -webkit-border-radius: 3px; -webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.2), 0 0 0 2px #fff inset;">OK</kbd>, and wait until Lazarus and FPC finish their work.
 +
[[File:003-build-many-modes.png|thumb|right]]
 +
** Go to your project folder and enjoy!
 +
[[File:004-executables.png|thumb|right|Write once, compile anywhere.]]
  
Package A has as output directory: lib/$(TargetOS)
+
==== FPC older than 2.1.1 ====
Project uses A.
+
For FPC versions older than 2.1.1, please see the History link (version before 19 August 2014) to see the steps needed.
1. The project is built for linux. The IDE compiles A for linux in <PackageDirOfA>/lib/linux/, then it compiles the project for linux.
+
* [http://www.stack.nl/~marcov/crossnotes.txt Notes about cross compiling]
2. The project is built for win32. The IDE compiles A for win32 in <PackageDirOfA>/lib/win32/, then it compiles the project for win32.
+
-->
3. 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.
+
== See also ==
 +
* [[Cross compiling]]
 +
* [[buildfaq]]
  
  
==Original Contributors==
+
[[Category:Cross compilation]]
This page has been converted from the epikwiki [http://lazarus-ccr.sourceforge.net/index.php?wiki=CrossCompilingForWin32UnderLinux version].
+
[[Category:FPC]]
 +
[[Category:Lazarus]]

Latest revision as of 00:36, 21 September 2023

English (en)

Light bulb  Note: Rewrite in progress, original content is retained inside comment tags; will pop up here again as it's reviewed and updated as necessary.

General

One of the FPC's features is its ability to cross-compile. It's very useful to be able to make Windows (32-bit and 64-bit) from your Linux workstation. Everything we do here relates to FPC, no changes are needed to Lazarus.

It's very important to note that these instructions apply to a FPC that was installed from packages from SourceForge; if you are using packages from your Linux distribution then they will almost certainly not work. In fact, in many cases, Linux distribution repository packages may not support building cross-compiles. Please consider uninstalling the distribution's packages and replacing them with ones from the official FPC/Lazarus SourceForge repository.

These instructions have been tested on Ubuntu Linux 21.04 "The Hirsute Hippo" and openSUSE Leap 15.3.

Install FPC/Lazarus

If you already have a working FPC/Lazarus install obtained from SourceForge, then skip this step. Install FPC and Lazarus using the SourceForge method as detailed in Installing Lazarus on Linux § Build Lazarus from Source It's a good idea to fire it up and make sure you can build and compile a simple app first.

Setting up x86_64 Linux to Windows Cross-compile

Note, we assume the use of FPC 3.2.2, if you're using a different version, put the right numbers in yourself!

The process here is to build the pre-compiled FPC units to suit Windows (both 32-bit and 64-bit) and, of course, the Windows compilers, ppcross386 and ppcross64. We then put a symlink to the compilers in the right place and make sure your FPC config files know about the Windows pre-compiled units.

Now to make a cross-compiler, the following needs to be done as root. It's easier (but slightly riskier) to sudo (or su) and stay as root for the whole process, so please be careful. We will set a temporary environment variable containing the FPC version number to make copy and pasting easy (assuming you are using FPC 3.2.2).

sudo -i 
export FPCVER="3.2.2"
cd "/usr/share/fpcsrc/${FPCVER}"
make clean all OS_TARGET=win64 CPU_TARGET=x86_64
make clean all OS_TARGET=win32 CPU_TARGET=i386

Those steps take awhile each. Watch for errors as the compile report scrolls by. It builds Windows versions of the units in the FPC-SRC tree. The next lines are pretty quick; they copy the units to /usr/lib/fpc/${FPCVER}/units and the ones after that make symlinks to the cross-compilers.

make crossinstall OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=/usr
make crossinstall OS_TARGET=win32 CPU_TARGET=i386 INSTALL_PREFIX=/usr
ln -sf "/usr/lib/fpc/${FPCVER}/ppcrossx64" /usr/bin/ppcrossx64
ln -sf "/usr/lib/fpc/${FPCVER}/ppcross386" /usr/bin/ppcross386

Assuming you did not see any errors, does your fpc.cfg file need attention?

grep 'Fu' /etc/fpc.cfg

Does the result listed include -Fu/usr/lib/fpc/${fpcversion}/units/${fpctarget}/*? If not, you need to fire up your favorite editor and add it. Look for the first line in the sample below, and when you find it, add the second line as shown.

# searchpath for units and other system dependent things
-Fu/usr/lib/fpc/${fpcversion}/units/${fpctarget}/*

That's it, press Ctrl+D to get out of the risky root mode, and test it out.

Testing

Fire up Lazarus and open a new project, then:

  • Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set Target OS (-T) to Win64
  • Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set LCLWidgetType to Win32
  • Run ⇒ Build
  • Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set Target OS (-T) to Win32
  • Project ⇒ ProjectOptions ⇒ ConfigAndTarget, and set Target CPU Family (-P) to i386
  • Project ⇒ ProjectOptions ⇒ AdditionsAndOverRides, and set LCLWidgetType to Win32
  • Run ⇒ Build

Obviously, you cannot 'run' that binary, but if one is made, it's almost certainly okay.

Lazarus/LCL

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

Lazarus projects can have 'Modes', and it's convenient to set a mode for each compilation you want to perform. Here you can make Linux64 binaries and Windows 32-bit and 64-bit executables, so make a mode for each and apply the appropriate settings to each mode.

In Project ⇒ ProjectOptions ⇒ ConfigAndTarget, set the Target OS to 'win64' and in "Additions and Overrides" click "Set LCL WidgetType" and select win32. That's all. The next time you build, you will create a win64 executable.

Similarly, to make a Win32 executable, do the above but also set Project ⇒ ProjectOptions ⇒ ConfigAndTarget and set Target CPU Family (-P) to i386.

The IDE will rescan for the Windows units, so that 'Find declaration' and code completion features will now work with the win32 rtl instead of the Linux rtl.

Hints for cross-compiling with 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, every time you switch. However, 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 file is). Create linux and win32 directories 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 the unit search path of the project. See for yourself in IDE: Project ⇒ Compiler options ⇒ Inherited.

Inheritance normally works only one way, but there are exceptions:

  • The target platform (OS and CPU) of the project override 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. For example:
    • Package A has as output directory: lib/$(TargetOS). Project uses A.
    1. The project is built for linux. The IDE compiles A for linux in <PackageDirOfA>/lib/linux/, then it compiles the project for linux.
    2. The project is built for win32. The IDE compiles A for win32 in <PackageDirOfA>/lib/win32/, then it compiles the project for win32.
    3. 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.


See also