summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Cole <david.cole@kitware.com>2009-08-05 18:59:14 (GMT)
committerDavid Cole <david.cole@kitware.com>2009-08-05 18:59:14 (GMT)
commitfe0b121da93262210f81129cd462c70c880997c2 (patch)
tree9b5ab067a0113191ae1acfa076265803c4d76f30
parent80f0201b37576a4229ec9453a31d3be804abbcb4 (diff)
downloadCMake-fe0b121da93262210f81129cd462c70c880997c2.zip
CMake-fe0b121da93262210f81129cd462c70c880997c2.tar.gz
CMake-fe0b121da93262210f81129cd462c70c880997c2.tar.bz2
Overhaul GetPrerequisites and BundleUtilities: make fixup_bundle do something useful on Windows and Linux.
Formerly, fixup_bundle was useful only on the Mac for making standalone bundle applications that could be drag-n-drop moved to anyplace in the file system. fixup_bundle is not just for the Mac any more. It will now analyze executable files on Windows and Linux, too, and copy necessary non-system dlls to the same folder that the executable is in. This should work with dlls that you build as part of your build and also with 3rd-party dlls as long as you give fixup_bundle the right list of directories to search for those dlls. Many thanks to Clinton Stimpson for his help in ironing out the details involved in making this work.
-rw-r--r--Modules/BundleUtilities.cmake74
-rw-r--r--Modules/GetPrerequisites.cmake322
2 files changed, 264 insertions, 132 deletions
diff --git a/Modules/BundleUtilities.cmake b/Modules/BundleUtilities.cmake
index d11979a..fba5a15 100644
--- a/Modules/BundleUtilities.cmake
+++ b/Modules/BundleUtilities.cmake
@@ -174,14 +174,18 @@ function(get_bundle_and_executable app bundle_var executable_var valid_var)
is_file_executable("${app}" is_executable)
if(is_executable)
get_dotapp_dir("${app}" dotapp_dir)
- if(EXISTS "${dotapp_dir}" AND EXISTS "${app}")
+ if(EXISTS "${dotapp_dir}")
set(${bundle_var} "${dotapp_dir}" PARENT_SCOPE)
set(${executable_var} "${app}" PARENT_SCOPE)
set(valid 1)
- #message(STATUS "info: handled executable file case...")
- else(EXISTS "${dotapp_dir}" AND EXISTS "${app}")
- message(STATUS "warning: *NOT* handled - executable file case...")
- endif(EXISTS "${dotapp_dir}" AND EXISTS "${app}")
+ #message(STATUS "info: handled executable file in .app dir case...")
+ else()
+ get_filename_component(app_dir "${app}" PATH)
+ set(${bundle_var} "${app_dir}" PARENT_SCOPE)
+ set(${executable_var} "${app}" PARENT_SCOPE)
+ set(valid 1)
+ #message(STATUS "info: handled executable file in any dir case...")
+ endif()
else(is_executable)
message(STATUS "warning: *NOT* handled - not .app dir, not executable file...")
endif(is_executable)
@@ -230,6 +234,9 @@ endfunction(get_bundle_all_executables)
#
function(get_item_key item key_var)
get_filename_component(item_name "${item}" NAME)
+ if(WIN32)
+ string(TOLOWER "${item_name}" item_name)
+ endif()
string(REGEX REPLACE "\\." "_" ${key_var} "${item_name}")
set(${key_var} ${${key_var}} PARENT_SCOPE)
endfunction(get_item_key)
@@ -388,15 +395,25 @@ endfunction(get_bundle_keys)
# copy_resolved_item_into_bundle
#
-# Copy a resolved item into the bundle if necessary. Copy is not necessary if the resolved_item
-# is the same as the resolved_embedded_item.
+# Copy a resolved item into the bundle if necessary. Copy is not necessary if
+# the resolved_item is "the same as" the resolved_embedded_item.
#
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
- if("${resolved_item}" STREQUAL "${resolved_embedded_item}")
+ if(WIN32)
+ # ignore case on Windows
+ string(TOLOWER "${resolved_item}" resolved_item_compare)
+ string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
+ else()
+ set(resolved_item_compare "${resolved_item}")
+ set(resolved_embedded_item_compare "${resolved_embedded_item}")
+ endif()
+
+ if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
- else("${resolved_item}" STREQUAL "${resolved_embedded_item}")
+ else()
+ #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
- endif("${resolved_item}" STREQUAL "${resolved_embedded_item}")
+ endif()
endfunction(copy_resolved_item_into_bundle)
@@ -503,9 +520,12 @@ function(fixup_bundle app libs dirs)
message(STATUS "fixup_bundle: fixing...")
foreach(key ${keys})
math(EXPR i ${i}+1)
- message(STATUS "${i}/${n}: fixing up '${${key}_RESOLVED_EMBEDDED_ITEM}'")
- #message(STATUS " exepath='${exepath}'")
- fixup_bundle_item("${${key}_RESOLVED_EMBEDDED_ITEM}" "${exepath}" "${dirs}")
+ if(APPLE)
+ message(STATUS "${i}/${n}: fixing up '${${key}_RESOLVED_EMBEDDED_ITEM}'")
+ fixup_bundle_item("${${key}_RESOLVED_EMBEDDED_ITEM}" "${exepath}" "${dirs}")
+ else(APPLE)
+ message(STATUS "${i}/${n}: fix-up not required on this platform '${${key}_RESOLVED_EMBEDDED_ITEM}'")
+ endif(APPLE)
endforeach(key)
message(STATUS "fixup_bundle: cleaning up...")
@@ -514,7 +534,7 @@ function(fixup_bundle app libs dirs)
message(STATUS "fixup_bundle: verifying...")
verify_app("${app}")
else(valid)
- message(STATUS "error: fixup_bundle: not a valid bundle")
+ message(SEND_ERROR "error: fixup_bundle: not a valid bundle")
endif(valid)
message(STATUS "fixup_bundle: done")
@@ -550,30 +570,42 @@ function(verify_bundle_prerequisites bundle result_var info_var)
is_file_executable("${f}" is_executable)
if(is_executable)
get_filename_component(exepath "${f}" PATH)
- message(STATUS "executable file: ${f}")
-
math(EXPR count "${count} + 1")
+ message(STATUS "executable file ${count}: ${f}")
+
set(prereqs "")
get_prerequisites("${f}" prereqs 1 1 "${exepath}" "")
+ # On the Mac,
# "embedded" and "system" prerequisites are fine... anything else means
# the bundle's prerequisites are not verified (i.e., the bundle is not
# really "standalone")
#
+ # On Windows (and others? Linux/Unix/...?)
+ # "local" and "system" prereqs are fine...
+ #
set(external_prereqs "")
+
foreach(p ${prereqs})
set(p_type "")
gp_file_type("${f}" "${p}" p_type)
- if (NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system")
- set(external_prereqs ${external_prereqs} "${p}")
- endif (NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system")
+
+ if(APPLE)
+ if(NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system")
+ set(external_prereqs ${external_prereqs} "${p}")
+ endif()
+ else()
+ if(NOT "${p_type}" STREQUAL "local" AND NOT "${p_type}" STREQUAL "system")
+ set(external_prereqs ${external_prereqs} "${p}")
+ endif()
+ endif()
endforeach(p)
if(external_prereqs)
- # Found non-system/non-embedded prerequisites:
+ # Found non-system/somehow-unacceptable prerequisites:
set(result 0)
- set(info ${info} "non-system/non-embedded prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n")
+ set(info ${info} "external prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n")
endif(external_prereqs)
endif(is_executable)
endforeach(f)
diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake
index e7457d6..a357f00 100644
--- a/Modules/GetPrerequisites.cmake
+++ b/Modules/GetPrerequisites.cmake
@@ -10,12 +10,13 @@
#
# The following functions are provided by this script:
# gp_append_unique
-# gp_file_type
# 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
+# gp_file_type
# get_prerequisites
# list_prerequisites
# list_prerequisites_by_glob
@@ -45,83 +46,6 @@ function(gp_append_unique list_var value)
endfunction(gp_append_unique)
-# 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}.
-#
-# Possible types are:
-# system
-# local
-# embedded
-# other
-#
-function(gp_file_type original_file file type_var)
- set(is_embedded 0)
- set(is_local 0)
- set(is_system 0)
-
- string(TOLOWER "${original_file}" original_lower)
- string(TOLOWER "${file}" lower)
-
- if("${file}" MATCHES "^@(executable|loader)_path")
- set(is_embedded 1)
- endif("${file}" MATCHES "^@(executable|loader)_path")
-
- if(NOT is_embedded)
- if(UNIX)
- if("${file}" MATCHES "^(/lib/|/lib32/|/lib64/)")
- set(is_system 1)
- endif("${file}" MATCHES "^(/lib/|/lib32/|/lib64/)")
- endif(UNIX)
-
- if(APPLE)
- if("${file}" MATCHES "^(/System/Library/|/usr/lib/)")
- set(is_system 1)
- endif("${file}" MATCHES "^(/System/Library/|/usr/lib/)")
- endif(APPLE)
-
- if(WIN32)
- string(TOLOWER "$ENV{SystemRoot}" sysroot)
- string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}")
-
- string(TOLOWER "$ENV{windir}" windir)
- string(REGEX REPLACE "\\\\" "/" windir "${windir}")
-
- if("${lower}" MATCHES "^(${sysroot}/system|${windir}/system|msvc[^/]+dll)")
- set(is_system 1)
- endif("${lower}" MATCHES "^(${sysroot}/system|${windir}/system|msvc[^/]+dll)")
- endif(WIN32)
-
- if(NOT is_system)
- get_filename_component(original_path "${original_lower}" PATH)
- get_filename_component(path "${lower}" PATH)
- if("${original_path}" STREQUAL "${path}")
- set(is_local 1)
- endif("${original_path}" STREQUAL "${path}")
- endif(NOT is_system)
- endif(NOT is_embedded)
-
- # Return type string based on computed booleans:
- #
- set(type "other")
-
- if(is_system)
- set(type "system")
- else(is_system)
- if(is_embedded)
- set(type "embedded")
- else(is_embedded)
- if(is_local)
- set(type "local")
- endif(is_local)
- endif(is_embedded)
- endif(is_system)
-
- set(${type_var} "${type}" PARENT_SCOPE)
-endfunction(gp_file_type)
-
-
# is_file_executable file result_var
#
# Return 1 in ${result_var} if ${file} is a binary executable.
@@ -204,39 +128,49 @@ endfunction(is_file_executable)
# gp_item_default_embedded_path_override function.
#
function(gp_item_default_embedded_path item default_embedded_path_var)
- #
- # 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.
- #
- # By default, embed things right next to the main bundle executable:
+ # On Windows and Linux, "embed" prerequisites in the same directory
+ # as the executable by default:
#
- set(path "@executable_path/../../Contents/MacOS")
-
+ set(path "@executable_path")
set(overridden 0)
- # Embed .dylibs right next to the main bundle executable:
+ # On the Mac, relative to the executable depending on the type
+ # of the thing we are embedding:
#
- if(item MATCHES "\\.dylib$")
- set(path "@executable_path/../MacOS")
- set(overridden 1)
- endif(item MATCHES "\\.dylib$")
+ 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.
+ #
- # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS):
- #
- if(NOT overridden)
- if(item MATCHES "[^/]+\\.framework/")
- set(path "@executable_path/../Frameworks")
+ # 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")
set(overridden 1)
- endif(item MATCHES "[^/]+\\.framework/")
- endif(NOT overridden)
+ endif(item MATCHES "\\.dylib$")
- # Provide a hook so that projects can override the default embedded location of
- # any given library by whatever logic they choose:
+ # 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)
+ endif(item MATCHES "[^/]+\\.framework/")
+ endif(NOT overridden)
+ endif()
+
+ # Provide a hook so that projects can override the default embedded location
+ # of any given library by whatever logic they choose:
#
if(COMMAND gp_item_default_embedded_path_override)
gp_item_default_embedded_path_override("${item}" path)
@@ -276,7 +210,7 @@ function(gp_resolve_item context item exepath dirs resolved_item_var)
set(resolved 1)
set(resolved_item "${ri}")
else(EXISTS "${ri}")
- message(STATUS "info: embedded item does not exist '${ri}'")
+ message(STATUS "warning: embedded item does not exist '${ri}'")
endif(EXISTS "${ri}")
endif(item MATCHES "@executable_path")
endif(NOT resolved)
@@ -296,16 +230,17 @@ function(gp_resolve_item context item exepath dirs resolved_item_var)
set(resolved 1)
set(resolved_item "${ri}")
else(EXISTS "${ri}")
- message(STATUS "info: embedded item does not exist '${ri}'")
+ message(STATUS "warning: embedded item does not exist '${ri}'")
endif(EXISTS "${ri}")
endif(item MATCHES "@loader_path")
endif(NOT resolved)
if(NOT resolved)
set(ri "ri-NOTFOUND")
- find_file(ri "${item}" ${dirs})
+ find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
+ find_file(ri "${item}" ${exepath} ${dirs} /usr/lib)
if(ri)
- #message(STATUS "info: found item in dirs (${ri})")
+ #message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
set(resolved 1)
set(resolved_item "${ri}")
set(ri "ri-NOTFOUND")
@@ -321,7 +256,7 @@ function(gp_resolve_item context item exepath dirs resolved_item_var)
"/System/Library/Frameworks"
)
if(fw)
- #message(STATUS "info: found framework (${fw})")
+ #message(STATUS "info: 'find_file' found framework (${fw})")
set(resolved 1)
set(resolved_item "${fw}")
set(fw "fw-NOTFOUND")
@@ -335,8 +270,10 @@ function(gp_resolve_item context item exepath dirs resolved_item_var)
if(WIN32)
if(NOT resolved)
set(ri "ri-NOTFOUND")
- find_program(ri "${item}" PATHS "${dirs}")
+ find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH)
+ find_program(ri "${item}" PATHS "${exepath};${dirs}")
if(ri)
+ #message(STATUS "info: 'find_program' in exepath/dirs (${ri})")
set(resolved 1)
set(resolved_item "${ri}")
set(ri "ri-NOTFOUND")
@@ -352,13 +289,164 @@ function(gp_resolve_item context item exepath dirs resolved_item_var)
endif(COMMAND gp_resolve_item_override)
if(NOT resolved)
- message(STATUS "warning: cannot resolve item '${item}'")
+ 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}'
+#******************************************************************************
+#")
endif(NOT resolved)
set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE)
endfunction(gp_resolve_item)
+# gp_resolved_file_type original_file file exepath dirs type_var
+#
+# Return the type of ${file} with respect to ${original_file}. String
+# describing type of prerequisite is returned in variable named ${type_var}.
+#
+# Use ${exepath} and ${dirs} if necessary to resolve non-absolute ${file}
+# values -- but only for non-embedded items.
+#
+# Possible types are:
+# system
+# local
+# embedded
+# other
+#
+function(gp_resolved_file_type original_file file exepath dirs type_var)
+ #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()
+
+ 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}")
+ gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file)
+ endif()
+
+ string(TOLOWER "${original_file}" original_lower)
+ string(TOLOWER "${resolved_file}" lower)
+
+ if(UNIX)
+ if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/)")
+ 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)
+ string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}")
+
+ string(TOLOWER "$ENV{windir}" windir)
+ string(REGEX REPLACE "\\\\" "/" windir "${windir}")
+
+ if(lower MATCHES "^(${sysroot}/system|${windir}/system|(.*/)*msvc[^/]+dll)")
+ set(is_system 1)
+ endif()
+ endif()
+
+ if(NOT is_system)
+ get_filename_component(original_path "${original_lower}" PATH)
+ get_filename_component(path "${lower}" PATH)
+ if("${original_path}" STREQUAL "${path}")
+ set(is_local 1)
+ 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()
+
+ set(${type_var} "${type}" PARENT_SCOPE)
+
+ #message(STATUS "**")
+endfunction()
+
+
+# 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}.
+#
+# Possible types are:
+# system
+# local
+# embedded
+# other
+#
+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)
+endfunction(gp_file_type)
+
+
# get_prerequisites target prerequisites_var exclude_system recurse dirs
#
# Get the list of shared library files required by ${target}. The list in
@@ -477,6 +565,14 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa
#
# </setup-gp_tool-vars>
+ if("${gp_tool}" STREQUAL "ldd")
+ set(old_ld_env "$ENV{LD_LIBRARY_PATH}")
+ foreach(dir ${exepath} ${dirs})
+ set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}")
+ endforeach(dir)
+ endif("${gp_tool}" STREQUAL "ldd")
+
+
# Track new prerequisites at each new level of recursion. Start with an
# empty list at each level:
#
@@ -489,6 +585,10 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa
OUTPUT_VARIABLE gp_cmd_ov
)
+ if("${gp_tool}" STREQUAL "ldd")
+ set(ENV{LD_LIBRARY_PATH} "${old_ld_env}")
+ endif("${gp_tool}" STREQUAL "ldd")
+
if(verbose)
message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>")
message(STATUS "gp_cmd_ov='${gp_cmd_ov}'")
@@ -535,7 +635,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa
if(${exclude_system})
set(type "")
- gp_file_type("${target}" "${item}" type)
+ gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type)
if("${type}" STREQUAL "system")
set(add_item 0)