Structural Bioinformatics Library
Template C++ / Python API for developping structural bioinformatics applications.
Developers Using SBL: Tutorial

Tutorial guiding a developer for creating programs using the SBL library.

Introduction

As seen from Fig. fig-intro-sbl-architecture , the SBL is a library providing various resources for developers. The goal of this tutorial is to help developers willing to develop programs based on the SBL library. In the following, each section focuses on a particular task:

– in section Compiling with SBL : compiling a program using the SBL library,

– in section Creating one's programs based on existing Applications : using the existing Applications with different Models, or user-defined Models ,

– in section Creating Workflow, Modules and Loaders : creating an application using the Modules framework.

Compiling with SBL

As mentioned in the Installation Guide , CMake is used to compile programs based on the SBL library. CMake uses a configuration file called CMakeLists.txt, from which the makefile monitoring the compilation of individual targets is created.

Given a CMakeLists.txt file, compiling a program that uses the SBL is only few commands. Assuming that your working directory contains the CMakeLists.txt file, and that the SBL library is installed in a standard location :

> mkdir build; cd build;
> cmake ..
> make

If you are working directly with an uninstalled version of the SBL (a cloned repository, or a downloaded tarball), you have to define the path to the root directory of the SBL library when running the cmake command :

> cmake .. -DSBL_DIR=</path/to/sbl/dir>

If you installed the SBL library in a non-standard location, you have to tell CMake where to find the configuration files for the SBL :

> cmake .. -DCMAKE_MODULE_PATH=</path/to/install/sbl/dir>/share/cmake/Modules

Here is an example configuration file used to compile files found in the standard distribution:

The different functions used in the CMakeLists.txt file are:

cmake_minimum_required(VERSION 2.6)

This function checks whether the current version of CMake is at least 2.6.

set(SBL_USE_LIBS ESBTL CGAL MPFR GMP Boost)

This function sets all the libraries used by the SBL libraries for compiling the programs, as explained in section Compiling SBL programs using external libraries .

find_package(SBL)

This function seeks a file SBLConfig.cmake on standard path of cmake, or in a directory pointed by the environment variable SBL_DIR, as explained in section Finding the SBL location .

include(${SBL_USE_FILE}) 

This function sets a number of flags for including headers and linking to libraries during the compilation, as explained in section Setting Include and Link .

create_sbl_program(example_alpha_complex_of_molecular_model)

This function creates a target in the make file for compiling the example_alpha_complex_of_molecular_model.cpp source file, as explained in section Creating a program .

Compiling SBL programs using external libraries

General mechanism to use a library/libraries.

We use make, and therefore assume that the library to be used, say LIB_NAME, provides a general config file named LIB_NAMEConfig.cmake. The cmake method find_package() indeed searches this file to the resources of a library.

In the following, we review the mechanism used in the SBL to make the process of using libraries homogeneous for all libraries.

The general idea is to list all the libraries used in a variable named SBL_USE_LIBS. To this end, we define two files for each library: a Find file, and a Use file.

The Find file: FindLIB_NAME.cmake

In a nutshell, this file checks several options so as finally to identify (i) the directory containing the included files provided by the library, and (ii) the lib files (.so, .a) provided by the library.

  • If LIB_NAME is located in a standard system location, nothing specific has to be done.
  • If not, one should define environment variables LIB_NAME_DIR or the like, which are used within FindLIB_NAME.cmake so as to locate the included and lib files.

The Use file: SBL_Use<LIB_NAME>.cmake

Assume that the library has been found, so that the INCLUDE_DIR AND LIBRARY_DIR (or LIBRARIES_DIR) directories are known. Using these directories, this file defines the resources of the library:

  • Include files. One defines the include directory:
    include_directories(${LIB_NAME_INCLUDE_DIR})
    
  • Link files. We use the common (shared by all libraries) variable SBL_COMMON_LIBS to aggregate info on all the libraries used:
