ecmwflibs-0.7.1/0000775000175000017500000000000015256416145013667 5ustar alastairalastairecmwflibs-0.7.1/.github/0000775000175000017500000000000015256416132015223 5ustar alastairalastairecmwflibs-0.7.1/.github/workflows/0000775000175000017500000000000015256416132017260 5ustar alastairalastairecmwflibs-0.7.1/.github/workflows/test-universal.yml0000664000175000017500000000231415256416132022770 0ustar alastairalastair# This workflow will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries name: Test Universal on: workflow_dispatch: {} jobs: checks: name: Checks runs-on: ubuntu-latest defaults: run: shell: bash steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.x" - name: Install packages run: | pip install black pytest flake8 isort - name: Code QA run: | black --version isort --check --profile=black . black --check . flake8 . - run: | python -m pip install --upgrade pip pip install setuptools wheel python setup.py bdist_wheel --universal pip install dist/ecmwflibs*none-any.whl mv ecmwflibs ecmwflibs.no rm -fr dist *.egg* ls -l - shell: python run: | import ecmwflibs print(ecmwflibs.__file__) assert ecmwflibs.universal() ecmwflibs-0.7.1/.github/workflows/build-linux.yml0000664000175000017500000002560415256416132022246 0ustar alastairalastair# (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. name: Build Linux on: workflow_dispatch: {} release: types: [ created ] # repository_dispatch: # types: [eccodes-updated, magics-updated] push: branches: - master paths: - "scripts/common.sh" - "scripts/build-linux.sh" - "scripts/wheel-linux.sh" - "tools/copy-licences.py" pull_request: branches: - master paths: - "scripts/common.sh" - "scripts/build-linux.sh" - "scripts/wheel-linux.sh" - "tools/copy-licences.py" jobs: build_manylinux2014: strategy: fail-fast: false matrix: include: - arch: x86_64 runner: ubuntu-latest image: dockcross/manylinux2014-x64:latest # dockcross's aarch64 image cross-compiles from x86_64, but its # bundled crosstool-ng toolchain doesn't actually honor the # manylinux2014 glibc baseline (glibc 2.31+ instead of 2.17 -- # a known, unresolved upstream issue: dockcross/dockcross#443). # Building natively on a real arm64 runner against the real PyPA # image sidesteps the cross-toolchain entirely. - arch: aarch64 runner: ubuntu-24.04-arm image: quay.io/pypa/manylinux2014_aarch64:latest runs-on: ${{ matrix.runner }} name: Build manylinux2014 (${{ matrix.arch }}) steps: - uses: actions/checkout@v6 # Reuses already-built dependencies across runs so an edit to # build-linux.sh doesn't force a full ~20min rebuild of every # unrelated (already-working) step -- only what the edit actually # touches, plus whatever comes after it in the script, re-runs. # Falls back to the most recent same-arch cache when the exact # hash doesn't match (restore-keys), and build-linux.sh's meson_setup # helper (in common.sh) handles reconfiguring an already-built dir. - uses: actions/cache@v4 with: path: | src install build-other key: build-linux2014-${{ matrix.arch }}-${{ matrix.runner }}-${{ hashFiles('scripts/build-linux.sh', 'scripts/common.sh') }} restore-keys: | build-linux2014-${{ matrix.arch }}-${{ matrix.runner }}- - run: | docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ ${{ matrix.image }} \ bash -lc './scripts/build-linux.sh' # The docker container runs as root, so files it creates under # src/install/build-other land on the runner's filesystem owned by # root with whatever (sometimes restrictive) permissions the build # left them with. actions/cache's post-job save step runs as the # unprivileged runner user and silently fails to tar them up # ("Permission denied", visible only as a ##[warning], not a job # failure) otherwise -- so the cache never actually got populated. - name: Fix permissions for caching run: sudo chmod -R a+rX src install build-other ################################################################ - run: | docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ ${{ matrix.image }} \ bash -lc './scripts/wheel-linux.sh 3.10' - uses: actions/upload-artifact@v4 name: Upload wheel 3.10 with: name: wheel-manylinux2014-${{ matrix.arch }}-3.10 path: wheelhouse/*.whl ################################################################ - run: | docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ ${{ matrix.image }} \ bash -lc './scripts/wheel-linux.sh 3.11' - uses: actions/upload-artifact@v4 name: Upload wheel 3.11 with: name: wheel-manylinux2014-${{ matrix.arch }}-3.11 path: wheelhouse/*.whl ################################################################ - run: | docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ ${{ matrix.image }} \ bash -lc './scripts/wheel-linux.sh 3.12' - uses: actions/upload-artifact@v4 name: Upload wheel 3.12 with: name: wheel-manylinux2014-${{ matrix.arch }}-3.12 path: wheelhouse/*.whl ################################################################ - run: | docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ ${{ matrix.image }} \ bash -lc './scripts/wheel-linux.sh 3.13' - uses: actions/upload-artifact@v4 name: Upload wheel 3.13 with: name: wheel-manylinux2014-${{ matrix.arch }}-3.13 path: wheelhouse/*.whl build_manylinux_2_28: strategy: fail-fast: false matrix: include: - arch: x86_64 runner: ubuntu-latest image: dockcross/manylinux_2_28-x64:latest # See build_manylinux2014's aarch64 entry for why this is native. - arch: aarch64 runner: ubuntu-24.04-arm image: quay.io/pypa/manylinux_2_28_aarch64:latest runs-on: ${{ matrix.runner }} name: Build manylinux_2_28 (${{ matrix.arch }}) steps: - uses: actions/checkout@v6 # See build_manylinux2014's cache step for why this is here. - uses: actions/cache@v4 with: path: | src install build-other key: build-linux228-${{ matrix.arch }}-${{ matrix.runner }}-${{ hashFiles('scripts/build-linux.sh', 'scripts/common.sh') }} restore-keys: | build-linux228-${{ matrix.arch }}-${{ matrix.runner }}- - run: | docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ ${{ matrix.image }} \ bash -lc './scripts/build-linux.sh' # See build_manylinux2014's "Fix permissions for caching" step for why. - name: Fix permissions for caching run: sudo chmod -R a+rX src install build-other ################################################################ - run: | docker run --rm \ -v "$PWD:/workspace" \ -w /workspace \ ${{ matrix.image }} \ bash -lc './scripts/wheel-linux.sh 3.14' - uses: actions/upload-artifact@v4 name: Upload wheel 3.14 with: name: wheel-manylinux_2_28-${{ matrix.arch }}-3.14 path: wheelhouse/*.whl test: needs: [ build_manylinux2014, build_manylinux_2_28 ] strategy: fail-fast: false matrix: include: - python-version: "3.10" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.11" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.12" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.13" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.14" wheel-tag: manylinux_2_28 arch: x86_64 - python-version: "3.10" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.11" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.12" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.13" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.14" wheel-tag: manylinux_2_28 arch: aarch64 runs-on: ${{ matrix.arch == 'aarch64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} env: ECCODES_PYTHON_USE_FINDLIBS: "1" name: Test ${{ matrix.arch }} with ${{ matrix.python-version }} steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: actions/download-artifact@v4 with: name: wheel-${{ matrix.wheel-tag }}-${{ matrix.arch }}-${{ matrix.python-version }} - run: pip install *.whl - run: pip install -r tests/requirements.txt - run: pip freeze - run: env | sort - name: Get some data run: | curl -L https://get.ecmwf.int/repository/test-data/metview/gallery/2m_temperature.grib -o data.grib curl -L https://github.com/ecmwf/climetlab/raw/main/docs/examples/test.grib -o climetlab.grib curl -L https://github.com/ecmwf/climetlab/raw/main/docs/examples/test.nc -o climetlab.nc ls -l working-directory: tests - run: pytest -v -s working-directory: tests - uses: actions/upload-artifact@v4 with: name: tests-${{ matrix.wheel-tag }}-${{ matrix.arch }}-${{ matrix.python-version }} path: tests/*.png deploy: if: ${{ github.event_name == 'release' }} strategy: fail-fast: false matrix: include: - python-version: "3.10" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.11" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.12" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.13" wheel-tag: manylinux2014 arch: x86_64 - python-version: "3.14" wheel-tag: manylinux_2_28 arch: x86_64 - python-version: "3.10" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.11" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.12" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.13" wheel-tag: manylinux2014 arch: aarch64 - python-version: "3.14" wheel-tag: manylinux_2_28 arch: aarch64 needs: [ test, build_manylinux2014, build_manylinux_2_28 ] name: Deploy wheel ${{ matrix.arch }} ${{ matrix.python-version }} runs-on: ubuntu-latest steps: - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - run: pip install twine - uses: actions/download-artifact@v4 with: name: wheel-${{ matrix.wheel-tag }}-${{ matrix.arch }}-${{ matrix.python-version }} - run: twine upload *.whl env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} ecmwflibs-0.7.1/.github/workflows/build-windows.yml0000664000175000017500000002015315256416132022573 0ustar alastairalastair# (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. name: Build Windows on: workflow_dispatch: {} release: types: [ created ] # repository_dispatch: # types: [eccodes-updated, magics-updated] push: paths: - "scripts/common.sh" - "scripts/build-windows.sh" - "scripts/wheel-windows.sh" - "tools/copy-licences.py" - "tools/copy-dlls.py" pull_request: branches: - master paths: - "scripts/common.sh" - "scripts/build-windows.sh" - "scripts/wheel-windows.sh" - "tools/copy-licences.py" - "tools/copy-dlls.py" jobs: build: runs-on: windows-latest strategy: fail-fast: false matrix: architecture: [ "x64" ] defaults: run: shell: bash name: Build on ${{ matrix.architecture }} env: WINARCH: ${{ matrix.architecture }} steps: - uses: actions/checkout@v6 - uses: seanmiddleditch/gha-setup-vsdevenv@master with: arch: ${{ matrix.architecture }} - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.10" architecture: ${{ matrix.architecture }} - name: Apply mirror if: false run: | import os a = "repo.msys2.org" b = "mirror.yandex.ru/mirrors/msys2" for root, _, files in os.walk("c:/vcpkg"): for f in files: if f.endswith(".cmake"): path = os.path.join(root, f) with open(path) as f: text = f.read() changed = text.replace(a, b) if text != changed: print("CHANGED", path) with open(path, "w") as f: f.write(changed) shell: python - run: ./scripts/build-windows.sh env: WINARCH: ${{ matrix.architecture }} ################################################################ - name: Set up Python 3.10 uses: actions/setup-python@v5 with: python-version: "3.10" architecture: ${{ matrix.architecture }} - run: ./scripts/wheel-windows.sh "3.10" - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.10 with: name: wheel-windows-3.10-${{ matrix.architecture }} path: wheelhouse/*.whl ################################################################ - name: Set up Python 3.11 uses: actions/setup-python@v5 with: python-version: "3.11" architecture: ${{ matrix.architecture }} - run: ./scripts/wheel-windows.sh "3.11" - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.11 with: name: wheel-windows-3.11-${{ matrix.architecture }} path: wheelhouse/*.whl ################################################################ - name: Set up Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" architecture: ${{ matrix.architecture }} - run: ./scripts/wheel-windows.sh "3.12" - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.12 with: name: wheel-windows-3.12-${{ matrix.architecture }} path: wheelhouse/*.whl ################################################################ - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: "3.13" architecture: ${{ matrix.architecture }} - run: ./scripts/wheel-windows.sh "3.13" - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.13 with: name: wheel-windows-3.13-${{ matrix.architecture }} path: wheelhouse/*.whl ################################################################ - name: Set up Python 3.14 uses: actions/setup-python@v5 with: python-version: "3.14" architecture: ${{ matrix.architecture }} - run: ./scripts/wheel-windows.sh "3.14" - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.14 with: name: wheel-windows-3.14-${{ matrix.architecture }} path: wheelhouse/*.whl test: if: ${{ github.event_name != 'pull_request' }} needs: build runs-on: windows-latest strategy: fail-fast: true matrix: python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] architecture: [ "x64" ] defaults: run: shell: bash env: ECCODES_PYTHON_USE_FINDLIBS: "1" name: Test with Python ${{ matrix.python-version }} ${{ matrix.architecture }} steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - uses: actions/download-artifact@v4 with: name: wheel-windows-${{ matrix.python-version }}-${{ matrix.architecture }} - run: pip install *.whl - run: pip install -r tests/requirements.txt - name: Configure ecCodes runtime lookup shell: python run: | import os import pathlib import ecmwflibs eccodes_path = ecmwflibs.find("eccodes") if not eccodes_path: raise RuntimeError("Cannot resolve bundled ecCodes path from ecmwflibs") libs_dir = str(pathlib.Path(eccodes_path).parent) with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as f: print(f"ECMWFLIBS_ECCODES={eccodes_path}", file=f) print(f"PATH={libs_dir};{os.environ.get('PATH', '')}", file=f) - run: pip freeze - name: Get some data run: | curl -L https://get.ecmwf.int/repository/test-data/metview/gallery/2m_temperature.grib -o data.grib curl -L https://github.com/ecmwf/climetlab/raw/main/docs/examples/test.grib -o climetlab.grib curl -L https://github.com/ecmwf/climetlab/raw/main/docs/examples/test.nc -o climetlab.nc ls -l working-directory: tests - run: pytest --verbose -s working-directory: tests timeout-minutes: 2 - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} with: name: tests-windows-${{ matrix.python-version }}-${{ matrix.architecture }} path: tests/*.png deploy: if: ${{ github.event_name == 'release' }} needs: [ test, build ] name: Deploy wheel ${{ matrix.python-version }} ${{ matrix.architecture }} runs-on: ubuntu-latest strategy: fail-fast: true matrix: python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] architecture: [ "x64" ] steps: - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - run: pip install twine - uses: actions/download-artifact@v4 with: name: wheel-windows-${{ matrix.python-version }}-${{ matrix.architecture }} - run: twine upload *.whl env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} notify: if: ${{ github.event_name == 'release' }} name: Notify universal needs: deploy runs-on: ubuntu-latest steps: - uses: mvasigh/dispatch-action@main with: token: ${{ secrets.NOTIFY_ECMWFLIBS }} repo: ecmwflibs owner: ecmwf event_type: ecmwflibs-windows-uploaded ecmwflibs-0.7.1/.github/workflows/debug-windows.yml0000664000175000017500000000172515256416132022566 0ustar alastairalastair# (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. name: Debug Windows on: workflow_dispatch: {} push: paths: - "scripts/debug-windows.sh" jobs: build: runs-on: windows-latest defaults: run: shell: bash name: Debug Windows env: WINARCH: x64 steps: - uses: actions/checkout@v6 - uses: seanmiddleditch/gha-setup-vsdevenv@master with: arch: x64 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.14" architecture: x64 - run: ./scripts/debug-windows.sh env: WINARCH: x64 ecmwflibs-0.7.1/.github/workflows/build-macos-arm.yml0000664000175000017500000001610115256416132022756 0ustar alastairalastair# (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. name: Build MacOS ARM on: workflow_dispatch: {} release: types: [ created ] # repository_dispatch: # types: [eccodes-updated, magics-updated] push: paths: - "scripts/common.sh" - "scripts/build-macos.sh" - "scripts/wheel-macos.sh" - "tools/copy-licences.py" pull_request: branches: - master paths: - "scripts/common.sh" - "scripts/build-macos.sh" - "scripts/wheel-macos.sh" - "tools/copy-licences.py" # We don't use the setup-python action here as it installs a universal python # which creates universal wheels. We want to create wheels for the specific # architecture we are running on. jobs: build: # if: false runs-on: [ self-hosted, macOS, ARM64 ] # runs-on: [self-hosted, platform-builder-macos13-m2-000] name: Build steps: - run: sudo mkdir -p /Users/runner - run: sudo chown administrator:staff /Users/runner - uses: actions/checkout@v6 - run: ./scripts/select-python.sh "3.10" - run: ./scripts/build-macos.sh ################################################################ - run: ./scripts/select-python.sh "3.10" - run: ./scripts/wheel-macos.sh "3.10" - run: ls -l wheelhouse - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.10 with: name: wheel-macos-3.10 path: wheelhouse/*.whl - run: rm -fr wheelhouse ################################################################ - run: ./scripts/select-python.sh "3.11" - run: ./scripts/wheel-macos.sh "3.11" - run: ls -l wheelhouse - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.11 with: name: wheel-macos-3.11 path: wheelhouse/*.whl - run: rm -fr wheelhouse ################################################################ - run: ./scripts/select-python.sh "3.12" - run: ./scripts/wheel-macos.sh "3.12" - run: ls -l wheelhouse - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.12 with: name: wheel-macos-3.12 path: wheelhouse/*.whl - run: rm -fr wheelhouse ################################################################ - run: ./scripts/select-python.sh "3.13" - run: ./scripts/wheel-macos.sh "3.13" - run: ls -l wheelhouse - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.13 with: name: wheel-macos-3.13 path: wheelhouse/*.whl - run: rm -fr wheelhouse ################################################################ - run: ./scripts/select-python.sh "3.14" - run: ./scripts/wheel-macos.sh "3.14" - run: ls -l wheelhouse - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} name: Upload wheel 3.14 with: name: wheel-macos-3.14 path: wheelhouse/*.whl - run: rm -fr wheelhouse test: if: ${{ github.event_name != 'pull_request' }} needs: build runs-on: [ self-hosted, macOS, ARM64 ] env: ECCODES_PYTHON_USE_FINDLIBS: "1" strategy: fail-fast: true max-parallel: 1 matrix: python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] name: Test with Python ${{ matrix.python-version }} steps: - uses: actions/checkout@v6 - uses: actions/download-artifact@v4 with: name: wheel-macos-${{ matrix.python-version }} - run: ls -l - run: ./scripts/select-python.sh ${{ matrix.python-version }} - run: python3 -m venv .venv - run: .venv/bin/pip install --upgrade pip - run: ls -l /opt/homebrew/opt/python@${{ matrix.python-version }}/libexec/bin - run: which python3 - run: python3 --version - run: pwd - run: ls -l - run: .venv/bin/pip install *.whl - run: .venv/bin/pip install -r tests/requirements.txt - name: Configure ecCodes/Magics runtime lookup run: | cat > "$RUNNER_TEMP/configure_runtime.py" <<'EOF' import os import pathlib import ecmwflibs eccodes_path = ecmwflibs.find("eccodes") magplus_path = ecmwflibs.find("magplus") if not eccodes_path: raise RuntimeError("Cannot resolve bundled ecCodes path from ecmwflibs") if not magplus_path: raise RuntimeError("Cannot resolve bundled MagPlus path from ecmwflibs") package_dir = str(pathlib.Path(ecmwflibs.__file__).resolve().parent) libs_dir = str(pathlib.Path(magplus_path).resolve().parent) with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as f: print(f"ECMWFLIBS_ECCODES={eccodes_path}", file=f) print(f"ECMWFLIBS_MAGPLUS={magplus_path}", file=f) print(f"MAGPLUS_HOME={package_dir}", file=f) print( f"DYLD_LIBRARY_PATH={libs_dir}:{os.environ.get('DYLD_LIBRARY_PATH', '')}", file=f, ) EOF .venv/bin/python3 "$RUNNER_TEMP/configure_runtime.py" - run: .venv/bin/pip freeze - name: Get some data run: | curl -L https://get.ecmwf.int/repository/test-data/metview/gallery/2m_temperature.grib -o data.grib curl -L https://github.com/ecmwf/climetlab/raw/main/docs/examples/test.grib -o climetlab.grib curl -L https://github.com/ecmwf/climetlab/raw/main/docs/examples/test.nc -o climetlab.nc ls -l working-directory: tests - run: ../.venv/bin/pytest -v -s working-directory: tests - uses: actions/upload-artifact@v4 if: ${{ github.event_name != 'pull_request' }} with: name: tests-macos-${{ matrix.python-version }} path: tests/*.png - run: rm -fr *.whl tests deploy: if: ${{ github.event_name == 'release' }} needs: [ test, build ] name: Deploy wheel ${{ matrix.python-version }} runs-on: [ self-hosted, macOS, ARM64 ] strategy: fail-fast: true max-parallel: 1 matrix: python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] steps: - uses: actions/checkout@v6 - run: ./scripts/select-python.sh ${{ matrix.python-version }} - run: pip3 install twine - uses: actions/download-artifact@v4 with: name: wheel-macos-${{ matrix.python-version }} - run: twine upload *.whl env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} ecmwflibs-0.7.1/.github/workflows/deploy-universal.yml0000664000175000017500000000453415256416132023313 0ustar alastairalastair# This workflow will upload a Python Package using Twine when a release is created # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries name: Deploy Universal on: workflow_dispatch: {} # repository_dispatch: # types: [ecmwflibs-windows-uploaded] # workflow_run: # workflows: ["Build Windows"] # branches: [main] # types: [completed] jobs: # First check that the wheels are properly pushed to PiPy so # no one gets the universal version by accident, because # it is pushed first checks: runs-on: ${{ matrix.os }} name: "Check ${{ matrix.os }}" strategy: matrix: os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] defaults: run: shell: bash steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.x" - name: Install packages run: | pip install black pytest flake8 isort setuptools - name: Code QA run: | black --version isort --check --profile=black . black --check . flake8 . - run: | python -m pip install --upgrade pip version=$(python ./setup.py --version) mv ecmwflibs ecmwflibs.no rm -fr dist *.egg* ls -l pip install ecmwflibs==$version - shell: python run: | import ecmwflibs print(ecmwflibs.__file__) assert not ecmwflibs.universal() deploy: runs-on: ubuntu-latest needs: checks steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.x" # - name: Check version # run: | # release=${GITHUB_REF##*/} # version=$(python setup.py --version) # test "$release" == "$version" - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: | python setup.py bdist_wheel --universal twine upload dist/* ecmwflibs-0.7.1/scripts/0000775000175000017500000000000015256416132015352 5ustar alastairalastairecmwflibs-0.7.1/scripts/build-macos-m1.sh0000775000175000017500000000234715256416132020431 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux platform=$(uname) arch=$(uname -a | sed 's/.* //') if [[ "$platform.$arch" != "Darwin.arm64" ]] then exit 1 fi here=$(dirname $0) cd $here/.. rm -fr build* dist install wheelhouse cairo.rb ./scripts/build-macos.sh mkdir -p /tmp/build-macos-m1 PATH=/tmp/build-macos-m1:$PATH for n in 10 11 12 13 14 do rm -fr wheelhouse brew install python@3.$n ln -sf $(ls -1d /opt/homebrew/Cellar/python@3.$n/*/bin/python3.$n) /tmp/build-macos-m1/python3 ln -sf $(ls -1d /opt/homebrew/Cellar/python@3.$n/*/bin/python3.$n) /tmp/build-macos-m1/python ln -sf $(ls -1d /opt/homebrew/Cellar/python@3.$n/*/bin/pip3.$n) /tmp/build-macos-m1/pip3 ln -sf $(ls -1d /opt/homebrew/Cellar/python@3.$n/*/bin/pip3.$n) /tmp/build-macos-m1/pip ./scripts/wheel-macos.sh "3.$n" pip3 install twine twine upload wheelhouse/*.whl done ecmwflibs-0.7.1/scripts/wheel-windows.sh0000775000175000017500000000105715256416132020510 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux python -m pip install --upgrade pip setuptools wheel rm -fr dist wheelhouse ecmwflibs.egg-info build python setup.py bdist_wheel mv dist wheelhouse ecmwflibs-0.7.1/scripts/c-no-arch.sh0000775000175000017500000000034515256416132017462 0ustar alastairalastair#!/bin/bash set -ex skip=0 for c do shift if [[ "$c" == "-arch" ]]; then skip=1 continue fi if [[ $skip -eq 1 ]]; then skip=0 continue fi set -- "$@" "$c" done clang "$@" ecmwflibs-0.7.1/scripts/debug-windows.sh0000775000175000017500000000131415256416132020466 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux for p in netcdf-c[core,netcdf-4] do echo $p:$WINARCH-windows vcpkg install $p:$WINARCH-windows done cd tests cmake . \ -G"NMake Makefiles" \ -DCMAKE_TOOLCHAIN_FILE=/c/vcpkg/scripts/buildsystems/vcpkg.cmake \ -DCMAKE_C_COMPILER=cl.exe cmake --build . ls -lrt ./open_netcdf test.nc4 ecmwflibs-0.7.1/scripts/versions.sh0000775000175000017500000000122115256416132017555 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux if [[ ! -s versions ]] then echo "versions is missing or empty" >&2 exit 1 fi cat versions cd src for n in * do cd $n url=$(git remote -v | head -1 | awk '{print $2;}') sha1=$(git rev-parse HEAD) echo git $url $sha1 cd .. done cd .. ecmwflibs-0.7.1/scripts/libs-macos.py0000775000175000017500000000164415256416132017765 0ustar alastairalastair#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import subprocess import sys null = open(os.devnull, "w") def otool(name): try: return subprocess.check_output(["otool", "-L", name], stderr=null).decode( "utf-8" ) except Exception: return "" def process(name, seen, depth=0): if name in seen: return seen.add(name) if name.endswith(".dylib"): print(f"{' ' * depth}{name}") deps = otool(name) for line in deps.split("\n"): line = line.strip() if line.endswith(":"): continue bits = line.split() if len(bits) < 1: continue lib = bits[0] if lib.startswith("@"): continue if name == lib: continue process(lib, seen, depth + 2) for n in sys.argv[1:]: process(n, set()) ecmwflibs-0.7.1/scripts/wheel-macos.sh0000775000175000017500000000303315256416132020114 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux echo $PATH VERSION=${1:-""} echo ${GITHUB_PATH:-""} || true if [[ -n "${GITHUB_PATH:-}" && -f "${GITHUB_PATH}" ]] then cat "${GITHUB_PATH}" || true fi python3 --version which python3 which pip3 # PATH=/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin # unset PKG_CONFIG_PATH # env | sort rm -fr tmp/wheel-venv python3 -m venv tmp/wheel-venv source tmp/wheel-venv/bin/activate python3 -m pip install --upgrade pip python3 -m pip install wheel delocate setuptools # https://setuptools.pypa.io/en/latest/userguide/ext_modules.html#cross-platform-compilation # Prevent ext_modules from being built as universal # CXX=./scripts/cxx-no-arch.sh # CC=./scripts/c-no-arch.sh which python3 python3 --version which delocate-wheel rm -fr dist wheelhouse python3 setup.py bdist_wheel # Do it twice to get the list of libraries delocate-wheel -w wheelhouse dist/*.whl unzip -l wheelhouse/*.whl | grep 'dylib' >libs python3 -m pip install -r tools/requirements.txt python3 ./tools/copy-licences.py libs rm -fr dist wheelhouse python3 setup.py bdist_wheel delocate-wheel -w wheelhouse dist/*.whl ecmwflibs-0.7.1/scripts/common.sh0000775000175000017500000000700315256416132017201 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux GIT_ECBUILD=https://github.com/ecmwf/ecbuild.git ECBUILD_VERSION=master GIT_ECCODES=https://github.com/ecmwf/eccodes.git ECCODES_VERSION=2.46.3 ECCODES_EXTRA_CMAKE_OPTIONS="-DENABLE_PNG=ON -DENABLE_JPG=ON" GIT_MAGICS=https://github.com/ecmwf/magics.git MAGICS_VERSION=4.16.1 GIT_SQLITE=https://github.com/sqlite/sqlite.git SQLITE_VERSION=master GIT_PROJ=https://github.com/OSGeo/PROJ.git PROJ_VERSION=master GIT_AEC=https://github.com/MathisRosenhauer/libaec.git AEC_VERSION=v1.1.7 GIT_ZLIB=https://github.com/madler/zlib.git ZLIB_VERSION=v1.3.1 GIT_PNG=https://github.com/pnggroup/libpng.git PNG_VERSION=v1.6.45 GIT_FREETYPE=https://gitlab.freedesktop.org/freetype/freetype.git FREETYPE_VERSION=VER-2-13-3 GIT_EXPAT=https://github.com/libexpat/libexpat.git EXPAT_VERSION=R_2_6_4 GIT_FONTCONFIG=https://gitlab.freedesktop.org/fontconfig/fontconfig.git FONTCONFIG_VERSION=2.16.2 GIT_LIBFFI=https://github.com/libffi/libffi.git LIBFFI_VERSION=v3.4.6 GIT_PCRE2=https://github.com/PCRE2Project/pcre2.git PCRE2_VERSION=pcre2-10.44 GIT_GLIB=https://gitlab.gnome.org/GNOME/glib.git GLIB_VERSION=2.78.6 GIT_PIXMAN=https://gitlab.freedesktop.org/pixman/pixman PIXMAN_VERSION=master GIT_CAIRO=https://gitlab.freedesktop.org/cairo/cairo CAIRO_VERSION=1.17.6 GIT_HARFBUZZ=https://github.com/harfbuzz/harfbuzz.git HARFBUZZ_VERSION=master GIT_FRIBIDI=https://github.com/fribidi/fribidi.git FRIBIDI_VERSION=master GIT_PANGO=https://gitlab.gnome.org/GNOME/pango.git PANGO_VERSION=master GIT_UDUNITS=https://github.com/b8raoult/UDUNITS-2.git UDUNITS_VERSION=master GIT_NETCDF=https://github.com/Unidata/netcdf-c.git NETCDF_VERSION=${NETCDF_VERSION:=master} GIT_HDF5=https://github.com/HDFGroup/hdf5.git HDF5_VERSION=${HDF5_VERSION:=hdf5_1_14_6} GIT_JPEG=https://github.com/libjpeg-turbo/libjpeg-turbo.git JPEG_VERSION=3.1.4.1 GIT_JASPER=https://github.com/jasper-software/jasper.git JASPER_VERSION=version-4.2.9 # CI caches install/, build-other/ and src/ across runs so an unrelated # script edit doesn't force a ~20min rebuild of every already-working # dependency. A restored build-other/X is already `meson setup` and needs # --reconfigure (plain `meson setup` errors out on an existing build dir); # --reconfigure also makes sure a cache hit still picks up new flags (e.g. # a changed LDFLAGS/meson option), rather than silently keeping stale ones. meson_setup() { local builddir="${!#}" if [[ -f "$builddir/build.ninja" ]] then meson setup --reconfigure "$@" else meson setup "$@" fi } mkdir -p src rm -fr src/ecbuild src/eccodes src/magics mkdir -p build build-ecmwf find build -mindepth 1 -maxdepth 1 -exec rm -rf {} + find build-ecmwf -mindepth 1 -maxdepth 1 -exec rm -rf {} + git clone --branch $ECBUILD_VERSION $GIT_ECBUILD src/ecbuild git clone --branch $ECCODES_VERSION $GIT_ECCODES src/eccodes git clone --branch $MAGICS_VERSION $GIT_MAGICS src/magics mkdir -p build-ecmwf/eccodes mkdir -p build-ecmwf/magics TOPDIR=$(/bin/pwd) echo "================================================================================" env | sort echo "================================================================================" ecmwflibs-0.7.1/scripts/build-macos.sh0000775000175000017500000000647215256416132020121 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux : > versions uname -a # HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_NO_INSTALL_CLEANUP=1 arch=$(arch) [[ $arch == "i386" ]] && arch="x86_64" # GitHub Actions on macOS declare i386 ARCH="arch -$arch" source scripts/common.sh brew_home=$(brew config | grep HOMEBREW_PREFIX | sed 's/.* //') $ARCH brew install cmake ninja pkg-config automake # brew cat cairo # We don't want a dependency on X11 brew cat cairo | sed ' s/xcb=enabled/xcb=disabled/ s/xlib=enabled/xlib=disabled/ s/enable-tee/disable-tee/ s/enable-xcb/disable-xcb/ s/enable-xlib/disable-xlib/ s/enable-xlib-xrender/disable-xlib-xrender/ s/enable-quartz-image/disable-quartz-image/' > cairo.rb cat cairo.rb $ARCH brew install pango $ARCH brew install netcdf $ARCH brew install proj $ARCH brew install libaec # Try to build cairo from local formula, fall back to standard cairo if it fails if ! $ARCH brew install --build-from-source ./cairo.rb 2>/dev/null then $ARCH brew install cairo fi for p in netcdf proj pango cairo do v=$(brew info $p | grep Cellar | awk '{print $1;}' | awk -F/ '{print $NF;}') echo "brew $p $v" >> versions done # Build eccodes cd $TOPDIR/build-ecmwf/eccodes # We disable JASPER because of a linking issue. JPEG support comes from # other librarues $ARCH $TOPDIR/src/ecbuild/bin/ecbuild \ $TOPDIR/src/eccodes \ -GNinja \ -DCMAKE_OSX_ARCHITECTURES=$arch \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_PYTHON=0 \ -DENABLE_FORTRAN=0 \ -DENABLE_BUILD_TOOLS=0 \ -DENABLE_JPG_LIBJASPER=0 \ -DENABLE_MEMFS=1 \ -DENABLE_INSTALL_ECCODES_DEFINITIONS=0 \ -DENABLE_INSTALL_ECCODES_SAMPLES=0 \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install \ -DCMAKE_INSTALL_RPATH=$TOPDIR/install/lib $ECCODES_EXTRA_CMAKE_OPTIONS cd $TOPDIR $ARCH cmake --build build-ecmwf/eccodes --target install # Build magics cd $TOPDIR/build-ecmwf/magics $ARCH $TOPDIR/src/ecbuild/bin/ecbuild \ $TOPDIR/src/magics \ -GNinja \ -DCMAKE_OSX_ARCHITECTURES=$arch \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_PYTHON=0 \ -DENABLE_FORTRAN=0 \ -DENABLE_BUILD_TOOLS=0 \ -Deccodes_DIR=$TOPDIR/install/lib/cmake/eccodes \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install \ -DCMAKE_INSTALL_RPATH=$TOPDIR/install/lib cd $TOPDIR $ARCH cmake --build build-ecmwf/magics --target install # Create wheel rm -fr dist wheelhouse ecmwflibs/share mkdir -p install/share/magics cp -r install/share ecmwflibs/ cp -r $brew_home/Cellar/proj/*/share ecmwflibs/ rm -fr ecmwflibs/share/proj/*.tif rm -fr ecmwflibs/share/proj/*.txt rm -fr ecmwflibs/share/proj/*.pol rm -fr ecmwflibs/share/magics/efas # echo "================================================================================" # for n in install/lib/*.dylib # do # echo $n # ./scripts/libs-macos.py $n # done # echo "================================================================================" strip -S install/lib/*.dylib ./scripts/versions.sh > ecmwflibs/versions.txt ecmwflibs-0.7.1/scripts/build-windows.sh0000775000175000017500000001025115256416132020477 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux : > versions source scripts/common.sh here=$(pwd) cd $VCPKG_INSTALLATION_ROOT url=$(git remote -v | head -1 | awk '{print $2;}') # git checkout 2021.05.12 sha1=$(git rev-parse HEAD) cd $here echo git $url $sha1 > versions # the results of the following suggested that pkg-config.exe is not installed on the latest # Windows runner #find /c/ -name pkg-config.exe #exit 1 # if [[ $WINARCH == "x64" ]]; then # PKG_CONFIG_EXECUTABLE=/c/rtools43/mingw64/bin/pkg-config.exe # else # PKG_CONFIG_EXECUTABLE=/c/rtools43/mingw32/bin/pkg-config.exe # fi vcpkg install pkgconf for p in expat netcdf-c[netcdf-4] pango sqlite3[core,tool] libpng do vcpkg install $p:$WINARCH-windows n=$(echo $p | sed 's/\[.*//') v=$(vcpkg list $n | awk '{print $2;}') echo "vcpkg $n $v" >> versions done echo ================================================================= find $VCPKG_INSTALLATION_ROOT -type f -name png.h -print echo ================================================================= pip install ninja wheel dll-diagnostics echo "pip $(pip freeze | grep dll-diagnostics | sed 's/==/ /')" >> versions # Build libaec git clone $GIT_AEC src/aec cd src/aec git checkout $AEC_VERSION cd $TOPDIR mkdir -p build-other/aec cd build-other/aec cmake \ $TOPDIR/src/aec -G"NMake Makefiles" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install \ -DCMAKE_TOOLCHAIN_FILE=/c/vcpkg/scripts/buildsystems/vcpkg.cmake \ -DCMAKE_C_COMPILER=cl.exe cd $TOPDIR cmake --build build-other/aec --target install # Build proj git clone $GIT_PROJ src/proj cd src/proj git checkout $PROJ_VERSION cd $TOPDIR mkdir -p build-other/proj cd build-other/proj cmake \ $TOPDIR/src/proj -G"NMake Makefiles" \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_TIFF=0 \ -DENABLE_CURL=0 \ -DBUILD_TESTING=0 \ -DBUILD_PROJSYNC=0 \ -DSQLITE3_BIN_PATH=C:/vcpkg/packages/sqlite3_${WINARCH}-windows/tools/sqlite3.exe \ -DBUILD_SHARED_LIBS=1 \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install \ -DCMAKE_TOOLCHAIN_FILE=/c/vcpkg/scripts/buildsystems/vcpkg.cmake \ -DCMAKE_C_COMPILER=cl.exe cd $TOPDIR cmake --build build-other/proj --target install # Build eccodes cd $TOPDIR/build-ecmwf/eccodes $TOPDIR/src/ecbuild/bin/ecbuild \ $TOPDIR/src/eccodes \ -G"NMake Makefiles" \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_PYTHON=0 \ -DENABLE_FORTRAN=0 \ -DENABLE_BUILD_TOOLS=0 \ -DENABLE_MEMFS=1 \ -DENABLE_INSTALL_ECCODES_DEFINITIONS=0 \ -DENABLE_INSTALL_ECCODES_SAMPLES=0 \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install \ -DCMAKE_TOOLCHAIN_FILE=/c/vcpkg/scripts/buildsystems/vcpkg.cmake \ -DCMAKE_C_COMPILER=cl.exe $ECCODES_EXTRA_CMAKE_OPTIONS # -DPKG_CONFIG_EXECUTABLE=$PKG_CONFIG_EXECUTABLE cd $TOPDIR cmake --build build-ecmwf/eccodes --target install # Build magics # -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON # cd $TOPDIR/build-ecmwf/magics $TOPDIR/src/ecbuild/bin/ecbuild \ $TOPDIR/src/magics \ -G"NMake Makefiles" \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_PYTHON=0 \ -DENABLE_FORTRAN=0 \ -DENABLE_BUILD_TOOLS=0 \ -Deccodes_DIR=$TOPDIR/install/lib/cmake/eccodes \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install \ -DCMAKE_TOOLCHAIN_FILE=/c/vcpkg/scripts/buildsystems/vcpkg.cmake \ -DCMAKE_C_COMPILER=cl.exe # -DPKG_CONFIG_EXECUTABLE=$PKG_CONFIG_EXECUTABLE cd $TOPDIR cmake --build build-ecmwf/magics --target install # Create wheel rm -fr dist wheelhouse ecmwflibs/share cp -r install/share ecmwflibs/ rm -fr ecmwflibs/share/magics/efas mkdir -p ecmwflibs/share/proj python tools/copy-dlls.py install/bin/MagPlus.dll ecmwflibs/ pip install -r tools/requirements.txt find ecmwflibs -name '*.dll' > libs python ./tools/copy-licences.py libs mkdir -p install/include ./scripts/versions.sh > ecmwflibs/versions.txt ecmwflibs-0.7.1/scripts/cxx-no-arch.sh0000775000175000017500000000034715256416132020044 0ustar alastairalastair#!/bin/bash set -ex skip=0 for c do shift if [[ "$c" == "-arch" ]]; then skip=1 continue fi if [[ $skip -eq 1 ]]; then skip=0 continue fi set -- "$@" "$c" done clang++ "$@" ecmwflibs-0.7.1/scripts/wheel-linux.sh0000775000175000017500000001161115256416132020152 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux # See build-linux.sh for why this is needed on a native (non-dockcross) build. CC="${CC:-}" version=$(echo $1| sed 's/\.//') pybin=$(ls -1d /opt/python/cp${version}-cp${version}*/bin/python3 2>/dev/null | head -1) if [[ -z "$pybin" ]] then pybin=$(ls -1d /opt/python/cp${version}t-cp${version}t*/bin/python3 2>/dev/null | head -1) fi if [[ -z "$pybin" ]] then echo "Cannot find Python binary for cp${version} under /opt/python" exit 1 fi TOPDIR=$(/bin/pwd) export LD_LIBRARY_PATH=$TOPDIR/install/lib:$TOPDIR/install/lib64:${LD_LIBRARY_PATH:-} # $pybin is a host-arch (x86_64) interpreter even when cross-compiling for # aarch64 -- the /opt/python pythons here are only used to run setup.py, not # to execute the target binaries -- so distutils' own platform detection # tags the wheel "linux_x86_64" regardless of what the .so's were actually # built for. Override it explicitly so auditwheel repair (which reads the # untagged wheel's platform to check it against $AUDITWHEEL_PLAT) has the # right starting tag to convert into e.g. manylinux2014_aarch64. plat_name_opt="" [[ "$CC" == aarch64* && "$(uname -m)" != aarch64* ]] && plat_name_opt="--plat-name=linux_aarch64" # The manylinux images ship their own auditwheel preinstalled under # /usr/local/bin (an internal pipx install). A bare `auditwheel` on PATH # resolves to that one, so install our own into $pybin's own site-packages # and invoke it explicitly via -m instead. # dockcross's image had setuptools/wheel preinstalled on its bootstrap # pythons; the real manylinux2014_aarch64 image's cp310 doesn't. Install # them explicitly instead of relying on what a given image happens to bundle. $pybin -m pip install --quiet --upgrade auditwheel setuptools wheel # auditwheel's --plat/$AUDITWHEEL_PLAT choices are always restricted to # policies for auditwheel's *own* auto-detected host architecture # (auditwheel.architecture.Architecture.detect(), which just reads # platform.machine(), i.e. this container's kernel/uname -- x86_64, even # though we're cross-compiling) -- so the dockcross image's baked-in # AUDITWHEEL_PLAT=manylinux*_aarch64 always gets rejected as an "invalid # choice", no matter which auditwheel is installed. Passing --plat=auto on # the command line does NOT fix this: auditwheel's argparse validates # $AUDITWHEEL_PLAT against those choices inside the *argument definition* # itself (EnvironmentDefault.__init__, invoked while building the parser), # which blows up before a single command-line argument is even looked at. # The env var itself has to be overridden. When repairing, auditwheel # separately determines the *wheel's own* architecture straight from the ELF # machine type of the .so's inside it, so plain "auto" mode still repairs the # wheel correctly for its actual (aarch64) contents. if [[ "$CC" == aarch64* && "$(uname -m)" != aarch64* ]] then export AUDITWHEEL_PLAT=auto fi # distutils names a compiled extension module after $pybin's *own* # sysconfig (EXT_SUFFIX), e.g. "_ecmwflibs.cpython-310-x86_64-linux-gnu.so" # -- even when $CC (honored via the CC/CXX env vars) is the aarch64 cross # compiler and the object code inside is genuinely aarch64. A real aarch64 # CPython's import system looks for a file matching *its own* EXT_SUFFIX # (...-aarch64-linux-gnu.so), doesn't find it, and reports the submodule as # entirely missing (ModuleNotFoundError, not an ELF-format error). The # compiled code itself is already correct for aarch64 -- only the filename # is wrong -- so build first, patch the filename in place, then package # with --skip-build so setup.py doesn't rebuild (and re-mis-name) it. build_wheel() { if [[ "$CC" == aarch64* && "$(uname -m)" != aarch64* ]] then $pybin setup.py build find build -name '*-x86_64-linux-gnu.so' -print0 | while IFS= read -r -d '' f do mv "$f" "${f/x86_64-linux-gnu/aarch64-linux-gnu}" done $pybin setup.py bdist_wheel --skip-build $plat_name_opt else $pybin setup.py bdist_wheel fi } rm -fr dist wheelhouse build_wheel # Do it twice to get the list of libraries $pybin -m auditwheel repair dist/*.whl unzip -l wheelhouse/*.whl | grep 'ecmwflibs.libs/' > libs # Bare pip3/python3 aren't on PATH in the real manylinux images (unlike # dockcross's, which exposed a distro-level default) -- use $pybin, same # as everywhere else in this script. $pybin -m pip install --quiet -r tools/requirements.txt $pybin ./tools/copy-licences.py libs rm -fr dist wheelhouse build_wheel $pybin -m auditwheel repair dist/*.whl rm -fr dist ecmwflibs-0.7.1/scripts/wheel-macos-arm.sh0000775000175000017500000000337315256416132020700 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. set -eaux arch=$(arch) [[ $arch == "i386" ]] && arch="x86_64" # GitHub Actions on macOS declare i386 ARCH="arch -$arch" diet() { if [[ $arch == "x86_64" ]]; then return fi # Remove the architectures we don't need echo ================================================================= pwd cd dist pwd name=$(ls -1 *.whl) echo $name unzip *.whl ls -l cd ecmwflibs ls -l so=$(ls -1 *.so) echo "$so" lipo -thin $arch $so -output $so.$arch mv $so.$arch $so lipo -info $so cd .. pwd zip -r $name ecmwflibs cd .. echo ================================================================= pwd ls -l dist } # version=$(echo $1| sed 's/\.//') env | sort pip3 install wheel delocate setuptools rm -fr dist wheelhouse tmp $ARCH python3 setup.py bdist_wheel diet name=$(ls -1 dist/*.whl) newname=$(echo $name | sed "s/_universal2/_${arch}/") echo $name $newname # Do it twice to get the list of libraries $ARCH delocate-wheel -w wheelhouse dist/*.whl unzip -l wheelhouse/*.whl | grep 'dylib' >libs pip3 install -r tools/requirements.txt python3 ./tools/copy-licences.py libs DISTUTILS_DEBUG=1 rm -fr dist wheelhouse $ARCH python3 setup.py bdist_wheel # --plat-name $arch diet # mv dist/$name $newname # find dist/*.dist-info -print $ARCH delocate-wheel -w wheelhouse dist/*.whl ecmwflibs-0.7.1/scripts/select-python.sh0000775000175000017500000000115315256416132020507 0ustar alastairalastair#!/bin/bash set -xe version=$1 brew install python@$1 echo /opt/homebrew/opt/python@$version/libexec/bin >> $GITHUB_PATH ls -l /opt/homebrew/opt/python@$version/libexec/bin # Looks like python3 is not a symlink to python in 3.11 if [[ ! -f /opt/homebrew/opt/python@$version/libexec/bin/python3 ]] then ln -s /opt/homebrew/opt/python@$version/libexec/bin/python /opt/homebrew/opt/python@$version/libexec/bin/python3 fi if [[ ! -f /opt/homebrew/opt/python@$version/libexec/bin/pip3 ]] then ln -s /opt/homebrew/opt/python@$version/libexec/bin/pip /opt/homebrew/opt/python@$version/libexec/bin/pip3 fi ls -l ecmwflibs-0.7.1/scripts/build-linux.sh0000775000175000017500000006032115256416132020147 0ustar alastairalastair#!/usr/bin/env bash # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. # (rm -fr build-other/netcdf/; cd src/netcdf/; git checkout -- .; git clean -f .) set -eaux # dockcross's cross-compiling images always export CC (to the cross # compiler); the real, native manylinux images don't export it at all, and # under set -u every "$CC" == aarch64* cross-compile check below would # otherwise abort with "CC: unbound variable" on a native build. CC="${CC:-}" : > versions # CI caching restores src/install/build-other via actions/cache, which runs # on the *host* as the unprivileged runner user -- so cached files end up # owned by that user, not root. This container runs as root, and git # refuses to operate inside a repo whose owning UID doesn't match the # current user ("dubious ownership", CVE-2022-24765 mitigation) -- which # every git checkout under src/ hits as soon as a cache actually restores. git config --global --add safe.directory '*' SUDO="" if [[ $(id -u) -ne 0 ]] then SUDO="sudo" fi PKG_MGR="" if command -v yum >/dev/null 2>&1 then PKG_MGR="yum" elif command -v dnf >/dev/null 2>&1 then PKG_MGR="dnf" else echo "Neither yum nor dnf found" exit 1 fi PKG_MGR_INSTALL_OPTS=(-y) if [[ "$PKG_MGR" == "dnf" ]] then repo_files=(/etc/yum.repos.d/*.repo) if grep -Eq '/8\.[0-9]+/' "${repo_files[@]}" 2>/dev/null then $SUDO sed -i -E 's#/8\.[0-9]+/#/8/#g' "${repo_files[@]}" || true fi rhel_major=$(rpm -E '%{rhel}' 2>/dev/null || true) if [[ -n "$rhel_major" ]] then PKG_MGR_INSTALL_OPTS+=(--releasever="$rhel_major") fi PKG_MGR_INSTALL_OPTS+=(--refresh) $SUDO dnf clean all || true fi pkg_install() { local pkg="$1" local max_attempts=3 local attempt=1 while (( attempt <= max_attempts )) do if $SUDO $PKG_MGR install "${PKG_MGR_INSTALL_OPTS[@]}" "$pkg" then return 0 fi if (( attempt == max_attempts )) then return 1 fi sleep $(( attempt * 5 )) attempt=$(( attempt + 1 )) done } # We want the sqlite3 we just compiled PATH=$(pwd)/install/bin:$PATH # The version in master does not compile with disabled curl support NETCDF_VERSION=v4.6.0 source scripts/common.sh for p in libpng-devel libtiff-devel fontconfig-devel gobject-introspection-devel expat-devel cairo-devel do pkg_install "$p" # There may be a better way $SUDO $PKG_MGR install "${PKG_MGR_INSTALL_OPTS[@]}" "$p" 2>&1 > tmp cat tmp v=$(grep 'already installed' < tmp | awk '{print $2;}' | sed 's/\\d://') echo "yum $p $v" >> versions done pkg_install flex pkg_install bison # For lddtree (a diagnostic-only call below, its output isn't used by the # build) -- unlike dockcross's image, the real manylinux2014_aarch64 image # doesn't have this package in its default repos, so don't let its absence # abort the whole build. pkg_install pax-utils || echo "warning: pax-utils unavailable, skipping lddtree diagnostics" bootstrap_python=$(ls -1d /opt/python/cp3*/bin/python3 | head -1) bootstrap_pip=$(dirname "$bootstrap_python")/pip3 bootstrap_python_config=$(dirname "$bootstrap_python")/python3-config $SUDO ln -sf "$bootstrap_python" /usr/local/bin/python3 $SUDO ln -sf "$bootstrap_python_config" /usr/local/bin/python3-config $SUDO ln -sf "$bootstrap_pip" /usr/local/bin/pip3 $SUDO pip3 install ninja auditwheel meson $SUDO ln -sf $(dirname "$bootstrap_python")/meson /usr/local/bin/meson $SUDO ln -sf $(dirname "$bootstrap_python")/ninja /usr/local/bin/ninja if [[ "$CC" == aarch64* && "$(uname -m)" != aarch64* ]] then # Cross-compiling: keep pkg-config scoped to our own from-source # install prefix only. The host's system pkgconfig dirs (/usr/lib64, # /usr/lib) only contain host-arch (x86_64) .pc files -- notably # Xft/Xrender/X11, pulled in transitively by cairo-devel -- which # pango auto-detects (dependency('xft', required: false), no meson # option to turn it off) and then fails to link against on the # aarch64 target ("File in wrong format"). We don't need X11 support # in the wheel, so just don't let pkg-config see those .pc files. # PKG_CONFIG_PATH alone doesn't do this -- pkg-config always additionally # searches its compiled-in default directories (which is exactly # /usr/lib64/pkgconfig etc.) unless PKG_CONFIG_LIBDIR (which *replaces* # rather than prepends to that default list) is also set. export PKG_CONFIG_PATH=$TOPDIR/install/lib/pkgconfig:$TOPDIR/install/lib64/pkgconfig:$TOPDIR/install/share/pkgconfig export PKG_CONFIG_LIBDIR=$TOPDIR/install/lib/pkgconfig:$TOPDIR/install/lib64/pkgconfig:$TOPDIR/install/share/pkgconfig else export PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH:-} export PKG_CONFIG_PATH=$TOPDIR/install/lib/pkgconfig:$TOPDIR/install/lib64/pkgconfig:$TOPDIR/install/share/pkgconfig:$PKG_CONFIG_PATH fi export LD_LIBRARY_PATH=$TOPDIR/install/lib:$TOPDIR/install/lib64:${LD_LIBRARY_PATH:-} # meson/autotools only add -L/-rpath-link for a target's *direct* # dependencies. glib's own test/tool binaries (gtester, gobject-query) and # harfbuzz's hb-info only declare a direct dependency on libglib/libgobject # themselves, not on the from-source libs those pull in transitively # (pcre2, libffi) -- so even though e.g. libpcre2-8.so.0 installs correctly # into $TOPDIR/install/lib, the linker has no search path telling it where # to find it and fails with "not found" / undefined reference. Make every # subsequent build's linker invocations aware of our install prefix. export LDFLAGS="-L$TOPDIR/install/lib -L$TOPDIR/install/lib64 -Wl,-rpath-link,$TOPDIR/install/lib -Wl,-rpath-link,$TOPDIR/install/lib64 ${LDFLAGS:-}" # Build sqlite [[ -d src/sqlite ]] || git clone --depth 1 $GIT_SQLITE src/sqlite cd src/sqlite ./configure \ --disable-tcl \ --prefix=$TOPDIR/install cd $TOPDIR make -C src/sqlite install # Build proj [[ -d src/proj ]] || git clone $GIT_PROJ src/proj cd src/proj git checkout $PROJ_VERSION cd $TOPDIR mkdir -p build-other/proj cd build-other/proj # PROJ needs to run sqlite3 at build time to generate proj.db, but our # target sqlite3 CLI can't execute on the build host when cross-compiling, # and the distro's own sqlite3 links against a mismatched system library. # Build a native, statically linked one just for this. [[ -d $TOPDIR/build-other/native-sqlite-src ]] || git clone --depth 1 $GIT_SQLITE $TOPDIR/build-other/native-sqlite-src ( cd $TOPDIR/build-other/native-sqlite-src CC=/usr/bin/gcc ./configure \ --disable-tcl \ --disable-shared \ --prefix=$TOPDIR/build-other/native-sqlite-install make install ) native_sqlite3=$TOPDIR/build-other/native-sqlite-install/bin/sqlite3 cmake \ $TOPDIR/src/proj -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_TIFF=0 \ -DENABLE_CURL=0 \ -DBUILD_TESTING=0 \ -DBUILD_PROJSYNC=0 \ -DBUILD_SHARED_LIBS=1 \ -DCMAKE_PREFIX_PATH=$TOPDIR/install \ -DEXE_SQLITE3=$native_sqlite3 \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install cd $TOPDIR cmake --build build-other/proj --target install # Build libaec (required by ecCodes for CCSDS/AEC compression) [[ -d src/aec ]] || git clone $GIT_AEC src/aec cd src/aec git checkout $AEC_VERSION cd $TOPDIR mkdir -p build-other/aec cd build-other/aec cmake \ $TOPDIR/src/aec -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_SHARED_LIBS=1 \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install cd $TOPDIR cmake --build build-other/aec --target install libaec_config_file=$(find "$TOPDIR/install" -maxdepth 6 \( -name 'libaec-config.cmake' -o -name 'libaecConfig.cmake' \) -print | head -1 || true) libaec_cmake_dir="" if [[ -n "$libaec_config_file" ]] then libaec_cmake_dir=$(dirname "$libaec_config_file") fi if [[ -z "$libaec_cmake_dir" ]] then echo "Could not find libaec CMake package under $TOPDIR/install" find "$TOPDIR/install" -maxdepth 5 \( -name 'libaec-config.cmake' -o -name 'libaecConfig.cmake' \) -print || true exit 1 fi # Build jasper (provides libjasper.so, required by ecCodes) [[ -d src/jasper ]] || git clone $GIT_JASPER src/jasper cd src/jasper git checkout $JASPER_VERSION cd $TOPDIR mkdir -p build-other/jasper cd build-other/jasper cmake \ $TOPDIR/src/jasper -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_SHARED_LIBS=1 \ -DJAS_ENABLE_OPENGL=OFF \ -DJAS_ENABLE_DOC=OFF \ -DJAS_ENABLE_PROGRAMS=OFF \ -DCMAKE_PREFIX_PATH=$TOPDIR/install \ -DJAS_STDC_VERSION=201710L \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install cd $TOPDIR cmake --build build-other/jasper --target install # Build libjpeg-turbo (provides libjpeg.so.62, required by ecCodes JPEG support) [[ -d src/jpeg ]] || git clone $GIT_JPEG src/jpeg cd src/jpeg git checkout $JPEG_VERSION cd $TOPDIR mkdir -p build-other/jpeg cd build-other/jpeg cmake \ $TOPDIR/src/jpeg -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_SHARED_LIBS=1 \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install cd $TOPDIR cmake --build build-other/jpeg --target install # Build zlib (system zlib-devel is host-arch only, no use when cross-compiling) [[ -d src/zlib ]] || git clone --depth 1 --branch $ZLIB_VERSION $GIT_ZLIB src/zlib mkdir -p build-other/zlib cd build-other/zlib cmake \ $TOPDIR/src/zlib -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_SHARED_LIBS=1 \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install cd $TOPDIR cmake --build build-other/zlib --target install # Without a cross-file, meson doesn't know it's cross-compiling and its # sanity check tries to run the freshly built (target-arch) test binary. meson_cross_file="" if [[ "$CC" == aarch64* && "$(uname -m)" != aarch64* ]] then meson_cross_file=$TOPDIR/build-other/meson-cross.ini cat > "$meson_cross_file" < src/pango/meson.build.patched cp src/pango/meson.build.patched src/pango/meson.build sed 's/.*G_LOG_USE_STRUCTURED.*//' < src/pango/pango/meson.build > src/pango/pango/meson.build.patched cp src/pango/pango/meson.build.patched src/pango/pango/meson.build mkdir -p build-other/pango cd src/pango # gobject-introspection's g-ir-scanner needs to run target-arch binaries it # just built, which isn't possible when cross-compiling (no qemu here) -- # and PKG_CONFIG_LIBDIR is scoped away from the host's gobject-introspection-1.0 # on the aarch64 cross build anyway (see above), so pango can't find it either. pango_gir_opt="" [[ "$CC" == aarch64* && "$(uname -m)" != aarch64* ]] && pango_gir_opt="-Dgir=false" meson_setup --prefix=$TOPDIR/install \ -Dwrap_mode=nofallback \ $meson_cross_opt \ $pango_gir_opt \ $TOPDIR/build-other/pango cd $TOPDIR ninja -C build-other/pango install # Build eccodes cd $TOPDIR/build-ecmwf/eccodes # ecbuild's ENABLE_TESTS defaults to ON, so without -DENABLE_TESTS=0 the # "install" target also pulls in eccodes' own ~150 test executables (none of # which ecmwflibs needs -- only libeccodes.so itself is used). Building them # is pure overhead, and on manylinux_2_28 (aarch64) one of them failed to # link ("undefined reference to grib_ieee_decode_array" and related # IEEE-encoding symbols), failing the whole job even though libeccodes.so # itself built fine. $TOPDIR/src/ecbuild/bin/ecbuild \ $TOPDIR/src/eccodes \ -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_PYTHON=0 \ -DENABLE_FORTRAN=0 \ -DENABLE_BUILD_TOOLS=0 \ -DENABLE_MEMFS=1 \ -DENABLE_INSTALL_ECCODES_DEFINITIONS=0 \ -DENABLE_INSTALL_ECCODES_SAMPLES=0 \ -DENABLE_TESTS=0 \ -DCMAKE_PREFIX_PATH="$TOPDIR/install;$TOPDIR/install/lib/cmake;$TOPDIR/install/lib64/cmake" \ -Dlibaec_DIR="$libaec_cmake_dir" \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install $ECCODES_EXTRA_CMAKE_OPTIONS cd $TOPDIR cmake --build build-ecmwf/eccodes --target install # Build magics cd $TOPDIR/build-ecmwf/magics # Unlike every other CMake-based dependency build in this script, this call # was missing -DCMAKE_PREFIX_PATH. On a native (x86_64) build CMake's # find_library/find_path still turn up our from-source libs some other way, # but when cross-compiling, CMAKE_FIND_ROOT_PATH restricts those commands to # the toolchain sysroot unless CMAKE_PREFIX_PATH explicitly adds our from- # source install prefix -- so e.g. FindPROJ.cmake's plain find_library/ # find_path fallback (after its pkg-config lookup, which itself fails because # it searches for a "PROJ" module but our from-source proj.pc is lowercase) # comes up empty on aarch64. $TOPDIR/src/ecbuild/bin/ecbuild \ $TOPDIR/src/magics \ -GNinja \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DENABLE_PYTHON=0 \ -DENABLE_FORTRAN=0 \ -DENABLE_BUILD_TOOLS=0 \ -Deccodes_DIR=$TOPDIR/install/lib/cmake/eccodes \ -DCMAKE_PREFIX_PATH=$TOPDIR/install \ -DCMAKE_INSTALL_PREFIX=$TOPDIR/install cd $TOPDIR cmake --build build-ecmwf/magics --target install # Create wheel command -v lddtree >/dev/null 2>&1 && lddtree install/lib*/libMagPlus.so || echo "lddtree not available, skipping" rm -fr dist wheelhouse ecmwflibs/share cp -r install/share ecmwflibs/ rm -fr ecmwflibs/share/magics/efas # On the aarch64 cross build, GNUInstallDirs' lib64-vs-lib heuristic # resolves to plain "lib" (it can't detect a lib64-using system while cross- # compiling), so install/lib64 never gets created -- nothing to consolidate. # # Found the actual root cause after chasing this crash through several # disguises (segfault on bulk `cp *.so`, segfault on a single-file `cp`, then # "file too short" on a single-file `cat`): LD_LIBRARY_PATH (exported above) # puts install/lib first in the dynamic loader's search path. On # manylinux_2_28 (x86_64), the system's own cp/cat/strip (a combined # `coreutils` binary) is itself dynamically linked against libpcre2-8.so.0 -- # a library we *also* build from source, with the same SONAME. While this # loop is busy writing our freshly-copied libpcre2-8.so.0 into install/lib, # any other process that needs that name resolves it via LD_LIBRARY_PATH to # our own copy -- mid-write and truncated -- instead of the system's, # producing "file too short" or a segfault depending on exact timing. Clear # LD_LIBRARY_PATH for these plain file operations so they use the system's # own libraries instead of racing ours. if compgen -G "install/lib64/*.so" > /dev/null then LD_LIBRARY_PATH= cp install/lib64/*.so install/lib/ fi # On the aarch64 cross build, plain `strip` is the host's native x86_64 # binary and can't parse aarch64 ELF files ("Unable to recognise the format # of the input file"), so every .so silently keeps its debug info -- same # cross-toolchain-prefix derivation as the --host= triple below. strip_bin=strip [[ "$CC" == aarch64* && "$(uname -m)" != aarch64* ]] && strip_bin="${CC%-gcc}-strip" for f in install/lib/*.so do LD_LIBRARY_PATH= "$strip_bin" --strip-debug "$f" || echo "warning: strip failed on $f, leaving it unstripped" done ./scripts/versions.sh > ecmwflibs/versions.txt ecmwflibs-0.7.1/ecmwflibs.code-workspace0000664000175000017500000000032015256416132020461 0ustar alastairalastair{ "folders": [ { "path": "." }, { "path": "../magics-python" }, { "path": "../cfgrib" } ], "settings": { "files.associations": { "*.make": "makefile", "vector": "cpp" } } } ecmwflibs-0.7.1/tests/0000775000175000017500000000000015256416132015025 5ustar alastairalastairecmwflibs-0.7.1/tests/test_climetlab.py0000664000175000017500000001247315256416132020401 0ustar alastairalastair#!/usr/bin/env python3 import pytest from Magics import macro def test_climetlab_grib(): actions = [ macro.output( output_formats=["png"], output_name="climetlab_grib_1", output_name_first_page_number=False, output_width=680, page_frame=False, page_id_line=False, page_x_length=10.0, page_y_length=5.555555555555555, subpage_x_length=10.0, subpage_x_position=0.0, subpage_y_length=5.555555555555555, subpage_y_position=0.0, super_page_x_length=10.0, super_page_y_length=5.555555555555555, ), macro.mmap( subpage_lower_left_latitude=33.0, subpage_lower_left_longitude=-27.0, subpage_map_projection="cylindrical", subpage_upper_right_latitude=73.0, subpage_upper_right_longitude=45.0, ), macro.mcoast( map_boundaries=True, map_coastline_colour="tan", map_coastline_land_shade=True, map_coastline_land_shade_colour="cream", map_grid=False, map_grid_colour="tan", map_grid_frame=True, map_grid_frame_thickness=5, map_label=False, ), macro.mgrib( grib_field_position=0, grib_file_address_mode="byte_offset", grib_input_file_name="climetlab.grib", ), macro.mcont( contour_automatic_setting="climetlab", legend=False, ), macro.mcoast( map_grid=False, map_grid_frame=True, map_grid_frame_thickness=5, map_label=False, ), ] print(actions) macro.plot(*actions) actions = [ macro.output( output_formats=["png"], output_name="climetlab_grib_2", output_name_first_page_number=False, output_width=680, page_frame=False, page_id_line=False, page_x_length=10.0, page_y_length=5.555555555555555, subpage_x_length=10.0, subpage_x_position=0.0, subpage_y_length=5.555555555555555, subpage_y_position=0.0, super_page_x_length=10.0, super_page_y_length=5.555555555555555, ), macro.mmap( subpage_lower_left_latitude=33.0, subpage_lower_left_longitude=-27.0, subpage_map_projection="cylindrical", subpage_upper_right_latitude=73.0, subpage_upper_right_longitude=45.0, ), macro.mcoast( map_boundaries=True, map_coastline_colour="tan", map_coastline_land_shade=True, map_coastline_land_shade_colour="cream", map_grid=False, map_grid_colour="tan", map_grid_frame=True, map_grid_frame_thickness=5, map_label=False, ), macro.mgrib( grib_field_position=526, grib_file_address_mode="byte_offset", grib_input_file_name="climetlab.grib", ), macro.mcont( contour_automatic_setting="climetlab", legend=False, ), macro.mcoast( map_grid=False, map_grid_frame=True, map_grid_frame_thickness=5, map_label=False, ), ] print(actions) macro.plot(*actions) @pytest.mark.parametrize("filename", ["climetlab.nc", "test.nc4"]) def test_climetlab_netcdf(filename): from Magics import macro output = filename.replace(".", "-") + ".png" actions = [ macro.output( output_file=output, output_width=680, page_frame=False, page_id_line=False, page_x_length=10, page_y_length=5.555555555555555, subpage_frame=False, subpage_x_length=10, subpage_x_position=0.0, subpage_y_length=5.555555555555555, subpage_y_position=0.0, super_page_frame=False, super_page_x_length=10, super_page_y_length=5.555555555555555, ), macro.mmap( subpage_lower_left_latitude=33.0, subpage_lower_left_longitude=-27.0, subpage_map_projection="cylindrical", subpage_upper_right_latitude=73.0, subpage_upper_right_longitude=45.0, ), macro.mcoast( map_boundaries=True, map_coastline_colour="tan", map_coastline_land_shade=True, map_coastline_land_shade_colour="cream", map_grid=False, map_grid_colour="tan", map_grid_frame=True, map_grid_frame_thickness=5, map_label=False, ), macro.mnetcdf( netcdf_filename=filename, netcdf_value_variable="t2m", ), macro.mcont( contour_automatic_setting="climetlab", legend=False, ), macro.mcoast( map_grid=False, map_grid_frame=True, map_grid_frame_thickness=5, map_label=False, ), ] print(actions) macro.plot(*actions) if __name__ == "__main__": test_climetlab_netcdf() # test_climetlab_grib() ecmwflibs-0.7.1/tests/open_netcdf.c0000664000175000017500000000065715256416132017465 0ustar alastairalastair #include #include #include int main(int argc, const char* argv[]) { int ncid, e; if (argc != 2) { fprintf(stderr, "Usage: %s file.nc\n", argv[0]); exit(1); } if ((e = nc_open(argv[1], NC_NOWRITE, &ncid))) { fprintf(stderr,"%s: %s\n", argv[1], nc_strerror(e)); exit(1); } printf("%s: file opened successfully\n", argv[1]); exit(0); } ecmwflibs-0.7.1/tests/test.nc40000664000175000017500000006266715256416132016433 0ustar alastairalastair‰HDF  ÿÿÿÿÿÿÿÿ·e0kYósOHDR º" %_ÆOìO L : GRIB_edition  % GRIB_centreecmf ^GRIB_centreDescription2European Centre for Medium-Range Weather Forecasts < GRIB_subCentre  ' ConventionsCF-1.7 S institution2European Centre for Medium-Range Weather Forecasts Ëhistory®GRIB to CDM+CF via cfgrib-0.9.5/ecCodes-2.17.0 with source='/Users/baudouin/git/climet/test.grib', filter_by_keys={}, encode_cf=('parameter', 'time', 'geography', 'vertical')ical')¥§VÝOHDR  ?@4 4ÿ*øt’ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ +CLASSDIMENSION_SCALE $NAME longitudeN ‘ D—OCHKjaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ?KOCHK + _Netcdf4Dimid  D _FillValue ?@4 4ÿø 'units degrees_east ,standard_name longitude ( long_name longitude=´5OHDR   ?@4 4ÿ*ø P:®P±XxG>‡OCHKt=@JŒþgFSSEt;ûúázÒX FSSE9 ö/íf¬ 11¹â‡DOCHK E_NCProperties"version=2,netcdf=4.8.0,hdf5=1.10.6.8.0,hdf5=1.10.6.10.6¹ÃÜ;À7À3À.À&ÀÀÀð?@"@*@1@5@9@=@€@@€B@€D@€F@àcƒC@¹…C`N†CÀ-†C`:†C@E†CˆC`d‡C€ˆCÀsˆCà‡ˆC@‰C]‰Cå‰C`!ŠCÀ ŠC a‰C@€‰C€g‰C@‡CÀ�‡C@ƇCÒ‡C ‘‡C ƇC@ú‡C(ˆC`QˆCшCàk‰C‡‰C€Ï‡C`#‰Càý‹Cà ŒCà&‰C`‰CÀO‰CÀë‹C yŒC`AˆCàHŠCÀO‰C€q‰CÀŒ‰CउC€Û‰C «‰Cà$ˆC`ìŠC ÙŒCàk‹C`{‹C€Ø‹C`ŠˆC K‰C ³‘C ˆŒC wŒCÀjŒCÀ/ŒCàAŒCÀb‹Cà‹C(ŠC rŠC€0ŠCÒŠC`�ŒC@ÞŠC€&ŒCD�CO�CÀƉCÀ2‰C€×ŽC �C ÒŒC —ŒC �CÀ,ŒC`±‹C "ŠC z‹C Æ‹CàÚŒC@Š�C/�Càˆ‹C �C€‰ŒC@L�Cï�C`ïŒCÀ ŒCàÔ�CÞ�Càµ�C }�C�CÀ†�C1�C WŒC·ŒC@X�CÀÏŽC :ŽC 'ŽC€jŽC mŽC mŽC¬ŽC€��Cà ŒC +ŽC€€ŽC`‡ŽC [ŽC@.ŽC@ŽC` ŽC€&�Càw�C9�C€òŒC€ŽC@ô�CÀþ�CÀ™�C‚ŽCp�C oŽC@БC ‚ŽC ýŽC`�C@�C€M�Cà�C€B�C �CÀÁ�C ç�C F‘C=”CÀ)•Cæ�C µ�C@UŽC ¶ŽC…”C ƒ•C %�CÀ†�C€?�C€û�C �C _’C€M�C@<“Cô‘C€”C`Ÿ’CÀP–Cà“C`”C,’C`ê’C 5“C`õ‘C`ñ�C ²�C`r�C ˜�C ñ�C€a�C`b‘C ¿�Cà„‘C *’C`x’C`§’C u’C€`’Cø’C€j–C Ý–C@-—C€Š˜CÀ’C¸‘C`ß‘C`…‘C@€‘Cà–�CבCÀ‘Co•C ešCàº�CÜ”CàÊ’C€“C “Càb“Có’CûšCà6šC ÏšCèÇGØøÅG¸½ÅG@[ÅGx‚ÄG¨OÃG8EÂGð�ÁG :ÁG8oÁG¨ûÁG�ÂG°$ÃG¨¸ÃGøYÄGPÓÄGTÅG¨§ÅG ¿ÅG (ÆGÈ�ÆG}ÆG}ÆGH>ÆGÐrÅG�¯ÄGp'ÄG0ÉÃGð‘ÃG8ºÃG˜ùÃG0)ÄGèNÄG€AÄG(ˆÄG�ÒÄGH"ÄG ˆÃG  ÇGÈ ÇGxÇG`ÄÆG¨èÆG–ÆGà ÆGØ�ÅG(@ÅG8 ÅGˆÅG„ÄGÀ5ÄGXVÄGÄG`ÄGàIÃGè¿ÁG8ÁG�[ÈGpfÈGhÈGè�ÇG1ÇGà<ÇGp"ÇG€æÆG`ÆGˆ¿ÅG8PÅG@ÂÄG 'ÄGˆðÃGx-ÄGXEÄG8ÄGàÖÂGŸÁG¸}ÈGØÊÈG¸ÃÈG°qÈGÀ[ÈG@!ÈG ¿ÇG@HÇG`¢ÆGÐÆGȈÅGÅGȼÄGÈ¥ÄGh ÅG=ÅG(ÅG°©ÄGíÃG¼ÇG8ÈGè/ÈGxÈG€·ÇG(oÇG�@ÇG° ÇG\ÆGHßÅG�ÓÅGàÐÅG`ÖÅG˜ùÅG˜RÆG°oÆGø ÆG ±ÅG`ùÄG¨âÆGâÆGxìÆG(ÎÆG¸˜ÆGà`ÆGh ÆGð±ÅGøCÅG(hÅGè—ÅGÀÆG˜’ÆGp‰ÆGðÿÆG°[ÇG8»ÆGðÆGpÜÄG@öÆGð®ÅG0{ÅG(ÅGpæÄGÈöÄG`¶ÄG?ÄGˆ?ÄG=ÅGð ÅG ¸ÅGðPÆGøqÇG¨ÈÇG@�ÇGp<ÇGÆG[ÅG€SÇG@ÆGpòÄGh�ÃG(¹ÃGÈ ÄGx‡ÄG -ÄGÀ]ÄGhîÄG”ÅG€ÆG8ïÆGòÆGÈAÇG°ÇGÀFÇG�[ÇG`uÆG`ÕÇG0ÊÆGø¹ÅGˆ­ÄG°YÄGh”ÄG¨åÄGhÅGÈ)ÅG¨‚ÅGØåÅG@¬ÆGˆÚÆGx¦ÆG•ÆGØUÆG0êÅG¸…ÅGX9ÆG€7ÈG°‹ÇGÈÎÆG`ÆGX�ÅGˆÅG°âÄG˜†ÄG]ÄG0‰ÄG@LÅGðuÆG ªÆGˆ“ÆGÈ…ÆG¸^ÆG·ÅGp³ÅGð&ÅG@R@@Q@@P@€N@€L@€J@€H@€F@€D@€B@€@@@á»^Pø®×AOHDR $     *À)D!Så l§Ø.OCHK Bunits'seconds since 1970-01-01T00:00:00+00:00 1 calendarproleptic_gregoriank¯Afsurface ] À0†R ‚OCHK‘˜�9ŠVyÃÑ/FSSE1ÿ&ÚŽrÊ[´FSSEÔPùjÀ¯wFRHPÿÿÿÿÿÿÿÿŸ1 !(ƒ7Gd³uBTHDd(ƒ!¬#�BTHD  d(ƒ!!.{¤ÖFSHDµPx(//h°ñBTLFª[~o´aü6u›:½2¡ Í2L¨ º< [@ôŠ$MKK.€/X- ç©Nu5<ƒQ -‹LfUö*§¡gð_ðA7u[à6¼yN' g�¾:RBTLF E[@›:Õ&û!2N'u%š?ÙA > X- …5 º< ö* -MK˜5[qC´a`u5ª[HMC�`ð_Í2OkD\¯+ ¹¼bBTINMC[í�ƒƒäjëBTLF¯+ e >“`lý"œ˜5 r ÙA [¢�`Ãæ¦2žæ÷©Oiž±qCFŒh³š?1¾¸kDË_}É…5 ,k¹ËHƒ#÷ÑÕ&É©×\c“‘éEcÍÐéû!” }ìu%ùL„ú> ÖoüÏlM˜GCOL^^ö^^ööö ^ ö ^ öÐFHDBS_":íDIMENSION_LISTƒ ƒ FHDBS†Ù7ø"GRIB_iDirectionIncrementInDegrees ?@4 4ÿ@ GRIB_iScansNegatively (GRIB_longitudeOfFirstGridPointInDegrees ?@4 4ÿ;À'GRIB_longitudeOfLastGridPointInDegrees ?@4 4ÿ€F@ GRIB_Ny  "GRIB_jDirectionIncrementInDegrees ?@4 4ÿ@ GRIB_jPointsAreConsecutive  GRIB_jScansPositively 'GRIB_latitudeOfFirstGridPointInDegrees ?@4 4ÿ@R@&GRIB_latitudeOfLastGridPointInDegrees ?@4 4ÿ€@@unitsK coordinates#number surface time step valid_time _Netcdf4Dimid FHDBSÅ´nù _Netcdf4Coordinates  _FillValue  À GRIB_paramId §GRIB_shortName2t GRIB_unitsK GRIB_name2 metre temperatureGRIB_cfVarNamet2mGRIB_dataTypean GRIB_missingValue ' GRIB_numberOfPoints Ñ GRIB_totalNumber GRIB_typeOfLevelsurface GRIB_NV  GRIB_stepUnits GRIB_stepTypeinstantGRIB_gridType regular_llGRIB_gridDefinitionDescriptionLatitude/Longitude Grid GRIB_Nx  long_name2 metre temperatureFHIBSƒ3ƒ/ƒ+ÿÿÿÿÿÿÿÿ/à?OHDR $     *Àm D#–:(;N;l>Ð8mOCHKmsl¹7msl¹7’‹îûFSHD Px ú_7€EOCHK # long_nametimeÖDÆÊÂ% OCHK |0REFERENCE_LIST6datasetdimension ‘¹7”;ÑBTHDd(&Q :Ÿ²ê²æôbFHIB–:ÆKÆGÆCÿÿÿÿÿÿÿÿF£3FRHPÿÿÿÿÿÿÿÿt; #(H:¶Û²BTHDd(Æ?#V3”´BTHD  d(Æ=##‡#~üFSHD*Px(Ë//xœBTLFß[~oéaü6u›:½2¡ „6L¨ ÿ<[@ôŠ$’KK.€/�- ç©Nª5<ƒQºAÈx/Re-‹LfUT?-Gœ^;*§¡g%_ðA7uK[à6¼y“' g�ófцBTLF E[@›:Õ'ü"6T?“'º%ß? A _> �- Ê5 ÿ<;*e-’K5K[¦CéaJ`ª5ß[:H‚CÅ`%_„6ݺAD Z\!¶+"äçm BTIN‚C[í�Æ;ÆA8tgLBTLF¶+"e >“J`lý"œ5 r A [¢Å`Ãæ¦6žæ÷©Ýiž±¦CFŒh³ß? 1¾¸D Ë_}ÉÊ5 ,k¹Ë:Hƒ#÷ÑÕ'É©×Z\!c“‘éEcÍÐéü"” }ìº%ùL„ú_> Öoü×ð¨FHDB–:ä^ coordinates#number surface time step valid_timeDIMENSION_LISTƒ ƒ  _Netcdf4Dimid FHDB–:óñ¢ã GRIB_Nx "GRIB_iDirectionIncrementInDegrees ?@4 4ÿ@ GRIB_iScansNegatively (GRIB_longitudeOfFirstGridPointInDegrees ?@4 4ÿ;À'GRIB_longitudeOfLastGridPointInDegrees ?@4 4ÿ€F@ GRIB_Ny  "GRIB_jDirectionIncrementInDegrees ?@4 4ÿ@ GRIB_jPointsAreConsecutive  GRIB_jScansPositively 'GRIB_latitudeOfFirstGridPointInDegrees ?@4 4ÿ@R@&GRIB_latitudeOfLastGridPointInDegrees ?@4 4ÿ€@@ long_nameMean sea level pressurestandard_nameair_pressure_at_mean_sea_levelFHDB–:µ©Ï _Netcdf4Coordinates  _FillValue  À GRIB_paramId —GRIB_shortNamemsl GRIB_unitsPa GRIB_nameMean sea level pressure GRIB_cfNameair_pressure_at_mean_sea_levelGRIB_cfVarNamemslGRIB_dataTypean GRIB_missingValue ' GRIB_numberOfPoints Ñ GRIB_totalNumber GRIB_typeOfLevelsurface GRIB_NV  GRIB_stepUnits GRIB_stepTypeinstantGRIB_gridType regular_llGRIB_gridDefinitionDescriptionLatitude/Longitude GridunitsPaBTHD d(·_ &öh3BTHDd(·a ç–€FRHPÿÿÿÿÿÿÿÿÔP (&U¡Þ\BTHD  d(&S ´:<ýFSHDPx(5 —›üBTLF&'L¨ �DôŠ$+Äç–<û+Èx/RA#_‹�Šd+e >“Ó(iž±}| ôí»M0$0xýƒ§ BTLF +A#d+�DÓ(û+&'M0}| _FúIFHDBP–“}?CLASSDIMENSION_SCALENAME latitude _Netcdf4Dimid  _FillValue ?@4 4ÿøunits degrees_northstandard_namelatitude long_namelatitudestored_direction decreasing0REFERENCE_LIST6datasetdimension ‘¹7OHDR    *€ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ; long_nameensemble member numerical id units1 .standard_name realizationˆøoÓOHDR b   *€ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 7 long_nameinitial time of forecast :standard_nameforecast_reference_time Bunits'seconds since 1970-01-01T00:00:00+00:00 1 calendarproleptic_gregorian·�_OHDR a ?@4 4ÿ*øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ D _FillValue ?@4 4ÿø A long_name"time since forecast_reference_time 2standard_nameforecast_period unitshours¬X7POHDR    *€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ O long_name0original GRIB coordinate for key: level(surface) units1q¤•æOHDR  ?@4 4ÿ*ø�Ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ D _FillValue ?@4 4ÿø 'standard_nametimeW91® CFRHPÿÿÿÿÿÿÿÿ 9  ·cWQfmBTLF8?èxWï¤LžøB‘¥àÛXœ^P^1t¾zgÙeÈêËbdDKÒ¿axú¨j`¯7BTLFLb1x‘¨¿ Ù) Ï€FHDB%_¤.oà longitudeölatitude^t2m‘msl¹7number&Ytime2Zstep [surface ] valid_time^ecmwflibs-0.7.1/tests/CMakeLists.txt0000664000175000017500000000051315256416132017564 0ustar alastairalastairproject( open_netcdf LANGUAGES C ) cmake_minimum_required(VERSION 3.21) find_path (NETCDF_INCLUDES_C netcdf.h HINTS NETCDF_DIR NETCDF_DIR) include_directories(${NETCDF_INCLUDES_C}) find_library (NETCDF_LIBRARIES_C NAMES netcdf) add_executable(open_netcdf open_netcdf.c) target_link_libraries(open_netcdf ${NETCDF_LIBRARIES_C}) ecmwflibs-0.7.1/tests/test_eccodes.py0000664000175000017500000000053415256416132020045 0ustar alastairalastairfrom eccodes import codes_get, codes_grib_new_from_file, codes_release def test_versions(): with open("data.grib", "rb") as f: grib = codes_grib_new_from_file(f) date = codes_get(grib, "date") assert date == 20130325, date print(date) print(codes_get(grib, "shortName")) codes_release(grib) ecmwflibs-0.7.1/tests/requirements.txt0000664000175000017500000000002615256416132020307 0ustar alastairalastairmagics eccodes pytest ecmwflibs-0.7.1/tests/test_magics.py0000664000175000017500000000133715256416132017705 0ustar alastairalastair#!/usr/bin/env python3 from Magics import macro as magics def test_magics_plot(): # Setting of the output file name output = magics.output( output_formats=["png"], output_name_first_page_number="off", output_name="magics1", ) # Import the data data = magics.mgrib( grib_input_file_name="data.grib", ) # proj = magics.mmap(subpage_map_projection="mollweide") # proj = magics.mmap(subpage_map_projection="cylindrical") proj = magics.mmap(subpage_map_projection="robinson") # Apply an automatic styling contour = magics.mcont( contour_automatic_setting="ecmwf", ) coast = magics.mcoast() magics.plot(output, proj, data, contour, coast) ecmwflibs-0.7.1/tests/test_versions.py0000664000175000017500000000074015256416132020307 0ustar alastairalastairimport json import ecmwflibs def test_versions(): versions = ecmwflibs.versions() print(json.dumps(versions, sort_keys=True, indent=4)) assert "eccodes" in versions, versions assert "magics" in versions, versions assert "ecmwflibs" in versions, versions for lib in ("eccodes", "magics"): print(lib, ecmwflibs.find(lib)) assert ecmwflibs.find(lib) is not None # def test_files(): # for f in ecmwflibs.files(): # print(f) ecmwflibs-0.7.1/tests/.gitignore0000664000175000017500000000010415256416132017010 0ustar alastairalastairCMakeFiles/ Makefile open_netcdf CMakeCache.txt cmake_install.cmake ecmwflibs-0.7.1/LICENSE0000664000175000017500000002613515256416132014677 0ustar alastairalastair Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ecmwflibs-0.7.1/HOWTO.md0000664000175000017500000000447215256416132015114 0ustar alastairalastair# ecmwflibs ## Compiling on a Mac ### Creating the macos version of the package Supported Python versions: 3.10 to 3.14. First make sure you have the required packages: ```bash brew install python3 brew install cmake brew install pango cairo proj pkg-config brew install netcdf ninja pip3 install delocate wheel ``` This can be achieved by simply typing: ```bash make tools ``` Then: ```bash cd ~/git/ecmwflibs make clean make twine upload wheelhouse/* ``` ### To select a specific supported Python version, use pyenv: ```bash brew install pyenv brew install pyenv-virtualenv pyenv install 3.10.16 pyenv virtualenv 3.10.16 py310 pyenv activate py310 pip3 install ninja delocate wheel cd ~/git/ecmwflibs make clean make twine upload wheelhouse/* ``` ### Creating the linux version of the package Cross compiling a Linux version on a Mac requires Docker installed. ```bash cd ~/git/ecmwflibs make image make clean ./dockcross-build-ecmwflibs make twine upload wheelhouse/* ``` You can also compile interactivaly ```bash ./dockcross-build-ecmwflibs bash make exit ``` ### Creating *all* linux versions of the package ```bash cd ~/git/ecmwflibs make image make clean ./dockcross-build-ecmwflibs make wheels.linux twine upload wheelhouse/* ``` ## Compiling on Linux Not tried, but the docker-based solution should work. # Usefull links On wheels: https://snarky.ca/the-challenges-in-designing-a-library-for-pep-425/ * https://stackoverflow.com/questions/47042483/how-to-build-and-distribute-a-python-cython-package-that-depends-on-third-party * https://cython.readthedocs.io/en/latest/src/tutorial/cython_tutorial.html * https://malramsay.com/post/perils_of_packaging/ * https://python-packaging-tutorial.readthedocs.io/en/latest/binaries_dependencies.html * https://scikit-build.readthedocs.io/en/latest/ * https://stackoverflow.com/questions/24347450/how-do-you-add-additional-files-to-a-wheel [vagrant@centos8 ~]$ pip3 install ecmwflibs Collecting ecmwflibs Could not find a version that satisfies the requirement ecmwflibs (from versions: ) No matching distribution found for ecmwflibs [vagrant@centos8 ~]$ pip3 install --upgrade pip3 Collecting pip3 Could not find a version that satisfies the requirement pip3 (from versions: ) No matching distribution found for pip3 [vagrant@centos8 ~]$ pip3 install --upgrade pip Collecting pip ecmwflibs-0.7.1/ecmwflibs/0000775000175000017500000000000015256416132015636 5ustar alastairalastairecmwflibs-0.7.1/ecmwflibs/_ecmwflibs.cc0000664000175000017500000000144115256416132020257 0ustar alastairalastair#include #include #include #include extern "C" const char* knowndrivers_(); static PyObject* versions(PyObject *self, PyObject *args) { long s = grib_get_api_version(); // Force linking auto p = knowndrivers_(); return Py_BuildValue("{s:s,s:s}", "eccodes", ECCODES_VERSION_STR, "magics", MAGICS_VERSION_STR); } static PyMethodDef ecmwflibs_methods[] = { { "versions", versions, METH_NOARGS, "Versions", }, {0,} }; static struct PyModuleDef ecmwflibs_definition = { PyModuleDef_HEAD_INIT, "ecmwflibs", "Load ECMWF libraries.", -1, ecmwflibs_methods }; PyMODINIT_FUNC PyInit__ecmwflibs(void) { Py_Initialize(); return PyModule_Create(&ecmwflibs_definition); } ecmwflibs-0.7.1/ecmwflibs/__init__.py0000664000175000017500000001346115256416132017754 0ustar alastairalastair#!/usr/bin/env python3 # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. # import atexit import json import os import sys import tempfile import warnings from findlibs import find as _find_library __version__ = "0.7.1" _here = os.path.join(os.path.dirname(__file__)) _universal = os.path.exists(os.path.join(_here, "_universal")) if _universal: _versions = dict else: _fonts = f""" {_here}/share/magics/ttf """ _fontcfg = tempfile.mktemp("ecmwflibs.xml") with open(_fontcfg, "w") as _f: print(_fonts, file=_f) if "ECMWFLIBS_MAGPLUS" not in os.environ: os.environ["FONTCONFIG_FILE"] = os.environ.get( "ECMWFLIBS_FONTCONFIG_FILE", _fontcfg ) os.environ["PROJ_LIB"] = os.environ.get( "ECMWFLIBS_PROJ_LIB", os.path.join(_here, "share", "proj") ) os.environ["MAGPLUS_HOME"] = os.environ.get("ECMWFLIBS_MAGPLUS_HOME", _here) if "ECMWFLIBS_ECCODES" not in os.environ: for env in ( "ECCODES_DEFINITION_PATH", "ECCODES_EXTRA_DEFINITION_PATH", "ECCODES_EXTRA_SAMPLES_PATH", "ECCODES_SAMPLES_PATH", "GRIB_DEFINITION_PATH", "GRIB_SAMPLES_PATH", ): if env in os.environ: if "ECMWFLIBS_" + env in os.environ: os.environ[env] = os.environ["ECMWFLIBS_" + env] warnings.warn( "ecmwflibs: using provided '{}' set to '{}".format( env, os.environ[env] ) ) else: warnings.warn( ( "ecmwflibs: ignoring provided '{}' set to '{}'. " "If you want ecmwflibs to use this environment variable, use ECMWFLIBS_{} instead. " "If you want to use your own ECCODES library, use ECMWFLIBS_ECCODES." ).format(env, os.environ[env], env) ) del os.environ[env] # This comes *after* the variables are set, so c++ has access to them try: from ._ecmwflibs import versions as _versions except ImportError as e: # ImportError: DLL load failed while importing _ecmwflibs: The specified module could not be found. warnings.warn(str(e)) raise def universal(): return _universal def _cleanup(): try: os.unlink(_fontcfg) except Exception: pass if not _universal: atexit.register(_cleanup) _MAP = { "magics": "MagPlus", "magplus": "MagPlus", "grib_api": "eccodes", "gribapi": "eccodes", } EXTENSIONS = { "darwin": ".dylib", "win32": ".dll", } def _lookup(name): return _MAP.get(name, name) def find(name): """Returns the path to the selected library, or None if not found.""" name = _lookup(name) if int(os.environ.get("ECMWFLIBS_DISABLED", "0")): warnings.warn(f"ECMWFLIBS_DISABLED is set looking for {name}") return None if int(os.environ.get("ECMWFLIBS_USED_INSTALLED", "0")): path = _find_library(name) if path is None: warnings.warn(f"ECMWFLIBS_USED_INSTALLED did not find {name}") else: warnings.warn(f"ECMWFLIBS_USED_INSTALLED found {name} at {path}") return path if _universal: # Universal version path = _find_library(name) if path: warnings.warn(f"ecmwflibs universal: found {name} at {path}") else: warnings.warn(f"ecmwflibs universal: cannot find a library called {name}") return path env = "ECMWFLIBS_" + name.upper() if env in os.environ: warnings.warn( "ecmwflibs: using provided '{}' set to '{}".format(env, os.environ[env]) ) return os.environ[env] here = os.path.dirname(__file__) extension = EXTENSIONS.get(sys.platform, ".so") for libdir in [here + ".libs", os.path.join(here, ".dylibs"), here]: if not name.startswith("lib"): names = ["lib" + name, name] else: names = [name, name[3:]] if os.path.exists(libdir): for file in os.listdir(libdir): if file.endswith(extension): for name in names: if name == file.split("-")[0].split(".")[0]: return os.path.join(libdir, file) return None def versions(): """Returns the list of libraries and their version.""" v = {"ecmwflibs": __version__} v.update(_versions()) return v def files(): here = os.path.dirname(__file__) for root, dirs, files in os.walk(here): for file in files: yield os.path.join(root, file).replace(here, "") def credits(): """Displays the list of embedded libraries and their copyright notices and/or licenses.""" here = os.path.dirname(__file__) with open(os.path.join(here, "copying/list.json")) as f: x = json.loads(f.read()) for k, v in sorted(x.items()): print("*" * 80) print("name", k) print("home", v["home"]) print("*" * 80) print() with open(os.path.join(here, v["path"])) as f: print(f.read()) print("*" * 80) def builds(): here = os.path.dirname(__file__) with open(os.path.join(here, "versions.txt")) as f: for d in f.readlines(): print(d) ecmwflibs-0.7.1/ecmwflibs/copying/0000775000175000017500000000000015256416132017306 5ustar alastairalastairecmwflibs-0.7.1/ecmwflibs/copying/.gitignore0000664000175000017500000000001515256416132021272 0ustar alastairalastair*.txt *.json ecmwflibs-0.7.1/ecmwflibs/__main__.py0000664000175000017500000000032715256416132017732 0ustar alastairalastairimport sys from ecmwflibs import credits if sys.argv[-1] != "credits": print("Type 'python -m ecmwflibs credits' to see the") print("Open Source software installed with ecmwflibs.") exit(0) credits() ecmwflibs-0.7.1/ecmwflibs/etc/0000775000175000017500000000000015256416132016411 5ustar alastairalastairecmwflibs-0.7.1/ecmwflibs/etc/README0000664000175000017500000000010515256416132017265 0ustar alastairalastairAdd files to this drirectory that will be installed with the package ecmwflibs-0.7.1/ecmwflibs/.gitignore0000664000175000017500000000000715256416132017623 0ustar alastairalastairshare/ ecmwflibs-0.7.1/setup.py0000664000175000017500000000633615256416132015405 0ustar alastairalastair#!/usr/bin/env python3 # (C) Copyright 2020 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not waive the privileges and immunities # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. # import io import os import os.path import sys from setuptools import Extension, find_packages, setup def read(fname): file_path = os.path.join(os.path.dirname(__file__), fname) return io.open(file_path, encoding="utf-8").read() version = None for line in read("ecmwflibs/__init__.py").split("\n"): if line.startswith("__version__"): version = line.split("=")[-1].strip()[1:-1] assert version libdir = os.path.realpath("install/lib") incdir = os.path.realpath("install/include") libs = ["eccodes", "MagPlus"] # https://docs.python.org/3/distutils/apiref.html ext_modules = [ Extension( "ecmwflibs._ecmwflibs", sources=["ecmwflibs/_ecmwflibs.cc"], language="c++", libraries=libs, library_dirs=[libdir], include_dirs=[incdir, os.path.join(incdir, "magics")], extra_link_args=["-Wl,-rpath," + libdir], ) ] def shared(directory): result = [] for path, dirs, files in os.walk(directory): for f in files: result.append(os.path.join(path, f)) return result # Paths must be relative to package directory... shared_files = ["versions.txt"] shared_files += [x[len("install/") :] for x in shared("install/share/magics")] shared_files += [x[len("ecmwflibs/") :] for x in shared("ecmwflibs/share/proj")] shared_files += [x[len("ecmwflibs/") :] for x in shared("ecmwflibs/etc")] shared_files += [x[len("ecmwflibs/") :] for x in shared("ecmwflibs/copying")] if os.name == "nt": for n in os.listdir("ecmwflibs"): if n.endswith(".dll"): shared_files.append(n) # print(shared_files) if "--universal" in sys.argv: ext_modules = [] with open("ecmwflibs/_universal", "wt"): pass shared_files = ["_universal"] setup( name="ecmwflibs", version=version, author="ECMWF", author_email="software.support@ecmwf.int", license="Apache 2.0", url="https://github.com/ecmwf/ecmwflibs", description="Wraps ECMWF tools (experimental)", long_description=read("README.md"), long_description_content_type="text/markdown", packages=find_packages(), include_package_data=True, package_data={"": shared_files}, install_requires=[ "findlibs", ], python_requires=">=3.10", zip_safe=True, classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Operating System :: OS Independent", ], ext_modules=ext_modules, ) ecmwflibs-0.7.1/tox.ini0000664000175000017500000000027615256416132015203 0ustar alastairalastair[flake8] ; ignore = E226,E302,E41 max-line-length = 120 ; exclude = tests/* ; See https://black.readthedocs.io/en/stable/the_black_code_style.html extend-ignore = E203 [isort] profile=black ecmwflibs-0.7.1/tools/0000775000175000017500000000000015256416132015023 5ustar alastairalastairecmwflibs-0.7.1/tools/copy-licences.py0000664000175000017500000002760215256416132020141 0ustar alastairalastair#!/usr/bin/env python import json import os import re import socket import time import sys import urllib.error import urllib.request try: from html2text import html2text except ImportError: html2text = None def identity(x): return x _HEADERS = { "User-Agent": ( "Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0" ), "Accept": "text/html,text/plain,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", } def _local_fallback_path(url): base = os.path.basename(url.rstrip("/")) if not base: return None licenses_dir = os.path.join(os.path.dirname(__file__), "licenses") for candidate in (base, base.lower()): path = os.path.join(licenses_dir, candidate) if os.path.exists(path): return path return None def fetch_url_text(url, lib_name, timeout=30, attempts=3): errors = [] for attempt in range(1, attempts + 1): try: req = urllib.request.Request(url, headers=_HEADERS) with urllib.request.urlopen(req, timeout=timeout) as response: return response.read().decode("utf-8") except urllib.error.HTTPError as error: body_preview = error.read().decode("utf-8", errors="replace").strip() if len(body_preview) > 500: body_preview = f"{body_preview[:500]}..." details = [ f"URL: {url}", f"Attempt: {attempt}/{attempts}", f"HTTP status: {error.code}", f"Reason: {error.reason}", ] if body_preview: details.append(f"Response body (first 500 chars):\n{body_preview}") errors.append("\n".join(details)) except (urllib.error.URLError, TimeoutError, socket.timeout, OSError) as error: reason = getattr(error, "reason", str(error)) errors.append( f"URL: {url}\n" f"Attempt: {attempt}/{attempts}\n" f"Network error: {reason}" ) if attempt < attempts: time.sleep(attempt) fallback_path = _local_fallback_path(url) if fallback_path: with open(fallback_path, "r", encoding="utf-8") as fallback_file: return fallback_file.read() raise RuntimeError( f"Failed to download license for {lib_name}.\n" + "\n\n---\n\n".join(errors) ) ENTRIES = { "libMagPlus": { "home": "https://github.com/ecmwf/magics", "copying": "https://raw.githubusercontent.com/ecmwf/magics/develop/LICENSE", }, "libeccodes": { "home": "https://github.com/ecmwf/eccodes", "copying": "https://raw.githubusercontent.com/ecmwf/eccodes/develop/LICENSE", }, "libnetcdf": { "home": "https://github.com/Unidata/netcdf-c", "copying": "https://raw.githubusercontent.com/Unidata/netcdf-c/master/COPYRIGHT", }, "libproj": { "home": "https://github.com/OSGeo/PROJ", "copying": "https://raw.githubusercontent.com/OSGeo/PROJ/master/COPYING", }, "libpixman": { "home": "https://gitlab.freedesktop.org/pixman/pixman", "copying": "https://gitlab.freedesktop.org/pixman/pixman/-/raw/master/COPYING", }, "libfribidi": { "home": "https://github.com/fribidi/fribidi", "copying": "https://raw.githubusercontent.com/fribidi/fribidi/master/COPYING", }, "libharfbuzz": { "home": "https://github.com/fribidi/fribidi", "copying": "https://raw.githubusercontent.com/fribidi/fribidi/master/COPYING", }, "libuuid": { "home": "https://github.com/karelzak/util-linux/tree/master/libuuid", "copying": "https://raw.githubusercontent.com/karelzak/util-linux/master/libuuid/COPYING", }, "libpango": { "home": "https://github.com/GNOME/pango", "copying": "https://raw.githubusercontent.com/GNOME/pango/master/COPYING", }, "libsqlite3": { "home": "https://sqlite.org/index.html", "copying": "Public Domain, see https://sqlite.org/copyright.html", }, "libfontconfig": { "home": "https://gitlab.freedesktop.org/fontconfig/fontconfig", "copying": "https://gitlab.freedesktop.org/fontconfig/fontconfig/-/raw/main/COPYING", }, "libbz2": { "home": "https://gitlab.com/federicomenaquintero/bzip2", "copying": "https://gitlab.com/federicomenaquintero/bzip2/-/raw/master/COPYING", }, "libhdf5": { "home": "https://github.com/HDFGroup/hdf5", "copying": "https://raw.githubusercontent.com/HDFGroup/hdf5/develop/LICENSE", }, "libhdf5_hl": None, # Assumed to be part of libhdf5 (hl = high level) "libpng": { "home": "https://github.com/glennrp/libpng", "copying": "https://raw.githubusercontent.com/glennrp/libpng/libpng16/LICENSE", }, "libaec": { "home": "https://github.com/MathisRosenhauer/libaec", "copying": "https://raw.githubusercontent.com/MathisRosenhauer/libaec/v1.1.3/LICENSE.txt", }, "libexpat": { "home": "https://libexpat.github.io", "copying": "https://raw.githubusercontent.com/libexpat/libexpat/master/expat/COPYING", }, "libz": { "home": "https://zlib.net", "copying": "https://zlib.net/zlib_license.html", "html": True, }, "libcairo": { "home": "https://cairographics.org", "copying": "https://gitlab.freedesktop.org/cairo/cairo/-/raw/master/COPYING", }, "libjasper": { "home": "https://github.com/jasper-software/jasper", "copying": "https://raw.githubusercontent.com/jasper-software/jasper/master/LICENSE.txt", }, "libopenjp2": { "home": "https://github.com/uclouvain/openjpeg", "copying": "https://raw.githubusercontent.com/uclouvain/openjpeg/master/LICENSE", }, # We build libjpeg-turbo from source (drop-in replacement, installs as libjpeg.so.62) "libjpeg": { "home": "https://github.com/libjpeg-turbo/libjpeg-turbo", "copying": "https://raw.githubusercontent.com/libjpeg-turbo/libjpeg-turbo/main/LICENSE.md", }, "libfreetype": { "home": "https://gitlab.freedesktop.org/freetype/freetype/", "copying": "https://gitlab.freedesktop.org/freetype/freetype/-/raw/master/docs/FTL.TXT", }, "libdatrie": { "home": "https://github.com/tlwg/libdatrie", "copying": "https://raw.githubusercontent.com/tlwg/libdatrie/master/COPYING", }, "libthai": { "home": "https://github.com/tlwg/libthai", "copying": "https://raw.githubusercontent.com/tlwg/libthai/master/COPYING", }, "libX11": { "home": "https://github.com/mirror/libX11", "copying": "https://raw.githubusercontent.com/mirror/libX11/master/COPYING", }, "libxcb": { "home": "https://github.com/corngood/libxcb", "copying": "https://raw.githubusercontent.com/corngood/libxcb/master/COPYING", }, "libpcre": { "home": "https://github.com/vmg/libpcre", "copying": "https://raw.githubusercontent.com/vmg/libpcre/master/LICENCE", }, "libgraphite2": { "home": "https://github.com/silnrsi/graphite", "copying": "https://raw.githubusercontent.com/silnrsi/graphite/master/COPYING", }, "libffi": { "home": "https://github.com/libffi/libffi", "copying": "https://raw.githubusercontent.com/libffi/libffi/master/LICENSE", }, "libtiff": { "home": "https://gitlab.com/libtiff/libtiff", "copying": "https://gitlab.com/libtiff/libtiff/-/raw/master/LICENSE.md", }, # See also https://www.gnu.org/software/gettext/manual/html_node/Discussions.html # intl(gettext) is GPL while libintl is LGPL "libintl": { "home": "https://www.gnu.org/software/gettext/manual/html_node/Licenses.html", "copying": "https://www.gnu.org/licenses/lgpl-3.0.txt", "html": True, }, # See also https://www.gnu.org/software/libiconv/ # iconv is GPL while libiconv is LGPL "libiconv": { "home": "https://www.gnu.org/software/libiconv/", "copying": "https://www.gnu.org/licenses/lgpl-3.0.txt", }, "libglib": { "home": "https://gitlab.gnome.org/GNOME/glib", "copying": "https://gitlab.gnome.org/GNOME/glib/-/raw/main/COPYING", }, "libbrotli": { "home": "https://github.com/google/brotli", "copying": "https://raw.githubusercontent.com/google/brotli/master/LICENSE", }, "libpcre2": { "home": "https://github.com/PCRE2Project/pcre2", "copying": "https://raw.githubusercontent.com/PCRE2Project/pcre2/main/LICENCE.md", }, "libzstd": { "home": "https://github.com/facebook/zstd", "copying": "https://raw.githubusercontent.com/facebook/zstd/master/LICENSE", }, # not completely clear what the definitive source for this library is "liblzma": { "home": "https://github.com/ShiftMediaProject/liblzma", "copying": "https://raw.githubusercontent.com/ShiftMediaProject/liblzma/master/COPYING", }, "libcurl": { "home": "https://github.com/curl/curl", "copying": "https://raw.githubusercontent.com/curl/curl/master/COPYING", }, "libtinyxml2": { "home": "https://github.com/leethomason/tinyxml2", "copying": "https://raw.githubusercontent.com/leethomason/tinyxml2/master/LICENSE.txt", }, "libwebp": { "home": "https://github.com/webmproject/libwebp", "copying": "https://raw.githubusercontent.com/webmproject/libwebp/main/COPYING", }, } PATTERNS = { r"^libpng\d+$": "libpng", r"^libproj(_\d+)+$": "libproj", } ALIASES = { "libbrotlicommon": "libbrotli", "libbrotlidec": "libbrotli", "libpangowin32": "libpango", "libzlib1": "libz", "libeccodes_memfs": "libeccodes", "libgobject": "libglib", # Part of libglib "libgmodule": "libglib", # Part of libglib "libgio": "libglib", # Part of libglib "libpangoft2": "libpango", # Assumed to be part of libpango "libpangocairo": "libpango", # Assumed to be part of libpango "libhdf5_hl": "libhdf5", # Legacy HDF SZIP license pages were retired; map to libaec. "libsz": "libaec", "libszip": "libaec", # X.Org stack libraries shipped transitively with cairo/pango. "libXrender": "libX11", "libXdmcp": "libX11", "libXau": "libX11", "libXext": "libX11", # sharpyuv is bundled inside the libwebp repo and shares its license. "libsharpyuv": "libwebp", } if False: for e in ENTRIES.values(): if isinstance(e, dict): copying = e["copying"] if copying.startswith("http"): urllib.request.urlopen(copying).close() libs = {} missing = [] seen = set() for line in open(sys.argv[1], "r", encoding="utf-8"): lib = "-no-regex-" lib = line.strip().split("/")[-1] lib = lib.split("-")[0].split(".")[0] if lib == "": continue if not lib.startswith("lib"): lib = f"lib{lib}" for k, v in PATTERNS.items(): if re.match(k, lib): lib = v lib = ALIASES.get(lib, lib) if lib not in ENTRIES: missing.append((lib, line)) continue if lib in seen: continue seen.add(lib) e = ENTRIES[lib] if e is None: continue libs[lib] = dict(path=f"copying/{lib}.txt", home=e["home"]) copying = e["copying"] filtering = identity if e.get("html") and html2text is not None: filtering = html2text if copying.startswith("http://") or copying.startswith("https://"): text = fetch_url_text(copying, lib) output_lines = filtering(text).split("\n") else: output_lines = copying.split("\n") with open(f"ecmwflibs/copying/{lib}.txt", "w", encoding="utf-8") as f: for n in output_lines: print(n, file=f) with open("ecmwflibs/copying/list.json", "w", encoding="utf-8") as f: print(json.dumps(libs), file=f) assert len(missing) == 0, json.dumps(missing, indent=4, sort_keys=True) ecmwflibs-0.7.1/tools/requirements.txt0000664000175000017500000000001215256416132020300 0ustar alastairalastairhtml2text ecmwflibs-0.7.1/tools/licenses/0000775000175000017500000000000015256416132016630 5ustar alastairalastairecmwflibs-0.7.1/tools/licenses/lgpl-3.0.txt0000664000175000017500000001674415256416132020641 0ustar alastairalastair GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ecmwflibs-0.7.1/tools/copy-dlls.py0000664000175000017500000000172015256416132017303 0ustar alastairalastair#!/usr/bin/env python import os import shutil import sys from dlldiag.common import ModuleHeader VCPKG1 = "C:/vcpkg/installed/{}-windows/bin/{}" VCPKG2 = "C:/vcpkg/installed/{}-windows/debug/bin/{}" def scan_module(module, depth, seen): name = os.path.basename(module) if name in seen: return if not os.path.exists(module): return print(" " * depth, module) seen[name] = module header = ModuleHeader(module) cwd = os.path.dirname(module) architecture = header.getArchitecture() for dll in header.listAllImports(): # print("DEBUG", dll) scan_module((cwd + "/" + dll), depth + 3, seen) scan_module(VCPKG1.format(architecture, dll), depth + 3, seen) scan_module(VCPKG2.format(architecture, dll), depth + 3, seen) seen = {} scan_module(sys.argv[1], 0, seen) for k, v in seen.items(): target = sys.argv[2] + "/" + k print("Copy", v, "to", target) shutil.copyfile(v, target) ecmwflibs-0.7.1/tools/sha1.sh0000775000175000017500000000052215256416132016215 0ustar alastairalastair#!/usr/bin/env bash here=$(dirname $0) source $here/../make/VERSIONS.make eccodes=$(git ls-remote $GIT_ECCODES $ECCODES_VERSION | awk '{print $1;}') magics=$(git ls-remote $GIT_MAGICS $MAGICS_VERSION | awk '{print $1;}') echo $eccodes echo $magics echo "::set-output name=eccodes::${eccodes}" echo "::set-output name=magics::${magics}" ecmwflibs-0.7.1/test.py0000664000175000017500000000015315256416132015213 0ustar alastairalastairfrom climetlab.utils.html import urlify print(urlify("https://raspstephan.github.io/blog/weatherbench/")) ecmwflibs-0.7.1/README.md0000664000175000017500000000214415256416132015143 0ustar alastairalastair# ecmwflibs A Python package that wraps some of ECMWF libraries to be used by Python interfaces to ECMWF software. ## Requirements - Python 3.10 or newer The snippet of code below should return the path to the *ecCodes* shared library. ```python import ecmwflibs lib = ecmwflibs.find("eccodes") ``` You can get the versions of libraries: ```python import ecmwflibs print(ecmwflibs.versions()) {'eccodes': '2.46.3', 'magics': '4.16.1', 'ecmwflibs': '0.7.1'} ``` ## Possible issues If you get this message on Windows: `DLL load failed while importing _ecmwflibs: The specified module could not be found.` this means that the C++ runtime library is not installed. Please download and install `vc_redist.x86.exe` or `vc_redist.x64.exe` from https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads. ## Acknowledgements *ecmwflibs* comes packaged with some third-party open source libraries which are dependencies of *Magics* and *ecCodes*. To display the list of embedded libraries and their copyright notices and/or licenses, please type: ```bash python3 -m ecmwflibs credits ``` ecmwflibs-0.7.1/.gitignore0000664000175000017500000000412015256416132015650 0ustar alastairalastair# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ *.swp *.whl build/ wheelhouse/ dist/ build-ecmwf/ install-ecmwf/ install-other/ src/ build-other/ install/ dockcross-* .inited 2m_temperature.grib magics.png junk/ *.old ? ?.* *.save *.grib *.png *.nc ecmwflibs/versions.txt libs tmp .gitignore *.rb versions ecmwflibs/_ecmwflibs.py ecmwflibs/_universal *.ps .DS_Store build-m1/ ecmwflibs-0.7.1/NOTICE.md0000664000175000017500000000023115256416132015162 0ustar alastairalastair# Notice To display the list of embedded libraries and their copyright notices and/or licenses, please type: ```bash python3 -m ecmwflibs credits ```