Packaging: macOS relocation of libraries without children
* macOS install path must be <bundle>/MacOS in order for QLibrary to find qt.conf to set the correct bundle paths * Refactored to add an explicit graph traversal to set the dynamic loader id to handle the case where a bundled resource does not have any children * Fixed the case where rpaths were not removed from libraries without children * Improved diagnostics when bundling fail to finds a dependent library in the search path Mantis: #0002886 Refs: #535
This commit is contained in:
parent
402aef2e63
commit
21080af2a7
|
@ -144,7 +144,7 @@ before_install:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export CMAKE_ARGS="${CMAKE_OPTS} -DFREECAD_USE_EXTERNAL_KDL=ON -DFREECAD_USE_EXTERNAL_PIVY=ON -DFREECAD_CREATE_MAC_APP=ON"
|
export CMAKE_ARGS="${CMAKE_OPTS} -DFREECAD_USE_EXTERNAL_KDL=ON -DFREECAD_USE_EXTERNAL_PIVY=ON -DFREECAD_CREATE_MAC_APP=ON"
|
||||||
export INSTALLED_APP_PATH="/usr/local/FreeCAD.app/Contents/bin/FreeCAD"
|
export INSTALLED_APP_PATH="/usr/local/FreeCAD.app/Contents/MacOS/FreeCAD"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
|
|
|
@ -196,7 +196,7 @@ if(APPLE)
|
||||||
${CMAKE_INSTALL_PREFIX}/${PACKAGE_NAME}.app/Contents)
|
${CMAKE_INSTALL_PREFIX}/${PACKAGE_NAME}.app/Contents)
|
||||||
set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib)
|
set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib)
|
||||||
endif(FREECAD_CREATE_MAC_APP)
|
endif(FREECAD_CREATE_MAC_APP)
|
||||||
set(CMAKE_MACOSX_RPATH 1)
|
set(CMAKE_MACOSX_RPATH TRUE)
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
OPTION(BUILD_FEM "Build the FreeCAD FEM module" ON)
|
OPTION(BUILD_FEM "Build the FreeCAD FEM module" ON)
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../bin/FreeCAD
|
|
1
src/MacAppBundle/FreeCAD.app/Contents/bin/FreeCAD
Symbolic link
1
src/MacAppBundle/FreeCAD.app/Contents/bin/FreeCAD
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../MacOS/FreeCAD
|
|
@ -49,11 +49,16 @@ if(BUILD_GUI)
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION bin
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
)
|
)
|
||||||
else(WIN32)
|
elseif(APPLE)
|
||||||
|
INSTALL(TARGETS FreeCADMain
|
||||||
|
RUNTIME DESTINATION MacOS
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
else()
|
||||||
INSTALL(TARGETS FreeCADMain
|
INSTALL(TARGETS FreeCADMain
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION bin
|
||||||
)
|
)
|
||||||
endif(WIN32)
|
endif()
|
||||||
endif(BUILD_GUI)
|
endif(BUILD_GUI)
|
||||||
######################## FreeCADMainCmd ########################
|
######################## FreeCADMainCmd ########################
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,8 @@ def create_dep_nodes(install_names, search_paths):
|
||||||
path = install_path
|
path = install_path
|
||||||
|
|
||||||
if not path:
|
if not path:
|
||||||
raise LibraryNotFound(lib_name + "not found in given paths")
|
logging.error("Unable to find LC_DYLD_LOAD entry: " + lib)
|
||||||
|
raise LibraryNotFound(lib_name + " not found in given search paths")
|
||||||
|
|
||||||
nodes.append(Node(lib_name, path))
|
nodes.append(Node(lib_name, path))
|
||||||
|
|
||||||
|
@ -190,9 +191,9 @@ def should_visit(prefix, path_filters, path):
|
||||||
s_filter = pf.strip('/').split('/')
|
s_filter = pf.strip('/').split('/')
|
||||||
length = len(s_filter)
|
length = len(s_filter)
|
||||||
matched = 0
|
matched = 0
|
||||||
for i in range(len(s_path)):
|
for i in range(len(s_path)):
|
||||||
if s_path[i] == s_filter[i]:
|
if s_path[i] == s_filter[i]:
|
||||||
matched += 1
|
matched += 1
|
||||||
if matched == length or matched == len(s_path):
|
if matched == length or matched == len(s_path):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -234,7 +235,12 @@ def build_deps_graph(graph, bundle_path, dirs_filter=None, search_paths=[]):
|
||||||
if not graph.in_graph(node):
|
if not graph.in_graph(node):
|
||||||
graph.add_node(node)
|
graph.add_node(node)
|
||||||
|
|
||||||
deps = create_dep_nodes(list_install_names(k2), s_paths)
|
try:
|
||||||
|
deps = create_dep_nodes(list_install_names(k2), s_paths)
|
||||||
|
except:
|
||||||
|
logging.error("Failed to resolve dependency in " + k2)
|
||||||
|
raise
|
||||||
|
|
||||||
for d in deps:
|
for d in deps:
|
||||||
if d.name not in node.children:
|
if d.name not in node.children:
|
||||||
node.children.append(d.name)
|
node.children.append(d.name)
|
||||||
|
@ -256,15 +262,12 @@ def copy_into_bundle(graph, node, bundle_path):
|
||||||
target = os.path.join(bundle_path, "lib", node.name)
|
target = os.path.join(bundle_path, "lib", node.name)
|
||||||
logging.info("Bundling {}".format(source))
|
logging.info("Bundling {}".format(source))
|
||||||
|
|
||||||
check_output([ "cp", "-L", source, target ])
|
check_call([ "cp", "-L", source, target ])
|
||||||
|
|
||||||
node.path = os.path.dirname(target)
|
node.path = os.path.dirname(target)
|
||||||
|
|
||||||
#fix permissions
|
#fix permissions
|
||||||
check_output([ "chmod", "a+w", target ])
|
check_call([ "chmod", "a+w", target ])
|
||||||
|
|
||||||
#Change the loader ID_DYLIB to a bundle-local name (i.e. non-absolute)
|
|
||||||
check_output([ "install_name_tool", "-id", node.name, target ])
|
|
||||||
|
|
||||||
def get_rpaths(library):
|
def get_rpaths(library):
|
||||||
"Returns a list of rpaths specified within library"
|
"Returns a list of rpaths specified within library"
|
||||||
|
@ -290,47 +293,53 @@ def get_rpaths(library):
|
||||||
return rpaths
|
return rpaths
|
||||||
|
|
||||||
def add_rpaths(graph, node, bundle_path):
|
def add_rpaths(graph, node, bundle_path):
|
||||||
if node.children:
|
lib = os.path.join(node.path, node.name)
|
||||||
lib = os.path.join(node.path, node.name)
|
|
||||||
if in_bundle(lib, bundle_path):
|
|
||||||
install_names = list_install_names(lib)
|
|
||||||
rpaths = []
|
|
||||||
|
|
||||||
logging.debug(lib)
|
if in_bundle(lib, bundle_path):
|
||||||
for install_name in install_names:
|
logging.debug(lib)
|
||||||
name = os.path.basename(install_name)
|
|
||||||
#change install names to use rpaths
|
|
||||||
logging.debug(" ~ " + name + " => @rpath/" + name)
|
|
||||||
check_call([ "install_name_tool", "-change",
|
|
||||||
install_name, "@rpath/" + name, lib ])
|
|
||||||
|
|
||||||
dep_node = node.children[node.children.index(name)]
|
# Remove existing rpaths that could take precedence
|
||||||
rel_path = os.path.relpath(graph.get_node(dep_node).path,
|
for rpath in get_rpaths(lib):
|
||||||
|
logging.debug(" - rpath: " + rpath)
|
||||||
|
check_call(["install_name_tool", "-delete_rpath", rpath, lib])
|
||||||
|
|
||||||
|
if node.children:
|
||||||
|
install_names = list_install_names(lib)
|
||||||
|
rpaths = []
|
||||||
|
|
||||||
|
|
||||||
|
for install_name in install_names:
|
||||||
|
name = os.path.basename(install_name)
|
||||||
|
#change install names to use rpaths
|
||||||
|
logging.debug(" ~ rpath: " + name + " => @rpath/" + name)
|
||||||
|
check_call([ "install_name_tool", "-change",
|
||||||
|
install_name, "@rpath/" + name, lib ])
|
||||||
|
|
||||||
|
dep_node = node.children[node.children.index(name)]
|
||||||
|
rel_path = os.path.relpath(graph.get_node(dep_node).path,
|
||||||
node.path)
|
node.path)
|
||||||
rpath = ""
|
rpath = ""
|
||||||
if rel_path == ".":
|
if rel_path == ".":
|
||||||
rpath = "@loader_path/"
|
rpath = "@loader_path/"
|
||||||
else:
|
else:
|
||||||
rpath = "@loader_path/" + rel_path + "/"
|
rpath = "@loader_path/" + rel_path + "/"
|
||||||
if rpath not in rpaths:
|
if rpath not in rpaths:
|
||||||
rpaths.append(rpath)
|
rpaths.append(rpath)
|
||||||
|
|
||||||
for rpath in get_rpaths(lib):
|
for rpath in rpaths:
|
||||||
# Remove existing rpaths because the libraries copied into the
|
# Ensure that lib has rpath set
|
||||||
# bundle will point to a location outside the bundle
|
if not rpath in get_rpaths(lib):
|
||||||
logging.debug(" - rpath: " + rpath)
|
logging.debug(" + rpath: " + rpath)
|
||||||
check_output(["install_name_tool", "-delete_rpath", rpath, lib])
|
check_call([ "install_name_tool", "-add_rpath", rpath, lib ])
|
||||||
|
|
||||||
for rpath in rpaths:
|
def change_libid(graph, node, bundle_path):
|
||||||
# Ensure that lib has rpath set
|
lib = os.path.join(node.path, node.name)
|
||||||
if not rpath in get_rpaths(lib):
|
|
||||||
logging.debug(" + rpath: " + rpath + " to library " + lib)
|
|
||||||
check_output([ "install_name_tool",
|
|
||||||
"-add_rpath", rpath, lib ])
|
|
||||||
|
|
||||||
#Change the loader ID_DYLIB to a bundle-local name (i.e. non-absolute)
|
logging.debug(lib)
|
||||||
logging.debug(" ~ id: " + node.name)
|
|
||||||
check_output([ "install_name_tool", "-id", node.name, lib ])
|
if in_bundle(lib, bundle_path):
|
||||||
|
logging.debug(" ~ id: " + node.name)
|
||||||
|
check_call([ "install_name_tool", "-id", node.name, lib ])
|
||||||
|
|
||||||
def print_child(graph, node, path):
|
def print_child(graph, node, path):
|
||||||
logging.debug(" >" + str(node))
|
logging.debug(" >" + str(node))
|
||||||
|
@ -354,7 +363,7 @@ def main():
|
||||||
|
|
||||||
#change to level to logging.DEBUG for diagnostic messages
|
#change to level to logging.DEBUG for diagnostic messages
|
||||||
logging.basicConfig(stream=sys.stdout, level=logging.INFO,
|
logging.basicConfig(stream=sys.stdout, level=logging.INFO,
|
||||||
format="%(asctime)s %(levelname)s: %(message)s" )
|
format="-- %(message)s" )
|
||||||
|
|
||||||
logging.info("Analyzing bundle dependencies...")
|
logging.info("Analyzing bundle dependencies...")
|
||||||
build_deps_graph(graph, bundle_path, dir_filter, search_paths)
|
build_deps_graph(graph, bundle_path, dir_filter, search_paths)
|
||||||
|
@ -368,6 +377,9 @@ def main():
|
||||||
logging.info("Updating dynamic loader paths...")
|
logging.info("Updating dynamic loader paths...")
|
||||||
graph.visit(add_rpaths, [bundle_path])
|
graph.visit(add_rpaths, [bundle_path])
|
||||||
|
|
||||||
|
logging.info("Setting bundled library IDs...")
|
||||||
|
graph.visit(change_libid, [bundle_path])
|
||||||
|
|
||||||
logging.info("Done.")
|
logging.info("Done.")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in New Issue
Block a user