Hi

Back in 2015, I heard how LibreOffice was cross-compiling its Windows build
on Linux+Wine (https://www.winehq.org/), so I decided to try that too.

I tried it a few times before and couldn't get very far. Our Windows build
uses Cygwin, and Cygwin didn't work on Wine for a long time (very old
versions of Cygwin work, but as Cygwin added new features using Windows
APIs that Wine doesn't implement yet, it broke). But after fixing some bugs
(eg. https://bugs.winehq.org/show_bug.cgi?id=48891) and other Wine
improvements, on recent Wine versions Cygwin is functional enough. A .NET 2
installation deadlock on FreeBSD (
https://bugs.winehq.org/show_bug.cgi?id=52447) also discouraged me from
trying it for a long time, but that was fixed in newer Wine versions when I
tested last year.

And so I tried to set up a clean WINEPREFIX, and in it installed Cygwin,
.NET 2, Windows SDK, Java, Ant, NASM, the usual minimal build environment
for Windows. Then started Cygwin with Wine in an XFCE terminal, and
followed the usual Windows build steps, and lo and behold, it actually
started building!

The build then broke in a number of places, but I was able to fix or work
around the issues.

-----------------------
Bugs in Wine
-----------------------
1. curl build failed with some error like "1: invalid command". Wine's
cmd.exe had a bug where a "2<&1" redirection in Curl's build script was
getting parsed incorrectly. I fixed it and my patch to Wine was merged (
https://bugs.winehq.org/show_bug.cgi?id=57795).

2. libxml2 build failed. It turned out that the build does "cscript
configure.js", and configure.js calls "vf.WriteBlankLines(1)" which fails
because Wine's scrrun.dll doesn't implement ITextStream::WriteBlankLines(),
so it returns the COM E_NOTIMPL error code, which becomes an exception that
causes the script to fail. I fixed that too and it's been merged (
https://bugs.winehq.org/show_bug.cgi?id=57810).

3. stoc build failed because xml2cmp crashed, and shell build failed
because lngconvex crashed, both due to the same reason: those tools find
the size of a file by seeking to the end, reading the current offset, and
then seeking back to the beginning (did they never learn how the stat(2)
system call lets you find a file's size easier and faster?), but Wine's
msvcp90.dll has a bug where ::std::ifstream::seekg() crashes. I initially
worked around that in AOO by calling seekg(0, ::std::ios::beg) instead, but
also reported it to Wine, and a few days ago other Wine developers fixed it
(https://bugs.winehq.org/show_bug.cgi?id=57817). It was a bug in Wine's
msvcp90.dll where a wrong structure field alignment caused the wrong
calling convention to be used for the function call.

4. postprocess build experienced 2 crashes in rebase due to an
unimplemented function in Wine.
---snip---
rebase -i ../wntmsci12.pro/misc/coffbase.txt  -e 10000 -l ../
wntmsci12.pro/misc/rebase_log.txt -R C:/AOO/main/solver/450/
wntmsci12.pro/bin -N no_rebase.txt  -v @../
wntmsci12.pro/misc/rebase_again.txt
wine: Call from 7BD748A5 to unimplemented function
imagehlp.dll.ReBaseImage64, aborting
wine: Unimplemented function imagehlp.dll.ReBaseImage64 called at address
7BD748A5 (thread 0688), starting debugger...
---snip---
However this does not break the build: when you close the popup dialog, it
continues building.

5. instsetoo_native build broke due to missing makecab.exe. Strictly
speaking, this is a Wine bug, because makecab.exe is a standard part of
Windows, so Wine should supply its own implementation of it. However
reading the Perl script where this is called, I saw this is only necessary
in some types of packaging (eg. MSI), yet the script fails even when using
"--with-package-format=installed" which doesn't need it. Therefore, for
now, I hacked the script to not check for it (attached).

6. insetstoo_native also breaks the first time it's run, due to some kind
of memory depletion:
     12 [main] perl 1440 child_copy: user heap read copy failed,
0x20000000..0x24390000, done 0, windows pid 1440, Win32 error 8
    550 [main] perl 1440 child_copy: data read copy failed,
0x402000..0x402044, done 0, windows pid 1440, Win32 error 5

To work around this and complete the build, Cygwin has to be exited,
"wineserver -k" run to kill all Wine instances, and then Cygwin has to be
started up again, and that module cleaned and rebuilt:
cd /cygdrive/c/AOO/main/instsetoo_native
rm -rf wntmsci12.pro
build --from instsetoo_native
This needs further investigation.

----------------------
Bugs in AOO
----------------------
I was doing a Win32 build of the windows-amd64 branch (with some extra
changes I haven't pushed yet), because I wanted to check if my
Windows/AMD64 changes caused any regressions in the Win32 build. And
indeed, I found and fixed some AOO regressions in that branch:

1. connectivity build of drivers/ado failed:
---snip---
[ build CXX ] connectivity/source/drivers/ado/AResultSet
AResultSet.cxx
c:/AOO/main/connectivity/source/drivers/ado/AResultSet.cxx(278) : error
C2664: 'Recordset15::get_AbsolutePosition' : cannot convert parameter 1
from 'sal_IntPtr *' to 'PositionEnum_Param *'
        Types pointed to are unrelated; conversion requires
reinterpret_cast, C-style cast or function-style cast
---snip---

Fixed that in commit 3f8f95abcd8c826bf60b52dcebad0438358a3937 by changing
the variable type to PositionEnum_Param in 2 places. The stupid data type
is an integer on Win64, and an enum on Win32, go figure.

2. scp2 build broke because it couldn't find ssl3.dll and crypto3.dll (from
OpenSSL). The issue there was that my scp2 changes expect the processor
type to be added to SCPDEFS, but the makefile never did so. When I fixed
that in commit ceb9a1253b970a2e0a3eb6c1810f24dfbb12ce38, it started using
the correct names, libssl-3.dll and libcrypto-3.dll.

-------------
Results
-------------
And after the above fixes and workarounds, the build finished successfully!
The resulting AOO binaries run in Wine, run on Windows, documents load,
unit tests pass, and it all works!

The build works reasonably well. Wine prints out some harmless warnings,
such as:
---snip---
01d8:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not
implemented!
03e0:fixme:msvcrt:__clean_type_info_names_internal (7B93C39C) stub
043c:fixme:file:NtSetInformationFile Unsupported class (16)
---snip---
because Cygwin and/or Microsoft's tools use the Windows API in dirty ways,
calling undocumented NTDLL functions, which Wine doesn't fully support. But
this doesn't cause any problems, and these warnings can be hidden by
setting the environment variable WINEDEBUG="fixme-all".

I hoped that the build would be faster, because the NTFS filesystem is slow
with many small files. Unfortunately that's not the case, "time" shows it's
even slower:
real    880m47.204s
user    176m38.196s
sys     212m19.741s
which is almost 15 hours, on a build without precompiled headers (and other
sub-optimal conditions).

--------------------------
Reflection
--------------------------
While making these fixes I kept wondering, why am I having these issues?
Back when LibreOffice did this, surely they would have submitted these bug
fixes to Wine? Did they avoid the Wine bugs somehow, such as by porting
curl and libxml2 to gbuild, which could be building them differently? Also
surely back in 2015 Wine was even less complete and more buggy?

I went and had a look at the original email where I read about LibreOffice
using Wine, and realized I misunderstood. The exact words in the email were
"according to past FOSDEM talk, LibreOffice developers cross-compile Win32
version of LibreOffice on Linux and test on Wine." That sounds more like
LibreOffice was only getting tested on Wine, not compiled on Wine. We've
now gone even further, and can both compile and test on Wine ;-).

And how is building on Wine useful?
- For those of us that develop on *nix (Linux, FreeBSD, possibly macOS), it
is much easier to build Windows versions. No separate machine necessary, no
extra memory and storage necessary, no separate operating system to
maintain.
- The build runs in a native terminal and supports native keyboard
shortcuts, not that annoying Cygwin window where you have to Ctrl+Insert to
copy and Shirt+Insert to paste.
- You should even be able to use the same source tree to build both *nix
and Windows binaries - they just go to different subdirectories (eg.
main/solver/450/wntmsci12.pro and main/solver/450/unxlngx6.pro).
- It is already so accurately emulating Windows APIs that it has helped to
find and fix bugs in the Windows build.
- Wine's very design makes deployment and administration of Windows
applications easier. Each WINEPREFIX directory fully contains all the state
of all the applications installed in it, with its own "Windows registry" in
.inf files, its own "C:" drive directory, etc. So just that one WINEPREFIX
directory can be copied, moved, backed up, etc., and all the applications
will go with it.
- When using cloud computing, it could be cheaper, eg. Github charges more
money for Windows VMs than Linux VMs.

Compared to other cross-compilation approaches, this is a "purer" build
than other ways of cross-compiling to Windows, much more representative of
what the build would do on real Windows:
- Both mingw-w64 and Clang can compile Windows binaries on *nix, but they
don't use the original Microsoft header files, and may lack DirectX, ATL
and other headers.
- Also I heard somewhere that debug info with MSVC was better than other
compilers (but not sure if that's still the case).
- We do have mingw build support in our source tree. It hasn't been used or
maintained in a long time, and it's unlikely to still work. It might offer
the ability to build from the *nix shell directly, without Cygwin, and thus
build faster. However its use must have involved enormous work: many
makefiles have special sections for it, many source files check for mingw,
we have a custom UNO bridge for it, etc. Since it's so different from the
MSVC build, both would need testing after changes are made. By comparison
the Wine+MSVC approach is minimally invasive, and requires very few changes
to the existing build.
- Subtle differences in how different compilers work mean that just because
one compiler works, doesn't mean another will. For example the old MSVC we
use may error out on code that compiles in Clang and mingw. Such bugs will
almost certainly reappear when running it on Wine, but will be hidden when
using another compiler.

---------------------------------
MS Windows SDK 7.1
---------------------------------
In a separate test, I tried to build in Wine using a newer MS Windows SDK.
We currently use "Microsoft Windows SDK for Windows 7 and .NET Framework
3.5 SP1", which is version 7.0 as per
https://en.wikipedia.org/wiki/Microsoft_Windows_SDK. I tried 7.1, the
"Microsoft Windows SDK for Windows 7 and .NET Framework 4". This was tried
before my successful build with 7.0, and it also encountered the curl and
libxml2 bugs (which I later fixed), as well as some new bugs. Python failed
to build, probably because python uses vcbuild, and msbuild replaced
vcbuild in that new SDK, so maybe the new SDK even requires Python 3. The
icu and gtest modules have strange header file errors, maybe they need to
be updated. And libtextcat failed because of missing msvcr100.dll, so it
sounds like quite a lot of changes would be needed to get the 7.1 SDK
working.

---------------------
Instructions
---------------------
To create the build environment:
1. Install the very latest version of Wine. You'll need to compile the
latest Git from source, or wait for development version 10.2 to be released
on Friday 21 February 2025 (and possibly a few more days for Linux
distributions to package it). I've only used the 32 bit Wine.
2. Export a WINEPREFIX variable set to some directory where the environment
will live:
export WINEPREFIX=/home/user/.wine-aoo-build
Make sure you do this in any new terminal/tab you open, where you plan to
run Wine on that environment.
3. Create that WINEPREFIX by running any Wine tool, eg. "winecfg". When it
asks to install Mono, click "No", Mono isn't good enough to replace .NET 2
for the Windows SDK. Configure any options in the winecfg window, then
click "OK" to close it.
4. Get winetricks (Linux distribution packages usually have it), and
install .NET 2 with:
winetricks dotnet20
It's needed for the Windows SDK.
5. Install the Windows SDK. Loop mount the ISO file onto a directory, and
in that directory run:
wine setup.exe
6. Install Cygwin. On https://cygwin.org/install.html scroll down, and use
the 32 bit Cygwin link, and these kind of options:
wine setup-x86.exe --no-verify --allow-unsupported-windows
and use one of the 32 bit URL links on that page. The setup screen looks
distorted on Wine, with widgets overlapping and sizes wrong, and is just
barely functional enough to select and install required packages.
7. Install the other Windows dependencies, preferably somewhere in that
WINEPREFIX directory.

To build in Wine:
(This will start Cygwin in the current terminal tab:)
cd $WINEPREFIX/drive_c/cygwin/bin
wine bash.exe
export PATH="/cygdrive/c/cygwin/bin:$PATH"
(if your AOO source is in $WINEPREFIX/drive_c/AOO:)
cd /cygdrive/c/AOO/main
(apply the attached patch:)
patch -p1 < no-makecab.patch
(and build as usual:)
./configure ....
./bootstrap
source winenv.set.sh
cd instsetoo_native
build --all -P2 -- -P2

Regards
Damjan
diff --git a/main/solenv/bin/modules/installer/control.pm b/main/solenv/bin/modules/installer/control.pm
index 9cf2715f50..008c1e245b 100644
--- a/main/solenv/bin/modules/installer/control.pm
+++ b/main/solenv/bin/modules/installer/control.pm
@@ -76,7 +76,7 @@ sub check_system_path
 {
 	# The following files have to be found in the environment variable PATH
 	# All platforms: zip
-	# Windows only: "msiinfo.exe", "msidb.exe", "uuidgen.exe", "makecab.exe", "msitran.exe", "expand.exe" for msi database and packaging
+	# Windows only: "msiinfo.exe", "msidb.exe", "uuidgen.exe", "msitran.exe", "expand.exe" for msi database and packaging
 
 	my $onefile;	
 	my $error = 0;
@@ -102,7 +102,7 @@ sub check_system_path
 
 	if (($installer::globals::iswin) && ($installer::globals::iswindowsbuild))
 	{
-		@needed_files_in_path = ("zip.exe", "msiinfo.exe", "msidb.exe", "uuidgen.exe", "makecab.exe", "msitran.exe", "expand.exe");
+		@needed_files_in_path = ("zip.exe", "msiinfo.exe", "msidb.exe", "uuidgen.exe", "msitran.exe", "expand.exe");
 	}
 	elsif ($installer::globals::iswin || $installer::globals::isos2)
 	{	
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@openoffice.apache.org
For additional commands, e-mail: dev-h...@openoffice.apache.org

Reply via email to