CTK 0.1.0
The Common Toolkit is a community effort to provide support code for medical image analysis, surgical navigation, and related projects.
ctkMacroBuildPlugin.cmake
Go to the documentation of this file.
1###########################################################################
2#
3# Library: CTK
4#
5# Copyright (c) Kitware Inc.
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0.txt
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19###########################################################################
20
21#
22# Depends on:
23# CTK/CMake/ctkMacroParseArguments.cmake
24# CTK/CMake/ctkMacroGeneratePluginManifest.cmake
25#
26#! \brief Build a CTK plug-in.
27#!
28#! This macro takes the usual arguments for building
29#! a shared library using Qt. Additionally, it generates
30#! plugin meta-data by creating a MANIFEST.MF text file
31#! which is embedded in the share library as a Qt resource.
32#!
33#! The following variables can be set in a file named
34#! manifest_headers.cmake, which will then be read by
35#! this macro:
36#!
37#! - Plugin-ActivationPolicy
38#! - Plugin-Category
39#! - Plugin-ContactAddress
40#! - Plugin-Copyright
41#! - Plugin-Description
42#! - Plugin-DocURL
43#! - Plugin-Icon
44#! - Plugin-License
45#! - Plugin-Name
46#! - Require-Plugin
47#! - Plugin-Vendor
48#! - Plugin-Version
49#!
50#! \ingroup CMakeAPI
51macro(ctkMacroBuildPlugin)
52 CtkMacroParseArguments(MY
53 "EXPORT_DIRECTIVE;SRCS;MOC_SRCS;MOC_OPTIONS;UI_FORMS;INCLUDE_DIRECTORIES;EXPORTED_INCLUDE_SUFFIXES;TARGET_LIBRARIES;RESOURCES;CACHED_RESOURCEFILES;TRANSLATIONS;OUTPUT_DIR"
54 "TEST_PLUGIN;NO_INSTALL"
55 ${ARGN}
56 )
57
58 # Keep parameter 'INCLUDE_DIRECTORIES' for backward compatiblity
59
60 # Sanity checks
61 if(NOT DEFINED MY_EXPORT_DIRECTIVE)
62 message(FATAL_ERROR "EXPORT_DIRECTIVE is mandatory")
63 endif()
64
65 # Print a warning if the project name does not match the directory name
66 get_filename_component(_dir_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
67 string(REPLACE "." "_" _dir_name_with_ ${_dir_name})
68 if(NOT _dir_name_with_ STREQUAL ${PROJECT_NAME})
69 message(WARNING "Discouraged mismatch of plug-in project name [${PROJECT_NAME}] and top-level directory name [${CMAKE_CURRENT_SOURCE_DIR}].")
70 endif()
71
72 # Define library name
73 set(lib_name ${PROJECT_NAME})
74
75 # Plug-in target names must contain at leas one _
76 if(NOT lib_name MATCHES _)
77 message(FATAL_ERROR "The plug-in project name ${lib_name} must contain at least one '_' character")
78 endif()
79
80 # Plugin are expected to be shared library
81 set(MY_LIBRARY_TYPE "SHARED")
82
83 # Clear the variables for the manifest headers
84 set(Plugin-ActivationPolicy )
85 set(Plugin-Category )
86 set(Plugin-ContactAddress )
87 set(Plugin-Copyright )
88 set(Plugin-Description )
89 set(Plugin-DocURL )
90 set(Plugin-Icon )
91 set(Plugin-License )
92 set(Plugin-Name )
93 set(Require-Plugin )
94 set(Plugin-SymbolicName )
95 set(Plugin-Vendor )
96 set(Plugin-Version )
97
98 set(Custom-Headers )
99
100 if(MY_TEST_PLUGIN)
101 # Since the test plug-ins are not considered when calculating
102 # target dependencies via DGraph, we add the dependencies
103 # manually here
104 #message("${lib_name}_DEPENDENCIES ${MY_TARGET_LIBRARIES}")
105 list(APPEND ${lib_name}_DEPENDENCIES ${MY_TARGET_LIBRARIES})
106 endif()
107
108 # If a file named manifest_headers.cmake exists, read it
109 set(manifest_headers_dep )
110 if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
111 include(${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake)
112 set(manifest_headers_dep "${CMAKE_CURRENT_SOURCE_DIR}/manifest_headers.cmake")
113 endif()
114
115 string(REPLACE "_" "." Plugin-SymbolicName ${lib_name})
116
117 # --------------------------------------------------------------------------
118 # Include dirs
119 if(MY_EXPORTED_INCLUDE_SUFFIXES)
120 set(${lib_name}_INCLUDE_SUFFIXES ${MY_EXPORTED_INCLUDE_SUFFIXES}
121 CACHE INTERNAL "List of exported plugin include dirs")
122
123 set(my_includes )
124 foreach(_suffix ${MY_EXPORTED_INCLUDE_SUFFIXES})
125 list(APPEND my_includes ${CMAKE_CURRENT_SOURCE_DIR}/${_suffix})
126 endforeach()
127 else()
128 set(${lib_name}_INCLUDE_SUFFIXES ""
129 CACHE INTERNAL "List of exported plugin include dirs")
130
131 set(my_includes )
132 endif()
133
134 list(APPEND my_includes
135 ${CMAKE_CURRENT_SOURCE_DIR}
136 ${CMAKE_CURRENT_BINARY_DIR}
137 )
138
139 # Add the include directories from the plugin dependencies
140 # and external dependencies
141 ctkFunctionGetIncludeDirs(my_includes ${lib_name})
142
143 if(CMAKE_VERSION VERSION_LESS 2.8.12)
144 include_directories(
145 ${my_includes}
146 )
147 endif()
148
149 if(CTK_QT_VERSION VERSION_LESS "5")
150 # Add Qt include dirs and defines
151 include(${QT_USE_FILE})
152 else()
153 find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
154 endif()
155
156 # Add the library directories from the external project
157 ctkFunctionGetLibraryDirs(my_library_dirs ${lib_name})
158
159 link_directories(
160 ${my_library_dirs}
161 )
162
163 set(MY_LIBRARY_EXPORT_DIRECTIVE ${MY_EXPORT_DIRECTIVE})
164 set(MY_EXPORT_HEADER_PREFIX "${lib_name}_")
165 set(MY_LIBNAME ${lib_name})
166
167 configure_file(
168 ${CTK_EXPORT_HEADER_TEMPLATE}
169 ${CMAKE_CURRENT_BINARY_DIR}/${MY_EXPORT_HEADER_PREFIX}Export.h
170 )
171 set(dynamicHeaders
172 "${dynamicHeaders};${CMAKE_CURRENT_BINARY_DIR}/${MY_EXPORT_HEADER_PREFIX}Export.h")
173
174 # Make sure variable are cleared
175 set(MY_MOC_CPP)
176 set(MY_UI_CPP)
177 set(MY_QRC_SRCS)
178
179 # Wrap
180 if (CTK_QT_VERSION VERSION_GREATER "4")
181 set(target)
182 if(Qt5Core_VERSION VERSION_GREATER "5.2.0")
183 set(target TARGET ${lib_name})
184 endif()
185 if(MY_MOC_SRCS)
186 # this is a workaround for Visual Studio. The relative include paths in the generated
187 # moc files can get very long and can't be resolved by the MSVC compiler.
188 foreach(moc_src ${MY_MOC_SRCS})
189 QT5_WRAP_CPP(MY_MOC_CPP ${moc_src} OPTIONS -f${moc_src} -DHAVE_QT5 ${MY_MOC_OPTIONS} ${target})
190 endforeach()
191 endif()
192 QT5_WRAP_UI(MY_UI_CPP ${MY_UI_FORMS})
193 if(DEFINED MY_RESOURCES)
194 QT5_ADD_RESOURCES(MY_QRC_SRCS ${MY_RESOURCES})
195 endif()
196 else()
197 if(MY_MOC_SRCS)
198 # this is a workaround for Visual Studio. The relative include paths in the generated
199 # moc files can get very long and can't be resolved by the MSVC compiler.
200 foreach(moc_src ${MY_MOC_SRCS})
201 QT4_WRAP_CPP(MY_MOC_CPP ${moc_src} OPTIONS -f${moc_src} ${MY_MOC_OPTIONS} TARGET ${lib_name})
202 endforeach()
203 endif()
204 QT4_WRAP_UI(MY_UI_CPP ${MY_UI_FORMS})
205 if(DEFINED MY_RESOURCES)
206 QT4_ADD_RESOURCES(MY_QRC_SRCS ${MY_RESOURCES})
207 endif()
208 endif()
209 # Add the generated manifest qrc file
210 set(manifest_qrc_src )
211 ctkFunctionGeneratePluginManifest(manifest_qrc_src
212 ACTIVATIONPOLICY ${Plugin-ActivationPolicy}
213 CATEGORY ${Plugin-Category}
214 CONTACT_ADDRESS ${Plugin-ContactAddress}
215 COPYRIGHT ${Plugin-Copyright}
216 DESCRIPTION ${Plugin-Description}
217 DOC_URL ${Plugin-DocURL}
218 ICON ${Plugin-Icon}
219 LICENSE ${Plugin-License}
220 NAME ${Plugin-Name}
221 REQUIRE_PLUGIN ${Require-Plugin}
222 SYMBOLIC_NAME ${Plugin-SymbolicName}
223 VENDOR ${Plugin-Vendor}
224 VERSION ${Plugin-Version}
225 CUSTOM_HEADERS ${Custom-Headers}
226 )
227
228 if(manifest_headers_dep)
229 set_property(SOURCE ${manifest_qrc_src} APPEND
230 PROPERTY OBJECT_DEPENDS ${manifest_headers_dep})
231 endif()
232 list(APPEND MY_QRC_SRCS ${manifest_qrc_src})
233
234 # Create translation files (.ts and .qm)
235 set(_plugin_qm_files )
236 set(_plugin_cached_resources_in_binary_tree )
237 set(_translations_dir "${CMAKE_CURRENT_BINARY_DIR}/CTK-INF/l10n")
238 if(MY_TRANSLATIONS)
239 set_source_files_properties(${MY_TRANSLATIONS}
240 PROPERTIES OUTPUT_LOCATION ${_translations_dir})
241 if(CTK_QT_VERSION VERSION_GREATER "4")
242 qt5_create_translation(_plugin_qm_files ${MY_SRCS} ${MY_UI_FORMS} ${MY_TRANSLATIONS})
243 else()
244 QT4_CREATE_TRANSLATION(_plugin_qm_files ${MY_SRCS} ${MY_UI_FORMS} ${MY_TRANSLATIONS})
245 endif()
246 endif()
247
248 if(_plugin_qm_files)
249 foreach(_qm_file ${_plugin_qm_files})
250 file(RELATIVE_PATH _relative_qm_file ${CMAKE_CURRENT_BINARY_DIR} ${_qm_file})
251 list(APPEND _plugin_cached_resources_in_binary_tree ${_relative_qm_file})
252 endforeach()
253 endif()
254
255 set(_plugin_cached_resources_in_source_tree )
256 if(MY_CACHED_RESOURCEFILES)
257 foreach(_cached_resource ${MY_CACHED_RESOURCEFILES})
258 if(IS_ABSOLUTE "${_cached_resource}")
259 # create a path relative to the current binary dir
260 file(RELATIVE_PATH _relative_cached_resource ${CMAKE_CURRENT_BINARY_DIR} ${_cached_resource})
261 list(APPEND _plugin_cached_resources_in_binary_tree ${_relative_cached_resource})
262 else()
263 list(APPEND _plugin_cached_resources_in_source_tree ${_cached_resource})
264 endif()
265 endforeach()
266 endif()
267
268 # Add any other additional resource files
269 if(_plugin_cached_resources_in_source_tree OR _plugin_cached_resources_in_binary_tree)
270 string(REPLACE "." "_" _plugin_symbolicname ${Plugin-SymbolicName})
271 ctkMacroGeneratePluginResourcefile(MY_QRC_SRCS
272 NAME ${_plugin_symbolicname}_cached.qrc
273 PREFIX ${Plugin-SymbolicName}
274 RESOURCES ${_plugin_cached_resources_in_source_tree}
275 BINARY_RESOURCES ${_plugin_cached_resources_in_binary_tree})
276 endif()
277
278 source_group("Resources" FILES
279 ${MY_RESOURCES}
280 ${MY_UI_FORMS}
281 ${MY_TRANSLATIONS}
282 )
283
284 source_group("Generated" FILES
285 ${MY_QRC_SRCS}
286 ${MY_MOC_CPP}
287 ${MY_UI_CPP}
288 ${_plugin_qm_files}
289 )
290
291 add_library(${lib_name} ${MY_LIBRARY_TYPE}
292 ${MY_SRCS}
293 ${MY_MOC_CPP}
294 ${MY_UI_CPP}
295 ${MY_QRC_SRCS}
296 ${_plugin_qm_files}
297 )
298
299 if(NOT CMAKE_VERSION VERSION_LESS 2.8.12)
300 target_include_directories(${lib_name}
301 PUBLIC "$<BUILD_INTERFACE:${my_includes}>"
302 "$<INSTALL_INTERFACE:${CTK_INSTALL_PLUGIN_INCLUDE_DIR}/${Plugin-SymbolicName}>"
303 )
304 if(CTK_QT_VERSION VERSION_LESS "5")
305 # Add Qt include dirs to the target
306 target_include_directories(${lib_name} PUBLIC ${QT_INCLUDE_DIR})
307 foreach(module QT3SUPPORT QTOPENGL QTASSISTANT QTDESIGNER QTMOTIF QTNSPLUGIN
308 QAXSERVER QAXCONTAINER QTDECLARATIVE QTSCRIPT QTSVG QTUITOOLS QTHELP
309 QTWEBKIT PHONON QTSCRIPTTOOLS QTMULTIMEDIA QTXMLPATTERNS QTGUI QTTEST
310 QTDBUS QTXML QTSQL QTNETWORK QTCORE)
311 if (QT_USE_${module} OR QT_USE_${module}_DEPENDS)
312 if (QT_${module}_FOUND)
313 target_include_directories(${lib_name} PUBLIC ${QT_${module}_INCLUDE_DIR})
314 endif ()
315 endif ()
316 endforeach()
317 endif()
318 else()
319 find_package(Qt5LinguistTools REQUIRED)
320 endif()
321
322 if(MY_TEST_PLUGIN AND CTK_QT_VERSION VERSION_GREATER "4")
323 find_package(Qt5Test REQUIRED)
324 if(CMAKE_VERSION VERSION_LESS 2.8.12)
325 target_link_libraries(${lib_name} Qt5::Test)
326 else()
327 target_link_libraries(${lib_name} PRIVATE Qt5::Test)
328 endif()
329 endif()
330
331 # Set the output directory for the plugin
332 if(MY_OUTPUT_DIR)
333 set(output_dir_suffix "/${MY_OUTPUT_DIR}")
334 else()
335 set(output_dir_suffix "")
336 endif()
337
338 foreach(type RUNTIME LIBRARY ARCHIVE)
339 if(NOT DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY AND CMAKE_${type}_OUTPUT_DIRECTORY)
340 # Put plug-ins by default into a "plugins" subdirectory
341 set(CTK_PLUGIN_${type}_OUTPUT_DIRECTORY "${CMAKE_${type}_OUTPUT_DIRECTORY}/plugins")
342 endif()
343
344 if(IS_ABSOLUTE "${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}")
345 set(plugin_${type}_output_dir "${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}${output_dir_suffix}")
346 elseif(CMAKE_${type}_OUTPUT_DIRECTORY)
347 set(plugin_${type}_output_dir "${CMAKE_${type}_OUTPUT_DIRECTORY}/${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}${output_dir_suffix}")
348 else()
349 set(plugin_${type}_output_dir "${CMAKE_CURRENT_BINARY_DIR}/${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}${output_dir_suffix}")
350 endif()
351
352 if(MY_TEST_PLUGIN)
353 # Test plug-ins will always be put in a separate directory
354 if(CMAKE_${type}_OUTPUT_DIRECTORY)
355 set(plugin_${type}_output_dir "${CMAKE_${type}_OUTPUT_DIRECTORY}/test_plugins")
356 else()
357 set(plugin_${type}_output_dir "${PROJECT_BINARY_DIR}/test_plugins")
358 endif()
359 endif()
360 endforeach()
361
362 set(plugin_compile_flags "-DQT_PLUGIN")
363 ctkFunctionGetCompilerVisibilityFlags(plugin_compile_flags)
364
365 # Apply properties to the library target.
366 set_target_properties(${lib_name} PROPERTIES
367 COMPILE_FLAGS "${plugin_compile_flags}"
368 RUNTIME_OUTPUT_DIRECTORY ${plugin_RUNTIME_output_dir}
369 LIBRARY_OUTPUT_DIRECTORY ${plugin_LIBRARY_output_dir}
370 ARCHIVE_OUTPUT_DIRECTORY ${plugin_ARCHIVE_output_dir}
371 PREFIX "lib"
372 )
373
374 if(NOT MY_TEST_PLUGIN AND NOT MY_NO_INSTALL)
375 # Install rules
376 install(TARGETS ${lib_name} EXPORT CTKExports
377 RUNTIME DESTINATION ${CTK_INSTALL_PLUGIN_DIR} COMPONENT RuntimePlugins
378 LIBRARY DESTINATION ${CTK_INSTALL_PLUGIN_DIR} COMPONENT RuntimePlugins
379 ARCHIVE DESTINATION ${CTK_INSTALL_PLUGIN_DIR} COMPONENT Development)
380 endif()
381
382 set(my_libs
383 ${MY_TARGET_LIBRARIES}
384 )
385
386 if(MINGW)
387 list(APPEND my_libs ssp) # add stack smash protection lib
388 endif()
389
390 if(CMAKE_VERSION VERSION_LESS 2.8.12)
391 target_link_libraries(${lib_name} ${my_libs})
392 else()
393 target_link_libraries(${lib_name} PUBLIC ${my_libs})
394 endif()
395
396 if(NOT MY_TEST_PLUGIN)
397 set(${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES ${${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES} ${lib_name} CACHE INTERNAL "CTK plugins" FORCE)
398 endif()
399
400 if(NOT MY_TEST_PLUGIN AND NOT MY_NO_INSTALL)
401 # Install headers
402 file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/*.tpp")
403 install(FILES
404 ${headers}
405 ${dynamicHeaders}
406 DESTINATION ${CTK_INSTALL_PLUGIN_INCLUDE_DIR}/${Plugin-SymbolicName} COMPONENT Development
407 )
408 endif()
409
410endmacro()
411