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
51 macro(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 
410 endmacro()
411