Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Expand
titleSolution

We will take a step-by-step approach, taking the build_cdo.sh template above as the starting point.

First thing we need to do is to decide where to install your personal CDO. A good choice would be using your PERM space, and we may use the same structure as the production installation. Because in the script we already have a variable called $VERSION containing the CDO version to install, we can also use that. This way we could have multiple versions of the same package installed alongside should we require it in the future:

Code Block
languagebash
# Define installation prefix and build directory
PREFIX=$PERM/apps/cdo/$VERSION

Next comes the decision on what to use for the build itself. Since it is a relatively small build, for performance you might use $TMPDIR which is local to the node.

Code Block
languagebash
BUILDDIR=$TMPDIR

However, if you are going to submit it as a batch job, the directory will be wiped at the end. While you are putting the build script together, it may be more practical to have the build directory somewhere that is not deleted after a failed build so you can inspect output files and troubleshoot. As an example, we could pick the following directory in PERM:

Code Block
languagebash
BUILDDIR=$PERM/apps/cdo/build

Let's look at the environment for the build. All of dependencies listed above are already available, so we may leverage that by loading all the corresponding modules:

Code Block
languagebash
# Define the environment for the build 
module load hdf5 netcdf4 aec ecmwf-toolbox proj cmor udunits

At this point, we need to refer to the installation instructions of this package in the official documentation. We can see it is a classic autotools package, which is typically built with the configure - make - make install sequence. We should then look at the configure --help to see how to enable all the extra options to configure the build:

No Format
# Configure the build
./configure --help && exit

Since we are just getting some short help, we can just run the script locally to get the configure output.

No Format
bash ./build_cdo.sh

We should inspect the output of the configure help command, and identify what options are to be used:

No Format
  --with-szlib=<yes|no|directory> (default=no)  
  --with-hdf5=<yes|no|directory> (default=no)
  --with-netcdf=<yes|no|directory> (default=no)
  --with-udunits2=<directory>
  --with-cmor=<directory> Specify location of CMOR library.
  --with-eccodes=<yes|no|directory> (default=no)
  --with-proj=<directory> Specify location of PROJ library for cartographic
                          projections.

Since all those dependencies are not installed on system paths, we will need to specify the installation directory for each one of them. We may then use the *_DIR environment variables defined by the corresponding modules we load just before.

We will also define where to install the package with the --prefix option. Let's amend the configure line with:

No Format
# Configure the build
./configure --prefix=$PREFIX --with-hdf5=$HDF5_DIR --with-netcdf=$NETCDF4_DIR --with-szlib=$AEC_DIR  --with-eccodes=$ECCODES_DIR  --with-proj=$proj_DIR --with-cmor=$CMOR_DIR --with-udunits2=$UDUNITS_DIR

Note that for PROJ, since the variable $PROJ_DIR has a special meaning in the package itself, we must use the lowercase version $proj_DIR.

We are now ready to attempt our first build.  Submit it the build script to the batch system with:

No Format
sbatch build_cdo.sh

While it builds, we may keep an eye on the progress with:

No Format
tail -f build_cdo.out

At this point CDO build and installation should complete successfully, but the execution of the newly installed CDO at the end fails with:

No Format
$ grep -v ECMWF-INFO build_cdo.out | tail
make[1]: Leaving directory '/etc/ecmwf/nfs/dh2_perm_a/user/apps/cdo/build/cdo-2.3.0/test/pytest'
make[1]: Entering directory '/etc/ecmwf/nfs/dh2_perm_a/user/apps/cdo/build/cdo-2.3.0'
make[2]: Entering directory '/etc/ecmwf/nfs/dh2_perm_a/user/apps/cdo/build/cdo-2.3.0'
make[2]: Nothing to be done for 'install-exec-am'.
make[2]: Nothing to be done for 'install-data-am'.
make[2]: Leaving directory '/etc/ecmwf/nfs/dh2_perm_a/user/apps/cdo/build/cdo-2.3.0'
make[1]: Leaving directory '/etc/ecmwf/nfs/dh2_perm_a/user/apps/cdo/build/cdo-2.3.0'
+ /perm/user/apps/cdo/2.3.0/bin/cdo -V
/perm/user/apps/cdo/2.3.0/bin/cdo: error while loading shared libraries: libproj.so.25: cannot open shared object file: No such file or directory

If we inspect the resulting binary with ldd, we will notice there are a few libraries that cannot be found at runtime:

