Skip to content

Add support for building cpython embed exe's. #826

Closed

Description

@AraHaan

I am using meson.build to hopefully bundle in a wheel of my package that not only builds C extensions, but also packages in:

  • 1 embed exe file that gets built my meson (I really wish py.executable was a think like py.extension_module is today which automatically passes in the following for me:
  • the python include folder(s)
  • the python lib folder(s) for the link step

Also the package expects a stub exe to be built as well which does NOT need the python include folder nor the python lib folder as it is meant to be act like a "SFX EXE" like file for the embedded python to get packed down to a single file.

The resulting expected files that are to be packed in the resulting wheels (after building everything)

  • py2exestub/ (temp package name for now)
    • __init__.py
    • __main__.py
    • DistBuilder.py
    • _resourceediting.pyd (built with the limited api)
    • data/
      • memimport.py (gets copied by the code in DistBuilder.py to a specific dist/site-packages folder for zipping up into a site-packages.zip file)
      • zipextimporter.py (gets copied by the code in DistBuilder.py to a specific dist/site-packages folder for zipping up into a site-packages.zip file)
      • embed.exe (gets copied by the code in DistBuilder.py to a specific dist/build_tmp folder for zipping up into a files.zip file)
      • stub.exe (gets copied by the code in DistBuilder.py to a specific dist folder and where then the icon, string, and zip file resources in the exe gets replaced using the _resourceediting extension, also the icon resource in the embed.exe gets replaced before it gets zipped into the zip file that is used to replace the initial empty one in the stub exe)

My meson.build file currently:

project('py2exestub', 'c')

py = import('python').find_installation(pure: false)

# Optional: enable Py_LIMITED_API for the embed exe
limited_api_args = ['-DPy_LIMITED_API=0x030C0000', '-D_UNICODE', '-DUNICODE']

# ----------------------------------------------------
# 1. Build _memimport from externals/py2exe sources
# ----------------------------------------------------

memimport_sources = files(
    '../py2exe/source/_memimport.c',
    '../py2exe/source/actctx.c',
    '../py2exe/source/import_mini.c',
    '../py2exe/source/MemoryModule.c',
    '../py2exe/source/MyLoadLibrary.c'
)

_memimport = py.extension_module(
    '_memimport',
    memimport_sources,
    install: true,
    include_directories: [include_directories('../py2exe/source')],
    c_args: ['-DSTANDALONE', '-DPy_LIMITED_API=0x030C0000', '-D_UNICODE', '-DUNICODE'],   # ← required define
)

_resourceediting = py.extension_module(
    '_resourceediting',
    'src/py2exestub/_resourceediting.c',
    install: true,
    include_directories: [include_directories('src/py2exestub')],
    c_args: limited_api_args,
)

# ----------------------------------------------------
# 2. Build embed (uses Python)
# ----------------------------------------------------

embed_sources = files(
    'src/py2exestub/embed.c',
    'src/py2exestub/embed.rc'
)

# Need the Python include + library dirs here
embed = executable(
    'embed',
    embed_sources,
    install: true,
    include_directories: [py_inc, py_plat_inc, include_directories('src/py2exestub')],
    link_with: py_lib,
    c_args: limited_api_args,
)

# ----------------------------------------------------
# 3. Build stub (no Python)
# ----------------------------------------------------

stub_sources = files(
    'src/py2exestub/stub.c',
    '../miniz-3.0.2/miniz.c',
    'src/py2exestub/stub.rc'
)

stub = executable(
    'stub_launcher',
    stub_sources,
    install: true,
    include_directories: [include_directories('../miniz-3.0.2'), include_directories('src/py2exestub')],
    c_args: ['-D_CRT_SECURE_NO_WARNINGS', '-D_UNICODE', '-DUNICODE'],
)

# Install the two executables, the C extension, and the contents of the data folder in src into py2exestub/data/
py.install_sources(
    [embed, stub, _memimport] + files('src/py2exestub/data'),
    subdir: 'py2exestub/data'
)

# Install the _resourceediting c extension required for the package to work.
py.install_sources(
    [_resourceediting],
    subdir: 'py2exestub'
)

Basically having support for building the embed exe itself would be great in meson-python. Also it would have been nice if one could also configure meson-python to pass in the following defines (_UNICODE, UNICODE, and _CRT_SECURE_NO_WARNINGS) as well from within pyproject.toml to avoid some duplication in the meson.build file.

My goal with this:
I been working hard on a project with the hopes of someday pull requesting the files to the py2exe repository to bring back single file exe support using my idea of having a SFX EXE type thing as a stub to do such.

Activity

  1. dnicolodi commented on Dec 28, 2025

    @dnicolodi
    Member

    I really wish py.executable was a think like py.extension_module is today which automatically passes in the following for me:

    • the python include folder(s)

    • the python lib folder(s) for the link step

    This is spelled like this:

    py = import('python').find_installation()
    executable(
        'foo',
        ...,
        dependencies: py.dependency(embed: true),
    )
    # Install the two executables, the C extension, and the contents of the data folder in src into py2exestub/data/
    py.install_sources(
        [embed, stub, _memimport] + files('src/py2exestub/data'),
        subdir: 'py2exestub/data'
    )
    

    I doubt this works. To install the executables in the right path you should set the install_dir parameter to something like py.get_install_dir() / 'py2exestub/data'. Also install_sources() does not work for installing directories, thus adding files('src/py2exestub/data') to the list of files to install does not work. You should list all files individually.

    # Install the _resourceediting c extension required for the package to work.
    py.install_sources(
        [_resourceediting],
        subdir: 'py2exestub'
    )
    

    This does not work either. You should just use the subdir argument to extension_module() call.

    Basically having support for building the embed exe itself would be great in meson-python.

    This is already supported by meson. See above.

    Also it would have been nice if one could also configure meson-python to pass in the following defines (_UNICODE, UNICODE, and _CRT_SECURE_NO_WARNINGS) as well from within pyproject.toml to avoid some duplication in the meson.build file.

    Why do you want to have these defines specified in pyproject.toml? If you want to have these passed to all compiler invocations you can use

    add_project_arguments('c', ['-D_CRT_SECURE_NO_WARNINGS', '-D_UNICODE', '-DUNICODE']
    

    in meson.build.

  2. AraHaan commented on Dec 28, 2025

    @AraHaan
    Author

    You should just use the subdir argument to extension_module() call.

    Now that I done this, I realized the executable calls don't have this argument supported though, it would be nice if it was supported in there though so I could then place the 2 python scripts in the data folder in the py.install_sources as the only call to it.

    In other news, it seems it cannot figure out how to compile the resource scripts for the 2 exes.

  3. dnicolodi commented on Dec 28, 2025

    @dnicolodi
    Member

    Now that I done this, I realized the executable calls don't have this argument supported though

    Please read my previous comment again. The solution is spelled out clearly there.

    In other news, it seems it cannot figure out how to compile the resource scripts for the 2 exes.

    I don't know what you mean by resources scripts, but in the case you are referring to MSVC resources, please refer to the Meson manual: https://mesonbuild.com/Windows-module.html#windows-module

  4. locked and limited conversation to collaborators on Dec 28, 2025
  5. converted this issue into a discussion #827 on Dec 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions