VTK/Build System Migration

From KitwarePublic
< VTK
Jump to: navigation, search

The CMake based build system was rewritten for VTK 6, using a modular approach creating smaller libraries with less inter-library dependencies. This means that you will need to port your VTK based application's build system to work with the new library layout. The libraries are arranged in two levels, the top-level resembles the layout of the kits in VTK 5.x, while the second level provides a greater degree of granularity.

Many classes that were in vtkCommon are now in vtkCommonCore, with some moving out to vtkCommonSystem and other modules. Moving between the class location to the library name is quite simple, all classes in Common/Core/ will be in the vtkCommonCore library for example, and the vtkRenderingOpenGL library's classes can be found in Rendering/OpenGL. Some modules such as vtkRenderingCore provide the interface classes, but require a concrete implementation module to do anything useful (such as vtkRenderingOpenGL). The implementation modules use the vtkObjectFactory to override the base classes with platform specific classes.

Building Against VTK 6

If you are building against VTK 6.0 a few things have changed as a result of the modularization and updates to the CMake infrastructure.

Finding and Linking to VTK

If you just want to find VTK and link to everything that was built not too much has changed. The NO_MODULE argument in the find_package call is there simply to avoid using the FindVTK.cmake file which has not been necessary since VTK 5.x and is certainly not desired for VTK 6. A simple CMakeLists.txt to accomplish this and build an application would be:

cmake_minimum_required(VERSION 2.8.7)
project(vtkApplication)
find_package(VTK 6.0 REQUIRED NO_MODULE)
include(${VTK_USE_FILE})
add_executable(myApplication application.cxx)
target_link_libraries(myApplication ${VTK_LIBRARIES})

This will work against a VTK build tree, or an installed VTK tree. It will find all modules built by VTK, and link to all of the C++ libraries produced. Including the VTK_USE_FILE will set up the include directories for the project, and add the appropriate compiler definitions to automatically initialize the object factory overrides. This is a reasonable starting point, but is not normally desired for applications in production. It is quite easy to find and link to only the components required, ensuring that they were built and only linking to the appropriate libraries.

cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(vtkApplication)
find_package(VTK 6.0 COMPONENTS vtkChartsCore vtkViewsContext2D NO_MODULE)
include(${VTK_USE_FILE})
add_executable(myApplication application.cxx)
target_link_libraries(myApplication ${VTK_LIBRARIES})

The above snippet will find the vtkChartsCore, vtkGUISupportQt and vtkViewsContext2D, along with all of their dependencies. It will only add their include directories (along with their dependencies), and the appropriate compiler definitions to initialize the object factories. This is probably the most appropriate way to find and link to VTK 6 in the majority of projects.

It is possible to manually specify the libraries to be linked to, avoiding the directory scope compiler definitions and include directories, as all of the appropriate settings are loaded into CMake variables.

cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(vtkApplication)
find_package(VTK 6.0 COMPONENTS vtkChartsCore vtkViewsContext2D NO_MODULE)
add_executable(myApplication application.cxx)
set_property(TARGET myApplication APPEND
  PROPERTY COMPILE_DEFINITIONS "${VTK_DEFINITIONS}")
set_property(TARGET myApplication APPEND
  PROPERTY INCLUDE_DIRECTORIES ${VTK_INCLUDE_DIRS})
target_link_libraries(myApplication ${VTK_LIBRARIES})

The VTK libraries are exported by CMake, and so can be referred to as any normal target. The correct library will be linked to for debug/release, along with any required interface libraries. The find_package(VTK) command is safe to call multiple times in the same project, and allows different components to be selected in different applications or libraries in the same project.

Optional Module Dependencies

If you wish to optionally depend on a module you should list all hard dependencies in the COMPONENTS section as before, but you can check for the existence of the module target, as shown below.

cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(vtkApplication)
find_package(VTK 6.0 COMPONENTS vtkChartsCore vtkViewsContext2D NO_MODULE)
include_directories(${VTK_INCLUDE_DIRS})
if(TARGET vtkRenderingVolumeOpenGL)
  message(STATUS "Building optional volume rendering component")
  find_package(VTK 6.0 COMPONENTS vtkChartsCore vtkViewsContext2D 
    vtkRenderingVolumeOpenGL)
  set(volumeRenderer volumerenderer.cxx)
endif()
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS ${VTK_DEFINITIONS})
add_executable(myApplication application.cxx ${volumeRenderer})
target_link_libraries(myApplication ${VTK_LIBRARIES})