No Format
$ ldd $PERM/apps/cdo/2.3.0/bin/cdo
        linux-vdso.so.1 (0x00007ffc3fbb6000)
        libproj.so.25 => not found
        libeccodes.so => not found
        libcmor.so => /usr/local/apps/cmor/3.7.1/lib/libcmor.so (0x00001484b970d000)
        libudunits2.so.0 => /usr/local/apps/udunits/2.2.28/lib/libudunits2.so.0 (0x00001484b94ed000)
        libexpat.so.1 => /lib64/libexpat.so.1 (0x00001484b92b1000)
        libnetcdf.so.19 => /usr/local/apps/netcdf4/4.9.1/GNU/8.5/lib/libnetcdf.so.19 (0x00001484b8e86000)
        libbz2.so.1 => /lib64/libbz2.so.1 (0x00001484b8c75000)
        libzstd.so.1 => /lib64/libzstd.so.1 (0x00001484b89d1000)
        libxml2.so.2 => /lib64/libxml2.so.2 (0x00001484b8669000)
        libcurl.so.4 => /lib64/libcurl.so.4 (0x00001484b83db000)
        libhdf5_hl.so.200 => /usr/local/apps/hdf5/1.12.2/GNU/8.5/lib/libhdf5_hl.so.200 (0x00001484b81ba000)
        libhdf5.so.200 => /usr/local/apps/hdf5/1.12.2/GNU/8.5/lib/libhdf5.so.200 (0x00001484b7bca000)
        libz.so.1 => /lib64/libz.so.1 (0x00001484b79b2000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00001484b77ae000)
        libsz.so.2 => /lib64/libsz.so.2 (0x00001484b75ab000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00001484b738b000)
        libuuid.so.1 => /usr/local/apps/cmor/3.7.1/lib/libuuid.so.1 (0x00001484b7187000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00001484b6df2000)
        libm.so.6 => /lib64/libm.so.6 (0x00001484b6a70000)
        libgomp.so.1 => /lib64/libgomp.so.1 (0x00001484b6838000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00001484b6620000)
        libc.so.6 => /lib64/libc.so.6 (0x00001484b625b000)
        /lib64/ld-linux-x86-64.so.2 (0x000014850b1a3000)
        libjson-c.so.4 => /usr/local/apps/cmor/3.7.1/lib/libjson-c.so.4 (0x00001484b604c000)
        libgfortran.so.5 => /lib64/libgfortran.so.5 (0x00001484b5bcd000)
        liblzma.so.5 => /lib64/liblzma.so.5 (0x00001484b59a6000)
        libnghttp2.so.14 => /lib64/libnghttp2.so.14 (0x00001484b577f000)
        libidn2.so.0 => /lib64/libidn2.so.0 (0x00001484b5561000)
        libssh.so.4 => /lib64/libssh.so.4 (0x00001484b52f2000)
        libpsl.so.5 => /lib64/libpsl.so.5 (0x00001484b50e1000)
        libssl.so.1.1 => /lib64/libssl.so.1.1 (0x00001484b4e4d000)
        libcrypto.so.1.1 => /lib64/libcrypto.so.1.1 (0x00001484b4964000)
        libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00001484b470f000)
        libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00001484b4425000)
        libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00001484b420e000)
        libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00001484b400a000)
        libldap-2.4.so.2 => /lib64/libldap-2.4.so.2 (0x00001484b3dbb000)
        liblber-2.4.so.2 => /lib64/liblber-2.4.so.2 (0x00001484b3bab000)
        libbrotlidec.so.1 => /lib64/libbrotlidec.so.1 (0x00001484b399e000)
        libaec.so.0 => /lib64/libaec.so.0 (0x00001484b3796000)
        libquadmath.so.0 => /lib64/libquadmath.so.0 (0x00001484b3555000)
        libunistring.so.2 => /lib64/libunistring.so.2 (0x00001484b31d4000)
        librt.so.1 => /lib64/librt.so.1 (0x00001484b2fcc000)
        libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00001484b2dbb000)
        libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00001484b2bb7000)
        libresolv.so.2 => /lib64/libresolv.so.2 (0x00001484b29a0000)
        libsasl2.so.3 => /lib64/libsasl2.so.3 (0x00001484b2782000)
        libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00001484b2561000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00001484b2337000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00001484b210e000)
        libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00001484b1e8a000)

We are missing the PROJ and ecCodes libraries. We will need to explicitly set RPATHs when we build CDO to make sure the libraries are found. In Autotools packages, and as shown in the configure help we ran earlier, we may pass any extra link flags through the LDFLAGS environment variable. We can amend our build script setting that variable just after loading the dependant modules:

