Skip to content

Commit

Permalink
Merge pull request #201 from miurahr/topic-filepatch-pkgconfig
Browse files Browse the repository at this point in the history
Improve patching
  • Loading branch information
miurahr authored Jan 28, 2021
2 parents e502b25 + 9c5abd1 commit c8bb556
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 64 deletions.
60 changes: 0 additions & 60 deletions aqt/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import logging
import multiprocessing
import os
import pathlib
import subprocess
import sys
import xml.etree.ElementTree as ElementTree
from typing import List, Optional
Expand Down Expand Up @@ -58,64 +56,6 @@ def altlink(url: str, alt: str, logger=None):
return next(filter(lambda mirror: not any(mirror.startswith(b) for b in blacklist), mirrors), alt)


def versiontuple(v: str):
return tuple(map(int, (v.split("."))))


class Updater:

def __init__(self, prefix: pathlib.Path, logger):
self.logger = logger
self.prefix = prefix
self.qmake_path = None
self.qconfigs = {}
self._detect_qmake(prefix)

def _patch_qtcore(self):
framework_dir = self.prefix.joinpath("lib", "QtCore.framework")
assert framework_dir.exists(), "Invalid installation prefix"
for component in ["QtCore", "QtCore_debug"]:
if framework_dir.joinpath(component).exists():
qtcore_path = framework_dir.joinpath(component).resolve()
self.logger.info("Patching {}".format(qtcore_path))
self._patch_file(qtcore_path, bytes(str(self.prefix), "ascii"))

def _patch_file(self, file: pathlib.Path, newpath: bytes):
PREFIX_VAR = b"qt_prfxpath="
st = file.stat()
data = file.read_bytes()
idx = data.find(PREFIX_VAR)
if idx > 0:
return
assert len(newpath) < 256, "Qt Prefix path is too long(255)."
data = data[:idx] + PREFIX_VAR + newpath + data[idx + len(newpath):]
file.write_bytes(data)
os.chmod(str(file), st.st_mode)

def _detect_qmake(self, prefix):
''' detect Qt configurations from qmake
'''
for qmake_path in [prefix.joinpath('bin', 'qmake'), prefix.joinpath('bin', 'qmake.exe')]:
if qmake_path.exists():
result = subprocess.run([str(qmake_path), '-query'], stdout=subprocess.PIPE)
if result.returncode == 0:
self.qmake_path = qmake_path
for line in result.stdout.splitlines():
vals = line.decode('UTF-8').split(':')
self.qconfigs[vals[0]] = vals[1]
break

def patch_qt(self, target):
''' patch works '''
self.logger.info("Patching qmake")
mac_exceptions = ['ios', 'android', 'wasm_32',
'android_x86_64', 'android_arm64_v8a', 'android_x86', 'android_armv7']
if target.os_name == 'mac' and target.arch not in mac_exceptions:
self._patch_qtcore()
if self.qmake_path is not None:
self._patch_file(self.qmake_path, bytes(str(self.prefix), 'UTF-8'))


