1 |
# - Functions to analyze and list executable file prerequisites. |
2 |
# This module provides functions to list the .dll, .dylib or .so |
3 |
# files that an executable or shared library file depends on. (Its |
4 |
# prerequisites.) |
5 |
# |
6 |
# It uses various tools to obtain the list of required shared library files: |
7 |
# dumpbin (Windows) |
8 |
# ldd (Linux/Unix) |
9 |
# otool (Mac OSX) |
10 |
# The following functions are provided by this module: |
11 |
# get_prerequisites |
12 |
# list_prerequisites |
13 |
# list_prerequisites_by_glob |
14 |
# gp_append_unique |
15 |
# is_file_executable |
16 |
# gp_item_default_embedded_path |
17 |
# (projects can override with gp_item_default_embedded_path_override) |
18 |
# gp_resolve_item |
19 |
# (projects can override with gp_resolve_item_override) |
20 |
# gp_resolved_file_type |
21 |
# (projects can override with gp_resolved_file_type_override) |
22 |
# gp_file_type |
23 |
# Requires CMake 2.6 or greater because it uses function, break, return and |
24 |
# PARENT_SCOPE. |
25 |
# |
26 |
# GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse> |
27 |
# <dirs>) |
28 |
# Get the list of shared library files required by <target>. The list in |
29 |
# the variable named <prerequisites_var> should be empty on first entry to |
30 |
# this function. On exit, <prerequisites_var> will contain the list of |
31 |
# required shared library files. |
32 |
# |
33 |
# <target> is the full path to an executable file. <prerequisites_var> is the |
34 |
# name of a CMake variable to contain the results. <exclude_system> must be 0 |
35 |
# or 1 indicating whether to include or exclude "system" prerequisites. If |
36 |
# <recurse> is set to 1 all prerequisites will be found recursively, if set to |
37 |
# 0 only direct prerequisites are listed. <exepath> is the path to the top |
38 |
# level executable used for @executable_path replacment on the Mac. <dirs> is |
39 |
# a list of paths where libraries might be found: these paths are searched |
40 |
# first when a target without any path info is given. Then standard system |
41 |
# locations are also searched: PATH, Framework locations, /usr/lib... |
42 |
# |
43 |
# LIST_PREREQUISITES(<target> [<recurse> [<exclude_system> [<verbose>]]]) |
44 |
# Print a message listing the prerequisites of <target>. |
45 |
# |
46 |
# <target> is the name of a shared library or executable target or the full |
47 |
# path to a shared library or executable file. If <recurse> is set to 1 all |
48 |
# prerequisites will be found recursively, if set to 0 only direct |
49 |
# prerequisites are listed. <exclude_system> must be 0 or 1 indicating whether |
50 |
# to include or exclude "system" prerequisites. With <verbose> set to 0 only |
51 |
# the full path names of the prerequisites are printed, set to 1 extra |
52 |
# informatin will be displayed. |
53 |
# |
54 |
# LIST_PREREQUISITES_BY_GLOB(<glob_arg> <glob_exp>) |
55 |
# Print the prerequisites of shared library and executable files matching a |
56 |
# globbing pattern. <glob_arg> is GLOB or GLOB_RECURSE and <glob_exp> is a |
57 |
# globbing expression used with "file(GLOB" or "file(GLOB_RECURSE" to retrieve |
58 |
# a list of matching files. If a matching file is executable, its prerequisites |
59 |
# are listed. |
60 |
# |
61 |
# Any additional (optional) arguments provided are passed along as the |
62 |
# optional arguments to the list_prerequisites calls. |
63 |
# |
64 |
# GP_APPEND_UNIQUE(<list_var> <value>) |
65 |
# Append <value> to the list variable <list_var> only if the value is not |
66 |
# already in the list. |
67 |
# |
68 |
# IS_FILE_EXECUTABLE(<file> <result_var>) |
69 |
# Return 1 in <result_var> if <file> is a binary executable, 0 otherwise. |
70 |
# |
71 |
# GP_ITEM_DEFAULT_EMBEDDED_PATH(<item> <default_embedded_path_var>) |
72 |
# Return the path that others should refer to the item by when the item |
73 |
# is embedded inside a bundle. |
74 |
# |
75 |
# Override on a per-project basis by providing a project-specific |
76 |
# gp_item_default_embedded_path_override function. |
77 |
# |
78 |
# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>) |
79 |
# Resolve an item into an existing full path file. |
80 |
# |
81 |
# Override on a per-project basis by providing a project-specific |
82 |
# gp_resolve_item_override function. |
83 |
# |
84 |
# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>) |
85 |
# Return the type of <file> with respect to <original_file>. String |
86 |
# describing type of prerequisite is returned in variable named <type_var>. |
87 |
# |
88 |
# Use <exepath> and <dirs> if necessary to resolve non-absolute <file> |
89 |
# values -- but only for non-embedded items. |
90 |
# |
91 |
# Possible types are: |
92 |
# system |
93 |
# local |
94 |
# embedded |
95 |
# other |
96 |
# Override on a per-project basis by providing a project-specific |
97 |
# gp_resolved_file_type_override function. |
98 |
# |
99 |
# GP_FILE_TYPE(<original_file> <file> <type_var>) |
100 |
# Return the type of <file> with respect to <original_file>. String |
101 |
# describing type of prerequisite is returned in variable named <type_var>. |
102 |
# |
103 |
# Possible types are: |
104 |
# system |
105 |
# local |
106 |
# embedded |
107 |
# other |
108 |
|
109 |
#============================================================================= |
110 |
# Copyright 2008-2009 Kitware, Inc. |
111 |
# |
112 |
# Distributed under the OSI-approved BSD License (the "License"); |
113 |
# see accompanying file Copyright.txt for details. |
114 |
# |
115 |
# This software is distributed WITHOUT ANY WARRANTY; without even the |
116 |
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
117 |
# See the License for more information. |
118 |
#============================================================================= |
119 |
# (To distribute this file outside of CMake, substitute the full |
120 |
# License text for the above reference.) |
121 |
|
122 |
function(gp_append_unique list_var value) |
123 |
set(contains 0) |
124 |
|
125 |
foreach(item ${${list_var}}) |
126 |
if("${item}" STREQUAL "${value}") |
127 |
set(contains 1) |
128 |
break() |
129 |
endif("${item}" STREQUAL "${value}") |
130 |
endforeach(item) |
131 |
|
132 |
if(NOT contains) |
133 |
set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE) |
134 |
endif(NOT contains) |
135 |
endfunction(gp_append_unique) |
136 |
|
137 |
|
138 |
function(is_file_executable file result_var) |
139 |
# |
140 |
# A file is not executable until proven otherwise: |
141 |
# |
142 |
set(${result_var} 0 PARENT_SCOPE) |
143 |
|
144 |
get_filename_component(file_full "${file}" ABSOLUTE) |
145 |
string(TOLOWER "${file_full}" file_full_lower) |
146 |
|
147 |
# If file name ends in .exe on Windows, *assume* executable: |
148 |
# |
149 |
if(WIN32 AND NOT UNIX) |
150 |
if("${file_full_lower}" MATCHES "\\.exe$") |
151 |
set(${result_var} 1 PARENT_SCOPE) |
152 |
return() |
153 |
endif("${file_full_lower}" MATCHES "\\.exe$") |
154 |
|
155 |
# A clause could be added here that uses output or return value of dumpbin |
156 |
# to determine ${result_var}. In 99%+? practical cases, the exe name |
157 |
# match will be sufficient... |
158 |
# |
159 |
endif(WIN32 AND NOT UNIX) |
160 |
|
161 |
# Use the information returned from the Unix shell command "file" to |
162 |
# determine if ${file_full} should be considered an executable file... |
163 |
# |
164 |
# If the file command's output contains "executable" and does *not* contain |
165 |
# "text" then it is likely an executable suitable for prerequisite analysis |
166 |
# via the get_prerequisites macro. |
167 |
# |
168 |
if(UNIX) |
169 |
if(NOT file_cmd) |
170 |
find_program(file_cmd "file") |
171 |
endif(NOT file_cmd) |
172 |
|
173 |
if(file_cmd) |
174 |
execute_process(COMMAND "${file_cmd}" "${file_full}" |
175 |
OUTPUT_VARIABLE file_ov |
176 |
OUTPUT_STRIP_TRAILING_WHITESPACE |
177 |
) |
178 |
|
179 |
# Replace the name of the file in the output with a placeholder token |
180 |
# (the string " _file_full_ ") so that just in case the path name of |
181 |
# the file contains the word "text" or "executable" we are not fooled |
182 |
# into thinking "the wrong thing" because the file name matches the |
183 |
# other 'file' command output we are looking for... |
184 |
# |
185 |
string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}") |
186 |
string(TOLOWER "${file_ov}" file_ov) |
187 |
|
188 |
#message(STATUS "file_ov='${file_ov}'") |
189 |
if("${file_ov}" MATCHES "executable") |
190 |
#message(STATUS "executable!") |
191 |
if("${file_ov}" MATCHES "text") |
192 |
#message(STATUS "but text, so *not* a binary executable!") |
193 |
else("${file_ov}" MATCHES "text") |
194 |
set(${result_var} 1 PARENT_SCOPE) |
195 |
return() |
196 |
endif("${file_ov}" MATCHES "text") |
197 |
endif("${file_ov}" MATCHES "executable") |
198 |
else(file_cmd) |
199 |
message(STATUS "warning: No 'file' command, skipping execute_process...") |
200 |
endif(file_cmd) |
201 |
endif(UNIX) |
202 |
endfunction(is_file_executable) |
203 |
|
204 |
|
205 |
function(gp_item_default_embedded_path item default_embedded_path_var) |
206 |
|
207 |
# On Windows and Linux, "embed" prerequisites in the same directory |
208 |
# as the executable by default: |
209 |
# |
210 |
set(path "@executable_path") |
211 |
set(overridden 0) |
212 |
|
213 |
# On the Mac, relative to the executable depending on the type |
214 |
# of the thing we are embedding: |
215 |
# |
216 |
if(APPLE) |
217 |
# |
218 |
# The assumption here is that all executables in the bundle will be |
219 |
# in same-level-directories inside the bundle. The parent directory |
220 |
# of an executable inside the bundle should be MacOS or a sibling of |
221 |
# MacOS and all embedded paths returned from here will begin with |
222 |
# "@executable_path/../" and will work from all executables in all |
223 |
# such same-level-directories inside the bundle. |
224 |
# |
225 |
|
226 |
# By default, embed things right next to the main bundle executable: |
227 |
# |
228 |
set(path "@executable_path/../../Contents/MacOS") |
229 |
|
230 |
# Embed .dylibs right next to the main bundle executable: |
231 |
# |
232 |
if(item MATCHES "\\.dylib$") |
233 |
set(path "@executable_path/../MacOS") |
234 |
set(overridden 1) |
235 |
endif(item MATCHES "\\.dylib$") |
236 |
|
237 |
# Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS): |
238 |
# |
239 |
if(NOT overridden) |
240 |
if(item MATCHES "[^/]+\\.framework/") |
241 |
set(path "@executable_path/../Frameworks") |
242 |
set(overridden 1) |
243 |
endif(item MATCHES "[^/]+\\.framework/") |
244 |
endif(NOT overridden) |
245 |
endif() |
246 |
|
247 |
# Provide a hook so that projects can override the default embedded location |
248 |
# of any given library by whatever logic they choose: |
249 |
# |
250 |
if(COMMAND gp_item_default_embedded_path_override) |
251 |
gp_item_default_embedded_path_override("${item}" path) |
252 |
endif(COMMAND gp_item_default_embedded_path_override) |
253 |
|
254 |
set(${default_embedded_path_var} "${path}" PARENT_SCOPE) |
255 |
endfunction(gp_item_default_embedded_path) |
256 |
|
257 |
|
258 |
function(gp_resolve_item context item exepath dirs resolved_item_var) |
259 |
set(resolved 0) |
260 |
set(resolved_item "${item}") |
261 |
|
262 |
# Is it already resolved? |
263 |
# |
264 |
if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}") |
265 |
set(resolved 1) |
266 |
endif(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}") |
267 |
|
268 |
if(NOT resolved) |
269 |
if(item MATCHES "@executable_path") |
270 |
# |
271 |
# @executable_path references are assumed relative to exepath |
272 |
# |
273 |
string(REPLACE "@executable_path" "${exepath}" ri "${item}") |
274 |
get_filename_component(ri "${ri}" ABSOLUTE) |
275 |
|
276 |
if(EXISTS "${ri}") |
277 |
#message(STATUS "info: embedded item exists (${ri})") |
278 |
set(resolved 1) |
279 |
set(resolved_item "${ri}") |
280 |
else(EXISTS "${ri}") |
281 |
message(STATUS "warning: embedded item does not exist '${ri}'") |
282 |
endif(EXISTS "${ri}") |
283 |
endif(item MATCHES "@executable_path") |
284 |
endif(NOT resolved) |
285 |
|
286 |
if(NOT resolved) |
287 |
if(item MATCHES "@loader_path") |
288 |
# |
289 |
# @loader_path references are assumed relative to the |
290 |
# PATH of the given "context" (presumably another library) |
291 |
# |
292 |
get_filename_component(contextpath "${context}" PATH) |
293 |
string(REPLACE "@loader_path" "${contextpath}" ri "${item}") |
294 |
get_filename_component(ri "${ri}" ABSOLUTE) |
295 |
|
296 |
if(EXISTS "${ri}") |
297 |
#message(STATUS "info: embedded item exists (${ri})") |
298 |
set(resolved 1) |
299 |
set(resolved_item "${ri}") |
300 |
else(EXISTS "${ri}") |
301 |
message(STATUS "warning: embedded item does not exist '${ri}'") |
302 |
endif(EXISTS "${ri}") |
303 |
endif(item MATCHES "@loader_path") |
304 |
endif(NOT resolved) |
305 |
|
306 |
if(NOT resolved) |
307 |
set(ri "ri-NOTFOUND") |
308 |
find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH) |
309 |
find_file(ri "${item}" ${exepath} ${dirs} /usr/lib) |
310 |
if(ri) |
311 |
#message(STATUS "info: 'find_file' in exepath/dirs (${ri})") |
312 |
set(resolved 1) |
313 |
set(resolved_item "${ri}") |
314 |
set(ri "ri-NOTFOUND") |
315 |
endif(ri) |
316 |
endif(NOT resolved) |
317 |
|
318 |
if(NOT resolved) |
319 |
if(item MATCHES "[^/]+\\.framework/") |
320 |
set(fw "fw-NOTFOUND") |
321 |
find_file(fw "${item}" |
322 |
"~/Library/Frameworks" |
323 |
"/Library/Frameworks" |
324 |
"/System/Library/Frameworks" |
325 |
) |
326 |
if(fw) |
327 |
#message(STATUS "info: 'find_file' found framework (${fw})") |
328 |
set(resolved 1) |
329 |
set(resolved_item "${fw}") |
330 |
set(fw "fw-NOTFOUND") |
331 |
endif(fw) |
332 |
endif(item MATCHES "[^/]+\\.framework/") |
333 |
endif(NOT resolved) |
334 |
|
335 |
# Using find_program on Windows will find dll files that are in the PATH. |
336 |
# (Converting simple file names into full path names if found.) |
337 |
# |
338 |
if(WIN32 AND NOT UNIX) |
339 |
if(NOT resolved) |
340 |
set(ri "ri-NOTFOUND") |
341 |
find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH) |
342 |
find_program(ri "${item}" PATHS "${exepath};${dirs}") |
343 |
if(ri) |
344 |
#message(STATUS "info: 'find_program' in exepath/dirs (${ri})") |
345 |
set(resolved 1) |
346 |
set(resolved_item "${ri}") |
347 |
set(ri "ri-NOTFOUND") |
348 |
endif(ri) |
349 |
endif(NOT resolved) |
350 |
endif(WIN32 AND NOT UNIX) |
351 |
|
352 |
# Provide a hook so that projects can override item resolution |
353 |
# by whatever logic they choose: |
354 |
# |
355 |
if(COMMAND gp_resolve_item_override) |
356 |
gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved) |
357 |
endif(COMMAND gp_resolve_item_override) |
358 |
|
359 |
if(NOT resolved) |
360 |
message(STATUS " |
361 |
warning: cannot resolve item '${item}' |
362 |
|
363 |
possible problems: |
364 |
need more directories? |
365 |
need to use InstallRequiredSystemLibraries? |
366 |
run in install tree instead of build tree? |
367 |
") |
368 |
# message(STATUS " |
369 |
#****************************************************************************** |
370 |
#warning: cannot resolve item '${item}' |
371 |
# |
372 |
# possible problems: |
373 |
# need more directories? |
374 |
# need to use InstallRequiredSystemLibraries? |
375 |
# run in install tree instead of build tree? |
376 |
# |
377 |
# context='${context}' |
378 |
# item='${item}' |
379 |
# exepath='${exepath}' |
380 |
# dirs='${dirs}' |
381 |
# resolved_item_var='${resolved_item_var}' |
382 |
#****************************************************************************** |
383 |
#") |
384 |
endif(NOT resolved) |
385 |
|
386 |
set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE) |
387 |
endfunction(gp_resolve_item) |
388 |
|
389 |
|
390 |
function(gp_resolved_file_type original_file file exepath dirs type_var) |
391 |
#message(STATUS "**") |
392 |
|
393 |
if(NOT IS_ABSOLUTE "${original_file}") |
394 |
message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file") |
395 |
endif() |
396 |
|
397 |
set(is_embedded 0) |
398 |
set(is_local 0) |
399 |
set(is_system 0) |
400 |
|
401 |
set(resolved_file "${file}") |
402 |
|
403 |
if("${file}" MATCHES "^@(executable|loader)_path") |
404 |
set(is_embedded 1) |
405 |
endif() |
406 |
|
407 |
if(NOT is_embedded) |
408 |
if(NOT IS_ABSOLUTE "${file}") |
409 |
gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file) |
410 |
endif() |
411 |
|
412 |
string(TOLOWER "${original_file}" original_lower) |
413 |
string(TOLOWER "${resolved_file}" lower) |
414 |
|
415 |
if(UNIX) |
416 |
if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)") |
417 |
set(is_system 1) |
418 |
endif() |
419 |
endif() |
420 |
|
421 |
if(APPLE) |
422 |
if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)") |
423 |
set(is_system 1) |
424 |
endif() |
425 |
endif() |
426 |
|
427 |
if(WIN32) |
428 |
string(TOLOWER "$ENV{SystemRoot}" sysroot) |
429 |
string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}") |
430 |
|
431 |
string(TOLOWER "$ENV{windir}" windir) |
432 |
string(REGEX REPLACE "\\\\" "/" windir "${windir}") |
433 |
|
434 |
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)") |
435 |
set(is_system 1) |
436 |
endif() |
437 |
|
438 |
if(UNIX) |
439 |
# if cygwin, we can get the properly formed windows paths from cygpath |
440 |
find_program(CYGPATH_EXECUTABLE cygpath) |
441 |
|
442 |
if(CYGPATH_EXECUTABLE) |
443 |
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W |
444 |
OUTPUT_VARIABLE env_windir |
445 |
OUTPUT_STRIP_TRAILING_WHITESPACE) |
446 |
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S |
447 |
OUTPUT_VARIABLE env_sysdir |
448 |
OUTPUT_STRIP_TRAILING_WHITESPACE) |
449 |
string(TOLOWER "${env_windir}" windir) |
450 |
string(TOLOWER "${env_sysdir}" sysroot) |
451 |
|
452 |
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)") |
453 |
set(is_system 1) |
454 |
endif() |
455 |
endif(CYGPATH_EXECUTABLE) |
456 |
endif(UNIX) |
457 |
endif(WIN32) |
458 |
|
459 |
if(NOT is_system) |
460 |
get_filename_component(original_path "${original_lower}" PATH) |
461 |
get_filename_component(path "${lower}" PATH) |
462 |
if("${original_path}" STREQUAL "${path}") |
463 |
set(is_local 1) |
464 |
endif() |
465 |
endif() |
466 |
endif() |
467 |
|
468 |
# Return type string based on computed booleans: |
469 |
# |
470 |
set(type "other") |
471 |
|
472 |
if(is_system) |
473 |
set(type "system") |
474 |
elseif(is_embedded) |
475 |
set(type "embedded") |
476 |
elseif(is_local) |
477 |
set(type "local") |
478 |
endif() |
479 |
|
480 |
#message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'") |
481 |
#message(STATUS " type: '${type}'") |
482 |
|
483 |
if(NOT is_embedded) |
484 |
if(NOT IS_ABSOLUTE "${resolved_file}") |
485 |
if(lower MATCHES "^msvc[^/]+dll" AND is_system) |
486 |
message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'") |
487 |
else() |
488 |
message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect") |
489 |
endif() |
490 |
endif() |
491 |
endif() |
492 |
|
493 |
# Provide a hook so that projects can override the decision on whether a |
494 |
# library belongs to the system or not by whatever logic they choose: |
495 |
# |
496 |
if(COMMAND gp_resolved_file_type_override) |
497 |
gp_resolved_file_type_override("${resolved_file}" type) |
498 |
endif() |
499 |
|
500 |
set(${type_var} "${type}" PARENT_SCOPE) |
501 |
|
502 |
#message(STATUS "**") |
503 |
endfunction() |
504 |
|
505 |
|
506 |
function(gp_file_type original_file file type_var) |
507 |
if(NOT IS_ABSOLUTE "${original_file}") |
508 |
message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file") |
509 |
endif() |
510 |
|
511 |
get_filename_component(exepath "${original_file}" PATH) |
512 |
|
513 |
set(type "") |
514 |
gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type) |
515 |
|
516 |
set(${type_var} "${type}" PARENT_SCOPE) |
517 |
endfunction(gp_file_type) |
518 |
|
519 |
|
520 |
function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs) |
521 |
set(verbose 0) |
522 |
set(eol_char "E") |
523 |
|
524 |
if(NOT IS_ABSOLUTE "${target}") |
525 |
message("warning: target '${target}' is not absolute...") |
526 |
endif(NOT IS_ABSOLUTE "${target}") |
527 |
|
528 |
if(NOT EXISTS "${target}") |
529 |
message("warning: target '${target}' does not exist...") |
530 |
endif(NOT EXISTS "${target}") |
531 |
|
532 |
# <setup-gp_tool-vars> |
533 |
# |
534 |
# Try to choose the right tool by default. Caller can set gp_tool prior to |
535 |
# calling this function to force using a different tool. |
536 |
# |
537 |
if("${gp_tool}" STREQUAL "") |
538 |
set(gp_tool "ldd") |
539 |
if(APPLE) |
540 |
set(gp_tool "otool") |
541 |
endif(APPLE) |
542 |
if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har! |
543 |
set(gp_tool "dumpbin") |
544 |
endif(WIN32 AND NOT UNIX) |
545 |
endif("${gp_tool}" STREQUAL "") |
546 |
|
547 |
set(gp_tool_known 0) |
548 |
|
549 |
if("${gp_tool}" STREQUAL "ldd") |
550 |
set(gp_cmd_args "") |
551 |
set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$") |
552 |
set(gp_regex_error "not found${eol_char}$") |
553 |
set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$") |
554 |
set(gp_regex_cmp_count 1) |
555 |
set(gp_tool_known 1) |
556 |
endif("${gp_tool}" STREQUAL "ldd") |
557 |
|
558 |
if("${gp_tool}" STREQUAL "otool") |
559 |
set(gp_cmd_args "-L") |
560 |
set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$") |
561 |
set(gp_regex_error "") |
562 |
set(gp_regex_fallback "") |
563 |
set(gp_regex_cmp_count 3) |
564 |
set(gp_tool_known 1) |
565 |
endif("${gp_tool}" STREQUAL "otool") |
566 |
|
567 |
if("${gp_tool}" STREQUAL "dumpbin") |
568 |
set(gp_cmd_args "/dependents") |
569 |
set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$") |
570 |
set(gp_regex_error "") |
571 |
set(gp_regex_fallback "") |
572 |
set(gp_regex_cmp_count 1) |
573 |
set(gp_tool_known 1) |
574 |
set(ENV{VS_UNICODE_OUTPUT} "") # Block extra output from inside VS IDE. |
575 |
endif("${gp_tool}" STREQUAL "dumpbin") |
576 |
|
577 |
if(NOT gp_tool_known) |
578 |
message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...") |
579 |
message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'") |
580 |
message(STATUS "Valid gp_tool values are dumpbin, ldd and otool.") |
581 |
return() |
582 |
endif(NOT gp_tool_known) |
583 |
|
584 |
set(gp_cmd_paths ${gp_cmd_paths} |
585 |
"C:/Program Files/Microsoft Visual Studio 9.0/VC/bin" |
586 |
"C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin" |
587 |
"C:/Program Files/Microsoft Visual Studio 8/VC/BIN" |
588 |
"C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN" |
589 |
"C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN" |
590 |
"C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN" |
591 |
"/usr/local/bin" |
592 |
"/usr/bin" |
593 |
) |
594 |
|
595 |
find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths}) |
596 |
|
597 |
if(NOT gp_cmd) |
598 |
message(STATUS "warning: could not find '${gp_tool}' - cannot analyze prerequisites...") |
599 |
return() |
600 |
endif(NOT gp_cmd) |
601 |
|
602 |
if("${gp_tool}" STREQUAL "dumpbin") |
603 |
# When running dumpbin, it also needs the "Common7/IDE" directory in the |
604 |
# PATH. It will already be in the PATH if being run from a Visual Studio |
605 |
# command prompt. Add it to the PATH here in case we are running from a |
606 |
# different command prompt. |
607 |
# |
608 |
get_filename_component(gp_cmd_dir "${gp_cmd}" PATH) |
609 |
get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE) |
610 |
# Use cmake paths as a user may have a PATH element ending with a backslash. |
611 |
# This will escape the list delimiter and create havoc! |
612 |
if(EXISTS "${gp_cmd_dlls_dir}") |
613 |
# only add to the path if it is not already in the path |
614 |
set(gp_found_cmd_dlls_dir 0) |
615 |
file(TO_CMAKE_PATH "$ENV{PATH}" env_path) |
616 |
foreach(gp_env_path_element ${env_path}) |
617 |
if("${gp_env_path_element}" STREQUAL "${gp_cmd_dlls_dir}") |
618 |
set(gp_found_cmd_dlls_dir 1) |
619 |
endif() |
620 |
endforeach(gp_env_path_element) |
621 |
|
622 |
if(NOT gp_found_cmd_dlls_dir) |
623 |
file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir) |
624 |
set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}") |
625 |
endif() |
626 |
endif(EXISTS "${gp_cmd_dlls_dir}") |
627 |
endif("${gp_tool}" STREQUAL "dumpbin") |
628 |
# |
629 |
# </setup-gp_tool-vars> |
630 |
|
631 |
if("${gp_tool}" STREQUAL "ldd") |
632 |
set(old_ld_env "$ENV{LD_LIBRARY_PATH}") |
633 |
foreach(dir ${exepath} ${dirs}) |
634 |
set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}") |
635 |
endforeach(dir) |
636 |
endif("${gp_tool}" STREQUAL "ldd") |
637 |
|
638 |
|
639 |
# Track new prerequisites at each new level of recursion. Start with an |
640 |
# empty list at each level: |
641 |
# |
642 |
set(unseen_prereqs) |
643 |
|
644 |
# Run gp_cmd on the target: |
645 |
# |
646 |
execute_process( |
647 |
COMMAND ${gp_cmd} ${gp_cmd_args} ${target} |
648 |
OUTPUT_VARIABLE gp_cmd_ov |
649 |
) |
650 |
|
651 |
if("${gp_tool}" STREQUAL "ldd") |
652 |
set(ENV{LD_LIBRARY_PATH} "${old_ld_env}") |
653 |
endif("${gp_tool}" STREQUAL "ldd") |
654 |
|
655 |
if(verbose) |
656 |
message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>") |
657 |
message(STATUS "gp_cmd_ov='${gp_cmd_ov}'") |
658 |
message(STATUS "</RawOutput>") |
659 |
endif(verbose) |
660 |
|
661 |
get_filename_component(target_dir "${target}" PATH) |
662 |
|
663 |
# Convert to a list of lines: |
664 |
# |
665 |
string(REGEX REPLACE ";" "\\\\;" candidates "${gp_cmd_ov}") |
666 |
string(REGEX REPLACE "\n" "${eol_char};" candidates "${candidates}") |
667 |
|
668 |
# check for install id and remove it from list, since otool -L can include a |
669 |
# reference to itself |
670 |
set(gp_install_id) |
671 |
if("${gp_tool}" STREQUAL "otool") |
672 |
execute_process( |
673 |
COMMAND otool -D ${target} |
674 |
OUTPUT_VARIABLE gp_install_id_ov |
675 |
) |
676 |
# second line is install name |
677 |
string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}") |
678 |
if(gp_install_id) |
679 |
# trim |
680 |
string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}") |
681 |
#message("INSTALL ID is \"${gp_install_id}\"") |
682 |
endif(gp_install_id) |
683 |
endif("${gp_tool}" STREQUAL "otool") |
684 |
|
685 |
# Analyze each line for file names that match the regular expression: |
686 |
# |
687 |
foreach(candidate ${candidates}) |
688 |
if("${candidate}" MATCHES "${gp_regex}") |
689 |
|
690 |
# Extract information from each candidate: |
691 |
if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}") |
692 |
string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}") |
693 |
else(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}") |
694 |
string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}") |
695 |
endif(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}") |
696 |
|
697 |
if(gp_regex_cmp_count GREATER 1) |
698 |
string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}") |
699 |
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}") |
700 |
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}") |
701 |
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}") |
702 |
endif(gp_regex_cmp_count GREATER 1) |
703 |
|
704 |
if(gp_regex_cmp_count GREATER 2) |
705 |
string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}") |
706 |
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}") |
707 |
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}") |
708 |
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}") |
709 |
endif(gp_regex_cmp_count GREATER 2) |
710 |
|
711 |
# Use the raw_item as the list entries returned by this function. Use the |
712 |
# gp_resolve_item function to resolve it to an actual full path file if |
713 |
# necessary. |
714 |
# |
715 |
set(item "${raw_item}") |
716 |
|
717 |
# Add each item unless it is excluded: |
718 |
# |
719 |
set(add_item 1) |
720 |
|
721 |
if("${item}" STREQUAL "${gp_install_id}") |
722 |
set(add_item 0) |
723 |
endif("${item}" STREQUAL "${gp_install_id}") |
724 |
|
725 |
if(add_item AND ${exclude_system}) |
726 |
set(type "") |
727 |
gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type) |
728 |
|
729 |
if("${type}" STREQUAL "system") |
730 |
set(add_item 0) |
731 |
endif("${type}" STREQUAL "system") |
732 |
endif(add_item AND ${exclude_system}) |
733 |
|
734 |
if(add_item) |
735 |
list(LENGTH ${prerequisites_var} list_length_before_append) |
736 |
gp_append_unique(${prerequisites_var} "${item}") |
737 |
list(LENGTH ${prerequisites_var} list_length_after_append) |
738 |
|
739 |
if(${recurse}) |
740 |
# If item was really added, this is the first time we have seen it. |
741 |
# Add it to unseen_prereqs so that we can recursively add *its* |
742 |
# prerequisites... |
743 |
# |
744 |
# But first: resolve its name to an absolute full path name such |
745 |
# that the analysis tools can simply accept it as input. |
746 |
# |
747 |
if(NOT list_length_before_append EQUAL list_length_after_append) |
748 |
gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item) |
749 |
set(unseen_prereqs ${unseen_prereqs} "${resolved_item}") |
750 |
endif(NOT list_length_before_append EQUAL list_length_after_append) |
751 |
endif(${recurse}) |
752 |
endif(add_item) |
753 |
else("${candidate}" MATCHES "${gp_regex}") |
754 |
if(verbose) |
755 |
message(STATUS "ignoring non-matching line: '${candidate}'") |
756 |
endif(verbose) |
757 |
endif("${candidate}" MATCHES "${gp_regex}") |
758 |
endforeach(candidate) |
759 |
|
760 |
list(LENGTH ${prerequisites_var} prerequisites_var_length) |
761 |
if(prerequisites_var_length GREATER 0) |
762 |
list(SORT ${prerequisites_var}) |
763 |
endif(prerequisites_var_length GREATER 0) |
764 |
if(${recurse}) |
765 |
set(more_inputs ${unseen_prereqs}) |
766 |
foreach(input ${more_inputs}) |
767 |
get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}") |
768 |
endforeach(input) |
769 |
endif(${recurse}) |
770 |
|
771 |
set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE) |
772 |
endfunction(get_prerequisites) |
773 |
|
774 |
|
775 |
function(list_prerequisites target) |
776 |
if("${ARGV1}" STREQUAL "") |
777 |
set(all 1) |
778 |
else("${ARGV1}" STREQUAL "") |
779 |
set(all "${ARGV1}") |
780 |
endif("${ARGV1}" STREQUAL "") |
781 |
|
782 |
if("${ARGV2}" STREQUAL "") |
783 |
set(exclude_system 0) |
784 |
else("${ARGV2}" STREQUAL "") |
785 |
set(exclude_system "${ARGV2}") |
786 |
endif("${ARGV2}" STREQUAL "") |
787 |
|
788 |
if("${ARGV3}" STREQUAL "") |
789 |
set(verbose 0) |
790 |
else("${ARGV3}" STREQUAL "") |
791 |
set(verbose "${ARGV3}") |
792 |
endif("${ARGV3}" STREQUAL "") |
793 |
|
794 |
set(count 0) |
795 |
set(count_str "") |
796 |
set(print_count "${verbose}") |
797 |
set(print_prerequisite_type "${verbose}") |
798 |
set(print_target "${verbose}") |
799 |
set(type_str "") |
800 |
|
801 |
get_filename_component(exepath "${target}" PATH) |
802 |
|
803 |
set(prereqs "") |
804 |
get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "") |
805 |
|
806 |
if(print_target) |
807 |
message(STATUS "File '${target}' depends on:") |
808 |
endif(print_target) |
809 |
|
810 |
foreach(d ${prereqs}) |
811 |
math(EXPR count "${count} + 1") |
812 |
|
813 |
if(print_count) |
814 |
set(count_str "${count}. ") |
815 |
endif(print_count) |
816 |
|
817 |
if(print_prerequisite_type) |
818 |
gp_file_type("${target}" "${d}" type) |
819 |
set(type_str " (${type})") |
820 |
endif(print_prerequisite_type) |
821 |
|
822 |
message(STATUS "${count_str}${d}${type_str}") |
823 |
endforeach(d) |
824 |
endfunction(list_prerequisites) |
825 |
|
826 |
|
827 |
function(list_prerequisites_by_glob glob_arg glob_exp) |
828 |
message(STATUS "=============================================================================") |
829 |
message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'") |
830 |
message(STATUS "") |
831 |
file(${glob_arg} file_list ${glob_exp}) |
832 |
foreach(f ${file_list}) |
833 |
is_file_executable("${f}" is_f_executable) |
834 |
if(is_f_executable) |
835 |
message(STATUS "=============================================================================") |
836 |
list_prerequisites("${f}" ${ARGN}) |
837 |
message(STATUS "") |
838 |
endif(is_f_executable) |
839 |
endforeach(f) |
840 |
endfunction(list_prerequisites_by_glob) |