summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorBill Hoffman <bill.hoffman@kitware.com>2008-02-16 21:33:08 (GMT)
committerBill Hoffman <bill.hoffman@kitware.com>2008-02-16 21:33:08 (GMT)
commit703be12c584ef8a3310f95b6c79a974e87065e06 (patch)
tree805a796ea5d29179c69286818266fe9c13c40729 /Modules
parent22927ede1e74e43ad78699ff4082d8b78e50599c (diff)
downloadCMake-703be12c584ef8a3310f95b6c79a974e87065e06.zip
CMake-703be12c584ef8a3310f95b6c79a974e87065e06.tar.gz
CMake-703be12c584ef8a3310f95b6c79a974e87065e06.tar.bz2
ENH: add script to ingest library depends into a bundle
Diffstat (limited to 'Modules')
-rw-r--r--Modules/CMakeIngestOSXBundleLibraries.cmake316
1 files changed, 316 insertions, 0 deletions
diff --git a/Modules/CMakeIngestOSXBundleLibraries.cmake b/Modules/CMakeIngestOSXBundleLibraries.cmake
new file mode 100644
index 0000000..65bd839
--- /dev/null
+++ b/Modules/CMakeIngestOSXBundleLibraries.cmake
@@ -0,0 +1,316 @@
+#
+# CMakeIngestOSXBundleLibraries.cmake
+#
+# Only for the Mac build.
+#
+# Depends on OS tools:
+# otool
+# install_name_tool
+#
+# This script ingests libraries and frameworks into an existing .app bundle and
+# then uses install_name_tool to fixup the references to the newly embedded
+# libraries so that they all refer to each other via "@executable_path."
+#
+# The main intent (and simplifying assumption used for developing the script)
+# is to have a single executable .app bundle that becomes "self-contained" by
+# copying all non-system libs that it depends on into itself. The further
+# assumption is that all such dependencies are simple .dylib shared library
+# files or Mac Framework libraries.
+#
+# This script can be used as part of the build via ADD_CUSTOM_COMMAND, or used
+# only during make install via INSTALL SCRIPT.
+#
+IF(NOT DEFINED input_file)
+ MESSAGE(FATAL_ERROR "
+${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): error: Variable input_file is not defined.
+
+Use a command line like this to use this script:
+ cmake \"-Dinput_file=filename\" \"-Dextra_libs=/path/to/lib1;/path/to/lib2\" \"-Dlib_path=/path/to/unqualified/libs\" -P \"${CMAKE_CURRENT_LIST_FILE}\"
+
+'input_file' should be the main executable inside a Mac bundle directory structure.
+For example, use 'bin/paraview.app/Contents/MacOS/paraview' from a ParaView binary dir.
+
+'extra_libs' should be a semi-colon separated list of full path names to extra libraries
+to copy into the bundle that cannot be derived from otool -L output. For example, you may
+also want to fixup dynamically loaded plugins from your build tree and copy them into the
+bundle.
+
+'lib_path' should be the path where to find libraries referenced without a path name in
+otool -L output.
+
+")
+ENDIF(NOT DEFINED input_file)
+
+SET(eol_char "E")
+
+IF(APPLE)
+ SET(dep_tool "otool")
+ SET(dep_cmd_args "-L")
+ SET(dep_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$")
+ENDIF(APPLE)
+
+
+MESSAGE("")
+MESSAGE("# Script \"${CMAKE_CURRENT_LIST_FILE}\" running...")
+MESSAGE("")
+MESSAGE("input_file: '${input_file}'")
+MESSAGE("extra_libs: '${extra_libs}'")
+MESSAGE("lib_path: '${lib_path}'")
+MESSAGE("")
+
+GET_FILENAME_COMPONENT(input_file_full "${input_file}" ABSOLUTE)
+MESSAGE("input_file_full: '${input_file_full}'")
+
+GET_FILENAME_COMPONENT(bundle "${input_file_full}/../../.." ABSOLUTE)
+MESSAGE("bundle: '${bundle}'")
+
+
+FIND_PROGRAM(dep_cmd ${dep_tool})
+
+
+MACRO(APPEND_UNIQUE au_list_var au_value)
+ SET(${au_list_var} ${${au_list_var}} "${au_value}")
+ENDMACRO(APPEND_UNIQUE)
+
+
+MACRO(GATHER_DEPENDENTS gd_target gd_dependents_var)
+ EXECUTE_PROCESS(
+ COMMAND ${dep_cmd} ${dep_cmd_args} ${gd_target}
+ OUTPUT_VARIABLE dep_tool_ov
+ )
+
+ STRING(REGEX REPLACE ";" "\\\\;" dep_candidates "${dep_tool_ov}")
+ STRING(REGEX REPLACE "\n" "${eol_char};" dep_candidates "${dep_candidates}")
+
+ SET(${gd_dependents_var} "")
+
+ FOREACH(candidate ${dep_candidates})
+ IF("${candidate}" MATCHES "${dep_regex}")
+ STRING(REGEX REPLACE "${dep_regex}" "\\1" raw_item "${candidate}")
+ STRING(REGEX REPLACE "${dep_regex}" "\\2" raw_compat_version "${candidate}")
+ STRING(REGEX REPLACE "${dep_regex}" "\\3" raw_current_version "${candidate}")
+
+ SET(item "${raw_item}")
+
+ 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}")
+
+ 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}")
+
+ #MESSAGE("${raw_item} - compat ${raw_compat_version} - current ${raw_current_version}")
+ APPEND_UNIQUE("${gd_dependents_var}" "${item}")
+ ELSE("${candidate}" MATCHES "${dep_regex}")
+ IF("${candidate}" STREQUAL "${gd_target}:${eol_char}")
+ #MESSAGE("info: ignoring target name...")
+ ELSE("${candidate}" STREQUAL "${gd_target}:${eol_char}")
+ MESSAGE("error: candidate='${candidate}'")
+ ENDIF("${candidate}" STREQUAL "${gd_target}:${eol_char}")
+ ENDIF("${candidate}" MATCHES "${dep_regex}")
+ ENDFOREACH(candidate)
+ENDMACRO(GATHER_DEPENDENTS)
+
+
+MESSAGE("Gathering dependent libraries for '${input_file_full}'...")
+GATHER_DEPENDENTS("${input_file_full}" deps)
+MESSAGE("")
+
+
+# Order lexicographically:
+#
+LIST(SORT deps)
+
+
+# Split into separate lists, "system" "embedded" and "nonsystem" libraries.
+# System libs are assumed to be available on all target runtime Macs and do not
+# need to be copied/fixed-up by this script. Embedded libraries are assumed to
+# be in the bundle and fixed-up already. Only non-system, non-embedded libs
+# need copying and fixing up...
+#
+SET(system_deps "")
+SET(embedded_deps "")
+SET(nonsystem_deps "")
+
+FOREACH(d ${deps})
+ SET(d_is_embedded_lib 0)
+ SET(d_is_system_lib 0)
+
+ IF("${d}" MATCHES "^(/System/Library|/usr/lib)")
+ SET(d_is_system_lib 1)
+ ELSE("${d}" MATCHES "^(/System/Library|/usr/lib)")
+ IF("${d}" MATCHES "^@executable_path")
+ SET(d_is_embedded_lib 1)
+ ENDIF("${d}" MATCHES "^@executable_path")
+ ENDIF("${d}" MATCHES "^(/System/Library|/usr/lib)")
+
+ IF(d_is_system_lib)
+ SET(system_deps ${system_deps} "${d}")
+ ELSE(d_is_system_lib)
+ IF(d_is_embedded_lib)
+ SET(embedded_deps ${embedded_deps} "${d}")
+ ELSE(d_is_embedded_lib)
+ SET(nonsystem_deps ${nonsystem_deps} "${d}")
+ ENDIF(d_is_embedded_lib)
+ ENDIF(d_is_system_lib)
+ENDFOREACH(d)
+
+MESSAGE("")
+MESSAGE("system_deps:")
+FOREACH(d ${system_deps})
+ MESSAGE("${d}")
+ENDFOREACH(d ${system_deps})
+
+MESSAGE("")
+MESSAGE("embedded_deps:")
+FOREACH(d ${embedded_deps})
+ MESSAGE("${d}")
+ENDFOREACH(d ${embedded_deps})
+
+MESSAGE("")
+MESSAGE("nonsystem_deps:")
+FOREACH(d ${nonsystem_deps})
+ MESSAGE("${d}")
+ENDFOREACH(d ${nonsystem_deps})
+
+MESSAGE("")
+
+
+MACRO(COPY_LIBRARY_INTO_BUNDLE clib_bundle clib_libsrc clib_dstlibs clib_fixups)
+ #
+ # If the source library is a framework, copy just the shared lib bit of the framework
+ # into the bundle under "${clib_bundle}/Contents/Frameworks" - if it is just a dylib
+ # copy it into the same directory with the main bundle executable under
+ # "${clib_bundle}/Contents/MacOS"
+ #
+ IF("${clib_libsrc}" MATCHES ".framework/.*/.*/.*")
+ GET_FILENAME_COMPONENT(fw_src "${clib_libsrc}" ABSOLUTE)
+ GET_FILENAME_COMPONENT(fw_srcdir "${clib_libsrc}/../../.." ABSOLUTE)
+ GET_FILENAME_COMPONENT(fwdirname "${fw_srcdir}" NAME)
+ STRING(REGEX REPLACE "^(.*)\\.framework$" "\\1" fwname "${fwdirname}")
+ STRING(REGEX REPLACE "^.*/${fwname}\\.framework/(.*)$" "\\1" fwlibname "${clib_libsrc}")
+ SET(fw_dstdir "${clib_bundle}/Contents/Frameworks/${fwdirname}")
+
+# MESSAGE("")
+# MESSAGE("fwdirname: '${fwdirname}'")
+# MESSAGE("fwname: '${fwname}'")
+# MESSAGE("fwlibname: '${fwlibname}'")
+# MESSAGE("fw_src: '${fw_src}'")
+# MESSAGE("fw_srcdir: '${fw_srcdir}'")
+# MESSAGE("fw_dstdir: '${fw_dstdir}'")
+# MESSAGE("new_name: '@executable_path/../Frameworks/${fwdirname}/${fwlibname}'")
+# MESSAGE("")
+
+ MESSAGE("Copying ${fw_srcdir} into bundle...")
+
+ # This command copies the *entire* framework recursively:
+ #
+# EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_directory
+# "${fw_srcdir}" "${fw_dstdir}"
+# )
+
+ # This command copies just the main shared lib of the framework:
+ # (This technique will not work for frameworks that have necessary
+ # resource or auxiliary files...)
+ #
+ EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy
+ "${fw_src}" "${fw_dstdir}/${fwlibname}"
+ )
+
+ EXECUTE_PROCESS(COMMAND install_name_tool
+ -id "@executable_path/../Frameworks/${fwdirname}/${fwlibname}"
+ "${clib_bundle}/Contents/Frameworks/${fwdirname}/${fwlibname}"
+ )
+ SET(${clib_dstlibs} ${${clib_dstlibs}}
+ "${clib_bundle}/Contents/Frameworks/${fwdirname}/${fwlibname}"
+ )
+ SET(${clib_fixups} ${${clib_fixups}}
+ "-change"
+ "${clib_libsrc}"
+ "@executable_path/../Frameworks/${fwdirname}/${fwlibname}"
+ )
+ ELSE("${clib_libsrc}" MATCHES ".framework/.*/.*/.*")
+ IF("${clib_libsrc}" MATCHES "/")
+ SET(clib_libsrcfull "${clib_libsrc}")
+ ELSE("${clib_libsrc}" MATCHES "/")
+ SET(clib_libsrcfull "${lib_path}/${clib_libsrc}")
+ IF(NOT EXISTS "${clib_libsrcfull}")
+ MESSAGE(FATAL_ERROR "error: '${clib_libsrcfull}' does not exist...")
+ ENDIF(NOT EXISTS "${clib_libsrcfull}")
+ ENDIF("${clib_libsrc}" MATCHES "/")
+
+ GET_FILENAME_COMPONENT(dylib_src "${clib_libsrcfull}" ABSOLUTE)
+ GET_FILENAME_COMPONENT(dylib_name "${dylib_src}" NAME)
+ SET(dylib_dst "${clib_bundle}/Contents/MacOS/${dylib_name}")
+
+# MESSAGE("dylib_src: ${dylib_src}")
+# MESSAGE("dylib_dst: ${dylib_dst}")
+# MESSAGE("new_name: '@executable_path/${dylib_name}'")
+
+ MESSAGE("Copying ${dylib_src} into bundle...")
+ EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy
+ "${dylib_src}" "${dylib_dst}")
+ EXECUTE_PROCESS(COMMAND install_name_tool
+ -id "@executable_path/${dylib_name}"
+ "${dylib_dst}"
+ )
+ SET(${clib_dstlibs} ${${clib_dstlibs}}
+ "${dylib_dst}"
+ )
+ SET(${clib_fixups} ${${clib_fixups}}
+ "-change"
+ "${clib_libsrc}"
+ "@executable_path/${dylib_name}"
+ )
+ ENDIF("${clib_libsrc}" MATCHES ".framework/.*/.*/.*")
+ENDMACRO(COPY_LIBRARY_INTO_BUNDLE)
+
+
+# Copy dependent "nonsystem" libraries into the bundle:
+#
+MESSAGE("Copying dependent libraries into bundle...")
+SET(srclibs ${nonsystem_deps} ${extra_libs})
+SET(dstlibs "")
+SET(fixups "")
+FOREACH(d ${srclibs})
+ COPY_LIBRARY_INTO_BUNDLE("${bundle}" "${d}" dstlibs fixups)
+ENDFOREACH(d)
+
+MESSAGE("")
+MESSAGE("dstlibs='${dstlibs}'")
+MESSAGE("")
+MESSAGE("fixups='${fixups}'")
+MESSAGE("")
+
+
+# Fixup references to copied libraries in the main bundle executable and in the
+# copied libraries themselves:
+#
+IF(NOT "${fixups}" STREQUAL "")
+ MESSAGE("Fixing up references...")
+ FOREACH(d ${dstlibs} "${input_file_full}")
+ MESSAGE("fixing up references in: '${d}'")
+ EXECUTE_PROCESS(COMMAND install_name_tool ${fixups} "${d}")
+ ENDFOREACH(d)
+ MESSAGE("")
+ENDIF(NOT "${fixups}" STREQUAL "")
+
+
+# List all references to eyeball them and make sure they look right:
+#
+MESSAGE("Listing references...")
+FOREACH(d ${dstlibs} "${input_file_full}")
+ EXECUTE_PROCESS(COMMAND otool -L "${d}")
+ MESSAGE("")
+ENDFOREACH(d)
+MESSAGE("")
+
+
+# Output file:
+#
+GET_FILENAME_COMPONENT(script_name "${CMAKE_CURRENT_LIST_FILE}" NAME)
+FILE(WRITE "${input_file_full}_${script_name}" "# Script \"${CMAKE_CURRENT_LIST_FILE}\" completed.\n")
+MESSAGE("")
+MESSAGE("# Script \"${CMAKE_CURRENT_LIST_FILE}\" completed.")
+MESSAGE("")