gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
#!/usr/bin/python3
################################################################################
2025-12-26 07:17:13 -08:00
# Small python script to retrieve DLL or DYLIB dependencies with object dumper
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
################################################################################
################################################################################
# Usage example
#
2025-12-26 07:17:13 -08:00
# python3 lib_bundle.py /path/to/run.exe /winenv/ /path/install
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
#
Fix typos
Found via:
```
codespell -q 3 -S ./ChangeLog*,*.po,./.git,./NEWS* -L als,ang,ba,chello,daa,doubleclick,foto,hist,iff,inport,klass,mut,nd,ower,paeth,params,pard,pevent,sinc,thru,tim,uint
```
2020-11-05 08:43:53 -08:00
# In this case, the DLL dependencies for executable run.exe will be extracted
# and copied into /path/install/bin folder. To copy the DLL, the root path to
2025-12-26 07:17:13 -08:00
# Windows environnment should be passed, here /winenv/ (usually MSYSTEM_PREFIX).
#
# python3 lib_bundle.py /path/to/run /macOSenv/ /path/install
#
# In this case, the DYLIB dependencies for executable run will be extracted
2025-12-29 07:41:09 -08:00
# and copied into /path/install/Frameworks folder. To copy the DYLIB,
2025-12-26 07:17:13 -08:00
# the root path to macOS environnment should be passed, here /macOSenv/ (usually /opt/local).
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
2020-10-20 08:58:56 -07:00
import argparse
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
import os
import subprocess
import re
import shutil
2020-10-20 08:58:56 -07:00
import sys
2025-04-25 06:11:13 -07:00
import struct
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
################################################################################
# Global variables
2025-12-26 07:17:13 -08:00
# Sets for executable and system DLL/DYLIB
2020-10-20 08:58:56 -07:00
dlls = set ( )
sys_dlls = set ( )
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
2025-12-26 07:17:13 -08:00
# Previously done DLLs/DYLIBs in previous runs
2022-02-20 16:23:37 -08:00
done_dlls = set ( )
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
# Common paths
2020-10-20 08:58:56 -07:00
bindir = ' bin '
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
2025-12-26 07:17:13 -08:00
# Platform mode (detected at runtime)
is_macos = False
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
################################################################################
# Functions
# Main function
2022-02-20 16:23:37 -08:00
def main ( binary , srcdirs , destdir , debug , dll_file ) :
global done_dlls
2025-12-26 07:17:13 -08:00
global is_macos
2022-02-20 16:23:37 -08:00
try :
if dll_file is not None :
with open ( dll_file , ' r ' ) as f :
done_dlls = { line . strip ( ) for line in f if len ( line . strip ( ) ) != 0 }
except FileNotFoundError :
pass
2024-06-13 04:47:40 -07:00
#sys.stdout.write("{} (INFO): searching for dependencies of {} in {}.\n".format(os.path.basename(__file__),
# binary, ', '.join(srcdirs)))
2021-10-04 07:26:50 -07:00
find_dependencies ( os . path . abspath ( binary ) , srcdirs )
2022-03-29 07:25:17 -07:00
if debug in [ ' debug-only ' , ' debug-run ' ] :
if debug == ' debug-only ' :
2025-12-26 07:17:13 -08:00
print ( " Running in debug-only mode (no DLL/DYLIB moved) for ' {} ' " . format ( binary ) )
2022-03-29 07:25:17 -07:00
else :
print ( " Running with debug output for ' {} ' " . format ( binary ) )
2020-10-20 08:58:56 -07:00
if len ( dlls ) > 0 :
2025-12-26 07:17:13 -08:00
sys . stdout . write ( " Needed DLLs/DYLIBs: \n \t - " )
2020-10-20 08:58:56 -07:00
sys . stdout . write ( " \n \t - " . join ( list ( dlls ) ) )
print ( )
if len ( sys_dlls ) > 0 :
2025-12-26 07:17:13 -08:00
sys . stdout . write ( ' System DLLs/DYLIBs: \n \t - ' )
2020-10-20 08:58:56 -07:00
sys . stdout . write ( " \n \t - " . join ( sys_dlls ) )
print ( )
removed_dlls = sys_dlls & dlls
if len ( removed_dlls ) > 0 :
2025-12-26 07:17:13 -08:00
sys . stdout . write ( ' Non installed DLLs/DYLIBs: \n \t - ' )
2020-10-20 08:58:56 -07:00
sys . stdout . write ( " \n \t - " . join ( removed_dlls ) )
print ( )
installed_dlls = dlls - sys_dlls
if len ( installed_dlls ) > 0 :
2025-12-26 07:17:13 -08:00
sys . stdout . write ( ' Installed DLLs/DYLIBs: \n \t - ' )
2020-10-20 08:58:56 -07:00
sys . stdout . write ( " \n \t - " . join ( installed_dlls ) )
print ( )
2022-03-29 07:25:17 -07:00
if debug == ' debug-run ' :
copy_dlls ( dlls - sys_dlls , srcdirs , destdir )
2020-10-20 08:58:56 -07:00
else :
2021-10-04 07:26:50 -07:00
copy_dlls ( dlls - sys_dlls , srcdirs , destdir )
2020-10-20 08:58:56 -07:00
2022-02-20 16:23:37 -08:00
if dll_file is not None :
with open ( dll_file , ' w ' ) as f :
f . write ( " \n " . join ( set . union ( done_dlls , dlls , sys_dlls ) ) )
2021-10-04 07:26:50 -07:00
def find_dependencies ( obj , srcdirs ) :
2020-10-20 08:58:56 -07:00
'''
2025-12-26 07:17:13 -08:00
List DLLs / DYLIBs of an object file in a recursive way .
2020-10-20 08:58:56 -07:00
'''
2025-12-26 07:17:13 -08:00
global is_macos
2022-02-20 16:23:37 -08:00
if obj in set . union ( done_dlls , sys_dlls ) :
# Already processed, either in a previous run of the script
# (done_dlls) or in this one.
return True
2021-10-04 07:26:50 -07:00
if not os . path . isabs ( obj ) :
for srcdir in srcdirs :
2025-12-26 07:17:13 -08:00
bindir = ' bin '
if is_macos :
bindir = ' lib '
2021-10-04 07:26:50 -07:00
abs_dll = os . path . join ( srcdir , bindir , obj )
abs_dll = os . path . abspath ( abs_dll )
if find_dependencies ( abs_dll , srcdirs ) :
return True
else :
2025-12-26 07:17:13 -08:00
# Found in none of the srcdirs: consider it a system DLL/DYLIB
2021-10-04 07:26:50 -07:00
sys_dlls . add ( os . path . basename ( obj ) )
return False
elif os . path . exists ( obj ) :
2025-12-26 07:17:13 -08:00
# If DLL/DYLIB exists, extract dependencies.
2020-10-01 17:06:26 -07:00
objdump = None
2025-04-25 06:11:13 -07:00
try :
with open ( obj , ' rb ' ) as f :
f . seek ( 0x3C )
pe_offset = struct . unpack ( ' <I ' , f . read ( 4 ) ) [ 0 ]
f . seek ( pe_offset )
pe_sig = f . read ( 4 )
2025-12-26 07:17:13 -08:00
if pe_sig == b ' PE \0 \0 ' :
f . seek ( pe_offset + 24 )
file_type = struct . unpack ( ' <H ' , f . read ( 2 ) ) [ 0 ]
if file_type == 0x20b :
objdump = ' x86_64-w64-mingw32-objdump '
elif file_type == 0x10b :
objdump = ' i686-w64-mingw32-objdump '
else :
return None
2025-04-25 06:11:13 -07:00
else :
2025-12-26 07:17:13 -08:00
# If not PE, then check for Mach-O magic (Feed Face/Face Feed variants)
f . seek ( 0 ) #rewind to start
magic = f . read ( 4 )
if magic in [ b ' \xfe \xed \xfa \xce ' , b ' \xfe \xed \xfa \xcf ' ,
b ' \xce \xfa \xed \xfe ' , b ' \xcf \xfa \xed \xfe ' ,
b ' \xca \xfe \xba \xbe ' , b ' \xbe \xba \xfe \xca ' ] :
is_macos = True
objdump = ' no_cross_objdump '
else :
return None
2025-04-25 06:11:13 -07:00
except Exception as e :
2025-12-26 07:17:13 -08:00
sys . stderr . write ( " Cannot read PE/Mach-O header for {} : {} \n " . format ( obj , e ) )
2025-04-25 06:11:13 -07:00
return None
2020-10-01 17:06:26 -07:00
if objdump is None :
2020-10-20 08:58:56 -07:00
sys . stderr . write ( ' File type of {} unknown: {} \n ' . format ( obj , file_type ) )
2021-10-10 04:47:15 -07:00
sys . exit ( 1 )
gitlab-ci, build: CI job to package GIMP on Windows from MSYS2 build.
This new job resulted in a package which allows to run GIMP on Windows
(as tested in a VM; at least it starts, I can create a new canvas and
paint). Of course I think this will need to be tweaked a little bit
more, as I'm sure we miss things here and there.
At the very least, even though I add the Python and Luajit binaries,
GIMP on Windows didn't find them. This will need to be investigated.
Also it looks like opening from a remote location may not work. Not sure
if this about a missing GIO module or maybe something which works
differently on Windows (I was not even able to drag'n drop from the
browser!). Anyway this needs to be looked at as well.
Note that gdk-pixbuf-query-loaders is apparently unneeded when GIMP is
built this way (unlike with our crossroad build).
All this to say that this is still an early attempt to full CI build for
Windows.
It doesn't invalidate the crossroad build, because cross-compilation
builds from Linux will always stay very important for Linux developers
to be able to easily fix Windows bugs too; yet the crossroad build has 2
major issues:
1. We haven't figured out yet how to run GObject Introspection tools for
cross-builds, so the crossroad builds are not full-featured (and this
is quite a major feature we are missing!).
2. Also I will want to run the installer in the CI at some point and the
one we use can only run on Windows itself AFAIK. We could try to run
it through Wine, but still anyway the point 1. is already quite a
blocker, let's do the simple thing.
Note that we will likely want to move to meson for this build, because
autotools is very slow on Windows. But as long as the few blocker meson
bugs are not fixed, let's stick to the slow yet good build.
2021-05-06 09:40:37 -07:00
elif shutil . which ( objdump ) is None :
# For native objdump case.
2025-12-26 08:58:47 -08:00
objdump = ' objdump '
if not is_macos and shutil . which ( ' dumpbin ' ) :
objdump = ' dumpbin '
2025-12-26 07:17:13 -08:00
if is_macos and shutil . which ( ' otool ' ) :
objdump = ' otool '
gitlab-ci, build: CI job to package GIMP on Windows from MSYS2 build.
This new job resulted in a package which allows to run GIMP on Windows
(as tested in a VM; at least it starts, I can create a new canvas and
paint). Of course I think this will need to be tweaked a little bit
more, as I'm sure we miss things here and there.
At the very least, even though I add the Python and Luajit binaries,
GIMP on Windows didn't find them. This will need to be investigated.
Also it looks like opening from a remote location may not work. Not sure
if this about a missing GIO module or maybe something which works
differently on Windows (I was not even able to drag'n drop from the
browser!). Anyway this needs to be looked at as well.
Note that gdk-pixbuf-query-loaders is apparently unneeded when GIMP is
built this way (unlike with our crossroad build).
All this to say that this is still an early attempt to full CI build for
Windows.
It doesn't invalidate the crossroad build, because cross-compilation
builds from Linux will always stay very important for Linux developers
to be able to easily fix Windows bugs too; yet the crossroad build has 2
major issues:
1. We haven't figured out yet how to run GObject Introspection tools for
cross-builds, so the crossroad builds are not full-featured (and this
is quite a major feature we are missing!).
2. Also I will want to run the installer in the CI at some point and the
one we use can only run on Windows itself AFAIK. We could try to run
it through Wine, but still anyway the point 1. is already quite a
blocker, let's do the simple thing.
Note that we will likely want to move to meson for this build, because
autotools is very slow on Windows. But as long as the few blocker meson
bugs are not fixed, let's stick to the slow yet good build.
2021-05-06 09:40:37 -07:00
if shutil . which ( objdump ) is None :
sys . stderr . write ( " Executable doesn ' t exist: {} \n " . format ( objdump ) )
2021-10-10 04:47:15 -07:00
sys . exit ( 1 )
2020-10-01 17:06:26 -07:00
2025-12-26 07:17:13 -08:00
if not is_macos :
2025-12-26 08:58:47 -08:00
if objdump == ' dumpbin ' :
result = subprocess . run ( [ objdump , ' /DEPENDENTS ' , obj ] , stdout = subprocess . PIPE )
out = result . stdout . decode ( ' utf-8 ' )
regex = re . finditer ( r ' \ b([ \ w \ -.]+ \ .dll) \ b ' , out , re . MULTILINE )
else :
result = subprocess . run ( [ objdump , ' -p ' , obj ] , stdout = subprocess . PIPE )
out = result . stdout . decode ( ' utf-8 ' )
regex = re . finditer ( r " DLL Name: *( \ S+.dll) " , out , re . MULTILINE )
2025-12-26 07:17:13 -08:00
else :
if objdump == ' otool ' :
result = subprocess . run ( [ ' otool ' , ' -l ' , obj ] , stdout = subprocess . PIPE )
else :
result = subprocess . run ( [ ' objdump ' , ' --macho ' , ' --private-headers ' , obj ] , stdout = subprocess . PIPE )
out = result . stdout . decode ( ' utf-8 ' , errors = ' replace ' )
regex = re . finditer ( r " name \ s+(?: \ d+ \ s+)?(?:.*/)?([^/ \ s]+ \ .dylib) " , out , re . MULTILINE )
# Parse lines with DLL/DYLIB Name instead of lib*.dll/lib*.dylib directly
for match in regex :
2020-10-20 08:58:56 -07:00
dll = match . group ( 1 )
2022-02-20 16:23:37 -08:00
if dll not in set . union ( done_dlls , dlls , sys_dlls ) :
2020-10-20 08:58:56 -07:00
dlls . add ( dll )
2021-10-04 07:26:50 -07:00
find_dependencies ( dll , srcdirs )
return True
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
else :
2021-10-04 07:26:50 -07:00
return False
2020-10-20 08:58:56 -07:00
2025-12-29 07:41:09 -08:00
# Copy a DLL/DYLIB set into the /destdir/bin or /destdir/Frameworks directory
2021-10-04 07:26:50 -07:00
def copy_dlls ( dll_list , srcdirs , destdir ) :
2025-12-26 07:17:13 -08:00
global bindir
dest_bindir = bindir
if is_macos :
2025-12-29 07:41:09 -08:00
dest_bindir = ' Frameworks '
2025-12-26 07:17:13 -08:00
destbin = os . path . join ( destdir , dest_bindir )
2020-10-20 08:58:56 -07:00
os . makedirs ( destbin , exist_ok = True )
2025-12-29 07:49:16 -08:00
if is_macos :
abs_binary = os . path . abspath ( args . bin )
abs_destdir = os . path . abspath ( destdir )
if abs_binary . startswith ( abs_destdir + os . sep ) :
2026-01-05 17:20:03 -08:00
#check_macos_version(abs_binary)
2025-12-29 07:49:16 -08:00
set_rpath ( abs_binary , destbin )
2020-10-20 08:58:56 -07:00
for dll in dll_list :
2021-10-04 07:26:50 -07:00
if not os . path . exists ( os . path . join ( destbin , dll ) ) :
# Do not overwrite existing files.
for srcdir in srcdirs :
2025-12-26 07:17:13 -08:00
if is_macos :
bindir = ' lib '
2021-10-04 07:26:50 -07:00
full_file_name = os . path . join ( srcdir , bindir , dll )
if os . path . isfile ( full_file_name ) :
2026-01-05 17:20:03 -08:00
if is_macos and not full_file_name . startswith ( ( " /opt/local/ " , " /opt/homebrew/ " ) ) :
check_macos_version ( full_file_name )
build/windows: Port building scripts to use Windows native shell
From now, Windows contributors can use the default shell provided by the OS
(which is PS since Windows build 10.0.14971.0), like Linux and macOS users do.
We still use MSYS2 but not the POSIX shell. This change adds these features:
'1_build-deps-msys2.sh' is now '1_build-deps-msys2.ps1'
- Faster clonning by using 'git-scm' (the MSYS2 one had performance problems)
- Easier to use non-MSYS2 binaries, not only 'git-scm' but also official meson,
deps from vckpg etc. This is a needed step towards the future use of MSVC.
'2_build-gimp-msys2.sh' is now '2_build-gimp-msys2.ps1'
- By default, vanilla builds (normally triggered on PS) will create a bundle,
dropping the need of 'gimp.cmd' (that adressed .typelib and .interp limits),
which is inline with other (Cmake-based) projects like Darktable and Inkscape.
This change is important because even Windows devs more experienced than me
get confused with the relocatibility stuff, which is the default on Windows.
'2_bundle-gimp-uni_base.sh'
- As a result of the change above, bundling code was changed to be a bit faster.
It still is, however, painfully slow, since meson doesn't have a 'install()'
function like Cmake to prepend targets in Ninja's 'install_manifest.txt'.
Since we are not using a POSIX shell (nor 'mintty') anymore:
- GIMP can be built from every path easily with R. Click "Open Terminal", with
IDE integrations etc, without needing to manual tweak MSYS2 .ini files etc.
We could tweak MSYS2 to get the features above but not top-tier integration.
- Developers can be more aware of Windows native vars, paths etc, and avoid bugs
Some build files were improved to support the 'Windows way of doing things'.
- No need to close and reopen terminal anymore after running 'pacman -Suy'!
---
REGRESSION: Vala plug-ins are temporarely gone due to 'vapigen' bugs, a small
regression since this is a gnomeish language but I will investigate how to fix.
2024-10-17 03:45:43 -07:00
sys . stdout . write ( " Bundling {} to {} \n " . format ( full_file_name , destbin ) )
2021-10-04 07:26:50 -07:00
shutil . copy ( full_file_name , destbin )
2025-12-29 07:49:16 -08:00
if is_macos :
2025-12-30 14:07:54 -08:00
set_rpath ( os . path . join ( destbin , dll ) , destbin )
2021-10-04 07:26:50 -07:00
break
else :
# This should not happen. We determined that the dll is in one
# of the srcdirs.
2025-12-26 07:17:13 -08:00
sys . stderr . write ( " Missing DLL/DYLIB: {} \n " . format ( dll ) )
2021-10-10 04:47:15 -07:00
sys . exit ( 1 )
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
2026-01-05 17:20:03 -08:00
def check_macos_version ( binary ) :
"""
Check LC_BUILD_VERSION for compatibility with MACOSX_DEPLOYMENT_TARGET
"""
supported_minos = os . environ . get ( " MACOSX_DEPLOYMENT_TARGET " )
if not supported_minos :
return
result = subprocess . run ( [ ' otool ' , ' -l ' , binary ] , stdout = subprocess . PIPE , check = True )
out = result . stdout . decode ( ' utf-8 ' , errors = ' replace ' )
bin_minos = re . findall ( r ' minos \ s+([ \ d \ .]+) ' , out ) [ 0 ]
if tuple ( map ( int , bin_minos . split ( ' . ' ) ) ) > tuple ( map ( int , supported_minos . split ( ' . ' ) ) ) :
sys . stderr . write ( f " \033 [31m(ERROR) \033 [0m: { binary } requires macOS { bin_minos } , which is higher than macOS { supported_minos } which GIMP was built against. \n " )
sys . exit ( 1 )
2025-12-29 07:49:16 -08:00
def set_rpath ( binary , destbin = None ) :
"""
Remove all LC_RPATH entries except those identical to relative destbin ,
and set it if not . Also rewrite LC_LOAD_DYLIB and LC_ID_DYLIB to use it .
"""
result = subprocess . run ( [ ' otool ' , ' -l ' , binary ] , stdout = subprocess . PIPE )
out = result . stdout . decode ( ' utf-8 ' , errors = ' replace ' )
2025-12-30 14:07:54 -08:00
# Handle LC_RPATH (only on executables as a rule)
if ( " .dylib " not in binary and " .so " not in binary ) or " cmd LC_RPATH " in out :
2025-12-29 07:49:16 -08:00
regex = re . findall ( r ' path (.+?) \ (offset ' , out )
new_rpath = os . path . join ( " @executable_path " , os . path . relpath ( destbin , os . path . dirname ( os . path . abspath ( binary ) ) ) )
for old_rpath in regex :
if old_rpath == new_rpath :
continue
try :
2025-12-30 04:51:30 -08:00
subprocess . run ( [ ' install_name_tool ' , ' -delete_rpath ' , old_rpath , binary ] , check = True , stderr = subprocess . DEVNULL )
2025-12-29 07:49:16 -08:00
#sys.stdout.write(f"Removed LC_RPATH {old_rpath} from {binary}\n")
except subprocess . CalledProcessError as e :
sys . stderr . write ( f " Failed to remove rpath { old_rpath } from { binary } : { e } \n " )
if new_rpath not in regex :
try :
2025-12-30 04:51:30 -08:00
subprocess . run ( [ ' install_name_tool ' , ' -add_rpath ' , new_rpath , binary ] , check = True , stderr = subprocess . DEVNULL )
2025-12-29 07:49:16 -08:00
#sys.stdout.write(f"Added LC_RPATH {new_rpath} to {binary}\n")
except subprocess . CalledProcessError as e :
sys . stderr . write ( f " Failed to add rpath { new_rpath } to { binary } : { e } \n " )
2025-12-30 14:07:54 -08:00
# Handle LC_LOAD_DYLIB (on executables, shared libraries)
2025-12-29 07:49:16 -08:00
regex = re . findall ( r ' name (.+?) \ (offset ' , out )
for old_dylib_path in regex :
if old_dylib_path . startswith ( " /usr " ) or old_dylib_path . startswith ( " /System " ) :
continue
2026-01-03 10:09:27 -08:00
if " .framework " in old_dylib_path :
new_dylib_path = os . path . join ( " @rpath " , old_dylib_path [ old_dylib_path . rfind ( " / " , 0 , old_dylib_path . find ( " .framework " ) ) + 1 : ] )
else :
new_dylib_path = os . path . join ( " @rpath " , os . path . basename ( old_dylib_path ) )
2025-12-29 07:49:16 -08:00
if old_dylib_path != new_dylib_path :
try :
2025-12-30 04:51:30 -08:00
subprocess . run ( [ ' install_name_tool ' , ' -change ' , old_dylib_path , new_dylib_path , binary ] , check = True , stderr = subprocess . DEVNULL )
2025-12-29 07:49:16 -08:00
#sys.stdout.write(f"Rewrote LC_LOAD_DYLIB {old_dylib_path} -> {new_dylib_path}\n")
except subprocess . CalledProcessError as e :
sys . stderr . write ( f " Failed to rewrite LC_LOAD_DYLIB { old_dylib_path } : { e } \n " )
# Handle LC_ID_DYLIB (only on shared libraries)
regex = re . search ( r ' cmd LC_ID_DYLIB.*? \ n \ s*name (.+?) \ (offset ' , out , re . DOTALL )
if ( " .dylib " in binary or " .so " in binary ) and regex :
old_dylib_path = regex . group ( 1 ) . strip ( )
new_dylib_path = os . path . join ( " @rpath " , os . path . basename ( old_dylib_path ) )
if old_dylib_path != new_dylib_path :
try :
2025-12-30 04:51:30 -08:00
subprocess . run ( [ ' install_name_tool ' , ' -id ' , new_dylib_path , binary ] , check = True , stderr = subprocess . DEVNULL )
2025-12-29 07:49:16 -08:00
#sys.stdout.write(f"Rewrote LC_ID_DYLIB {old_dylib_path} -> {new_dylib_path}\n")
except subprocess . CalledProcessError as e :
sys . stderr . write ( f " Failed to rewrite LC_ID_DYLIB { old_dylib_path } : { e } \n " )
2025-12-30 10:36:06 -08:00
# Normalize signature after all the changes
2025-12-30 04:51:30 -08:00
result = subprocess . run ( [ " codesign " , " -v " , binary ] , capture_output = True , text = True )
if " invalid signature " in result . stdout + result . stderr :
try :
subprocess . run ( [ " codesign " , " --sign " , " - " , " --force " , " --preserve-metadata=entitlements,requirements,flags,runtime " , binary ] , check = True , stderr = subprocess . DEVNULL )
#sys.stdout.write(f"Re-signed {binary}\n")
except subprocess . CalledProcessError as e :
2025-12-30 10:36:06 -08:00
sys . stderr . write ( f " Failed to ad-hoc sign { binary } : { e } \n " )
2025-12-30 04:51:30 -08:00
gitlab, build: Win32 distribution jobs for our CI.
The main purpose of these jobs is to only package the strict necessary
for a working GIMP under Windows, i.e. getting rid of all unnecessary
executables, and inspecting binary dependencies recursively to only
package used DLLs.
The dll_link.py script is taken from Siril codebase (see commit a86e82a8
on Siril repository, by FlorianBen). This was a very nice idea, and
makes for much smaller test archive (Siril is also GPLv3 so licensing is
ok for the reuse, also anyway it's just a small independent build
script).
Moreover having it as a separate job allows to have artifacts with only
the finale distribution (artifacts on the build job also have the build
directory and the whole prefix, which we want to keep in order to debug
when needed).
Hopefully I am not missing anything. Siril seems to package more, like
various gdk-pixbuf-*.exe, gspawn-*.exe and gdbus.exe. I am wondering if
these are actually necessary. I could run GIMP fine without these in
quick tests, but I guess I'll have to investigate a bit more to figure
this out. That's what nightly builds are for, after all, so hopefully
people will report if we miss some runtime dependencies.
2020-09-29 10:38:23 -07:00
if __name__ == " __main__ " :
2020-10-20 08:58:56 -07:00
parser = argparse . ArgumentParser ( )
2022-03-29 07:25:17 -07:00
parser . add_argument ( ' --debug ' , dest = ' debug ' ,
choices = [ ' debug-only ' , ' debug-run ' , ' run-only ' ] , default = ' run-only ' )
2022-02-20 16:23:37 -08:00
parser . add_argument ( ' --output-dll-list ' , dest = ' dll_file ' , action = ' store ' , default = None )
2020-10-20 08:58:56 -07:00
parser . add_argument ( ' bin ' )
2021-10-04 07:26:50 -07:00
parser . add_argument ( ' src ' , nargs = ' + ' )
2020-10-20 08:58:56 -07:00
parser . add_argument ( ' dest ' )
args = parser . parse_args ( sys . argv [ 1 : ] )
2022-02-20 16:23:37 -08:00
main ( args . bin , args . src , args . dest , args . debug , args . dll_file )