# ------------------------------------------------------------------ # Copyright (c) 2024 PyInstaller Development Team. # # This file is distributed under the terms of the GNU General Public # License (version 2.0 or later). # # The full license is available in LICENSE, distributed with # this software. # # SPDX-License-Identifier: GPL-2.0-or-later # ------------------------------------------------------------------ import os import pathlib from PyInstaller import isolated from PyInstaller.utils.hooks import collect_data_files, logger # Collect the headers (eccodes.h, gribapi.h) that are bundled with the package. datas = collect_data_files('gribapi') # Collect the eccodes shared library. Starting with eccodes 2.37.0, binary wheels with bundled shared library are # provided for linux and macOS, and since 2.39.0, also for Windows. # NOTE: custom isolated function is used here instead of `get_module_attribute('gribapi.bindings', 'library_path')` # hook utility function because with eccodes 2.37.0, `eccodes` needs to be imported before `gribapi` to avoid circular # imports... Also, this way, we can obtain the root directory of eccodes package at the same time. @isolated.decorate def get_eccodes_library_path(): import eccodes import gribapi.bindings return ( # Path to eccodes shared library used by the gribapi bindings. str(gribapi.bindings.library_path), # Path to eccodes package (implicitly assumed to be next to the gribapi package, since they are part of the # same eccodes dist). str(eccodes.__path__[0]), ) binaries = [] hiddenimports = [] try: library_path, package_path = get_eccodes_library_path() except Exception: logger.warning("hook-gribapi: failed to query gribapi.bindings.library_path!", exc_info=True) library_path = None if library_path: if not os.path.isabs(library_path): from PyInstaller.depend.utils import _resolveCtypesImports resolved_binary = _resolveCtypesImports([os.path.basename(library_path)]) if resolved_binary: library_path = resolved_binary[0][1] else: logger.warning("hook-gribapi: failed to resolve shared library name %r!", library_path) library_path = None else: logger.warning("hook-gribapi: could not determine path to eccodes shared library!") if library_path: # If we are collecting eccodes shared library that is bundled with eccodes >= 2.37.0 binary wheel, attempt to # preserve its parent directory layout. This ensures that the library is found at run-time, but implicitly requires # PyInstaller 6.x, whose binary dependency analysis (that might also pick up this shared library) also preserves the # parent directory layout of discovered shared libraries. With PyInstaller 5.x, this will result in duplication # because binary dependency analysis collects into top-level application directory, but that copy will not be # discovered at run-time, so duplication is unavoidable. library_parent_path = pathlib.PurePath(library_path).parent package_parent_path = pathlib.PurePath(package_path).parent if package_parent_path in library_parent_path.parents: # Should end up being `eccodes.libs` on Linux, `eccodes/.dylib` on macOS, and `eccodes` on Windows. dest_dir = str(library_parent_path.relative_to(package_parent_path)) else: # External copy; collect into top-level application directory. dest_dir = '.' logger.info( "hook-gribapi: collecting eccodes shared library %r to destination directory %r", library_path, dest_dir ) binaries.append((library_path, dest_dir)) # If the shared library is available in the stand-alone `eccodeslib` package, add this package to to hidden imports, # so that `findlibs.find()` can import it and query its `__file__` attribute. if 'eccodeslib' in library_parent_path.parts: hiddenimports += ['eccodeslib']