diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c510fa19..4852e9e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,21 +69,17 @@ jobs: - name: 'Setup project' run: | set -eux - python .github/scripts/meson_setup_or_dump_log.py build/ -D headless=true + python .github/scripts/meson_setup_or_dump_log.py build/ - name: 'Build project' run: | set -eux meson compile -C build/ - - name: 'Start xvfb' - run: | - /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & - echo ">>> Started xvfb" - name: 'Run tests' run: | set -eux - python tests/run.py helloworld --build-dir build/ - env: - DISPLAY: ':99.0' + python tests/run.py helloworld \ + --build-dir build/ \ + -- --display-driver headless # - name: 'Generate artifact archive' # run: | # set -eux @@ -133,10 +129,18 @@ jobs: pip install -r requirements.in - name: 'Setup project' run: | - python .github/scripts/meson_setup_or_dump_log.py build/ -D headless=true + set -eux + python .github/scripts/meson_setup_or_dump_log.py build/ - name: 'Build project' run: | + set -eux meson compile -C build/ + - name: 'Run tests' + run: | + set -eux + python tests/run.py helloworld \ + --build-dir build/ \ + -- --display-driver headless # - name: 'Install Mesa3D OpenGL' # shell: bash # run: | @@ -173,44 +177,43 @@ jobs: ################################################################################# -# macos-build: -# name: '🍎 macOS build' -# runs-on: macos-latest -# env: -# CC: clang -# LD: lld -# PLATFORM: 'osx-x86_64' -# steps: -# - name: 'Checkout' -# uses: actions/checkout@f1d3225b5376a0791fdee5a0e8eac5289355e43a # pin@v2 -# with: -# submodules: true -# - name: 'Set up Python' -# uses: actions/setup-python@0291cefc54fa79cd1986aee8fa5ecb89ad4defea # pin@v2 -# with: -# python-version: ${{ env.PYTHON_VERSION }} -# - name: 'Setup venv' -# run: | -# set -eux -# ${{ env.CC }} --version -# python --version -# brew update -# brew install zlib openssl -# brew install --cask xquartz -# pip install -U pip -# pip install -r requirements.txt -# - name: 'Setup project' -# run: | -# set -eux -# meson setup build/ -D headless=true -# - name: 'Build project' -# run: | -# set -eux -# meson compile -C build/ - # - name: 'Run tests' - # run: | - # set -eux - # python tests/run.py --build-dir build/ --godot-binary ${{ GODOT_BINARY_VERSION }} + macos-build: + name: '🍎 macOS build' + runs-on: macos-latest + env: + CC: clang + LD: lld + PLATFORM: 'macos-x86_64' + steps: + - name: 'Checkout' + uses: actions/checkout@f1d3225b5376a0791fdee5a0e8eac5289355e43a # pin@v2 + with: + submodules: true + - name: 'Set up Python' + uses: actions/setup-python@0291cefc54fa79cd1986aee8fa5ecb89ad4defea # pin@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - name: 'Setup venv' + run: | + set -eux + ${{ env.CC }} --version + python --version + pip install -U pip + pip install -r requirements.txt + - name: 'Setup project' + run: | + set -eux + python .github/scripts/meson_setup_or_dump_log.py build/ + - name: 'Build project' + run: | + set -eux + meson compile -C build/ + - name: 'Run tests' + run: | + set -eux + python tests/run.py helloworld \ + --build-dir build/ \ + -- --display-driver headless # - name: 'Generate artifact archive' # run: | # set -eux diff --git a/build_tools/dist.py b/build_tools/dist.py deleted file mode 100644 index c2cf1c87..00000000 --- a/build_tools/dist.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import List, Tuple -from pathlib import Path -import shutil - -import isengard - - -isg = isengard.get_parent() - - -@isg.rule( - output="{build_dir}/dist/", - # TODO: support globbing in inputs - # inputs=["{build_dir}/dist/**"], - inputs=["{build_dir}/dist/pythonscript.gdextension", "{build_dir}/dist/addons/"], -) -def dist( - output: Path, - inputs: List[Path], -) -> None: - pass - - -# TODO: support simple copy metarule -@isg.rule( - output="{build_dir}/dist/pythonscript.gdextension", - input="../misc/release_pythonscript.gdextension", -) -def dist_gdextension( - output: Path, - input: Path, -) -> None: - if output.exists(): # TODO: remove me - return - output.parent.mkdir(parents=True, exist_ok=True) - shutil.copy(input, output) - - -@isg.rule( - output="{build_dir}/dist/addons/", - # inputs=["{build_pythonscript_dir}/pythonscript.so", "{build_platform_dir}/cpython_distrib/"], - inputs=[ - "pythonscript_lib@", - "underscore_pythonscript_lib@", - "{build_platform_dir}/cpython_distrib/", - "{rootdir}/pythonscript/godot/", - ], -) -def dist_addons( - output: Path, - inputs: Tuple[isengard.VirtualTargetResolver, isengard.VirtualTargetResolver, Path], - host_platform: str, -) -> None: - *libs_items, cpython_distrib, godot_py = inputs - platform_output = output / "pythonscript" / host_platform - if not platform_output.exists(): # TODO: remove me - platform_output.parent.mkdir(parents=True, exist_ok=True) - # Put CPython distrib into the plaform dir and add pythonscript.so - shutil.copytree(cpython_distrib, platform_output, symlinks=True) - for lib_items in libs_items: - for item in lib_items: - shutil.copy(item, platform_output) - # Copy .py files - # TODO: make this cleaner ! - godot_dir = platform_output / "Lib/site-packages/godot" - godot_dir.mkdir(parents=True, exist_ok=True) - for py in godot_py.glob("*.py"): - if py.name == "BUILD.py": - continue - shutil.copy(py, godot_dir) diff --git a/build_tools/godot_binary.py b/build_tools/godot_binary.py deleted file mode 100644 index 0a22f1f1..00000000 --- a/build_tools/godot_binary.py +++ /dev/null @@ -1,124 +0,0 @@ -from typing import Tuple -from pathlib import Path - -import isengard - - -isg = isengard.get_parent() - - -### Godot binary (to run tests) ### - - -@isg.lazy_rule -def godot_version( - godot_binary_hint: Tuple[str, str, str, str], - build_platform: str, -): - godot_build_platform = { - "linux-x86_64": "linux.64", - "linux-x86": "linux.32", - "windows-x86_64": "win64", - "windows-x86": "win32", - "osx-x86_64": "osx.universal", - }.get(build_platform) - if not godot_build_platform: - raise RuntimeError( - f"Godot doesn't seem to support platform `{build_platform}`, available platforms: {', '.join(godot_build_platform.keys())}" - ) - - if isinstance(godot_binary_hint, Path): - # Consider hint is pointing to an already existing binary - binary_path = godot_binary_hint - - else: - major, minor, patch, extra = godot_binary_hint - version = f"{major}.{minor}.{patch}" if patch != "0" else f"{major}.{minor}" - binary_name = f"Godot_v{version}-{extra}_{godot_build_platform}" - if extra == "stable": - url = f"https://downloads.tuxfamily.org/godotengine/{version}/Godot_v{version}-{extra}_{godot_build_platform}.zip" - else: - url = f"https://downloads.tuxfamily.org/godotengine/{version}/{extra}/Godot_v{version}-{extra}_{godot_build_platform}.zip" - - if build_platform[0] == "Darwin": - zippath = "Godot.app/Contents/MacOS/Godot" - else: - zippath = binary_name - binary_path = build_platform_dir / binary_name - - # TODO: properly handle `last_run_resolved` - # if binary_path != last_binary_path or not last_binary_path.exists(): - if not binary_path.exists(): - print(f"Downloading {url}...") - with urlopen(url) as rep: - zipfile = ZipFile(BytesIO(rep.read())) - if zippath not in zipfile.namelist(): - raise RuntimeError(f"Archive doesn't contain {zippath}") - - print(f"Decompressing {binary_path}...") - binary_path.write_bytes(zipfile.open(zippath).read()) - if build_platform[0] != "Windows": - os.chmod(binary_path, 0o755) - - output.resolve(binary_path) - - -@isg.rule(output="godot_binary@") -def fetch_godot_binary( - output: isengard.VirtualTargetResolver, - build_platform_dir: Path, - godot_binary_hint: Tuple[str, str, str, str], - build_platform: str, -): - import os - from io import BytesIO - from zipfile import ZipFile - from urllib.request import urlopen - - last_binary_path = output.last_run_resolved - assert last_binary_path is None or isinstance(last_binary_path, Path) - - godot_build_platform = { - "linux-x86_64": "linux.64", - "linux-x86": "linux.32", - "windows-x86_64": "win64", - "windows-x86": "win32", - "osx-x86_64": "osx.universal", - }.get(build_platform) - if not godot_build_platform: - raise RuntimeError(f"Godot doesn't seem to support platform `{build_platform}`") - - if isinstance(godot_binary_hint, Path): - # Consider hint is pointing to an already existing binary - binary_path = godot_binary_hint - - else: - major, minor, patch, extra = godot_binary_hint - version = f"{major}.{minor}.{patch}" if patch != "0" else f"{major}.{minor}" - binary_name = f"Godot_v{version}-{extra}_{godot_build_platform}" - if extra == "stable": - url = f"https://downloads.tuxfamily.org/godotengine/{version}/Godot_v{version}-{extra}_{godot_build_platform}.zip" - else: - url = f"https://downloads.tuxfamily.org/godotengine/{version}/{extra}/Godot_v{version}-{extra}_{godot_build_platform}.zip" - - if build_platform[0] == "Darwin": - zippath = "Godot.app/Contents/MacOS/Godot" - else: - zippath = binary_name - binary_path = build_platform_dir / binary_name - - # TODO: properly handle `last_run_resolved` - # if binary_path != last_binary_path or not last_binary_path.exists(): - if not binary_path.exists(): - print(f"Downloading {url}...") - with urlopen(url) as rep: - zipfile = ZipFile(BytesIO(rep.read())) - if zippath not in zipfile.namelist(): - raise RuntimeError(f"Archive doesn't contain {zippath}") - - print(f"Decompressing {binary_path}...") - binary_path.write_bytes(zipfile.open(zippath).read()) - if build_platform[0] != "Windows": - os.chmod(binary_path, 0o755) - - output.resolve(binary_path) diff --git a/build_tools/python_distrib.py b/build_tools/python_distrib.py deleted file mode 100644 index 9185e823..00000000 --- a/build_tools/python_distrib.py +++ /dev/null @@ -1,232 +0,0 @@ -from typing import Tuple -from urllib.request import urlopen -from pathlib import Path -import shutil -import json -import tarfile -from zstandard import ZstdDecompressor - -import isengard - - -isg = isengard.get_parent() - - -PREBUILDS_BASE_URL = "https://github.com/indygreg/python-build-standalone/releases/download" -PLATFORM_TO_PREBUILDS = { - "3.9.7": { - "linux-x86_64": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-x86_64-unknown-linux-gnu-pgo+lto-20211017T1616.tar.zst", - "windows-x86": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-i686-pc-windows-msvc-shared-pgo-20211017T1616.tar.zst", - "windows-x86_64": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-x86_64-pc-windows-msvc-shared-pgo-20211017T1616.tar.zst", - "osx-x86_64": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-x86_64-apple-darwin-pgo+lto-20211017T1616.tar.zst", - } -} - - -@isg.lazy_config -def cpython_prebuild_url(host_platform: str, host_cpython_version: str) -> str: - per_platform = PLATFORM_TO_PREBUILDS.get(host_cpython_version) - if not per_platform: - raise RuntimeError( - f"Unsupported CPython version `{host_cpython_version}`, valid values are: {', '.join(PLATFORM_TO_PREBUILDS.keys())}" - ) - prebuild_url = per_platform.get(host_platform) - if not prebuild_url: - raise RuntimeError(f"No CPython prebuild for host_platform `{host_platform}`") - return prebuild_url - - -@isg.lazy_config -def cpython_prebuild_archive_path(build_platform_dir: Path, cpython_prebuild_url: str) -> str: - _, name = cpython_prebuild_url.rsplit("/", 1) - return build_platform_dir / name - - -@isg.lazy_config -def libpython_config( - host_cpython_version: str, - host_platform: str, - cc_is_msvc: bool, - build_platform_dir: Path, -) -> str: - basedir = build_platform_dir / "cpython_prebuild" - - major, minor, _ = host_cpython_version.split(".") - if host_platform.startswith("windows"): - python_header_dir = basedir / "python/install/include" - python_lib_dir = basedir / "python/install/libs" - python_lib = f"python{major}{minor}" - else: - python_header_dir = basedir / f"python/install/include/python{major}.{minor}" - python_lib_dir = basedir / "python/install/lib" - python_lib = f"python{major}.{minor}" - - if cc_is_msvc: - cflags = (f"/I{python_header_dir}",) - linkflags = (f"/LIBPATH:{python_lib_dir}", f"{python_lib}.lib") - else: - cflags = (f"-I{python_header_dir}",) - linkflags = (f"-L{python_lib_dir}", f"-l{python_lib}") - - return (cflags, linkflags) - - -@isg.lazy_config -def libpython_cflags(python_config: Tuple[Tuple[str, ...], Tuple[str, ...]]) -> Tuple[str, ...]: - return python_config[0] - - -@isg.lazy_config -def libpython_linkflags(python_config: Tuple[Tuple[str, ...], Tuple[str, ...]]) -> Tuple[str, ...]: - return python_config[1] - - -@isg.rule(output="{cpython_prebuild_archive_path}#") -def download_cpython_prebuild_archive(output: Path, cpython_prebuild_url: str) -> None: - print(f"Downloading {cpython_prebuild_url}...") - with urlopen(cpython_prebuild_url) as infd: - with open(output, "bw") as outfd: - outfd.write(infd.read()) - - -@isg.rule( - output="{build_platform_dir}/cpython_prebuild/", - input="{cpython_prebuild_archive_path}#", -) -def extract_cpython_prebuild_archive( - output: Path, - input: Path, - host_cpython_version: str, - host_platform: str, - cc_is_msvc: bool, -) -> None: - print(f"Extracting {input}...") - with open(input, "rb") as fh: - dctx = ZstdDecompressor() - with dctx.stream_reader(fh) as reader: - with tarfile.open(mode="r|", fileobj=reader) as tf: - tf.extractall(output) - - -### Generate custom build from the prebuild ### - - -@isg.rule( - output="{build_platform_dir}/cpython_distrib/", input="{build_platform_dir}/cpython_prebuild/" -) -def generate_cpython_distrib( - output: Path, - input: Path, - host_cpython_version: str, - cpython_compressed_stdlib: bool, - host_platform: str, -): - global _generate_cpython_distrib_linux, _generate_cpython_distrib_windows - - prebuild = input / "python" - distrib = output - - conf = json.loads((prebuild / "PYTHON.json").read_text()) - assert conf["version"] == "7" - assert conf["libpython_link_mode"] == "shared" - if host_platform.startswith("linux"): - _generate_cpython_distrib_linux( - conf, distrib, prebuild, host_cpython_version, cpython_compressed_stdlib - ) - elif host_platform.startswith("windows"): - _generate_cpython_distrib_windows( - conf, distrib, prebuild, host_cpython_version, cpython_compressed_stdlib - ) - else: - raise RuntimeError(f"Don't know how to build for host_platform `{host_platform}`") - - -def _generate_cpython_distrib_linux( - conf: dict, distrib: Path, prebuild: Path, host_cpython_version: str, compressed_stdlib: bool -) -> None: - assert conf["target_triple"] in ("x86_64-unknown-linux-gnu", "x86-unknown-linux-gnu") - major, minor, _ = host_cpython_version.split(".") - - shutil.copytree(str(prebuild / "install"), str(distrib), symlinks=True) - shutil.copytree(str(prebuild / "licenses"), str(distrib / "licenses"), symlinks=True) - - shutil.rmtree(str(distrib / "share")) - - # Remove static library stuff - config = conf["python_stdlib_platform_config"] - assert config.startswith("install/lib/") - config = distrib / config[len("install/") :] - assert config.exists() - shutil.rmtree(str(config)) - (distrib / f"lib/libpython{major}.{minor}.a").unlink() # Remove symlink - - stdlib_path = distrib / f"lib/python{major}.{minor}" - - # Remove tests lib (pretty big and basically useless) - shutil.rmtree(str(stdlib_path / "test")) - - # Also remove __pycache__ & .pyc stuff - for pycache in stdlib_path.glob("**/__pycache__"): - shutil.rmtree(str(pycache)) - - # Make sure site-packages is empty to avoid including pip (ensurepip should be used instead) - shutil.rmtree(str(stdlib_path / "site-packages")) - - # Zip the stdlib to save plenty of space \o/ - if compressed_stdlib: - tmp_stdlib_path = distrib / f"lib/tmp_python{major}.{minor}" - shutil.move(str(stdlib_path), str(tmp_stdlib_path)) - stdlib_path.mkdir() - shutil.move(str(tmp_stdlib_path / "lib-dynload"), str(stdlib_path / "lib-dynload")) - shutil.make_archive( - base_name=str(distrib / f"lib/python{major}{minor}"), - format="zip", - root_dir=str(tmp_stdlib_path), - ) - shutil.rmtree(str(tmp_stdlib_path)) - # Oddly enough, os.py must be present (even if empty !) otherwise - # Python failed to find it home... - (stdlib_path / "os.py").touch() - - (stdlib_path / "site-packages").mkdir() - - -def _generate_cpython_distrib_windows( - conf: dict, distrib: Path, prebuild: Path, host_cpython_version: str, compressed_stdlib: bool -) -> None: - assert conf["target_triple"] in ("x86_64-pc-windows-msvc", "x86-pc-windows-msvc") - major, minor, _ = host_cpython_version.split(".") - - shutil.copytree(str(prebuild / "install"), str(distrib), symlinks=True) - shutil.copytree(str(prebuild / "licenses"), str(distrib / "licenses"), symlinks=True) - - stdlib_path = distrib / "Lib" - - # Remove tests lib (pretty big and basically useless) - shutil.rmtree(str(stdlib_path / "test")) - - # Remove .pdb debug symbols - for pdbfile in (distrib / "DLLs").glob("*.pdb"): - pdbfile.unlink() - - # Also remove __pycache__ & .pyc stuff - for pycache in stdlib_path.glob("**/__pycache__"): - shutil.rmtree(str(pycache)) - - # Make sure site-packages is empty to avoid including pip (ensurepip should be used instead) - shutil.rmtree(str(stdlib_path / "site-packages")) - - # Zip the stdlib to save plenty of space \o/ - if compressed_stdlib: - shutil.make_archive( - base_name=str(distrib / f"python{major}{minor}"), - format="zip", - root_dir=str(stdlib_path), - ) - shutil.rmtree(str(stdlib_path)) - stdlib_path.mkdir() - # Oddly enough, os.py must be present (even if empty !) otherwise - # Python failed to find it home... - (stdlib_path / "os.py").touch() - - (stdlib_path / "site-packages").mkdir() diff --git a/meson.build b/meson.build index 68bbba74..2cdabc17 100644 --- a/meson.build +++ b/meson.build @@ -27,10 +27,10 @@ endif # We stick with Godot's platform naming -# Note darwin may refere to ios and osx (because it's sooo clever to designate -# two totally diferent things with the same name...) but we only support osx +# Note darwin may refere to iOS and macOS (because it's sooo clever to designate +# two totally diferent things with the same name...) but we only support macOS if host_platform == 'darwin-x86_64' - host_platform = 'osx-x86_64' + host_platform = 'macos-x86_64' endif @@ -65,9 +65,14 @@ cythonize_command = [ if host_platform.startswith('windows') python_native_module_name_prefix = '' python_native_module_name_suffix = 'pyd' -else +elif host_platform.startswith('linux') python_native_module_name_prefix = '' python_native_module_name_suffix = 'so' +elif host_platform.startswith('macos') + python_native_module_name_prefix = '' + python_native_module_name_suffix = 'dynlib' +else + error('Unsupported host_platform', host_platform) endif @@ -119,7 +124,7 @@ python_prebuild_name = 'python_prebuild-@0@-@1@'.format( python_prebuild_path = join_paths(meson.current_build_dir(), python_prebuild_name) python_distrib_path = join_paths(meson.current_build_dir(), 'python_distrib') # Define the `dep_python` dependency -if build_machine.system() == 'linux' +if host_platform.startswith('linux') dep_python = declare_dependency( dependencies: cc.find_library( # e.g. python3.9 @@ -134,7 +139,22 @@ if build_machine.system() == 'linux' host_platform, 'lib/python@0@.@1@/site-packages'.format(cpython_distrib_version_major, cpython_distrib_version_minor) ) -elif build_machine.system() == 'windows' +elif host_platform.startswith('macos') + dep_python = declare_dependency( + dependencies: cc.find_library( + # e.g. python3.9 + 'python@0@.@1@'.format(cpython_distrib_version_major, cpython_distrib_version_minor), + dirs: join_paths(python_prebuild_path , 'python/install/lib') + ), + include_directories: [join_paths(python_prebuild_name, 'python/install/include/python@0@.@1@'.format(cpython_distrib_version_major, cpython_distrib_version_minor))], + ) + # e.g. addons/pythonscript/linux-x86_64/lib/python3.9/site-packages + python_site_packages_install_path = join_paths( + 'addons/pythonscript', + host_platform, + 'lib/python@0@.@1@/site-packages'.format(cpython_distrib_version_major, cpython_distrib_version_minor) + ) +elif host_platform.startswith('windows') dep_python = declare_dependency( dependencies: cc.find_library( # e.g. python39 @@ -151,6 +171,8 @@ elif build_machine.system() == 'windows' host_platform, 'Lib/site-packages' ) +else + error('Unsupported host_platform', host_platform) endif diff --git a/meson_options.txt b/meson_options.txt index b252454b..3b6bab48 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,2 @@ option('cpython_distrib_version', type: 'string', value: '3.9.7', description: 'Version of CPython to ship') option('godot_headers', type: 'string', value: 'godot_headers', description: 'Path to Godot extension headers directory') -option('headless', type: 'boolean', value: false, description: 'Run test in headless mode') diff --git a/misc/release_pythonscript.gdextension b/misc/release_pythonscript.gdextension index 4498566e..2feb547c 100644 --- a/misc/release_pythonscript.gdextension +++ b/misc/release_pythonscript.gdextension @@ -8,4 +8,4 @@ linux.64="res://addons/pythonscript/linux-x86_64/libpythonscript.so" linux.32="res://addons/pythonscript/linux-x86/libpythonscript.so" windows.64="res://addons/pythonscript/windows-x86_64/pythonscript.dll" windows.32="res://addons/pythonscript/windows-x86/pythonscript.dll" -osx.64="res://addons/pythonscript/osx-64/libpythonscript.dylib" +macos.64="res://addons/pythonscript/macos-x86_64/libpythonscript.dylib" diff --git a/scripts/python_distrib.py b/scripts/python_distrib.py index 1a1416be..67186ac2 100644 --- a/scripts/python_distrib.py +++ b/scripts/python_distrib.py @@ -15,7 +15,7 @@ "linux-x86_64": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-x86_64-unknown-linux-gnu-pgo+lto-20211017T1616.tar.zst", "windows-x86": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-i686-pc-windows-msvc-shared-pgo-20211017T1616.tar.zst", "windows-x86_64": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-x86_64-pc-windows-msvc-shared-pgo-20211017T1616.tar.zst", - "osx-x86_64": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-x86_64-apple-darwin-pgo+lto-20211017T1616.tar.zst", + "macos-x86_64": f"{PREBUILDS_BASE_URL}/20211017/cpython-3.9.7-x86_64-apple-darwin-pgo+lto-20211017T1616.tar.zst", } } @@ -114,6 +114,57 @@ def install_linux(conf: dict, build_dir: Path, prebuild_dir: Path, compressed_st (stdlib_path / "site-packages").mkdir() +def install_macos(conf: dict, build_dir: Path, prebuild_dir: Path, compressed_stdlib: bool) -> None: + print(f"Create clean distribution {build_dir}...") + + if conf["target_triple"] not in ("x86_64-apple-darwin",): + raise RuntimeError(f"Unexpected target_triple `{conf['target_triple']}`") + major, minor = conf["python_major_minor_version"].split(".") + + shutil.copytree(prebuild_dir / "python/install", build_dir, symlinks=True) + shutil.copytree(prebuild_dir / "python/licenses", build_dir / "licenses", symlinks=True) + + shutil.rmtree(build_dir / "share") + + # Remove static library stuff + config = conf["python_stdlib_platform_config"] + assert config.startswith("install/lib/") + config = build_dir / config[len("install/") :] + assert config.exists() + shutil.rmtree(config) + (build_dir / f"lib/libpython{major}.{minor}.a").unlink() # Remove symlink + + stdlib_path = build_dir / f"lib/python{major}.{minor}" + + # Remove tests lib (pretty big and basically useless) + shutil.rmtree(stdlib_path / "test") + + # Also remove __pycache__ & .pyc stuff + for pycache in stdlib_path.glob("**/__pycache__"): + shutil.rmtree(pycache) + + # Make sure site-packages is empty to avoid including pip (ensurepip should be used instead) + shutil.rmtree(stdlib_path / "site-packages") + + # Zip the stdlib to save plenty of space \o/ + if compressed_stdlib: + tmp_stdlib_path = build_dir / f"lib/tmp_python{major}.{minor}" + shutil.move(stdlib_path, tmp_stdlib_path) + stdlib_path.mkdir() + shutil.move(tmp_stdlib_path / "lib-dynload", stdlib_path / "lib-dynload") + shutil.make_archive( + base_name=str(build_dir / f"lib/python{major}{minor}"), + format="zip", + root_dir=tmp_stdlib_path, + ) + shutil.rmtree(tmp_stdlib_path) + # Oddly enough, os.py must be present (even if empty !) otherwise + # Python failed to find it home... + (stdlib_path / "os.py").touch() + + (stdlib_path / "site-packages").mkdir() + + def install_windows( conf: dict, build_dir: Path, prebuild_dir: Path, compressed_stdlib: bool ) -> None: @@ -183,6 +234,13 @@ def build_distrib( prebuild_dir=prebuild_dir, compressed_stdlib=args.compressed_stdlib, ) + elif config["python_platform_tag"].startswith("macosx"): + install_macos( + conf=config, + build_dir=build_dir, + prebuild_dir=prebuild_dir, + compressed_stdlib=args.compressed_stdlib, + ) else: raise RuntimeError(f"Unsupported Python platform tag: `{config['python_platform_tag']}`") diff --git a/src/godot/meson.build b/src/godot/meson.build index 1ecbd22b..7c518b6f 100644 --- a/src/godot/meson.build +++ b/src/godot/meson.build @@ -75,11 +75,18 @@ c_builtins = custom_target( ) +if host_platform.startswith('linux') + _builtins_rpath = '$ORIGIN/../../..' +elif host_platform.startswith('macos') + _builtins_rpath = '@loader_path/../../..' +endif + + shared_library( '_builtins', c_builtins, - dependencies : [dep_godot, dep_python, dep_pythonscript], - install_rpath: '$ORIGIN/../../..', # To find libpython + dependencies : [dep_godot, dep_python, dep__pythonscript], + install_rpath: _builtins_rpath, # To find libpython name_prefix: python_native_module_name_prefix, name_suffix: python_native_module_name_suffix, install: true, diff --git a/src/meson.build b/src/meson.build index b59314d7..e2c09548 100644 --- a/src/meson.build +++ b/src/meson.build @@ -104,36 +104,31 @@ h_pythonscript = c_pythonscript[1] # Header for other Cython modules c_pythonscript = c_pythonscript[0] -lib_pythonscript = shared_library( +if host_platform.startswith('linux') + lib__pythonscript_rpath = '$ORIGIN/../..' +elif host_platform.startswith('macos') + lib__pythonscript_rpath = '@loader_path/../..' +endif + + +lib__pythonscript = shared_library( '_pythonscript', c_pythonscript, dependencies : [dep_godot, dep_python], - install_rpath: '$ORIGIN/../..', # To find libpython + install_rpath: lib__pythonscript_rpath, # To find libpython name_prefix: python_native_module_name_prefix, name_suffix: python_native_module_name_suffix, install: true, install_dir: python_site_packages_install_path, ) -dep_pythonscript = declare_dependency( - # include_directories: lib_pythonscript.private_dir_include(), # To expose _pythonscript_api.h - sources: [h_pythonscript, h_pythonscript_api], - link_with: lib_pythonscript, -) -lib_pythonscript = shared_library( - 'pythonscript', - ['pythonscript.c'], - dependencies: [dep_godot, dep_python, dep_pythonscript], - install_rpath: '$ORIGIN/lib', # To find libpython - install: true, - install_dir: 'addons/pythonscript/' + host_platform, +dep__pythonscript = declare_dependency( + # include_directories: lib__pythonscript.private_dir_include(), # To expose _pythonscript_api.h + sources: [h_pythonscript, h_pythonscript_api], + link_with: lib__pythonscript, ) -# dep_pythonscript = declare_dependency( -# link_with: lib_pythonscript, -# ) - ############################################################################## # Load subdirs (and their config) # @@ -158,12 +153,18 @@ subdir('godot') # to how Python loads native modules. -# shared_library( -# 'pythonscript', -# ['pythonscript.c', c_pythonscript], -# dependencies: [dep_godot, dep_python], -# # dependencies: [dep_godot, dep_python, dep_pythonscript], -# install_rpath: '$ORIGIN/lib', -# install: true, -# install_dir: 'addons/pythonscript/' + host_platform, -# ) +if host_platform.startswith('linux') + lib_pythonscript_rpath = '$ORIGIN/lib' +elif host_platform.startswith('macos') + lib_pythonscript_rpath = '@loader_path/lib' +endif + + +lib_pythonscript = shared_library( + 'pythonscript', + ['pythonscript.c'], + dependencies: [dep_godot, dep_python, dep__pythonscript], + install_rpath: lib_pythonscript_rpath, # To find libpython + install: true, + install_dir: 'addons/pythonscript/' + host_platform, +) diff --git a/tests/helloworld/pythonscript.gdextension b/tests/helloworld/pythonscript.gdextension index 4498566e..2feb547c 100644 --- a/tests/helloworld/pythonscript.gdextension +++ b/tests/helloworld/pythonscript.gdextension @@ -8,4 +8,4 @@ linux.64="res://addons/pythonscript/linux-x86_64/libpythonscript.so" linux.32="res://addons/pythonscript/linux-x86/libpythonscript.so" windows.64="res://addons/pythonscript/windows-x86_64/pythonscript.dll" windows.32="res://addons/pythonscript/windows-x86/pythonscript.dll" -osx.64="res://addons/pythonscript/osx-64/libpythonscript.dylib" +macos.64="res://addons/pythonscript/macos-x86_64/libpythonscript.dylib" diff --git a/tests/pythonscript4_poc/pythonscript.gdextension b/tests/pythonscript4_poc/pythonscript.gdextension index 34f772f7..2feb547c 100644 --- a/tests/pythonscript4_poc/pythonscript.gdextension +++ b/tests/pythonscript4_poc/pythonscript.gdextension @@ -4,8 +4,8 @@ entry_symbol = "pythonscript_init" [libraries] -linux.64="res://addons/pythonscript/linux-64/libpythonscript.so" -linux.32="res://addons/pythonscript/linux-32/libpythonscript.so" -windows.64="res://addons/pythonscript/windows-64/pythonscript.dll" -windows.32="res://addons/pythonscript/windows-32/pythonscript.dll" -osx.64="res://addons/pythonscript/osx-64/libpythonscript.dylib" +linux.64="res://addons/pythonscript/linux-x86_64/libpythonscript.so" +linux.32="res://addons/pythonscript/linux-x86/libpythonscript.so" +windows.64="res://addons/pythonscript/windows-x86_64/pythonscript.dll" +windows.32="res://addons/pythonscript/windows-x86/pythonscript.dll" +macos.64="res://addons/pythonscript/macos-x86_64/libpythonscript.dylib" diff --git a/tests/run.py b/tests/run.py index b3e9f259..06110079 100644 --- a/tests/run.py +++ b/tests/run.py @@ -49,9 +49,9 @@ def fetch_godot_binary(build_dir: Path, version: GodotBinaryVersion) -> Path: "windows-x64": "win64", "windows-amd64": "win64", "windows-x86": "win32", - "osx-x86_64": "osx.universal", - "osx-x64": "osx.universal", - "osx-amd64": "osx.universal", + "darwin-x86_64": "osx.universal", + "darwin-x64": "osx.universal", + "darwin-amd64": "osx.universal", }.get(build_platform) if not godot_build_platform: raise RuntimeError(