Jc,
I thought I'd share a script I've made as a replacement for BundleUtilities.
It is designed ground up with rpaths in mind, whereas BundleUtilities was
designed with @executable_path as the basis. This also fits with Windows where
there are no rpaths, but there is a limited set of implicit loader paths
relative to binaries. The caller can also pass in additional paths if
additional paths will exist at runtime via LD_LIBRARY_PATH, PATH, rpaths
across dlopen(), etc... that the binaries themselves are not aware of.
It also supports any layout you want instead of only a Bundle layout, or a
layout where shared libraries exist next to the main executable. For example,
on Mac/Linux there is the typical bin/ and lib/ separation.
Of course, this needs polishing before it can be considered for addition to
CMake.
Clint
On Thursday, May 16, 2013 07:57:01 PM Jean-Christophe Fillion-Robin wrote:
> Hi Clinton,
>
> This is to create bundle that use @rpath. Particularly useful when you want
> to support plugin.
>
> Let me know what you think, I just updated my the toy project.
> Other list of changes have been pushed into CMake stage:
> http://cmake.org/gitweb?p=stage/cmake.git;a=shortlog;h=refs/heads/tweak-bund
> leutilities-for-rpath
>
> Thanks
> Jc
>
> On Thu, May 2, 2013 at 11:51 AM, Clinton Stimpson
<[email protected]>wrote:
> > On Wednesday, April 24, 2013 06:45:11 PM Jean-Christophe Fillion-Robin
> >
> > wrote:
> > > Hi Folks,
> > >
> > > I have been working on improving BundleUtilities and GetPrerequisites
> > > module so that it can be used to easily fixup a MacOSX bundle using
> >
> > @rpath.
> >
> > > The set of changes I would like to propose is here:
> > https://github.com/jcfr/CMakeBundleUtilitiesExample/compare/f7a594ffba72b8
> > cb>
> > > 83df9a166d7887bedc49f38b...75fa538
> > >
> > > To try out the change, you could build the small project I created for
> >
> > that
> >
> > > purpose: https://github.com/jcfr/CMakeBundleUtilitiesExample#readme
> > >
> > > Let me know what you think.
> > >
> > > Thanks
> > > Jc
> >
> > Is it to help make the same @executable_path based bundles but support
> > copying
> > in libraries and frameworks that used @rpath and change them over to be
> > @executable_path based?
> >
> > Or is this to help create bundles that result in the use of @rpath?
> >
> > --
> > Clinton Stimpson
> > Elemental Technologies, Inc
> > Computational Simulation Software, LLC
> > www.csimsoft.com
> > --
> >
> > Powered by www.kitware.com
> >
> > Visit other Kitware open-source projects at
> > http://www.kitware.com/opensource/opensource.html
> >
> > Please keep messages on-topic and check the CMake FAQ at:
> > http://www.cmake.org/Wiki/CMake_FAQ
> >
> > Follow this link to subscribe/unsubscribe:
> > http://www.cmake.org/mailman/listinfo/cmake
--
Clinton Stimpson
Elemental Technologies, Inc
Computational Simulation Software, LLC
www.csimsoft.com
# silence anything about debug msvc system dlls and make sure they are identified as system libraries
function(gp_resolve_item_override context item exepath dirs resolved_item resolved)
if(item MATCHES "^[Mm][Ss][Vv][Cc][^/]+[Dd].dll")
set(${resolved} 1 PARENT_SCOPE)
set(${resolved_item} "$ENV{windir}/system32/${item}" PARENT_SCOPE)
endif()
endfunction()
include(GetPrerequisites)
include(CMakeParseArguments)
# Get rpaths for a binary.
# On windows, the implicit loader directories are returned.
# On other platforms, the embedded rpaths are returned.
function(get_rpaths binary rpaths run_paths)
get_filename_component(binary_dir "${binary}" PATH)
unset(myrpaths)
unset(myrunpaths)
if(WIN32)
set(myrpaths "${binary_dir}")
elseif(APPLE)
execute_process(COMMAND otool -l "${binary}"
COMMAND grep -A2 LC_RPATH
COMMAND grep path
OUTPUT_VARIABLE paths)
string(REPLACE "\n" ";" paths "${paths}")
foreach(str ${paths})
string(REGEX REPLACE " path (.*) \\(offset.*" "\\1" rpath "${str}")
string(STRIP "${rpath}" rpath)
string(REPLACE "@loader_path" "${binary_dir}" rpath "${rpath}")
list(APPEND myrpaths "${rpath}")
endforeach()
else()
execute_process(COMMAND objdump -p "${binary}"
COMMAND grep RPATH
OUTPUT_VARIABLE paths)
execute_process(COMMAND objdump -p "${binary}"
COMMAND grep RUNPATH
OUTPUT_VARIABLE paths2)
string(REPLACE "\n" ";" paths "${paths}")
string(REPLACE "\n" ";" paths2 "${paths2}")
foreach(str ${paths})
string(REGEX REPLACE " RPATH[ ]*(.*)" "\\1" rpath "${str}")
string(STRIP "${rpath}" rpath)
string(REPLACE "\$ORIGIN" "${binary_dir}" rpath "${rpath}")
list(APPEND myrpaths "${rpath}")
endforeach()
foreach(str ${paths2})
string(REGEX REPLACE " RUNPATH[ ]*(.*)" "\\1" rpath "${str}")
string(STRIP "${rpath}" rpath)
string(REPLACE "\$ORIGIN" "${binary_dir}" rpath "${rpath}")
list(APPEND myrunpaths "${rpath}")
endforeach()
endif()
set(${rpaths} ${myrpaths} PARENT_SCOPE)
set(${run_paths} ${myrunpaths} PARENT_SCOPE)
endfunction()
# get the name of a prerequisite
# if its a framework, the name foo.framework/.../foo is returned.
function(get_name prereq result)
get_filename_component(name "${prereq}" NAME)
if(prereq MATCHES "[^/]+\\.framework/")
set(fwname "${name}")
string(REGEX REPLACE "^.*(${name}.framework/.*/?${name})" "\\1" name "${prereq}")
endif()
set(${result} "${name}" PARENT_SCOPE)
endfunction()
# copy a prerequisite to a given location
function(copy_prerequisite prereq lib_dir fw_dir result_file)
get_filename_component(name "${prereq}" NAME)
set(is_framework 0)
set(copy_dir "${lib_dir}")
if(prereq MATCHES "[^/]+\\.framework/")
set(fwname "${name}")
string(REGEX REPLACE "^.*(${name}.framework/.*/?${name})" "\\1" name "${prereq}")
string(LENGTH "${name}" name_len)
string(LENGTH "${prereq}" prereq_len)
math(EXPR fwdir_len "${prereq_len} - ${name_len} - 1")
string(SUBSTRING "${prereq}" 0 ${fwdir_len} prereq_fwdir)
set(copy_dir "${fw_dir}")
set(is_framework 1)
endif()
set(result "${copy_dir}/${name}")
if(EXISTS "${result}")
set(${result_file} ${result} PARENT_SCOPE)
return()
endif()
# copy library
# strip it of rpaths it may already have
message(STATUS "copying ${prereq} to ${result}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${prereq}" "${result}")
if(UNIX AND NOT APPLE)
file(RPATH_REMOVE FILE "${result}")
elseif(APPLE)
get_rpaths("${result}" rpaths run_paths)
foreach(rpath ${rpaths})
execute_process(COMMAND install_name_tool -delete_rpath "${rpath}" "${result}")
endforeach()
endif()
# also copy resource directory
if(is_framework AND EXISTS "${prereq_fwdir}/${fwname}.framework/Resources")
message(STATUS "copying ${prereq_fwdir}/${fwname}.framework/Resources to ${fw_dir}/${fwname}.framework/Resources")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory
"${prereq_fwdir}/${fwname}.framework/Resources"
"${fw_dir}/${fwname}.framework/Resources")
endif()
set(${result_file} ${result} PARENT_SCOPE)
endfunction()
# Verify a binary.
# Check that the non-system prerequisites for this binary are included under ROOT.
# Additional RPATHS may be provided if the application has some way of assisting the loader.
# For example, if the binary is a plugin, and its prerequisites are already loaded by
# the executable, it had assistance from the application. In that case, the plugin may
# be missing rpaths, but they can be supplied with RPATHS.
# Usage: verify_binary(binary ROOT ... RPATHS ...
# SEARCH_PATHS ... COPY_LIB_DIR ...
# COPY_FRAMEWORK_DIR ...)
function(verify_binary binary)
if(NOT EXISTS "${binary}" )
message(SEND_ERROR "${binary} does not exist" )
endif()
cmake_parse_arguments(VERIFY "" "ROOT;COPY_LIB_DIR;COPY_FRAMEWORK_DIR"
"RPATHS;SEARCH_PATHS" ${ARGN})
get_name("${binary}" binary_name)
# if we've already processed this one, skip it.
list(FIND verified_binaries "${binary}" find_index)
if(find_index GREATER -1)
message(STATUS "${indent}${binary_name} [visited]")
return()
endif()
set(verified_binaries ${verified_binaries} ${binary})
message(STATUS "${indent}${binary_name}")
set(indent "${indent}-")
# verify that the binary being checked is under the root
if(NOT IS_ABSOLUTE "${binary}" OR NOT EXISTS "${binary}")
message(SEND_ERROR "verify_binary called on non existent ${binary}")
endif()
file(RELATIVE_PATH rel_dir "${VERIFY_ROOT}" "${binary}")
if(IS_ABSOLUTE "${rel_dir}" OR rel_dir MATCHES "^\\.\\.")
message(SEND_ERROR "verify_binary called on ${binary} which doesn't exist under the root ${VERIFY_ROOT}")
endif()
# get the run_paths of this binary
unset(run_paths)
unset(search_paths)
get_rpaths("${binary}" search_paths run_paths)
set(myrpaths ${VERIFY_RPATHS} ${search_paths})
#message("myrpaths=${myrpaths}")
#message("myrunpaths=${myrunpaths}")
# verify that all rpaths point inside the install root
# the caller is responsible for making sure rpaths are correct
foreach(rpath ${myrpaths} ${run_paths})
file(RELATIVE_PATH rel_dir "${VERIFY_ROOT}" "${rpath}")
if(rel_dir MATCHES "^\\.\\.")
message(SEND_ERROR "${binary} has an rpath=${rpath} that is not in the package.")
endif()
endforeach()
# verify the install name / id of the shared library
if(0)
if(APPLE)
execute_process(COMMAND otool -D "${binary}"
OUTPUT_VARIABLE install_id)
string(STRIP "${install_id}" install_id)
string(REPLACE "\n" ";" install_id "${install_id}")
list(LENGTH install_id install_id_length)
if(install_id_length GREATER 1)
list(GET install_id 1 install_id)
else()
unset(install_id)
endif()
if(install_id AND NOT install_id MATCHES "@rpath")
message(SEND_ERROR "${binary} does not have an @rpath install name [\"${install_id}\"].")
endif()
endif()
endif()
# Get the prerequisites for this binary.
# Note: if the application is linked with a non-system library, and it doesn't exists
# where rpaths would locate it, but there is a system one by the same name and the caller
# didn't provide a search path, we get a false positive and it uses the system one
# instead, so we can't really tell if it is supposed to be included in the bundle.
if(NOT "${CMAKE_VERSION}" VERSION_GREATER "2.8.12" AND CMAKE_SYSTEM_NAME MATCHES Linux)
set(resolve_paths "${VERIFY_SEARCH_PATHS};${myrpaths};${run_paths}")
else()
set(resolve_paths "${myrpaths};${run_paths};${VERIFY_SEARCH_PATHS}")
endif()
set(prereqs)
get_prerequisites("${binary}" prereqs 1 0 "" "${resolve_paths}")
foreach(prereq ${prereqs})
gp_resolve_item("" "${prereq}" "" "${resolve_paths}" resolved_prereq)
get_filename_component(resolved_prereq "${resolved_prereq}" ABSOLUTE)
if(NOT IS_ABSOLUTE "${resolved_prereq}" OR NOT EXISTS "${resolved_prereq}")
message(SEND_ERROR "${prereq_type} ${prereq} referenced by ${binary} as ${prereq} can not be resolved. You may need to add some SEARCH_PATHS.")
endif()
# check that the prereq is contained under ROOT
file(RELATIVE_PATH rel_dir "${VERIFY_ROOT}" "${resolved_prereq}")
if(IS_ABSOLUTE "${rel_dir}" OR rel_dir MATCHES "^\\.\\.")
# copy it into the bundle
if(IS_ABSOLUTE "${resolved_prereq}")
#message(STATUS "resolved_prereq1=${resolved_prereq}")
copy_prerequisite("${resolved_prereq}" "${VERIFY_COPY_LIB_DIR}"
"${VERIFY_COPY_FRAMEWORK_DIR}"
resolved_prereq)
#message(STATUS "resolved_prereq2=${resolved_prereq}")
else()
message(SEND_ERROR "${prereq_type} ${prereq} referenced by ${binary} as ${prereq} can not be found.")
endif()
endif()
# If the prerequisites was already an absolute path, convert to @rpath.
# This assumes an rpath is available in the user binary.
# TODO: handle case where the install name was simply the file name without @...
if(APPLE AND IS_ABSOLUTE "${prereq}")
get_name("${prereq}" name)
message(STATUS "install_name_tool -change \"${prereq}\" \"@rpath/${name}\" \"${binary}\"")
execute_process(COMMAND install_name_tool -change "${prereq}" "@rpath/${name}" "${binary}")
endif()
# check recursively
verify_binary("${resolved_prereq}" ROOT "${VERIFY_ROOT}" RPATHS "${myrpaths}"
SEARCH_PATHS "${VERIFY_SEARCH_PATHS}" COPY_LIB_DIR "${VERIFY_COPY_LIB_DIR}"
COPY_FRAMEWORK_DIR "${VERIFY_COPY_FRAMEWORK_DIR}")
endforeach()
set(verified_binaries ${verified_binaries} PARENT_SCOPE)
endfunction()
# Verify prerequisites for specified binaries exist under ROOT.
# It is suggested that executables and plugins be passed in.
# Usage: verify_prerequisites(BINARIES ... ROOT ... RPATHS ...)
# SEARCH_PATHS ... COPY_LIB_DIR ...
# COPY_FRAMEWORK_DIR ...)
function(verify_prerequisites)
cmake_parse_arguments(VERIFY "" "ROOT;COPY_LIB_DIR;COPY_FRAMEWORK_DIR"
"BINARIES;RPATHS;SEARCH_PATHS" ${ARGN})
message(STATUS
"Verifying binary prerequisites...\n
ROOT=${VERIFY_ROOT}\n
RPATHS=${VERIFY_RPATHS}\n
SEARCH_PATHS=${VERIFY_SEARCH_PATHS}\n
COPY_LIB_DIR=${VERIFY_COPY_LIB_DIR}\n
COPY_FRAMEWORK_DIR=${VERIFY_COPY_FRAMEWORK_DIR}")
set(verified_binaries)
if(VERIFY_SEARCH_PATHS AND NOT VERIFY_COPY_LIB_DIR)
message(SEND_ERROR "SEARCH_PATHS was given but no COPY_LIB_DIR for copying libraries.")
endif()
if(VERIFY_SEARCH_PATHS AND NOT VERIFY_COPY_LIB_DIR)
message(SEND_ERROR "SEARCH_PATHS was given but no COPY_FRAMEWORK_DIR for copying frameworks.")
endif()
set(indent "")
foreach(binary ${VERIFY_BINARIES})
verify_binary("${binary}" ROOT ${VERIFY_ROOT} RPATHS ${VERIFY_RPATHS}
SEARCH_PATHS ${VERIFY_SEARCH_PATHS}
COPY_LIB_DIR ${VERIFY_COPY_LIB_DIR}
COPY_FRAMEWORK_DIR ${VERIFY_COPY_FRAMEWORK_DIR})
endforeach()
endfunction()
--
Powered by www.kitware.com
Visit other Kitware open-source projects at
http://www.kitware.com/opensource/opensource.html
Please keep messages on-topic and check the CMake FAQ at:
http://www.cmake.org/Wiki/CMake_FAQ
Follow this link to subscribe/unsubscribe:
http://public.kitware.com/cgi-bin/mailman/listinfo/cmake-developers