Updates will be applied on October 27th between 12pm - 12:45pm EDT (UTC-0400). Gitlab may be slow during the maintenance window.

GetPrerequisites.cmake 32.2 KB
Newer Older
1
2
3
4
5
6
7
8
#.rst:
# GetPrerequisites
# ----------------
#
# Functions to analyze and list executable file prerequisites.
#
# This module provides functions to list the .dll, .dylib or .so files
# that an executable or shared library file depends on.  (Its
9
# prerequisites.)
10
#
11
12
13
14
15
16
17
18
19
20
# It uses various tools to obtain the list of required shared library
# files:
#
# ::
#
#    dumpbin (Windows)
#    objdump (MinGW on Windows)
#    ldd (Linux/Unix)
#    otool (Mac OSX)
#
21
22
# The following functions are provided by this module:
#
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# ::
#
#    get_prerequisites
#    list_prerequisites
#    list_prerequisites_by_glob
#    gp_append_unique
#    is_file_executable
#    gp_item_default_embedded_path
#      (projects can override with gp_item_default_embedded_path_override)
#    gp_resolve_item
#      (projects can override with gp_resolve_item_override)
#    gp_resolved_file_type
#      (projects can override with gp_resolved_file_type_override)
#    gp_file_type
#
# Requires CMake 2.6 or greater because it uses function, break, return
# and PARENT_SCOPE.
#
# ::
#
#   GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse>
44
#                     <exepath> <dirs> [<rpaths>])
45
46
47
48
49
50
51
52
53
54
#
# Get the list of shared library files required by <target>.  The list
# in the variable named <prerequisites_var> should be empty on first
# entry to this function.  On exit, <prerequisites_var> will contain the
# list of required shared library files.
#
# <target> is the full path to an executable file.  <prerequisites_var>
# is the name of a CMake variable to contain the results.
# <exclude_system> must be 0 or 1 indicating whether to include or
# exclude "system" prerequisites.  If <recurse> is set to 1 all
55
# prerequisites will be found recursively, if set to 0 only direct
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# prerequisites are listed.  <exepath> is the path to the top level
# executable used for @executable_path replacment on the Mac.  <dirs> is
# a list of paths where libraries might be found: these paths are
# searched first when a target without any path info is given.  Then
# standard system locations are also searched: PATH, Framework
# locations, /usr/lib...
#
# ::
#
#   LIST_PREREQUISITES(<target> [<recurse> [<exclude_system> [<verbose>]]])
#
# Print a message listing the prerequisites of <target>.
#
# <target> is the name of a shared library or executable target or the
# full path to a shared library or executable file.  If <recurse> is set
# to 1 all prerequisites will be found recursively, if set to 0 only
# direct prerequisites are listed.  <exclude_system> must be 0 or 1
# indicating whether to include or exclude "system" prerequisites.  With
# <verbose> set to 0 only the full path names of the prerequisites are
# printed, set to 1 extra informatin will be displayed.
#
# ::
#
#   LIST_PREREQUISITES_BY_GLOB(<glob_arg> <glob_exp>)
#
# Print the prerequisites of shared library and executable files
# matching a globbing pattern.  <glob_arg> is GLOB or GLOB_RECURSE and
# <glob_exp> is a globbing expression used with "file(GLOB" or
# "file(GLOB_RECURSE" to retrieve a list of matching files.  If a
# matching file is executable, its prerequisites are listed.
86
87
88
89
#
# Any additional (optional) arguments provided are passed along as the
# optional arguments to the list_prerequisites calls.
#
90
91
92
93
94
95
# ::
#
#   GP_APPEND_UNIQUE(<list_var> <value>)
#
# Append <value> to the list variable <list_var> only if the value is
# not already in the list.
96
#
97
98
99
100
101
102
103
104
105
106
# ::
#
#   IS_FILE_EXECUTABLE(<file> <result_var>)
#
# Return 1 in <result_var> if <file> is a binary executable, 0
# otherwise.
#
# ::
#
#   GP_ITEM_DEFAULT_EMBEDDED_PATH(<item> <default_embedded_path_var>)
107
108
109
110
111
112
113
#
# Return the path that others should refer to the item by when the item
# is embedded inside a bundle.
#
# Override on a per-project basis by providing a project-specific
# gp_item_default_embedded_path_override function.
#
114
115
# ::
#
116
117
#   GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>
#                   [<rpaths>])
118
#
119
120
121
122
123
# Resolve an item into an existing full path file.
#
# Override on a per-project basis by providing a project-specific
# gp_resolve_item_override function.
#
124
125
# ::
#
126
127
#   GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>
#                         [<rpaths>])
128
129
130
131
#
# Return the type of <file> with respect to <original_file>.  String
# describing type of prerequisite is returned in variable named
# <type_var>.
132
133
134
135
136
#
# Use <exepath> and <dirs> if necessary to resolve non-absolute <file>
# values -- but only for non-embedded items.
#
# Possible types are:
137
138
139
140
141
142
143
144
#
# ::
#
#    system
#    local
#    embedded
#    other
#
145
146
147
# Override on a per-project basis by providing a project-specific
# gp_resolved_file_type_override function.
#
148
149
150
151
152
153
154
# ::
#
#   GP_FILE_TYPE(<original_file> <file> <type_var>)
#
# Return the type of <file> with respect to <original_file>.  String
# describing type of prerequisite is returned in variable named
# <type_var>.
155
156
#
# Possible types are:
157
158
159
160
161
162
163
#
# ::
#
#    system
#    local
#    embedded
#    other
164

