diff --git a/buildsystem/check_qml_module.py b/buildsystem/check_qml_module.py new file mode 100644 index 0000000000..5143e3e6ed --- /dev/null +++ b/buildsystem/check_qml_module.py @@ -0,0 +1,168 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +import argparse +import sys +import os +import subprocess +from tempfile import NamedTemporaryFile + +def checkQmakePath(path): + if path is None: + return False + + if path == "**Unknown**": + return False + + if not os.path.isdir(path): + return False + + return True + +def findProgram(path, progName): + if not checkQmakePath(path): + return None + + progPath = os.path.join(path, progName) + if not os.path.isfile(progPath): + return None + + return progPath + + +class QmlModuleChecker: + def __init__(self): + self.qt5 = True + self.qmlimportscanner = None + self.qmlpath = None + + def genQmlFile(self, f, modules): + for module, version in modules: + f.write("import {} {}\n".format(module, version)) + f.write("Item {}\n") + f.flush() + + def scanImports(self, f, modules): + ret = subprocess.run( + [ + self.qmlimportscanner, + "-qmlFiles", f.name, + "-importPath", self.qmlpath + ], + capture_output=True, + ) + + if ret.returncode != 0: + return None + + return json.loads(ret.stdout) + + def checkImports(self, scanlist, modules): + ret = True + for name, version in modules: + found = False + for entry in scanlist: + if entry["type"] == "module" and entry["name"] == name: + #qt6 modules have no version + if self.qt5 and not entry["version"] == version: + continue + if "classname" in entry: + found = True + break + print("checking for {} {}... {}".format(name, version, "yes" if found else "no")) + if not found: + ret = False + + return ret + + + def getInstallInfo(self, qmake): + if not os.path.isfile(qmake): + print("qmake not found") + return False + + ret = subprocess.run( + [ qmake, "-query"], + capture_output=True, + encoding="utf8" + ) + + if ret.returncode != 0: + return None + + binpath = None + libexec = None + qmlpath = None + qtmajor = "" + for l in ret.stdout.splitlines(): + l.strip() + if l.startswith("QT_HOST_BINS:"): + binpath = l.split(":", 1)[1] + elif l.startswith("QT_HOST_LIBEXECS:"): + libexec = l.split(":", 1)[1] + elif l.startswith("QT_INSTALL_QML:"): + self.qmlpath = l.split(":", 1)[1] + elif l.startswith("QT_VERSION:"): + qmlversion = l.split(":", 1)[1] + qtmajor = qmlversion.split(".")[0] + + if qtmajor == "6": + self.qt5 = False + + if not checkQmakePath(self.qmlpath): + print("Qml path not found") + return False + + self.qmlimportscanner = findProgram(binpath, "qmlimportscanner") + if self.qmlimportscanner is not None: + return True + + #Qt6 may place qmlimportscanner in libexec + self.qmlimportscanner = findProgram(libexec, "qmlimportscanner") + if self.qmlimportscanner is not None: + return True + + print("qmlimportscanner not found") + return False + + +class KeyValue(argparse.Action): + def __call__( self , parser, namespace, + values, option_string = None): + setattr(namespace, self.dest, []) + + for value in values: + key, value = value.split('=') + getattr(namespace, self.dest).append((key, value)) + +def main(): + parser = argparse.ArgumentParser("check for qml runtime dependencies") + parser.add_argument( + "--qmake", type=str, required=True, + help="qmake path") + + parser.add_argument( + "--modules", nargs="+", action=KeyValue, required=True, + help="list of modules to check in the form module=version (ex QtQuick=2.12)") + + args = parser.parse_args() + + moduleChecker = QmlModuleChecker() + if not moduleChecker.getInstallInfo(args.qmake): + exit(-1) + + with NamedTemporaryFile(mode="w+", suffix=".qml") as f: + moduleChecker.genQmlFile(f, args.modules) + + scanlist = moduleChecker.scanImports(f, args.modules) + if scanlist is None: + exit(-1) + + if not moduleChecker.checkImports(scanlist, args.modules): + exit(-1) + + exit(0) + +if __name__ == "__main__": + main() diff --git a/configure.ac b/configure.ac index 61b5b0b4b3..8d4b6bd28d 100644 --- a/configure.ac +++ b/configure.ac @@ -3950,15 +3950,11 @@ AS_IF([test "${enable_qt}" != "no"], [ AC_MSG_WARN([Not building Qt Interface with wayland support.]) ]) - PKG_CHECK_MODULES([QT5_QUICK_TEST], [Qt5QuickTest], [ - have_qt5_quick_test="yes" - ],[ - ]) - QT_PATH="$(eval $PKG_CONFIG --variable=exec_prefix Qt5Core)" QT_HOST_PATH="$(eval $PKG_CONFIG --variable=host_bins Qt5Core)" QT_INCLUDE_DIRECTORY="$(eval $PKG_CONFIG --variable=includedir Qt5Core)" QT_VERSION="$(eval $PKG_CONFIG --modversion Qt5Gui)" + AC_PATH_PROGS(QMAKE, [qmake], qmake, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) AC_PATH_PROGS(MOC, [moc-qt5 moc], moc, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) AC_PATH_PROGS(RCC, [rcc-qt5 rcc], rcc, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) AC_PATH_PROGS(UIC, [uic-qt5 uic], uic, ["${QT_HOST_PATH}" "${QT_PATH}/bin"]) @@ -3967,6 +3963,28 @@ AS_IF([test "${enable_qt}" != "no"], [ AC_MSG_WARN([qmlcachegen not found]) ]) + AC_CHECK_PROGS(PYTHON3, [python3], [no]) + AS_IF([test "$PYTHON3" != "no" && ${PYTHON3} ${srcdir}/buildsystem/check_qml_module.py \ + --qmake "${QMAKE}" \ + --modules \ + QtQml.Models=2.12 \ + QtQuick.Layouts=1.12 \ + QtQuick.Window=2.12 \ + QtQuick.Controls=2.12 \ + QtGraphicalEffects=1.12], + [],[ + AC_MSG_WARN([qt runtime dependencies are missing, disabling qt interface]) + enable_qt="no" + ]) + + PKG_CHECK_MODULES([QT5_QUICK_TEST], [Qt5QuickTest], [ + AS_IF([test "$PYTHON3" != "no" && ${PYTHON3} ${srcdir}/buildsystem/check_qml_module.py --qmake "${QMAKE}" --modules QtTest=1.12], [], [ + have_qt5_quick_test="yes" + ]) + ],[ + ]) + + dnl Check private headers avaibility VLC_SAVE_FLAGS CPPFLAGS="${CPPFLAGS} ${QT_CFLAGS}"