# ------------------------------------------------------------------
|
# 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']
|