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

From Lazarus wiki
Jump to navigationJump to search
(missing install line)
 
(30 intermediate revisions by 11 users not shown)
Line 1: Line 1:
{{Translate}}<!--{{Cross compiling for Win32 under Linux}}-->
+
{{Cross compiling for Win32 under Linux}}
  
== Hints to Cross Compile Win32 binaries under Linux ==
+
== General ==
 +
 
 +
{{ Note | Rewrite in progress, original content is down below, will pop up here again as its reviewed and updated as necessary}}
 +
 
 +
One of the FPC features is is ability to cross compile. Its very useful to be able to make Windows (32bit and 64bit) from your Linux workstation. Everything we do here relates to FPC, no changes are needed to Lazarus.
 +
 
 +
Its 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. https://sourceforge.net/projects/lazarus/files/Lazarus
 +
 
 +
These instructions have been tested on Linux Ubuntu 21.04 and SUSE Leap 15.3.
 +
 
 +
=== Install FPC/Lazarus ===
 +
If you already have a working FPC/Lazarus install obtained from SourceForge, skip the this step. Install FPC and Lazarus Using the SourceForge method as detailed in https://wiki.freepascal.org/Installing_Lazarus_on_Linux#Build_Lazarus_from_Source Good idea to fire it up and make sure you can build and compile a simple app.
 +
 
 +
=== Setting up a 64bit Linux to Windows Cross Compile. ===
 +
 
 +
Note, we, assume fpc 3.2.2, if not, put the right numbers in yourself !
 +
 
 +
The process here is to build the pre compiled FPC units to suit Windows (both 32bit and 64bit) 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 knows about the Windows pre compiled units.
 +
 
 +
Now to make a cross compiler, the following needs to be done as root, its 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 env variable containing the FPC version number to make copy and pasting easy (assuming you are using FPC3.2.2) -
 +
 
 +
<syntaxhighlight lang="bash">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>               
 +
 
 +
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 copies the units to /usr/lib/fpc/3.2.2/units and the ones after that makes a symlinks to the crosscompilers.
 +
 
 +
<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>
 +
 
 +
Right, assuming you did not see any errors, does your fpc.cfg file need attention ?
 +
 
 +
<syntaxhighlight lang="bash">grep Fu /etc/fpc.cfg</syntaxhighlight>
 +
 
 +
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, when you find it, add the second line as shown.
  