165
166
167
168
169
170
171
172
173
174
#=============================================================================
# Copyright 2008-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
175
# (To distribute this file outside of CMake, substitute the full
176
#  License text for the above reference.)
177
178
179
180
181

function(gp_append_unique list_var value)
  set(contains 0)

  foreach(item ${${list_var}})
182
    if(item STREQUAL "${value}")
183
184
      set(contains 1)
      break()
185
186
    endif()
  endforeach()
187
188
189

  if(NOT contains)
    set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE)
190
191
  endif()
endfunction()
192
193
194
195
196
197
198
199
200
201
202


function(is_file_executable file result_var)
  #
  # A file is not executable until proven otherwise:
  #
  set(${result_var} 0 PARENT_SCOPE)

  get_filename_component(file_full "${file}" ABSOLUTE)
  string(TOLOWER "${file_full}" file_full_lower)

203
  # If file name ends in .exe on Windows, *assume* executable:
204
  #
205
  if(WIN32 AND NOT UNIX)
206
    if("${file_full_lower}" MATCHES "\\.exe$")
207
208
      set(${result_var} 1 PARENT_SCOPE)
      return()
209
    endif()
210
211

    # A clause could be added here that uses output or return value of dumpbin
212
    # to determine ${result_var}. In 99%+? practical cases, the exe name
213
214
    # match will be sufficient...
    #
215
  endif()
216
217
218
219
220
221
222
223
224
225
226

  # Use the information returned from the Unix shell command "file" to
  # determine if ${file_full} should be considered an executable file...
  #
  # If the file command's output contains "executable" and does *not* contain
  # "text" then it is likely an executable suitable for prerequisite analysis
  # via the get_prerequisites macro.
  #
  if(UNIX)
    if(NOT file_cmd)
      find_program(file_cmd "file")
227
      mark_as_advanced(file_cmd)
228
    endif()
229
230
231

    if(file_cmd)
      execute_process(COMMAND "${file_cmd}" "${file_full}"
232
        RESULT_VARIABLE file_rv
233
        OUTPUT_VARIABLE file_ov
234
        ERROR_VARIABLE file_ev
235
236
        OUTPUT_STRIP_TRAILING_WHITESPACE
        )
237
238
239
      if(NOT file_rv STREQUAL "0")
        message(FATAL_ERROR "${file_cmd} failed: ${file_rv}\n${file_ev}")
      endif()
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

      # Replace the name of the file in the output with a placeholder token
      # (the string " _file_full_ ") so that just in case the path name of
      # the file contains the word "text" or "executable" we are not fooled
      # into thinking "the wrong thing" because the file name matches the
      # other 'file' command output we are looking for...
      #
      string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}")
      string(TOLOWER "${file_ov}" file_ov)

      #message(STATUS "file_ov='${file_ov}'")
      if("${file_ov}" MATCHES "executable")
        #message(STATUS "executable!")
        if("${file_ov}" MATCHES "text")
          #message(STATUS "but text, so *not* a binary executable!")
255
        else()
256
257
          set(${result_var} 1 PARENT_SCOPE)
          return()
