X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=library-bundler;h=b08b9c89c0e2b71cf7dbddb7c8ff89bcbbe5c0b1;hp=c1ba97d9b2ec421925ced97324bb4fb61ea0be99;hb=HEAD;hpb=de440edcca995a46bf38bc769be295416c2c907c diff --git a/library-bundler b/library-bundler index c1ba97d9..e9a7ccdd 100755 --- a/library-bundler +++ b/library-bundler @@ -1,7 +1,34 @@ #! /usr/bin/env bash +set -e + +export LANG='C.UTF-8' +export LANGUAGE="${LANG}" + +_sed () { + case "${system_name}" in + 'macos'|'freebsd') + gsed "${@}" + ;; + *) + sed "${@}" + ;; + esac +} + +_cp () { + case "${system_name}" in + 'macos'|'freebsd') + gcp -R --preserve=timestamps -H -L "${1}" "${2}" + ;; + *) + cp -R --preserve=timestamps -H -L "${1}" "${2}" + ;; + esac +} + Common::noOp () { - printf '' + true } Common::getPath () { @@ -12,7 +39,7 @@ Common::getPath () { if [ "${file_path}" = '-' ] then tr '\n' '\0' \ - | xargs -0 -n1 -P1 -I{} \ + | xargs -0 -P1 -I{} \ cygpath --unix '{}' else cygpath --unix "${file_path}" @@ -25,20 +52,35 @@ 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 () { case "${system_name}" in - 'linux') + 'linux'|'freebsd') + # - always bundle built-in libraries # - always rely on up-to-date x11 and gl libraries, bundling them will break on future distros # - gtk is not easily bundlable on linux because it looks for harcoded system path to optional # shared libraries like image codecs, theme engines, sound notification system, etc. @@ -47,11 +89,35 @@ Multi::excludeLdd () { # which is likely to pull gtk itself, x11 and gl dependencies # - old fontconfig does not work correctly if newer fontconfig configuration is installed # - if gtk and fontconfig is installed, pango and freetype are - egrep -v '/libc\.|/libstdc\+\+\.|/libdl\.|/libm\.|/libX|/libxcb|/libGL|/libICE\.|/libSM\.|/libpthread\.' \ - | egrep -v '/libatk|/libgdk|/libgtk|/libgio|/libglib|/libgmodule|/libgobject|/libcairo|/libpango|/libfontconfig|/libfreetype' + local ldd_line + while read ldd_line + do + if echo "${ldd_line}" | egrep '/builtins/' + then + echo "${ldd_line}" + elif echo "${ldd_line}" \ + | egrep -q '/libc\.|/libstdc\+\+\.|/libdl\.|/libm\.|/libX|/libxcb|/libGL|/libICE\.|/libSM\.|/libpthread\.' + then + Common::noOp + elif echo "${ldd_line}" \ + | egrep -q '/libatk|/libgdk|/libgtk|/libgio|/libglib|/libgmodule|/libgobject|/libcairo|/libpango|/libfontconfig|/libfreetype' + then + Common::noOp + # FreeBSD specific + elif echo "${ldd_line}" \ + | egrep -q '/libc\+\+|/libgxxrt' + then + Common::noOp + else + echo "${ldd_line}" + fi + done ;; 'windows') - egrep -i '\.dll => [A-Z]:\\msys64\\' \ + egrep -i '\.dll => [A-Z]:\\msys64\\' + ;; + 'macos') + egrep -v '^\t/System/|^\t/usr/lib/' ;; esac } @@ -67,34 +133,42 @@ Multi::printLdd () { local exe_file="${1}" case "${system_name}" in - 'linux') + 'linux'|'freebsd') ldd "${exe_file}" ;; 'windows') ntldd --recursive "${exe_file}" ;; + 'macos') + otool -L "${exe_file}" esac } Multi::getGtkThemeName () { case "${system_name}" in - 'linux') + 'linux'|'freebsd') echo 'Adwaita' ;; 'windows') echo 'MS-Windows' ;; + *) + echo 'Raleigh' + ;; esac } Multi::getGtkLibName () { case "${system_name}" in - 'linux') + 'linux'|'freebsd') echo 'libgtk-x11-2.0.so.0' ;; 'windows') echo 'libgtk-win32-2.0-0.dll' ;; + 'macos') + echo 'libgtk-quartz-2.0.0.dylib' + ;; esac } @@ -103,12 +177,14 @@ Multi::getRootPrefix () { case "${system_name}" in 'linux') - echo "${lib_file}" \ - | cut -f2 -d'/' + echo 'usr' + ;; + 'freebsd'|'macos') + echo 'usr/local' ;; 'windows') basename "${lib_file}" \ - | xargs -n1 -P 1 which \ + | xargs -n1 -P1 which \ | cut -f2 -d'/' ;; esac @@ -118,13 +194,16 @@ Multi::getLibPrefix () { local lib_file="${1}" case "${system_name}" in - 'linux') + 'linux'|'freebsd') dirname "${lib_file}" \ | cut -f3- -d'/' ;; 'windows') echo 'lib' ;; + 'macos') + echo 'lib' + ;; esac } @@ -132,15 +211,30 @@ 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'|'freebsd'|'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 + etc/fonts + share/themes/${gtk_theme_name}/gtk-2.0 + share/fontconfig + share/icons/hicolor + share/locale + ${lib_prefix}/gdk-pixbuf-2.0 + ${lib_prefix}/gtk-2.0 + EOF + ;; + esac case "${system_name}" in - 'linux') + 'linux'|'freebsd') cat <<-EOF ${lib_prefix}/libatk-bridge-2.0.so.0 ${lib_prefix}/libcanberra-0.30 @@ -151,8 +245,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}")" @@ -165,14 +280,18 @@ 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}")" - cp -H -r --preserve=timestamps \ - "/${root_prefix}/${component_dir}" \ + _cp \ + "${real_component_dir}" \ "${bundle_dir}/${bundle_component_dir}" + + Multi::rewriteLoadersCache "${bundle_dir}/${bundle_component_dir}" fi done fi @@ -180,11 +299,18 @@ Multi::bundleGtkDepsFromFile () { Multi::bundleLibFromFile () { local exe_file="${1}" + local lib_file Multi::printLdd "${exe_file}" \ | Multi::filterLib \ | while read lib_file do + if [ "${lib_file}" = 'not found' ] + then + printf 'ERROR: library not found while bundling %s (but link worked)\n' "${exe_file}" >&2 + Multi::printLdd "${exe_file}" | grep 'not found' + exit 1 + fi lib_basename="$(basename "${lib_file}")" if [ -f "${lib_dir}/${lib_basename}" ] @@ -192,29 +318,39 @@ Multi::bundleLibFromFile () { continue fi - cp --preserve=timestamps \ + _cp \ "${lib_file}" \ "${lib_dir}/${lib_basename}" Multi::bundleGtkDepsFromFile "${lib_file}" + + case "${system_name}" in + 'macos') + Multi::bundleLibFromFile "${lib_file}" + ;; + esac done } Multi::cleanUp () { + # Remove from bundle things that useless to be distributed, + # like headers or static libraries, also remove + # empty directories. 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 {} \; + -empty \ + -exec rmdir {} \; } Linux::getRpath () { @@ -224,7 +360,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' @@ -253,6 +389,7 @@ Linux::patchExe () { local exe_file="${1}" local linux_rpath_string=$"$(Linux::getRpath "${exe_file}")" + chmod u+w,go-w "${exe_file}" patchelf --set-rpath "${linux_rpath_string}" "${exe_file}" } @@ -266,6 +403,39 @@ Linux::patchLib () { | while read exe_file do Linux::patchExe "${exe_file}" + chmod ugo-x "${exe_file}" + 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 } @@ -278,7 +448,7 @@ Windows::listLibForManifest () { -name '*.dll' \ -exec basename {} \; \ | tr '\n' '\0' \ - | xargs -0 -n1 -P1 -I{} \ + | xargs -0 -P1 -I{} \ printf ' \n' } @@ -295,7 +465,11 @@ Windows::writeManifest () { system_name="${1}"; shift bundle_dir="${1}"; shift -exe_file="${1}"; shift + +if ! [ -z "${1}" ] +then + exe_file="${1}"; shift +fi bundle_dir="$(Common::getPath "${bundle_dir}")" registry_dir="${bundle_dir}/registry" @@ -308,24 +482,28 @@ 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 ;; - 'linux') + 'linux'|'freebsd') exe_action='Linux::patchExe' lib_action='Linux::patchLib' ;; '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