view rust/hgcli/pyoxidizer.bzl @ 45095:8e04607023e5

procutil: ensure that procutil.std{out,err}.write() writes all bytes Python 3 offers different kind of streams and it’s not guaranteed for all of them that calling write() writes all bytes. When Python is started in unbuffered mode, sys.std{out,err}.buffer are instances of io.FileIO, whose write() can write less bytes for platform-specific reasons (e.g. Linux has a 0x7ffff000 bytes maximum and could write less if interrupted by a signal; when writing to Windows consoles, it’s limited to 32767 bytes to avoid the "not enough space" error). This can lead to silent loss of data, both when using sys.std{out,err}.buffer (which may in fact not be a buffered stream) and when using the text streams sys.std{out,err} (I’ve created a CPython bug report for that: https://bugs.python.org/issue41221). Python may fix the problem at some point. For now, we implement our own wrapper for procutil.std{out,err} that calls the raw stream’s write() method until all bytes have been written. We don’t use sys.std{out,err} for larger writes, so I think it’s not worth the effort to patch them.
author Manuel Jacob <me@manueljacob.de>
date Fri, 10 Jul 2020 12:27:58 +0200
parents 118f067f6bd1
children 57b5452a55d5
line wrap: on
line source

ROOT = CWD + "/../.."

# Code to run in Python interpreter.
RUN_CODE = "import hgdemandimport; hgdemandimport.enable(); from mercurial import dispatch; dispatch.run()"

set_build_path(ROOT + "/build/pyoxidizer")

def make_distribution():
    return default_python_distribution()

def make_distribution_windows():
    return default_python_distribution(flavor = "standalone_dynamic")

def make_exe(dist):
    """Builds a Rust-wrapped Mercurial binary."""
    config = PythonInterpreterConfig(
        raw_allocator = "system",
        run_eval = RUN_CODE,
        # We want to let the user load extensions from the file system
        filesystem_importer = True,
        # We need this to make resourceutil happy, since it looks for sys.frozen.
        sys_frozen = True,
        legacy_windows_stdio = True,
    )

    exe = dist.to_python_executable(
        name = "hg",
        resources_policy = "prefer-in-memory-fallback-filesystem-relative:lib",
        config = config,
        # Extension may depend on any Python functionality. Include all
        # extensions.
        extension_module_filter = "all",
    )

    # Add Mercurial to resources.
    for resource in dist.pip_install(["--verbose", ROOT]):
        # This is a bit wonky and worth explaining.
        #
        # Various parts of Mercurial don't yet support loading package
        # resources via the ResourceReader interface. Or, not having
        # file-based resources would be too inconvenient for users.
        #
        # So, for package resources, we package them both in the
        # filesystem as well as in memory. If both are defined,
        # PyOxidizer will prefer the in-memory location. So even
        # if the filesystem file isn't packaged in the location
        # specified here, we should never encounter an errors as the
        # resource will always be available in memory.
        if type(resource) == "PythonPackageResource":
            exe.add_filesystem_relative_python_resource(".", resource)
            exe.add_in_memory_python_resource(resource)
        else:
            exe.add_python_resource(resource)

    # On Windows, we install extra packages for convenience.
    if "windows" in BUILD_TARGET_TRIPLE:
        exe.add_python_resources(
            dist.pip_install(["-r", ROOT + "/contrib/packaging/requirements_win32.txt"]),
        )

    return exe

def make_manifest(dist, exe):
    m = FileManifest()
    m.add_python_resource(".", exe)

    return m

def make_embedded_resources(exe):
    return exe.to_embedded_resources()

register_target("distribution_posix", make_distribution)
register_target("distribution_windows", make_distribution_windows)

register_target("exe_posix", make_exe, depends = ["distribution_posix"])
register_target("exe_windows", make_exe, depends = ["distribution_windows"])

register_target(
    "app_posix",
    make_manifest,
    depends = ["distribution_posix", "exe_posix"],
    default = "windows" not in BUILD_TARGET_TRIPLE,
)
register_target(
    "app_windows",
    make_manifest,
    depends = ["distribution_windows", "exe_windows"],
    default = "windows" in BUILD_TARGET_TRIPLE,
)

resolve_targets()

# END OF COMMON USER-ADJUSTED SETTINGS.
#
# Everything below this is typically managed by PyOxidizer and doesn't need
# to be updated by people.

PYOXIDIZER_VERSION = "0.7.0"