258
259
        endif()
      endif()
260
261
262
263
264
265
266
267

      # Also detect position independent executables on Linux,
      # where "file" gives "shared object ... (uses shared libraries)"
      if("${file_ov}" MATCHES "shared object.*\(uses shared libs\)")
        set(${result_var} 1 PARENT_SCOPE)
        return()
      endif()

268
269
270
271
272
273
274
      # "file" version 5.22 does not print "(used shared libraries)"
      # but uses "interpreter"
      if("${file_ov}" MATCHES "shared object.*interpreter")
        set(${result_var} 1 PARENT_SCOPE)
        return()
      endif()

275
    else()
276
      message(STATUS "warning: No 'file' command, skipping execute_process...")
277
278
279
    endif()
  endif()
endfunction()
280
281


282
283
function(gp_item_default_embedded_path item default_embedded_path_var)

284
285
  # On Windows and Linux, "embed" prerequisites in the same directory
  # as the executable by default:
286
  #
287
  set(path "@executable_path")
288
289
  set(overridden 0)

290
291
  # On the Mac, relative to the executable depending on the type
  # of the thing we are embedding:
292
  #
293
294
295
296
297
298
299
300
301
  if(APPLE)
    #
    # The assumption here is that all executables in the bundle will be
    # in same-level-directories inside the bundle. The parent directory
    # of an executable inside the bundle should be MacOS or a sibling of
    # MacOS and all embedded paths returned from here will begin with
    # "@executable_path/../" and will work from all executables in all
    # such same-level-directories inside the bundle.
    #
302

303
304
305
306
307
308
309
310
    # By default, embed things right next to the main bundle executable:
    #
    set(path "@executable_path/../../Contents/MacOS")

    # Embed .dylibs right next to the main bundle executable:
    #
    if(item MATCHES "\\.dylib$")
      set(path "@executable_path/../MacOS")
311
      set(overridden 1)
312
    endif()
313

314
315
316
317
318
319
    # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS):
    #
    if(NOT overridden)
      if(item MATCHES "[^/]+\\.framework/")
        set(path "@executable_path/../Frameworks")
        set(overridden 1)
320
321
      endif()
    endif()
322
323
324
325
  endif()

  # Provide a hook so that projects can override the default embedded location
  # of any given library by whatever logic they choose:
326
327
328
  #
  if(COMMAND gp_item_default_embedded_path_override)
    gp_item_default_embedded_path_override("${item}" path)
329
  endif()
330
331

  set(${default_embedded_path_var} "${path}" PARENT_SCOPE)
332
endfunction()
333
334
335
336
337


function(gp_resolve_item context item exepath dirs resolved_item_var)
  set(resolved 0)
  set(resolved_item "${item}")
338
339
340
341
342
  if(ARGC GREATER 5)
    set(rpaths "${ARGV5}")
  else()
    set(rpaths "")
  endif()
343
344
345

  # Is it already resolved?
  #
346
  if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
347
    set(resolved 1)
348
  endif()
349
350

  if(NOT resolved)
351
    if(item MATCHES "^@executable_path")
352
353
354
355
356
357
358
359
360
361
      #
      # @executable_path references are assumed relative to exepath
      #
      string(REPLACE "@executable_path" "${exepath}" ri "${item}")
      get_filename_component(ri "${ri}" ABSOLUTE)

      if(EXISTS "${ri}")
        #message(STATUS "info: embedded item exists (${ri})")
        set(resolved 1)
        set(resolved_item "${ri}")
362
      else()
363
        message(STATUS "warning: embedded item does not exist '${ri}'")
364
365
366
      endif()
    endif()
  endif()
367
368

  if(NOT resolved)
369
    if(item MATCHES "^@loader_path")
370
371
372
373
374
375
376
377
378
379
380
381
      #
      # @loader_path references are assumed relative to the
      # PATH of the given "context" (presumably another library)
      #
      get_filename_component(contextpath "${context}" PATH)
      string(REPLACE "@loader_path" "${contextpath}" ri "${item}")
      get_filename_component(ri "${ri}" ABSOLUTE)

      if(EXISTS "${ri}")
        #message(STATUS "info: embedded item exists (${ri})")
        set(resolved 1)
        set(resolved_item "${ri}")
382
      else()
