Search code examples
pythonpyinstaller

Pyinstaller Onefile Exe is Looking for Python.dll, Failing


I've read several similar posts, but have not found one describing the issue I am having.

I have a short python script referencing an external image and using pygame's freetype. I believe I've handled the configuration and code changes necessary to support both of these. The executables made by pyinstaller work well both locally and when moved to another computer, except when I try to build as a "onefile". Building as 'onefile' will really help for my use case.

When I attempt to configure it to use 'onefile' to build all dependencies into the .exe file. Every attempt at this is resulting in an error that it can't find python310.dll. Python310.dll should be built into the .exe and should not need to be found elsewhere.

Python script and .spec file are below. Can anyone see what I am doing wrong?

import os
import sys
import pygame

pygame.init()
clock  = pygame.time.Clock()
screen = pygame.display.set_mode([800, 600])

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

splash_image = pygame.image.load(resource_path("chef_full.png"))
screen.blit(splash_image, (5,5))

font   = pygame.freetype.Font(None, 24)
text = "Press 'Esc' to quit."
text_rect = font.get_rect(text)
text_rect.center = (400, 450)
text_rect = font.render_to(screen, text_rect, text, "white")

pygame.display.flip()

playing = True
while playing:
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                playing = False
        elif event.type == pygame.QUIT:
            playing = False
    clock.tick(30)
pygame.quit()

The .spec file:

# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

a = Analysis(
    ['splash.py'],
    pathex=[],
    binaries=[],
    datas=[('chef_full.png', '.')],    # A png we use.
    hiddenimports=['pygame.freetype'], # For display on screen
    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)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=False,  # Don't exclude for onefile builds
    name='splash',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

# Skip collect for onefile
#coll = COLLECT(
#    exe,
#    a.binaries,
#    a.zipfiles,
#    a.datas,
#    strip=False,
#    upx=True,
#    upx_exclude=[],
#    name='splash',
#)

Thanks in advance.


Solution

  • The issue lies in the .spec file. The way that I got this to work, was to use the following pyinstaller command to auto-generate a --onefile .spec file:

    pyi-makespec --onefile splash.py
    

    Then, I modified datas and hiddenimports variables in the recently created splash.spec file to what you had in your original .spec file:

    datas=[('chef_full.png', '.')],    # A png we use.
    hiddenimports=['pygame.freetype'], # For display on screen
    

    Finally, I ran the following command:

    pyinstaller --clean splash.spec
    

    The --clean option ensures that the pyinstaller cache is cleaned and temporary files are removed before building.

    Now, a single splash.exe should be located in the dist folder that pyinstaller outputted.

    Here is the full splash.spec file that I used. I highlighted the areas that were added:

    # -*- mode: python ; coding: utf-8 -*-
    
    
    block_cipher = None
    
    
    a = Analysis(
        ['splash.py'],
        pathex=[],
        binaries=[],
        datas=[('chef_full.png', '.')],    # A png we use.
        hiddenimports=['pygame.freetype'], # For display on screen
        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)
    
    exe = EXE(
        pyz,
        a.scripts,
        a.binaries, # Added
        a.zipfiles, # Added
        a.datas, # Added
        [],
        name='splash',
        debug=False,
        bootloader_ignore_signals=False,
        strip=False,
        upx=True,
        upx_exclude=[], # Added
        runtime_tmpdir=None, # Added
        console=False,
        disable_windowed_traceback=False,
        argv_emulation=False,
        target_arch=None,
        codesign_identity=None,
        entitlements_file=None,
    )
    

    Notice that the exclude_binaries variable is not present here.