#! /usr/bin/env bash
Common::noOp () {
printf ''
}
Common::getPath () {
local file_path="${1}"
if command -v cygpath >/dev/null
then
if [ "${file_path}" = '-' ]
then
tr '\n' '\0' \
| xargs -0 -n1 -P1 -I{} \
cygpath --unix '{}'
else
cygpath --unix "${file_path}"
fi
else
if [ "${file_path}" = '-' ]
then
cat
else
printf '%s\n' "${file_path}"
fi
fi
}
Common::grepLdd () {
egrep ' => '
}
Common::stripLdd () {
sed -e 's/ (0x[0-9a-f]*)$//;s/^.* => //'
}
Multi::excludeLdd () {
case "${system_name}" in
'linux')
egrep -v '/libc\./|/libX|/libxcb|/libGL' \
| egrep -v '/libbsd\.|/libICE\.|/libselinux\.|/libSM.\.'
;;
'windows')
egrep -i '\.dll => [A-Z]:\\msys64\\' \
;;
esac
}
Multi::filterLib () {
Common::grepLdd \
| Multi::excludeLdd \
| Common::stripLdd \
| Common::getPath -
}
Multi::printLdd () {
local exe_file="${1}"
case "${system_name}" in
'linux')
ldd "${exe_file}"
;;
'windows')
ntldd --recursive "${exe_file}"
;;
esac
}
Multi::getGtkThemeName () {
case "${system_name}" in
'linux')
echo 'Adwaita'
;;
'windows')
echo 'MS-Windows'
;;
esac
}
Multi::getGtkLibName () {
case "${system_name}" in
'linux')
echo 'libgtk-x11-2.0.so.0'
;;
'windows')
echo 'libgtk-win32-2.0-0.dll'
;;
esac
}
Multi::getRootPrefix () {
local lib_file="${1}"
case "${system_name}" in
'linux')
echo "${lib_file}" \
| cut -f2 -d'/'
;;
'windows')
basename "${lib_file}" \
| xargs -n1 -P 1 which \
| cut -f2 -d'/'
;;
esac
}
Multi::getLibPrefix () {
local lib_file="${1}"
case "${system_name}" in
'linux')
dirname "${lib_file}" \
| cut -f3- -d'/'
;;
'windows')
echo 'lib'
;;
esac
}
Multi::bundleGtkDepsFromFile () {
local lib_file="${1}"
lib_basename="$(basename "${lib_file}")"
gtk_lib_name="$(Multi::getGtkLibName)"
if [ "${lib_basename}" = "${gtk_lib_name}" ]
then
root_prefix="$(Multi::getRootPrefix "${lib_file}")"
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'
do
if ! [ -d "${bundle_dir}/${component_dir}" ]
then
mkdir --parents "${bundle_dir}/$(dirname "${component_dir}")"
cp -r --preserve=timestamps \
"/${root_prefix}/${component_dir}" \
"${bundle_dir}/${component_dir}"
fi
done
fi
}
Multi::bundleLibFromFile () {
local exe_file="${1}"
Multi::printLdd "${exe_file}" \
| Multi::filterLib \
| while read lib_file
do
lib_basename="$(basename "${lib_file}")"
if [ -f "${bundle_dir}/${lib_basename}" ]
then
continue
fi
cp --preserve=timestamps \
"${lib_file}" \
"${lib_dir}/${lib_basename}"
Multi::bundleGtkDepsFromFile "${lib_file}"
done
}
Multi::cleanUp () {
find "${bundle_dir}/lib" \
-type f \
-name '*.a' \
-exec rm {} \;
find "${bundle_dir}/lib" \
-type f \
-name '*.h' \
-exec rm {} \;
find "${bundle_dir}/lib" \
-type d \
-depth \
-exec rmdir --ignore-fail-on-non-empty {} \;
}
Linux::patchExe () {
local exe_file="${1}"
patchelf --set-rpath "${rpath_string}" "${exe_file}"
}
Linux::patchLib () {
local lib_dir="${1}"
find "${lib_dir}" \
-type f \
-name '*.so*' \
-exec patchelf --set-rpath "${rpath_string}" {} \;
}
Windows::listLibForManifest () {
local lib_dir="${1}"
find "${lib_dir}" \
-maxdepth 1 \
-type f \
-name '*.dll' \
-exec basename {} \; \
| tr '\n' '\0' \
| xargs -0 -n1 -P1 -I{} \
printf ' \n'
}
Windows::writeManifest () {
local lib_dir="${1}"
cat > "${manifest_file}" <<-EOF
$(Windows::listLibForManifest "${lib_dir}")
EOF
}
system_name="${1}"; shift
bundle_dir="${1}"; shift
exe_file="${1}"; shift
bundle_dir="$(Common::getPath "${bundle_dir}")"
registry_dir="${bundle_dir}/registry"
lib_dir="${bundle_dir}/lib"
manifest_file="${lib_dir}/lib.manifest"
rpath_string='@executable_path:lib'
exe_action='Common::noOp'
lib_action='Common::noOp'
case "${system_name}" in
'register')
mkdir --parents "${registry_dir}"
Common::getPath "${exe_file}" > "${registry_dir}/$(uuidgen)"
exit
;;
'linux')
exe_action='Linux::patchExe'
lib_action='Linux::patchLib'
;;
'windows')
lib_action='Windows::writeManifest'
;;
*)
printf 'ERROR: unsupported system: %s\n' "${system_name}" >&2
exit 1
;;
esac
mkdir --parents "${lib_dir}"
if [ -d "${registry_dir}" ]
then
for registry_entry in "${registry_dir}"/*
do
exe_file="$(cat "${registry_entry}")"
Multi::bundleLibFromFile "${exe_file}"
"${exe_action}" "${exe_file}"
rm "${registry_entry}"
"${exe_action}" "${exe_file}"
done
rmdir "${registry_dir}"
fi
"${lib_action}" "${lib_dir}"
Multi::cleanUp