InstallationTools.cmake
Go to the documentation of this file.
1 # ============================================================================
2 # Copyright (c) 2011-2012 University of Pennsylvania
3 # Copyright (c) 2013-2016 Andreas Schuh
4 # All rights reserved.
5 #
6 # See COPYING file for license information or visit
7 # https://cmake-basis.github.io/download.html#license
8 # ============================================================================
9 
10 ##############################################################################
11 # @file InstallationTools.cmake
12 # @brief CMake functions used for installation.
13 #
14 # @ingroup CMakeTools
15 ##############################################################################
16 
17 # ----------------------------------------------------------------------------
18 # include guard
20  return ()
21 else ()
23 endif ()
24 
25 
26 ## @addtogroup CMakeUtilities
27 # @{
28 
29 
30 # ============================================================================
31 # Installation
32 # ============================================================================
33 
34 # ----------------------------------------------------------------------------
35 ## @brief Specify rules to run at install time.
36 #
37 # This function replaces CMake's
38 # <a href="https://cmake.org/cmake/help/v3.5/command/install.html">install()</a> command.
39 #
40 # @sa https://cmake.org/cmake/help/v3.5/command/install.html
41 #
42 # @ingroup CMakeAPI
43 function (basis_install)
44  include(CMakeParseArguments)
45  cmake_parse_arguments (ARGN "" "DIRECTORY" "TARGETS;FILES;PROGRAMS" ${ARGN})
46  set (_errmsg "Options TARGETS, FILES, PROGRAMS, and DIRECTORY are mutually exclusive!")
47  if (ARGN_DIRECTORY)
48  if (ARGN_FILES OR ARGN_PROGRAMS OR ARGN_TARGETS)
49  message (FATAL_ERROR "${_errmsg}")
50  endif ()
51  install (DIRECTORY ${ARGN_DIRECTORY} ${ARGN_UNPARSED_ARGUMENTS})
52  elseif (ARGN_FILES)
53  if (ARGN_PROGRAMS OR ARGN_TARGETS OR ARGN_DIRECTORY)
54  message (FATAL_ERROR "${_errmsg}")
55  endif ()
56  install (FILES ${ARGN_FILES} ${ARGN_UNPARSED_ARGUMENTS})
57  elseif (ARGN_PROGRAMS)
58  if (ARGN_FILES OR ARGN_TARGETS OR ARGN_DIRECTORY)
59  message (FATAL_ERROR "${_errmsg}")
60  endif ()
61  install (PROGRAMS ${ARGN_PROGRAMS} ${ARGN_UNPARSED_ARGUMENTS})
62  elseif (ARGN_TARGETS)
63  if (ARGN_FILES OR ARGN_PROGRAMS OR ARGN_DIRECTORY)
64  message (FATAL_ERROR "${_errmsg}")
65  endif ()
66  install (TARGETS ${ARGN_TARGETS} ${ARGN_UNPARSED_ARGUMENTS})
67  else ()
68  install (${ARGN_UNPARSED_ARGUMENTS})
69  endif ()
70 endfunction ()
71 
72 # ----------------------------------------------------------------------------
73 ## @brief Install content of source directory excluding typical files.
74 #
75 # Files which are excluded are typical backup files, system files, files
76 # from revision control systems, and CMakeLists.txt files.
77 #
78 # Example:
79 # @code
80 # basis_install_directory("${INSTALL_DATA_DIR}")
81 # basis_install_directory(. "${INSTALL_DATA_DIR}")
82 # basis_install_directory("${CMAKE_CURRENT_SOURCE_DIR}" "${INSTALL_DATA_DIR}")
83 # basis_install_directory(images "${INSTALL_DATA_DIR}/images")
84 # @endcode
85 #
86 # @param [in] ARGN The first two arguments are extracted from the beginning
87 # of this list in the named order (without option name),
88 # and the remaining arguments are passed on to CMake's
89 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:install">
90 # <tt>install(DIRECTORY)</tt></a> command.
91 # @par
92 # <table border="0">
93 # <tr>
94 # @tp @b SOURCE @endtp
95 # <td>Source directory. Defaults to current source directory
96 # if only one argument, the @p DESTINATION, is given./td>
97 # </tr>
98 # <tr>
99 # @tp @b DESTINATION @endtp
100 # <td>Destination directory.</td>
101 # </tr>
102 # </table>
103 #
104 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:install
105 #
106 # @ingroup CMakeAPI
107 function (basis_install_directory)
108  if (ARGC EQUAL 1)
109  set (SOURCE "${CMAKE_CURRENT_SOURCE_DIR}")
110  set (DESTINATION "${ARGV0}")
111  set (OPTIONS "${ARGV}")
112  list (REMOVE_AT OPTIONS 0)
113  elseif (ARGC GREATER 1)
114  set (SOURCE "${ARGV0}")
115  set (DESTINATION "${ARGV1}")
116  set (OPTIONS "${ARGV}")
117  list (REMOVE_AT OPTIONS 0 1)
118  else ()
119  message (FATAL_ERROR "Too few arguments given!")
120  endif ()
121  # check arguments
122  if (NOT IS_ABSOLUTE "${SOURCE}")
123  get_filename_component (SOURCE "${SOURCE}" ABSOLUTE)
124  endif ()
125  string (REGEX REPLACE "/+$" "" SOURCE "${SOURCE}")
126  if (NOT IS_ABSOLUTE "${DESTINATION}")
127  set (DESTINATION "${CMAKE_INSTALL_PREFIX}/${DESTINATION}")
128  endif ()
129  basis_sanitize_for_regex (PROJECT_SOURCE_DIR_RE "${PROJECT_SOURCE_DIR}")
130  if ("${DESTINATION}" MATCHES "^${PROJECT_SOURCE_DIR_RE}(/|$)")
131  message (FATAL_ERROR "Installation directory ${DESTINATION} is inside the project source tree!")
132  endif ()
133  # parse options
134  set (MATCH)
135  set (EXCLUDE)
136  set (COMPONENT)
137  set (CONFIGURATIONS)
138  set (RE)
139  set (OPT)
140  set (NUM)
141  foreach (O IN LISTS OPTIONS)
142  if (O MATCHES "^(FILES_MATCHING|.*PERMISSIONS|OPTIONAL|DESTINATION|DIRECTORY)$")
143  message (FATAL_ERROR "Option ${O} not supported by basis_install_directory()!")
144  endif ()
145  if (OPT MATCHES "^(PATTERN|REGEX)$")
146  if (NUM EQUAL 1)
147  if (O MATCHES "^EXCLUDE$")
148  list (APPEND EXCLUDE "${RE}")
149  else ()
150  list (APPEND MATCH "${RE}")
151  endif ()
152  set (OPT)
153  set (RE)
154  else ()
155  if (OPT MATCHES "PATTERN")
156  string (REGEX REPLACE "(^|[^\\])\\*" "&#9734" O "${O}")
157  basis_sanitize_for_regex (O "${O}")
158  string (REPLACE "&#9734" ".*" O "${O}")
159  endif ()
160  set (RE "${O}")
161  set (NUM 1)
162  endif ()
163  elseif (OPT MATCHES "^COMPONENT$")
164  set (COMPONENT "${O}")
165  set (OPT)
166  elseif (OPT MATCHES "^CONFIGURATIONS$")
167  if (O MATCHES "^(PATTERN|REGEX|COMPONENT|CONFIGURATIONS)$")
168  set (OPT)
169  else ()
170  list (APPEND CONFIGURATIONS "${O}")
171  math (EXPR NUM "${NUM} + 1")
172  endif ()
173  endif ()
174  if (O MATCHES "^(PATTERN|REGEX|COMPONENT|CONFIGURATIONS)$")
175  if (OPT)
176  message (FATAL_ERROR "basis_install_directory(): Option ${OPT} is missing an argument!")
177  endif ()
178  set (OPT ${O})
179  set (NUM 0)
180  endif ()
181  endforeach ()
182  if (OPT MATCHES "CONFIGURATIONS" AND NOT NUM GREATER 0)
183  message (FATAL_ERROR "basis_install_directory(): Missing argument(s) for ${OPT} option!")
184  elseif (OPT MATCHES "PATTERN|REGEX")
185  if (NUM EQUAL 1)
186  list (APPEND MATCH "${RE}")
187  else ()
188  message (FATAL_ERROR "basis_install_directory(): Missing argument(s) for ${OPT} option!")
189  endif ()
190  elseif (OPT MATCHES "COMPONENT")
191  message (FATAL_ERROR "basis_install_directory(): Missing argument for ${OPT} option!")
192  endif ()
193  list (APPEND EXCLUDE "CMakeLists.txt$" "/.svn/" "/.git/" ".DS_Store$" ".*~$") # default excludes
194  basis_list_to_delimited_string (MATCH "|" NOAUTOQUOTE ${MATCH})
195  basis_list_to_delimited_string (EXCLUDE "|" NOAUTOQUOTE ${EXCLUDE})
196  string (REPLACE "\\" "\\\\" MATCH "${MATCH}")
197  string (REPLACE "\"" "\\\"" MATCH "${MATCH}")
198  string (REPLACE "\\" "\\\\" EXCLUDE "${EXCLUDE}")
199  string (REPLACE "\"" "\\\"" EXCLUDE "${EXCLUDE}")
200  # Add installation instructions. Note that install(DIRECTORY) is here not
201  # used to avoid the generation of empty directories
202  set (OPTIONS)
203  if (CONFIGURATIONS)
204  list (APPEND OPTIONS CONFIGURATIONS ${CONFIGURATIONS})
205  endif ()
206  if (COMPONENT)
207  list (APPEND OPTIONS COMPONENT ${COMPONENT})
208  endif ()
209  install (CODE
210  "# -----------------------------------------------------------------------
211  # basis_install_directory(): ${SOURCE}
212  set (BASIS_INSTALL_DIRECTORY_FILES)
213  set (BASIS_INSTALL_DIRECTORY_SOURCE \"${SOURCE}\")
214  set (BASIS_INSTALL_DIRECTORY_DESTINATION \"\$ENV{DESTDIR}${DESTINATION}\")
215  set (BASIS_INSTALL_DIRECTORY_MATCH \"${MATCH}\")
216  set (BASIS_INSTALL_DIRECTORY_EXCLUDE \"${EXCLUDE}\")
217  file (GLOB_RECURSE BASIS_INSTALL_DIRECTORY_ALL_FILES \"\${BASIS_INSTALL_DIRECTORY_SOURCE}/*\")
218  foreach (BASIS_INSTALL_DIRECTORY_FILE IN LISTS BASIS_INSTALL_DIRECTORY_ALL_FILES)
219  if (NOT BASIS_INSTALL_DIRECTORY_MATCH OR
220  BASIS_INSTALL_DIRECTORY_FILE MATCHES \"\${BASIS_INSTALL_DIRECTORY_MATCH}\" AND
221  NOT BASIS_INSTALL_DIRECTORY_FILE MATCHES \"\${BASIS_INSTALL_DIRECTORY_EXCLUDE}\")
222  list (APPEND BASIS_INSTALL_DIRECTORY_FILES \"\${BASIS_INSTALL_DIRECTORY_FILE}\")
223  endif ()
224  endforeach ()
225  foreach (BASIS_INSTALL_DIRECTORY_FILE IN LISTS BASIS_INSTALL_DIRECTORY_FILES)
226  file (RELATIVE_PATH BASIS_INSTALL_DIRECTORY_FILE \"\${BASIS_INSTALL_DIRECTORY_SOURCE}\" \"\${BASIS_INSTALL_DIRECTORY_FILE}\")
227  execute_process (
228  COMMAND \"${CMAKE_COMMAND}\" -E compare_files
229  \"\${BASIS_INSTALL_DIRECTORY_SOURCE}/\${BASIS_INSTALL_DIRECTORY_FILE}\"
230  \"\${BASIS_INSTALL_DIRECTORY_DESTINATION}/\${BASIS_INSTALL_DIRECTORY_FILE}\"
231  RESULT_VARIABLE RC
232  OUTPUT_QUIET
233  ERROR_QUIET
234  )
235  if (RC EQUAL 0)
236  message (STATUS \"Up-to-date: \${BASIS_INSTALL_DIRECTORY_DESTINATION}/\${BASIS_INSTALL_DIRECTORY_FILE}\")
237  else ()
238  message (STATUS \"Installing: \${BASIS_INSTALL_DIRECTORY_DESTINATION}/\${BASIS_INSTALL_DIRECTORY_FILE}\")
239  execute_process (
240  COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different
241  \"\${BASIS_INSTALL_DIRECTORY_SOURCE}/\${BASIS_INSTALL_DIRECTORY_FILE}\"
242  \"\${BASIS_INSTALL_DIRECTORY_DESTINATION}/\${BASIS_INSTALL_DIRECTORY_FILE}\"
243  RESULT_VARIABLE RC
244  OUTPUT_QUIET
245  ERROR_QUIET
246  )
247  if (RC EQUAL 0)
248  list (APPEND CMAKE_INSTALL_MANIFEST_FILES \"\${BASIS_INSTALL_DIRECTORY_DESTINATION}/\${BASIS_INSTALL_DIRECTORY_FILE}\")
249  else ()
250  message (STATUS \"Failed to install \${BASIS_INSTALL_DIRECTORY_DESTINATION}/\${BASIS_INSTALL_DIRECTORY_FILE}\")
251  endif ()
252  endif ()
253  endforeach ()
254  # -----------------------------------------------------------------------"
255  ${OPTIONS}
256  )
257 endfunction ()
258 
259 # ----------------------------------------------------------------------------
260 ## @brief Add installation rule for project template.
261 function (basis_install_template TEMPLATE DESTINATION)
262  if (NOT IS_ABSOLUTE "${TEMPLATE}")
263  set (TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE}")
264  endif ()
265  install (
266  DIRECTORY "${TEMPLATE}/"
267  DESTINATION "${DESTINATION}"
268  PATTERN *~ EXCLUDE
269  PATTERN .svn EXCLUDE
270  PATTERN .git EXCLUDE
271  PATTERN .hg EXCLUDE
272  PATTERN .DS_Store EXCLUDE
273  )
274 endfunction ()
275 
276 # ----------------------------------------------------------------------------
277 ## @brief Add installation rule to create a symbolic link.
278 #
279 # Note that the installation rule will only be effective on a Unix-like
280 # system, i.e., one which supports the creation of a symbolic link.
281 #
282 # @param [in] OLD The value of the symbolic link.
283 # @param [in] NEW The name of the symbolic link.
284 #
285 # @returns Adds installation rule to create the symbolic link @p NEW.
286 #
287 # @ingroup CMakeAPI
288 function (basis_install_link OLD NEW)
289  set (CMD_IN
290  "
291  set (OLD \"@OLD@\")
292  set (NEW \"@NEW@\")
293 
294  if (NOT IS_ABSOLUTE \"\${OLD}\")
295  set (OLD \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${OLD}\")
296  endif ()
297  if (NOT IS_ABSOLUTE \"\${NEW}\")
298  set (NEW \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${NEW}\")
299  endif ()
300 
301  if (IS_SYMLINK \"\${NEW}\")
302  file (REMOVE \"\${NEW}\")
303  endif ()
304 
305  if (EXISTS \"\${NEW}\")
306  message (STATUS \"Skipping: \${NEW} -> \${OLD}\")
307  else ()
308  message (STATUS \"Installing: \${NEW} -> \${OLD}\")
309 
310  get_filename_component (SYMDIR \"\${NEW}\" PATH)
311 
312  file (RELATIVE_PATH OLD \"\${SYMDIR}\" \"\${OLD}\")
313 
314  if (NOT EXISTS \${SYMDIR})
315  file (MAKE_DIRECTORY \"\${SYMDIR}\")
316  endif ()
317 
318  execute_process (
319  COMMAND \"${CMAKE_COMMAND}\" -E create_symlink \"\${OLD}\" \"\${NEW}\"
320  RESULT_VARIABLE RETVAL
321  )
322 
323  if (NOT RETVAL EQUAL 0)
324  message (ERROR \"Failed to create (symbolic) link \${NEW} -> \${OLD}\")
325  else ()
326  list (APPEND CMAKE_INSTALL_MANIFEST_FILES \"\${NEW}\")
327  endif ()
328  endif ()
329  "
330  )
331 
332  string (CONFIGURE "${CMD_IN}" CMD @ONLY)
333  install (CODE "${CMD}")
334 endfunction ()
335 
336 # ----------------------------------------------------------------------------
337 ## @brief Adds installation rules to create default symbolic links.
338 #
339 # This function creates for each main executable a symbolic link directly
340 # in the directory @c INSTALL_PREFIX/bin if @c INSTALL_SINFIX is TRUE and the
341 # software is installed on a Unix-like system, i.e., one which
342 # supports the creation of symbolic links.
343 #
344 # @returns Adds installation command for creation of symbolic links in the
345 # installation tree.
346 function (basis_install_links)
347  if (NOT UNIX)
348  return ()
349  endif ()
350 
351  # main executables
352  basis_get_project_property (TARGETS PROPERTY TARGETS)
353  foreach (TARGET_UID ${TARGETS})
354  get_target_property (IMPORTED ${TARGET_UID} IMPORTED)
355 
356  if (NOT IMPORTED)
357  get_target_property (BASIS_TYPE ${TARGET_UID} BASIS_TYPE)
358  get_target_property (LIBEXEC ${TARGET_UID} LIBEXEC)
359  get_target_property (IS_TEST ${TARGET_UID} TEST)
360 
361  if (BASIS_TYPE MATCHES "EXECUTABLE" AND NOT LIBEXEC AND NOT IS_TEST)
362  get_target_property (SYMLINK_NAME ${TARGET_UID} SYMLINK_NAME)
363  if (NOT "${SYMLINK_NAME}" MATCHES "^none$|^None$|^NONE$")
364  get_target_property (SYMLINK_PREFIX ${TARGET_UID} SYMLINK_PREFIX)
365  get_target_property (SYMLINK_SUFFIX ${TARGET_UID} SYMLINK_SUFFIX)
366  get_target_property (INSTALL_DIR ${TARGET_UID} RUNTIME_INSTALL_DIRECTORY)
367  if (NOT INSTALL_DIR)
368  get_target_property (INSTALL_DIR ${TARGET_UID} INSTALL_DIRECTORY)
369  endif ()
370 
371  basis_get_target_location (OUTPUT_NAME ${TARGET_UID} NAME)
372 
373  if (NOT SYMLINK_NAME)
374  set (SYMLINK_NAME "${OUTPUT_NAME}")
375  endif ()
376  if (SYMLINK_PREFIX)
377  set (SYMLINK_NAME "${SYMLINK_PREFIX}${SYMLINK_NAME}")
378  endif ()
379  if (SYMLINK_SUFFIX)
380  set (SYMLINK_NAME "${SYMLINK_NAME}${SYMLINK_SUFFIX}")
381  endif ()
382 
383  # avoid creation of symbolic link if there would be a conflict with
384  # the subdirectory in bin/ where the actual executables are installed
385  if (INSTALL_SINFIX AND "${SYMLINK_NAME}" STREQUAL "${BASIS_INSALL_SINFIX}")
386  message (STATUS \"Skipping: ${INSTALL_DIR}/${OUTPUT_NAME} -> ${INSTALL_PREFIX}/bin/${SYMLINK_NAME}\")
387  else ()
388  basis_install_link (
389  "${INSTALL_DIR}/${OUTPUT_NAME}"
390  "bin/${SYMLINK_NAME}"
391  )
392  endif ()
393  endif ()
394  endif ()
395  endif ()
396  endforeach ()
397 endfunction ()
398 
399 # ============================================================================
400 # Package registration
401 # ============================================================================
402 
403 # ----------------------------------------------------------------------------
404 ## @brief Register installed package with CMake.
405 #
406 # This function adds an entry to the CMake registry for packages with the
407 # path of the directory where the package configuration file is located in
408 # order to help CMake find the package.
409 #
410 # The uninstaller whose template can be found in cmake_uninstaller.cmake.in
411 # is responsible for removing the registry entry again.
412 function (basis_register_package)
413  set (PKGDIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}")
414  set (PKGUID "${TOPLEVEL_PROJECT_PACKAGE_UID}")
415  if (WIN32)
416  install (CODE
417  "execute_process (
418  COMMAND reg add \"HKCU\\\\Software\\\\Kitware\\\\CMake\\\\Packages\\\\${PROJECT_PACKAGE_CONFIG_PREFIX}\" /v \"${PKGUID}\" /d \"${PKGDIR}\" /t REG_SZ /f
419  RESULT_VARIABLE RT
420  ERROR_VARIABLE ERR
421  OUTPUT_QUIET
422  )
423  if (RT EQUAL 0)
424  message (STATUS \"Register: Added HKEY_CURRENT_USER\\\\Software\\\\Kitware\\\\CMake\\\\Packages\\\\${PROJECT_PACKAGE_CONFIG_PREFIX}\\\\${PKGUID}\")
425  else ()
426  string (STRIP \"\${ERR}\" ERR)
427  message (STATUS \"Register: Failed to add registry entry: \${ERR}\")
428  endif ()"
429  )
430  elseif (IS_DIRECTORY "$ENV{HOME}")
431  file (WRITE "${BINARY_CONFIG_DIR}/${PROJECT_PACKAGE_CONFIG_PREFIX}RegistryFile" "${PKGDIR}")
432  install (
433  FILES "${BINARY_CONFIG_DIR}/${PROJECT_PACKAGE_CONFIG_PREFIX}RegistryFile"
434  DESTINATION "$ENV{HOME}/.cmake/packages/${PROJECT_PACKAGE_CONFIG_PREFIX}"
435  RENAME "${PKGUID}"
436  )
437  endif ()
438 endfunction ()
439 
440 # ============================================================================
441 # Deinstallation
442 # ============================================================================
443 
444 # ----------------------------------------------------------------------------
445 ## @brief Add uninstall target.
446 #
447 # @returns Adds the custom target @c uninstall and code to
448 # <tt>cmake_install.cmake</tt> to install an uninstaller.
449 function (basis_add_uninstall)
450  # add uninstall target
451  configure_file (
452  ${BASIS_MODULE_PATH}/cmake_uninstall.cmake.in
453  ${PROJECT_BINARY_DIR}/cmake_uninstall.cmake
454  @ONLY
455  )
456  add_custom_target (
457  uninstall
458  COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake"
459  COMMENT "Uninstalling..."
460  )
461 endfunction ()
462 
463 
464 ## @}
465 # end of Doxygen group
cmake INSTALL_CONFIG_DIR
Path of installation directory for CMake package configuration files relative to CMAKE_INSTALL_PREFIX...
cmake CMAKE_INSTALL_PREFIX
Installation prefix, i.e., root directory of installation.
cmake CMD
function basis_get_project_property(out VARIABLE, in ARGN)
Get project-global property value.
function basis_install()
Specify rules to run at install time.
macro basis_sanitize_for_regex(out OUT, in STR)
Sanitize string variable for use in regular expression.
cmake TOPLEVEL_PROJECT_PACKAGE_UID
UID of package in CMake&#39;s user package registry.
cmake NAME
function basis_install_template(in TEMPLATE, in DESTINATION)
Add installation rule for project template.
cmake __BASIS_INSTALLATIONTOOLS_INCLUDED
function basis_add_uninstall()
Add uninstall target.
function basis_install_links()
Adds installation rules to create default symbolic links.
cmake TEMPLATE
#define UNIX
Whether the sources are compiled on a Unix-based system.
Definition: config.h:60
cmake COMMAND
function basis_list_to_delimited_string(out STR, in DELIM, in ARGN)
Concatenates all list elements into a single delimited string.
function basis_install_directory(in ARGN)
Install content of source directory excluding typical files.
function basis_install_link(in OLD, in NEW)
Add installation rule to create a symbolic link.
function get_filename_component(inout ARGN)
Fixes CMake&#39;s get_filename_component() command.
function basis_get_target_location(out VAR, in TARGET_NAME, in PART)
Get location of build target output file(s).