[gobject-introspection/msvc.fixes: 2/3] Windows: Fix building and running on Python 3.8+



commit 1e121a984f09dbc9d2a0f96a7413ba30d4f7e4b9
Author: Chun-wei Fan <fanchunwei src gnome org>
Date:   Wed Jan 15 15:32:38 2020 +0800

    Windows: Fix building and running on Python 3.8+
    
    Python 3.8.x and later changed the way how dependent DLLs can be found for a
    given Python module that depends on the presence of external, non-system DLLs,
    for more fine-grained DLLs searching and loading, as well as for security
    purposes, which required the use of os.add_dll_directory().
    
    Thus, the scripts in scanner/ must be updated such that:
    
    -We are able to find and load the GObject and GLib DLLs, at least, on
     initialization, via the use of 'pkg-config --variable bindir', as we already
     depend on the GLib DLLs.   Note that since the gobject-2.0.pc file does not
     have a 'bindir' entry, we use gio-2.0.pc instead to discover the bindir of the
     GObject and GLib DLLs.  Likewise, we use the same technique for pkg-config
     files that are dependent upon when using g-ir-scanner (or friends) on items
     that are higher up in the stack.
    
    -We are able to find any other DLLs (e.g. non-GNOME DLLs such as ZLib) that
     are dependent but are not found in the path(s) given by 'pkg-config --variable
     bindir' with the envvar GI_EXTRA_BASE_DLL_DIRS, as needed.  Note that
     GI_EXTRA_BASE_DLL_DIRS can be multiple paths, and that the results from
     'pkg-config --variable bindir' takes precendence, in a LIFO manner.

 giscanner/dumper.py         |  5 +++++
 giscanner/pkgconfig.py      | 17 +++++++++++++++++
 giscanner/sourcescanner.py  |  5 ++++-
 giscanner/utils.py          | 37 +++++++++++++++++++++++++++++++++++++
 tools/g-ir-tool-template.in |  4 ++++
 5 files changed, 67 insertions(+), 1 deletion(-)
---
diff --git a/giscanner/dumper.py b/giscanner/dumper.py
index 61942faf..936ee540 100644
--- a/giscanner/dumper.py
+++ b/giscanner/dumper.py
@@ -28,6 +28,7 @@ import tempfile
 from .gdumpparser import IntrospectionBinary
 from . import pkgconfig, utils
 from .ccompiler import CCompiler
+from .utils import dll_dirs
 
 # bugzilla.gnome.org/558436
 # Compile a binary program which is then linked to a library
@@ -252,6 +253,9 @@ class DumpCompiler(object):
             for ldflag in shlex.split(os.environ.get('LDFLAGS', '')):
                 args.append(ldflag)
 
+        dll_dirs = utils.dll_dirs()
+        dll_dirs.add_dll_dirs(self._packages)
+
         if not self._options.quiet:
             print("g-ir-scanner: link: %s" % (
                 subprocess.list2cmdline(args), ))
@@ -278,6 +282,7 @@ class DumpCompiler(object):
         finally:
             if msys:
                 os.remove(tf_name)
+            dll_dirs.cleanup_dll_dirs()
 
 
 def compile_introspection_binary(options, get_type_functions,
diff --git a/giscanner/pkgconfig.py b/giscanner/pkgconfig.py
index 6f0b2d57..315a3e11 100644
--- a/giscanner/pkgconfig.py
+++ b/giscanner/pkgconfig.py
@@ -56,3 +56,20 @@ def libs(packages, msvc_syntax=False, ignore_errors=True, command=None):
     flags.extend(packages)
     out = check_output(flags, ignore_errors, command)
     return shlex.split(out)
+
+
+# Used on Windows for Python 3.8+ so that we can use it to control in a fine-grain manner
+# where we load dependent DLLs via os.add_dll_directory().  This retrieves from pkg-config
+# the bindir's that are used for individual dependent packages, so that we know where their
+# DLLs can be found.
+def bindir(packages, ignore_errors=True, command=None):
+    if os.name == 'nt' and hasattr(os, 'add_dll_directory'):
+        flags = []
+        flags.append('--variable')
+        flags.append('bindir')
+        flags.extend(packages)
+        out = check_output(flags, ignore_errors, command)
+        return shlex.split(out)
+
+    else:
+        return None
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index 1632e1cb..e50f40aa 100644
--- a/giscanner/sourcescanner.py
+++ b/giscanner/sourcescanner.py
@@ -24,13 +24,16 @@ import tempfile
 from .libtoolimporter import LibtoolImporter
 from .message import Position
 from .ccompiler import CCompiler
-from .utils import have_debug_flag
+from .utils import have_debug_flag, dll_dirs
 
 with LibtoolImporter(None, None):
+    dlldirs = dll_dirs()
+    dlldirs.add_dll_dirs(['gio-2.0'])
     if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
         from _giscanner import SourceScanner as CSourceScanner
     else:
         from giscanner._giscanner import SourceScanner as CSourceScanner
+    dlldirs.cleanup_dll_dirs()
 
 HEADER_EXTS = ['.h', '.hpp', '.hxx']
 SOURCE_EXTS = ['.c', '.cpp', '.cc', '.cxx']
diff --git a/giscanner/utils.py b/giscanner/utils.py
index 25160c3e..5a582bbe 100644
--- a/giscanner/utils.py
+++ b/giscanner/utils.py
@@ -24,6 +24,7 @@ import subprocess
 import platform
 import shutil
 import time
+import giscanner.pkgconfig
 
 
 _debugflags = None
@@ -282,3 +283,39 @@ def rmtree(*args, **kwargs):
             continue
         else:
             return
+
+
+# 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():
+    _cached_dll_dirs = None
+    _cached_added_dll_dirs = None
+
+    def __init__(self):
+        if os.name == 'nt' and hasattr(os, 'add_dll_directory'):
+            self._cached_dll_dirs = []
+            self._cached_added_dll_dirs = []
+
+
+    def add_dll_dirs(self, pkgs):
+        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))
+
+            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))
+
+    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()
+        if self._cached_dll_dirs is not None:
+            self._cached_dll_dirs.clear()
diff --git a/tools/g-ir-tool-template.in b/tools/g-ir-tool-template.in
index 8462f88a..c4a10a28 100755
--- a/tools/g-ir-tool-template.in
+++ b/tools/g-ir-tool-template.in
@@ -96,5 +96,9 @@ if not os.path.isfile(os.path.join(pylibdir, 'giscanner', '_giscanner' + py_mod_
 
 sys.path.insert(0, pylibdir)
 
+from giscanner.utils import dll_dirs
+dll_dirs = dll_dirs()
+dll_dirs.add_dll_dirs(['gio-2.0'])
+
 from giscanner.@TOOL_MODULE@ import @TOOL_FUNCTION@
 sys.exit(@TOOL_FUNCTION@(sys.argv))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]