link_directories(${Marmote_LIBRARIES_DIR})
set(SBL_COMMON_LIBS ${SBL_COMMON_LIBS}   LIB_NAME_file1.so/a  LIB_NAME_file2.so/a

The following example shows the file SBL_UseGMP.cmake to use the GMP library within the SBL:

message(STATUS "Looking for GMP library...")
find_package(GMP)
if(GMP_FOUND)
message(STATUS "GMP library found: ${GMP_LIBRARIES_DIR}")
include_directories("${GMP_INCLUDE_DIR}")
link_directories("${GMP_LIBRARIES_DIR}")
if(BUILD_STATIC_SBL)
message(STATUS "Using static GMP")
if("${WIN32}" OR "${WIN64}")
file(GLOB gmp_lib_file RELATIVE "${GMP_LIBRARIES_DIR}" "${GMP_LIBRARIES_DIR}/*gmp*.lib")
if(gmp_lib_file)
set(GMP_LINK "${GMP_LIBRARIES_DIR}/${gmp_lib_file}")
message(STATUS "Found library : ${GMP_LINK}")
endif()
else()
set(GMP_LINK "${GMP_LIBRARIES_DIR}/libgmp.a")
endif()
else()
set(GMP_LINK gmp)
endif()
if(SBL_COMMON_LIBS)
set(SBL_COMMON_LIBS "${SBL_COMMON_LIBS};${GMP_LINK}")
else()
set(SBL_COMMON_LIBS ${GMP_LINK})
endif()
set(SBL_WITH_GMP ON)
else()
set(SBL_WITH_GMP OFF)
endif()
The cmake method target_link_libraries allows to link the libraries, but in order to have an homogeneous way to link all the libraries during the compilation, using the cmake variable SBL_COMMON_LIBS) is recommended.


If the files are not in the standard path, it is possible to indicate the directories that contain them using the cmake variable CMAKE_MODULE_PATH.


The CMakeList.txt

While a CMakeList can naturally use all the primary command seen above, namely find_package(), link_directories(), target_link_libraries(), etc, the mechanisms presented above eases the process. Indeed, instructions specific to a library have been factored out in its Find and Use files.

Using these, a typical CMakeList merely assembles all these resources:

  • List the libs used:
    set(SBL_USE_LIBS Boost LIB_NAME1 LIB_NAME2 LIB_NAME3)
    
  • Run find_package(LIB_NAME) for all libs, in order to update the include and link resources.
  • Define executable and commands: add_executable(), target_link_libraries(), etc.

The developer is referred to CMakeList files present in all packages, e.g. those from the Applications group.

Finding the SBL location

In order to find the location of the SBL library, the cmake method find_package is used: this method looks for a SBLConfig.cmake file (or possibly a FindSBL.cmake file) in the cmake path. As mentioned in section Compiling SBL programs using external libraries , if one's copy of the SBL s not in a standard path, the cmake variable CMAKE_MODULE_PATH lists all the directories where CMake should look for these files.

The following example is the SBLConfig.cmake file implemented in the library, to find all the cmake configuration files related to the SBL:

# This files contains definitions needed to use SBL in a program.
# DO NOT EDIT THIS.
# This file is loaded by cmake via the command "find_package(SBL)"
#
# This file correspond to a possibly out-of-sources SBL configuration, thus the actual location
# must be given by the cmake variable or environment variable SBL_DIR.
set(SBL_INTERNAL_ARCHITECTURE OFF)
set(SBL_FOUND TRUE)
if(EXISTS ${SBL_DIR}/UseSBL.cmake)
set(SBL_CMAKE_DIR "${SBL_DIR}")
set(SBL_USE_FILE "${SBL_CMAKE_DIR}/UseSBL.cmake")
else()
if(EXISTS ${SBL_DIR}/cmake/Modules/UseSBL.cmake)
set(SBL_CMAKE_DIR "${SBL_DIR}/cmake/Modules")
set(SBL_USE_FILE "${SBL_CMAKE_DIR}/UseSBL.cmake")
else()
if(EXISTS ${SBL_DIR}/share/cmake/Modules/UseSBL.cmake)
set(SBL_CMAKE_DIR "${SBL_DIR}/share/cmake/Modules")
set(SBL_USE_FILE "${SBL_CMAKE_DIR}/UseSBL.cmake")
endif()
endif()
endif()

When found, this file is processed and a cmake variable SBL_FOUND is created and set to ON, indicating that the library was correctly found. In addition, it sets a number of cmake variables for finding all other files used by CMake with the SBL library. In particular, it sets the cmake variable SBL_USE_FILE to the location of the file UseSBL.cmake that will include all the dependencies of the SBL library.

Setting Include and Link

As mentioned in section Finding the SBL location, including the file pointed by the cmake variable SBL_USE_FILE will configure most of the other cmake variables required for compiling a program. In particular, it does the following tasks:

  • setting the version number of the SBL library,
  • setting the compile mode to Release if no one has been given,
  • including the header SBLConfig.h used in all applications,
  • configuring all the external libraries by including the headers and configuring the link to the programs,
  • including all headers from the Core and Models parts of the SBL library,
  • including the cmake methods designed specially for the SBL library,
  • adding all the possible warnings (excepting the unused local typedefs warning) if compiling in debug mode,
  • including the mandatory flags for compiling in static mode, if the cmake variable BUILD_STATIC_TYPE was defined and set to ON.

