From 09b9220034102b47a69efcb653b169c56e26f5f3 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 22 Dec 2020 18:37:36 +0100 Subject: [PATCH] bundle: bundle macos binaries (no .app file done yet) --- CMakeLists.txt | 2 +- bundle/CMakeLists.txt | 4 +- library-bundler | 180 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 164 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d78aa47..05ba8a3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,7 +273,7 @@ if (BUILD_BINARIES) include_directories("${PROJECT_SOURCE_DIR}/include") include_directories("${PROJECT_SOURCE_DIR}/libs") - if (WIN32 OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + if (WIN32 OR APPLE OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(BUNDLING_SUPPORTED ON) endif() diff --git a/bundle/CMakeLists.txt b/bundle/CMakeLists.txt index 1ab7609c..73750b70 100644 --- a/bundle/CMakeLists.txt +++ b/bundle/CMakeLists.txt @@ -1,6 +1,8 @@ if (WIN32) set(BUNDLE_OS_NAME "windows") - set(BUNDLE_SHARE ON) + set(BUNDLE_SHARE ON) +elseif (APPLE) + set(BUNDLE_OS_NAME "macos") elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(BUNDLE_OS_NAME "linux") else () diff --git a/library-bundler b/library-bundler index 1a3389c9..9a48a626 100755 --- a/library-bundler +++ b/library-bundler @@ -5,6 +5,28 @@ set -e export LANG='C.UTF-8' export LANGUAGE="${LANG}" +_sed () { + case "${system_name}" in + 'macos') + gsed "${@}" + ;; + *) + sed "${@}" + ;; + esac +} + +_cpr () { + case "${system_name}" in + 'macos') + cp -R ${@} + ;; + *) + cp -R --preserve=timestamps ${@} + ;; + esac +} + Common::noOp () { true } @@ -30,15 +52,29 @@ Common::getPath () { printf '%s\n' "${file_path}" fi fi \ - | sed -e 's|/*$||' + | _sed -e 's|/*$||' } Common::grepLdd () { - egrep ' => ' + case "${system_name}" in + 'macos') + egrep '^\t/' + ;; + *) + egrep ' => ' + ;; + esac } Common::stripLdd () { - sed -e 's/ (0x[0-9a-f]*)$//;s/^.* => //' + case "${system_name}" in + 'macos') + _sed -e 's/^\t\(.*\) (compatibility version .*/\1/' + ;; + *) + _sed -e 's/ (0x[0-9a-f]*)$//;s/^.* => //' + ;; + esac } Multi::excludeLdd () { @@ -75,6 +111,9 @@ Multi::excludeLdd () { 'windows') egrep -i '\.dll => [A-Z]:\\msys64\\' ;; + 'macos') + egrep -v '^\t/System/|^\t/usr/lib/' + ;; esac } @@ -95,6 +134,8 @@ Multi::printLdd () { 'windows') ntldd --recursive "${exe_file}" ;; + 'macos') + otool -L "${exe_file}" esac } @@ -106,6 +147,9 @@ Multi::getGtkThemeName () { 'windows') echo 'MS-Windows' ;; + *) + echo 'Default' + ;; esac } @@ -117,6 +161,9 @@ Multi::getGtkLibName () { 'windows') echo 'libgtk-win32-2.0-0.dll' ;; + 'macos') + echo 'libgtk-quartz-2.0.0.dylib' + ;; esac } @@ -133,6 +180,8 @@ Multi::getRootPrefix () { | xargs -n1 -P1 which \ | cut -f2 -d'/' ;; + 'macos') + echo 'usr/local' esac } @@ -147,6 +196,9 @@ Multi::getLibPrefix () { 'windows') echo 'lib' ;; + 'macos') + echo 'lib' + ;; esac } @@ -154,12 +206,24 @@ Multi::getGtkDeps () { local lib_prefix="${1}" local gtk_theme_name="${2}" - cat <<-EOF - share/themes/${gtk_theme_name}/gtk-2.0 - share/icons/hicolor - ${lib_prefix}/gdk-pixbuf-2.0 - ${lib_prefix}/gtk-2.0 - EOF + case "${system_name}" in + 'linux'|'windows') + cat <<-EOF + share/themes/${gtk_theme_name}/gtk-2.0 + share/icons/hicolor + ${lib_prefix}/gdk-pixbuf-2.0 + ${lib_prefix}/gtk-2.0 + EOF + ;; + 'macos') + cat <<-EOF + share/icons/hicolor + share/locale + ${lib_prefix}/gdk-pixbuf-2.0 + ${lib_prefix}/gtk-2.0 + EOF + ;; + esac case "${system_name}" in 'linux') @@ -173,8 +237,29 @@ Multi::getGtkDeps () { esac } +Multi::rewriteLoadersCache () { + local bundle_component_path="${1}" + local cache_file + + find "${bundle_component_path}" \ + -type f \ + \( \ + -name 'loaders.cache' \ + -o -name 'immodules.cache' \ + \) \ + | while read cache_file + do + _sed \ + -e 's|^"/[^"]*/lib/|"lib/|;s| "/[^"]*/share/| "share/|;/^# ModulesPath = /d;/^# Created by /d;/^#$/d' \ + -i "${cache_file}" + done +} + Multi::bundleGtkDepsFromFile () { local lib_file="${1}" + local component_dir + local real_component_dir + local bundle_component_dir lib_basename="$(basename "${lib_file}")" @@ -187,14 +272,22 @@ Multi::bundleGtkDepsFromFile () { for component_dir in $(Multi::getGtkDeps "${lib_prefix}" "${gtk_theme_name}") do - bundle_component_dir="$(echo "${component_dir}" | sed -e 's|^'"${lib_prefix}"'|lib|')" + bundle_component_dir="$(echo "${component_dir}" | _sed -e 's|^'"${lib_prefix}"'|lib|')" if ! [ -e "${bundle_dir}/${bundle_component_dir}" ] then - mkdir --parents "${bundle_dir}/$(dirname "${bundle_component_dir}")" + real_component_dir="$(realpath "/${root_prefix}/${component_dir}")" + + mkdir -p "${bundle_dir}/$(dirname "${bundle_component_dir}")" + + _cpr -H -L \ + "${real_component_dir}" \ + "${bundle_dir}/${bundle_component_dir}" - cp -H -r --preserve=timestamps \ - "/${root_prefix}/${component_dir}" \ + touch -r \ + "/${real_component_dir}" \ "${bundle_dir}/${bundle_component_dir}" + + Multi::rewriteLoadersCache "${bundle_dir}/${bundle_component_dir}" fi done fi @@ -221,11 +314,21 @@ Multi::bundleLibFromFile () { continue fi - cp --preserve=timestamps \ + cp -H \ + "${lib_file}" \ + "${lib_dir}/${lib_basename}" + + touch -r \ "${lib_file}" \ "${lib_dir}/${lib_basename}" Multi::bundleGtkDepsFromFile "${lib_file}" + + case "${system_name}" in + 'macos') + Multi::bundleLibFromFile "${lib_file}" + ;; + esac done } @@ -233,17 +336,18 @@ Multi::cleanUp () { find "${bundle_dir}/lib" \ -type f \ -name '*.a' \ - -exec rm {} \; + -exec rm -f {} \; find "${bundle_dir}/lib" \ -type f \ -name '*.h' \ - -exec rm {} \; + -exec rm -f {} \; find "${bundle_dir}/lib" \ -depth \ -type d \ - -exec rmdir --ignore-fail-on-non-empty {} \; + -exec rmdir {} \; \ + || true } Linux::getRpath () { @@ -253,7 +357,7 @@ Linux::getRpath () { local path_start="$(printf '%s' "${bundle_dir}" | wc -c)" path_start="$((${path_start} + 1))" - local exe_subdir="$(echo "${exe_dir}" | cut -c "${path_start}-" | sed -e 's|//*|/|;s|^/||')" + local exe_subdir="$(echo "${exe_dir}" | cut -c "${path_start}-" | _sed -e 's|//*|/|;s|^/||')" local rpath_origin='$ORIGIN' @@ -298,6 +402,38 @@ Linux::patchLib () { done } +Darwin::patchExe () { + local exe_file="${1}" + + Multi::printLdd "${exe_file}" \ + | Multi::filterLib \ + | while read lib_file + do + new_path="$(echo "${lib_file}" | _sed -e 's|^/.*/lib/|@executable_path/lib/|')" + id_name="$(echo "${lib_file}" | _sed -e 's|.*/||g')" + chmod u+w,go-w "${exe_file}" + install_name_tool -change "${lib_file}" "${new_path}" "${exe_file}" + install_name_tool -id "${id_name}" "${exe_file}" + done +} + +Darwin::patchLib () { + local lib_dir="${1}" + local exe_file + + find "${lib_dir}" \ + -type f \ + \( \ + -name '*.dylib' \ + -o -name '*.so' \ + \) \ + | while read exe_file + do + Darwin::patchExe "${exe_file}" + chmod ugo-x "${exe_file}" + done +} + Windows::listLibForManifest () { local lib_dir="${1}" @@ -341,7 +477,7 @@ lib_action='Common::noOp' case "${system_name}" in 'register') - mkdir --parents "${registry_dir}" + mkdir -p "${registry_dir}" Common::getPath "${exe_file}" > "${registry_dir}/$(uuidgen)" exit ;; @@ -352,13 +488,17 @@ case "${system_name}" in 'windows') lib_action='Windows::writeManifest' ;; + 'macos') + exe_action='Darwin::patchExe' + lib_action='Darwin::patchLib' + ;; *) printf 'ERROR: unsupported system: %s\n' "${system_name}" >&2 exit 1 ;; esac -mkdir --parents "${lib_dir}" +mkdir -p "${lib_dir}" if [ -d "${registry_dir}" ] then -- 2.39.2