# Module Migration VTK 8.2 and older contained a module system which was based on variables and informed CMake's migration to target-based properties and interactions. This was incompatible with the way VTK ended up doing it. With VTK 9, its module system has been reworked to use CMake's targets. This document may be used as a guide to updating code using old VTK modules into code using new VTK modules. ## Using modules If your project is just using VTK's modules and not declaring any of your own modules, porting involves a few changes to the way VTK is found and used. The old module system made variables available for using VTK. ~~~{.cmake} find_package(VTK REQUIRED COMPONENTS vtkCommonCore vtkRenderingOpenGL2) include(${VTK_USE_FILE}) add_library(usesvtk ...) target_link_libraries(usesvtk ${visibility} ${VTK_LIBRARIES}) target_include_directories(usesvtk ${visibility} ${VTK_INCLUDE_DIRS}) # Pass any VTK autoinit defines to the target. target_compile_definitions(usesvtk PRIVATE ${VTK_DEFINITIONS}) ~~~ This causes problems if VTK is found multiple times within a source tree with different components. The new pattern is: ~~~{.cmake} find_package(VTK #9.0 # Compatibility support is not provided if 9.0 is requested. REQUIRED COMPONENTS # Old component names are OK, but deprecated. #vtkCommonCore #vtkRenderingOpenGL2 # New names reflect the target names in use. CommonCore RenderingOpenGL2) # No longer needed; warns or errors depending on the version requested when # finding VTK. #include(${VTK_USE_FILE}) add_library(usesvtk ...) # VTK_LIBRARIES is provided for compatibility, but not recommended. #target_link_libraries(usesvtk ${visibility} ${VTK_LIBRARIES}) target_link_libraries(usesvtk ${visibility} VTK::CommonCore VTK::RenderingOpenGL2) # Rather than defining a single `VTK_DEFINITIONS` for use by all relevant # targets, the definitions are made as needed with the exact set needed for the # listed modules. vtk_module_autoinit( TARGETS usesvtk #MODULES ${VTK_LIBRARIES} # Again, works, but is not recommended. MODULES VTK::CommonCore VTK::RenderingOpenGL2) ~~~ ## Module declaration The old module system had CMake code declare modules in `module.cmake` files. This allowed logic and other things to happen within them which could cause module dependencies to be hard to follow. The new module system now provides facilities for disabling modules in certain configurations (using `CONDITION`) and for optionally depending on modules (using `OPTIONAL_DEPENDS`). ~~~{.cmake} if (NOT SOME_OPTION) set(depends) if (SOME_OTHER_OPTION) list(APPEND depends vtkSomeDep) endif () vtk_module(vtkModuleName GROUPS # groups the module belongs to KIT # the kit the module belongs to IMPLEMENTS # modules containing vtkObjectFactory instances that are implemented here DEPENDS # public dependencies #${depends} # no analogy in the new system PRIVATE_DEPENDS # private dependencies ${depends} COMPILE_DEPENDS # modules which must be built before this one but which are not actually # linked. TEST_DEPENDS # test dependencies TEST_OPTIONAL_DEPENDS # optional test dependencies ${depends} #EXCLUDE_FROM_WRAPPING # present for modules which cannot be wrapped ) endif () ~~~ This is now replaced with a declarative file named `vtk.module`. This file is not CMake code and is instead parsed as an argument list in CMake (variable expansions are also not allowed). The above example would translate into: ~~~{.cmake} MODULE vtkModuleName CONDITION SOME_OPTION GROUPS # groups the module belongs to KIT # the kit the module belongs to #IMPLEMENTABLE # Implicit in the old build system. Now explicit. IMPLEMENTS # modules containing vtkObjectFactory instances that are implemented here DEPENDS # public dependencies PRIVATE_DEPENDS # private dependencies OPTIONAL_DEPENDS vtkSomeDep ORDER_DEPENDS # modules which must be built before this one but which are not actually # linked. TEST_DEPENDS # test dependencies TEST_OPTIONAL_DEPENDS # optional test dependencies vtkSomeDep #EXCLUDE_WRAP # present for modules which cannot be wrapped ~~~ Modules may also now be provided by the current project or by an external project found by `find_package` as well. ## Declaring sources Sources used to be listed just as `.cxx` files. The module system would then search for a corresponding `.h` file, then add it to the list. Some source file properties could be used to control header-only or private headers. In this example, we have a module with the following sources: - `vtkPublicClass.cxx` and `vtkPublicClass.h`: Public VTK class meant to be wrapped and its header installed. - `vtkPrivateClass.cxx` and `vtkPrivateClass.h`: Priavte VTK class not meant for use outside of the module. - `helper.cpp` and `helper.h`: Private API, but not following VTK's naming conventions. - `public_helper.cpp` and `public_helper.h`: Public API, but not following VTK's naming conventions. - `vtkImplSource.cxx`: A source file without a header. - `public_header.h`: A public header without a source file. - `template.tcc` and `template.h`: Public API, but not following VTK's naming conventions. - `private_template.tcc` and `private_template.h`: Private API, but not following VTK's naming conventions. - `vtkPublicTemplate.txx` and `vtkPublicTemplate.h`: Public template sources. Wrapped and installed. - `vtkPrivateTemplate.txx` and `vtkPrivateTemplate.h`: Private template sources. - `vtkOptional.cxx` and `vtkOptional.h`: Private API which requires an optional dependency. The old module's way of building these sources is: ~~~{.cmake} set(Module_SRCS vtkPublicClass.cxx vtkPrivateClass.cxx helper.cpp helper.h public_helper.cpp public_helper.h public_header.h vtkImplSource.cxx vtkPublicTemplate.txx vtkPrivateTemplate.txx template.tcc # Not detected as a template, so not installed. template.h private_template.tcc private_template.h ) # Mark some files as only being header files. set_source_files_properties( public_header.h HEADER_FILE_ONLY ) # Mark some headers as being private. set_source_files_properties( helper.h private_template.h public_header.h template.h vtkImplSource.cxx # no header vtkPrivateTemplate.h PROPERTIES SKIP_HEADER_INSTALL 1 ) set(${vtk-module}_HDRS # Magic variable public_helper.h template.h #helper.h # private headers just go ignored. ) # Optional dependencies are detected through variables. if (Module_vtkSomeDep) list(APPEND Module_SRCS # Some optional file. vtkOptional.cxx) endif () vtk_module_library(vtkModuleName ${Module_SRCS}) ~~~ While with the new system, source files are explicitly declared using argument parsing. ~~~{.cmake} set(classes vtkPublicClass) set(private_classes vtkPrivateClass) set(sources helper.cpp public_helper.cpp vtkImplSource.cxx) set(headers public_header.h public_helper.h template.h) set(private_headers helper.h private_template.h) set(template_classes vtkPublicTemplate) set(private_template_classes vtkPrivateTemplate) set(templates template.tcc) set(private_templates private_template.tcc) # Optional dependencies are detected as targets. if (TARGET vtkSomeDep) # Optional classes may not be public (though there's no way to actually # enforce it, optional dependencies are always treated as private. list(APPEND private_classes vtkOptional) endif () vtk_module_add_module(vtkModuleName # File pairs which follow VTK's conventions. The headers will be wrapped and # installed. CLASSES ${classes} # File pairs which follow VTK's conventions, but are not for use outside the # module. PRIVATE_CLASSES ${private_classes} # Standalone sources (those without headers or which do not follow VTK's # conventions). SOURCES ${sources} # Standalone headers (those without sources or which do not follow VTK's # conventions). These will be installed. HEADERS ${public_headers} # Standalone headers (those without sources or which do not follow VTK's # conventions), but are not for use outside the module. PRIVATE_HEADERS ${private_headers} # Templates are also supported. # Template file pairs which follow VTK's conventions. Both files will be # installed (only the headers will be wrapped). TEMPLATE_CLASSES ${template_classes} # Template file pairs which follow VTK's conventions, but are not for use # outside the module. PRIVATE_TEMPLATE_CLASSES ${private_template_classes} # Standalone template files (those without headers or which do not follow # VTK's conventions). These will be installed. TEMPLATES ${templates} # Standalone template files (those without headers or which do not follow # VTK's conventions), but are not for use outside the module. PRIVATE_TEMPLATES ${private_templates} ) ~~~ Note that the arguments with `CLASSES` in their name expand to pairs of files with the `.h` and either `.cxx` or `.txx` extension based on whether it is a template or not. Projects not using this convention may use the `HEADERS`, `SOURCES`, and `TEMPLATES` arguments instead. ## Object Factories Previously, object factories were made using implicit variable declaration magic behind the scenes. This is no longer the case and proper CMake APIs for them are available. ~~~{.cmake} set(sources vtkObjectFactoryImpl.cxx # This path is made by `vtk_object_factory_configure` later. "${CMAKE_CURRENT_BINARY_DIR}/${vtk-module}ObjectFactory.cxx") # Make a list of base classes we will be overriding. set(overrides vtkObjectFactoryBase) # Make a variable declaring what the override for the class is. set(vtk_module_vtkObjectFactoryBase_override "vtkObjectFactoryImpl") # Generate a source using the list of base classes overridden. vtk_object_factory_configure("${overrides}") vtk_module_library("${vtk-module}" "${sources}") ~~~ This is now handled using proper APIs instead of variable lookups. ~~~{.cmake} set(classes vtkObjectFactoryImpl) # Explicitly declare the override relationship. vtk_object_factory_declare( BASE vtkObjectFactoryBase OVERRIDE vtkObjectFactoryImpl) # Collects the set of declared overrides and writes out a source file. vtk_object_factory_declare( # The path to the source is returned as a variable. SOURCE_FILE factory_source # As is its header file. HEADER_FILE factory_header # The export macro is now explicitly passed (instead of assumed based on the # current module context). EXPORT_MACRO MODULE_EXPORT) vtk_module_add_module(vtkModuleName CLASSES ${classes} SOURCES "${factory_source}" PRIVATE_HEADERS "${factory_header}") ~~~ ## Building a group of modules This was not well supported in the old module system. Basically, it involved setting up the source tree like VTK expects and then including the `vtkModuleTop` file. This is best just rewritten using the following CMake APIs: - [vtk_module_find_modules] - [vtk_module_find_kits] - [vtk_module_scan] - [vtk_module_build] [vtk_module_find_modules]: @ref vtk_module_find_modules [vtk_module_find_kits]: @ref vtk_module_find_kits [vtk_module_scan]: @ref vtk_module_scan [vtk_module_build]: @ref vtk_module_build