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