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