]> git.xonotic.org Git - xonotic/netradiant.git/blob - library-bundler
bundle: more linux bundling
[xonotic/netradiant.git] / library-bundler
1 #! /usr/bin/env bash
2
3 Common::noOp () {
4         printf ''
5 }
6
7 Common::getPath () {
8         local file_path="${1}"
9
10         if command -v cygpath >/dev/null
11         then
12                 if [ "${file_path}" = '-' ]
13                 then
14                         tr '\n' '\0' \
15                         | xargs -0 -n1 -P1 -I{} \
16                                 cygpath --unix '{}'
17                 else
18                         cygpath --unix "${file_path}"
19                 fi
20         else
21                 if [ "${file_path}" = '-' ]
22                 then
23                         cat
24                 else
25                         printf '%s\n' "${file_path}"
26                 fi
27         fi
28 }
29
30 Common::grepLdd () {
31         egrep ' => '
32 }
33
34 Common::stripLdd () {
35         sed -e 's/ (0x[0-9a-f]*)$//;s/^.* => //'
36 }
37
38 Multi::excludeLdd () {
39         case "${system_name}" in
40                 'linux')
41                         egrep -v '/libc\.|/libdl\.|/libm\.|/libX|/libxcb|/libGL'
42                         ;;
43                 'windows')
44                         egrep -i '\.dll => [A-Z]:\\msys64\\' \
45                         ;;
46         esac
47 }
48
49 Multi::filterLib () {
50         Common::grepLdd \
51         | Multi::excludeLdd \
52         | Common::stripLdd \
53         | Common::getPath -
54 }
55
56 Multi::printLdd () {
57         local exe_file="${1}"
58
59         case "${system_name}" in
60                 'linux')
61                         ldd "${exe_file}"
62                         ;;
63                 'windows')
64                         ntldd --recursive "${exe_file}"
65                         ;;
66         esac
67 }
68
69 Multi::getGtkThemeName () {
70         case "${system_name}" in
71                 'linux')
72                         echo 'Adwaita'
73                         ;;
74                 'windows')
75                         echo 'MS-Windows'
76                         ;;
77         esac
78 }
79
80 Multi::getGtkLibName () {
81         case "${system_name}" in
82                 'linux')
83                         echo 'libgtk-x11-2.0.so.0'
84                         ;;
85                 'windows')
86                         echo 'libgtk-win32-2.0-0.dll'
87                         ;;
88         esac
89 }
90
91 Multi::getRootPrefix () {
92         local lib_file="${1}"
93
94         case "${system_name}" in
95                 'linux')
96                         echo "${lib_file}" \
97                         | cut -f2 -d'/'
98                         ;;
99                 'windows')
100                         basename "${lib_file}" \
101                         | xargs -n1 -P 1 which \
102                         | cut -f2 -d'/'
103                         ;;
104         esac
105 }
106
107 Multi::getLibPrefix () {
108         local lib_file="${1}"
109
110         case "${system_name}" in
111                 'linux')
112                         dirname "${lib_file}" \
113                         | cut -f3- -d'/'
114                         ;;
115                 'windows')
116                         echo 'lib'
117                         ;;
118         esac
119 }
120
121 Multi::getGtkDeps () {
122         local lib_prefix="${1}"
123         local gtk_theme_name="${2}"
124
125         cat <<-EOF
126         share/themes/${gtk_theme_name}/gtk-2.0
127         share/icons/hicolor
128         ${lib_prefix}/gdk-pixbuf-2.0
129         ${lib_prefix}/gtk-2.0
130         EOF
131
132         case "${system_name}" in
133                 'linux')
134                         cat <<-EOF
135                         ${lib_prefix}/libatk-bridge-2.0.so.0
136                         ${lib_prefix}/libcanberra-0.30
137                         ${lib_prefix}/libcanberra.so.0
138                         ${lib_prefix}/libcanberra-gtk.so.0
139                         EOF
140                         ;;
141         esac
142 }
143
144 Multi::bundleGtkDepsFromFile () {
145         local lib_file="${1}"
146
147         lib_basename="$(basename "${lib_file}")"
148
149         gtk_lib_name="$(Multi::getGtkLibName)"
150         if [ "${lib_basename}" = "${gtk_lib_name}" ]
151         then
152                 root_prefix="$(Multi::getRootPrefix "${lib_file}")"
153                 lib_prefix="$(Multi::getLibPrefix "${lib_file}")"
154                 gtk_theme_name="$(Multi::getGtkThemeName)"
155
156                 for component_dir in $(Multi::getGtkDeps "${lib_prefix}" "${gtk_theme_name}")
157                 do
158                         bundle_component_dir="$(echo "${component_dir}" | sed -e 's|^'"${lib_prefix}"'|lib|')"
159                         if ! [ -e "${bundle_dir}/${bundle_component_dir}" ]
160                         then
161                                 mkdir --parents "${bundle_dir}/$(dirname "${bundle_component_dir}")"
162
163                                 cp -H -r --preserve=timestamps \
164                                         "/${root_prefix}/${component_dir}" \
165                                         "${bundle_dir}/${bundle_component_dir}"
166                         fi
167                 done
168         fi
169 }
170
171 Multi::bundleLibFromFile () {
172         local exe_file="${1}"
173
174         Multi::printLdd "${exe_file}" \
175         | Multi::filterLib \
176         | while read lib_file
177         do
178                 lib_basename="$(basename "${lib_file}")"
179
180                 if [ -f "${bundle_dir}/${lib_basename}" ]
181                 then
182                         continue
183                 fi
184
185                 cp --preserve=timestamps \
186                         "${lib_file}" \
187                         "${lib_dir}/${lib_basename}"
188
189                 Multi::bundleGtkDepsFromFile "${lib_file}"
190         done
191 }
192
193 Multi::cleanUp () {
194         find "${bundle_dir}/lib" \
195                 -type f \
196                 -name '*.a' \
197                 -exec rm {} \;
198
199         find "${bundle_dir}/lib" \
200                 -type f \
201                 -name '*.h' \
202                 -exec rm {} \;
203
204         find "${bundle_dir}/lib" \
205                 -type d \
206                 -depth \
207                 -exec rmdir --ignore-fail-on-non-empty {} \;
208 }
209
210 Linux::patchExe () {
211         local exe_file="${1}"
212
213         patchelf --set-rpath "${rpath_string}" "${exe_file}"
214 }
215
216 Linux::patchLib () {
217         local lib_dir="${1}"
218
219         find "${lib_dir}" \
220                 -type f \
221                 -name '*.so*' \
222                 -exec patchelf --set-rpath "${rpath_string}" {} \;
223 }
224
225 Windows::listLibForManifest () {
226         local lib_dir="${1}"
227
228         find "${lib_dir}" \
229                 -maxdepth 1 \
230                 -type f \
231                 -name '*.dll' \
232                 -exec basename {} \; \
233         | tr '\n' '\0' \
234         | xargs -0 -n1 -P1 -I{} \
235                 printf '  <file name="{}"/>\n'
236 }
237
238 Windows::writeManifest () {
239         local lib_dir="${1}"
240
241         cat > "${manifest_file}" <<-EOF
242         <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
243           <assemblyIdentity type="win32" name="lib" version="1.0.0.0"/>
244         $(Windows::listLibForManifest "${lib_dir}")
245         </assembly>
246         EOF
247 }
248
249 system_name="${1}"; shift
250 bundle_dir="${1}"; shift
251 exe_file="${1}"; shift
252
253 bundle_dir="$(Common::getPath "${bundle_dir}")"
254 registry_dir="${bundle_dir}/registry"
255 lib_dir="${bundle_dir}/lib"
256
257 manifest_file="${lib_dir}/lib.manifest"
258
259 rpath_string='@executable_path:lib'
260
261 exe_action='Common::noOp'
262 lib_action='Common::noOp'
263
264 case "${system_name}" in
265         'register')
266                 mkdir --parents "${registry_dir}"
267                 Common::getPath "${exe_file}" > "${registry_dir}/$(uuidgen)"
268                 exit
269                 ;;
270         'linux')
271                 exe_action='Linux::patchExe'
272                 lib_action='Linux::patchLib'
273                 ;;
274         'windows')
275                 lib_action='Windows::writeManifest'
276                 ;;
277         *)
278                 printf 'ERROR: unsupported system: %s\n' "${system_name}" >&2
279                 exit 1
280                 ;;
281 esac
282
283 mkdir --parents "${lib_dir}"
284
285 if [ -d "${registry_dir}" ]
286 then
287         for registry_entry in "${registry_dir}"/*
288         do
289                 exe_file="$(cat "${registry_entry}")"
290
291                 Multi::bundleLibFromFile "${exe_file}"
292
293                 "${exe_action}" "${exe_file}"
294
295                 rm "${registry_entry}"
296
297                 "${exe_action}" "${exe_file}"
298         done
299
300         rmdir "${registry_dir}"
301 fi
302
303 "${lib_action}" "${lib_dir}"
304
305 Multi::cleanUp