#! /usr/bin/env bash
+set -e
+
Common::noOp () {
- printf ''
+ true
}
Common::getPath () {
else
printf '%s\n' "${file_path}"
fi
- fi
+ fi \
+ | sed -e 's|/*$||'
}
Common::grepLdd () {
Multi::excludeLdd () {
case "${system_name}" in
+ 'linux')
+ # - 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.
+ # so expect user to install gtk first
+ # - since we ask user to instal gtk, we can also ask them to install gtkglext,
+ # 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'
+ ;;
'windows')
egrep -i '\.dll => [A-Z]:\\msys64\\' \
;;
local exe_file="${1}"
case "${system_name}" in
+ 'linux')
+ ldd "${exe_file}"
+ ;;
'windows')
ntldd --recursive "${exe_file}"
;;
Multi::getGtkThemeName () {
case "${system_name}" in
+ 'linux')
+ echo 'Adwaita'
+ ;;
'windows')
echo 'MS-Windows'
;;
Multi::getGtkLibName () {
case "${system_name}" in
+ 'linux')
+ echo 'libgtk-x11-2.0.so.0'
+ ;;
'windows')
echo 'libgtk-win32-2.0-0.dll'
;;
local lib_file="${1}"
case "${system_name}" in
+ 'linux')
+ echo "${lib_file}" \
+ | cut -f2 -d'/'
+ ;;
'windows')
basename "${lib_file}" \
- | xargs -n1 -P 1 which \
+ | xargs -n1 -P1 which \
| cut -f2 -d'/'
;;
esac
local lib_file="${1}"
case "${system_name}" in
+ 'linux')
+ dirname "${lib_file}" \
+ | cut -f3- -d'/'
+ ;;
'windows')
echo 'lib'
;;
esac
}
+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')
+ cat <<-EOF
+ ${lib_prefix}/libatk-bridge-2.0.so.0
+ ${lib_prefix}/libcanberra-0.30
+ ${lib_prefix}/libcanberra.so.0
+ ${lib_prefix}/libcanberra-gtk.so.0
+ EOF
+ ;;
+ esac
+}
+
Multi::bundleGtkDepsFromFile () {
local lib_file="${1}"
lib_prefix="$(Multi::getLibPrefix "${lib_file}")"
gtk_theme_name="$(Multi::getGtkThemeName)"
- for component_dir in \
- 'share/themes/'"${gtk_theme_name}"'/gtk-2.0' \
- 'share/icons/hicolor' \
- "${lib_prefix}"'/gdk-pixbuf-2.0' \
- "${lib_prefix}"'/gtk-2.0'
+ for component_dir in $(Multi::getGtkDeps "${lib_prefix}" "${gtk_theme_name}")
do
- if ! [ -d "${bundle_dir}/${component_dir}" ]
+ 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 "${component_dir}")"
+ mkdir --parents "${bundle_dir}/$(dirname "${bundle_component_dir}")"
- cp -r --preserve=timestamps \
+ cp -H -r --preserve=timestamps \
"/${root_prefix}/${component_dir}" \
- "${bundle_dir}/${component_dir}"
+ "${bundle_dir}/${bundle_component_dir}"
fi
done
fi
do
lib_basename="$(basename "${lib_file}")"
- if [ -f "${bundle_dir}/${lib_basename}" ]
+ if [ -f "${lib_dir}/${lib_basename}" ]
then
continue
fi
-exec rm {} \;
find "${bundle_dir}/lib" \
- -type d \
-depth \
+ -type d \
-exec rmdir --ignore-fail-on-non-empty {} \;
}
+Linux::getRpath () {
+ local exe_file="${1}"
+
+ local exe_dir="$(dirname "${exe_file}")"
+ 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 rpath_origin='$ORIGIN'
+
+ if [ "${exe_subdir}" = '' ]
+ then
+ printf '%s/lib\n' "${rpath_origin}"
+ else
+ if [ "${exe_subdir}" = 'lib' ]
+ then
+ printf '%s\n' "${rpath_origin}"
+ else
+ local num_parent_dir="$(echo "${exe_subdir}" | tr '/' '\n' | wc -l)"
+ local rpath_subdir
+ local i=0
+ while [ "${i}" -lt "${num_parent_dir}" ]
+ do
+ rpath_subdir="${rpath_subdir}/.."
+ i="$((${i} + 1))"
+ done
+ printf '%s%s/lib\n' "${rpath_origin}" "${rpath_subdir}"
+ fi
+ fi
+}
+
+Linux::patchExe () {
+ local exe_file="${1}"
+
+ local linux_rpath_string=$"$(Linux::getRpath "${exe_file}")"
+ patchelf --set-rpath "${linux_rpath_string}" "${exe_file}"
+}
+
+Linux::patchLib () {
+ local lib_dir="${1}"
+ local exe_file
+
+ find "${lib_dir}" \
+ -type f \
+ -name '*.so*' \
+ | while read exe_file
+ do
+ Linux::patchExe "${exe_file}"
+ done
+}
+
Windows::listLibForManifest () {
local lib_dir="${1}"
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"
lib_dir="${bundle_dir}/lib"
-rpath_string='@executable_path:lib'
+manifest_file="${lib_dir}/lib.manifest"
+exe_action='Common::noOp'
lib_action='Common::noOp'
case "${system_name}" in
Common::getPath "${exe_file}" > "${registry_dir}/$(uuidgen)"
exit
;;
+ 'linux')
+ exe_action='Linux::patchExe'
+ lib_action='Linux::patchLib'
+ ;;
'windows')
lib_action='Windows::writeManifest'
;;
"${exe_action}" "${exe_file}"
rm "${registry_entry}"
+
+ "${exe_action}" "${exe_file}"
done
rmdir "${registry_dir}"