blob: 53584c639a386844ddcf9e0363518e77b0ee6e96 [file] [log] [blame]
Ryan Prichard7aea7e92022-01-13 17:30:17 -08001# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5GetPrerequisites
6----------------
7
8.. deprecated:: 3.16
9
10 Use :command:`file(GET_RUNTIME_DEPENDENCIES)` instead.
11
12Functions to analyze and list executable file prerequisites.
13
14This module provides functions to list the .dll, .dylib or .so files
15that an executable or shared library file depends on. (Its
16prerequisites.)
17
18It uses various tools to obtain the list of required shared library
19files:
20
21::
22
23 dumpbin (Windows)
24 objdump (MinGW on Windows)
25 ldd (Linux/Unix)
26 otool (Mac OSX)
27
28.. versionchanged:: 3.16
29 The tool specified by ``CMAKE_OBJDUMP`` will be used, if set.
30
31The following functions are provided by this module:
32
33::
34
35 get_prerequisites
36 list_prerequisites
37 list_prerequisites_by_glob
38 gp_append_unique
39 is_file_executable
40 gp_item_default_embedded_path
41 (projects can override with gp_item_default_embedded_path_override)
42 gp_resolve_item
43 (projects can override with gp_resolve_item_override)
44 gp_resolved_file_type
45 (projects can override with gp_resolved_file_type_override)
46 gp_file_type
47
48::
49
50 GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse>
51 <exepath> <dirs> [<rpaths>])
52
53Get the list of shared library files required by <target>. The list
54in the variable named <prerequisites_var> should be empty on first
55entry to this function. On exit, <prerequisites_var> will contain the
56list of required shared library files.
57
58<target> is the full path to an executable file. <prerequisites_var>
59is the name of a CMake variable to contain the results.
60<exclude_system> must be 0 or 1 indicating whether to include or
61exclude "system" prerequisites. If <recurse> is set to 1 all
62prerequisites will be found recursively, if set to 0 only direct
63prerequisites are listed. <exepath> is the path to the top level
64executable used for @executable_path replacement on the Mac. <dirs> is
65a list of paths where libraries might be found: these paths are
66searched first when a target without any path info is given. Then
67standard system locations are also searched: PATH, Framework
68locations, /usr/lib...
69
70.. versionadded:: 3.14
71 The variable GET_PREREQUISITES_VERBOSE can be set to true to enable verbose
72 output.
73
74::
75
76 LIST_PREREQUISITES(<target> [<recurse> [<exclude_system> [<verbose>]]])
77
78Print a message listing the prerequisites of <target>.
79
80<target> is the name of a shared library or executable target or the
81full path to a shared library or executable file. If <recurse> is set
82to 1 all prerequisites will be found recursively, if set to 0 only
83direct prerequisites are listed. <exclude_system> must be 0 or 1
84indicating whether to include or exclude "system" prerequisites. With
85<verbose> set to 0 only the full path names of the prerequisites are
86printed, set to 1 extra information will be displayed.
87
88::
89
90 LIST_PREREQUISITES_BY_GLOB(<glob_arg> <glob_exp>)
91
92Print the prerequisites of shared library and executable files
93matching a globbing pattern. <glob_arg> is GLOB or GLOB_RECURSE and
94<glob_exp> is a globbing expression used with "file(GLOB" or
95"file(GLOB_RECURSE" to retrieve a list of matching files. If a
96matching file is executable, its prerequisites are listed.
97
98Any additional (optional) arguments provided are passed along as the
99optional arguments to the list_prerequisites calls.
100
101::
102
103 GP_APPEND_UNIQUE(<list_var> <value>)
104
105Append <value> to the list variable <list_var> only if the value is
106not already in the list.
107
108::
109
110 IS_FILE_EXECUTABLE(<file> <result_var>)
111
112Return 1 in <result_var> if <file> is a binary executable, 0
113otherwise.
114
115::
116
117 GP_ITEM_DEFAULT_EMBEDDED_PATH(<item> <default_embedded_path_var>)
118
119Return the path that others should refer to the item by when the item
120is embedded inside a bundle.
121
122Override on a per-project basis by providing a project-specific
123gp_item_default_embedded_path_override function.
124
125::
126
127 GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>
128 [<rpaths>])
129
130Resolve an item into an existing full path file.
131
132Override on a per-project basis by providing a project-specific
133gp_resolve_item_override function.
134
135::
136
137 GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>
138 [<rpaths>])
139
140Return the type of <file> with respect to <original_file>. String
141describing type of prerequisite is returned in variable named
142<type_var>.
143
144Use <exepath> and <dirs> if necessary to resolve non-absolute <file>
145values -- but only for non-embedded items.
146
147Possible types are:
148
149::
150
151 system
152 local
153 embedded
154 other
155
156Override on a per-project basis by providing a project-specific
157gp_resolved_file_type_override function.
158
159::
160
161 GP_FILE_TYPE(<original_file> <file> <type_var>)
162
163Return the type of <file> with respect to <original_file>. String
164describing type of prerequisite is returned in variable named
165<type_var>.
166
167Possible types are:
168
169::
170
171 system
172 local
173 embedded
174 other
175#]=======================================================================]
176
177cmake_policy(PUSH)
178cmake_policy(SET CMP0057 NEW) # if IN_LIST
179
180function(gp_append_unique list_var value)
181 if(NOT value IN_LIST ${list_var})
182 set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE)
183 endif()
184endfunction()
185
186
187function(is_file_executable file result_var)
188 #
189 # A file is not executable until proven otherwise:
190 #
191 set(${result_var} 0 PARENT_SCOPE)
192
193 get_filename_component(file_full "${file}" ABSOLUTE)
194 string(TOLOWER "${file_full}" file_full_lower)
195
196 # If file name ends in .exe on Windows, *assume* executable:
197 #
198 if(WIN32 AND NOT UNIX)
199 if("${file_full_lower}" MATCHES "\\.exe$")
200 set(${result_var} 1 PARENT_SCOPE)
201 return()
202 endif()
203
204 # A clause could be added here that uses output or return value of dumpbin
205 # to determine ${result_var}. In 99%+? practical cases, the exe name
206 # match will be sufficient...
207 #
208 endif()
209
210 # Use the information returned from the Unix shell command "file" to
211 # determine if ${file_full} should be considered an executable file...
212 #
213 # If the file command's output contains "executable" and does *not* contain
214 # "text" then it is likely an executable suitable for prerequisite analysis
215 # via the get_prerequisites macro.
216 #
217 if(UNIX)
218 if(NOT file_cmd)
219 find_program(file_cmd "file")
220 mark_as_advanced(file_cmd)
221 endif()
222
223 if(file_cmd)
224 execute_process(COMMAND "${file_cmd}" "${file_full}"
225 RESULT_VARIABLE file_rv
226 OUTPUT_VARIABLE file_ov
227 ERROR_VARIABLE file_ev
228 OUTPUT_STRIP_TRAILING_WHITESPACE
229 )
230 if(NOT file_rv STREQUAL "0")
231 message(FATAL_ERROR "${file_cmd} failed: ${file_rv}\n${file_ev}")
232 endif()
233
234 # Replace the name of the file in the output with a placeholder token
235 # (the string " _file_full_ ") so that just in case the path name of
236 # the file contains the word "text" or "executable" we are not fooled
237 # into thinking "the wrong thing" because the file name matches the
238 # other 'file' command output we are looking for...
239 #
240 string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}")
241 string(TOLOWER "${file_ov}" file_ov)
242
243 #message(STATUS "file_ov='${file_ov}'")
244 if("${file_ov}" MATCHES "executable")
245 #message(STATUS "executable!")
246 if("${file_ov}" MATCHES "text")
247 #message(STATUS "but text, so *not* a binary executable!")
248 else()
249 set(${result_var} 1 PARENT_SCOPE)
250 return()
251 endif()
252 endif()
253
254 # Also detect position independent executables on Linux,
255 # where "file" gives "shared object ... (uses shared libraries)"
256 if("${file_ov}" MATCHES "shared object.*\(uses shared libs\)")
257 set(${result_var} 1 PARENT_SCOPE)
258 return()
259 endif()
260
261 # "file" version 5.22 does not print "(used shared libraries)"
262 # but uses "interpreter"
263 if("${file_ov}" MATCHES "shared object.*interpreter")
264 set(${result_var} 1 PARENT_SCOPE)
265 return()
266 endif()
267
268 else()
269 message(STATUS "warning: No 'file' command, skipping execute_process...")
270 endif()
271 endif()
272endfunction()
273
274
275function(gp_item_default_embedded_path item default_embedded_path_var)
276
277 # On Windows and Linux, "embed" prerequisites in the same directory
278 # as the executable by default:
279 #
280 set(path "@executable_path")
281
282 # On the Mac, relative to the executable depending on the type
283 # of the thing we are embedding:
284 #
285 if(APPLE)
286 #
287 # The assumption here is that all executables in the bundle will be
288 # in same-level-directories inside the bundle. The parent directory
289 # of an executable inside the bundle should be MacOS or a sibling of
290 # MacOS and all embedded paths returned from here will begin with
291 # "@executable_path/../" and will work from all executables in all
292 # such same-level-directories inside the bundle.
293 #
294
295 # By default, embed things right next to the main bundle executable:
296 #
297 set(path "@executable_path/../../Contents/MacOS")
298
299 # Embed frameworks and .dylibs in the embedded "Frameworks" directory
300 # (sibling of MacOS):
301 #
302 if(item MATCHES "[^/]+\\.framework/" OR item MATCHES "\\.dylib$")
303 set(path "@executable_path/../Frameworks")
304 endif()
305 endif()
306
307 # Provide a hook so that projects can override the default embedded location
308 # of any given library by whatever logic they choose:
309 #
310 if(COMMAND gp_item_default_embedded_path_override)
311 gp_item_default_embedded_path_override("${item}" path)
312 endif()
313
314 set(${default_embedded_path_var} "${path}" PARENT_SCOPE)
315endfunction()
316
317
318function(gp_resolve_item context item exepath dirs resolved_item_var)
319 set(resolved 0)
320 set(resolved_item "${item}")
321 if(ARGC GREATER 5)
322 set(rpaths "${ARGV5}")
323 else()
324 set(rpaths "")
325 endif()
326
327 # Is it already resolved?
328 #
329 if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
330 set(resolved 1)
331 endif()
332
333 if(NOT resolved)
334 if(item MATCHES "^@executable_path")
335 #
336 # @executable_path references are assumed relative to exepath
337 #
338 string(REPLACE "@executable_path" "${exepath}" ri "${item}")
339 get_filename_component(ri "${ri}" ABSOLUTE)
340
341 if(EXISTS "${ri}")
342 #message(STATUS "info: embedded item exists (${ri})")
343 set(resolved 1)
344 set(resolved_item "${ri}")
345 else()
346 message(STATUS "warning: embedded item does not exist '${ri}'")
347 endif()
348 endif()
349 endif()
350
351 if(NOT resolved)
352 if(item MATCHES "^@loader_path")
353 #
354 # @loader_path references are assumed relative to the
355 # PATH of the given "context" (presumably another library)
356 #
357 get_filename_component(contextpath "${context}" PATH)
358 string(REPLACE "@loader_path" "${contextpath}" ri "${item}")
359 get_filename_component(ri "${ri}" ABSOLUTE)
360
361 if(EXISTS "${ri}")
362 #message(STATUS "info: embedded item exists (${ri})")
363 set(resolved 1)
364 set(resolved_item "${ri}")
365 else()
366 message(STATUS "warning: embedded item does not exist '${ri}'")
367 endif()
368 endif()
369 endif()
370
371 if(NOT resolved)
372 if(item MATCHES "^@rpath")
373 #
374 # @rpath references are relative to the paths built into the binaries with -rpath
375 # We handle this case like we do for other Unixes
376 #
377 string(REPLACE "@rpath/" "" norpath_item "${item}")
378
379 set(ri "ri-NOTFOUND")
380 find_file(ri "${norpath_item}" ${exepath} ${dirs} ${rpaths} NO_DEFAULT_PATH)
381 if(ri)
382 #message(STATUS "info: 'find_file' in exepath/dirs/rpaths (${ri})")
383 set(resolved 1)
384 set(resolved_item "${ri}")
385 set(ri "ri-NOTFOUND")
386 endif()
387
388 endif()
389 endif()
390
391 if(NOT resolved)
392 set(ri "ri-NOTFOUND")
393 find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
394 find_file(ri "${item}" ${exepath} ${dirs} /usr/lib)
395
396 get_filename_component(basename_item "${item}" NAME)
397 find_file(ri "${basename_item}" PATHS ${exepath} ${dirs} NO_DEFAULT_PATH)
398 find_file(ri "${basename_item}" PATHS /usr/lib)
399
400 if(ri)
401 #message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
402 set(resolved 1)
403 set(resolved_item "${ri}")
404 set(ri "ri-NOTFOUND")
405 endif()
406 endif()
407
408 if(NOT resolved)
409 if(item MATCHES "[^/]+\\.framework/")
410 set(fw "fw-NOTFOUND")
411 find_file(fw "${item}"
412 "~/Library/Frameworks"
413 "/Library/Frameworks"
414 "/System/Library/Frameworks"
415 )
416 if(fw)
417 #message(STATUS "info: 'find_file' found framework (${fw})")
418 set(resolved 1)
419 set(resolved_item "${fw}")
420 set(fw "fw-NOTFOUND")
421 endif()
422 endif()
423 endif()
424
425 # Using find_program on Windows will find dll files that are in the PATH.
426 # (Converting simple file names into full path names if found.)
427 #
428 if(WIN32 AND NOT UNIX)
429 if(NOT resolved)
430 set(ri "ri-NOTFOUND")
431 find_program(ri "${item}" PATHS ${exepath} ${dirs} NO_DEFAULT_PATH)
432 find_program(ri "${item}" PATHS ${exepath} ${dirs})
433 if(ri)
434 #message(STATUS "info: 'find_program' in exepath/dirs (${ri})")
435 set(resolved 1)
436 set(resolved_item "${ri}")
437 set(ri "ri-NOTFOUND")
438 endif()
439 endif()
440 endif()
441
442 # Provide a hook so that projects can override item resolution
443 # by whatever logic they choose:
444 #
445 if(COMMAND gp_resolve_item_override)
446 gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved)
447 endif()
448
449 if(NOT resolved)
450 message(STATUS "
451warning: cannot resolve item '${item}'
452
453 possible problems:
454 need more directories?
455 need to use InstallRequiredSystemLibraries?
456 run in install tree instead of build tree?
457")
458# message(STATUS "
459#******************************************************************************
460#warning: cannot resolve item '${item}'
461#
462# possible problems:
463# need more directories?
464# need to use InstallRequiredSystemLibraries?
465# run in install tree instead of build tree?
466#
467# context='${context}'
468# item='${item}'
469# exepath='${exepath}'
470# dirs='${dirs}'
471# resolved_item_var='${resolved_item_var}'
472#******************************************************************************
473#")
474 endif()
475
476 set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE)
477endfunction()
478
479
480function(gp_resolved_file_type original_file file exepath dirs type_var)
481 if(ARGC GREATER 5)
482 set(rpaths "${ARGV5}")
483 else()
484 set(rpaths "")
485 endif()
486 #message(STATUS "**")
487
488 if(NOT IS_ABSOLUTE "${original_file}")
489 message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file")
490 endif()
491 if(IS_ABSOLUTE "${original_file}")
492 get_filename_component(original_file "${original_file}" ABSOLUTE) # canonicalize path
493 endif()
494
495 set(is_embedded 0)
496 set(is_local 0)
497 set(is_system 0)
498
499 set(resolved_file "${file}")
500
501 if("${file}" MATCHES "^@(executable|loader)_path")
502 set(is_embedded 1)
503 endif()
504
505 if(NOT is_embedded)
506 if(NOT IS_ABSOLUTE "${file}")
507 gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file "${rpaths}")
508 endif()
509 if(IS_ABSOLUTE "${resolved_file}")
510 get_filename_component(resolved_file "${resolved_file}" ABSOLUTE) # canonicalize path
511 endif()
512
513 string(TOLOWER "${original_file}" original_lower)
514 string(TOLOWER "${resolved_file}" lower)
515
516 if(UNIX)
517 if(resolved_file MATCHES "^(/lib/|/lib32/|/libx32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/libx32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
518 set(is_system 1)
519 endif()
520 endif()
521
522 if(APPLE)
523 if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)")
524 set(is_system 1)
525 endif()
526 endif()
527
528 if(WIN32)
529 string(TOLOWER "$ENV{SystemRoot}" sysroot)
530 file(TO_CMAKE_PATH "${sysroot}" sysroot)
531
532 string(TOLOWER "$ENV{windir}" windir)
533 file(TO_CMAKE_PATH "${windir}" windir)
534
535 if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*(msvc|api-ms-win-|vcruntime)[^/]+dll)")
536 set(is_system 1)
537 endif()
538
539 if(UNIX)
540 # if cygwin, we can get the properly formed windows paths from cygpath
541 find_program(CYGPATH_EXECUTABLE cygpath)
542
543 if(CYGPATH_EXECUTABLE)
544 execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W
545 RESULT_VARIABLE env_rv
546 OUTPUT_VARIABLE env_windir
547 ERROR_VARIABLE env_ev
548 OUTPUT_STRIP_TRAILING_WHITESPACE)
549 if(NOT env_rv STREQUAL "0")
550 message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -W failed: ${env_rv}\n${env_ev}")
551 endif()
552 execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S
553 RESULT_VARIABLE env_rv
554 OUTPUT_VARIABLE env_sysdir
555 ERROR_VARIABLE env_ev
556 OUTPUT_STRIP_TRAILING_WHITESPACE)
557 if(NOT env_rv STREQUAL "0")
558 message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -S failed: ${env_rv}\n${env_ev}")
559 endif()
560 string(TOLOWER "${env_windir}" windir)
561 string(TOLOWER "${env_sysdir}" sysroot)
562
563 if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*(msvc|api-ms-win-|vcruntime)[^/]+dll)")
564 set(is_system 1)
565 endif()
566 endif()
567 endif()
568 endif()
569
570 if(NOT is_system)
571 get_filename_component(original_path "${original_lower}" PATH)
572 get_filename_component(path "${lower}" PATH)
573 if(original_path STREQUAL path)
574 set(is_local 1)
575 else()
576 string(LENGTH "${original_path}/" original_length)
577 string(LENGTH "${lower}" path_length)
578 if(${path_length} GREATER ${original_length})
579 string(SUBSTRING "${lower}" 0 ${original_length} path)
580 if("${original_path}/" STREQUAL path)
581 set(is_embedded 1)
582 endif()
583 endif()
584 endif()
585 endif()
586 endif()
587
588 # Return type string based on computed booleans:
589 #
590 set(type "other")
591
592 if(is_system)
593 set(type "system")
594 elseif(is_embedded)
595 set(type "embedded")
596 elseif(is_local)
597 set(type "local")
598 endif()
599
600 #message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'")
601 #message(STATUS " type: '${type}'")
602
603 if(NOT is_embedded)
604 if(NOT IS_ABSOLUTE "${resolved_file}")
605 if(lower MATCHES "^(msvc|api-ms-win-|vcruntime)[^/]+dll" AND is_system)
606 message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'")
607 else()
608 message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect")
609 endif()
610 endif()
611 endif()
612
613 # Provide a hook so that projects can override the decision on whether a
614 # library belongs to the system or not by whatever logic they choose:
615 #
616 if(COMMAND gp_resolved_file_type_override)
617 gp_resolved_file_type_override("${resolved_file}" type)
618 endif()
619
620 set(${type_var} "${type}" PARENT_SCOPE)
621
622 #message(STATUS "**")
623endfunction()
624
625
626function(gp_file_type original_file file type_var)
627 if(NOT IS_ABSOLUTE "${original_file}")
628 message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file")
629 endif()
630
631 get_filename_component(exepath "${original_file}" PATH)
632
633 set(type "")
634 gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type)
635
636 set(${type_var} "${type}" PARENT_SCOPE)
637endfunction()
638
639
640function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs)
641 set(verbose 0)
642 set(eol_char "E")
643 if(ARGC GREATER 6)
644 set(rpaths "${ARGV6}")
645 else()
646 set(rpaths "")
647 endif()
648
649 if(GET_PREREQUISITES_VERBOSE)
650 set(verbose 1)
651 endif()
652
653 if(NOT IS_ABSOLUTE "${target}")
654 message("warning: target '${target}' is not absolute...")
655 endif()
656
657 if(NOT EXISTS "${target}")
658 message("warning: target '${target}' does not exist...")
659 return()
660 endif()
661
662 # Check for a script by extension (.bat,.sh,...) or if the file starts with "#!" (shebang)
663 file(READ ${target} file_contents LIMIT 5)
664 if(target MATCHES "\\.(bat|c?sh|bash|ksh|cmd)$" OR file_contents MATCHES "^#!")
665 message(STATUS "GetPrequisites(${target}) : ignoring script file")
666 # Clear var
667 set(${prerequisites_var} "" PARENT_SCOPE)
668 return()
669 endif()
670
671 set(gp_cmd_paths ${gp_cmd_paths}
672 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/../../VC/bin"
673 "$ENV{VS140COMNTOOLS}/../../VC/bin"
674 "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin"
675 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/../../VC/bin"
676 "$ENV{VS120COMNTOOLS}/../../VC/bin"
677 "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin"
678 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/../../VC/bin"
679 "$ENV{VS110COMNTOOLS}/../../VC/bin"
680 "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin"
681 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/../../VC/bin"
682 "$ENV{VS100COMNTOOLS}/../../VC/bin"
683 "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin"
684 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/../../VC/bin"
685 "$ENV{VS90COMNTOOLS}/../../VC/bin"
686 "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"
687 "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"
688 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/../../VC/bin"
689 "$ENV{VS80COMNTOOLS}/../../VC/bin"
690 "C:/Program Files/Microsoft Visual Studio 8/VC/BIN"
691 "C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"
692 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/../../VC7/bin"
693 "$ENV{VS71COMNTOOLS}/../../VC7/bin"
694 "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"
695 "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"
696 )
697
698 # <setup-gp_tool-vars>
699 #
700 # Try to choose the right tool by default. Caller can set gp_tool prior to
701 # calling this function to force using a different tool.
702 #
703 if(NOT gp_tool)
704 set(gp_tool "ldd")
705
706 if(APPLE)
707 set(gp_tool "otool")
708 endif()
709
710 if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har!
711 find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths})
712 if(gp_dumpbin)
713 set(gp_tool "dumpbin")
714 elseif(CMAKE_OBJDUMP) # Try harder. Maybe we're on MinGW
715 set(gp_tool "${CMAKE_OBJDUMP}")
716 else()
717 set(gp_tool "objdump")
718 endif()
719 endif()
720 endif()
721
722 find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths})
723
724 if(NOT gp_cmd)
725 message(STATUS "warning: could not find '${gp_tool}' - cannot analyze prerequisites...")
726 return()
727 endif()
728
729 set(gp_cmd_maybe_filter) # optional command to pre-filter gp_tool results
730
731 if(gp_tool MATCHES "ldd$")
732 set(gp_cmd_args "")
733 set(gp_regex "^[\t ]*[^\t ]+ =>[\t ]+([^\t\(]+)( \(.+\))?${eol_char}$")
734 set(gp_regex_error "not found${eol_char}$")
735 set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
736 set(gp_regex_cmp_count 1)
737 elseif(gp_tool MATCHES "otool$")
738 set(gp_cmd_args "-L")
739 set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)(, weak)?\\)${eol_char}$")
740 set(gp_regex_error "")
741 set(gp_regex_fallback "")
742 set(gp_regex_cmp_count 3)
743 elseif(gp_tool MATCHES "dumpbin$")
744 set(gp_cmd_args "/dependents")
745 set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$")
746 set(gp_regex_error "")
747 set(gp_regex_fallback "")
748 set(gp_regex_cmp_count 1)
749 elseif(gp_tool MATCHES "objdump(\\.exe)?$")
750 set(gp_cmd_args "-p")
751 set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$")
752 set(gp_regex_error "")
753 set(gp_regex_fallback "")
754 set(gp_regex_cmp_count 1)
755 # objdump generates copious output so we create a grep filter to pre-filter results
756 if(WIN32)
757 find_program(gp_grep_cmd findstr)
758 else()
759 find_program(gp_grep_cmd grep)
760 endif()
761 if(gp_grep_cmd)
762 set(gp_cmd_maybe_filter COMMAND ${gp_grep_cmd} "-a" "^[[:blank:]]*DLL Name: ")
763 endif()
764 else()
765 message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...")
766 message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'")
767 message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.")
768 return()
769 endif()
770
771
772 if(gp_tool MATCHES "dumpbin$")
773 # When running dumpbin, it also needs the "Common7/IDE" directory in the
774 # PATH. It will already be in the PATH if being run from a Visual Studio
775 # command prompt. Add it to the PATH here in case we are running from a
776 # different command prompt.
777 #
778 get_filename_component(gp_cmd_dir "${gp_cmd}" PATH)
779 get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE)
780 # Use cmake paths as a user may have a PATH element ending with a backslash.
781 # This will escape the list delimiter and create havoc!
782 if(EXISTS "${gp_cmd_dlls_dir}")
783 # only add to the path if it is not already in the path
784 set(gp_found_cmd_dlls_dir 0)
785 file(TO_CMAKE_PATH "$ENV{PATH}" env_path)
786 foreach(gp_env_path_element ${env_path})
787 if(gp_env_path_element STREQUAL gp_cmd_dlls_dir)
788 set(gp_found_cmd_dlls_dir 1)
789 endif()
790 endforeach()
791
792 if(NOT gp_found_cmd_dlls_dir)
793 file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir)
794 set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}")
795 endif()
796 endif()
797 endif()
798 #
799 # </setup-gp_tool-vars>
800
801 if(gp_tool MATCHES "ldd$")
802 set(old_ld_env "$ENV{LD_LIBRARY_PATH}")
803 set(new_ld_env "${exepath}")
804 foreach(dir ${dirs})
805 string(APPEND new_ld_env ":${dir}")
806 endforeach()
807 set(ENV{LD_LIBRARY_PATH} "${new_ld_env}:$ENV{LD_LIBRARY_PATH}")
808 endif()
809
810
811 # Track new prerequisites at each new level of recursion. Start with an
812 # empty list at each level:
813 #
814 set(unseen_prereqs)
815
816 # Run gp_cmd on the target:
817 #
818 execute_process(
819 COMMAND ${gp_cmd} ${gp_cmd_args} ${target}
820 ${gp_cmd_maybe_filter}
821 RESULT_VARIABLE gp_rv
822 OUTPUT_VARIABLE gp_cmd_ov
823 ERROR_VARIABLE gp_ev
824 )
825
826 if(gp_tool MATCHES "dumpbin$")
827 # Exclude delay load dependencies under windows (they are listed in dumpbin output after the message below)
828 string(FIND "${gp_cmd_ov}" "Image has the following delay load dependencies" gp_delayload_pos)
829 if (${gp_delayload_pos} GREATER -1)
830 string(SUBSTRING "${gp_cmd_ov}" 0 ${gp_delayload_pos} gp_cmd_ov_no_delayload_deps)
831 string(SUBSTRING "${gp_cmd_ov}" ${gp_delayload_pos} -1 gp_cmd_ov_delayload_deps)
832 if (verbose)
833 message(STATUS "GetPrequisites(${target}) : ignoring the following delay load dependencies :\n ${gp_cmd_ov_delayload_deps}")
834 endif()
835 set(gp_cmd_ov ${gp_cmd_ov_no_delayload_deps})
836 endif()
837 endif()
838
839 if(NOT gp_rv STREQUAL "0")
840 if(gp_tool MATCHES "dumpbin$")
841 # dumpbin error messages seem to go to stdout
842 message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}\n${gp_cmd_ov}")
843 else()
844 message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}")
845 endif()
846 endif()
847
848 if(gp_tool MATCHES "ldd$")
849 set(ENV{LD_LIBRARY_PATH} "${old_ld_env}")
850 endif()
851
852 if(verbose)
853 message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>")
854 message(STATUS "gp_cmd_ov='${gp_cmd_ov}'")
855 message(STATUS "</RawOutput>")
856 endif()
857
858 get_filename_component(target_dir "${target}" PATH)
859
860 # Convert to a list of lines:
861 #
862 string(REPLACE ";" "\\;" candidates "${gp_cmd_ov}")
863 string(REPLACE "\n" "${eol_char};" candidates "${candidates}")
864
865 # check for install id and remove it from list, since otool -L can include a
866 # reference to itself
867 set(gp_install_id)
868 if(gp_tool MATCHES "otool$")
869 execute_process(
870 COMMAND ${gp_cmd} -D ${target}
871 RESULT_VARIABLE otool_rv
872 OUTPUT_VARIABLE gp_install_id_ov
873 ERROR_VARIABLE otool_ev
874 )
875 if(NOT otool_rv STREQUAL "0")
876 message(FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}")
877 endif()
878 # second line is install name
879 string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}")
880 if(gp_install_id)
881 # trim
882 string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}")
883 #message("INSTALL ID is \"${gp_install_id}\"")
884 endif()
885 endif()
886
887 # Analyze each line for file names that match the regular expression:
888 #
889 foreach(candidate ${candidates})
890 if("${candidate}" MATCHES "${gp_regex}")
891
892 # Extract information from each candidate:
893 if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}")
894 string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}")
895 else()
896 string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}")
897 endif()
898
899 if(gp_regex_cmp_count GREATER 1)
900 string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}")
901 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}")
902 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}")
903 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}")
904 endif()
905
906 if(gp_regex_cmp_count GREATER 2)
907 string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}")
908 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}")
909 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}")
910 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}")
911 endif()
912
913 # Use the raw_item as the list entries returned by this function. Use the
914 # gp_resolve_item function to resolve it to an actual full path file if
915 # necessary.
916 #
917 set(item "${raw_item}")
918
919 # Add each item unless it is excluded:
920 #
921 set(add_item 1)
922
923 if(item STREQUAL gp_install_id)
924 set(add_item 0)
925 endif()
926
927 if(add_item AND ${exclude_system})
928 set(type "")
929 gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type "${rpaths}")
930
931 if(type STREQUAL "system")
932 set(add_item 0)
933 endif()
934 endif()
935
936 if(add_item)
937 list(LENGTH ${prerequisites_var} list_length_before_append)
938 gp_append_unique(${prerequisites_var} "${item}")
939 list(LENGTH ${prerequisites_var} list_length_after_append)
940
941 if(${recurse})
942 # If item was really added, this is the first time we have seen it.
943 # Add it to unseen_prereqs so that we can recursively add *its*
944 # prerequisites...
945 #
946 # But first: resolve its name to an absolute full path name such
947 # that the analysis tools can simply accept it as input.
948 #
949 if(NOT list_length_before_append EQUAL list_length_after_append)
950 gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}")
951 if(EXISTS "${resolved_item}")
952 # Recurse only if we could resolve the item.
953 # Otherwise the prerequisites_var list will be cleared
954 set(unseen_prereqs ${unseen_prereqs} "${resolved_item}")
955 endif()
956 endif()
957 endif()
958 endif()
959 else()
960 if(verbose)
961 message(STATUS "ignoring non-matching line: '${candidate}'")
962 endif()
963 endif()
964 endforeach()
965
966 list(LENGTH ${prerequisites_var} prerequisites_var_length)
967 if(prerequisites_var_length GREATER 0)
968 list(SORT ${prerequisites_var})
969 endif()
970 if(${recurse})
971 set(more_inputs ${unseen_prereqs})
972 foreach(input ${more_inputs})
973 get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}" "${rpaths}")
974 endforeach()
975 endif()
976
977 set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE)
978endfunction()
979
980
981function(list_prerequisites target)
982 if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "")
983 set(all "${ARGV1}")
984 else()
985 set(all 1)
986 endif()
987
988 if(ARGC GREATER 2 AND NOT "${ARGV2}" STREQUAL "")
989 set(exclude_system "${ARGV2}")
990 else()
991 set(exclude_system 0)
992 endif()
993
994 if(ARGC GREATER 3 AND NOT "${ARGV3}" STREQUAL "")
995 set(verbose "${ARGV3}")
996 else()
997 set(verbose 0)
998 endif()
999
1000 set(count 0)
1001 set(count_str "")
1002 set(print_count "${verbose}")
1003 set(print_prerequisite_type "${verbose}")
1004 set(print_target "${verbose}")
1005 set(type_str "")
1006
1007 get_filename_component(exepath "${target}" PATH)
1008
1009 set(prereqs "")
1010 get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "")
1011
1012 if(print_target)
1013 message(STATUS "File '${target}' depends on:")
1014 endif()
1015
1016 foreach(d ${prereqs})
1017 math(EXPR count "${count} + 1")
1018
1019 if(print_count)
1020 set(count_str "${count}. ")
1021 endif()
1022
1023 if(print_prerequisite_type)
1024 gp_file_type("${target}" "${d}" type)
1025 set(type_str " (${type})")
1026 endif()
1027
1028 message(STATUS "${count_str}${d}${type_str}")
1029 endforeach()
1030endfunction()
1031
1032
1033function(list_prerequisites_by_glob glob_arg glob_exp)
1034 message(STATUS "=============================================================================")
1035 message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'")
1036 message(STATUS "")
1037 file(${glob_arg} file_list ${glob_exp})
1038 foreach(f ${file_list})
1039 is_file_executable("${f}" is_f_executable)
1040 if(is_f_executable)
1041 message(STATUS "=============================================================================")
1042 list_prerequisites("${f}" ${ARGN})
1043 message(STATUS "")
1044 endif()
1045 endforeach()
1046endfunction()
1047
1048cmake_policy(POP)