Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 3 additions & 29 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@ jobs:
name: ${{ matrix.os }} wheel
runs-on: ${{ matrix.os }}
env:
QT_VERSION: "6.5.3"
QT_INSTALL_DIR: Qt
QT_VERSION: "6.5.3" # used in cibuildwheel
strategy:
fail-fast: false
matrix:
# TODO: fix ubuntu-latest
os: [windows-latest, macos-13, macos-latest]
os: [ubuntu-latest, windows-latest, macos-13, macos-latest]
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -38,34 +36,10 @@ jobs:
- uses: astral-sh/setup-uv@v5
- uses: ilammy/msvc-dev-cmd@v1
with:
toolset: "14.29"

- name: Install Qt
shell: bash
run: |
case "${{ runner.os }}" in
Linux)
export QT_HOST_OS=linux
export QT_ARCH=gcc_64
;;
macOS)
export QT_HOST_OS=mac
export QT_ARCH=clang_64
;;
Windows)
export QT_HOST_OS=windows
export QT_ARCH=win64_msvc2019_64
;;
esac

uvx --from aqtinstall aqt install-qt "$QT_HOST_OS" desktop \
"$QT_VERSION" "$QT_ARCH" --outputdir "$QT_INSTALL_DIR" \
--base http://mirrors.ocf.berkeley.edu/qt/
toolset: 14.29

- name: Build wheels via cibuildwheel
uses: pypa/cibuildwheel@v2.22
env:
CIBW_BUILD_VERBOSITY: 1

- name: Upload wheels artifacts
uses: actions/upload-artifact@v4
Expand Down
10 changes: 4 additions & 6 deletions project.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import os
from pathlib import Path
import subprocess

from pyqtbuild import PyQtBindings, PyQtProject

ROOT = Path(__file__).parent


class PyQt6Ads(PyQtProject):
def __init__(self):
super().__init__()
self.bindings_factories = [PyQt6Adsmod]
self.verbose = bool(os.getenv("CI") or os.getenv("CIBUILDWHEEL"))

def apply_user_defaults(self, tool):
if tool == "sdist":
Expand All @@ -19,13 +17,13 @@ def apply_user_defaults(self, tool):
if os.name == "nt":
qmake_path += ".exe"
try:
qmake_bin = str(next(ROOT.rglob(qmake_path)).absolute())
qmake_bin = str(next(Path(self.root_dir).rglob(qmake_path)).absolute())
except StopIteration:
raise RuntimeError(
"qmake not found.\n"
"Please run `uvx --from aqtinstall aqt install-qt <plat> "
"desktop <qtversion> <arch> --outputdir Qt`"
"Please run `uvx --from aqtinstall aqt install-qt ...`"
)
print(f"USING QMAKE: {qmake_bin}")
self.builder.qmake = qmake_bin
return super().apply_user_defaults(tool)