class Settings(object):
"""Class to hold configuration and settings.
Actual values are stored in 'settings.ini' file.
Expand Down
6 changes: 3 additions & 3 deletions aqt/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
from aqt.archives import (ArchiveConnectionError, ArchiveDownloadError,
ArchiveListError, PackagesList, QtArchives,
SrcDocExamplesArchives, ToolArchives)
from aqt.helper import Settings, Updater, altlink, versiontuple
from aqt.helper import Settings, altlink
from aqt.updater import Updater

try:
from importlib import metadata as importlib_metadata # noqa
Expand Down Expand Up @@ -530,5 +531,4 @@ def finisher(target, base_dir, logger):
raise e
prefix = pathlib.Path(base_dir) / target.version / target.arch
updater = Updater(prefix, logger)
if versiontuple(target.version) < (5, 14, 2):
updater.patch_qt(target)
updater.qtpatch(target)
91 changes: 91 additions & 0 deletions aqt/updater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import os
import pathlib
import subprocess


class Updater:

def __init__(self, prefix: pathlib.Path, logger):
self.logger = logger
self.prefix = prefix
self.qmake_path = None
self.qconfigs = {}
self._detect_qmake(prefix)

def _patch_qtcore(self, lib_dir, components, encoding):
for component in components:
if lib_dir.joinpath(component).exists():
qtcore_path = lib_dir.joinpath(component).resolve()
self.logger.info("Patching {}".format(qtcore_path))
newpath = bytes(str(self.prefix), encoding)
self._patch_binfile(qtcore_path, b"qt_prfxpath=", newpath)

def _patch_binfile(self, file: pathlib.Path, key: bytes, newpath: bytes):
"""Patch binary file with key/value"""
st = file.stat()
data = file.read_bytes()
idx = data.find(key)
if idx > 0:
return
assert len(newpath) < 256, "Qt Prefix path is too long(255)."
data = data[:idx] + key + newpath + data[idx + len(newpath):]
file.write_bytes(data)
os.chmod(str(file), st.st_mode)

def _patch_pkgconfig(self, file: pathlib.Path):
for pcfile in file.glob("*.pc"):
self.logger.info("Patching {}".format(pcfile))
self._patch_textfile(pcfile, "prefix=/home/qt/work/install", 'prefix={}'.format(str(self.prefix)))

def _patch_textfile(self, file: pathlib.Path, old: str, new: str):
st = file.stat()
data = file.read_text("UTF-8")
data = data.replace(old, new)
file.write_text(data, "UTF-8")
os.chmod(str(file), st.st_mode)

def _detect_qmake(self, prefix):
""" detect Qt configurations from qmake
"""
for qmake_path in [prefix.joinpath('bin', 'qmake'), prefix.joinpath('bin', 'qmake.exe')]:
if not qmake_path.exists():
return
try:
result = subprocess.run([str(qmake_path), '-query'], stdout=subprocess.PIPE)
except subprocess.SubprocessError:
return
else:
if result.returncode == 0:
self.qmake_path = qmake_path
for line in result.stdout.splitlines():
vals = line.decode('UTF-8').split(':')
self.qconfigs[vals[0]] = vals[1]

def _versiontuple(self, v: str):
return tuple(map(int, (v.split("."))))

def qtpatch(self, target):
""" patch works """
if target.os_name == 'linux':
self.logger.info("Patching pkgconfig configurations")
self._patch_pkgconfig(self.prefix.joinpath("lib", "pkgconfig"))

if target.arch not in ['ios', 'android', 'wasm_32', 'android_x86_64', 'android_arm64_v8a', 'android_x86',
'android_armv7']:
if target.os_name == 'mac':
self.logger.info("Patching QtCore")
self._patch_qtcore(self.prefix.joinpath("lib", "QtCore.framework"), ["QtCore", "QtCore_debug"], "UTF-8")
elif target.os_name == 'linux':
self.logger.info("Patching libQt(5|6)Core")
self._patch_qtcore(self.prefix.joinpath("lib"), ["libQt5Core.so", "libQt6Core.so"], "UTF-8")
elif target.os_name == 'windows':
self.logger.info("Patching Qt(5|6)Core.dll")
self._patch_qtcore(self.prefix.joinpath("bin"), ["Qt5Cored.dll", "Qt5Core.dll", "Qt6Core.dll",
"Qt6Cored.dll"], "UTF-8")
else:
# no need to patch Qt5Core
pass

if self.qmake_path is not None:
self.logger.info("Patching qmake")
self._patch_binfile(self.qmake_path, key=b"qt_prfxpath=", newpath=bytes(str(self.prefix), 'UTF-8'))
5 changes: 4 additions & 1 deletion ci/steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ steps:
} else {
Write-Host '##vso[task.setvariable variable=VSVER]2015'
}
cd $(Build.BinariesDirectory)\Qt\$(QT_VERSION)\$(ARCHDIR)\bin
Invoke-WebRequest -Uri https://download.qt.io/official_releases/jom/jom.zip -OutFile jom.zip
unzip jom.zip
condition: eq( variables['Agent.OS'], 'Windows_NT')
displayName: Detect toolchain for Windows and update PATH
- script: |
Expand All @@ -176,7 +179,7 @@ steps:
7z x $(Build.SourcesDirectory)\ci\helloworld.7z
cd ..
qmake $(Build.BinariesDirectory)\tests\helloworld
nmake
jom
condition: and(eq( variables['Agent.OS'], 'Windows_NT'), eq(variables['TOOLCHAIN'], 'MSVC'), eq(variables['MODULE'], ''), ne(variables['VSVER'], '2019'))
displayName: build test with qmake with MSVC w/o extra module
- powershell: |
Expand Down

0 comments on commit c8bb556

Please sign in to comment.