This is the most reliable way to detect if a given module has been built, you can optionally call find_package(VTK) with the additional module. The concept of optional components was added in CMake 2.8.8, this could be added to the CMake scripts in VTK to make optional module dependencies a little simpler. If find_package is not called again then the compiler definitions and VTK_LIBRARIES variable will not necessarily take care of correctly initializing the module if required.

Implementation Modules

Most of the rendering in VTK is done with OpenGL, and platform specific interactor/window classes are needed. In VTK this is accomplished using a combination of interface classes written in a platform independent fashion, that are then overridden at runtime using the vtkObjectFactory mechanism. The classes in vtkRenderingCore are normally used in application code, such as vtkRenderWindow, but a platform specific one will be returned by the class's New method. The pure interface classes will return a null pointer from their New method if no valid override was in place.

The application code must list vtkRenderingOpenGL in its components to use OpenGL for rendering and ensure the compiler definitions are added to the target (simplest way to achieve this is to include the VTK_USE_FILE). This ensures that the object factory for the vtkRenderingOpenGL module will be initialized and used whenever classes from vtkRenderingCore are included in your application code. If you notice the interface classes in these modules returning NULL pointers it is likely that one of these implementation modules providing appropriate overrides is missing from the dependency list. Some common implementation modules you may want to use include,

  • vtkRenderingCore has several implementation modules:
    • vtkRenderingOpenGL
    • vtkRenderingFreeTypeOpenGL
    • vtkInteractionStyle
  • vtkRenderingVolume provides interface, with one implementation
    • vtkRenderingVolumeOpenGL
  • vtkIOSQL also has several implementation modules:
    • vtkIOMySQL
    • vtkIOPostgreSQL

As all dependencies are recursive, simply adding vtkRenderingOpenGL to COMPONENTS will add vtkRenderingCore and any other dependencies. Other modules, such as the SQL modules, add new functionality if they are linked to. In the case of the SQL modules, the ability to link to MySQL databases is added. These modules use alternative APIs to register additional database drivers at link time, but the same compiler definition mechanism ensures that they are initialized upon application startup.

Common Implementation Modules

Most applications that wish to use the OpenGL rendering module in VTK will need to link to vtkRenderingOpenGL, in addition vtkRenderingVolumeOpenGL is required for volume rendering. A line such as the following would find the commonly used implementation modules mentioned in the previous section.

cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(vtkApplication)
find_package(VTK 6.0 COMPONENTS vtkRenderingOpenGL vtkInteractionStyle vtkRenderingVolumeOpenGL vtkRenderingFreetypeOpenGL NO_MODULE)
include_directories(${VTK_INCLUDE_DIRS})
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS ${VTK_DEFINITIONS})
add_executable(myApplication application.cxx)
target_link_libraries(myApplication ${VTK_LIBRARIES})

How Implementation Modules Are Initialized

When you add an implementation module to the DEPENDS, COMPILE_DEPENDS, or TEST_DEPENDS section of a CMake vtk_module() call inside a module.cmake file, CMake adds a compiler definition to the compiler command line for all the files in the module (or, for TEST_DEPENDS, to the files in the test harness). As an example, say we have added vtkRenderingOpenGL as a dependency to a new module named vtkSuperDuper. Every file in vtkSuperDuper will be compiled with

-DvtkRenderingCore_AUTOINIT="1(vtkRenderingOpenGL)"

passed to the compiler. If any file in vtkSuperDuper #includes a class from vtkRenderingCore, then vtkRenderingCoreModule.h will be included and its VTK_AUTOINIT macro will expand into a file-static global struct whose constructor calls an initialization function in the vtkRenderingOpenGL library (thus introducing a link-time dependency on vtkRenderingOpenGL). Any code in vtkSuperDuper which calls upon the object factory to produce a vtkRenderWindow (except code used to initialize static variables, because the order of initialization of static variables is not guaranteed) will have ensured that the OpenGL render window subclass is already registered with the object factory.

An alternative initialization method was added if not using the compiler definitions, the two can safely be combined without issue. The following snippet of code placed in a compiled source file in your application executable would initialize the factories for the vtkRenderingOpenGL and vtkInteractionStyle module (you must ensure you also link to those modules).

 #include <vtkAutoInit.h>
 VTK_MODULE_INIT(vtkRenderingOpenGL);
 VTK_MODULE_INIT(vtkInteractionStyle);

This design allows "interface" libraries that define abstract classes to construct concrete subclasses at run time without themselves being linked to "implementation" libraries that define the concrete subclasses.