383
        message(STATUS "warning: embedded item does not exist '${ri}'")
384
385
386
      endif()
    endif()
  endif()
387

388
  if(NOT resolved)
389
    if(item MATCHES "^@rpath")
390
391
392
393
394
395
396
      #
      # @rpath references are relative to the paths built into the binaries with -rpath
      # We handle this case like we do for other Unixes
      #
      string(REPLACE "@rpath/" "" norpath_item "${item}")

      set(ri "ri-NOTFOUND")
397
      find_file(ri "${norpath_item}" ${exepath} ${dirs} ${rpaths} NO_DEFAULT_PATH)
398
      if(ri)
399
        #message(STATUS "info: 'find_file' in exepath/dirs/rpaths (${ri})")
400
401
402
        set(resolved 1)
        set(resolved_item "${ri}")
        set(ri "ri-NOTFOUND")
403
      endif()
404

405
406
    endif()
  endif()
407

408
409
  if(NOT resolved)
    set(ri "ri-NOTFOUND")
410
411
    find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
    find_file(ri "${item}" ${exepath} ${dirs} /usr/lib)
412
    if(ri)
413
      #message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
414
415
416
      set(resolved 1)
      set(resolved_item "${ri}")
      set(ri "ri-NOTFOUND")
417
418
    endif()
  endif()
419
420
421
422
423
424
425
426
427
428

  if(NOT resolved)
    if(item MATCHES "[^/]+\\.framework/")
      set(fw "fw-NOTFOUND")
      find_file(fw "${item}"
        "~/Library/Frameworks"
        "/Library/Frameworks"
        "/System/Library/Frameworks"
      )
      if(fw)
429
        #message(STATUS "info: 'find_file' found framework (${fw})")
430
431
432
        set(resolved 1)
        set(resolved_item "${fw}")
        set(fw "fw-NOTFOUND")
433
434
435
      endif()
    endif()
  endif()
436
437
438
439

  # Using find_program on Windows will find dll files that are in the PATH.
  # (Converting simple file names into full path names if found.)
  #
440
  if(WIN32 AND NOT UNIX)
441
442
  if(NOT resolved)
    set(ri "ri-NOTFOUND")
443
444
    find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH)
    find_program(ri "${item}" PATHS "${exepath};${dirs}")
445
    if(ri)
446
      #message(STATUS "info: 'find_program' in exepath/dirs (${ri})")
447
448
449
      set(resolved 1)
      set(resolved_item "${ri}")
      set(ri "ri-NOTFOUND")
450
451
452
    endif()
  endif()
  endif()
453
454
455
456
457
458

  # Provide a hook so that projects can override item resolution
  # by whatever logic they choose:
  #
  if(COMMAND gp_resolve_item_override)
    gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved)
459
  endif()
460
461

  if(NOT resolved)
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    message(STATUS "
warning: cannot resolve item '${item}'

  possible problems:
    need more directories?
    need to use InstallRequiredSystemLibraries?
    run in install tree instead of build tree?
")
#    message(STATUS "
#******************************************************************************
#warning: cannot resolve item '${item}'
#
#  possible problems:
#    need more directories?
#    need to use InstallRequiredSystemLibraries?
#    run in install tree instead of build tree?
#
#    context='${context}'
#    item='${item}'
#    exepath='${exepath}'
#    dirs='${dirs}'
#    resolved_item_var='${resolved_item_var}'
#******************************************************************************
#")
486
  endif()
487
488

  set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE)
489
endfunction()
490
491


492
function(gp_resolved_file_type original_file file exepath dirs type_var)
493
494
495
496
497
  if(ARGC GREATER 5)
    set(rpaths "${ARGV5}")
  else()
    set(rpaths "")
  endif()
498
499
500
501
502
  #message(STATUS "**")

  if(NOT IS_ABSOLUTE "${original_file}")
    message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file")
  endif()
503
  get_filename_component(original_file "${original_file}" ABSOLUTE) # canonicalize path
504
505
506
507
508
509
510
511
512
513
514
515
516

  set(is_embedded 0)
  set(is_local 0)
  set(is_system 0)

  set(resolved_file "${file}")

  if("${file}" MATCHES "^@(executable|loader)_path")
    set(is_embedded 1)
  endif()

  if(NOT is_embedded)
    if(NOT IS_ABSOLUTE "${file}")
