1# 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)
1049