mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
improve PyQt install
- use a single script for all PyQt versions - add hashes - add a new ./run-qt5.14 script for testing with PyQt5.14
This commit is contained in:
parent
70292f07a6
commit
a14eb6a1e8
23 changed files with 346 additions and 413 deletions
23
defs.bzl
23
defs.bzl
|
@ -7,8 +7,7 @@ load("//proto:protobuf.bzl", "setup_protobuf_binary")
|
|||
load("//proto:clang_format.bzl", "setup_clang_format")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories", "yarn_install")
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_repositories")
|
||||
load("//python/pyqt5:defs.bzl", "install_pyqt5")
|
||||
load("//python/pyqt6:defs.bzl", "install_pyqt6")
|
||||
load("//python/pyqt:defs.bzl", "install_pyqt")
|
||||
load("@rules_python//python:pip.bzl", "pip_parse")
|
||||
|
||||
anki_version = "2.1.49"
|
||||
|
@ -40,14 +39,22 @@ def setup_deps():
|
|||
extra_pip_args = ["--require-hashes"],
|
||||
)
|
||||
|
||||
install_pyqt5(
|
||||
name = "pyqt5",
|
||||
python_runtime = "@python//:python",
|
||||
)
|
||||
|
||||
install_pyqt6(
|
||||
install_pyqt(
|
||||
name = "pyqt6",
|
||||
python_runtime = "@python//:python",
|
||||
requirements = "//python/pyqt:6_2/requirements.txt",
|
||||
)
|
||||
|
||||
install_pyqt(
|
||||
name = "pyqt515",
|
||||
python_runtime = "@python//:python",
|
||||
requirements = "//python/pyqt:5_15/requirements.txt",
|
||||
)
|
||||
|
||||
install_pyqt(
|
||||
name = "pyqt514",
|
||||
python_runtime = "@python//:python",
|
||||
requirements = "//python/pyqt:5_14/requirements.txt",
|
||||
)
|
||||
|
||||
node_repositories(
|
||||
|
|
|
@ -3,15 +3,7 @@ Sadly this is complicated by the fact that Python can only tell us which transit
|
|||
are required by actually installing packages, and if you run pip-tools on a Mac or Linux machine,
|
||||
it will miss packages that are required on Windows and vice versa.
|
||||
|
||||
So we're stuck manually merging dependencies for now. To update deps:
|
||||
Currently the Windows dependencies are a strict superset, so the package locks need to be generated
|
||||
on a Windows machine. To do so, run "bazel run update" from this folder.
|
||||
|
||||
- run 'bazel run update' to update requirements.txt for the current
|
||||
platform
|
||||
- consult the git diff, and manually merge the changes, undoing the removal
|
||||
of items pinned on other platforms
|
||||
- repeat the process on the other platform
|
||||
- run the tests to ensure nothing has broken on either platform
|
||||
- commit the changes to requirements.txt
|
||||
|
||||
At the time of writing, Macs and Linux machines have identical output - it is only
|
||||
Windows that differs. But we should not assume that will always be the case.
|
||||
pyqt is handled separately - see pyqt/
|
||||
|
|
3
python/pyqt/5_14/requirements.in
Normal file
3
python/pyqt/5_14/requirements.in
Normal file
|
@ -0,0 +1,3 @@
|
|||
pyqt5==5.14.1
|
||||
pyqtwebengine==5.14.0
|
||||
pyqt5_sip==12.8.1
|
42
python/pyqt/5_14/requirements.txt
Normal file
42
python/pyqt/5_14/requirements.txt
Normal file
|
@ -0,0 +1,42 @@
|
|||
pyqt5==5.14.1 \
|
||||
--hash=sha256:2d94ec761fb656707050c68b41958e3a9f755bb1df96c064470f4096d2899e32 \
|
||||
--hash=sha256:2f230f2dbd767099de7a0cb915abdf0cbc3256a0b5bb910eb09b99117db7a65b \
|
||||
--hash=sha256:31b142a868152d60c6323e0527edb692fdf05fd7cb4fe2fe9ce07d1ce560221a \
|
||||
--hash=sha256:713b9a201f5e7b2fca8691373e5d5c8c2552a51d87ca9ffbb1461e34e3241211 \
|
||||
--hash=sha256:a0bfe9fd718bca4de3e33000347e048f73126b6dc46530eb020b0251a638ee9d
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqtwebengine
|
||||
pyqt5-sip==12.8.1 \
|
||||
--hash=sha256:0304ca9114b9817a270f67f421355075b78ff9fc25ac58ffd72c2601109d2194 \
|
||||
--hash=sha256:0cd969be528c27bbd4755bd323dff4a79a8fdda28215364e6ce3e069cb56c2a9 \
|
||||
--hash=sha256:2f35e82fd7ec1e1f6716e9154721c7594956a4f5bd4f826d8c6a6453833cc2f0 \
|
||||
--hash=sha256:30e944db9abee9cc757aea16906d4198129558533eb7fadbe48c5da2bd18e0bd \
|
||||
--hash=sha256:34dcd29be47553d5f016ff86e89e24cbc5eebae92eb2f96fb32d2d7ba028c43c \
|
||||
--hash=sha256:5a011aeff89660622a6d5c3388d55a9d76932f3b82c95e82fc31abd8b1d2990d \
|
||||
--hash=sha256:6c1ebee60f1d2b3c70aff866b7933d8d8d7646011f7c32f9321ee88c290aa4f9 \
|
||||
--hash=sha256:7b81382ce188d63890a0e35abe0f9bb946cabc873a31873b73583b0fc84ac115 \
|
||||
--hash=sha256:832fd60a264de4134c2824d393320838f3ab648180c9c357ec58a74524d24507 \
|
||||
--hash=sha256:84ba7746762bd223bed22428e8561aa267a229c28344c2d28c5d5d3f8970cffb \
|
||||
--hash=sha256:9312ec47cac4e33c11503bc1cbeeb0bdae619620472f38e2078c5a51020a930f \
|
||||
--hash=sha256:a1b8ef013086e224b8e86c93f880f776d01b59195bdfa2a8e0b23f0480678fec \
|
||||
--hash=sha256:a29e2ac399429d3b7738f73e9081e50783e61ac5d29344e0802d0dcd6056c5a2 \
|
||||
--hash=sha256:b6d42250baec52a5f77de64e2951d001c5501c3a2df2179f625b241cbaec3369 \
|
||||
--hash=sha256:bb5a87b66fc1445915104ee97f7a20a69decb42f52803e3b0795fa17ff88226c \
|
||||
--hash=sha256:c317ab1263e6417c498b81f5c970a9b1af7acefab1f80b4cc0f2f8e661f29fc5 \
|
||||
--hash=sha256:c9800729badcb247765e4ffe2241549d02da1fa435b9db224845bc37c3e99cb0 \
|
||||
--hash=sha256:c9d6d448c29dc6606bb7974696608f81f4316c8234f7c7216396ed110075e777 \
|
||||
--hash=sha256:da9c9f1e65b9d09e73bd75befc82961b6b61b5a3b9d0a7c832168e1415f163c6 \
|
||||
--hash=sha256:ed897c58acf4a3cdca61469daa31fe6e44c33c6c06a37c3f21fab31780b3b86a \
|
||||
--hash=sha256:f168f0a7f32b81bfeffdf003c36f25d81c97dee5eb67072a5183e761fe250f13
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqt5
|
||||
# pyqtwebengine
|
||||
pyqtwebengine==5.14.0 \
|
||||
--hash=sha256:01cd7f38ba4efa5f4c0983219ab15dad7747a0ca9378c7832a3077a53988f5ea \
|
||||
--hash=sha256:37c4a820c5bcc82a6cb43ad33b8c81eee4c4772fc03e180a8fa37a59f99f6a48 \
|
||||
--hash=sha256:3d0cba04f64d4f66087cc92e254ff8b33ec4a4e6c7751417fe2bd53c3ed740a7 \
|
||||
--hash=sha256:85e1fac1b2c9bebf0b2e8cd9a75c14a38aad75165a8d8bcb8f6318944b779b25 \
|
||||
--hash=sha256:e11595051f8bfbfa49175d899b2c8c2eea3a3deac4141edf4db68c3555221c92
|
||||
# via -r requirements.in
|
3
python/pyqt/5_15/requirements.in
Normal file
3
python/pyqt/5_15/requirements.in
Normal file
|
@ -0,0 +1,3 @@
|
|||
pyqt5==5.15.2
|
||||
pyqtwebengine==5.15.2
|
||||
pyqt5_sip==12.8.1
|
42
python/pyqt/5_15/requirements.txt
Normal file
42
python/pyqt/5_15/requirements.txt
Normal file
|
@ -0,0 +1,42 @@
|
|||
pyqt5==5.15.2 \
|
||||
--hash=sha256:29889845688a54d62820585ad5b2e0200a36b304ff3d7a555e95599f110ba4ce \
|
||||
--hash=sha256:372b08dc9321d1201e4690182697c5e7ffb2e0770e6b4a45519025134b12e4fc \
|
||||
--hash=sha256:894ca4ae767a8d6cf5903784b71f755073c78cb8c167eecf6e4ed6b3b055ac6a \
|
||||
--hash=sha256:ea24f24b7679bf393dd2e4f53fe0ce65021be18304c1ff7a226c2fc5c356d0da \
|
||||
--hash=sha256:faaecb76ec65e12673a968e7f5bc02495957e6996f0a3fa0d98895f9e4113746
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqtwebengine
|
||||
pyqt5-sip==12.8.1 \
|
||||
--hash=sha256:0304ca9114b9817a270f67f421355075b78ff9fc25ac58ffd72c2601109d2194 \
|
||||
--hash=sha256:0cd969be528c27bbd4755bd323dff4a79a8fdda28215364e6ce3e069cb56c2a9 \
|
||||
--hash=sha256:2f35e82fd7ec1e1f6716e9154721c7594956a4f5bd4f826d8c6a6453833cc2f0 \
|
||||
--hash=sha256:30e944db9abee9cc757aea16906d4198129558533eb7fadbe48c5da2bd18e0bd \
|
||||
--hash=sha256:34dcd29be47553d5f016ff86e89e24cbc5eebae92eb2f96fb32d2d7ba028c43c \
|
||||
--hash=sha256:5a011aeff89660622a6d5c3388d55a9d76932f3b82c95e82fc31abd8b1d2990d \
|
||||
--hash=sha256:6c1ebee60f1d2b3c70aff866b7933d8d8d7646011f7c32f9321ee88c290aa4f9 \
|
||||
--hash=sha256:7b81382ce188d63890a0e35abe0f9bb946cabc873a31873b73583b0fc84ac115 \
|
||||
--hash=sha256:832fd60a264de4134c2824d393320838f3ab648180c9c357ec58a74524d24507 \
|
||||
--hash=sha256:84ba7746762bd223bed22428e8561aa267a229c28344c2d28c5d5d3f8970cffb \
|
||||
--hash=sha256:9312ec47cac4e33c11503bc1cbeeb0bdae619620472f38e2078c5a51020a930f \
|
||||
--hash=sha256:a1b8ef013086e224b8e86c93f880f776d01b59195bdfa2a8e0b23f0480678fec \
|
||||
--hash=sha256:a29e2ac399429d3b7738f73e9081e50783e61ac5d29344e0802d0dcd6056c5a2 \
|
||||
--hash=sha256:b6d42250baec52a5f77de64e2951d001c5501c3a2df2179f625b241cbaec3369 \
|
||||
--hash=sha256:bb5a87b66fc1445915104ee97f7a20a69decb42f52803e3b0795fa17ff88226c \
|
||||
--hash=sha256:c317ab1263e6417c498b81f5c970a9b1af7acefab1f80b4cc0f2f8e661f29fc5 \
|
||||
--hash=sha256:c9800729badcb247765e4ffe2241549d02da1fa435b9db224845bc37c3e99cb0 \
|
||||
--hash=sha256:c9d6d448c29dc6606bb7974696608f81f4316c8234f7c7216396ed110075e777 \
|
||||
--hash=sha256:da9c9f1e65b9d09e73bd75befc82961b6b61b5a3b9d0a7c832168e1415f163c6 \
|
||||
--hash=sha256:ed897c58acf4a3cdca61469daa31fe6e44c33c6c06a37c3f21fab31780b3b86a \
|
||||
--hash=sha256:f168f0a7f32b81bfeffdf003c36f25d81c97dee5eb67072a5183e761fe250f13
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqt5
|
||||
# pyqtwebengine
|
||||
pyqtwebengine==5.15.2 \
|
||||
--hash=sha256:4d72fea774071ce6f76e341a3d2c5d595886c9906a9b9493239c841cce54a634 \
|
||||
--hash=sha256:8ba247d0873bbeaee70f273cafbfa985a93947b2b8df23059607ec3595fee128 \
|
||||
--hash=sha256:db99bdf6c01c84bcc7369b0c49702e40451bc48372d4281777e17dbb84874c1a \
|
||||
--hash=sha256:e9f4a09cce6d8ab8b6c7e7a11e21f606ae713ff275c749839aa4d871a799fccc \
|
||||
--hash=sha256:fe5a02d4cf2327b6a930f8146589842b7664ac703cfddf1f83172423a32e3164
|
||||
# via -r requirements.in
|
5
python/pyqt/6_2/requirements.in
Normal file
5
python/pyqt/6_2/requirements.in
Normal file
|
@ -0,0 +1,5 @@
|
|||
pyqt6==6.2.0
|
||||
pyqt6-qt6==6.2.0
|
||||
pyqt6-webengine==6.2.0
|
||||
pyqt6-webengine-qt6==6.2.0
|
||||
pyqt6_sip==13.1.0
|
51
python/pyqt/6_2/requirements.txt
Normal file
51
python/pyqt/6_2/requirements.txt
Normal file
|
@ -0,0 +1,51 @@
|
|||
pyqt6==6.2.0 \
|
||||
--hash=sha256:142ce7fa574d7ebb13fb0a2ebd18c86087c35829f786c094a71a0749155d8fee \
|
||||
--hash=sha256:8d883b66d2b902862b90ff51696e408aa618f1e4e9d392c98f13925b5983068b \
|
||||
--hash=sha256:c43f4bb7dbe58b262b88da137de54a140091a5273b3333406a76507551c34321 \
|
||||
--hash=sha256:ea4c1502214eed9090937f46ed33aca054ddc46240d6c5ce09886d1d6886b50e
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqt6-webengine
|
||||
pyqt6-qt6==6.2.0 \
|
||||
--hash=sha256:7749e44ab5ca98aba766b283bcd08ebeb0ffb09e427efc7beac0c571525f3c5e \
|
||||
--hash=sha256:806dc7289614f08e1fa28ac5abbbbf2d61305a416b8183045b9c535321c7309c \
|
||||
--hash=sha256:c0d4c1b1b7bd0c9f21a6284d6aa53c041cb36dc464acb403cbe1bc8845090d30 \
|
||||
--hash=sha256:ed1f73e40deddadb10781ddc40672e14c2e7309ca389263c081ed702267451c7
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqt6
|
||||
pyqt6-sip==13.1.0 \
|
||||
--hash=sha256:04c5f6dd0c5be27f27be286e500cf1dd718b9e00b735b88b5e2ada74d86326e6 \
|
||||
--hash=sha256:1afbba8d83a55164e150e04f8ed52a3a5292a347ddaecbc6f0b819fadccdb176 \
|
||||
--hash=sha256:48fab3bc4121d77c081102ad074f63deeae4736b1a88dd19fe05364421f28376 \
|
||||
--hash=sha256:5d5001c1ed83b0b9946f5a2b9a9b27f9631dc6613306f88f3946437205d176be \
|
||||
--hash=sha256:6f0c3fa80b81bb28701772b9d89e11fe3677591048b18d50224bea0138063597 \
|
||||
--hash=sha256:7c31073fe8e6cb8a42e85d60d3a096700a9047c772b354d6227dfe965566ec8a \
|
||||
--hash=sha256:a892f66d506a7adc40c03d95ef54c152614f32c2975f534cd9deb44ca94d1124 \
|
||||
--hash=sha256:c20fdbc8f50052242c84680d9cf1580dd815b2d55ae73e71885864b0320b3585 \
|
||||
--hash=sha256:c6d017380c0a3e8ef94f6eecc119a056ec3e6f71c9c5b7957a1c2dc51007901f \
|
||||
--hash=sha256:c6e1864f0018bb2e27a42a32018fe298790bac1e835bc2a699f341b51c884e7b \
|
||||
--hash=sha256:cbc4ee1997c029d84c2f5ac8ab10089943d93e7b5eb9399a967b93969127c61d \
|
||||
--hash=sha256:d286186990c2180d2b631660b5eaee202bbba031b87b73272d7b7c2ae1c4d001 \
|
||||
--hash=sha256:da4742ad9a983dd384a28d743dd14c47de4842ab476c90a36e8f261998cf83ec \
|
||||
--hash=sha256:ee7e635aadc3d3baaeb8ec509e562d7d6cc5c6d738cb874186f076742a2aec27 \
|
||||
--hash=sha256:f36c6c73137b835024f0bcefe17b74a1fcacd759ebf1a01460f353ced1c34a30 \
|
||||
--hash=sha256:f591414ea4e3029a4873286496b6685d6a260249f0375657c1043e4db5a5514c
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqt6
|
||||
# pyqt6-webengine
|
||||
pyqt6-webengine==6.2.0 \
|
||||
--hash=sha256:3df64503bcd80e529181ed14993d39469611bf2cd0b81a4959a5f258020cc06e \
|
||||
--hash=sha256:4f12a984efd01d202a89baea3437c6fb2001a042f9bdef512d324eb4e81ef693 \
|
||||
--hash=sha256:7a27c567c8ffe17ec52687469b90465330e33deb9856f7de6f269b8d75101704 \
|
||||
--hash=sha256:9df1479df84b7857c86bc233d66ec50edcee7c5e38c11ff5511e0006763bc7e4
|
||||
# via -r requirements.in
|
||||
pyqt6-webengine-qt6==6.2.0 \
|
||||
--hash=sha256:310cb5e5e18b0ae380b2c3b111657cccec1cd89d5e380460baa34d5bacad24e0 \
|
||||
--hash=sha256:9940aa76488bc2cd6ae924be5dc111fcafbe960e70571f274fa6e5583272ae6d \
|
||||
--hash=sha256:d03f68c9a12b715656c8539b38c8d0a106f4462c96344528beb6d9797b1ee4fb \
|
||||
--hash=sha256:ea204703f25ba93a929138860a80f26530057478510e6e0ff526d3279abc6505
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pyqt6-webengine
|
10
python/pyqt/README.md
Normal file
10
python/pyqt/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
We handle PyQt specially for a few reasons:
|
||||
|
||||
- It ships with a few issues in its .pyi files that we want to correct.
|
||||
- Bazel's Python tools install each package in a separate folder, but the
|
||||
various PyQt packages expect to be installed in the same location, and
|
||||
will give runtime linking issues when they are split up.
|
||||
|
||||
To update the version lock file, change to the folder with requirements.in,
|
||||
modify the file, and then run "bazel run //python:update". PyQt does not
|
||||
depend on platform-specific packages, so the update can be run on any platform.
|
|
@ -3,7 +3,7 @@
|
|||
def _execute(repository_ctx, arguments, quiet = False):
|
||||
return repository_ctx.execute(arguments, environment = {}, quiet = quiet)
|
||||
|
||||
def _install_pyqt5_impl(repository_ctx):
|
||||
def _install_pyqt_impl(repository_ctx):
|
||||
python_interpreter = repository_ctx.attr.python_interpreter
|
||||
if repository_ctx.attr.python_runtime:
|
||||
python_interpreter = repository_ctx.path(repository_ctx.attr.python_runtime)
|
||||
|
@ -12,13 +12,14 @@ def _install_pyqt5_impl(repository_ctx):
|
|||
python_interpreter,
|
||||
repository_ctx.path(repository_ctx.attr._script),
|
||||
repository_ctx.path("."),
|
||||
repository_ctx.path(repository_ctx.attr.requirements),
|
||||
]
|
||||
|
||||
result = _execute(repository_ctx, args, quiet = repository_ctx.attr.quiet)
|
||||
if result.return_code:
|
||||
fail("failed: %s (%s)" % (result.stdout, result.stderr))
|
||||
|
||||
install_pyqt5 = repository_rule(
|
||||
install_pyqt = repository_rule(
|
||||
attrs = {
|
||||
"python_interpreter": attr.string(default = "python", doc = """
|
||||
The command to run the Python interpreter used to invoke pip and unpack the
|
||||
|
@ -30,13 +31,14 @@ If the label is specified it will overwrite the python_interpreter attribute.
|
|||
"""),
|
||||
"_script": attr.label(
|
||||
executable = True,
|
||||
default = Label("//python/pyqt5:install_pyqt5.py"),
|
||||
default = Label("//python/pyqt:install.py"),
|
||||
cfg = "host",
|
||||
),
|
||||
"requirements": attr.label(allow_files = True),
|
||||
"quiet": attr.bool(
|
||||
default = True,
|
||||
doc = "If stdout and stderr should be printed to the terminal.",
|
||||
),
|
||||
},
|
||||
implementation = _install_pyqt5_impl,
|
||||
implementation = _install_pyqt_impl,
|
||||
)
|
127
python/pyqt/install.py
Normal file
127
python/pyqt/install.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
# based on https://github.com/ali5h/rules_pip/blob/master/src/whl.py
|
||||
# MIT
|
||||
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from pip._internal.commands import create_command
|
||||
|
||||
|
||||
def install_packages(requirements_path, directory, pip_args):
|
||||
pip_args = [
|
||||
"--isolated",
|
||||
"--disable-pip-version-check",
|
||||
"--target",
|
||||
directory,
|
||||
"--no-deps",
|
||||
"--ignore-requires-python",
|
||||
"--require-hashes",
|
||||
"-r",
|
||||
requirements_path
|
||||
] + pip_args
|
||||
cmd = create_command("install")
|
||||
cmd.main(pip_args)
|
||||
|
||||
|
||||
def fix_pyi_types():
|
||||
"Fix broken PyQt types."
|
||||
for dirpath, dirnames, fnames in os.walk("."):
|
||||
for fname in fnames:
|
||||
if not fname.endswith(".pyi"):
|
||||
continue
|
||||
path = os.path.join(dirpath, fname)
|
||||
|
||||
with open(path, "r+") as file:
|
||||
lines = file.readlines()
|
||||
file.seek(0)
|
||||
for line in lines:
|
||||
# inheriting from the missing sip.sipwrapper definition
|
||||
# causes missing attributes not to be detected, as it's treating
|
||||
# the class as inheriting from Any
|
||||
line = line.replace("PyQt6.sip.wrapper", "object")
|
||||
# # remove blanket getattr in QObject which also causes missing
|
||||
# # attributes not to be detected
|
||||
if "def __getattr__(self, name: str) -> typing.Any" in line:
|
||||
continue
|
||||
file.write(line + "\n")
|
||||
|
||||
def fix_webengine_codesigning():
|
||||
"Fix a codesigning issue in the 6.2.0 release."
|
||||
path = "PyQt6/Qt6/lib/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess"
|
||||
if os.path.exists(path):
|
||||
subprocess.run(["codesign", "-s", "-", path], check=True)
|
||||
|
||||
def main():
|
||||
base = sys.argv[1]
|
||||
requirements_file = sys.argv[2]
|
||||
|
||||
# has user told us to use a custom existing folder instead?
|
||||
if local_site_packages := os.environ.get("PYTHON_SITE_PACKAGES"):
|
||||
subprocess.run(
|
||||
[
|
||||
"rsync",
|
||||
"-ai",
|
||||
"--include=PyQt**",
|
||||
"--exclude=*",
|
||||
local_site_packages,
|
||||
base + "/",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
with open(os.path.join(base, "__init__.py"), "w") as file:
|
||||
pass
|
||||
|
||||
else:
|
||||
arm_darwin = sys.platform.startswith("darwin") and platform.machine() == "arm64"
|
||||
pip_args = []
|
||||
if arm_darwin:
|
||||
# pyqt messed up the architecture tags in the 6.2.0 release
|
||||
pip_args.extend(
|
||||
[
|
||||
"--platform=macosx_10_14_arm64",
|
||||
"--only-binary=pyqt6-qt6,pyqt6-webengine-qt6",
|
||||
])
|
||||
|
||||
install_packages(requirements_file, base, pip_args)
|
||||
fix_pyi_types()
|
||||
fix_webengine_codesigning()
|
||||
|
||||
with open(os.path.join(base, "__init__.py"), "w") as file:
|
||||
file.write("__path__ = __import__('pkgutil').extend_path(__path__, __name__)")
|
||||
|
||||
pkg_name = os.path.basename(base)
|
||||
result = """
|
||||
load("@rules_python//python:defs.bzl", "py_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
py_library(
|
||||
name = "{}",
|
||||
srcs = glob(["**/*.py"]),
|
||||
data = glob(["**/*"], exclude = [
|
||||
"**/*.py",
|
||||
"**/*.pyc",
|
||||
"**/* *",
|
||||
"BUILD",
|
||||
"WORKSPACE",
|
||||
"bin/*",
|
||||
"__pycache__",
|
||||
# these make building slower
|
||||
"Qt/qml/**",
|
||||
"**/*.sip",
|
||||
"**/*.png",
|
||||
]),
|
||||
# This makes this directory a top-level in the python import
|
||||
# search path for anything that depends on this.
|
||||
imports = ["."],
|
||||
)
|
||||
""".format(pkg_name)
|
||||
|
||||
with open(os.path.join(base, "BUILD"), "w") as f:
|
||||
f.write(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,157 +0,0 @@
|
|||
# based on https://github.com/ali5h/rules_pip/blob/master/src/whl.py
|
||||
# MIT
|
||||
|
||||
"""downloads and parses info of a pkg and generates a BUILD file for it"""
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from pip._internal.commands import create_command
|
||||
|
||||
|
||||
def install_package(pkg, directory, pip_args):
|
||||
"""Downloads wheel for a package. Assumes python binary provided has
|
||||
pip and wheel package installed.
|
||||
|
||||
Args:
|
||||
pkg: package name
|
||||
directory: destination directory to download the wheel file in
|
||||
python: python binary path used to run pip command
|
||||
pip_args: extra pip args sent to pip
|
||||
Returns:
|
||||
str: path to the wheel file
|
||||
"""
|
||||
pip_args = [
|
||||
"--isolated",
|
||||
"--disable-pip-version-check",
|
||||
"--target",
|
||||
directory,
|
||||
"--no-deps",
|
||||
"--ignore-requires-python",
|
||||
pkg,
|
||||
] + pip_args
|
||||
cmd = create_command("install")
|
||||
cmd.main(pip_args)
|
||||
|
||||
def _cleanup(directory, pattern):
|
||||
for p in glob.glob(os.path.join(directory, pattern)):
|
||||
shutil.rmtree(p)
|
||||
|
||||
|
||||
fix_none = re.compile(r"(\s*None) =")
|
||||
|
||||
|
||||
def copy_and_fix_pyi(source, dest):
|
||||
"Fix broken PyQt types."
|
||||
with open(source) as input_file:
|
||||
with open(dest, "w") as output_file:
|
||||
for line in input_file.readlines():
|
||||
# assigning to None is a syntax error
|
||||
line = fix_none.sub(r"\1_ =", line)
|
||||
# inheriting from the missing sip.sipwrapper definition
|
||||
# causes missing attributes not to be detected, as it's treating
|
||||
# the class as inheriting from Any
|
||||
line = line.replace("sip.simplewrapper", "object")
|
||||
line = line.replace("sip.wrapper", "object")
|
||||
# remove blanket getattr in QObject which also causes missing
|
||||
# attributes not to be detected
|
||||
if "def __getattr__(self, name: str) -> typing.Any" in line:
|
||||
continue
|
||||
output_file.write(line)
|
||||
|
||||
|
||||
def merge_files(root, source):
|
||||
for dirpath, _dirnames, filenames in os.walk(source):
|
||||
target_dir = os.path.join(root, os.path.relpath(dirpath, source))
|
||||
if not os.path.exists(target_dir):
|
||||
os.mkdir(target_dir)
|
||||
for fname in filenames:
|
||||
source_path = os.path.join(dirpath, fname)
|
||||
target_path = os.path.join(target_dir, fname)
|
||||
if not os.path.exists(target_path):
|
||||
if fname.endswith(".pyi"):
|
||||
copy_and_fix_pyi(source_path, target_path)
|
||||
else:
|
||||
shutil.copy2(source_path, target_path)
|
||||
|
||||
|
||||
def main():
|
||||
base = sys.argv[1]
|
||||
|
||||
local_site_packages = os.environ.get("PYTHON_SITE_PACKAGES")
|
||||
if local_site_packages:
|
||||
subprocess.run(
|
||||
[
|
||||
"rsync",
|
||||
"-ai",
|
||||
"--include=PyQt**",
|
||||
"--exclude=*",
|
||||
local_site_packages,
|
||||
base + "/",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
with open(os.path.join(base, "__init__.py"), "w") as file:
|
||||
pass
|
||||
|
||||
else:
|
||||
packages = [
|
||||
("pyqt5", "pyqt5==5.15.2"),
|
||||
("pyqtwebengine", "pyqtwebengine==5.15.2"),
|
||||
("pyqt5-sip", "pyqt5_sip==12.8.1"),
|
||||
]
|
||||
|
||||
for (name, with_version) in packages:
|
||||
# install package in subfolder
|
||||
folder = os.path.join(base, "temp")
|
||||
install_package(with_version, folder, [])
|
||||
# merge into parent
|
||||
merge_files(base, folder)
|
||||
shutil.rmtree(folder)
|
||||
|
||||
with open(os.path.join(base, "__init__.py"), "w") as file:
|
||||
file.write("__path__ = __import__('pkgutil').extend_path(__path__, __name__)")
|
||||
|
||||
# add missing py.typed file
|
||||
with open(os.path.join(base, "py.typed"), "w") as file:
|
||||
pass
|
||||
|
||||
result = """
|
||||
load("@rules_python//python:defs.bzl", "py_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
py_library(
|
||||
name = "pkg",
|
||||
srcs = glob(["**/*.py"]),
|
||||
data = glob(["**/*"], exclude = [
|
||||
"**/*.py",
|
||||
"**/*.pyc",
|
||||
"**/* *",
|
||||
"BUILD",
|
||||
"WORKSPACE",
|
||||
"bin/*",
|
||||
"__pycache__",
|
||||
# these make building slower
|
||||
"Qt/qml/**",
|
||||
"**/*.sip",
|
||||
"**/*.png",
|
||||
]),
|
||||
# This makes this directory a top-level in the python import
|
||||
# search path for anything that depends on this.
|
||||
imports = ["."],
|
||||
)
|
||||
"""
|
||||
|
||||
# clean up
|
||||
_cleanup(base, "__pycache__")
|
||||
|
||||
with open(os.path.join(base, "BUILD"), "w") as f:
|
||||
f.write(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,42 +0,0 @@
|
|||
# based off https://github.com/ali5h/rules_pip/blob/master/defs.bzl
|
||||
|
||||
def _execute(repository_ctx, arguments, quiet = False):
|
||||
return repository_ctx.execute(arguments, environment = {}, quiet = quiet)
|
||||
|
||||
def _install_pyqt6_impl(repository_ctx):
|
||||
python_interpreter = repository_ctx.attr.python_interpreter
|
||||
if repository_ctx.attr.python_runtime:
|
||||
python_interpreter = repository_ctx.path(repository_ctx.attr.python_runtime)
|
||||
|
||||
args = [
|
||||
python_interpreter,
|
||||
repository_ctx.path(repository_ctx.attr._script),
|
||||
repository_ctx.path("."),
|
||||
]
|
||||
|
||||
result = _execute(repository_ctx, args, quiet = repository_ctx.attr.quiet)
|
||||
if result.return_code:
|
||||
fail("failed: %s (%s)" % (result.stdout, result.stderr))
|
||||
|
||||
install_pyqt6 = repository_rule(
|
||||
attrs = {
|
||||
"python_interpreter": attr.string(default = "python", doc = """
|
||||
The command to run the Python interpreter used to invoke pip and unpack the
|
||||
wheels.
|
||||
"""),
|
||||
"python_runtime": attr.label(doc = """
|
||||
The label to the Python run-time interpreted used to invoke pip and unpack the wheels.
|
||||
If the label is specified it will overwrite the python_interpreter attribute.
|
||||
"""),
|
||||
"_script": attr.label(
|
||||
executable = True,
|
||||
default = Label("//python/pyqt6:install_pyqt6.py"),
|
||||
cfg = "host",
|
||||
),
|
||||
"quiet": attr.bool(
|
||||
default = True,
|
||||
doc = "If stdout and stderr should be printed to the terminal.",
|
||||
),
|
||||
},
|
||||
implementation = _install_pyqt6_impl,
|
||||
)
|
|
@ -1,167 +0,0 @@
|
|||
# based on https://github.com/ali5h/rules_pip/blob/master/src/whl.py
|
||||
# MIT
|
||||
|
||||
"""downloads and parses info of a pkg and generates a BUILD file for it"""
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import platform
|
||||
|
||||
from pip._internal.commands import create_command
|
||||
|
||||
def install_package(pkg, directory, pip_args):
|
||||
"""Downloads wheel for a package. Assumes python binary provided has
|
||||
pip and wheel package installed.
|
||||
|
||||
Args:
|
||||
pkg: package name
|
||||
directory: destination directory to download the wheel file in
|
||||
python: python binary path used to run pip command
|
||||
pip_args: extra pip args sent to pip
|
||||
Returns:
|
||||
str: path to the wheel file
|
||||
"""
|
||||
pip_args = [
|
||||
"--isolated",
|
||||
"--disable-pip-version-check",
|
||||
"--target",
|
||||
directory,
|
||||
"--no-deps",
|
||||
"--ignore-requires-python",
|
||||
pkg,
|
||||
] + pip_args
|
||||
cmd = create_command("install")
|
||||
cmd.main(pip_args)
|
||||
|
||||
def _cleanup(directory, pattern):
|
||||
for p in glob.glob(os.path.join(directory, pattern)):
|
||||
shutil.rmtree(p)
|
||||
|
||||
|
||||
def copy_and_fix_pyi(source, dest):
|
||||
"Fix broken PyQt types."
|
||||
with open(source) as input_file:
|
||||
with open(dest, "w") as output_file:
|
||||
for line in input_file.readlines():
|
||||
# inheriting from the missing sip.sipwrapper definition
|
||||
# causes missing attributes not to be detected, as it's treating
|
||||
# the class as inheriting from Any
|
||||
line = line.replace("PyQt6.sip.wrapper", "object")
|
||||
# # remove blanket getattr in QObject which also causes missing
|
||||
# # attributes not to be detected
|
||||
if "def __getattr__(self, name: str) -> typing.Any" in line:
|
||||
continue
|
||||
output_file.write(line)
|
||||
|
||||
|
||||
def merge_files(root, source):
|
||||
for dirpath, _dirnames, filenames in os.walk(source):
|
||||
target_dir = os.path.join(root, os.path.relpath(dirpath, source))
|
||||
if not os.path.exists(target_dir):
|
||||
os.mkdir(target_dir)
|
||||
for fname in filenames:
|
||||
source_path = os.path.join(dirpath, fname)
|
||||
target_path = os.path.join(target_dir, fname)
|
||||
if not os.path.exists(target_path):
|
||||
if fname.endswith(".pyi"):
|
||||
copy_and_fix_pyi(source_path, target_path)
|
||||
else:
|
||||
shutil.copy2(source_path, target_path)
|
||||
|
||||
def fix_webengine_codesigning(base: str):
|
||||
"Fix a codesigning issue in the 6.2.0 release."
|
||||
path = os.path.join(base, "PyQt6/Qt6/lib/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess")
|
||||
subprocess.run(["codesign", "-s", "-", path], check=True)
|
||||
|
||||
def main():
|
||||
base = sys.argv[1]
|
||||
|
||||
local_site_packages = os.environ.get("PYTHON_SITE_PACKAGES")
|
||||
if local_site_packages:
|
||||
subprocess.run(
|
||||
[
|
||||
"rsync",
|
||||
"-ai",
|
||||
"--include=PyQt**",
|
||||
"--exclude=*",
|
||||
local_site_packages,
|
||||
base + "/",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
with open(os.path.join(base, "__init__.py"), "w") as file:
|
||||
pass
|
||||
|
||||
else:
|
||||
packages = [
|
||||
("pyqt6", "pyqt6==6.2.0"),
|
||||
("pyqt6-qt6", "pyqt6-qt6==6.2.0"),
|
||||
("pyqt6-webengine", "pyqt6-webengine==6.2.0"),
|
||||
("pyqt6-webengine-qt6", "pyqt6-webengine-qt6==6.2.0"),
|
||||
("pyqt6-sip", "pyqt6_sip==13.1.0"),
|
||||
]
|
||||
|
||||
arm_darwin = sys.platform.startswith("darwin") and platform.machine() == "arm64"
|
||||
|
||||
for (name, with_version) in packages:
|
||||
# install package in subfolder
|
||||
folder = os.path.join(base, "temp")
|
||||
pip_args = []
|
||||
if arm_darwin:
|
||||
if name in ("pyqt6-qt6", "pyqt6-webengine-qt6"):
|
||||
# pyqt messed up the architecture tags
|
||||
pip_args.extend(
|
||||
[
|
||||
"--platform=macosx_10_14_arm64",
|
||||
"--only-binary=:all:",
|
||||
])
|
||||
install_package(with_version, folder, pip_args)
|
||||
# merge into parent
|
||||
merge_files(base, folder)
|
||||
shutil.rmtree(folder)
|
||||
|
||||
with open(os.path.join(base, "__init__.py"), "w") as file:
|
||||
file.write("__path__ = __import__('pkgutil').extend_path(__path__, __name__)")
|
||||
|
||||
if arm_darwin:
|
||||
fix_webengine_codesigning(base)
|
||||
|
||||
result = """
|
||||
load("@rules_python//python:defs.bzl", "py_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
py_library(
|
||||
name = "pkg",
|
||||
srcs = glob(["**/*.py"]),
|
||||
data = glob(["**/*"], exclude = [
|
||||
"**/*.py",
|
||||
"**/*.pyc",
|
||||
"**/* *",
|
||||
"BUILD",
|
||||
"WORKSPACE",
|
||||
"bin/*",
|
||||
"__pycache__",
|
||||
# these make building slower
|
||||
"Qt/qml/**",
|
||||
"**/*.sip",
|
||||
"**/*.png",
|
||||
]),
|
||||
# This makes this directory a top-level in the python import
|
||||
# search path for anything that depends on this.
|
||||
imports = ["."],
|
||||
)
|
||||
"""
|
||||
|
||||
# clean up
|
||||
_cleanup(base, "__pycache__")
|
||||
|
||||
with open(os.path.join(base, "BUILD"), "w") as f:
|
||||
f.write(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -59,7 +59,7 @@ py_test(
|
|||
deps = [
|
||||
"//pylib/anki",
|
||||
"//qt/aqt:aqt_without_data",
|
||||
"@pyqt6//:pkg",
|
||||
"@pyqt6",
|
||||
requirement("mypy"),
|
||||
],
|
||||
)
|
||||
|
@ -81,7 +81,7 @@ py_test(
|
|||
"//pylib/anki",
|
||||
"//qt/aqt:aqt_without_data",
|
||||
requirement("pylint"),
|
||||
"@pyqt6//:pkg",
|
||||
"@pyqt6",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -130,11 +130,12 @@ py_binary(
|
|||
deps = [
|
||||
"//pylib/anki",
|
||||
"//qt/aqt:aqt_with_data",
|
||||
"@pyqt6",
|
||||
],
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "runanki_qt5",
|
||||
name = "runanki_qt515",
|
||||
srcs = [
|
||||
"bazelfixes.py",
|
||||
"runanki.py",
|
||||
|
@ -144,7 +145,24 @@ py_binary(
|
|||
tags = ["manual"],
|
||||
deps = [
|
||||
"//pylib/anki",
|
||||
"//qt/aqt:aqt_with_data_qt5",
|
||||
"//qt/aqt:aqt_with_data",
|
||||
"@pyqt515",
|
||||
],
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "runanki_qt514",
|
||||
srcs = [
|
||||
"bazelfixes.py",
|
||||
"runanki.py",
|
||||
],
|
||||
imports = ["."],
|
||||
main = "runanki.py",
|
||||
tags = ["manual"],
|
||||
deps = [
|
||||
"//pylib/anki",
|
||||
"//qt/aqt:aqt_with_data",
|
||||
"@pyqt514",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ py_library(
|
|||
data = aqt_core_data,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = aqt_deps + [
|
||||
"@pyqt6//:pkg",
|
||||
"@pyqt6",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -75,20 +75,7 @@ py_library(
|
|||
srcs = _py_srcs_and_forms,
|
||||
data = aqt_core_data + ["//qt/aqt/data"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = aqt_deps + [
|
||||
"@pyqt6//:pkg",
|
||||
],
|
||||
)
|
||||
|
||||
py_library(
|
||||
name = "aqt_with_data_qt5",
|
||||
srcs = _py_srcs_and_forms,
|
||||
data = aqt_core_data + ["//qt/aqt/data"],
|
||||
tags = ["manual"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = aqt_deps + [
|
||||
"@pyqt5//:pkg",
|
||||
],
|
||||
deps = aqt_deps,
|
||||
)
|
||||
|
||||
py_package(
|
||||
|
|
|
@ -5,7 +5,7 @@ py_binary(
|
|||
name = "build_ui",
|
||||
srcs = ["build_ui.py"],
|
||||
legacy_create_init = False,
|
||||
deps = ["@pyqt6//:pkg"],
|
||||
deps = ["@pyqt6"],
|
||||
)
|
||||
|
||||
compile_all(
|
||||
|
|
|
@ -49,7 +49,12 @@ def fix_run_on_macos():
|
|||
if not sys.platform.startswith("darwin"):
|
||||
return
|
||||
exec_folder = os.path.dirname(sys.argv[0])
|
||||
qt_version = 5 if "runanki_qt5" in exec_folder else 6
|
||||
if "runanki_qt515" in exec_folder:
|
||||
qt_version = 515
|
||||
elif "runanki_qt514" in exec_folder:
|
||||
qt_version = 514
|
||||
else:
|
||||
qt_version = 6
|
||||
pyqt_repo = os.path.join(exec_folder, f"../../../../../../../external/pyqt{qt_version}")
|
||||
if os.path.exists(pyqt_repo):
|
||||
# pyqt must point to real folder, not a symlink
|
||||
|
|
|
@ -11,8 +11,7 @@ nonstandard_header = {
|
|||
"pylib/anki/importing/supermemo_xml.py",
|
||||
"pylib/anki/statsbg.py",
|
||||
"pylib/tools/protoc-gen-mypy.py",
|
||||
"python/pyqt5/install_pyqt5.py",
|
||||
"python/pyqt6/install_pyqt6.py",
|
||||
"python/pyqt/install.py",
|
||||
"qt/aqt/mpv.py",
|
||||
"qt/aqt/winpaths.py",
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
set -e
|
||||
|
||||
export PYTHONWARNINGS=default
|
||||
bazel run $BUILDARGS //qt:runanki_qt5 -- $*
|
||||
bazel run $BUILDARGS //qt:runanki_qt514 -- $*
|
6
scripts/run-qt5.15
Executable file
6
scripts/run-qt5.15
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export PYTHONWARNINGS=default
|
||||
bazel run $BUILDARGS //qt:runanki_qt515 -- $*
|
Loading…
Reference in a new issue