forked from rraju1/cmake-buildscripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LibraryUtils.cmake
235 lines (174 loc) · 9.04 KB
/
LibraryUtils.cmake
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# Script containing several advanced functions for handling libraries in CMake.
# linker flag prefix -- if a link library starts with this character, it will be ignored by import_libraries()
# this is needed because FindMKL can return linker flags mixed with its libraries (which is actually the official CMake way of doing things)
if(TARGET_WINDOWS AND NOT MINGW)
set(LINKER_FLAG_PREFIX "/") # stupid illogical MSVC command-line format...
else()
set(LINKER_FLAG_PREFIX "-")
endif()
#Unfortunately, CMake doesn't let you import a library without knowing whether it is shared or static, and there's no easy way to tell which it is.
#sets OUTPUT_VARAIBLE to "IMPORT", "SHARED", or "STATIC" depending on the library passed
function(get_lib_type LIBRARY OUTPUT_VARIABLE)
if(NOT EXISTS ${LIBRARY})
message(FATAL_ERROR "get_lib_type(): library ${LIBRARY} does not exist!")
endif()
get_filename_component(LIB_NAME ${LIBRARY} NAME)
set(CACHE_VAR_NAME GET_LIB_TYPE_CACHED_RESULT_${LIB_NAME})
if((DEFINED ${CACHE_VAR_NAME}) AND ("${${CACHE_VAR_NAME}}" STREQUAL "SHARED" OR "${${CACHE_VAR_NAME}}" STREQUAL "STATIC" OR "${${CACHE_VAR_NAME}}" STREQUAL "IMPORT"))
# use cache variable
set(${OUTPUT_VARIABLE} ${${CACHE_VAR_NAME}} PARENT_SCOPE)
return()
endif()
# first, check for import libraries
if(TARGET_WINDOWS)
if(MINGW)
# on MinGW, import libraries have a different file extension, so our job is easy.
if(${LIB_NAME} MATCHES ".*${CMAKE_IMPORT_LIBRARY_SUFFIX}")
set(LIB_TYPE IMPORT)
endif()
else() # MSVC, Intel, or some other Windows compiler
# we have to work a little harder, and use Dumpbin to check the library type, since import and static libraries have the same extensions
find_program(DUMPBIN dumpbin)
if(NOT DUMPBIN)
message(FATAL_ERROR "The Microsoft Dumpbin tool was not found. It is needed to analyze libraries, so please set the DUMPBIN variable to point to it.")
endif()
# NOTE: this can take around 2-5 seconds for large libraries -- hence why we cache the result of this function
execute_process(COMMAND ${DUMPBIN} -headers ${LIBRARY} OUTPUT_VARIABLE DUMPBIN_OUTPUT ERROR_VARIABLE DUMPBIN_ERROUT RESULT_VARIABLE DUMPBIN_RESULT)
# sanity check
if(NOT ${DUMPBIN_RESULT} EQUAL 0)
message(FATAL_ERROR "Could not analyze the type of library ${LIBRARY}: dumpbin failed to execute with output ${DUMPBIN_OUTPUT} and error message ${DUMPBIN_ERROUT}")
endif()
# check for dynamic symbol entries
# https://stackoverflow.com/questions/488809/tools-for-inspecting-lib-files
if("${DUMPBIN_OUTPUT}" MATCHES "Symbol name :")
# found one! It's an import library!
set(LIB_TYPE IMPORT)
else()
# by process of elimination, it's a static library
set(LIB_TYPE STATIC)
endif()
endif()
endif()
# now we can figure the rest out by suffix matching
if((NOT TARGET_WINDOWS) OR (TARGET_WINDOWS AND MINGW AND "${LIB_TYPE}" STREQUAL ""))
if(${LIB_NAME} MATCHES ".*${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(LIB_TYPE SHARED)
elseif(${LIB_NAME} MATCHES ".*${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(LIB_TYPE STATIC)
else()
message(FATAL_ERROR "Could not determine whether \"${LIBRARY}\" is a static or shared library, it does not have a known suffix.")
endif()
endif()
set(${OUTPUT_VARIABLE} ${LIB_TYPE} PARENT_SCOPE)
set(${CACHE_VAR_NAME} ${LIB_TYPE} CACHE INTERNAL "Result of get_lib_type() for ${LIB_NAME}" FORCE)
endfunction(get_lib_type)
# Takes a list of CMake libraries that you might pass to a target, and returns a list of resolved library paths that correspond to it.
# Some libraries are known by full path, others are known by link-name only (as in, the name you'd pass to "ld -l"). They'll be returned in order in LIB_PATHS.
# Accepts 5 types of thing: full paths to libraries, CMake library targets built by this project, CMake imported targets, CMake interface targets (returns their INTERFACE_LINK_LIBRARIES), and regular linker library names.
# For example, on my system,
# resolve_cmake_library_list("-Wl,--no-as-needed;mkl::core;mkl::threading;mkl::fortran_interface;/usr/lib/x86_64-linux-gnu/libdl.so;Threads::Threads;arpack;/usr/lib/x86_64-linux-gnu/libm.so")
# returns
# "-Wl,--no-as-needed;/home/jamie/anaconda3/lib/libmkl_core.so;/home/jamie/anaconda3/lib/libmkl_sequential.so;/home/jamie/anaconda3/lib/libmkl_gf_lp64.so;/usr/lib/x86_64-linux-gnu/libdl.so;-lpthread;arpack;/usr/lib/x86_64-linux-gnu/libm.so"
# usage: resolve_cmake_library_list(<path output var> <libs...>)
function(resolve_cmake_library_list LIB_PATH_OUTPUT)
# we don't want to add generator expressions to the library tracker...
string(GENEX_STRIP "${ARGN}" LIBS_TO_RESOLVE)
set(LIB_PATHS "")
foreach(LIBRARY ${LIBS_TO_RESOLVE})
if("${LIBRARY}" MATCHES "^${LINKER_FLAG_PREFIX}")
# linker flag
list(APPEND LIB_PATHS "${LIBRARY}")
elseif(EXISTS "${LIBRARY}")
# full path to library
list(APPEND LIB_PATHS "${LIBRARY}")
elseif(TARGET "${LIBRARY}")
get_property(TARGET_LIB_TYPE TARGET ${LIBRARY} PROPERTY TYPE)
# it's an error to check if an interface library has an imported location
if("${TARGET_LIB_TYPE}" STREQUAL "INTERFACE_LIBRARY")
# interface library -- but it will have dependencies that we need to get.
get_property(INTERFACE_LIB_DEPENDENCIES TARGET ${LIBRARY} PROPERTY INTERFACE_LINK_LIBRARIES)
if(NOT "${INTERFACE_LIB_DEPENDENCIES}" STREQUAL "")
# avoid crashing CMake if somebody accidentally made an interface library depend on itself
list(REMOVE_ITEM INTERFACE_LIB_DEPENDENCIES "${LIBRARY}")
# now parse those dependencies!
resolve_cmake_library_list(INTERFACE_DEPS_LIB_PATHS ${INTERFACE_LIB_DEPENDENCIES})
list(APPEND LIB_PATHS ${INTERFACE_DEPS_LIB_PATHS})
endif()
else()
# must be either imported or an actual target
get_property(LIBRARY_HAS_IMPORTED_LOCATION TARGET ${LIBRARY} PROPERTY IMPORTED_LOCATION SET)
if(LIBRARY_HAS_IMPORTED_LOCATION)
# CMake imported target
get_property(LIBRARY_IMPORTED_LOCATION TARGET ${LIBRARY} PROPERTY IMPORTED_LOCATION)
list(APPEND LIB_PATHS "${LIBRARY_IMPORTED_LOCATION}")
else()
# else it's a CMake target that is built by this project
# detect if the library has been renamed
get_property(LIB_NAME TARGET ${LIBRARY} PROPERTY OUTPUT_NAME)
if("${LIB_NAME}" STREQUAL "")
# use target name if output name was not set
set(LIB_NAME ${LIBRARY})
endif()
list(APPEND LIB_PATHS ${LIB_NAME})
endif()
endif()
else()
# otherwise it's a library name to find on the linker search path (using CMake in "naive mode")
list(APPEND LIB_PATHS ${LIBRARY})
endif()
endforeach()
# debugging code
if(FALSE)
message("resolve_cmake_library_list(${LIBS_TO_RESOLVE}):")
message(" -> ${LIB_PATHS}")
endif()
set(${LIB_PATH_OUTPUT} ${LIB_PATHS} PARENT_SCOPE)
endfunction(resolve_cmake_library_list)
# gets the "linker name" (the name you'd pass to "ld -l") for a library file.
function(get_linker_name LIBRARY_PATH OUTPUT_VARIABLE)
# get full library name
get_filename_component(LIBNAME "${LIBRARY_PATH}" NAME)
#remove prefix
string(REGEX REPLACE "^lib" "" LIBNAME "${LIBNAME}")
#remove numbers after the file extension
string(REGEX REPLACE "(\\.[0-9])+$" "" LIBNAME "${LIBNAME}")
#remove the file extension
get_lib_type(${LIBRARY_PATH} LIB_TYPE)
if("${LIB_TYPE}" STREQUAL "IMPORT")
string(REGEX REPLACE "${CMAKE_IMPORT_LIBRARY_SUFFIX}$" "" LIBNAME ${LIBNAME})
elseif("${LIB_TYPE}" STREQUAL "STATIC")
string(REGEX REPLACE "${CMAKE_STATIC_LIBRARY_SUFFIX}\$" "" LIBNAME ${LIBNAME})
else()
string(REGEX REPLACE "${CMAKE_SHARED_LIBRARY_SUFFIX}\$" "" LIBNAME ${LIBNAME})
endif()
set(${OUTPUT_VARIABLE} ${LIBNAME} PARENT_SCOPE)
endfunction(get_linker_name)
# Converts the result of resolve_cmake_library_list() into a "link line" ready to be passed to gcc or ld, and a
# list of directories to add to the search path.
macro(resolved_lib_list_to_link_line LINK_LINE_VAR DIRS_VAR) # ARGN: LIB_PATH_LIST
set(${LINK_LINE_VAR} "")
set(${DIRS_VAR} "")
foreach(LIBRARY ${ARGN})
if("${LIBRARY}" MATCHES "^${LINKER_FLAG_PREFIX}")
# linker flag -- put in library list to preserve ordering
list(APPEND ${LINK_LINE_VAR} "${LIBRARY}")
elseif("${LIBRARY}" MATCHES "(.+)\\.framework")
# OS X framework -- translate to "-framework" linker flag
get_filename_component(FRAMEWORK_BASENAME "${LIBRARY}" NAME_WE)
string(TOLOWER "${FRAMEWORK_BASENAME}" FRAMEWORK_BASENAME_LCASE)
list(APPEND ${LINK_LINE_VAR} "-framework" "${FRAMEWORK_BASENAME_LCASE}")
elseif(EXISTS "${LIBRARY}")
if(NOT IS_DIRECTORY "${LIBRARY}")
# full path to library
get_filename_component(LIB_DIR ${LIBRARY} DIRECTORY)
list(APPEND ${DIRS_VAR} ${LIB_DIR})
get_linker_name("${LIBRARY}" LIB_LINK_NAME)
list(APPEND ${LINK_LINE_VAR} "-l${LIB_LINK_NAME}")
endif()
else()
# target built by this project
list(APPEND ${LINK_LINE_VAR} "-l${LIBRARY}")
endif()
endforeach()
list(REMOVE_DUPLICATES ${DIRS_VAR})
endmacro(resolved_lib_list_to_link_line)