diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b44f338..c1509bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,23 +12,25 @@ jobs: build-and-release: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: include: - - os: ubuntu-latest - build_command: make libvinput.so - artifact_name: libvinput.so - os: windows-latest - build_command: make libvinput.dll - artifact_name: libvinput.dll + build_command: make vinput.lib + artifact_name: vinput.lib + extra_setup: echo "No extra setup needed" - os: macos-latest build_command: make libvinput.dylib artifact_name: libvinput.dylib + extra_setup: echo "No extra setup needed" steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.9' + - name: Setup Dependencies + run: ${{ matrix.extra_setup }} - name: Build native binary run: | ${{ matrix.build_command }} @@ -38,16 +40,19 @@ jobs: python -m build --wheel - uses: actions/upload-artifact@v4 with: - name: python-package-distributions + name: python-package-distributions-${{ matrix.os }} path: dist + - name: Install Twine + run: pip install twine + - name: Upload to PyPI if: github.event.inputs.deploy_to_testpypi == 'false' - uses: pypa/gh-action-pypi-publish@v1.8.14 - with: - password: ${{ secrets.PYPI_API_TOKEN }} + run: twine upload dist/* --verbose --skip-existing -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} + env: + TWINE_REPOSITORY_URL: https://upload.pypi.org/legacy/ + - name: Upload to TestPyPI if: github.event.inputs.deploy_to_testpypi == 'true' - uses: pypa/gh-action-pypi-publish@v1.8.14 - with: - repository_url: 'https://test.pypi.org/legacy/' - password: ${{ secrets.TEST_PYPI_API_TOKEN }} + run: twine upload dist/* --verbose --skip-existing -u __token__ -p ${{ secrets.TEST_PYPI_API_TOKEN }} + env: + TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/ diff --git a/.github/workflows/release_linux_sdist.yml b/.github/workflows/release_linux_sdist.yml new file mode 100644 index 0000000..61b7015 --- /dev/null +++ b/.github/workflows/release_linux_sdist.yml @@ -0,0 +1,54 @@ +name: Build + Release Wheels Linux + +on: + workflow_dispatch: + inputs: + deploy_to_testpypi: + description: "Whether the build should be deployed to test.pypi.org instead of regular PyPI" + required: true + default: 'false' + +jobs: + build-and-release: + runs-on: ubuntu-latest + + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.9' + + - name: Install build tools + run: | + python -m pip install --upgrade pip + pip install build twine auditwheel + + - name: Build in manylinux container + run: | + docker pull quay.io/pypa/manylinux1_x86_64 + docker run --rm -v `pwd`:/io quay.io/pypa/manylinux2014_x86_64 /io/ci/build_manylinux.sh build + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd) + + - uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + - name: Determine PyPI target + id: pypi_target + run: | + if [ "${{ github.event.inputs.deploy_to_testpypi }}" == "true" ]; then + echo "::set-output name=repository_url::https://test.pypi.org/legacy/" + echo "::set-output name=api_token::${{ secrets.TEST_PYPI_API_TOKEN }}" + else + echo "::set-output name=repository_url::https://upload.pypi.org/legacy/" + echo "::set-output name=api_token::${{ secrets.PYPI_API_TOKEN }}" + fi + + - name: Upload to PyPI repositories + run: twine upload dist/* --verbose --skip-existing -u __token__ -p ${{ steps.pypi_target.outputs.api_token }} + env: + TWINE_REPOSITORY_URL: ${{ steps.pypi_target.outputs.repository_url }} diff --git a/Makefile b/Makefile index c5d40b9..aa366ec 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,12 @@ wordlogger_mac: wordlogger.c libvinput.dll: src/libvinput.c src/windows_emu.c src/windows.c x86_64-w64-mingw32-gcc $(CFLAGS) -fPIC -o $@ -shared $^ +vinput.lib: src/libvinput.c src/windows_emu.c src/windows.c + x86_64-w64-mingw32-gcc $(CFLAGS) -c src/libvinput.c -o libvinput.o + x86_64-w64-mingw32-gcc $(CFLAGS) -c src/windows_emu.c -o windows_emu.o + x86_64-w64-mingw32-gcc $(CFLAGS) -c src/windows.c -o windows.o + ar rcs vinput.lib libvinput.o windows_emu.o windows.o + wordlogger.exe: libvinput.dll wordlogger.c x86_64-w64-mingw32-gcc $(CFLAGS) wordlogger.c -o $@ -L. -l:$< diff --git a/ci/build_manylinux.sh b/ci/build_manylinux.sh new file mode 100755 index 0000000..b714e76 --- /dev/null +++ b/ci/build_manylinux.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# From: https://github.com/pypa/python-manylinux-demo/blob/master/travis/build-wheels.sh +# which is in the public domain. +# +# This is run inside a CentOS 5 virtual machine to build manylinux wheels: +# +# $ docker run -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/ci/build_manylinux.sh +# + +set -e -x + +action=$1 + +yum --assumeyes install epel-release +yum --assumeyes update +yum --assumeyes install libxdo-devel libX11-devel libXtst-devel libXext-devel + +if [[ $action == "build" ]]; then + # Compile wheels + cd /io + for PYBIN in /opt/python/*/bin; do + if echo "$PYBIN" | grep 3; then + "$PYBIN/python" setup.py bdist_wheel -d ~/wheelhouse/ + fi + done + cd ~ + + # Bundle external shared libraries into the wheels + for whl in wheelhouse/*.whl; do + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/io + auditwheel repair "$whl" -w /io/dist/ + done + +elif [[ $action == "test" ]]; then + # Install packages and test + TOXBIN=/opt/python/cp27-cp27m/bin + "$TOXBIN/pip" install -r /io/requirements/ci.pip + + for PYBIN in /opt/python/*/bin/; do + PYNAME=$("$PYBIN/python" -c "import sys; print('python{0[0]}.{0[1]}'.format(sys.version_info))") + TOXENV=$("$PYBIN/python" -c "import sys; print('py{0[0]}{0[1]}'.format(sys.version_info))") + ln -s "$PYBIN/$PYNAME" /usr/local/bin/$PYNAME + "$TOXBIN/tox" -e $TOXENV + rm -f /usr/local/bin/$PYNAME + #"${PYBIN}/pip" install python-manylinux-demo --no-index -f /io/dist + #(cd "$HOME"; "${PYBIN}/nosetests" pymanylinuxdemo) + done + +else + echo "Need an action to perform!" +fi diff --git a/setup.py b/setup.py index fad9b06..be2bed1 100644 --- a/setup.py +++ b/setup.py @@ -1,18 +1,45 @@ from setuptools import setup, Extension +from setuptools.command.build_ext import build_ext +import subprocess +import sys with open("README.md", "r") as fh: long_description = fh.read() +libraries = ['vinput'] +if sys.platform == 'win32': + libraries.append('User32') + +class CustomBuildExt(build_ext): + def run(self): + lib = "" + if sys.platform == 'win32': + lib = "vinput.lib" + elif sys.platform == 'darwin': + lib = "libvinput.dylib" + else: + lib = "libvinput.so" + protoc_command = ["make", lib] + if subprocess.call(protoc_command) != 0: + sys.exit(-1) + super().run() + module = Extension('libvinput', - sources=['src/pybind.c'], - include_dirs=['src'], - libraries=['vinput'], - library_dirs=['.']) + sources=['src/pybind.c'], + include_dirs=['src'], + libraries=libraries, + library_dirs=['.']) -setup(name='libvinput', - version='1.0', - author='Slendi', - description='Python interface for libvinput', - long_description=long_description, - long_description_content_type="text/markdown", - ext_modules=[module]) +setup( + name='libvinput', + version='1.0', + author='Slendi', + description='Python interface for libvinput', + long_description=long_description, + long_description_content_type="text/markdown", + ext_modules=[module], + cmdclass={ + 'build_ext': CustomBuildExt, # Changed from build_py to build_ext + }, + setup_requires=['wheel'] # Ensure wheel is available for building +)