Since 0.9.10 there is an rpm 'fpc-crosswin32', that installs the needed binutils (e.g. cross assembler, cross linker), the fpc .ppu files cross compiled for win32 and modifies /etc/fpc.cfg.
+
<syntaxhighlight># searchpath for units and other system dependent things
 +
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/*</syntaxhighlight>
  
It does not contain the cross compiled LCL .ppu files. You need to compile them yourself, after installing fpc-crosswin32.
+
Thats it, press Control-D, to get out of the risky root mode. And test it out.
  
== General ==
+
=== Testing ===
 +
Fire up Lazarus, open a new project, then
 +
* Project->ProjectOptions->ConfigAndTarget and Set Target OS (-T) to Win64
 +
* Project->ProjectOptions->ConfigAndTarget, 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, Set LCLWidgetType" to Win32
 +
* Run->Build
 +
 
 +
 
 +
Obviously, you cannot 'run' that binary but if one is made, its almost certainly OK.
 +
 
 +
== 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 ===
  
=== Introduction - knowing, what you are doing ===
+
Lazarus Projects can have 'Modes' and its convenient to set a mode for each compile you want to do. So, here, now you can make Linux64 binaries and Windows 32bit and 64bit executables, so make a mode for each and apply the appropriate settings to each mode.
  
This is a short introduction for newbies.
+
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.  
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? FreePascal 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.  
 
  
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.
+
Similarly, to make a Win32 executable, do the above but also set Project->ProjectOptions->ConfigAndTarget and Set Target CPU Family (-P) to i386
  
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.
+
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.
  
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.
 
  
== Free Pascal ==
+
=== Hints for Cross compiling and Lazarus ===
=== Why *nix to Windows and not the other way around ===
 
  
The main reason for this is that generating Unix binaries on a foreign platform (even another Unix or Linux system) is more complicated. Static linking is already complicated, let alone shared.
+
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.
  
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).
+
Example 1: Cross compiling a project for linux and win32.  
  
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.
+
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.
  
=== Newer FPCs - 2.1.1 and newer ===
+
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).
If you are compiling a 2.1.1 or newer version of FPC you can just do:
 
  
<syntaxhighlight lang="bash">$ make all OS_TARGET=win32 CPU_TARGET=i386</syntaxhighlight>
 
and then
 
<syntaxhighlight lang="bash">$ su -c "make crossinstall OS_TARGET=win32 CPU_TARGET=i386"</syntaxhighlight>
 
  
{{Note|To build for win64 the make command is: make all OS_TARGET&#61;win64 CPU_TARGET&#61;x86_64}}
+
Example 2: Cross compiling a project for various platforms and widget sets.
  
The reason for this simplicity is the internal linker included in this version of fpc.
+
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.
  
=== FPC older than 2.1.1 ===
+
The same can be done for packages.
For FPC versions older than 2.1.1, more steps are needed:
 
==== Download the FPC Sources ====
 
  
The binaries are not enough, you need the complete fpc sources.
+
=== Cross compiling and Lazarus Packages ===
See www.freepascal.org. You can use SVN or a daily snapshot ([http://svn.freepascal.org/svn/fpc/ HERE]).
 
For the following examples the FPC sources were downloaded to ~/sources/fpc.
 
  
You can download those files from [http://freepascal.org/down/source/sources.var HERE]
+
Lazarus packages are not limited to libraries. They can be used to compile nearly everything. And the IDE automatically recompiles them if needed.  
  
==== Download the gnu binutils ====
+
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.
For example binutils-2.18.tar.gz downloaded to
 
~/download/binutils-2.18.tar.gz.
 
  
You can download those files from [http://ftp.gnu.org/gnu/binutils/ HERE]
+
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.
  
==== Cross build binutils ====
+
For example:
In the fpcbuild repository there is a script to build the binutils for all
 
cross platforms: install/cross/buildcrossbinutils
 
  
Download install of fpcbuild, e.g something like (adjust version):
+
Package A has as output directory: lib/$(TargetOS)
<syntaxhighlight lang=bash>
+
Project uses A.
cd ~
+
# The project is built for linux. The IDE compiles A for linux in <PackageDirOfA>/lib/linux/, then it compiles the project for linux.
svn co http://svn.freepascal.org/svn/fpcbuild/branches/fixes_2_4/install install
+
# The project is built for win32. The IDE compiles A for win32 in <PackageDirOfA>/lib/win32/, then it compiles the project for win32.
</syntaxhighlight>
+
# 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.
  
Create a copy of the script:
+
So, using the macros saves a lot of time.
<syntaxhighlight lang=bash>
 
cd ~/install/cross/
 
cp buildcrossbinutils buildcrossbinutils.sh
 
</syntaxhighlight>
 
  
Edit the variables at the start of the new script.
 
  
The BASE variable points to a building and installation directory. So, it
+
== Legacy (for now) ==
should be an empty directory. For example:
 
  BASE=~/cross_fpc
 
  
Now the downloaded binutils file. If for instance you downloaded ~/download/binutils-2.18.tar.gz then set
+
'''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 above heading.'''
  BINUTILSPATH=~/download/
 
  BINUTILSBASE=binutils
 
  BINUTILSVERSION=2.18
 
  BINUTILS_GZIP=yes
 
  
The script will automatically combine this to ~/download/binutils-2.18.tar.gz.
 
  
The rest of the variables define what target platforms you have. The default is to
+
=== Introduction - knowing, what you are doing ===
build quite a lot, so compilation will take some time (hours on slow machines).
 
For cross compile to windows, you need only
 
  
  TARGETS_WIN="mingw32"
+
'''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]].
 
 
and comment out all others:
 
 
 
  #BSD="freebsd netbsd openbsd"
 
  #TARGETS_WIN="cygwin mingw32 msdosdjgpp"
 
  #TARGETS_I386="${BSD} linux solaris darwin"
 
  #TARGETS_POWERPC="${BSD} linux darwin"
 
  #TARGETS_SPARC="${BSD} linux solaris"
 
  #TARGETS_M68k=
 
  
 +
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 into binaries (machine language). These binaries also contain information about how the operating system should start the executable. Therefore these binaries are platform specific.
  
Then run the script:
+
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.  
<syntaxhighlight lang=bash>
 
sh buildcrossbinutils.sh
 
</syntaxhighlight>
 
  
The script creates a subdirectory 'logs' full of log files. If something goes
+
There is 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 'ld' and assembler 'as' for every target platform. These are the binutils.
wrong, start looking there.
 
  
Note that the cross binutils for several platforms (Linux,FreeBSD, win32) these are available in compiled form already. See ftp://freepascal.stack.nl/pub/fpc/contrib/cross/
+
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.
  
==== Cross build FPC ====
+
=== Free Pascal ===
In the fpcbuild repository there is a script to build the fpc snapshot for all cross platforms: install/cross/buildcrosssnapshot
+
==== Why *nix to Windows and not the other way around ====
Create a copy of the script:
 
  
<syntaxhighlight lang=bash>
+
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.
cd ~/install/cross/
 
cp buildcrosssnapshot buildcrosssnapshot.sh
 
</syntaxhighlight>
 
  
Edit the variables at the start of the new script.
+
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).
  
Normally you will change at least CROSSTOOLSROOT, FPCCVS, DESTDIR, TARGETS_OS
+
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.
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:
 
  
<syntaxhighlight lang=bash>
+
==== Newer FPCs - 2.1.1 and newer ====
sh buildcrosssnapshot.sh
+
If you are compiling a 2.1.1 or newer version of FPC you can just do:
</syntaxhighlight>
 
  
After this you got cross compiled units in ~/cross_fpc/
+
<syntaxhighlight lang="bash">$ make all OS_TARGET=win32 CPU_TARGET=i386</syntaxhighlight>
 +
and then
 +
<syntaxhighlight lang="bash">$ su -c "make crossinstall OS_TARGET=win32 CPU_TARGET=i386"</syntaxhighlight>
  
==== Configure your fpc.cfg ====
+
{{Note|<br />make crossinstall by default installs all files in '''/usr/local/lib/fpc/$fpcversion/units''' directory, that's why you will need to add to '''/etc/fpc.cfg file''' a new search path:<br />
 +
'''-Fu/usr/local/lib/fpc/$fpcversion/units/$fpctarget/*'''.<br />
 +
<br />
 +
Another option is to use INSTALL_PREFIX&#61;'''/usr''' while performing crossinstall. In this case, you will not need to change anything in '''/etc/fpc.cfg''' file because all will be installed in '''/usr/lib/fpc/$fpcversion/units''' which is already there}}
  
As root, open your /etc/fpc.cfg or copy /etc/fpc.cfg to ~/fpc.cfg and edit this file. Search in the config for the unit search paths.
+
{{Note|<br />To build for win64 the make command is: make all OS_TARGET&#61;win64 CPU_TARGET&#61;x86_64}}
  
Note: On older versions of FPC $version and $target are used instead of $fpcversion and $fpctarget
+
The reason for this simplicity is the internal linker included in this version of fpc.
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget
 
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/*
 
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/rtl
 
  
Replace them with special paths for other platforms. For example, for normal linux and cross compiled win32:
+
==== An example under Bunsen Labs (Debian 8) ====
#IFDEF win32
 
-Fu~/cross_fpc/lib/fpc/$fpcversion/cross/units/i386-win32/
 
-Fu~/cross_fpc/lib/fpc/$fpcversion/cross/units/i386-win32/*
 
-Fu~/cross_fpc/lib/fpc/$fpcversion/cross/units/i386-win32/rtl
 
-XPi686-mingw32-
 
-FD~/cross_fpc/cross/bin
 
#ELSE linux
 
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget
 
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/*
 
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/rtl
 
#-Fu~/fpc/packages/*;~/fpc/rtl/linux
 
#ENDIF
 
  
== Lazarus/LCL ==
+
- Cross-compile to Win32 and Win64 using FPC 3.0.0 and Lazarus 1.6.2
  
=== Cross compiling the LCL and lazarus components ===
+
- Open up your terminal and execute the following commands (Many thanks to Handoko and Leledumbo for this, you are awesome).
  
At the command line:
+
<syntaxhighlight lang="bash">
<syntaxhighlight lang=bash>
 
cd lazarus; make clean all OS_TARGET=win32
 
cd lazarus/lcl; make clean all
 
</syntaxhighlight>
 
  
This will first cross compile everything for win32 (including the IDE, which is unecessary, but this way is the easiest to explain).  
+
# Navigate to the fpc source folder.
 +
cd /usr/share/fpcsrc/3.0.0
  
Or in the IDE: Set LCL, Synedit, Codetools, Package Registration and IDE Interface to Clean+Build, set LCL interface to win32/win64 and set 'Target OS' to win32. Then 'build lazarus'.  
+
# Compile the cross-compiler.
These four parts have split output directories, so your linux .ppu/.o files are *not* overwritten and you don't need to recompile them.
+
sudo make clean all OS_TARGET=win32 CPU_TARGET=i386
  
=== Cross compiling a project ===
+
# Install the cross-compiler.
 +
sudo make crossinstall OS_TARGET=win32 CPU_TARGET=i386 INSTALL_PREFIX=/usr
  
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.
+
# Link the cross-compiler and place the link where Lazarus can see it.
 +
sudo ln -sf /usr/lib/fpc/3.0.0/ppcross386 /usr/bin/ppcross386
  
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.
+
# Do the same using x64 as target
When you open another project or reopen this project the IDE will automatically switch.
+
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
  
=== Hints for Cross compiling and Lazarus ===
+
</syntaxhighlight>
  
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.
+
- Make sure your cross-compilers are alive: (Note, the links to the cross compiling binaries appear in /usr/bin rather than /usr/lib as shown in the images below).
The Lazarus IDE supports macros.
 
  
Example 1: Cross compiling a project for linux and win32.  
+
[[File:000-cross-compilers-compiled.png]]
  
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).
+
- Make sure your cross-compilers were properly linked:
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).
+
[[File:000-cross-compilers-linked.png]]
  
 +
- Now open Lazarus.
  
Example 2: Cross compiling a project for various platforms and widget sets.
+
- Find the "Paths" item in the right list of the "Project Options" window. Press Ctrl+Shift+F11 or navigate through the Projects->Project Options->Paths menus.
  
Set the Unit output directory to
+
- For each build, configure your paths so that all necessary libraries are reacheable 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".
$(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.
+
[[File:002-paths-win32.png]]
  
=== Cross compiling and Lazarus Packages ===
+
- Create builds and edit build names in the "Build Mode: [BuildName]" window (click in the upper [...] button to open it).
  
Lazarus packages are not limited to libraries. They can be used to compile nearly everything. And the IDE automatically recompiles them if needed.  
+
[[File:001-build-modes.png]]
  
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.
+
- Click ok when your done.
  
Inheritance normally works only one way, but there are exceptions:
+
- Now go to Run -> "Build Many Modes". Press ok. Wait until Lazarus and FPC finishes their work.  
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:
+
[[File:003-build-many-modes.png]]
  
Package A has as output directory: lib/$(TargetOS)
+
- Go to your project folder and enjoy!
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.
+
[[File:004-executables.png||||Write once, compile anywhere.]]
  
 +
==== 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
  
 
==See also==
 
==See also==

Latest revision as of 10:49, 24 July 2021

English (en)

General

Note-icon.png

Note: Rewrite in progress, original content is down below, will pop up here again as its reviewed and updated as necessary

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

Its 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. https://sourceforge.net/projects/lazarus/files/Lazarus

These instructions have been tested on Linux Ubuntu 21.04 and SUSE Leap 15.3.

Install FPC/Lazarus

If you already have a working FPC/Lazarus install obtained from SourceForge, skip the this step. Install FPC and Lazarus Using the SourceForge method as detailed in https://wiki.freepascal.org/Installing_Lazarus_on_Linux#Build_Lazarus_from_Source Good idea to fire it up and make sure you can build and compile a simple app.

Setting up a 64bit Linux to Windows Cross Compile.

Note, we, assume fpc 3.2.2, if not, put the right numbers in yourself !

The process here is to build the pre compiled FPC units to suit Windows (both 32bit and 64bit) 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 knows about the Windows pre compiled units.

Now to make a cross compiler, the following needs to be done as root, its 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 env variable containing the FPC version number to make copy and pasting easy (assuming you are using FPC3.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 copies the units to /usr/lib/fpc/3.2.2/units and the ones after that makes a symlinks to the crosscompilers.

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

Right, 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, 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/*

Thats it, press Control-D, to get out of the risky root mode. And test it out.

Testing

Fire up Lazarus, open a new project, then

  • Project->ProjectOptions->ConfigAndTarget and Set Target OS (-T) to Win64
  • Project->ProjectOptions->ConfigAndTarget, 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, Set LCLWidgetType" to Win32
  • Run->Build


Obviously, you cannot 'run' that binary but if one is made, its almost certainly OK.

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 its convenient to set a mode for each compile you want to do. So, here, now you can make Linux64 binaries and Windows 32bit and 64bit 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 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.

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.


Legacy (for now)

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 above heading.


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.

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 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 cross-platform 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.

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.

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

and then

$ su -c "make crossinstall OS_TARGET=win32 CPU_TARGET=i386"
Note-icon.png

Note:
make crossinstall by default installs all files in /usr/local/lib/fpc/$fpcversion/units directory, that's why you will need to add to /etc/fpc.cfg file a new search path:
-Fu/usr/local/lib/fpc/$fpcversion/units/$fpctarget/*.

Another option is to use INSTALL_PREFIX=/usr while performing crossinstall. In this case, you will not need to change anything in /etc/fpc.cfg file because all will be installed in /usr/lib/fpc/$fpcversion/units which is already there

Note-icon.png

Note:
To build for win64 the make command is: make all OS_TARGET=win64 CPU_TARGET=x86_64

The reason for this simplicity is the internal linker included in this version of fpc.

An example under Bunsen Labs (Debian 8)

- 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).

# Navigate to the fpc source folder.
cd /usr/share/fpcsrc/3.0.0

# Compile the cross-compiler.
sudo make clean all OS_TARGET=win32 CPU_TARGET=i386

# Install the cross-compiler.
sudo make crossinstall OS_TARGET=win32 CPU_TARGET=i386 INSTALL_PREFIX=/usr

# Link the cross-compiler and place the link where Lazarus can see it.
sudo ln -sf /usr/lib/fpc/3.0.0/ppcross386 /usr/bin/ppcross386

# 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

- Make sure your cross-compilers are alive: (Note, the links to the cross compiling binaries appear in /usr/bin rather than /usr/lib as shown in the images below).

000-cross-compilers-compiled.png

- Make sure your cross-compilers were properly linked:

000-cross-compilers-linked.png

- Now open Lazarus.

- Find the "Paths" item in the right list of the "Project Options" window. Press Ctrl+Shift+F11 or navigate through the Projects->Project Options->Paths menus.

- For each build, configure your paths so that all necessary libraries are reacheable 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".

002-paths-win32.png

- Create builds and edit build names in the "Build Mode: [BuildName]" window (click in the upper [...] button to open it).

001-build-modes.png

- Click ok when your done.

- Now go to Run -> "Build Many Modes". Press ok. Wait until Lazarus and FPC finishes their work.

003-build-many-modes.png

- Go to your project folder and enjoy!

Write once, compile anywhere.

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

See also