517
      gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file "${rpaths}")
518
    endif()
519
    get_filename_component(resolved_file "${resolved_file}" ABSOLUTE) # canonicalize path
520
521
522
523
524

    string(TOLOWER "${original_file}" original_lower)
    string(TOLOWER "${resolved_file}" lower)

    if(UNIX)
525
      if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
526
527
528
529
530
531
532
533
534
535
536
537
        set(is_system 1)
      endif()
    endif()

    if(APPLE)
      if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)")
        set(is_system 1)
      endif()
    endif()

    if(WIN32)
      string(TOLOWER "$ENV{SystemRoot}" sysroot)
538
      file(TO_CMAKE_PATH "${sysroot}" sysroot)
539
540

      string(TOLOWER "$ENV{windir}" windir)
541
      file(TO_CMAKE_PATH "${windir}" windir)
542

543
      if(lower MATCHES "^(api-ms-win-|${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
544
545
        set(is_system 1)
      endif()
546
547
548
549
550
551
552

      if(UNIX)
        # if cygwin, we can get the properly formed windows paths from cygpath
        find_program(CYGPATH_EXECUTABLE cygpath)

        if(CYGPATH_EXECUTABLE)
          execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W
553
                          RESULT_VARIABLE env_rv
554
                          OUTPUT_VARIABLE env_windir
555
                          ERROR_VARIABLE env_ev
556
                          OUTPUT_STRIP_TRAILING_WHITESPACE)
557
558
559
          if(NOT env_rv STREQUAL "0")
            message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -W failed: ${env_rv}\n${env_ev}")
          endif()
560
          execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S
561
                          RESULT_VARIABLE env_rv
562
                          OUTPUT_VARIABLE env_sysdir
563
                          ERROR_VARIABLE env_ev
564
                          OUTPUT_STRIP_TRAILING_WHITESPACE)
565
566
567
          if(NOT env_rv STREQUAL "0")
            message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -S failed: ${env_rv}\n${env_ev}")
          endif()
568
569
570
          string(TOLOWER "${env_windir}" windir)
          string(TOLOWER "${env_sysdir}" sysroot)

571
          if(lower MATCHES "^(api-ms-win-|${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
572
573
            set(is_system 1)
          endif()
574
575
576
        endif()
      endif()
    endif()
577
578
579

    if(NOT is_system)
      get_filename_component(original_path "${original_lower}" PATH)
580
      get_filename_component(path "${lower}" PATH)
581
      if(original_path STREQUAL path)
582
583
584
585
586
587
        set(is_local 1)
      else()
        string(LENGTH "${original_path}/" original_length)
        string(LENGTH "${lower}" path_length)
        if(${path_length} GREATER ${original_length})
          string(SUBSTRING "${lower}" 0 ${original_length} path)
588
          if("${original_path}/" STREQUAL path)
589
590
            set(is_embedded 1)
          endif()
591
        endif()
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
      endif()
    endif()
  endif()

  # Return type string based on computed booleans:
  #
  set(type "other")

  if(is_system)
    set(type "system")
  elseif(is_embedded)
    set(type "embedded")
  elseif(is_local)
    set(type "local")
  endif()

  #message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'")
  #message(STATUS "                type: '${type}'")

  if(NOT is_embedded)
    if(NOT IS_ABSOLUTE "${resolved_file}")
      if(lower MATCHES "^msvc[^/]+dll" AND is_system)
        message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'")
      else()
        message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect")
      endif()
    endif()
  endif()

621
622
623
624
625
626
627
  # Provide a hook so that projects can override the decision on whether a
  # library belongs to the system or not by whatever logic they choose:
  #
  if(COMMAND gp_resolved_file_type_override)
    gp_resolved_file_type_override("${resolved_file}" type)
  endif()

628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
  set(${type_var} "${type}" PARENT_SCOPE)

  #message(STATUS "**")
endfunction()


function(gp_file_type original_file file type_var)
  if(NOT IS_ABSOLUTE "${original_file}")
    message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file")
  endif()

  get_filename_component(exepath "${original_file}" PATH)

  set(type "")
  gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type)

  set(${type_var} "${type}" PARENT_SCOPE)
645
endfunction()
646
647


648
function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs)
649
650
  set(verbose 0)
  set(eol_char "E")
