From becb4fa339e8d3774cf106df4dbbf75a8a1e5d4f Mon Sep 17 00:00:00 2001 From: Andoni Morales Alastruey Date: Thu, 27 Jan 2022 15:04:03 +0100 Subject: windows: fix build using subprojects with python >=3.8 Fixes #416 --- giscanner/pkgconfig.py | 10 ++++++++++ giscanner/utils.py | 41 +++++++++++++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/giscanner/pkgconfig.py b/giscanner/pkgconfig.py index 66822378..39db41d6 100644 --- a/giscanner/pkgconfig.py +++ b/giscanner/pkgconfig.py @@ -50,6 +50,16 @@ def cflags(packages, msvc_syntax=False, ignore_errors=True, command=None): return shlex.split(out) +def libs_only_L(packages, static=False, msvc_syntax=False, ignore_errors=True, command=None): + flags = ['--msvc-syntax'] if msvc_syntax else [] + if static: + flags.append('--static') + flags.append('--libs-only-L') + flags.extend(packages) + out = check_output(flags, ignore_errors, command) + return shlex.split(out) + + def libs(packages, msvc_syntax=False, ignore_errors=True, command=None): flags = ['--msvc-syntax'] if msvc_syntax else [] flags.append('--libs') diff --git a/giscanner/utils.py b/giscanner/utils.py index 45807f17..31c7ea48 100644 --- a/giscanner/utils.py +++ b/giscanner/utils.py @@ -26,6 +26,7 @@ import shutil import sys import time import giscanner.pkgconfig +from typing import Dict _debugflags = None @@ -272,12 +273,26 @@ def rmtree(*args, **kwargs): return +class Singleton(type): + ''' + A helper class to be used as metaclass to implement a singleton. + ''' + _instances: Dict[type, object] = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + # Mainly used for builds against Python 3.8.x and later on Windows where we need to be # more explicit on where dependent DLLs are located, via the use of -# os.add_dll_directory(). So, we make use of the envvar GI_EXTRA_BASE_DLL_DIRS and the -# newly-added bindir() method of our pkgconfig module to acquire the paths where dependent -# DLLs could be found. -class dll_dirs(): +# os.add_dll_directory(). +# To acquire the paths where dependent DLLs could be found we use: +# * The envvar GI_EXTRA_BASE_DLL_DIRS +# * The bindir variable from gio-2.0.pc when dependencies are installed in a prefix +# * -L directories from pkg-config --libs-only-L for uninstalled dependencies +class dll_dirs(metaclass=Singleton): _cached_dll_dirs = None _cached_added_dll_dirs = None @@ -290,22 +305,28 @@ class dll_dirs(): if os.name == 'nt' and hasattr(os, 'add_dll_directory'): if 'GI_EXTRA_BASE_DLL_DIRS' in os.environ: for path in os.environ.get('GI_EXTRA_BASE_DLL_DIRS').split(os.pathsep): - if path not in self._cached_dll_dirs: - self._cached_dll_dirs.append(path) - self._cached_added_dll_dirs.append(os.add_dll_directory(path)) + self._add_dll_dir(path) + + for path in giscanner.pkgconfig.libs_only_L(pkgs, True): + libpath = path.replace('-L', '') + self._add_dll_dir(libpath) for path in giscanner.pkgconfig.bindir(pkgs): - if path not in self._cached_dll_dirs: - self._cached_dll_dirs.append(path) - self._cached_added_dll_dirs.append(os.add_dll_directory(path)) + self._add_dll_dir(path) def cleanup_dll_dirs(self): if self._cached_added_dll_dirs is not None: for added_dll_dir in self._cached_added_dll_dirs: added_dll_dir.close() + self._cached_added_dll_dirs.clear() if self._cached_dll_dirs is not None: self._cached_dll_dirs.clear() + def _add_dll_dir(self, path): + if path not in self._cached_dll_dirs: + self._cached_dll_dirs.append(path) + self._cached_added_dll_dirs.append(os.add_dll_directory(path)) + # monkey patch distutils.cygwinccompiler # somehow distutils returns runtime library only up to -- cgit v1.2.1