Expand Down
44 changes: 33 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build-backend = "sipbuild.api"
[project]
name = "PyQt6Ads"
# TODO: make dynamic in project.py
version = "4.4.0"
version = "4.4.0"
requires-python = ">=3.9"
description = "Python bindings for Qt Advanced Docking System"
license = { text = "LGPL v2.1" }
Expand Down Expand Up @@ -109,17 +109,39 @@ sources = [

[tool.cibuildwheel]
build-verbosity = 1
skip = ["*-manylinux_i686", "*-musllinux*", "*-win32", "pp*"]
build = ["cp39-*"]
build = [
"cp39-win_amd64",
"cp39-manylinux_x86_64",
"cp39-macosx_x86_64",
"cp39-macosx_arm64",
]
manylinux-x86_64-image = "manylinux_2_28"
build-frontend = "build[uv]"
test-command = "pytest {project}/tests -v"
test-groups = ["test"]

[tool.cibuildwheel.linux]
environment-pass = ["QT_VERSION"]
before-build = [
"yum install -y libxkbcommon-devel",
"uvx --from aqtinstall aqt install-qt linux desktop $QT_VERSION --outputdir Qt --base http://mirrors.ocf.berkeley.edu/qt/",
]
repair-wheel-command = ["python scripts/repair_wheel.py {dest_dir} {wheel}"]
test-command = [
"yum install -y epel-release",
"yum install -y libxkbcommon-x11 xcb-util-cursor xcb-util-wm xcb-util-keysyms xorg-x11-server-Xvfb",
"uv pip install pytest-xvfb",
"pytest {project}/tests -v"
]

[tool.cibuildwheel.macos]
before-build = "uvx --from aqtinstall aqt install-qt mac desktop $QT_VERSION --outputdir Qt --base http://mirrors.ocf.berkeley.edu/qt/"
repair-wheel-command = ["python scripts/repair_wheel.py {dest_dir} {wheel}"]

[tool.cibuildwheel.windows]
before-build = "uvx --from aqtinstall aqt install-qt windows desktop %QT_VERSION% win64_msvc2019_64 --outputdir Qt --base http://mirrors.ocf.berkeley.edu/qt/"


# https://docs.astral.sh/ruff/
[tool.ruff]
line-length = 88
Expand All @@ -130,14 +152,14 @@ fix = true
[tool.ruff.lint]
pydocstyle = { convention = "numpy" }
select = [
"E", # style errors
"F", # flakes
"W", # warnings
"I", # isort
"UP", # pyupgrade
"TC", # flake8-typecheck
"TID", # flake8-tidy-imports
"RUF", # ruff-specific rules
"E", # style errors
"F", # flakes
"W", # warnings
"I", # isort
"UP", # pyupgrade
"TC", # flake8-typecheck
"TID", # flake8-tidy-imports
"RUF", # ruff-specific rules
]

[tool.ruff.lint.per-file-ignores]
Expand Down
30 changes: 23 additions & 7 deletions scripts/repair_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@


def main() -> None:
if sys.platform != "darwin":
if sys.platform == "win32":
# nothing to do on non-Windows platforms
return

dest_dir, wheel, *_ = sys.argv[1:]
Expand All @@ -18,9 +19,11 @@ def main() -> None:
shutil.unpack_archive(wheel, tmp_dir, format="zip")

# fix the rpath in the tmp directory
if sys.platform == "darwin":
for so in Path(tmp_dir).rglob("*.so"):
fix_rpath(so)
for so in Path(tmp_dir).rglob("*.so"):
if sys.platform == "darwin":
fix_rpath_macos(so)
else:
fix_rpath_linux(so)

# re-zip the tmp directory and place it at dest_dir / wheel.name
new_wheel = Path(dest_dir) / Path(wheel).name
Expand All @@ -31,19 +34,32 @@ def main() -> None:
print("Placed the repaired wheel at", new_wheel)


RPATH_RE = re.compile(r"^\s*path (.+) \(offset \d+\)$", re.MULTILINE)
RPATH_RE_MAC = re.compile(r"^\s*path (.+) \(offset \d+\)$", re.MULTILINE)


def fix_rpath(so: Path, new_rpath: str = "@loader_path/PyQt6/Qt6/lib") -> None:
def fix_rpath_macos(so: Path, new_rpath: str = "@loader_path/PyQt6/Qt6/lib") -> None:
# delete all current rpaths
current_rpath = run(["otool", "-l", str(so)], capture_output=True, text=True)
for rpath in RPATH_RE.findall(current_rpath.stdout):
for rpath in RPATH_RE_MAC.findall(current_rpath.stdout):
run(["install_name_tool", "-delete_rpath", rpath, so], check=True)

# add new rpath
run(["install_name_tool", "-add_rpath", new_rpath, so], check=True)
print(f"Updated RPATH for {so} to {new_rpath}")


def fix_rpath_linux(so: Path, new_rpath: str = "$ORIGIN/PyQt6/Qt6/lib") -> None:
# delete all current rpaths
current_rpath = run(
["patchelf", "--print-rpath", str(so)], capture_output=True, text=True
).stdout.strip()

# Remove the old RPATH and add the new one
run(["patchelf", "--remove-rpath", str(so)], check=True)
run(["patchelf", "--set-rpath", new_rpath, str(so)], check=True)

print(f"Updated RPATH for {so} from {current_rpath} to {new_rpath}")


if __name__ == "__main__":
main()
6 changes: 5 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.