Static linking under CMake

This page describes how static linking is achieved for VisIt and specifically some of the mechanisms in VisIt's CMake build system that make it possible.

For static linking to work, all 3rd party dependencies must themselves be created as static libraries. When VisIt's components are linked, they must be linked against static 3rd party libraries and static VisIt libraries which are generated during the build process.

Building VisIt 3rd Party Libraries

You can use the build_visit script to build VisIt's 3rd party library dependencies statically. You can build most libraries but there are some I/O libraries that must be excluded from a static build because they contain duplicate symbols that interfere with static linking.

 build_visit --console --static --no-visit --thirdparty-path ~/Development/thirdparty_static \
             --all-io --no-boxlib --no-itaps --no-ccmio --no-hdf4 --makeflags -j8

Configuring

To produce a static build of VisIt, you must use the VISIT_STATIC flag when configuring cmake.

cmake -DVISIT_STATIC:BOOL=ON .

Details

When VisIt builds statically all of its libraries are included into the executable components (gui, viewer, vcl, engine, mdserver, and so on). For components that load plugins, this also means that plugins are compiled into the components. The usual PluginManager objects that load plugins are still used but they are slightly modified such that instead of reading a plugin directory to load plugins, they call user-supplied functions that supply the list of available plugins and return pointers to the various plugins. These functions are known as "Static Symbol Locators" (SSL) and they are compiled into the components that load plugins. Some of the sources that make up these SSLs are generated by CMake at configure time.

SSL generation

The SSLs include generated header files called:

  • enabled_databases.h
  • enabled_plots.h
  • enabled_operators.h

The SSL for a component includes the header files and uses the preprocessor to expand their contents out into tables and code that let the SSL return the relevant plugin information.

These header files are automatically generated by cmake as part of the configure process and include a list of the plugins that are enabled. Plugins that are not enabled (such as when an I/O library is missing) are not included in the header files. The code to generate these headers is located in plots/CMakeLists.txt, operators/CMakeLists.txt, and databases/CMakeLists.txt. In addition, those CMakeLists.txt files also set up variables containing the plugin library names that can be used later for linking and for dependencies. For example, the variable gui_exe_GPlot will contain the names of all libG plugins that must be linked with the GUI in order to produce a static GUI. The variables are set up and then used later in the gui/main/CMakeLists.txt, viewer/main/CMakeLists.txt and so on. The configuration process for static builds is split up a little differently than dynamic so that plugins are configured before the main program of the component that loads them.

SSL anatomy

An SSL is defined in a C++ code file next to the main.C of the program with which it will be linked. The name of the file will be of the form: *StaticSymbolLocator.C. The SSL must implement 2 functions in order to perform its job: fake_dlsym and StaticGetSupportedLibs.

fake_dlsym

This function is used by the PluginManager object to obtain symbols from a plugin, which in the static case turns out to be a lookup.

StaticGetSupportedLibs

The PluginManager uses this function to obtain a vector of available plugins names for a given plugin type.

CMakeLists.txt

The main CMakeLists.txt that links a component that uses plugins must be sure to link plugins into the component by using variables that have been set up beforehand in the plugin CMakeLists.txt file. It is also important to set up dependencies that ensure the needed plugins are built before the component is linked otherwise linking will fail.

Step 1 - Add the SSL code to the executable when building statically

IF(VISIT_STATIC)
    SET(GUI_STATIC_SOURCES GUIStaticSymbolLocator.C)
ENDIF(VISIT_STATIC)

...

ADD_EXECUTABLE(gui_exe ${GUI_SOURCES} ${GUI_STATIC_SOURCES})

Step 2 - Make sure the executable links with the right plugins

TARGET_LINK_LIBRARIES(gui_exe 
gui
${gui_exe_IOperator}
${gui_exe_GOperator}
${gui_exe_IPlot}
${gui_exe_GPlot}
)

(The name of the variables to use are of the form: ${target}_${component}${plugintype} where target is the name of the executable target, component is in [I,G,S,V,E,M], and plugintype is one of Database,Operator,Plot.)

Step 3 - Make sure to create dependencies on the plugins

# If we're building statically then the gui can't be linked until the plugin
# sources are built
IF(VISIT_STATIC)
    ADD_DEPENDENCIES(gui_exe
        ${gui_exe_IOperator}
        ${gui_exe_GOperator}
        ${gui_exe_IPlot}
        ${gui_exe_GPlot}
    )
ENDIF(VISIT_STATIC)

Important variables

  • ALL_THIRDPARTY_IO_LIBRARY_DIR = Contains all 3rd party I/O library directories needed for linking
  • ALL_THIRDPARTY_IO_LIB = Contains the name of all 3rd party I/O libraries needed for linking