This lesson is being piloted (Beta version)

Installing Modules Using CMake

Overview

Teaching: 25 min
Exercises: 10 min
Questions
  • What is the CMake standard build workflow?

  • How do I modify this workflow to suit my needs?

Objectives
  • Understand the basics of the CMake standard build procedure.

  • Install an example module using the CMake build system.

Adapting our workflow for projects using CMake

A newer tool that is growing in popularity for building software with complex sets of dependencies is CMake, by KitWare. CMake is powerful because it allows developers to create install procedures that automatically find and include a large number of dependencies that may be installed differently on different machines. The cmake command itself is meant to be something of a replacement for the configure command, making the build procedure into cmake; make; make install.

Using the same basic workflow that we did for the GNU installation procedure, we can install self-managed modules using CMake as well. We will use Luscus for our example this time, a tool for displaying and editing molecular systems in a graphical user interface. Let’s begin by downloading the source code into a new directory and unpacking it:

$ mkdir ~/software.source/luscus
$ cd ~/software.source/luscus
$ wget https://downloads.sourceforge.net/project/luscus/luscus_0.8.6.tar.gz
$ tar xvf luscus_0.8.6.tar.gz
$ cd luscus_0.8.6
$ ls
backup_file.c   gv_about.c      gv_gtk_atom_list.c   gv_notebook.c        mcube.h           surface.c
build/          gv_atoms.c      gv_gtk.h             gv_system.c          molcas024001.png  surface.h
center16.xpm    gv_atoms.h      gv_gtk_help.c        icon_revert20.xpm    molcas024001.xpm  TODO
center.xpm      gveps.c         gv_gtk_make_graph.c  install_diffdens.sh  mystring.c        vstring.c
CITATION.txt    gveps.h         gv_gtk_multiview.c   INSTALL.txt          pixeldata.c       vstring.h
CMakeLists.txt  gv_fragments.c  gv_gtk_multiview.h   License.txt          plugins/          write_file.c
config_files/   gv_functions.h  gv_gtk_xyz_editor.c  luscus_gtk_handle.c  plugins_babel/
DOC/            gv_geom.c       gv.h                 luscus_gtk_util.c    read_file.c
findsym.c       gv_glx.h        gv.h.in              luscus.h.in          read_file_org.c
findsym.h       gvgrp.c         gv_menubar.c         main.c               screenshot.c
gtk_gui.c       gvgrp.h         gv_menubar_org.c     mcube.c              stdext.c

Again, we see that there is an INSTALL.txt file containing installation instructions. This one is a little more verbose so we won’t be using as reference today, but it contains all of the same information. Two other items to note in this directory are the CMakeLists.txt file, and the build directory, both of which are part of the cmake build procedure. The CMakeLists.txt file contains the logic for finding dependencies and configuring the build, and the build directory is typically where we work from when building a cmake project. It is important not to build the code directly in the root directory of the project with cmake, as this can cause all sorts of errors. So the standard approach is to build the software from a build subdirectory. Often you will have to make it yourself, but this project provides it for us.

Continuing with the installation procedure, let’s move into the empty build directory:

$ cd build

The cmake tool is installed as a module, so we need to load it. The most basic way to call cmake is to run:

$ module load cmake
$ cmake DIR

where DIR is the location of the CMakeLists.txt file. In this case, that’s the parent directory of build, so we would call

$ cmake ..

We also need to specify any installation options that we want at this point. The equivalent to the --prefix command is -DCMAKE_INSTALL_PREFIX, and we can set the compilers to use via environment variables as we did with make. To see all of the options available, we can run the command:

$ ccmake ..

This will drop us into a menu where we can see the options that cmake will accept. Once in this menu we need to hit c to generate the list of properties. Once this is done we can hit t to show the full list of variables used for this build. In this case we will only be changing the install prefix, so we can hit q to quit without making changes.

This project can use gcc as well, so we can use the same procedure as before to set the CC environment variable, as well as the CXX environment variable for compiling both C and C++ code.

$ module load gcc/7.2.0
$ export CC=$(which gcc)
$ export CXX=$(which g++)
$ cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/software.install/luscus/0.8.6
$ make
$ make install

This software also needs some configuration files to be copied over to the installation directory that aren’t included in the operations run by make install. This isn’t uncommon, so watch out for this in the developer’s installation documentation. In this case, we can copy over the needed files via:

$ cp -r config_files $HOME/software.install/luscus/0.8.6/luscus_homedir

Once the installation step is complete, we can create our modulefile as before:

$ mkdir ~/modulefiles.local/luscus
$ nano ~/modulefiles.local/luscus/0.8.6
#%Module
module load gcc/7.2.0

set basedir "/users/3/dunn0404/software.install/luscus/0.8.6"

prepend-path PATH $basedir/bin
setenv LUSCUS_DIR $basedir/luscus_homedir

This modulefile adds a couple of new bits of information. First, we didn’t include cmake as one of the modules to load as a dependency. That is because cmake is almost never used when actually running the software it builds, so it is safe to omit. Second, we are setting an internal variable to the modulefile with the set directive. This is because we want to modify two environment variables, and we can save typing and reduce chances for error by storing the root directory of the luscus install to basedir, and referencing it later in the file with $basedir. Finally, we are introducing the setenv directive, which creates a new environment variable (or completely overwrites an old one) to se the value of LUSCUS_DIR, which is a variable needed by luscus at runtime.

And finally we can record the install procedure in our recipe file:

$ nano ~/software.source/luscus/README.recipe
# dunn0404 - April 12, 2022
# luscus v0.8.6 with gcc/7.2.0

wget https://downloads.sourceforge.net/project/luscus/luscus_0.8.6.tar.gz
tar xvf luscus_0.8.6.tar
cd luscus_0.8.6/build

module load cmake
module load gcc/7.2.0

export CC=$(which gcc)
export CXX=$(which g++)

cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/software.install/luscus/0.8.6
make
make install

Key Points

  • CMake is a more modern and advanced build system that is popular for packages with complex sets of dependencies.

  • We can still customize it to our needs for the same modulefile workflow we’ve been using.