651
652
653
654
655
  if(ARGC GREATER 6)
    set(rpaths "${ARGV6}")
  else()
    set(rpaths "")
  endif()
656

657
658
  if(NOT IS_ABSOLUTE "${target}")
    message("warning: target '${target}' is not absolute...")
659
  endif()
660
661
662

  if(NOT EXISTS "${target}")
    message("warning: target '${target}' does not exist...")
663
  endif()
664

665
  set(gp_cmd_paths ${gp_cmd_paths}
666
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/../../VC/bin"
667
    "$ENV{VS140COMNTOOLS}/../../VC/bin"
668
    "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin"
669
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/../../VC/bin"
670
    "$ENV{VS120COMNTOOLS}/../../VC/bin"
671
    "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin"
672
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/../../VC/bin"
673
    "$ENV{VS110COMNTOOLS}/../../VC/bin"
674
    "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin"
675
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/../../VC/bin"
676
    "$ENV{VS100COMNTOOLS}/../../VC/bin"
677
    "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin"
678
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/../../VC/bin"
679
    "$ENV{VS90COMNTOOLS}/../../VC/bin"
680
681
    "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"
    "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"
682
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/../../VC/bin"
683
    "$ENV{VS80COMNTOOLS}/../../VC/bin"
684
685
    "C:/Program Files/Microsoft Visual Studio 8/VC/BIN"
    "C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"
686
    "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/../../VC7/bin"
687
    "$ENV{VS71COMNTOOLS}/../../VC7/bin"
688
689
690
691
692
693
    "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"
    "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"
    "/usr/local/bin"
    "/usr/bin"
    )

694
695
696
697
698
  # <setup-gp_tool-vars>
  #
  # Try to choose the right tool by default. Caller can set gp_tool prior to
  # calling this function to force using a different tool.
  #
699
  if(NOT gp_tool)
700
    set(gp_tool "ldd")
701

702
703
    if(APPLE)
      set(gp_tool "otool")
704
    endif()
705

706
    if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har!
707
708
709
710
711
712
      find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths})
      if(gp_dumpbin)
        set(gp_tool "dumpbin")
      else() # Try harder. Maybe we're on MinGW
        set(gp_tool "objdump")
      endif()
713
714
    endif()
  endif()
715

716
717
718
719
720
721
722
  find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths})

  if(NOT gp_cmd)
    message(STATUS "warning: could not find '${gp_tool}' - cannot analyze prerequisites...")
    return()
  endif()

723
724
  set(gp_cmd_maybe_filter)      # optional command to pre-filter gp_tool results

725
  if(gp_tool STREQUAL "ldd")
726
    set(gp_cmd_args "")
727
    set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$")
728
729
    set(gp_regex_error "not found${eol_char}$")
    set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
730
    set(gp_regex_cmp_count 1)
731
  elseif(gp_tool STREQUAL "otool")
732
733
    set(gp_cmd_args "-L")
    set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$")
734
735
    set(gp_regex_error "")
    set(gp_regex_fallback "")
736
    set(gp_regex_cmp_count 3)
737
  elseif(gp_tool STREQUAL "dumpbin")
738
739
    set(gp_cmd_args "/dependents")
    set(gp_regex "^    ([^ ].*[Dd][Ll][Ll])${eol_char}$")
740
741
    set(gp_regex_error "")
    set(gp_regex_fallback "")
742
    set(gp_regex_cmp_count 1)
743
  elseif(gp_tool STREQUAL "objdump")
744
745
746
747
748
    set(gp_cmd_args "-p")
    set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$")
    set(gp_regex_error "")
    set(gp_regex_fallback "")
    set(gp_regex_cmp_count 1)
749
    # objdump generaates copious output so we create a grep filter to pre-filter results
750
751
752
753
754
    if(WIN32)
      find_program(gp_grep_cmd findstr)
    else()
      find_program(gp_grep_cmd grep)
    endif()
755
756
757
    if(gp_grep_cmd)
      set(gp_cmd_maybe_filter COMMAND ${gp_grep_cmd} "^[[:blank:]]*DLL Name: ")
    endif()
758
  else()
759
760
    message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...")
    message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'")
761
    message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.")
762
    return()
763
  endif()
764
765


766
  if(gp_tool STREQUAL "dumpbin")
