The following exercises will help you understand how to build your own software on the ATOS HPCF or ECS.
Ensure your environment is clean by running:
module reset |
Create a directory for this tutorial and cd into it:
mkdir -p compiling_tutorial cd compiling_tutorial |
We are going to use a simple program that will display versions of different libraries linked to it. Create a file called versions.c
using your favourite editor with the following contents:
#include <stdio.h> #include <hdf5.h> #include <netcdf.h> #include <eccodes.h> int main() { #if defined(__INTEL_LLVM_COMPILER) printf("Compiler: Intel LLVM %d\n", __INTEL_LLVM_COMPILER); #elif defined(__INTEL_COMPILER) printf("Compiler: Intel Classic %d\n", __INTEL_COMPILER); #elif defined(__clang_version__) printf("Compiler: Clang %s\n", __clang_version__); #elif defined(__GNUC__) printf("Compiler: GCC %d.%d.%d\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); #else printf("Compiler information not available\n"); #endif // HDF5 version unsigned majnum, minnum, relnum; H5get_libversion(&majnum, &minnum, &relnum); printf("HDF5 version: %u.%u.%u\n", majnum, minnum, relnum); // NetCDF version printf("NetCDF version: %s\n", nc_inq_libvers()); // ECCODES version printf("ECCODES version: "); codes_print_api_version(stdout); printf("\n"); return 0; } |
Try to naively compile this program with:
$CC -o versions versions.c |
Let's use the existing software installed on the system with modules, and benefit from the corresponding environment variables *_DIR which are defined in them to manually construct the include and library flags:
$CC -o versions versions.c -I$HDF5_DIR/include -I$NETCDF4_DIR/include -I/$ECCODES_DIR/include -L$HDF5_DIR/lib -lhdf5 -L$NETCDF4_DIR/lib -lnetcdf -L$ECCODES_DIR/lib -leccodes |
Load the appropriate modules so that the line above completes successfully and generates the versions
executable:
You will need to load the following modules to. have those variables defined.:
The
|
Run ./versions
. You will get an error such as the one below:
$ ./versions ./versions: error while loading shared libraries: libhdf5.so.200: cannot open shared object file: No such file or directory |
While you passed the location of the libraries at compile time, the program cannot not find the libraries at runtime. Inspect the executable with ldd
to see what libraries are missing
ldd is a utility that prints the shared libraries required by each program or shared library specified on the command line:
|
Can you make that program run successfully?
While you passed the location of the libraries at compile time, the program cannot not find the libraries at runtime. There are two solutions: Use the environment variable LD_LIBRARY_PATH- not recommended for long term Use the environment variable LD_LIBRARY_PATH. Check that ldd with the environment variable defined reports all libraries found:
Use Use the
Check that ldd with the environment variable defined reports all libraries found:
Final version For convenience, all those software modules define the
You can use those in for your compilation directly, with the following simplified compilation line:
Now you can run your program without any additional settings:
|
Can you rebuild the program so it uses the "old" versions of all those libraries in modules? Ensure the output of the program matches the versions loaded in modules? Do the same with the latest.
You need to load the desired versions or the modules:
And then rebuild and run the program:
The output should match the versions loaded by the modules:
Repeat the operation with
|
To simplify the build process, let's create a simple Makefile for this program. With your favourite editor, create a file called Makefile
in the same directory with the following contents:
# # Makefile # # Make sure all the relevant modules are loaded before running make EXEC = versions # TODO: Add the necessary variables into CFLAGS and LDFLAGS definition CFLAGS = LDFLAGS = all: $(EXEC) %: %.c $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) test: $(EXEC) ./$(EXEC) clean: rm -f $(EXEC) |
You can test it works by running:
make clean ldd test |
Edit the Makefile and add the
Then run it with:
|
So far we have used the default compiler toolchain to build this program. Because of the installation paths of the library, it is easy to see both the version of the library used as well as the compiler flavour with ldd:
$ make ldd libhdf5.so.200 => /usr/local/apps/hdf5/<HDF5 version>/GNU/8.5/lib/libhdf5.so.200 (0x000014f612b7d000) libnetcdf.so.19 => /usr/local/apps/netcdf4/<NetCDF version>/GNU/8.5/lib/libnetcdf.so.19 (0x000014f611f2a000) libeccodes.so => /usr/local/apps/ecmwf-toolbox/<ecCodes version>/GNU/8.5/lib/libeccodes.so (0x000014f611836000) |
Rebuild the program with:
Use the following command to test and show what versions of the libraries are being used at any point:
make clean ldd test |
You can perform this test with the following one-liner, exploiting the
Pay attention to the following aspects:
|
To put into practice what we have learned so far, let's try to build and install CDO. You would typically not need to build this particular application, since it is already available as part of the standard software stack via modules or easily installable with conda. However, it is a good illustration of how to build a real-world software with dependencies to other software packages and libraries.
The goal of this exercise is for you to be able to build CDO and install it under one of your storage spaces (HOME or PERM), and then successfully run:
<PREFIX>/bin/cdo -V |
You will need to:
Make sure that CDO is built at least with support to:
It is strongly recommended you bundle all your build process in a job script that you can submit in batch. That way you can request additional cpus and speed up your compilation exploiting build parallelism with make -j If you would like a starting point for such a job, you can start from the following example, adding and amending the necessary bits as needed:
|
This is the complete job script to
You can submit it to the batch system with
While it builds, you may want to keep an eye on the progress with:
Make sure the job completes successfully and that the output of the CDO executable you built is what you would expect. |