mitsuba/data/cmake/MtsBundleUtilities.cmake

321 lines
13 KiB
CMake

# - Functions to help assemble a standalone bundle application (with @rpath).
# A collection of CMake utility functions useful for dealing with .app
# bundles on the Mac and bundle-like directories on any OS.
# This module is based on the "BundleUtilities" module included with
# CMake 2.8.5, the difference is that for the Mac this module assumes
# that all dependencies (both dynamic libraries and frameworks) are installed
# under <bundle>/Contents/Frameworks. Thus the fixup stage will use @rpath
# instead of @executable_path.
#
# The following functions are provided by this module:
# mts_fixup_bundle
# mts_clear_bundle_keys (privately used by mts_fixup_bundle)
# mts_fixup_bundle_item (privately used by mts_fixup_bundle)
# Requires CMake 2.6 or greater because it uses function, break and
# PARENT_SCOPE. Also depends on GetPrerequisites.cmake and the standard
# BundleUtilities.cmake.
#
# MTS_FIXUP_BUNDLE(<app> <libs> <dirs>)
# Fix up a bundle in-place and make it standalone, such that it can be
# drag-n-drop copied to another machine and run on that machine as long as all
# of the system libraries are compatible.
#
# If you pass plugins to fixup_bundle as the libs parameter, you should install
# them or copy them into the bundle before calling fixup_bundle. The "libs"
# parameter is a list of libraries that must be fixed up, but that cannot be
# determined by otool output analysis. (i.e., plugins)
#
# Gather all the keys for all the executables and libraries in a bundle, and
# then, for each key, copy each prerequisite into the bundle. Then fix each one
# up according to its own list of prerequisites.
#
# Then clear all the keys and call verify_app on the final bundle to ensure
# that it is truly standalone.
#
# MTS_FIXUP_BUNDLE_ITEM(<resolved_embedded_item> <exepath> <dirs>)
# Get the direct/non-system prerequisites of the resolved embedded item. For
# each prerequisite, change the way it is referenced to the value of the
# _EMBEDDED_ITEM keyed variable for that prerequisite. (Most likely changing to
# an "@executable_path" style reference.)
#
# This function requires that the resolved_embedded_item be "inside" the bundle
# already. In other words, if you pass plugins to fixup_bundle as the libs
# parameter, you should install them or copy them into the bundle before
# calling fixup_bundle. The "libs" parameter is a list of libraries that must
# be fixed up, but that cannot be determined by otool output analysis. (i.e.,
# plugins)
#
# Also, change the id of the item being fixed up to its own _EMBEDDED_ITEM
# value.
#
# Accumulate changes in a local variable and make *one* call to
# install_name_tool at the end of the function with all the changes at once.
#
# If the BU_CHMOD_BUNDLE_ITEMS variable is set then bundle items will be
# marked writable before install_name_tool tries to change them.
#=============================================================================
# Copyright 2008-2009 Kitware, Inc.
#
# CMake - Cross Platform Makefile Generator
# Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
# nor the names of their contributors may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ----------------------------------------------------------------------------
#
# The above copyright and license notice applies to distributions of
# CMake in source and binary form. Some source files contain additional
# notices of original copyright by their contributors; see each source
# for details. Third-party software packages supplied with CMake under
# compatible licenses provide their own copyright notices documented in
# corresponding subdirectories.
#
# ----------------------------------------------------------------------------
#
# CMake was initially developed by Kitware with the following sponsorship:
#
# * National Library of Medicine at the National Institutes of Health
# as part of the Insight Segmentation and Registration Toolkit (ITK).
#
# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
# Visualization Initiative.
#
# * National Alliance for Medical Image Computing (NAMIC) is funded by the
# National Institutes of Health through the NIH Roadmap for Medical
# Research, Grant U54 EB005149.
#
#=============================================================================
# The module still depends on the standard BundleUtilities and GetPrerequites modules
include(BundleUtilities)
include(GetPrerequisites)
function(mts_clear_bundle_keys keys_var)
foreach(key ${${keys_var}})
set(${key}_ITEM PARENT_SCOPE)
set(${key}_RESOLVED_ITEM PARENT_SCOPE)
set(${key}_DEFAULT_EMBEDDED_PATH PARENT_SCOPE)
set(${key}_EMBEDDED_ITEM PARENT_SCOPE)
set(${key}_RESOLVED_EMBEDDED_ITEM PARENT_SCOPE)
set(${key}_COPYFLAG PARENT_SCOPE)
set(${key}_EMBEDDED_RPATH PARENT_SCOPE)
set(${key}_RPATH_DIR PARENT_SCOPE)
endforeach(key)
set(${keys_var} PARENT_SCOPE)
endfunction(mts_clear_bundle_keys)
function(mts_fixup_bundle_item resolved_embedded_item exepath dirs)
# This item's key is "ikey":
#
get_item_key("${resolved_embedded_item}" ikey)
# Ensure the item is "inside the .app bundle" -- it should not be fixed up if
# it is not in the .app bundle... Otherwise, we'll modify files in the build
# tree, or in other varied locations around the file system, with our call to
# install_name_tool. Make sure that doesn't happen here:
#
get_dotapp_dir("${exepath}" exe_dotapp_dir)
string(LENGTH "${exe_dotapp_dir}/" exe_dotapp_dir_length)
string(LENGTH "${resolved_embedded_item}" resolved_embedded_item_length)
set(path_too_short 0)
set(is_embedded 0)
if(${resolved_embedded_item_length} LESS ${exe_dotapp_dir_length})
set(path_too_short 1)
endif()
if(NOT path_too_short)
string(SUBSTRING "${resolved_embedded_item}" 0 ${exe_dotapp_dir_length} item_substring)
if("${exe_dotapp_dir}/" STREQUAL "${item_substring}")
set(is_embedded 1)
endif()
endif()
if(NOT is_embedded)
message(" exe_dotapp_dir/='${exe_dotapp_dir}/'")
message(" item_substring='${item_substring}'")
message(" resolved_embedded_item='${resolved_embedded_item}'")
message("")
message("Install or copy the item into the bundle before calling mts_fixup_bundle.")
message("Or maybe there's a typo or incorrect path in one of the args to mts_fixup_bundle?")
message("")
message(FATAL_ERROR "cannot fixup an item that is not in the bundle...")
endif()
set(prereqs "")
get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}")
set(changes "")
set(rpath_dirlst "")
foreach(pr ${prereqs})
# Each referenced item's key is "rkey" in the loop:
#
get_item_key("${pr}" rkey)
if(NOT "${${rkey}_EMBEDDED_ITEM}" STREQUAL "")
set(changes ${changes} "-change" "${pr}" "${${rkey}_EMBEDDED_ITEM}")
else(NOT "${${rkey}_EMBEDDED_ITEM}" STREQUAL "")
message("warning: unexpected reference to '${pr}'")
endif(NOT "${${rkey}_EMBEDDED_ITEM}" STREQUAL "")
if(NOT "${${rkey}_EMBEDDED_RPATH}" STREQUAL "" AND
NOT "${${rkey}_RPATH_DIR}" STREQUAL "")
# Assumes the rpath dir is already an absolute path
list(APPEND rpath_dirlst "${${rkey}_RPATH_DIR}")
endif()
endforeach(pr)
if(BU_CHMOD_BUNDLE_ITEMS)
execute_process(COMMAND chmod u+w "${resolved_embedded_item}")
endif()
# Assumes that the only required location
if(rpath_dirlst)
list(REMOVE_DUPLICATES rpath_dirlst)
list(LENGTH rpath_dirlst rpath_len)
if (rpath_len GREATER 1)
message(WARNING "Only one location for rpath is supported, ${rpath_len} provided.")
endif ()
list(GET rpath_dirlst 0 rpath_dir)
# Determine how to get from the current component directory to rpath_dir
get_filename_component(resolved_embedded_path "${resolved_embedded_item}" PATH)
file(RELATIVE_PATH rpath_relative "${resolved_embedded_path}" "${rpath_dir}")
if("${rpath_relative}" STREQUAL "")
set(embedded_rpath "@loader_path/.")
else()
set(embedded_rpath "@loader_path/${rpath_relative}")
endif()
list(APPEND changes "-add_rpath" "${embedded_rpath}")
endif()
# Change this item's id and all of its references in one call
# to install_name_tool:
#
execute_process(COMMAND install_name_tool
${changes} -id "${${ikey}_EMBEDDED_ITEM}" "${resolved_embedded_item}"
)
endfunction(mts_fixup_bundle_item)
function(mts_fixup_bundle app libs dirs)
message(STATUS "mts_fixup_bundle")
message(STATUS " app='${app}'")
message(STATUS " libs='${libs}'")
message(STATUS " dirs='${dirs}'")
get_bundle_and_executable("${app}" bundle executable valid)
if(valid)
get_filename_component(exepath "${executable}" PATH)
message(STATUS "mts_fixup_bundle: preparing...")
get_bundle_keys("${app}" "${libs}" "${dirs}" keys)
message(STATUS "mts_fixup_bundle: copying...")
list(LENGTH keys n)
math(EXPR n ${n}*2)
set(i 0)
foreach(key ${keys})
math(EXPR i ${i}+1)
if(${${key}_COPYFLAG})
message(STATUS "${i}/${n}: copying '${${key}_RESOLVED_ITEM}'")
else(${${key}_COPYFLAG})
message(STATUS "${i}/${n}: *NOT* copying '${${key}_RESOLVED_ITEM}'")
endif(${${key}_COPYFLAG})
# Modify the embedded flag to use rpath. Assumes all frameworks and
# libraries were copied to <bundle>/Contents/Frameworks
if (APPLE AND NOT "${${key}_EMBEDDED_ITEM}" STREQUAL "" AND
"${${key}_ITEM}" MATCHES "[^/]+(\\.framework/|\\.dylib$)")
file(RELATIVE_PATH irelpath "${bundle}/Contents/Frameworks" "${${key}_RESOLVED_EMBEDDED_ITEM}")
# Check if the item is in fact in the "Frameworks" directory
string(SUBSTRING "${irelpath}" 0 3 irelpath_start)
if (NOT "${irelpath_start}" STREQUAL "../")
# Replace the embedded key for one using rpath
set (${key}_EMBEDDED_ITEM "@rpath/${irelpath}")
# Flag this key as using RPATH
set (${key}_EMBEDDED_RPATH 1)
set (${key}_RPATH_DIR "${bundle}/Contents/Frameworks")
endif ()
endif ()
set(show_status 0)
if(show_status)
message(STATUS "key='${key}'")
message(STATUS "item='${${key}_ITEM}'")
message(STATUS "resolved_item='${${key}_RESOLVED_ITEM}'")
message(STATUS "default_embedded_path='${${key}_DEFAULT_EMBEDDED_PATH}'")
message(STATUS "embedded_item='${${key}_EMBEDDED_ITEM}'")
message(STATUS "resolved_embedded_item='${${key}_RESOLVED_EMBEDDED_ITEM}'")
message(STATUS "copyflag='${${key}_COPYFLAG}'")
message(STATUS "embedded_rpath='${${key}_EMBEDDED_RPATH}'")
message(STATUS "rpath_dir='${${key}_RPATH_DIR}'")
message(STATUS "")
endif(show_status)
if(${${key}_COPYFLAG})
set(item "${${key}_ITEM}")
if(item MATCHES "[^/]+\\.framework/")
copy_resolved_framework_into_bundle("${${key}_RESOLVED_ITEM}"
"${${key}_RESOLVED_EMBEDDED_ITEM}")
else()
copy_resolved_item_into_bundle("${${key}_RESOLVED_ITEM}"
"${${key}_RESOLVED_EMBEDDED_ITEM}")
endif()
endif(${${key}_COPYFLAG})
endforeach(key)
message(STATUS "mts_fixup_bundle: fixing...")
foreach(key ${keys})
math(EXPR i ${i}+1)
if(APPLE)
message(STATUS "${i}/${n}: fixing up '${${key}_RESOLVED_EMBEDDED_ITEM}'")
mts_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 "mts_fixup_bundle: cleaning up...")
mts_clear_bundle_keys(keys)
message(STATUS "mts_fixup_bundle: verifying...")
verify_app("${app}")
else(valid)
message(SEND_ERROR "error: mts_fixup_bundle: not a valid bundle")
endif(valid)
message(STATUS "mts_fixup_bundle: done")
endfunction(mts_fixup_bundle)