767
768
769
770
771
772
773
    # When running dumpbin, it also needs the "Common7/IDE" directory in the
    # PATH. It will already be in the PATH if being run from a Visual Studio
    # command prompt. Add it to the PATH here in case we are running from a
    # different command prompt.
    #
    get_filename_component(gp_cmd_dir "${gp_cmd}" PATH)
    get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE)
774
775
    # Use cmake paths as a user may have a PATH element ending with a backslash.
    # This will escape the list delimiter and create havoc!
776
    if(EXISTS "${gp_cmd_dlls_dir}")
777
      # only add to the path if it is not already in the path
778
      set(gp_found_cmd_dlls_dir 0)
779
780
      file(TO_CMAKE_PATH "$ENV{PATH}" env_path)
      foreach(gp_env_path_element ${env_path})
781
        if(gp_env_path_element STREQUAL gp_cmd_dlls_dir)
782
783
          set(gp_found_cmd_dlls_dir 1)
        endif()
784
      endforeach()
785
786

      if(NOT gp_found_cmd_dlls_dir)
787
        file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir)
788
        set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}")
789
      endif()
790
791
    endif()
  endif()
792
793
794
  #
  # </setup-gp_tool-vars>

795
  if(gp_tool STREQUAL "ldd")
796
    set(old_ld_env "$ENV{LD_LIBRARY_PATH}")
797
798
799
    set(new_ld_env "${exepath}")
    foreach(dir ${dirs})
      set(new_ld_env "${new_ld_env}:${dir}")
800
    endforeach()
801
    set(ENV{LD_LIBRARY_PATH} "${new_ld_env}:$ENV{LD_LIBRARY_PATH}")
802
  endif()
803
804


805
806
807
808
809
810
811
812
813
  # Track new prerequisites at each new level of recursion. Start with an
  # empty list at each level:
  #
  set(unseen_prereqs)

  # Run gp_cmd on the target:
  #
  execute_process(
    COMMAND ${gp_cmd} ${gp_cmd_args} ${target}
814
    ${gp_cmd_maybe_filter}
815
    RESULT_VARIABLE gp_rv
816
    OUTPUT_VARIABLE gp_cmd_ov
817
    ERROR_VARIABLE gp_ev
818
    )
819
820
821
822
823
824
825
826
  if(NOT gp_rv STREQUAL "0")
    if(gp_tool STREQUAL "dumpbin")
      # dumpbin error messages seem to go to stdout
      message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}\n${gp_cmd_ov}")
    else()
      message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}")
    endif()
  endif()
827

828
  if(gp_tool STREQUAL "ldd")
829
    set(ENV{LD_LIBRARY_PATH} "${old_ld_env}")
830
  endif()
831

832
833
834
835
  if(verbose)
    message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>")
    message(STATUS "gp_cmd_ov='${gp_cmd_ov}'")
    message(STATUS "</RawOutput>")
836
  endif()
837
838
839
840
841

  get_filename_component(target_dir "${target}" PATH)

  # Convert to a list of lines:
  #
842
843
  string(REPLACE ";" "\\;" candidates "${gp_cmd_ov}")
  string(REPLACE "\n" "${eol_char};" candidates "${candidates}")
844

845
846
847
  # check for install id and remove it from list, since otool -L can include a
  # reference to itself
  set(gp_install_id)
848
  if(gp_tool STREQUAL "otool")
849
850
    execute_process(
      COMMAND otool -D ${target}
851
      RESULT_VARIABLE otool_rv
852
      OUTPUT_VARIABLE gp_install_id_ov
853
      ERROR_VARIABLE otool_ev
854
      )
855
856
857
    if(NOT otool_rv STREQUAL "0")
      message(FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}")
    endif()
858
859
860
861
862
863
    # second line is install name
    string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}")
    if(gp_install_id)
      # trim
      string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}")
      #message("INSTALL ID is \"${gp_install_id}\"")
864
865
    endif()
  endif()
866

867
868
869
870
  # Analyze each line for file names that match the regular expression:
  #
  foreach(candidate ${candidates})
  if("${candidate}" MATCHES "${gp_regex}")
871

872
    # Extract information from each candidate:
873
    if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}")
874
      string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}")
875
    else()
876
      string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}")
877
    endif()
878
879
880
881
882
883

    if(gp_regex_cmp_count GREATER 1)
      string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}")
      string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}")
      string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}")
      string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}")
