diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 36da775bcd9..988d797956b 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -116,6 +116,22 @@ jobs: conda list displayName: List installed packages + # Cache the ${HOME}/.gmt directory, for docs and testing + - task: Cache@2 + inputs: + key: cachedata | 20200519 + path: $(HOME)/.gmt + cacheHitVar: CACHE_CACHEDATA_RESTORED + displayName: Cache GMT remote data for testing + + # Download remote files, if not already cached + - bash: | + set -x -e + source activate testing + gmt which -Gu @earth_relief_10m @earth_relief_60m @ridge.txt @Table_5_11.txt @tut_bathy.nc @tut_quakes.ngdc @tut_ship.xyz + displayName: Download remote data + condition: ne(variables['CACHE_CACHEDATA_RESTORED'], true) + # Install the package - bash: | set -x -e @@ -163,8 +179,6 @@ jobs: CONDA_REQUIREMENTS: requirements.txt CONDA_REQUIREMENTS_DEV: requirements-dev.txt CONDA_INSTALL_EXTRA: "codecov gmt=6.0.0" - # ctypes.CDLL cannot find conda's libraries - GMT_LIBRARY_PATH: 'C:\Miniconda\envs\testing\Library\bin' strategy: matrix: @@ -198,6 +212,22 @@ jobs: conda list displayName: List installed packages + # Cache the ${HOME}/.gmt directory, for docs and testing + - task: Cache@2 + inputs: + key: cachedata | 20200519 + path: $(HOMEPATH)/.gmt + cacheHitVar: CACHE_CACHEDATA_RESTORED + displayName: Cache GMT remote data for testing + + # Download remote files, if not already cached + - bash: | + set -x -e + source activate testing + gmt which -Gu @earth_relief_10m @earth_relief_60m @ridge.txt @Table_5_11.txt @tut_bathy.nc @tut_quakes.ngdc @tut_ship.xyz + displayName: Download remote data + condition: ne(variables['CACHE_CACHEDATA_RESTORED'], true) + # Install the package that we want to test - bash: | set -x -e diff --git a/pygmt/clib/loading.py b/pygmt/clib/loading.py index a0b9b18c043..4b552fc8444 100644 --- a/pygmt/clib/loading.py +++ b/pygmt/clib/loading.py @@ -7,11 +7,12 @@ import os import sys import ctypes +from ctypes.util import find_library from ..exceptions import GMTOSError, GMTCLibError, GMTCLibNotFoundError -def load_libgmt(env=None): +def load_libgmt(): """ Find and load ``libgmt`` as a :py:class:`ctypes.CDLL`. @@ -19,12 +20,6 @@ def load_libgmt(env=None): the environment variable ``GMT_LIBRARY_PATH``. If it's not set, will let ctypes try to find the library. - Parameters - ---------- - env : dict or None - A dictionary containing the environment variables. If ``None``, will - default to ``os.environ``. - Returns ------- :py:class:`ctypes.CDLL` object @@ -37,14 +32,11 @@ def load_libgmt(env=None): couldn't access the functions). """ - if env is None: - env = os.environ - libnames = clib_name(os_name=sys.platform) - libpath = env.get("GMT_LIBRARY_PATH", "") + lib_fullnames = clib_full_names() error = True - for libname in libnames: + for libname in lib_fullnames: try: - libgmt = ctypes.CDLL(os.path.join(libpath, libname)) + libgmt = ctypes.CDLL(libname) check_libgmt(libgmt) error = False break @@ -52,12 +44,14 @@ def load_libgmt(env=None): error = err if error: raise GMTCLibNotFoundError( - "Error loading the GMT shared library '{}':".format(", ".join(libnames)) + "Error loading the GMT shared library '{}':".format( + ", ".join(lib_fullnames) + ) ) return libgmt -def clib_name(os_name): +def clib_names(os_name): """ Return the name of GMT's shared library for the current OS. @@ -68,20 +62,51 @@ def clib_name(os_name): Returns ------- - libname : list of str + libnames : list of str List of possible names of GMT's shared library. """ if os_name.startswith("linux"): - libname = ["libgmt.so"] + libnames = ["libgmt.so"] elif os_name == "darwin": # Darwin is macOS - libname = ["libgmt.dylib"] + libnames = ["libgmt.dylib"] elif os_name == "win32": - libname = ["gmt.dll", "gmt_w64.dll", "gmt_w32.dll"] + libnames = ["gmt.dll", "gmt_w64.dll", "gmt_w32.dll"] else: raise GMTOSError('Operating system "{}" not supported.'.format(sys.platform)) - return libname + return libnames + + +def clib_full_names(env=None): + """ + Return the full path of GMT's shared library for the current OS. + + Parameters + ---------- + env : dict or None + A dictionary containing the environment variables. If ``None``, will + default to ``os.environ``. + + Returns + ------- + lib_fullnames: list of str + List of possible full names of GMT's shared library. + + """ + if env is None: + env = os.environ + libnames = clib_names(os_name=sys.platform) # e.g. libgmt.so, libgmt.dylib, gmt.dll + libpath = env.get("GMT_LIBRARY_PATH", "") # e.g. $HOME/miniconda/envs/pygmt/lib + + lib_fullnames = [os.path.join(libpath, libname) for libname in libnames] + # Search for DLLs in PATH if GMT_LIBRARY_PATH is not defined [Windows only] + if not libpath and sys.platform == "win32": + for libname in libnames: + libfullpath = find_library(libname) + if libfullpath: + lib_fullnames.append(libfullpath) + return lib_fullnames def check_libgmt(libgmt): diff --git a/pygmt/tests/test_clib_loading.py b/pygmt/tests/test_clib_loading.py index 1cc6dc4f4df..9c11cb698c1 100644 --- a/pygmt/tests/test_clib_loading.py +++ b/pygmt/tests/test_clib_loading.py @@ -1,9 +1,10 @@ """ Test the functions that load libgmt """ +import os import pytest -from ..clib.loading import clib_name, load_libgmt, check_libgmt +from ..clib.loading import clib_names, load_libgmt, check_libgmt from ..exceptions import GMTCLibError, GMTOSError, GMTCLibNotFoundError @@ -20,16 +21,25 @@ def test_load_libgmt(): def test_load_libgmt_fail(): "Test that loading fails when given a bad library path." - env = {"GMT_LIBRARY_PATH": "not/a/real/path"} + # save the old value (if any) before setting a fake "GMT_LIBRARY_PATH" + old_gmt_library_path = os.environ.get("GMT_LIBRARY_PATH") + + os.environ["GMT_LIBRARY_PATH"] = "/not/a/real/path" with pytest.raises(GMTCLibNotFoundError): - load_libgmt(env=env) + load_libgmt() + + # revert back to the original status (if any) + if old_gmt_library_path: + os.environ["GMT_LIBRARY_PATH"] = old_gmt_library_path + else: + del os.environ["GMT_LIBRARY_PATH"] -def test_clib_name(): +def test_clib_names(): "Make sure we get the correct library name for different OS names" for linux in ["linux", "linux2", "linux3"]: - assert clib_name(linux) == ["libgmt.so"] - assert clib_name("darwin") == ["libgmt.dylib"] - assert clib_name("win32") == ["gmt.dll", "gmt_w64.dll", "gmt_w32.dll"] + assert clib_names(linux) == ["libgmt.so"] + assert clib_names("darwin") == ["libgmt.dylib"] + assert clib_names("win32") == ["gmt.dll", "gmt_w64.dll", "gmt_w32.dll"] with pytest.raises(GMTOSError): - clib_name("meh") + clib_names("meh")