All these operations are done in cmake/Modules/UseSBL.cmake:

cmake_policy(SET CMP0011 NEW)
cmake_policy(SET CMP0012 NEW)
cmake_policy(SET CMP0054 NEW)
#If Win32, forbids the use of some packages(like Hierarchical Serialization)
if("${WIN32}" OR "${WIN64}")
add_definitions(-DWINDOWS_PLATFORM)
set(CMAKE_CXX_FLAGS "/EHsc")
endif()
# List of options
#add_compile_options(-Wall -Wextra -pedantic -Werror)
#add_compile_options(-Wall -Werror)
#set (CMAKE_CXX_STANDARD 11)
#set the version number to add to all applications
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/VERSION)
file (STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/VERSION SBL_VERSION)
else()
set(SBL_VERSION "2.1.0")
endif()
string(REPLACE "." ";" SBL_VERSION_LIST ${SBL_VERSION})
list(GET SBL_VERSION_LIST 0 SBL_VERSION_MAJOR)
add_definitions(-DSBL_VERSION_MAJOR=${SBL_VERSION_MAJOR})
list(GET SBL_VERSION_LIST 1 SBL_VERSION_MINOR)
add_definitions(-DSBL_VERSION_MINOR=${SBL_VERSION_MINOR})
list(GET SBL_VERSION_LIST 2 SBL_VERSION_PATCH)
add_definitions(-DSBL_VERSION_PATCH=${SBL_VERSION_PATCH})
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode...")
include_directories("${PROJECT_BINARY_DIR}")
include(${SBL_CMAKE_DIR}/SBL_UseLibraries.cmake)
include(${SBL_CMAKE_DIR}/SBL_UseOptionalLibraries.cmake)
include(${SBL_CMAKE_DIR}/SBL_UseCore.cmake)
include(${SBL_CMAKE_DIR}/SBL_UseModels.cmake)
include(${SBL_CMAKE_DIR}/SBL_CreateSBLProgram.cmake)
if(BUILD_STATIC_SBL)
message(STATUS "Building STATIC executables...")
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Building as most as possible STATIC executables...")
else()
if("${WIN32}" OR "${WIN64}")
message(STATUS "Building as most as possible STATIC executables...")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
if(CMAKE_EXE_LINKER_FLAGS)
set(CMAKE_EXE_LINKER_FLAGS "-static ${CMAKE_EXE_LINKER_FLAGS}")
else()
set(CMAKE_EXE_LINKER_FLAGS "-static")
endif()
endif()
endif()
endif()
if(NOT "${WIN32}" AND NOT "${WIN64}" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(SBL_COMMON_LIBS ${SBL_COMMON_LIBS} pthread)
endif()
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
#Traits class defines types that are not used locally, so we desactive these warnings
#In the CGAL mesher, there are unused variables ?
#add_definitions(-Wall -Wno-unused-local-typedefs -Wno-unused-variable)
add_definitions(-Wall -Wno-unused-local-typedefs)
endif()
if(SBL_OPENMP)
message(STATUS "Using OpenMP...")
find_package(OpenMP)
if(OPENMP_FOUND)
set(CMAKE_EXE_LINKER_FLAGS " ${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_CXX_FLAGS}")
add_definitions(-DSBL_OPENMP)
else()
message(STATUS "OpenMP not found !")
endif()
endif()
if(SBL_PROFILING)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
add_definitions(-pg -g)
endif()
if(SBL_MISSING_DEPENDENCIES)
message(STATUS "WARNING !!! The following libraries are missing and some of the executables will not be compiled : ${SBL_MISSING_DEPENDENCIES}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
add_definitions(-pg -g)
endif()

Using Pre-compiled high level functionalities

It is possible to use pre-compiled functionalities of the SBL by adding SBL to the list of librairies SBL_USE_LIBS. The incentive to do so is twofold:

  • Reduce the compilation time – main motivation.
  • Enjoy the high level functionalities available from the headers under include/SBL/lib . Note that for a given package, the resources made available this way (cpp files and headers) are located the lib/src and include/SBL/lib subfolders of the package. As an example, one may check Molecular_potential_energy .

Creating a program

We provide two methods to create a program using the SBL library–see also illustrations in section Compiling with SBL :

  • create_sbl_program : takes at least one argument, that is the name of the main source file (.cpp) without extension; all other optional arguments are other source files (.cpp) required to compile the main source file.
  • create_sbl_application : does the same, but offers in addition the possibility to install the resulting program, i.e to copy it in a bin target directory.

In both cases, a program will be created with the name of the main source file, and the extension .exe. It is also in this method that all the libraries listed in SBL_USE_LIBS (or the flags in SBL_COMMON_LIBS) are linked.

These methods are defined in the file cmake/Modules/SBL_CreateSBLProgram.cmake:

#Creates a target <arg_1>.exe from the main source file <arg_1>.cpp,
#and possibly linking to all other optional sources <arg_n>.cpp
function(create_sbl_program arg)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${arg}.cpp")
set(SBL_LOCAL_CPPS ${arg}.cpp)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${arg}.c")
set(SBL_LOCAL_CPPS ${arg}.c)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${arg}.C")
set(SBL_LOCAL_CPPS ${arg}.C)
endif()
foreach(f ${ARGN})
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${f}.cpp")
set(SBL_LOCAL_CPPS ${SBL_LOCAL_CPPS} ${f}.cpp)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${f}.c")
set(SBL_LOCAL_CPPS ${SBL_LOCAL_CPPS} ${f}.c)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${f}.C")
set(SBL_LOCAL_CPPS ${SBL_LOCAL_CPPS} ${f}.C)
endif()
endforeach()
if(WIN32 OR WIN64)
set(program_name ${arg})
else()
set(program_name ${arg}.exe)
endif()
add_executable(${program_name} ${SBL_LOCAL_CPPS})
set_target_properties(${program_name} PROPERTIES CXX_STANDARD 17)
set_target_properties(${program_name} PROPERTIES CXX_STANDARD_REQUIRED ON)
if(BUILD_STATIC_SBL)
set_target_properties(${program_name} PROPERTIES LINK_SEARCH_START_STATIC 1)
set_target_properties(${program_name} PROPERTIES LINK_SEARCH_END_STATIC 1)
endif()
target_link_libraries(${program_name} ${SBL_COMMON_LIBS})
install(TARGETS ${program_name} DESTINATION bin COMPONENT apps)
#install(TARGETS ${program_name} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" COMPONENT apps)
endfunction()
#Identical to the previous function
#Nb: initially, containing the install() command, while create_sbl_program did not
function(create_sbl_application arg)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${arg}.cpp")
set(SBL_LOCAL_CPPS ${arg}.cpp)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${arg}.c")
set(SBL_LOCAL_CPPS ${arg}.c)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${arg}.C")
set(SBL_LOCAL_CPPS ${arg}.C)
endif()
foreach(f ${ARGN})
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${f}.cpp")
set(SBL_LOCAL_CPPS ${SBL_LOCAL_CPPS} ${f}.cpp)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${f}.c")
set(SBL_LOCAL_CPPS ${SBL_LOCAL_CPPS} ${f}.c)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${f}.C")
set(SBL_LOCAL_CPPS ${SBL_LOCAL_CPPS} ${f}.C)
endif()
endforeach()
if(WIN32 OR WIN64)
set(program_name ${arg})
else()
set(program_name ${arg}.exe)
endif()
add_executable(${program_name} ${SBL_LOCAL_CPPS})
set_target_properties(${program_name} PROPERTIES CXX_STANDARD 17)
set_target_properties(${program_name} PROPERTIES CXX_STANDARD_REQUIRED ON)
if(BUILD_STATIC_SBL)
set_target_properties(${program_name} PROPERTIES LINK_SEARCH_START_STATIC 1)
set_target_properties(${program_name} PROPERTIES LINK_SEARCH_END_STATIC 1)
endif()
target_link_libraries(${program_name} ${SBL_COMMON_LIBS})
install(TARGETS ${program_name} DESTINATION bin COMPONENT apps)
# install(TARGETS ${program_name} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" COMPONENT apps)
endfunction()
function(create_sbl_project_application arg1 arg2)
if(NOT TARGET ${arg1})
add_custom_target(${arg1})
endif()
create_sbl_application(${arg2})
add_dependencies(${arg1} ${arg2}.exe)
endfunction()
#Creates a target <arg_1>.exe using visual components such as Qt,
#OpenGL and QGLViewer. It works exactly as create_sbl_program, but
#uses the qt4 precompilation wrappers on all local .ui files and the
#main .cpp file.
function(create_sbl_visual_program arg)
set(SBL_LOCAL_CPPS ${arg}.cpp)
foreach(f ${ARGN})
set(SBL_LOCAL_CPPS ${SBL_LOCAL_CPPS} ${f}.cpp)
endforeach()
file(GLOB UI_FILES "${CMAKE_SOURCE_DIR}/*ui")
qt4_wrap_ui(uis ${UI_FILES})
qt4_automoc(${arg}.cpp)
add_executable(${program_name} ${uis} ${SBL_LOCAL_CPPS})
set_target_properties(${program_name} PROPERTIES CXX_STANDARD 17)
set_target_properties(${program_name} PROPERTIES CXX_STANDARD_REQUIRED ON)
target_link_libraries(${program_name} ${SBL_COMMON_LIBS})
endfunction()

Advanced CMake Configuration

Profiling.

In order to use gprof for profiling the code, it is possible to use the CMake option SBL_PROFILING :

cmake . -DSBL_PROFILING=ON

This option will add the necessary links to use gprof. The generation of the "gmon.out" file which contains the execution information requires compiling and running the executable:

make <executable.exe>
./<executable.exe>

Finally, one runs gprof and saves the output :

gprof <executable.exe> > myprofile.txt

gprof has many options, see : gprof page.

Creating one's programs based on existing Applications

All the Applications in the SBL library are instantiated with one or more C++ models, leading to different programs solving the same problem, with different input or different algorithms.

Reusing C++ classes from the SBL, developers may create programs re-using the main classes defined for the Applications , in two ways:

Re-using existing SBL C++ models

An application in SBL is based on two important classes:

  • the Workflow class, that is the C++ class that defines how loaders and modules are combined to run the calculations,
  • the Traits class, that is the C++ class that defines the types used by the Workflow for instantiating the modules and loaders.

Usually, the Traits class is a C++ template class, the template arguments being some C++ concepts for which we provide C++ models in Models. For example, the Traits class T_Space_filling_model_interface_traits for the application Space_filling_model_interface have five template parameters, each of them being the C++ model of a C++ concept defined in Models. Thus, developing a program for an application consists on creating a unique source file (.cpp) that:

  • instantiates the Traits class of the application with the C++ models corresponding to the required C++ concepts,
  • instantiates the Workflow class with the instantiated Traits class,
  • defines the main method where an object of the class Workflow is created and the method start of the class Workflow is called.

Here is an example of a program for the Space_filling_model_interface application that computes the interface in a binary complex made of atoms, each atom being annotated with a name and a radius:

To use this code with different C++ models, it is sufficient to replace the corresponding C++ model of interest when instantiating the Traits class. The usable C++ models for each template parameter of the Traits class are described in the reference manual of the Traits class. Note that each template parameter is documented and refers to the C++ concept it represents in the Models documentation.

Developing SBL C++ Models

Developing a C++ model consists of developing a C++ class that follows a C++ concept described in one of the packages in Models . For example, the C++ concept ParticleTraits defines types defining a particle. A C++ model of this C++ concept is the class SBL::Models::T_Atom_with_flat_info_traits, that represents an atom where the coordinates are represented with double number type. For adding a radius to an atom, one should use the class SBL::Models::T_Atom_with_flat_info_and_annotations_traits that adds to the atom an annotated name and an annotated radius. In practice, the class SBL::Models::T_Atom_with_flat_info_and_annotations_traits inherits from the class SBL::Models::T_Atom_with_flat_info_traits and redefines some types related to the atom.

All the requirements of a C++ concept is described in the user manual of the corresponding package in Models. Furthermore, each time that a class has as template argument a C++ concept represented by a package in Models, this template argument is documented and references the corresponding package in Models.

Creating Workflow, Modules and Loaders

All SBL applications are designed with a workflow class (see class SBL::Modules::Module_based_workflow in package Module_base). Such as class registers modules and loaders, and links them in a directed graph describing the flow of calculations along the run process. The main class to use is the class SBL::Modules::Module_based_workflow , which offers all the functionalities to create and manage the previous graph.

To create one's loaders and modules, it is sufficient to inherit from the base classes SBL::IO::Loader_base and SBL::Modules::Module_base. These classes have pure virtual methods, so that selected methods will have to be re-implemented in order to use them in the workflow.

For a detailed description of the classes SBL::Modules::Module_based_workflow, SBL::IO::Loader_base and SBL::Modules::Module_base, and their underlying mechanisms, we refer you to the user manual of the package Module_base.