Last active
June 15, 2016 15:52
-
-
Save ales-erjavec/9d636f3172ab07261716618f0855dc55 to your computer and use it in GitHub Desktop.
Convert a PyQt4/PyQt5 official windows installer into a pip installable wheel file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /bin/bash | |
set -e | |
function print_usage { | |
echo 'pyqt-convert.sh INSTALLER | |
Convert a PyQt4/PyQt5 windows installer into a wheel package. | |
note: 7z executable must be on PATH. | |
Options: | |
-d -dist-dir DIR Directory where the build .whl package is put (the | |
default is "dist") | |
-h --help Print this help and exit. | |
' | |
} | |
while [ "${1:0:1}" == "-" ] | |
do | |
case $1 in | |
-d|--dist-dir) | |
DISTDIR=${2:?"--dist-dir requires an argument"} | |
shift 2;; | |
--dist-dir=*) | |
DISTDIR=${1##*=} | |
shift 1;; | |
-h|-help) | |
print_usage | |
exit 0 ;; | |
-*) | |
echo "Unrecognized option $1" | |
print_usage | |
exit 1 ;; | |
esac | |
done | |
INSTALLER=${1:?"Missing a required positional argument"} | |
DISTDIR=${DISTDIR:-dist} | |
if [[ ! -f "$INSTALLER" ]]; then | |
echo "$INSTALLER does not exist" | |
print_usage | |
exit 1 | |
fi | |
INSTBASE=$(basename "$INSTALLER") | |
# Get the PyQt4 version, python version, and platform tag from the installer | |
# filename. | |
# PyQt4-{VERSION}-gpl-Py{PYVER}-{QTVER}-(x32|x64).exe | |
NAME=${INSTBASE%%-*} | |
VERSION=$(echo "$INSTBASE" | cut -d "-" -f 2) | |
PYVERSION=$(echo "$INSTBASE" | cut -d "-" -f 4 | sed s/\\.//g | awk '{print tolower($0)}') | |
PYTAG=cp${PYVERSION##py} | |
PLATTAG=$(echo "$INSTBASE" | cut -d "-" -f 6 | cut -d "." -f 1) | |
echo " | |
NAME=${NAME} | |
VERSION=${VERSION} | |
PYVERSION=${PYVERSION} | |
PLATTAG=${PLATTAG} | |
" | |
case $PLATTAG in | |
x32) | |
PLATTAG=win32 | |
;; | |
x64) | |
PLATTAG=win_amd64 | |
;; | |
*) | |
echo "Unrecognized platform tag $PLATTAG" | |
exit 1; | |
esac | |
mkdir -p "$DISTDIR" | |
function extract-installer { | |
local installer=${1:?} | |
local wheelbase=${2:?} | |
local base=$(basename "${installer}") | |
local name=${base%%-*} | |
local tmpdir=$(mktemp -d -t "${base}") | |
7z -o"${tmpdir:?}" -y x "$installer" | |
cp -a -f "$tmpdir"/Lib/site-packages/* "$wheelbase"/ | |
# Some (older?) installers had .pyd files packaged separately | |
if [[ -d "$tmpdir"/'$_OUTDIR' ]]; then | |
# * .pyd, doc/, examples/, mkspecs/, qsci/, include/, uic/ | |
cp -a -f "$tmpdir"/'$_OUTDIR'/*.pyd "$wheelbase"/${name}/ | |
cp -a -f "$tmpdir"/'$_OUTDIR'/uic "$wheelbase"/${name}/ | |
# ignore the rest | |
fi | |
rm -r "$tmpdir" | |
rm "$wheelbase"/${name}/Uninstall.exe || true | |
rm -r "$wheelbase"/${name}/{doc,sip,mkspecs,examples} | |
} | |
function wheel-convert { | |
local installer=${1:?} | |
local distdir=${2:?} | |
local base=$(basename "${installer}") | |
local name=${base%%-*} | |
local version=$VERSION | |
local pytag=$PYTAG | |
local plattag=$PLATTAG | |
mkdir -p "$distdir" | |
local wheelbase=$(mktemp -d -t ${name}-wheel-convert) | |
mkdir -p "${wheelbase:?}"/${name} | |
mkdir -p "$wheelbase"/${name}-${version}.dist-info | |
mkdir -p "$wheelbase"/${name}-${version}.data/data | |
mkdir -p "$wheelbase"/${name}-${version}.data/data/Scripts | |
extract-installer "$installer" "$wheelbase" | |
echo "[PATHS] | |
Prefix = Lib/site-packages/${name} | |
" > "$wheelbase"/${name}-${version}.data/data/qt.conf | |
echo "[PATHS] | |
Prefix = ../Lib/site-packages/${name} | |
" > "$wheelbase"/${name}-${version}.data/data/Scripts/qt.conf | |
echo "[PATHS] | |
Prefix = . | |
" > "$wheelbase"/${name}/qt.conf | |
echo "Wheel-Version: 1.0 | |
Generator: pyqt-convert.sh | |
Root-Is-Purelib: false | |
Tag: ${pytag}-none-${plattag} | |
Build: 1" > "${wheelbase}"/${name}-${version}.dist-info/WHEEL | |
echo "Metadata-Version: 1.1 | |
Name: ${name} | |
Version: ${version} | |
Summary: xxx | |
Home-page: xxx | |
Author: xxx | |
Author-email: xxx | |
License: GPLv3 | |
Download-URL: xxx | |
Description: xxx | |
" > "${wheelbase}"/${name}-${version}.dist-info/METADATA | |
local qtver=${name#Py} | |
cat <<EOF > "${wheelbase}"/${name}-${version}.dist-info/metadata.json | |
{ | |
"generator": "pyqt-convert.sh (0.0.0)", | |
"summary": "Python bindings for ${qtver}.", | |
"classifiers": [ | |
"Environment :: X11 Applications :: Qt" | |
], | |
"extensions": { | |
"python.details": { | |
"project_urls": { | |
"Home": "http://riverbankcomputing.com/" | |
}, | |
"contacts": [] | |
}, | |
"python.commands": { | |
"wrap_console": [{"pyuic${version%%.*}": "${name}.uic.pyuic"}] | |
} | |
}, | |
"keywords": [ | |
"${qtver}", "GUI", "bindings" | |
], | |
"license": "GPLv3+", | |
"metadata_version": "2.0", | |
"name": "${name}", | |
"run_requires": [ | |
{ | |
"requires": [] | |
} | |
], | |
"extras": [], | |
"version": "${version}" | |
} | |
EOF | |
generate_record "$wheelbase" > "$wheelbase"/${name}-${version}.dist-info/RECORD | |
echo "${name}-${version}.dist-info/RECORD,," >> "$wheelbase"/${name}-${version}.dist-info/RECORD | |
local wheelname=${name}-${version}-${pytag}-none-${plattag}.whl | |
wheel-zip "$wheelbase" "$wheelname" | |
mv "$wheelbase/$wheelname" "$distdir" | |
# cleanup tmp work dir | |
rm -r "$wheelbase" | |
} | |
function wheel-zip { | |
local wheelbase=${1:?} | |
local wheelname=${2:?} | |
wheelbase=$(cd "${wheelbase:?}"; pwd;) | |
(cd "$wheelbase"; find . -print | zip "$wheelname" -@) | |
} | |
function find_all { | |
(cd "${1:?}"; find . ;) | |
} | |
function urlsafe_b64encode_nopad { | |
python3 -c "import base64; print(base64.urlsafe_b64encode(bytes.fromhex('$1')).rstrip(b'=').decode())" | |
} | |
function generate_record { | |
local wheelbase=$1 | |
find_all "$wheelbase" | while read line | |
do | |
line=${line##./} # strip the leading ./.. | |
local filepath="$wheelbase"/"$line" | |
if [[ -f "$filepath" ]] | |
then | |
local sha=$(_sha256sum "$filepath") | |
local size=$(stat -f "%z" "$filepath") | |
echo "$line,sha256=$sha,$size" | |
fi | |
done | |
} | |
# _sha256sum PATH | |
# Return the sha256 checksum of PATH encoded with urlsafe_b64encode | |
# suitable for wheel RECORD file (see PEP 427 for details) | |
function _sha256sum { | |
python -c" | |
import sys, hashlib, base64 | |
sha = hashlib.sha256() | |
for line in open(sys.argv[1], \"rb\"): | |
sha.update(line) | |
print(base64.urlsafe_b64encode(sha.digest()).rstrip(b'=').decode()) | |
" "${1:?}" | |
} | |
wheel-convert "$INSTALLER" "$DISTDIR" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment