diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000..498dbb3 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,157 @@ +# download at https://api.cirrus-ci.com/v1/artifact/github/electronstudio/raylib-python-cffi/pi/binary.zip +# https://api.cirrus-ci.com/v1/artifact/github/electronstudio/raylib-python-cffi/mac/binary.zip + +pi_task: + arm_container: + matrix: + - image: dtcooper/raspberrypi-os:python3.12-bullseye + - image: dtcooper/raspberrypi-os:python3.11-bullseye + - image: dtcooper/raspberrypi-os:python3.10-bullseye + - image: dtcooper/raspberrypi-os:python3.9-bullseye + env: + matrix: + - RAYLIB_PLATFORM: "Desktop" + RAYLIB_OPENGL: "2.1" + - RAYLIB_PLATFORM: "SDL" + RAYLIB_OPENGL: "2.1" + - RAYLIB_PLATFORM: "DRM" + RAYLIB_OPENGL: "ES 2.0" + setup_script: + - apt update + - apt -y install cmake libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev libgbm-dev libdrm-dev + build_sdl_script: + - wget https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.7.tar.gz + - tar xvfz release-2.30.7.tar.gz + - mkdir buildsdl + - cd buildsdl + - cmake ../SDL-release-2.30.7 -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release + - cmake --build . --config Release + - cmake --install . + - cd .. + build_raylib_script: + - git submodule update --init --recursive + - cd raylib-c + - mkdir build + - cd build + - cmake -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + - make -j2 + - make install + build_raylib_again_script: + - cd raylib-c + - mkdir build2 + - cd build2 + - cmake -DPLATFORM=${RAYLIB_PLATFORM} -DOPENGL_VERSION="${RAYLIB_OPENGL}" -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + - make -j2 + - cp raylib/libraylib.a /usr/local/lib/libraylib.a + build_script: + - cp -r raylib-c/src/external/glfw/include/GLFW /usr/local/include/ + - cp physac/src/physac.h /usr/local/include/ + - cp raygui/src/raygui.h /usr/local/include/ + - python -m pip install --break-system-packages --upgrade pip + - python -m pip install --break-system-packages cffi + - python -m pip install --break-system-packages setuptools + - python -m pip install --break-system-packages wheel + - python setup.py bdist_wheel --plat-name manylinux2014_aarch64 + test_script: + - python -m pip install --break-system-packages dist/*.whl + - cd / + - python -c 'import pyray; pyray.init_window(100,100,"test")' >/tmp/output 2>&1 || true + - cat /tmp/output + - if grep -q "INFO: Initializing raylib" /tmp/output; then + - echo "Passed" + - exit 0 + - else + - echo "Failed" + - exit 1 + - fi + artifacts: + path: "dist/*" + +mac_task: + macos_instance: + matrix: + - image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest + env: + MACOSX_DEPLOYMENT_TARGET: "11.0" + matrix: + - env: + PY_VER: "3.9" + RAYLIB_PLATFORM: Desktop + - env: + PY_VER: "3.9" + RAYLIB_PLATFORM: SDL + - env: + PY_VER: "3.10" + RAYLIB_PLATFORM: Desktop + - env: + PY_VER: "3.10" + RAYLIB_PLATFORM: SDL + - env: + PY_VER: "3.11" + RAYLIB_PLATFORM: Desktop + - env: + PY_VER: "3.11" + RAYLIB_PLATFORM: SDL + - env: + PY_VER: "3.12" + RAYLIB_PLATFORM: Desktop + - env: + PY_VER: "3.12" + RAYLIB_PLATFORM: SDL + - env: + PY_VER: "3.13" + RAYLIB_PLATFORM: Desktop + - env: + PY_VER: "3.13" + RAYLIB_PLATFORM: SDL + + + setup_script: + - brew update + - brew install python@${PY_VER} + build_sdl_script: + - wget https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.7.tar.gz + - tar xvfz release-2.30.7.tar.gz + - mkdir buildsdl + - cd buildsdl + - cmake ../SDL-release-2.30.7 -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release + - cmake --build . --config Release + - sudo cmake --install . + - cd .. + build_raylib_script: + - git submodule update --init --recursive + - cd raylib-c + - mkdir build + - cd build + - cmake -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + - make -j8 + - sudo make install + build_raylib_again_script: + - cd raylib-c + - mkdir build2 + - cd build2 + - cmake -DPLATFORM=${RAYLIB_PLATFORM} -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + - make -j8 + - sudo cp raylib/libraylib.a /usr/local/lib/libraylib.a + build_script: + - sudo cp -r raylib-c/src/external/glfw/include/GLFW /usr/local/include/ + - sudo cp physac/src/physac.h /usr/local/include/ + - sudo cp raygui/src/raygui.h /usr/local/include/ + - /opt/homebrew/bin/python${PY_VER} -m pip install --break-system-packages cffi + - /opt/homebrew/bin/python${PY_VER} -m pip install --break-system-packages setuptools + - /opt/homebrew/bin/python${PY_VER} -m pip install --break-system-packages wheel + - /opt/homebrew/bin/python${PY_VER} setup.py bdist_wheel + test_script: + - /opt/homebrew/bin/python${PY_VER} -m pip install --break-system-packages dist/*.whl + - cd / + - /opt/homebrew/bin/python${PY_VER} -c 'import pyray; pyray.init_window(100,100,"test")' >/tmp/output 2>&1 || true + - cat /tmp/output + - if grep -q "INFO: Initializing raylib" /tmp/output; then + - echo "Passed" + - exit 0 + - else + - echo "Failed" + - exit 1 + - fi + artifacts: + path: "dist/*" \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 733b19e..2a3c7f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,30 +4,39 @@ name: Build on: # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ master ] pull_request: - branches: [ master ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: - build-mac: - runs-on: macos-11 + build-mac-intel: + runs-on: macos-13 strategy: matrix: - python-version: [ '3.6', '3.7', '3.8', '3.9', '3.10.0-rc.2' ] + python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.9', 'pypy-3.10', 'pypy-3.11' ] + raylib-platform: ['Desktop', 'SDL'] env: - MACOSX_DEPLOYMENT_TARGET: 10.14 + MACOSX_DEPLOYMENT_TARGET: '10.13' steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive + - name: Build SDL + run: | + wget https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.7.tar.gz + tar xvfz release-2.30.7.tar.gz + mkdir build + cd build + cmake ../SDL-release-2.30.7 -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release + sudo cmake --install . + - name: Setup Python - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v5 with: # Version range or exact version of a Python version to use, using SemVer's version range syntax. python-version: ${{ matrix.python-version }} @@ -35,72 +44,226 @@ jobs: architecture: x64 # Runs a set of commands using the runners shell - - name: Build raylib + - name: Build raylib without SDL because SDL version has incorrect pkg-config run: | cd raylib-c - cd src + mkdir build + cd build + cmake -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. make -j2 - sudo cp libraylib.a /usr/local/lib/libraylib.a + sudo make install + + - name: Build raylib with SDL if selected + run: | + cd raylib-c + mkdir build2 + cd build2 + cmake -DPLATFORM=${{ matrix.raylib-platform }} -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + make -j2 + sudo cp raylib/libraylib.a /usr/local/lib/libraylib.a + + - name: Copy extras + run: | + sudo cp -r raylib-c/src/external/glfw/include/GLFW /usr/local/include/ + sudo cp physac/src/physac.h /usr/local/include/ + sudo cp raygui/src/raygui.h /usr/local/include/ + - name: Build raylib-python-cffi + env: + RAYLIB_PLATFORM: ${{ matrix.raylib-platform }} run: | python -m pip install --upgrade pip - pip3 install cffi + pip3 install "cffi>=1.17.1" pip3 install wheel - python setup.py bdist_wheel + pip3 install setuptools + python setup.py bdist_wheel --plat-name macosx_10_13_x86_64 + + - name: Test + run: | + pip3 install dist/*.whl + cd / + python3 -c 'import pyray; pyray.init_window(100,100,"test")' >/tmp/output 2>&1 || true + cat /tmp/output + if grep -q "INFO: Initializing raylib" /tmp/output; then + echo "Passed" + exit 0 + else + echo "Failed" + exit 1 + fi - name: Upload build Artifact wheel - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4 with: - name: wheel + name: wheel-mac-${{ matrix.raylib-platform }}-${{ matrix.python-version }} path: dist/* +# build-mac-universal: +# runs-on: macos-14 +# strategy: +# matrix: +# python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10' ] +# raylib-platform: ['Desktop', 'SDL'] +# env: +# MACOSX_DEPLOYMENT_TARGET: 11.0 +# steps: +# # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it +# - uses: actions/checkout@v4 +# with: +# submodules: recursive +# +# - name: Build SDL +# run: | +# wget https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.7.tar.gz +# tar xvfz release-2.30.7.tar.gz +# mkdir build +# cd build +# cmake ../SDL-release-2.30.7 -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release +# cmake --build . --config Release +# sudo cmake --install . +# +# - name: Setup Python +# uses: actions/setup-python@v5 +# with: +# # Version range or exact version of a Python version to use, using SemVer's version range syntax. +# python-version: ${{ matrix.python-version }} +# architecture: arm64 +# +# # Runs a set of commands using the runners shell +# - name: Build raylib without SDL because SDL version has incorrect pkg-config +# run: | +# cd raylib-c +# mkdir build +# cd build +# cmake -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. +# make -j2 +# sudo make install +# +# - name: Build raylib with SDL if selected +# run: | +# cd raylib-c +# mkdir build2 +# cd build2 +# cmake -DPLATFORM=${{ matrix.raylib-platform }} -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DPLATFORM=SDL -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. +# make -j2 +# sudo cp raylib/libraylib.a /usr/local/lib/libraylib.a +# +# - name: Copy extras +# run: | +# sudo cp -r raylib-c/src/external/glfw/include/GLFW /usr/local/include/ +# sudo cp physac/src/physac.h /usr/local/include/ +# sudo cp raygui/src/raygui.h /usr/local/include/ +# +# - name: Build raylib-python-cffi +# run: | +# python -m pip install --upgrade pip +# pip3 install "cffi>=1.17.1" +# pip3 install wheel +# pip3 install setuptools +# RAYLIB_PLATFORM=${{ matrix.raylib-platform }} python setup.py bdist_wheel +# +# - name: Upload build Artifact wheel +# uses: actions/upload-artifact@v3.2.1 +# with: +# name: wheel +# path: dist/* + +# +# # Name defaults to universal2 and it technically is, but we override name to arm64. Why don't we make a working universal2 wheel? Because +# # I'd rather have a separate x86_64 that I can test, and I want it to work on 10_15 but I'm not sure a 'macosx_10_15_universal2' is valid +# # given that there is no SDK for universal until macosx_11_0 +# - name: Build raylib-python-cffi +# run: | +# python -m pip install --upgrade pip +# pip3 install cffi +# pip3 install wheel +# python setup.py bdist_wheel --plat-name macosx_12_0_arm64 + + build-linux: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: # You can use PyPy versions in python-version. # For example, pypy2 and pypy3 matrix: - python-version: [ '3.6', '3.7', '3.8', '3.9', '3.10.0-rc.2', 'pypy-3.6', 'pypy-3.7' ] + python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.9', 'pypy-3.10', 'pypy-3.11' ] + raylib-platform: ['Desktop', 'SDL', 'DRM'] steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup Python - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v5 with: # Version range or exact version of a Python version to use, using SemVer's version range syntax. python-version: ${{ matrix.python-version }} # The target architecture (x86, x64) of the Python interpreter. architecture: x64 + - name: install prereqs + run: | + sudo apt update + sudo apt install libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev + - name: Build SDL + run: | + wget https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.7.tar.gz + tar xvfz release-2.30.7.tar.gz + mkdir build + cd build + cmake ../SDL-release-2.30.7 -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release + sudo cmake --install . + # Runs a set of commands using the runners shell - name: Build raylib run: | - sudo apt update - sudo apt install libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev cd raylib-c mkdir build cd build - cmake -DINCLUDE_EVERYTHING=on -DSUPPORT_FILEFORMAT_JPG=on -DWITH_PIC=on -DCMAKE_BUILD_TYPE=Release .. + cmake -DPLATFORM=${{ matrix.raylib-platform }} -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release -DOpenGL_GL_PREFERENCE=GLVND .. make -j2 sudo make install + - name: Copy extras + run: | + sudo cp -r raylib-c/src/external/glfw/include/GLFW /usr/local/include/ + sudo cp physac/src/physac.h /usr/local/include/ + sudo cp raygui/src/raygui.h /usr/local/include/ - name: Build raylib-python-cffi + env: + RAYLIB_PLATFORM: ${{ matrix.raylib-platform }} run: | python -m pip install --upgrade pip - pip3 install cffi + pip3 install "cffi>=1.17.1" pip3 install wheel + pip3 install setuptools python setup.py bdist_wheel --plat-name manylinux2014_x86_64 + - name: Test + run: | + pip3 install dist/*.whl + cd / + python3 -c 'import pyray; pyray.init_window(100,100,"test")' >/tmp/output 2>&1 || true + cat /tmp/output + if grep -q "INFO: Initializing raylib" /tmp/output; then + echo "Passed" + exit 0 + else + echo "Failed" + exit 1 + fi + - name: Upload build Artifact wheel - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4 with: - name: wheel + name: wheel-linux-${{ matrix.raylib-platform }}-${{ matrix.python-version }} path: dist/* + + build-windows: # The type of runner that the job will run on runs-on: windows-2019 @@ -108,15 +271,27 @@ jobs: # You can use PyPy versions in python-version. # For example, pypy2 and pypy3 matrix: - python-version: [ '3.6', '3.7', '3.8', '3.9', '3.10.0-rc.2' ] + python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.9', 'pypy-3.10', 'pypy-3.11' ] + raylib-platform: ['Desktop', 'SDL'] steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive + - name: Download SDL2 + run: curl -L -o SDL2.zip https://github.com/libsdl-org/SDL/releases/download/release-2.30.8/SDL2-devel-2.30.8-VC.zip + + - name: Create extraction directory + run: mkdir ${{ runner.temp }}\SDL2 + + - name: Unzip SDL2 + run: tar -xf SDL2.zip -C ${{ runner.temp }}\SDL2 --strip-components=1 + + - name: Set SDL2_DIR environment variable + run: echo SDL2_DIR=${{ runner.temp }}\SDL2\cmake >> $env:GITHUB_ENV + - name: Setup Python - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v5 with: # Version range or exact version of a Python version to use, using SemVer's version range syntax. python-version: ${{ matrix.python-version }} @@ -124,94 +299,142 @@ jobs: architecture: x64 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v2 - name: Build raylib run: | cd raylib-c mkdir build cd build - cmake -DINCLUDE_EVERYTHING=on -DSUPPORT_FILEFORMAT_JPG=on -DWITH_PIC=on -DCMAKE_BUILD_TYPE=Release .. + cmake -DPLATFORM=${{ matrix.raylib-platform }} -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. msbuild raylib.sln /target:raylib /property:Configuration=Release copy raylib\Release\raylib.lib ..\.. cd ..\.. shell: cmd - name: Build raylib-python-cffi + env: + RAYLIB_PLATFORM: ${{ matrix.raylib-platform }} run: | + copy ${{ runner.temp }}\SDL2\lib\x64\SDL2.lib . + copy ${{ runner.temp }}\SDL2\lib\x64\SDL2.dll raylib\ python -m pip install --upgrade pip - pip3 install cffi + pip3 install "cffi>=1.17.1" pip3 install wheel + pip3 install setuptools del raylib\dynamic\*.so* >nul 2>&1 del raylib\dynamic\*.dll >nul 2>&1 del raylib\dynamic\*.dylib >nul 2>&1 del raylib\dynamic\32bit\* >nul 2>&1 python setup.py bdist_wheel shell: cmd + + - name: Test + shell: bash + run: | + pip3 install --no-deps dist/*.whl + cd / + python3 -c 'import pyray; pyray.init_window(100,100,"test")' >/tmp/output 2>&1 || true + cat /tmp/output + if grep -q "INFO: Initializing raylib" /tmp/output; then + echo "Passed" + exit 0 + else + echo "Failed" + exit 1 + fi - name: Upload build Artifact wheel - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4 with: - name: wheel + name: wheel-windows-${{ matrix.raylib-platform }}-${{ matrix.python-version }} path: dist/* source-distro: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup Python - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v5 with: # Version range or exact version of a Python version to use, using SemVer's version range syntax. - python-version: '3.9' + python-version: '3.12' # The target architecture (x86, x64) of the Python interpreter. architecture: x64 + - name: Build raylib + run: | + sudo apt update + sudo apt install libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev libwayland-dev libxkbcommon-dev + cd raylib-c + mkdir build + cd build + cmake -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + make -j2 + sudo make install + - name: Copy extras + run: | + sudo cp -r raylib-c/src/external/glfw/include/GLFW /usr/local/include/ + sudo cp physac/src/physac.h /usr/local/include/ + sudo cp raygui/src/raygui.h /usr/local/include/ + - name: Build raylib-python-cffi run: | python -m pip install --upgrade pip - pip3 install cffi + pip3 install "cffi>=1.17.1" pip3 install wheel + pip3 install setuptools python setup.py sdist - name: Upload build Artifact wheel - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4 with: - name: wheel + name: wheel-source path: dist/* dynamic-distro: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup Python - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v5 with: # Version range or exact version of a Python version to use, using SemVer's version range syntax. - python-version: '3.9' + python-version: '3.12' # The target architecture (x86, x64) of the Python interpreter. architecture: x64 - name: Build raylib-python-cffi-dynamic run: | python -m pip install --upgrade pip - pip3 install cffi + pip3 install "cffi>=1.17.1" pip3 install wheel + pip3 install setuptools cd dynamic python setup.py sdist - name: Upload build Artifact wheel - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v4 + with: + name: wheel-dynamic + path: dynamic/dist/* + + merge: + needs: [build-mac-intel, build-windows, build-linux, source-distro, dynamic-distro] + runs-on: ubuntu-latest + steps: + - name: Merge All Artifacts + uses: actions/upload-artifact/merge@v4 with: name: wheel - path: dynamic/dist/* + pattern: wheel-* diff --git a/.gitmodules b/.gitmodules index 36278dc..f6c3e1d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "raylib-c"] path = raylib-c - url = https://github.com/raysan5/raylib.git + url = https://git.terah.dev/UnrealXR/raylib.git +[submodule "raygui"] + path = raygui + url = https://github.com/raysan5/raygui.git +[submodule "physac"] + path = physac + url = https://github.com/victorfisac/Physac.git diff --git a/BUILDING.rst b/BUILDING.rst index 9c9185d..a32dd6f 100644 --- a/BUILDING.rst +++ b/BUILDING.rst @@ -4,16 +4,32 @@ Building from source Have Pip build from source -------------------------- -Useful if the binaries don’t work on your system. +This is useful if the binaries don’t work on your system, or you want to use a newer version of Raylib. -Make sure Raylib is installed and then: +First make sure Raylib is installed. On Linux/Mac it must include the pkg-config files. Best way to ensure this +is to compile and install Raylib using CMake: https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux#build-raylib-using-cmake + +Requirements for build: cmake, pkg-config. :: - pip3 install --no-binary raylib --upgrade --force-reinstall raylib + cd raylib-5.0 + mkdir build + cd build + cmake -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + make + sudo make install -Build from source manually --------------------------- + + +Then ask Pip to build from source: + +:: + + pip3 install --no-cache-dir --no-binary raylib --upgrade --force-reinstall raylib + +Or, Build from source manually +------------------------------ Useful if the Pip build doesn’t work and you want to debug it, or you want to contribute to the project. @@ -22,7 +38,7 @@ project. If the Pip build doesn’t work, please submit a bug. (And if you have fixed it, a PR.) -Manual instructions follow, but see also how we actually build the wheels +Manual instructions follow, but are probably outdated, so see instead how we actually build the wheels at https://github.com/electronstudio/raylib-python-cffi/blob/master/.github/workflows/build.yml Windows manual build @@ -56,9 +72,7 @@ Build and install Raylib from the raylib-c directory. copy raylib\Release\raylib.lib ..\.. cd ..\.. -To update the dynamic libs, download the official release, -e.g. https://github.com/raysan5/raylib/releases/download/3.7.0/raylib-3.7.0_win64_msvc16.zip -and extract ``raylib.dll`` into ``dynamic/raylib``. + To build a binary wheel distribution: @@ -69,13 +83,6 @@ To build a binary wheel distribution: pip3 install wheel python setup.py bdist_wheel -Alternatively, if you don’t want the static binaries and just want to -use DLLs with raylib.dynamic: - -:: - - python3 setup_dynamic.py bdist_wheel - Then install it: :: @@ -88,8 +95,6 @@ here.) Linux manual build ~~~~~~~~~~~~~~~~~~~~~~ -These instructions have been tested on Ubuntu 20.10 and 16.04. - Clone this repo including submodules so you get correct version of Raylib. @@ -99,13 +104,17 @@ Raylib. Build and install Raylib from the raylib-c directory. +.. note:: + You can instead use a different version of Raylib you installed from elsewhere, and it should still + work! + :: - sudo apt install cmake libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev + sudo apt install cmake libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev pkg-config cmake cd raylib-python-cffi/raylib-c mkdir build cd build - cmake -DWITH_PIC=on -DCMAKE_BUILD_TYPE=Release .. + cmake -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. sudo make install .. note:: Optional: Build the Raylib shared libs, if you plan to use @@ -123,18 +132,7 @@ Build and install Raylib from the raylib-c directory. cd ../.. -.. note:: Optional: Make a patched version of raylib header. (**Not necessary** if - you’ve already got raylib_modifed.h from repo and haven’t changed - anything.) - - :: - - cd raylib - cp raylib.h raylib_modified.h - patch -p0 `__ -for a Raspberry Pi wheel) diff --git a/MANIFEST.in b/MANIFEST.in index 9446b73..6c6394e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,16 @@ include raylib/*.so include raylib/*.pyi +include pyray/*.pyi include raylib/*.pyd +include raylib/*.dll exclude raylib/*.a include raylib/*.h -include raylib/*.pyi +include raylib/*.h.modified exclude raylib/*.c exclude raylib/*.o +include version.py +exclude tests/* +include raylib/py.typed +include pyray/py.typed + + diff --git a/README.md b/README.md index f696db3..64c695b 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,217 @@ -# Python Bindings for Raylib 3.7 +# Python Bindings for Raylib 5.5 +## Libraries: raymath, raygui, rlgl, physac and GLFW +## Backends: Desktop, SDL, DRM, Web +## Platforms: Windows, Mac, Linux, Raspberry Pi, Web + +![PyPI - Downloads](https://img.shields.io/pypi/dm/raylib) + +Chatroom: [Discord](https://discord.gg/fKDwt85aX6) + +HELP WANTED: [writing examples](https://github.com/electronstudio/raylib-python-cffi/issues/155) + +Features: + +* CFFI API static bindings. +* Automatically generated to be as close as possible to +original Raylib. +* Faster, fewer bugs and easier to maintain than ctypes. +* Commercial-friendly license. +* Docstrings and auto-completion. +* Type checking with Mypy -New CFFI API static bindings. Automatically generated to be as close as possible to -original Raylib. Faster, fewer bugs and easier to maintain than ctypes. Commercial-friendly license. -Docstrings and auto-completion. [Full documentation](http://electronstudio.github.io/raylib-python-cffi) # Quickstart -`pip3 install raylib` - - from pyray import * - init_window(800, 450, "Hello") - while not window_should_close(): - begin_drawing() - clear_background(WHITE) - draw_text("Hello world", 190, 200, 20, VIOLET) - end_drawing() - close_window() - +`pip3 install raylib==5.5.0.2 --break-system-packages` +```python +from pyray import * +init_window(800, 450, "Hello") +while not window_should_close(): + begin_drawing() + clear_background(WHITE) + draw_text("Hello world", 190, 200, 20, VIOLET) + end_drawing() +close_window() +``` # Installation -First make sure you have latest pip installed: +If you are on a modern Linux you will probably want to create a venv: + + python3 -m venv venv + source venv/bin/activate + +Then make sure you have the latest pip installed: python3 -m pip install --upgrade pip Then install - python3 -m pip install raylib + python3 -m pip install setuptools + python3 -m pip install raylib==5.5.0.2 -On most platforms it should install a binary wheel (Windows 10 x64, MacOS 10.15 x64, Linux Ubuntu1804 x64). - -If yours isn't available then pip will attempt to build from source, in which case you will need to have Raylib development libs installed, e.g. +On most platforms it should install a binary wheel. If yours isn't available then pip will attempt to build from +source, in which case you will need to have Raylib development libs installed, e.g. using homebrew, apt, etc. -[If it doesn't work, you can build manually.](BUILDING.md) +## Windows + +Binaries require x64 Windows 10 or newer. (For x86 or older Windows you will have to build from source.) + +Use an [official Windows Python release](https://www.python.org/downloads/windows/) rather than WSL, MSYS, etc. + +## MacOS + +Binaries require: + * arm64 MacOS 14 + * x64 MacOS 10.13, or newer. + +Older MacOS requires building from source but this is usually simple: + + brew install pkg-config + brew install raylib + python3 -m pip install raylib==5.5.0.2 + +(I do have binaries for arm64 MacOS 11, 12 and 13 but I have no way of testing they work, so post an issue +if you want to test them.) + +## Linux + +Binaries require OS newer than Ubuntu 2020, x64 or arm64. Otherwise build from source. +(Pip should attempt automatically but will need Raylib itself installed and also pkg-config.) + +The arm64 binaries are built on Raspberry Pi arm64 Bullseye with OpenGL 2.0 +so may not work on other boards. + +## Raspberry Pi + +[Using on Rasperry Pi](RPI.rst) + +# Backends ## Dynamic binding version There is now a separate dynamic version of this binding: + python3 -m pip uninstall raylib python3 -m pip install raylib_dynamic -[Read this before using raylib_dynamic](https://electronstudio.github.io/raylib-python-cffi/dynamic.html) +It works on some systems where the static version doesn't, [but be sure to read these caveats before using it](https://electronstudio.github.io/raylib-python-cffi/dynamic.html) + +You can't have multiple raylib packages installed at once. + +## SDL backend + +This is not well tested but has better support for controllers: + + python3 -m pip uninstall raylib + python3 -m pip install raylib_sdl + +You can't have multiple raylib packages installed at once. + +## DRM backend + +This uses the Linux framebuffer for devices that don't run X11/Wayland: + + python3 -m pip uninstall raylib + python3 -m pip install raylib_drm + +You can't have multiple raylib packages installed at once. + +## Problems? + +If it doesn't work, [try to build manually.](BUILDING.rst). If that works then [submit an issue](https://github.com/electronstudio/raylib-python-cffi/issues) +to let us know what you did. + +If you need help you can try asking on [our discord](https://discord.gg/fKDwt85aX6). There is also a large [Raylib discord](https://discord.gg/raylib) +for issues that are not Python-specific. + +If it still doesn't work, [submit an issue](https://github.com/electronstudio/raylib-python-cffi/issues). # How to use -There are two APIs, you can use either or both: +There are *two* modules in the raylib package, `raylib` and `pyray`. (There is no separate package for +pyray. Do *not* `pip install pyray`). You can use either or both: ### If you are familiar with C coding and the Raylib C library and you want to use an exact copy of the C API -Use [the C API](https://electronstudio.github.io/raylib-python-cffi/raylib.html). +Use [the raylib module](https://electronstudio.github.io/raylib-python-cffi/raylib.html). -### If you prefer a slightly more Pythonistic API and don't mind it might be slightly slower +### If you prefer a more Pythonistic API -Use [the Python API](https://electronstudio.github.io/raylib-python-cffi/pyray.html). +Use [the pyray module](https://electronstudio.github.io/raylib-python-cffi/pyray.html). +# Running in a web browser +[Pygbag](https://pypi.org/project/pygbag/) >=0.8.7 supports running in a web browser. Usually the latest git version +is recommended. +Make a folder `my_project` with a file `main.py`: + +```python +# /// script +# dependencies = [ +# "cffi", +# "raylib" +# ] +# /// +import asyncio +import platform +from pyray import * + +async def main(): # You MUST have an async main function + init_window(500, 500, "Hello") + platform.window.window_resize() # You MAY want to add this line + while not window_should_close(): + begin_drawing() + clear_background(WHITE) + draw_text("Hello world", 190, 200, 20, VIOLET) + end_drawing() + await asyncio.sleep(0) # You MUST call this in your main loop + close_window() + +asyncio.run(main()) +``` + +Then to create the web files and launch a web server: + + python3.12 -m pip install --user --upgrade pygbag + python3.12 -m pygbag --PYBUILD 3.12 --ume_block 0 --template noctx.tmpl --git my_project + +Point your browser to http://localhost:8000 + +Some features may not work, so you can disable them like this: + +```python +if platform.system() != "Emscripten": # audio may not work on current version of emscripten + init_audio_device() +``` + +This is all done by Pygbag rather than by me, so you should probably contact them with any issues. +Carefully read all their [documentation](https://pygame-web.github.io/). + +It does work for most of [these examples](https://electronstudio.github.io/raylib-python-cffi-pygbag-examples/) + +# App showcase + +[Tempest-raylib](https://github.com/Emtyloc/tempest-raylib) + +[KarabinerKeyboard](https://github.com/bilbofroggins/KarabinerKeyboard) + +[PyTaiko](https://github.com/Yonokid/PyTaiko) + +[DOOM-Clone](https://github.com/StanislavPetrovV/DOOM-Clone) + +[Tanki](https://github.com/pkulev/tanki) + +[Alloy Bloxel Editor](https://pebaz.itch.io/alloy-bloxel-editor) + +[Eidolon](https://github.com/Miou-zora/Eidolon) + +Add your app here! # RLZero @@ -72,29 +224,43 @@ A related library (that is a work in progress!): * Converting more examples from C to Python * Testing on more platforms -# License (updated) +# License -The bindings are now under the Eclipse Public License, so you are free to +Eclipse Public License, so you are free to statically link and use in non-free / proprietary / commercial projects! # Performance -For fastest performance use Pypy rather than standard Python. +If you need more performance, do in this order: -Every call to C is costly, so it's slightly faster if you use Python data structures and functions when calculating +1. Use Pypy rather than standard CPython. It is much, much faster and will make more difference than any other optimisations you might do. + +2. Every call to C is costly, so it's slightly faster if you use Python data structures and functions when calculating in your update loop and then only convert them to C data structures when you have to call the C functions for drawing. +3. The raylib.* functions are potentially *slightly* faster than the pyray.* equivalents, so if you need a tiny bit more performance +you can switch your inner loop functions to these. + +4. There is a version of Python that is faster than Pypy: GraalPy. However it's not fully compatible with all Python +packages. It doesn't work with CFFI and so doesn't work with this binding. But it *is* compatible with the +*Java* binding, Jaylib! There is an example of this here: https://github.com/electronstudio/megabunny/tree/master/raylib-python-jaylib + ## Bunnymark -| Library | Implementation | Bunnies (60 FPS) | Percentage | -| ------------- | ------------- | ------------- | ------------- | -| Raylib 3.7 | C | 168100 | 100% | -| Raylib Python CFFI 3.7 | Pypy 3.7 | 33800 | 20% | -| Raylib Python CFFI 3.7 | Python 3.9 | 7700 | 4.5% | -| Raylib Python CFFI 3.7 | Python 3.9 Nuitka | 8600 | 5.1% | -| Raylib Python CFFI 3.7 Dynamic | Python 3.9 | 6300 | 3.7% | +| Library | Implementation | Bunnies (60 FPS) | Percentage | +|--------------------------------|-------------------|------------------|------------| +| Raylib 5.0 | C | 180000 | 100% | +| Raylib Python CFFI 5.0.0.2 | Python 3.12 | 10500 | 5.8% | +| Raylib Python CFFI 5.0.0.2 | Pypy 3.10 | 95000 | 53% | +| Raylib 3.7 | C | 168100 | 100% | +| Raylib Python CFFI 3.7 | Pypy 3.7 | 33800 | 20% | +| Raylib Python CFFI 3.7 | Python 3.9 | 7700 | 4.5% | +| Raylib Python CFFI 3.7 | Python 3.9 Nuitka | 8600 | 5.1% | +| Raylib Python CFFI 3.7 Dynamic | Python 3.9 | 6300 | 3.7% | + +See also https://github.com/electronstudio/megabunny/ # Packaging your app @@ -109,4 +275,4 @@ You can create a standalone binary using the Nuitka compiler. For example, here [RetroWar: 8-bit Party Battle](https://store.steampowered.com/app/664240/RetroWar_8bit_Party_Battle/?git) is out now. Defeat up to 15 of your friends in a tournament of 80s-inspired retro mini games. [Coding Games With Pygame Zero & Python](https://github.com/electronstudio/pygame-zero-book) is -a book for Python beginners. \ No newline at end of file +a book for Python beginners. diff --git a/RPI.rst b/RPI.rst new file mode 100644 index 0000000..a5c62e3 --- /dev/null +++ b/RPI.rst @@ -0,0 +1,97 @@ +Raspberry Pi +==================== + +Please use Raspberry Pi OS Bookworm. Bullseye should also work. Older OSes are not tested. + +Option 1: Binary wheel +---------------------- + +We have published binary wheels compiled for 64-bit Raspberry OS Bullseye in X11 mode. + +:: + + python -m pip install --break-system-packages raylib + +Alternatively there is a DRM wheel called ``raylib_drm`` to use the framebuffer without X11. You can't have both wheels +installed at once. + +If it doesn't work, or you're not on Bullseye, or you're 32 bit, you will need to compile your own raylib. See below. +For full instructions on this, see https://github.com/raysan5/raylib/wiki/Working-on-Raspberry-Pi . If you need help with this ask Raylib. + +Option 2: Compile Raylib from source X11 mode +--------------------------------------------- + +This should work for everyone. + +:: + + sudo apt update + sudo apt install python3-pip cmake libegl1-mesa-dev libgbm-dev libgles2-mesa-dev libdrm-dev libglfw3-dev + git clone https://github.com/raysan5/raylib.git --branch 5.0 --single-branch + cd raylib + mkdir build + rm -rf build/* + cd build + cmake -DPLATFORM="Desktop" -DOPENGL_VERSION=2.1 -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DWITH_PIC=ON -DCMAKE_BUILD_TYPE=Release .. + make + sudo make install + sudo cp -r ../src/external/glfw/include/GLFW /usr/local/include/ + +Then have pip compile and install the wheel: + +:: + + python3 -m pip install --break-system-packages setuptools + python3 -m pip install --no-cache-dir --no-binary raylib --upgrade --force-reinstall --break-system-packages raylib==5.5.0.0 + +Option 3: Compile Raylib from source DRM mode +--------------------------------------------- + +This seems to work on Raspberry Pi 4. Note you must not be running X11 when you run your programs. + +If you have ever installed Raylib or raylib-python-cffi before, remove all traces of it: + +:: + + sudo apt remove raylib raylib-dev libraylib libraylib-dev + sudo rm /usr/local/lib/pkgconfig/raylib.pc + sudo rm -rf /usr/local/lib/libraylib.* /usr/lib/libraylib.* + +Remove all GLFW: + +:: + + sudo apt remove libglfw3-dev libglfw3 + sudo rm -rf /usr/local/include/GLFW + +Build a shared lib version of Raylib in DRM mode and install to /usr: + +:: + + sudo apt update + sudo apt install python3-pip cmake libegl1-mesa-dev libgbm-dev libgles2-mesa-dev libdrm-dev + git clone https://github.com/raysan5/raylib.git --branch 5.0 --single-branch + cd raylib + mkdir build + rm rf build/* + cd build + cmake -DPLATFORM="DRM" -DBUILD_EXAMPLES=OFF -DCUSTOMIZE_BUILD=ON -DSUPPORT_FILEFORMAT_JPG=ON -DSUPPORT_FILEFORMAT_FLAC=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr .. + make + sudo make install + + +Then have pip compile and install the wheel: + +:: + + python3 -m pip install --break-system-packages setuptools + python3 -m pip install --no-cache-dir --no-binary raylib --upgrade --force-reinstall --break-system-packages raylib==5.5.0.0 + + + + +.. attention:: + + If you intend to use the Broadcom proprietary Open GL ES 2.0 drivers (the ones installed by Raspbian into ``/opt/vc`` and compiled in Raylib + with ``PLATFORM_RPI``) be aware they not work with Bullseye and have not been tested with the bindings. They will probably + require additional linker arguments to be added to ``build.py``. Suggest you try ``PLATFORM_DRM`` instead. diff --git a/create_define_consts.py b/create_define_consts.py new file mode 100644 index 0000000..6817e4c --- /dev/null +++ b/create_define_consts.py @@ -0,0 +1,105 @@ +# Copyright (c) 2021 Richard Smith and others +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# licenses when the conditions for such availability set forth in the Eclipse +# Public License, v. 2.0 are satisfied: GNU General Public License, version 2 +# with the GNU Classpath Exception which is +# available at https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +from raylib import rl, ffi + +from inspect import ismethod, getmembers, isbuiltin +import inflection, sys, json, re + +two_or = re.compile(r'''\(\s*([^\s]*)\s*\|\s*([^\s]*)\s*\)''') +three_or = re.compile(r'''\(\s*([^\s]*)\s*\|\s*([^\s]*)\s*\|\s*(^\s*)\s*\)''') +two_mult = re.compile(r'''\(\s*([^\s]*)\s*\*\s*([^\s]*)\s*\)''') +two_div = re.compile(r'''\(\s*([^\s]*)\s*/\s*([^\s]*)\s*\)''') + +def process(filename): + f = open(filename, "r") + js = json.load(f) + known_define = [] + known_enum = [] + for e in js['enums']: + if e['name'] and e['values']: + for v in e['values']: + if v['name']: + known_enum.append(str(v['name']).strip()) + for e in js['defines']: + if e['type'] in ('INT', 'FLOAT', 'STRING'): + if e['type'] == 'INT': + print(e['name'] + ": int = " + str(e['value']).strip()) + elif e['type'] == 'FLOAT': + print(e['name'] + ": float = " + str(e['value']).strip()) + else: + print(e['name'] + ": str = \"" + str(e['value']).strip() + '"') + known_define.append(str(e['name']).strip()) + elif e['type'] == "UNKNOWN": + strval = str(e['value']).strip() + if strval.startswith("__"): + continue + if strval in known_enum: + print(e['name'] + " = raylib." + strval) + elif strval in known_define: + print(e['name'] + " = " + strval) + else: + matches = two_or.match(strval) + if not matches: + matches = three_or.match(strval) + if matches: + match_defs = [str(m).strip() for m in matches.groups()] + if all(d in known_enum for d in match_defs): + print(e['name'] + " = " + " | ".join(("raylib."+m) for m in match_defs)) + elif all(d in known_define for d in match_defs): + print(e['name'] + " = " + " | ".join(match_defs)) + else: + continue + known_define.append(str(e['name']).strip()) + elif e['type'] == "FLOAT_MATH": + strval = str(e['value']).strip() + matches = two_mult.match(strval) + if matches: + match_defs = [str(m).strip() for m in matches.groups()] + match_parts = [] + for m in match_defs: + if "." in m: + match_parts.append(m.rstrip("f")) + else: + match_parts.append(m) + if all(d in known_enum for d in match_parts): + print(e['name'] + " = " + " * ".join(("raylib." + m) for m in match_parts)) + elif all(d in known_define for d in match_parts): + print(e['name'] + " = " + " * ".join(match_parts)) + else: + matches = two_div.match(strval) + if matches: + match_defs = [str(m).strip() for m in matches.groups()] + match_parts = [] + for m in match_defs: + if "." in m: + match_parts.append(m.rstrip("f")) + else: + match_parts.append(m) + if any(d in known_enum for d in match_parts): + print(e['name'] + " = " + " / ".join(("raylib." + m) for m in match_parts)) + elif any(d in known_define for d in match_parts): + print(e['name'] + " = " + " / ".join(match_parts)) + else: + continue + +print("import raylib\n") + +process("raylib.json") +process("raymath.json") +process("rlgl.json") +process("raygui.json") +process("physac.json") +process("glfw3.json") + diff --git a/create_enums.py b/create_enums.py new file mode 100644 index 0000000..d7fbeec --- /dev/null +++ b/create_enums.py @@ -0,0 +1,38 @@ +# Copyright (c) 2021 Richard Smith and others +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# licenses when the conditions for such availability set forth in the Eclipse +# Public License, v. 2.0 are satisfied: GNU General Public License, version 2 +# with the GNU Classpath Exception which is +# available at https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +from raylib import rl, ffi + +from inspect import ismethod, getmembers, isbuiltin +import inflection, sys, json + +def process(filename): + f = open(filename, "r") + js = json.load(f) + + for e in js['enums']: + if e['name'] and e['values']: + print ("class "+e['name']+"("+"IntEnum):") + print(f' """{e['description']}."""') + for value in e['values']: + print(" "+value['name']+" = "+str(value['value'])) + print("") + +print("""from enum import IntEnum +""") + +process("raylib.json") +process("raygui.json") +process("glfw3.json") +process("physac.json") diff --git a/create_stub_pyray.py b/create_stub_pyray.py index 69b61c3..fb334e7 100644 --- a/create_stub_pyray.py +++ b/create_stub_pyray.py @@ -12,13 +12,32 @@ # # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +from pathlib import Path from raylib import rl, ffi - +from pyray import _underscore from inspect import ismethod, getmembers, isbuiltin import inflection, sys, json -f = open("raylib_api.json", "r") -js = json.load(f) +known_functions = {} +known_structs = {} +for filename in (Path("raylib.json"), Path("raymath.json"), Path("rlgl.json"), Path("raygui.json"), Path("physac.json"), + Path("glfw3.json")): + f = open(filename, "r") + js = json.load(f) + for fn in js["functions"]: + if fn["name"] not in known_functions: + known_functions[fn["name"]] = fn + for st in js["structs"]: + if st["name"] not in known_structs: + known_structs[st["name"]] = st + for e in js['enums']: + if e['name'] and e['values']: + print("class "+e['name']+"(int):") + print(f' """{e['description']}."""') + for value in e['values']: + print(" "+value['name']+" = "+str(value['value'])) + print("") + def ctype_to_python_type(t): if t == '_Bool': @@ -27,48 +46,71 @@ def ctype_to_python_type(t): return 'None' elif t == "long": return "int" + elif t == "unsigned long long": + return "int" + elif t == "uint64_t": + return "int" + elif t == "short": + return "int" + elif t == "unsigned short": + return "int" elif t == "double": return "float" + elif "char * *" in t: + return "list[str]" elif "char *" in t: return "str" - elif "char" in t: + elif t == "char": return "str" # not sure about this one + elif t == "unsigned char": + return "int" elif "*" in t: return "Any" + elif "[" in t: + return "list" # TODO FIXME type of items in the list elif t.startswith("struct"): - return t.replace("struct ","") + return t.replace("struct ", "") elif t.startswith("unsigned"): return t.replace("unsigned ", "") + elif t.startswith("enum"): + return t.replace("enum ", "") else: return t + print("""from typing import Any +from warnings import deprecated +import _cffi_backend # type: ignore - -def pointer(struct): - ... +ffi: _cffi_backend.FFI +PhysicsShapeType = int """) - - +# These words can be used for c arg names, but not in python +reserved_words = ("in", "list", "tuple", "set", "dict", "from", "range", "min", "max", "any", "all", "len") for name, attr in getmembers(rl): - uname = inflection.underscore(name).replace('3_d', '_3d').replace('2_d', '_2d') + uname = _underscore(name) if isbuiltin(attr) or str(type(attr)) == "": - json_array = [x for x in js['functions'] if x['name'] == name] - json_object = {} - if len(json_array) > 0: - json_object = json_array[0] + json_object = known_functions.get(name, None) + if json_object is None: + # this is _not_ an exported function from raylib, raymath, rlgl raygui or physac + # so we don't want it in the pyray API + continue sig = "" for i, arg in enumerate(ffi.typeof(attr).args): param_name = arg.cname.replace("struct", "").replace("char *", "str").replace("*", "_pointer").replace( - " ", "")+"_"+str(i) + " ", "") + "_" + str(i) if 'params' in json_object: p = json_object['params'] - param_name = list(p)[i] - + param_name = list(p)[i]['name'] + # don't use a python reserved word: + if param_name in reserved_words: + param_name = param_name + "_" + str(i) param_type = ctype_to_python_type(arg.cname) + if "struct" in arg.cname: + param_type += "|list|tuple" sig += f"{param_name}: {param_type}," return_type = ffi.typeof(attr).result.cname @@ -78,40 +120,77 @@ for name, attr in getmembers(rl): if 'description' in json_object: description = json_object['description'] - print( - f'def {uname}({sig}) -> {ctype_to_python_type(return_type)}:\n """{description}"""\n ...') + if 'physics' in uname: + print('@deprecated("Raylib no longer recommends the use of Physac library")') + print(f'def {uname}({sig}) -> {ctype_to_python_type(return_type)}:') + print(f' """{description}."""') + print(f' ...') elif str(type(attr)) == "": return_type = ffi.typeof(attr).result.cname print( f'def {uname}(*args) -> {ctype_to_python_type(return_type)}:\n """VARARG FUNCTION - MAY NOT BE SUPPORTED BY CFFI"""\n ...') else: - #print("*****", str(type(attr))) - print(f"{name}: {str(type(attr))[8:-2]}") # this isolates the type + # print("*****", str(type(attr))) + t = str(type(attr))[8:-2] # this isolates the type + if t != "int": + print(f"{name}: {t}") for struct in ffi.list_types()[0]: print("processing", struct, file=sys.stderr) - # json_array = [x for x in js['structs'] if x['name'] == name] - # json_object = {} - # if len(json_array) > 0: - # json_object = json_array[0] + if ffi.typeof(struct).kind == "struct": + json_object = known_structs.get(struct, None) + if json_object is None: + # this is _not_ an exported struct from raylib, raymath, rlgl raygui or physac + # so we don't want it in the pyray API + continue if ffi.typeof(struct).fields is None: - print("weird empty struct, skipping", file=sys.stderr) - break + print("weird empty struct, skipping " + struct, file=sys.stderr) + continue print(f"class {struct}:") - print(f' """ struct """') + print(f' """{known_structs[struct]['description']}."""') sig = "" for arg in ffi.typeof(struct).fields: - sig += ", " + arg[0] + ptype = ctype_to_python_type(arg[1].type.cname) + if arg[1].type.kind == "struct": + ptype += "|list|tuple" + sig += f", {arg[0]}: {ptype}|None = None" print(f" def __init__(self{sig}):") for arg in ffi.typeof(struct).fields: - print(f" self.{arg[0]}={arg[0]}") + print(f" self.{arg[0]}:{ctype_to_python_type(arg[1].type.cname)} = {arg[0]} # type: ignore") - elif ffi.typeof(struct).kind == "enum": - print(f"{struct}: int") + # elif ffi.typeof(struct).kind == "enum": + # print(f"{struct}: int") else: - print("ERROR UNKNOWN TYPE", ffi.typeof(struct), file=sys.stderr) - + print("WARNING: SKIPPING UNKNOWN TYPE", ffi.typeof(struct), file=sys.stderr) +print(""" +LIGHTGRAY : Color +GRAY : Color +DARKGRAY : Color +YELLOW : Color +GOLD : Color +ORANGE : Color +PINK : Color +RED : Color +MAROON : Color +GREEN : Color +LIME : Color +DARKGREEN : Color +SKYBLUE : Color +BLUE : Color +DARKBLUE : Color +PURPLE : Color +VIOLET : Color +DARKPURPLE : Color +BEIGE : Color +BROWN : Color +DARKBROWN : Color +WHITE : Color +BLACK : Color +BLANK : Color +MAGENTA : Color +RAYWHITE : Color +""") diff --git a/create_stub_static.py b/create_stub_static.py index 26631ec..0c43c6d 100644 --- a/create_stub_static.py +++ b/create_stub_static.py @@ -12,15 +12,23 @@ # # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +from pathlib import Path from raylib import rl, ffi - from inspect import ismethod, getmembers, isbuiltin import inflection, sys, json -f = open("raylib_api.json", "r") -js = json.load(f) - - +known_functions = {} +known_structs = {} +for filename in (Path("raylib.json"), Path("raymath.json"), Path("rlgl.json"), Path("raygui.json"), Path("physac.json"), + Path("glfw3.json")): + f = open(filename, "r") + js = json.load(f) + for fn in js["functions"]: + if fn["name"] not in known_functions: + known_functions[fn["name"]] = fn + for st in js["structs"]: + if st["name"] not in known_structs: + known_structs[st["name"]] = st def ctype_to_python_type(t): @@ -30,44 +38,75 @@ def ctype_to_python_type(t): return 'None' elif t == "long": return "int" + elif t == "unsigned long long": + return "int" + elif t == "uint64_t": + return "int" + elif t == "short": + return "int" + elif t == "unsigned short": + return "int" elif t == "double": return "float" + elif "char * *" in t: + return "list[bytes]" elif "char *" in t: - return "str" + return "bytes" elif "char" in t: - return "str" # not sure about this one + return "bytes" # not sure about this one elif "*" in t: return "Any" + elif "[" in t: + return "list" # TODO FIXME type of items in the list elif t.startswith("struct"): - return t.replace("struct ","") + return t.replace("struct ", "") elif t.startswith("unsigned"): return t.replace("unsigned ", "") + elif t.startswith("enum"): + return t.replace("enum ", "") else: return t print("""from typing import Any +from warnings import deprecated +import _cffi_backend # type: ignore + +ffi: _cffi_backend.FFI +rl: _cffi_backend.Lib +PhysicsShapeType = int class struct: ... """) +# These words can be used for c arg names, but not in python +reserved_words = ("in", "list", "tuple", "set", "dict", "from", "range", "min", "max", "any", "all", "len") + for name, attr in getmembers(rl): uname = name if isbuiltin(attr) or str(type(attr)) == "": - json_array = [x for x in js['functions'] if x['name'] == name] - json_object = {} - if len(json_array) > 0: - json_object = json_array[0] + json_object = known_functions.get(name, {}) sig = "" for i, arg in enumerate(ffi.typeof(attr).args): - param_name = arg.cname.replace("struct", "").replace("char *", "str").replace("*", - "_pointer").replace( - " ", "")+"_"+str(i) + if ")(" in arg.cname: + # fn signature in arg types + param_name = str(arg.cname).split("(", 1)[0] + "_callback_" + str(i) + else: + param_name = arg.cname.replace("struct", "").replace("char *", "str").replace("*", + "_pointer").replace(" ", + "") + "_" + str( + i) if 'params' in json_object: p = json_object['params'] - param_name = list(p)[i] + # print("param_name: ", param_name, "i", i, "params: ",p,file=sys.stderr) + param_name = list(p)[i]['name'] + # don't use a python reserved word: + if param_name in reserved_words: + param_name = param_name + "_" + str(i) param_type = ctype_to_python_type(arg.cname) + if "struct" in arg.cname: + param_type += "|list|tuple" sig += f"{param_name}: {param_type}," return_type = ffi.typeof(attr).result.cname @@ -76,15 +115,19 @@ for name, attr in getmembers(rl): if 'description' in json_object: description = json_object['description'] - print( - f'def {uname}({sig}) -> {ctype_to_python_type(return_type)}:\n """{description}"""\n ...') + if 'Physics' in uname: + print('@deprecated("Raylib no longer recommends the use of Physac library")') + print(f'def {uname}({sig}) -> {ctype_to_python_type(return_type)}:') + print(f' """{description}."""') + print(f' ...') + elif str(type(attr)) == "": return_type = ffi.typeof(attr).result.cname print( f'def {uname}(*args) -> {ctype_to_python_type(return_type)}:\n """VARARG FUNCTION - MAY NOT BE SUPPORTED BY CFFI"""\n ...') else: - #print("*****", str(type(attr))) + # print("*****", str(type(attr))) print(f"{name}: {str(type(attr))[8:-2]}") # this isolates the type for struct in ffi.list_types()[0]: @@ -92,19 +135,52 @@ for struct in ffi.list_types()[0]: if ffi.typeof(struct).kind == "struct": # if ffi.typeof(struct).fields is None: # print("weird empty struct, skipping", file=sys.stderr) - # break - print(f"{struct}: struct") + # continue + print(f"class {struct}:") # sig = "" - # for arg in ffi.typeof(struct).fields: - # sig += ", " + arg[0] + fields = ffi.typeof(struct).fields + if fields is not None: + #print(ffi.typeof(struct).fields) + #print(f" {arg}: {arg}") # print(f" def __init__(self{sig}):") # - # for arg in ffi.typeof(struct).fields: + for arg in ffi.typeof(struct).fields: + print(f" {arg[0]}: {ctype_to_python_type(arg[1].type.cname)}") + else: + print(" ...") # print(f" self.{arg[0]}={arg[0]}") elif ffi.typeof(struct).kind == "enum": - print(f"{struct}: int") + print(f"{struct} = int") else: - print("ERROR UNKNOWN TYPE", ffi.typeof(struct), file=sys.stderr) + print("WARNING: SKIPPING UNKNOWN TYPE", ffi.typeof(struct), file=sys.stderr) +print(""" +LIGHTGRAY : Color +GRAY : Color +DARKGRAY : Color +YELLOW : Color +GOLD : Color +ORANGE : Color +PINK : Color +RED : Color +MAROON : Color +GREEN : Color +LIME : Color +DARKGREEN : Color +SKYBLUE : Color +BLUE : Color +DARKBLUE : Color +PURPLE : Color +VIOLET : Color +DARKPURPLE : Color +BEIGE : Color +BROWN : Color +DARKBROWN : Color +WHITE : Color +BLACK : Color +BLANK : Color +MAGENTA : Color +RAYWHITE : Color +""") diff --git a/docs-src/RPI.rst b/docs-src/RPI.rst new file mode 120000 index 0000000..60349ff --- /dev/null +++ b/docs-src/RPI.rst @@ -0,0 +1 @@ +../RPI.rst \ No newline at end of file diff --git a/docs-src/index.rst b/docs-src/index.rst index de6618c..2177832 100644 --- a/docs-src/index.rst +++ b/docs-src/index.rst @@ -15,6 +15,7 @@ Raylib Python raylib dynamic BUILDING + RPI * :ref:`search` diff --git a/docs-src/pyray.rst b/docs-src/pyray.rst index dfd9b81..2a2ac0e 100644 --- a/docs-src/pyray.rst +++ b/docs-src/pyray.rst @@ -1,18 +1,18 @@ Python API ============== -.. comment:: - - Link to API reference: - toctree:: - :maxdepth: 1 - - autoapi/pyray/index - - This is a wrapper around the C API with some syntactic sugar. -The API is *still the same as Raylib*, so you should still reply on `the official Raylib docs `_, except: +The API is *still the same as Raylib*, so you should still reply on: + +* `the C Raylib docs `_ + +* `the C Raylib examples `_ + +* `the C Raylib header file `_ + + +The *differences* are: * the function names are in **snake_case**. @@ -20,6 +20,9 @@ The API is *still the same as Raylib*, so you should still reply on `the officia * There are some helper functions to create structures. +Examples +-------- + Example program: .. code-block:: @@ -30,10 +33,9 @@ Example program: pr.set_target_fps(60) camera = pr.Camera3D([18.0, 16.0, 18.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], 45.0, 0) - pr.set_camera_mode(camera, pr.CAMERA_ORBITAL) while not pr.window_should_close(): - pr.update_camera(camera) + pr.update_camera(camera, pr.CAMERA_ORBITAL) pr.begin_drawing() pr.clear_background(pr.RAYWHITE) pr.begin_mode_3d(camera) @@ -54,9 +56,9 @@ Example program: init_window(800, 450, "Raylib texture test") ... - You don't need to use the PyRay() class anymore. -See also https://github.com/electronstudio/raylib-python-cffi/blob/master/tests/test_pyray.py +`See all examples here `_ + API reference ------------- diff --git a/docs-src/raylib.rst b/docs-src/raylib.rst index 51d5f73..b3119dd 100644 --- a/docs-src/raylib.rst +++ b/docs-src/raylib.rst @@ -1,7 +1,8 @@ C API ============= -The goal of the C API is make usage as similar to the original C as CFFI will allow. The `example programs `_ +The goal of the C API is make usage as similar to the original C as CFFI will allow. +So the `example programs `_ are very, very similar to the C originals. Example program: @@ -14,10 +15,9 @@ Example program: SetTargetFPS(60) camera = ffi.new("struct Camera3D *", [[18.0, 16.0, 18.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], 45.0, 0]) - SetCameraMode(camera[0], CAMERA_ORBITAL) while not WindowShouldClose(): - UpdateCamera(camera) + UpdateCamera(camera, CAMERA_ORBITAL) BeginDrawing() ClearBackground(RAYWHITE) BeginMode3D(camera[0]) @@ -39,11 +39,16 @@ If you want to be more portable (i.e. same code will work with dynamic bindings) ... -See also https://github.com/electronstudio/raylib-python-cffi/blob/master/tests/test_static.py .. note:: Whenever you need to convert stuff between C and Python see https://cffi.readthedocs.io -.. important:: Your **primary reference** should always be `the official Raylib docs `_ +.. important:: Your **primary reference** should always be + + * `the C Raylib docs `_ + + * `the C Raylib examples `_ + + * `the C Raylib header file `_ However, here is a list of available functions: diff --git a/docs/.buildinfo b/docs/.buildinfo index 536dc0f..48257a1 100644 --- a/docs/.buildinfo +++ b/docs/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 76ca504c87a97062e6f8d0973741212b +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: f2032a6434b52f7c68551d0ad70d555b tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/BUILDING.html b/docs/BUILDING.html index cc3f946..b3a1d99 100644 --- a/docs/BUILDING.html +++ b/docs/BUILDING.html @@ -1,21 +1,26 @@ + + - + - + + - Building from source — Raylib Python documentation - - - - - - - + Building from source — Raylib Python documentation + + + + + + + + + + + @@ -24,25 +29,32 @@