884
    endif()
885
886
887
888
889
890

    if(gp_regex_cmp_count GREATER 2)
      string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}")
      string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}")
      string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}")
      string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}")
891
    endif()
892

893
894
895
    # Use the raw_item as the list entries returned by this function. Use the
    # gp_resolve_item function to resolve it to an actual full path file if
    # necessary.
896
    #
897
    set(item "${raw_item}")
898
899
900
901
902

    # Add each item unless it is excluded:
    #
    set(add_item 1)

903
    if(item STREQUAL gp_install_id)
904
      set(add_item 0)
905
    endif()
906
907

    if(add_item AND ${exclude_system})
908
      set(type "")
909
      gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type "${rpaths}")
910

911
      if(type STREQUAL "system")
912
        set(add_item 0)
913
914
      endif()
    endif()
915
916
917
918
919
920
921
922
923
924
925

    if(add_item)
      list(LENGTH ${prerequisites_var} list_length_before_append)
      gp_append_unique(${prerequisites_var} "${item}")
      list(LENGTH ${prerequisites_var} list_length_after_append)

      if(${recurse})
        # If item was really added, this is the first time we have seen it.
        # Add it to unseen_prereqs so that we can recursively add *its*
        # prerequisites...
        #
926
927
928
        # But first: resolve its name to an absolute full path name such
        # that the analysis tools can simply accept it as input.
        #
929
        if(NOT list_length_before_append EQUAL list_length_after_append)
930
          gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}")
931
          set(unseen_prereqs ${unseen_prereqs} "${resolved_item}")
932
933
934
935
        endif()
      endif()
    endif()
  else()
936
937
    if(verbose)
      message(STATUS "ignoring non-matching line: '${candidate}'")
938
939
940
    endif()
  endif()
  endforeach()
941

942
943
944
  list(LENGTH ${prerequisites_var} prerequisites_var_length)
  if(prerequisites_var_length GREATER 0)
    list(SORT ${prerequisites_var})
945
  endif()
946
947
948
  if(${recurse})
    set(more_inputs ${unseen_prereqs})
    foreach(input ${more_inputs})
949
      get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}" "${rpaths}")
950
951
    endforeach()
  endif()
952
953

  set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE)
954
endfunction()
955
956
957


function(list_prerequisites target)
958
  if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "")
959
    set(all "${ARGV1}")
960
961
  else()
    set(all 1)
962
  endif()
963

964
  if(ARGC GREATER 2 AND NOT "${ARGV2}" STREQUAL "")
965
    set(exclude_system "${ARGV2}")
966
967
  else()
    set(exclude_system 0)
968
  endif()
969

970
  if(ARGC GREATER 3 AND NOT "${ARGV3}" STREQUAL "")
971
    set(verbose "${ARGV3}")
972
973
  else()
    set(verbose 0)
974
  endif()
975
976
977
978
979
980
981
982

  set(count 0)
  set(count_str "")
  set(print_count "${verbose}")
  set(print_prerequisite_type "${verbose}")
  set(print_target "${verbose}")
  set(type_str "")

983
984
  get_filename_component(exepath "${target}" PATH)

985
  set(prereqs "")
986
  get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "")
987
988
989

  if(print_target)
    message(STATUS "File '${target}' depends on:")
990
  endif()
991
992
993
994
995
996

  foreach(d ${prereqs})
    math(EXPR count "${count} + 1")

    if(print_count)
      set(count_str "${count}. ")
997
    endif()
998
999
1000
1001

    if(print_prerequisite_type)
      gp_file_type("${target}" "${d}" type)
      set(type_str " (${type})")
1002
    endif()
1003
1004

    message(STATUS "${count_str}${d}${type_str}")
1005
1006
  endforeach()
endfunction()
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019


function(list_prerequisites_by_glob glob_arg glob_exp)
  message(STATUS "=============================================================================")
  message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'")
  message(STATUS "")
  file(${glob_arg} file_list ${glob_exp})
  foreach(f ${file_list})
    is_file_executable("${f}" is_f_executable)
    if(is_f_executable)
      message(STATUS "=============================================================================")
      list_prerequisites("${f}" ${ARGN})
      message(STATUS "")
1020
1021
1022
    endif()
  endforeach()
endfunction()