No Format
# Define the environment for the build 
module load hdf5 netcdf4 aec ecmwf-toolbox proj cmor udunits

# We will need to explicitly set rpath for proj and eccodes since CDO build system will not
export LDFLAGS="-Wl,-rpath,$proj_DIR/lib -Wl,-rpath,$ECCODES_DIR/lib"

If we submit the the build job again and wait for it to complete, we should see something like:

No Format
$ grep -v ECMWF-INFO build_cdo.out | tail -n 25
make[2]: Nothing to be done for 'install-exec-am'.
make[2]: Nothing to be done for 'install-data-am'.
make[2]: Leaving directory '/etc/ecmwf/nfs/dh2_perm_a/user/apps/cdo/build/cdo-2.3.0'
make[1]: Leaving directory '/etc/ecmwf/nfs/dh2_perm_a/user/apps/cdo/build/cdo-2.3.0'
+ /perm/user/apps/cdo/2.3.0/bin/cdo -V
Climate Data Operators version 2.3.0 (https://mpimet.mpg.de/cdo)
System: x86_64-pc-linux-gnu
CXX Compiler: g++ -std=gnu++17 -g -O2 -fopenmp -pthread
CXX version : g++ (GCC) 8.5.0 20210514 (Red Hat 8.5.0-15)
C Compiler: gcc -g -O2 -fopenmp -pthread -pthread
C version : gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-15)
F77 Compiler: gfortran -g -O2
F77 version : GNU Fortran (GCC) 8.5.0 20210514 (Red Hat 8.5.0-15)
Features: 7/503GB 16/256threads c++17 OpenMP45 Fortran pthreads HDF5 NC4/HDF5/threadsafe OPeNDAP sz udunits2 proj cmor sse2                                                                
Libraries: yac/3.0.1 NetCDF/4.9.1 HDF5/1.12.2 proj/9.1.1 cmor/3.7.1
CDI data types: SizeType=size_t
CDI file types: srv ext ieg grb1 grb2 nc1 nc2 nc4 nc4c nc5 nczarr
     CDI library version : 2.3.0
 cgribex library version : 2.1.1
 ecCodes library version : 2.30.2
  NetCDF library version : 4.9.1 of Feb  9 2023 13:54:09 $
    exse library version : 1.5.0
    FILE library version : 1.9.1

For reference, this is the complete job script:

Code Block
languagebash
titlebuild_cdo.sh
collapsetrue
#!/bin/bash
#SBATCH -J build_cdo
#SBATCH -o %x.out
#SBATCH -n 8

set -x
set -e
set -u
set -o pipefail

# Get the URL and VERSION for the latest CDO available
URL=$(curl -s https://code.mpimet.mpg.de/projects/cdo/files | grep attachments/download  | sed -e "s:.*href=\"\(.*\)\".*:https\://code.mpimet.mpg.de\1:" | head -n 1)
VERSION=$(echo $URL | sed -e "s:.*/cdo-\(.*\).tar.gz:\1:")

# Define installation prefix and build directory
PREFIX=$PERM/apps/cdo/$VERSION
#BUILDDIR=$TMPDIR
BUILDDIR=$PERM/apps/cdo/build

# Move to our BUILD DIRECTORY
mkdir -p $BUILDDIR
cd $BUILDDIR

# Download source
[ -f cdo-$VERSION.tar.gz ] || wget $URL
[ -d cdo-$VERSION ] || tar xvf cdo-$VERSION.tar.gz
cd cdo-$VERSION

# Define the environment for the build 
module load hdf5 netcdf4 aec ecmwf-toolbox proj cmor udunits

# We will need to explicitly set rpath for proj and eccodes since CDO build system will not
export LDFLAGS="-Wl,-rpath,$proj_DIR/lib -Wl,-rpath,$ECCODES_DIR/lib"

# Configure the build
./configure --prefix=$PREFIX --with-hdf5=$HDF5_DIR --with-netcdf=$NETCDF4_DIR --with-szlib=$AEC_DIR  --with-eccodes=$ECCODES_DIR  --with-proj=$proj_DIR --with-cmor=$CMOR_DIR --with-udunits2=$UDUNITS_DIR

# Make sure we start a clean build
make clean

# Build
make -j $SLURM_NTASKS

# Install
make install

# Check installed binary
$PREFIX/bin/cdo -V


...