From 9a2ece74d0bbbbc2a1cf92df1e118386c0f509a9 Mon Sep 17 00:00:00 2001 From: Pavel Tomanek Date: Fri, 20 Dec 2024 12:18:32 +0100 Subject: [PATCH] #476 debug qscintilla sip problem + update eblock (2h) --- 476_QGIS/block_qscintilla.py | 214 +++++++++++++++++++++++++++++++++++ 476_QGIS/qscintilla.eb | 15 ++- 2 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 476_QGIS/block_qscintilla.py diff --git a/476_QGIS/block_qscintilla.py b/476_QGIS/block_qscintilla.py new file mode 100644 index 00000000..c8c12e50 --- /dev/null +++ b/476_QGIS/block_qscintilla.py @@ -0,0 +1,214 @@ +## +# Copyright 2009-2024 Ghent University +# +# This file is part of EasyBuild, +# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), +# with support of Ghent University (http://ugent.be/hpc), +# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be), +# Flemish Research Foundation (FWO) (http://www.fwo.be/en) +# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). +# +# https://github.com/easybuilders/easybuild +# +# EasyBuild is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation v2. +# +# EasyBuild is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with EasyBuild. If not, see . +## +""" +EasyBuild support for building and installing QScintilla, implemented as an easyblock + +author: Kenneth Hoste (HPC-UGent) +author: Maxime Boissonneault (Compute Canada) +""" +import os +from easybuild.tools import LooseVersion + +from easybuild.easyblocks.generic.configuremake import ConfigureMake +from easybuild.easyblocks.generic.pythonpackage import det_pylibdir +from easybuild.tools.build_log import EasyBuildError +from easybuild.tools.filetools import apply_regex_substitutions, mkdir, symlink, write_file, find_glob_pattern +from easybuild.tools.modules import get_software_root, get_software_version +from easybuild.tools.run import run_cmd +from easybuild.tools.systemtools import get_shared_lib_ext + + +class EB_QScintilla(ConfigureMake): + """Support for building/installing QScintilla.""" + + def prepare_step(self, *args, **kwargs): + """Prepare build environment for building & installing QScintilla.""" + + super(EB_QScintilla, self).prepare_step(*args, **kwargs) + + pyqt5 = get_software_root('PyQt5') + pyqt = get_software_root('PyQt') + qt5 = get_software_root('Qt5') + if pyqt5: + self.pyqt_root = pyqt5 + self.pyqt_pkg_name = "PyQt5" + elif pyqt: + self.pyqt_root = pyqt + self.pyqt_pkg_name = "PyQt4" + elif qt5: + self.pyqt_root = qt5 + self.pyqt_pkg_name = "PyQt5" + else: + raise EasyBuildError("Failed to determine PyQt(5) installation prefix. Missing PyQt(5) dependency?") + + # PyQt5 is supported by QScintilla from version 2.11, otherwise there are some additional hacks/patches needed + if LooseVersion(self.version) < LooseVersion('2.11') and pyqt5: + raise EasyBuildError("PyQt5 is supported by QScintilla in version 2.11 and greater.") + + def configure_step(self): + """Custom configuration procedure for QScintilla.""" + + srcdir = os.path.join(self.cfg['start_dir'], 'Qt4Qt5') + try: + os.chdir(srcdir) + except OSError as err: + raise EasyBuildError("Failed to change to %s: %s", srcdir, err) + + # replace template values for install locations in qscintilla.pro configuration file + regex_subs = [ + (r'\$\$\[QT_HOST_DATA\]', os.path.join(self.installdir, 'data')), + (r'\$\$\[QT_INSTALL_DATA\]', os.path.join(self.installdir, 'data')), + (r'\$\$\[QT_INSTALL_HEADERS\]', os.path.join(self.installdir, 'include')), + (r'\$\$\[QT_INSTALL_LIBS\]', os.path.join(self.installdir, 'lib')), + (r'\$\$\[QT_INSTALL_TRANSLATIONS\]', os.path.join(self.installdir, 'trans')), + ] + apply_regex_substitutions('qscintilla.pro', regex_subs) + + run_cmd("qmake qscintilla.pro") + + def build_step(self): + """Custom build procedure for QScintilla.""" + + # make sure that $CXXFLAGS is being passed down + self.cfg.update('buildopts', r'CXXFLAGS="$CXXFLAGS \$(DEFINES)"') + + super(EB_QScintilla, self).build_step() + + def install_step(self): + """Custom install procedure for QScintilla.""" + + super(EB_QScintilla, self).install_step() + + # also install Python bindings if Python is included as a dependency + python = get_software_root('Python') + if python: + pydir = os.path.join(self.cfg['start_dir'], 'Python') + try: + os.chdir(pydir) + except OSError as err: + raise EasyBuildError("Failed to change to %s: %s", pydir, err) + + # apparently this directory has to be there + qsci_sipdir = os.path.join(self.installdir, 'share', 'sip', self.pyqt_pkg_name) + mkdir(qsci_sipdir, parents=True) + + pylibdir = os.path.join(det_pylibdir(), self.pyqt_pkg_name) + pyshortver = '.'.join(get_software_version('Python').split('.')[:2]) + + sip_incdir = find_glob_pattern(os.path.join(self.pyqt_root, 'include', 'python%s*' % pyshortver), False) + # depending on PyQt5 versions and how it was installed, the sip directory could be in various places + # test them and figure out the first one that matches + pyqt_sip_subdir = [os.path.join('share', 'python%s*' % pyshortver, 'site-packages', 'sip', + self.pyqt_pkg_name), + os.path.join('share', 'sip', self.pyqt_pkg_name), + os.path.join('share', 'sip'), + os.path.join('lib', 'python%s*' % pyshortver, 'site-packages', self.pyqt_pkg_name, + 'bindings') + ] + pyqt_sipdir_options = [os.path.join(self.pyqt_root, subdir) for subdir in pyqt_sip_subdir] + for pyqt_sipdir_option in pyqt_sipdir_options: + pyqt_sipdir = find_glob_pattern(pyqt_sipdir_option, False) + if pyqt_sipdir: + break + + if not pyqt_sipdir: + raise EasyBuildError("Failed to find PyQt5 sip directory") + + cfgopts = [ + '--destdir %s' % os.path.join(self.installdir, pylibdir), + '--qsci-sipdir %s' % qsci_sipdir, + '--qsci-incdir %s' % os.path.join(self.installdir, 'include'), + '--qsci-libdir %s' % os.path.join(self.installdir, 'lib'), + '--pyqt-sipdir %s' % pyqt_sipdir, + '--apidir %s' % os.path.join(self.installdir, 'qsci', 'api', 'python'), + '--no-stubs', + '--sip $EBROOTSIP' + ] + if sip_incdir: + cfgopts += ['--sip-incdir %s' % sip_incdir] + + if LooseVersion(self.version) >= LooseVersion('2.10.7'): + cfgopts.append('--no-dist-info') + + # This flag was added in version 2.11 + if LooseVersion(self.version) >= LooseVersion('2.11'): + cfgopts.append("--pyqt=%s" % self.pyqt_pkg_name) + + run_cmd("python configure.py %s" % ' '.join(cfgopts)) + + super(EB_QScintilla, self).build_step() + super(EB_QScintilla, self).install_step() + + target_dir = os.path.join(self.installdir, pylibdir) + pyqt_pylibdir = os.path.join(self.pyqt_root, pylibdir) + try: + os.chdir(target_dir) + for entry in [x for x in os.listdir(pyqt_pylibdir) if not x.startswith('__init__.py')]: + symlink(os.path.join(pyqt_pylibdir, entry), os.path.join(target_dir, entry)) + except OSError as err: + raise EasyBuildError("Failed to symlink PyQt Python bindings in %s: %s", target_dir, err) + + # also requires empty __init__.py file to ensure Python modules can be imported from this location + write_file(os.path.join(target_dir, '__init__.py'), '') + + def sanity_check_step(self): + """Custom sanity check for QScintilla.""" + + if LooseVersion(self.version) >= LooseVersion('2.10'): + if self.pyqt_pkg_name == 'PyQt5': + qsci_lib = 'libqscintilla2_qt5' + else: + qsci_lib = 'libqscintilla2_qt4' + else: + qsci_lib = 'libqscintilla2' + + custom_paths = { + 'files': [os.path.join('lib', qsci_lib + '.' + get_shared_lib_ext())], + 'dirs': ['data', os.path.join('include', 'Qsci'), 'trans'], + } + # also install Python bindings if Python is included as a dependency + python = get_software_root('Python') + + custom_commands = [] + if python: + custom_paths['dirs'].extend([ + os.path.join(det_pylibdir(), self.pyqt_pkg_name), + os.path.join('qsci', 'api', 'python'), + os.path.join('share', 'sip', self.pyqt_pkg_name), + ]) + custom_commands.append("python -c 'import %s.Qsci'" % self.pyqt_pkg_name) + + super(EB_QScintilla, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) + + def make_module_extra(self): + """Custom extra module file entries for QScintilla.""" + txt = super(EB_QScintilla, self).make_module_extra() + python = get_software_root('Python') + if python: + if self.cfg['multi_deps'] and 'Python' in self.cfg['multi_deps']: + txt += self.module_generator.prepend_paths('EBPYTHONPREFIXES', '') + else: + txt += self.module_generator.prepend_paths('PYTHONPATH', [det_pylibdir()]) + return txt \ No newline at end of file diff --git a/476_QGIS/qscintilla.eb b/476_QGIS/qscintilla.eb index cdf7a563..95556fcf 100644 --- a/476_QGIS/qscintilla.eb +++ b/476_QGIS/qscintilla.eb @@ -26,6 +26,7 @@ builddependencies = [ dependencies = [ ('Python', '3.11.3'), ('PyQt5', '5.15.10'), + ('SIP', '6.8.1'), ] prebuildopts = 'export CPATH=$EBROOTQT5/include/QtWidgets:$EBROOTQT5/include/QtPrintSupport:$CPATH && ' @@ -33,9 +34,17 @@ prebuildopts = 'export CPATH=$EBROOTQT5/include/QtWidgets:$EBROOTQT5/include/QtP moduleclass = 'vis' # E2: -# == 2024-12-19 16:16:38,691 build_log.py:171 ERROR EasyBuild crashed with an error (at easybuild/easybuild-framework/easybuild/base/exceptions.py:126 in __init__): -# cmd "python configure.py --destdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/lib/python3.11/site-packages/PyQt5 --qsci-sipdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/share/sip/PyQt5 --qsci-incdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/include --qsci-libdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/lib --pyqt-sipdir /apps/gent/RHEL8/cascadelake-ib/software/PyQt5/5.15.10-GCCcore-12.3.0/share/sip --apidir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/qsci/api/python --no-stubs --no-dist-info --pyqt=PyQt5" exited with exit code 1 and output: -# Error: Make sure you have a working sip on your PATH or use the --sip argument to explicitly specify a working sip. + # -> add SIP as dep + # == 2024-12-19 16:16:38,691 build_log.py:171 ERROR EasyBuild crashed with an error (at easybuild/easybuild-framework/easybuild/base/exceptions.py:126 in __init__): + # cmd "python configure.py + # --destdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/lib/python3.11/site-packages/PyQt5 + # --qsci-sipdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/share/sip/PyQt5 + # --qsci-incdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/include + # --qsci-libdir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/lib + # --pyqt-sipdir /apps/gent/RHEL8/cascadelake-ib/software/PyQt5/5.15.10-GCCcore-12.3.0/share/sip + # --apidir /scratch/gent/vo/001/gvo00117/easybuild/RHEL8/cascadelake-ampere-ib/software/QScintilla/2.11.6-GCCcore-12.3.0/qsci/api/python + # --no-stubs --no-dist-info --pyqt=PyQt5" exited with exit code 1 and output: + # Error: Make sure you have a working sip on your PATH or use the --sip argument to explicitly specify a working sip. # E1: -> stay with 2.11.6 - next version is changed (2.12.0) # -> has its own EBlock: https://github.com/easybuilders/easybuild-easyblocks/blob/develop/easybuild/easyblocks/q/qscintilla.py