summaryrefslogtreecommitdiff
path: root/cmake/copy_runtime_deps.cmake
blob: 9317e74312cd20112425cf2ca895cd8d444769e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
if(NOT DEFINED APP_EXE OR NOT DEFINED APP_OUT_DIR)
  message(FATAL_ERROR "APP_EXE and APP_OUT_DIR must be set")
endif()

if(NOT EXISTS "${APP_EXE}")
  message(FATAL_ERROR "Executable not found: ${APP_EXE}")
endif()

set(_search_dirs "")
if((NOT DEFINED TOOLCHAIN_BIN_DIR OR TOOLCHAIN_BIN_DIR STREQUAL "") AND EXISTS "${APP_OUT_DIR}/CMakeCache.txt")
  file(
    STRINGS "${APP_OUT_DIR}/CMakeCache.txt"
    _compiler_line
    REGEX "^CMAKE_CXX_COMPILER:FILEPATH="
    LIMIT_COUNT 1
  )
  if(_compiler_line)
    string(REPLACE "CMAKE_CXX_COMPILER:FILEPATH=" "" TOOLCHAIN_BIN_DIR "${_compiler_line}")
    get_filename_component(TOOLCHAIN_BIN_DIR "${TOOLCHAIN_BIN_DIR}" DIRECTORY)
  endif()
endif()

if(DEFINED TOOLCHAIN_BIN_DIR AND NOT TOOLCHAIN_BIN_DIR STREQUAL "" AND EXISTS "${TOOLCHAIN_BIN_DIR}")
  list(APPEND _search_dirs "${TOOLCHAIN_BIN_DIR}")
endif()

if(NOT DEFINED TOOLCHAIN_BIN_DIR OR TOOLCHAIN_BIN_DIR STREQUAL "" OR NOT EXISTS "${TOOLCHAIN_BIN_DIR}")
  message(FATAL_ERROR "TOOLCHAIN_BIN_DIR is required and must exist")
endif()

file(TO_CMAKE_PATH "${TOOLCHAIN_BIN_DIR}" _toolchain_bin_dir_norm)
string(TOLOWER "${_toolchain_bin_dir_norm}" _toolchain_bin_dir_norm_lower)

function(_literal_to_case_insensitive_regex _literal _out_var)
  string(LENGTH "${_literal}" _len)
  if(_len EQUAL 0)
    set(${_out_var} "" PARENT_SCOPE)
    return()
  endif()

  set(_regex "")
  math(EXPR _last "${_len} - 1")
  foreach(_i RANGE 0 ${_last})
    string(SUBSTRING "${_literal}" ${_i} 1 _ch)
    if(_ch MATCHES "[A-Za-z]")
      string(TOUPPER "${_ch}" _up)
      string(TOLOWER "${_ch}" _lo)
      string(APPEND _regex "[${_up}${_lo}]")
    elseif(_ch STREQUAL ".")
      string(APPEND _regex "\\\\.")
    else()
      string(APPEND _regex "${_ch}")
    endif()
  endforeach()

  set(${_out_var} "${_regex}" PARENT_SCOPE)
endfunction()

set(_pre_exclude_regexes
  "^api-ms-win-.*"
  "^ext-ms-.*"
)

set(_windows_system_dlls
  "KERNEL32.dll"
  "USER32.dll"
  "GDI32.dll"
  "SHELL32.dll"
  "WINMM.dll"
  "OPENGL32.dll"
  "ADVAPI32.dll"
  "OLE32.dll"
  "OLEAUT32.dll"
  "COMDLG32.dll"
  "WS2_32.dll"
  "SECUR32.dll"
  "NTDLL.dll"
)

foreach(_dll IN LISTS _windows_system_dlls)
  _literal_to_case_insensitive_regex("${_dll}" _dll_regex)
  list(APPEND _pre_exclude_regexes "(^|.*[/\\\\])${_dll_regex}$")
endforeach()

file(GET_RUNTIME_DEPENDENCIES
  EXECUTABLES "${APP_EXE}"
  RESOLVED_DEPENDENCIES_VAR _resolved_deps
  UNRESOLVED_DEPENDENCIES_VAR _unresolved_deps
  CONFLICTING_DEPENDENCIES_PREFIX _conflicting_deps
  DIRECTORIES ${_search_dirs}
  PRE_EXCLUDE_REGEXES ${_pre_exclude_regexes}
  POST_EXCLUDE_REGEXES
    ".*[/\\\\]Windows[/\\\\]System32[/\\\\].*"
)

if(_unresolved_deps)
  message(STATUS "Unresolved runtime dependencies: ${_unresolved_deps}")
endif()

if(_conflicting_deps_FILENAMES)
  message(STATUS "Conflicting runtime dependency names: ${_conflicting_deps_FILENAMES}")
endif()

foreach(_dep IN LISTS _resolved_deps)
  file(TO_CMAKE_PATH "${_dep}" _dep_norm)
  string(TOLOWER "${_dep_norm}" _dep_norm_lower)
  if(NOT _dep_norm_lower MATCHES "^${_toolchain_bin_dir_norm_lower}/")
    continue()
  endif()

  get_filename_component(_name "${_dep}" NAME)
  if(_name STREQUAL "")
    continue()
  endif()

  execute_process(
    COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${_dep}" "${APP_OUT_DIR}/${_name}"
    RESULT_VARIABLE _copy_rc
  )
  if(NOT _copy_rc EQUAL 0)
    message(FATAL_ERROR "Failed to copy runtime dependency: ${_dep}")
  endif()
endforeach()