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 replacment 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 # "file" version 5.22 does not print "(used shared libraries)" 209 # but uses "interpreter" 210 if("${file_ov}" MATCHES "shared object.*interpreter") 211 set(${result_var} 1 PARENT_SCOPE) 212 return() 213 endif() 214 215 else() 216 message(STATUS "warning: No 'file' command, skipping execute_process...") 217 endif() 218 endif() 219endfunction() 220 221 222function(gp_item_default_embedded_path item default_embedded_path_var) 223 224 # On Windows and Linux, "embed" prerequisites in the same directory 225 # as the executable by default: 226 # 227 set(path "@executable_path") 228 set(overridden 0) 229 230 # On the Mac, relative to the executable depending on the type 231 # of the thing we are embedding: 232 # 233 if(APPLE) 234 # 235 # The assumption here is that all executables in the bundle will be 236 # in same-level-directories inside the bundle. The parent directory 237 # of an executable inside the bundle should be MacOS or a sibling of 238 # MacOS and all embedded paths returned from here will begin with 239 # "@executable_path/../" and will work from all executables in all 240 # such same-level-directories inside the bundle. 241 # 242 243 # By default, embed things right next to the main bundle executable: 244 # 245 set(path "@executable_path/../../Contents/MacOS") 246 247 # Embed .dylibs right next to the main bundle executable: 248 # 249 if(item MATCHES "\\.dylib$") 250 set(path "@executable_path/../MacOS") 251 set(overridden 1) 252 endif() 253 254 # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS): 255 # 256 if(NOT overridden) 257 if(item MATCHES "[^/]+\\.framework/") 258 set(path "@executable_path/../Frameworks") 259 set(overridden 1) 260 endif() 261 endif() 262 endif() 263 264 # Provide a hook so that projects can override the default embedded location 265 # of any given library by whatever logic they choose: 266 # 267 if(COMMAND gp_item_default_embedded_path_override) 268 gp_item_default_embedded_path_override("${item}" path) 269 endif() 270 271 set(${default_embedded_path_var} "${path}" PARENT_SCOPE) 272endfunction() 273 274 275function(gp_resolve_item context item exepath dirs resolved_item_var) 276 set(resolved 0) 277 set(resolved_item "${item}") 278 279 # Is it already resolved? 280 # 281 if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}") 282 set(resolved 1) 283 endif() 284 285 if(NOT resolved) 286 if(item MATCHES "@executable_path") 287 # 288 # @executable_path references are assumed relative to exepath 289 # 290 string(REPLACE "@executable_path" "${exepath}" ri "${item}") 291 get_filename_component(ri "${ri}" ABSOLUTE) 292 293 if(EXISTS "${ri}") 294 #message(STATUS "info: embedded item exists (${ri})") 295 set(resolved 1) 296 set(resolved_item "${ri}") 297 else() 298 message(STATUS "warning: embedded item does not exist '${ri}'") 299 endif() 300 endif() 301 endif() 302 303 if(NOT resolved) 304 if(item MATCHES "@loader_path") 305 # 306 # @loader_path references are assumed relative to the 307 # PATH of the given "context" (presumably another library) 308 # 309 get_filename_component(contextpath "${context}" PATH) 310 string(REPLACE "@loader_path" "${contextpath}" ri "${item}") 311 get_filename_component(ri "${ri}" ABSOLUTE) 312 313 if(EXISTS "${ri}") 314 #message(STATUS "info: embedded item exists (${ri})") 315 set(resolved 1) 316 set(resolved_item "${ri}") 317 else() 318 message(STATUS "warning: embedded item does not exist '${ri}'") 319 endif() 320 endif() 321 endif() 322 323 if(NOT resolved) 324 if(item MATCHES "@rpath") 325 # 326 # @rpath references are relative to the paths built into the binaries with -rpath 327 # We handle this case like we do for other Unixes 328 # 329 string(REPLACE "@rpath/" "" norpath_item "${item}") 330 331 set(ri "ri-NOTFOUND") 332 find_file(ri "${norpath_item}" ${exepath} ${dirs} NO_DEFAULT_PATH) 333 if(ri) 334 #message(STATUS "info: 'find_file' in exepath/dirs (${ri})") 335 set(resolved 1) 336 set(resolved_item "${ri}") 337 set(ri "ri-NOTFOUND") 338 endif() 339 340 endif() 341 endif() 342 343 if(NOT resolved) 344 set(ri "ri-NOTFOUND") 345 find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH) 346 find_file(ri "${item}" ${exepath} ${dirs} /usr/lib) 347 if(ri) 348 #message(STATUS "info: 'find_file' in exepath/dirs (${ri})") 349 set(resolved 1) 350 set(resolved_item "${ri}") 351 set(ri "ri-NOTFOUND") 352 endif() 353 endif() 354 355 if(NOT resolved) 356 if(item MATCHES "[^/]+\\.framework/") 357 set(fw "fw-NOTFOUND") 358 find_file(fw "${item}" 359 "~/Library/Frameworks" 360 "/Library/Frameworks" 361 "/System/Library/Frameworks" 362 ) 363 if(fw) 364 #message(STATUS "info: 'find_file' found framework (${fw})") 365 set(resolved 1) 366 set(resolved_item "${fw}") 367 set(fw "fw-NOTFOUND") 368 endif() 369 endif() 370 endif() 371 372 # Using find_program on Windows will find dll files that are in the PATH. 373 # (Converting simple file names into full path names if found.) 374 # 375 if(WIN32 AND NOT UNIX) 376 if(NOT resolved) 377 set(ri "ri-NOTFOUND") 378 find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH) 379 find_program(ri "${item}" PATHS "${exepath};${dirs}") 380 if(ri) 381 #message(STATUS "info: 'find_program' in exepath/dirs (${ri})") 382 set(resolved 1) 383 set(resolved_item "${ri}") 384 set(ri "ri-NOTFOUND") 385 endif() 386 endif() 387 endif() 388 389 # Provide a hook so that projects can override item resolution 390 # by whatever logic they choose: 391 # 392 if(COMMAND gp_resolve_item_override) 393 gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved) 394 endif() 395 396 if(NOT resolved) 397 message(STATUS " 398warning: cannot resolve item '${item}' 399 400 possible problems: 401 need more directories? 402 need to use InstallRequiredSystemLibraries? 403 run in install tree instead of build tree? 404") 405# message(STATUS " 406#****************************************************************************** 407#warning: cannot resolve item '${item}' 408# 409# possible problems: 410# need more directories? 411# need to use InstallRequiredSystemLibraries? 412# run in install tree instead of build tree? 413# 414# context='${context}' 415# item='${item}' 416# exepath='${exepath}' 417# dirs='${dirs}' 418# resolved_item_var='${resolved_item_var}' 419#****************************************************************************** 420#") 421 endif() 422 423 set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE) 424endfunction() 425 426 427function(gp_resolved_file_type original_file file exepath dirs type_var) 428 #message(STATUS "**") 429 430 if(NOT IS_ABSOLUTE "${original_file}") 431 message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file") 432 endif() 433 434 set(is_embedded 0) 435 set(is_local 0) 436 set(is_system 0) 437 438 set(resolved_file "${file}") 439 440 if("${file}" MATCHES "^@(executable|loader)_path") 441 set(is_embedded 1) 442 endif() 443 444 if(NOT is_embedded) 445 if(NOT IS_ABSOLUTE "${file}") 446 gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file) 447 endif() 448 449 string(TOLOWER "${original_file}" original_lower) 450 string(TOLOWER "${resolved_file}" lower) 451 452 if(UNIX) 453 if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)") 454 set(is_system 1) 455 endif() 456 endif() 457 458 if(APPLE) 459 if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)") 460 set(is_system 1) 461 endif() 462 endif() 463 464 if(WIN32) 465 string(TOLOWER "$ENV{SystemRoot}" sysroot) 466 string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}") 467 468 string(TOLOWER "$ENV{windir}" windir) 469 string(REGEX REPLACE "\\\\" "/" windir "${windir}") 470 471 if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)") 472 set(is_system 1) 473 endif() 474 475 if(UNIX) 476 # if cygwin, we can get the properly formed windows paths from cygpath 477 find_program(CYGPATH_EXECUTABLE cygpath) 478 479 if(CYGPATH_EXECUTABLE) 480 execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W 481 OUTPUT_VARIABLE env_windir 482 OUTPUT_STRIP_TRAILING_WHITESPACE) 483 execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S 484 OUTPUT_VARIABLE env_sysdir 485 OUTPUT_STRIP_TRAILING_WHITESPACE) 486 string(TOLOWER "${env_windir}" windir) 487 string(TOLOWER "${env_sysdir}" sysroot) 488 489 if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)") 490 set(is_system 1) 491 endif() 492 endif() 493 endif() 494 endif() 495 496 if(NOT is_system) 497 get_filename_component(original_path "${original_lower}" PATH) 498 get_filename_component(path "${lower}" PATH) 499 if("${original_path}" STREQUAL "${path}") 500 set(is_local 1) 501 else() 502 string(LENGTH "${original_path}/" original_length) 503 string(LENGTH "${lower}" path_length) 504 if(${path_length} GREATER ${original_length}) 505 string(SUBSTRING "${lower}" 0 ${original_length} path) 506 if("${original_path}/" STREQUAL "${path}") 507 set(is_embedded 1) 508 endif() 509 endif() 510 endif() 511 endif() 512 endif() 513 514 # Return type string based on computed booleans: 515 # 516 set(type "other") 517 518 if(is_system) 519 set(type "system") 520 elseif(is_embedded) 521 set(type "embedded") 522 elseif(is_local) 523 set(type "local") 524 endif() 525 526 #message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'") 527 #message(STATUS " type: '${type}'") 528 529 if(NOT is_embedded) 530 if(NOT IS_ABSOLUTE "${resolved_file}") 531 if(lower MATCHES "^msvc[^/]+dll" AND is_system) 532 message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'") 533 else() 534 message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect") 535 endif() 536 endif() 537 endif() 538 539 # Provide a hook so that projects can override the decision on whether a 540 # library belongs to the system or not by whatever logic they choose: 541 # 542 if(COMMAND gp_resolved_file_type_override) 543 gp_resolved_file_type_override("${resolved_file}" type) 544 endif() 545 546 set(${type_var} "${type}" PARENT_SCOPE) 547 548 #message(STATUS "**") 549endfunction() 550 551 552function(gp_file_type original_file file type_var) 553 if(NOT IS_ABSOLUTE "${original_file}") 554 message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file") 555 endif() 556 557 get_filename_component(exepath "${original_file}" PATH) 558 559 set(type "") 560 gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type) 561 562 set(${type_var} "${type}" PARENT_SCOPE) 563endfunction() 564 565 566function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs) 567 set(verbose 0) 568 set(eol_char "E") 569 570 if(NOT IS_ABSOLUTE "${target}") 571 message("warning: target '${target}' is not absolute...") 572 endif() 573 574 if(NOT EXISTS "${target}") 575 message("warning: target '${target}' does not exist...") 576 endif() 577 578 set(gp_cmd_paths ${gp_cmd_paths} 579 "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin" 580 "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin" 581 "C:/Program Files/Microsoft Visual Studio 8/VC/BIN" 582 "C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN" 583 "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN" 584 "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN" 585 "/usr/local/bin" 586 "/usr/bin" 587 ) 588 589 # <setup-gp_tool-vars> 590 # 591 # Try to choose the right tool by default. Caller can set gp_tool prior to 592 # calling this function to force using a different tool. 593 # 594 if("${gp_tool}" STREQUAL "") 595 set(gp_tool "ldd") 596 597 if(APPLE) 598 set(gp_tool "otool") 599 endif() 600 601 if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har! 602 find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths}) 603 if(gp_dumpbin) 604 set(gp_tool "dumpbin") 605 else() # Try harder. Maybe we're on MinGW 606 set(gp_tool "objdump") 607 endif() 608 endif() 609 endif() 610 611 find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths}) 612 613 if(NOT gp_cmd) 614 message(FATAL_ERROR "FATAL ERROR: could not find '${gp_tool}' - cannot analyze prerequisites!") 615 return() 616 endif() 617 618 set(gp_tool_known 0) 619 620 if("${gp_tool}" STREQUAL "ldd") 621 set(gp_cmd_args "") 622 set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$") 623 set(gp_regex_error "not found${eol_char}$") 624 set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$") 625 set(gp_regex_cmp_count 1) 626 set(gp_tool_known 1) 627 endif() 628 629 if("${gp_tool}" STREQUAL "otool") 630 set(gp_cmd_args "-L") 631 set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$") 632 set(gp_regex_error "") 633 set(gp_regex_fallback "") 634 set(gp_regex_cmp_count 3) 635 set(gp_tool_known 1) 636 endif() 637 638 if("${gp_tool}" STREQUAL "dumpbin") 639 set(gp_cmd_args "/dependents") 640 set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$") 641 set(gp_regex_error "") 642 set(gp_regex_fallback "") 643 set(gp_regex_cmp_count 1) 644 set(gp_tool_known 1) 645 endif() 646 647 if("${gp_tool}" STREQUAL "objdump") 648 set(gp_cmd_args "-p") 649 set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$") 650 set(gp_regex_error "") 651 set(gp_regex_fallback "") 652 set(gp_regex_cmp_count 1) 653 set(gp_tool_known 1) 654 endif() 655 656 if(NOT gp_tool_known) 657 message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...") 658 message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'") 659 message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.") 660 return() 661 endif() 662 663 664 if("${gp_tool}" STREQUAL "dumpbin") 665 # When running dumpbin, it also needs the "Common7/IDE" directory in the 666 # PATH. It will already be in the PATH if being run from a Visual Studio 667 # command prompt. Add it to the PATH here in case we are running from a 668 # different command prompt. 669 # 670 get_filename_component(gp_cmd_dir "${gp_cmd}" PATH) 671 get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE) 672 # Use cmake paths as a user may have a PATH element ending with a backslash. 673 # This will escape the list delimiter and create havoc! 674 if(EXISTS "${gp_cmd_dlls_dir}") 675 # only add to the path if it is not already in the path 676 set(gp_found_cmd_dlls_dir 0) 677 file(TO_CMAKE_PATH "$ENV{PATH}" env_path) 678 foreach(gp_env_path_element ${env_path}) 679 if("${gp_env_path_element}" STREQUAL "${gp_cmd_dlls_dir}") 680 set(gp_found_cmd_dlls_dir 1) 681 endif() 682 endforeach() 683 684 if(NOT gp_found_cmd_dlls_dir) 685 file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir) 686 set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}") 687 endif() 688 endif() 689 endif() 690 # 691 # </setup-gp_tool-vars> 692 693 if("${gp_tool}" STREQUAL "ldd") 694 set(old_ld_env "$ENV{LD_LIBRARY_PATH}") 695 foreach(dir ${exepath} ${dirs}) 696 set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}") 697 endforeach() 698 endif() 699 700 701 # Track new prerequisites at each new level of recursion. Start with an 702 # empty list at each level: 703 # 704 set(unseen_prereqs) 705 706 # Run gp_cmd on the target: 707 # 708 execute_process( 709 COMMAND ${gp_cmd} ${gp_cmd_args} ${target} 710 OUTPUT_VARIABLE gp_cmd_ov 711 ) 712 713 if("${gp_tool}" STREQUAL "ldd") 714 set(ENV{LD_LIBRARY_PATH} "${old_ld_env}") 715 endif() 716 717 if(verbose) 718 message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>") 719 message(STATUS "gp_cmd_ov='${gp_cmd_ov}'") 720 message(STATUS "</RawOutput>") 721 endif() 722 723 get_filename_component(target_dir "${target}" PATH) 724 725 # Convert to a list of lines: 726 # 727 string(REGEX REPLACE ";" "\\\\;" candidates "${gp_cmd_ov}") 728 string(REGEX REPLACE "\n" "${eol_char};" candidates "${candidates}") 729 730 # check for install id and remove it from list, since otool -L can include a 731 # reference to itself 732 set(gp_install_id) 733 if("${gp_tool}" STREQUAL "otool") 734 execute_process( 735 COMMAND otool -D ${target} 736 OUTPUT_VARIABLE gp_install_id_ov 737 ) 738 # second line is install name 739 string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}") 740 if(gp_install_id) 741 # trim 742 string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}") 743 #message("INSTALL ID is \"${gp_install_id}\"") 744 endif() 745 endif() 746 747 # Analyze each line for file names that match the regular expression: 748 # 749 foreach(candidate ${candidates}) 750 if("${candidate}" MATCHES "${gp_regex}") 751 752 # Extract information from each candidate: 753 if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}") 754 string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}") 755 else() 756 string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}") 757 endif() 758 759 if(gp_regex_cmp_count GREATER 1) 760 string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}") 761 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}") 762 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}") 763 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}") 764 endif() 765 766 if(gp_regex_cmp_count GREATER 2) 767 string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}") 768 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}") 769 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}") 770 string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}") 771 endif() 772 773 # Use the raw_item as the list entries returned by this function. Use the 774 # gp_resolve_item function to resolve it to an actual full path file if 775 # necessary. 776 # 777 set(item "${raw_item}") 778 779 # Add each item unless it is excluded: 780 # 781 set(add_item 1) 782 783 if("${item}" STREQUAL "${gp_install_id}") 784 set(add_item 0) 785 endif() 786 787 if(add_item AND ${exclude_system}) 788 set(type "") 789 gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type) 790 791 if("${type}" STREQUAL "system") 792 set(add_item 0) 793 endif() 794 endif() 795 796 if(add_item) 797 list(LENGTH ${prerequisites_var} list_length_before_append) 798 gp_append_unique(${prerequisites_var} "${item}") 799 list(LENGTH ${prerequisites_var} list_length_after_append) 800 801 if(${recurse}) 802 # If item was really added, this is the first time we have seen it. 803 # Add it to unseen_prereqs so that we can recursively add *its* 804 # prerequisites... 805 # 806 # But first: resolve its name to an absolute full path name such 807 # that the analysis tools can simply accept it as input. 808 # 809 if(NOT list_length_before_append EQUAL list_length_after_append) 810 gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item) 811 set(unseen_prereqs ${unseen_prereqs} "${resolved_item}") 812 endif() 813 endif() 814 endif() 815 else() 816 if(verbose) 817 message(STATUS "ignoring non-matching line: '${candidate}'") 818 endif() 819 endif() 820 endforeach() 821 822 list(LENGTH ${prerequisites_var} prerequisites_var_length) 823 if(prerequisites_var_length GREATER 0) 824 list(SORT ${prerequisites_var}) 825 endif() 826 if(${recurse}) 827 set(more_inputs ${unseen_prereqs}) 828 foreach(input ${more_inputs}) 829 get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}") 830 endforeach() 831 endif() 832 833 set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE) 834endfunction() 835 836 837function(list_prerequisites target) 838 if("${ARGV1}" STREQUAL "") 839 set(all 1) 840 else() 841 set(all "${ARGV1}") 842 endif() 843 844 if("${ARGV2}" STREQUAL "") 845 set(exclude_system 0) 846 else() 847 set(exclude_system "${ARGV2}") 848 endif() 849 850 if("${ARGV3}" STREQUAL "") 851 set(verbose 0) 852 else() 853 set(verbose "${ARGV3}") 854 endif() 855 856 set(count 0) 857 set(count_str "") 858 set(print_count "${verbose}") 859 set(print_prerequisite_type "${verbose}") 860 set(print_target "${verbose}") 861 set(type_str "") 862 863 get_filename_component(exepath "${target}" PATH) 864 865 set(prereqs "") 866 get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "") 867 868 if(print_target) 869 message(STATUS "File '${target}' depends on:") 870 endif() 871 872 foreach(d ${prereqs}) 873 math(EXPR count "${count} + 1") 874 875 if(print_count) 876 set(count_str "${count}. ") 877 endif() 878 879 if(print_prerequisite_type) 880 gp_file_type("${target}" "${d}" type) 881 set(type_str " (${type})") 882 endif() 883 884 message(STATUS "${count_str}${d}${type_str}") 885 endforeach() 886endfunction() 887 888 889function(list_prerequisites_by_glob glob_arg glob_exp) 890 message(STATUS "=============================================================================") 891 message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'") 892 message(STATUS "") 893 file(${glob_arg} file_list ${glob_exp}) 894 foreach(f ${file_list}) 895 is_file_executable("${f}" is_f_executable) 896 if(is_f_executable) 897 message(STATUS "=============================================================================") 898 list_prerequisites("${f}" ${ARGN}) 899 message(STATUS "") 900 endif() 901 endforeach() 902endfunction() 903