Repository: pyinstaller/pyinstaller

Issue:
PyInstaller v5.11.0 build (with splash screen) runs on Linux but not on Windows
I ran the same PyInstaller version 5.11.0 on the spec listed below on Windows and Linux:

```
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
from PyInstaller.utils.hooks import collect_all

datas = []
binaries = []
hiddenimports = ['pyi_splash']
datas += collect_data_files('src')
datas += collect_data_files('kb_ftd2xx')
datas += collect_data_files('tkinter')
tmp_ret = collect_all('appcore')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]


block_cipher = None


a = Analysis(
    ['src/Application.py'],
    pathex=[],
    binaries=binaries,
    datas=datas,
    hiddenimports=hiddenimports,
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
splash = Splash(
    'src/gui/images/app_with_background.png',
    binaries=a.binaries,
    datas=a.datas,
    text_pos=(35, 320),
    text_size=9,
    text_color='#d3d3d3',
    minify_script=True,
    always_on_top=True,
)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    splash,
    splash.binaries,
    [],
    name='Application',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
```

The resulting Linux binary ran successfully but the Windows binary error-ed out because of splash screen tcl font issues.

![splash_pyinstaller_error](https://github.com/pyinstaller/pyinstaller/assets/82004081/1f320e19-e37b-4ae0-9123-c71f6e622a8a)

A diff of the splash.toc generated on the Windows and Linux contains just one material difference -- a Boolean whose function I do not understand.

First 14 lines of splash.toc generated on Windows. (Take note of `False` on line 14)

```
('C:/repos/app/src/gui/images/app_with_background.png',
 'C:/repos/app/build/Application/Splash-00.res',
 'C:/repos/app/build/Application/Splash-00_script.tcl',
 (35, 320),
 9,
 'TkDefaultFont',
 '#d3d3d3',
 'Initializing',
 True,
 False,
 True,
 '__splash',
 (760, 480),
 False, <-----
```

First 14 lines of splash.toc generated on Linux. (Take note of `True` on line 14)

```
 ('/mnt/c/repos/app/src/gui/images/app_with_background.png',
 '/mnt/c/repos/app/build/Application/Splash-00.res',
 '/mnt/c/repos/app/build/Application/Splash-00_script.tcl',
 (35, 320),
 9,
 'TkDefaultFont',
 '#d3d3d3',
 'Initializing',
 True,
 False,
 True,
 '__splash',
 (760, 480),
 True, <-----
```

I don't know whether this Boolean is relevant.



Discussion:
Following on from this; is there a way to automatically tell if a PyInstaller build is running successfully? I would like to configure our CI/CD system to detect whether a build was successful by simply running it.

I can get the PID of the running process however this does not tell discriminate between an application that has started successfully and one that has failed and presented a failure dialog to the user. Does PyInstaller have a feature that can be used to tell these two modes apart?  I can get `_MEIPASS2` from the application environment variable (i.e. `/proc/<PID>/environ` but all I cannot do much with this other than to list files.
That boolean that differs between linux and Windows `splash.toc` is `uses_tkinter` field, and indicates whether (we believe that) your application uses `tkinter` or not (i.e., is `tkinter` already collected as part of your application). If your application uses `tkinter` and this flag ended false, then my guess would be that `tkinter` is not available (or importable) in your environment.

Can you check that any `tkinter`-related files are in fact collected into your windows build?
> That boolean that differs between linux and Windows `splash.toc` is `uses_tkinter` field, and indicates whether (we believe that) your application uses `tkinter` or not (i.e., is `tkinter` already collected as part of your application). If your application uses `tkinter` and this flag ended false, then my guess would be that `tkinter` is not available (or importable) in your environment.
> 
> Can you check that any `tkinter`-related files are in fact collected into your windows build?

My application does not use `tkinter`. Its a purely GTK3 application.

`tlinter` is probably in my Windows (MYSYS2) environment because `python3 -c "import tkinter"` works.

At the state of the execution where the program errors out the _MEIPASS directory contains only a `__splash` directory and `tck86.tcl` and `tk86.dll`.

I am not sure how to check what PyInstaller packaged as part of the Windows executable although I can see the TOC files I know it is a list of what PyInstaller _wants_ to package and said tcl files are on the list.

Here's are the last 20 lines of the splash TOC

```
 'raise .',
 ('tcl86.dll', 'C:\\msys64\\mingw64\\bin/tcl86.dll'),
 ('tk86.dll', 'C:\\msys64\\mingw64\\bin/tk86.dll'),
 {'tcl86.dll',
  'tk/text.tcl',
  'tk/tk.tcl',
  'tk/ttk/cursors.tcl',
  'tk/ttk/fonts.tcl',
  'tk/ttk/ttk.tcl',
  'tk/ttk/utils.tcl',
  'tk86.dll'},
 [('tcl86.dll', 'C:\\msys64\\mingw64\\bin/tcl86.dll', 'BINARY'),
  ('tk86.dll', 'C:\\msys64\\mingw64\\bin/tk86.dll', 'BINARY'),
  ('tk/text.tcl', 'C:/msys64/mingw64/lib/tk8.6/text.tcl', 'DATA'),
  ('tk/tk.tcl', 'C:/msys64/mingw64/lib/tk8.6/tk.tcl', 'DATA'),
  ('tk/ttk/cursors.tcl', 'C:/msys64/mingw64/lib/tk8.6/ttk/cursors.tcl', 'DATA'),
  ('tk/ttk/fonts.tcl', 'C:/msys64/mingw64/lib/tk8.6/ttk/fonts.tcl', 'DATA'),
  ('tk/ttk/ttk.tcl', 'C:/msys64/mingw64/lib/tk8.6/ttk/ttk.tcl', 'DATA'),
  ('tk/ttk/utils.tcl', 'C:/msys64/mingw64/lib/tk8.6/ttk/utils.tcl', 'DATA')],
 'C:/msys64/mingw64/lib/python3.10/lib-dynload/_tkinter.cp310-mingw_x86_64.pyd')
```

> My application does not use tkinter. Its a purely GTK3 application.

The splash screen uses tkinter.
Ah, so this is MSYS2 build. Then it is very likely that this is a path separator issue. I.e., the referenced filenames used by splash use POSIX separator, while the PKG normalizes them to Windows-style backslash, and hence they cannot be found.
Is there a bult-in solution to the [clean startup detection sub-question](https://github.com/pyinstaller/pyinstaller/issues/7828#issuecomment-1667641763)?
> Is there a bult-in solution to the [clean startup detection sub-question](https://github.com/pyinstaller/pyinstaller/issues/7828#issuecomment-1667641763)?

No.
BTW, in case it is of interest, here are the last 43 lines of splash TOC file when I run PyInstaller in the Windows command line -- produced a tiny executable executable that splashed the screen but did complained about missing GTK libraries.

```
 'raise .',
 ('tcl86t.dll',
  'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\DLLs\\tcl86t.dll'),
 ('tk86t.dll',
  'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\DLLs\\tk86t.dll'),
 {'VCRUNTIME140.dll',
  'tcl86t.dll',
  'tk86t.dll',
  'tk\\license.terms',
  'tk\\text.tcl',
  'tk\\tk.tcl',
  'tk\\ttk\\cursors.tcl',
  'tk\\ttk\\fonts.tcl',
  'tk\\ttk\\ttk.tcl',
  'tk\\ttk\\utils.tcl'},
 [('tcl86t.dll',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\DLLs\\tcl86t.dll',
   'BINARY'),
  ('tk86t.dll',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\DLLs\\tk86t.dll',
   'BINARY'),
  ('tk\\license.terms',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\tcl\\tk8.6\\license.terms',
   'DATA'),
  ('tk\\text.tcl',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\tcl\\tk8.6\\text.tcl',
   'DATA'),
  ('tk\\tk.tcl',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\tcl\\tk8.6\\tk.tcl',
   'DATA'),
  ('tk\\ttk\\cursors.tcl',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\tcl\\tk8.6\\ttk\\cursors.tcl',
   'DATA'),
  ('tk\\ttk\\fonts.tcl',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\tcl\\tk8.6\\ttk\\fonts.tcl',
   'DATA'),
  ('tk\\ttk\\ttk.tcl',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\tcl\\tk8.6\\ttk\\ttk.tcl',
   'DATA'),
  ('tk\\ttk\\utils.tcl',
   'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\tcl\\tk8.6\\ttk\\utils.tcl',
   'DATA')],
 'C:\\Users\\<my_name>\\AppData\\Local\\Programs\\Python\\Python310\\DLLs\\_tkinter.pyd')
```

Resolving patch:
diff --git a/PyInstaller/archive/writers.py b/PyInstaller/archive/writers.py
--- a/PyInstaller/archive/writers.py
+++ b/PyInstaller/archive/writers.py
@@ -347,6 +347,18 @@ def __init__(self, filename, name_list, tcl_libname, tk_libname, tklib, rundir,
         :param str script: The tcl/tk script to execute to create the screen.
         """
 
+        # Ensure forward slashes in dependency names are on Windows converted to back slashes '\\', as on Windows the
+        # bootloader works only with back slashes.
+        def _normalize_filename(filename):
+            filename = os.path.normpath(filename)
+            if is_win and os.path.sep == '/':
+                # When building under MSYS, the above path normalization uses Unix-style separators, so replace them
+                # manually.
+                filename = filename.replace(os.path.sep, '\\')
+            return filename
+
+        name_list = [_normalize_filename(name) for name in name_list]
+
         with open(filename, "wb") as fp:
             # Reserve space for the header.
             fp.write(b'\0' * self._HEADER_LENGTH)


Test patch: