aboutsummaryrefslogtreecommitdiffstats
path: root/packaging/macosx/osx-app.sh.in
blob: 6de11f41a932eabebae8a4b7351366109e1ecac8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
#!/bin/bash
#
# USAGE
# osx-app [-s] [-l /path/to/libraries]
#
# This script attempts to build an Wireshark.app bundle for macOS, resolving
# dynamic libraries, etc.
# It strips the executable and libraries if '-s' is given.
# The Info.plist file can be found in the base wireshark directory once
# configure has been run.
#
# AUTHORS
#		 Kees Cook <kees@outflux.net>
#		 Michael Wybrow <mjwybrow@users.sourceforge.net>
#		 Jean-Olivier Irisson <jo.irisson@gmail.com>
#
# Copyright (C) 2005 Kees Cook
# Copyright (C) 2005-2007 Michael Wybrow
# Copyright (C) 2007 Jean-Olivier Irisson
#
# Released under GNU GPL, read the file 'COPYING' for more information
#
# Thanks to GNUnet's "build_app" script for help with library dep resolution.
#		https://gnunet.org/svn/GNUnet/contrib/OSX/build_app
#
# NB:
# This originally came from Inkscape; Inkscape's configure script has an
# "--enable-osxapp", which causes some of Inkscape's installation data
# files to have macOS-ish paths under Contents/Resources of the bundle
# or under /Library/Application Support.  We don't have such an option;
# we just put them in "bin", "etc", "lib", and "share" directories
# under Contents/Resources, rather than in the "bin", "etc", "lib",
# and "share" directories under the installation directory.
#

shopt -s extglob

# Defaults
strip=false
exclude_prefixes="/System/|/Library/|/usr/lib/|/usr/X11/|/opt/X11/|@rpath|@executable_path"

# Bundle always has the same name. Version information is stored in
# the Info.plist file which is filled in by the configure script.
bundle="Wireshark.app"

# Location for libraries (tools/macos-setup.sh defaults to whatever the
# various support libraries use as their standard installation location,
# which is /usr/local)
if [ -z "$LIBPREFIX" ]; then
	LIBPREFIX="/usr/local"
fi


# Help message
#----------------------------------------------------------
help()
{
echo -e "
Create an app bundle for macOS

USAGE
	$0 [-s] [-l /path/to/libraries]

OPTIONS
	-h,--help
		Display this help message.
	-s
		Strip the libraries and executables from debugging symbols.
	-l,--libraries
		Specify the path to the libraries Wireshark depends on
		(typically /sw or /opt/local). By default it is
		/usr/local.

EXAMPLE
	$0 -s -l /opt/local
"
}


# Parse command line arguments
#----------------------------------------------------------
while [ "$1" != "" ]
do
	case $1 in
		-s)
			strip=true ;;
		-l|--libraries)
			LIBPREFIX="$2"
			shift 1 ;;
		-h|--help)
			help
			exit 0 ;;
		*)
			echo "Invalid command line option: $1"
			exit 2 ;;
	esac
	shift 1
done

# Safety tests
if [ ! -e "$LIBPREFIX" ]; then
	echo "Cannot find the directory containing the libraries: $LIBPREFIX" >&2
	exit 1
fi

if [ ! -d "$bundle" ] ; then
	echo "$bundle not found" >&2
	exit 1
fi

qt_frameworks_dir=$( "@QT_QMAKE_EXECUTABLE@" -query QT_INSTALL_LIBS )
if [ ! -d "$qt_frameworks_dir" ] ; then
	echo "Can't find the Qt frameworks directory" >&2
	exit 1
fi

#
# Leave the Qt frameworks out of the special processing.
#
exclude_prefixes="$exclude_prefixes|$qt_frameworks_dir"

# Package paths
pkgexec="$bundle/Contents/MacOS"
#pkgres="$bundle/Contents/Resources"
pkglib="$bundle/Contents/Frameworks"
pkgplugin="$bundle/Contents/PlugIns/wireshark/@PLUGIN_PATH_ID@"

# Set the 'macosx' directory, usually the current directory.
#resdir=$( pwd )

#
# Get a list of all binaries in the bundle.
# Treat all plain files with read and execute permissions for all as
# binaries.
#
secondary_binary_list=$( find $pkgexec \! -name Wireshark -type f -perm -0555 -print | sort )
plugin_library_list=$( find $pkgplugin -name "*.so" -type f -perm -0555 -print | sort )
bundle_binary_list="Wireshark $secondary_binary_list $plugin_library_list"

echo -e "\\nFixing up $bundle..."

echo -e "\\nPrepopulating our libraries"

# Copy only <library>.<SOVERSION>.dylib.
cp -v +([^.]).+([[:digit:]]).dylib "$pkglib"

# Find out libs we need from Fink, MacPorts, or from a custom install
# (i.e. $LIBPREFIX), then loop until no changes.
a=1
nfiles=0
endl=true
while $endl; do
	echo -e "\\nLooking for dependencies. Round $a"
	#
	# To find dependencies, we:
	#
	#    run otool -L on all the binaries in the bundle, and on all
	#    the shared libraries in the $pkglib directory, to find all
	#    the libraries they depend on (we don't bother with the
	#    frameworks, as the only frameworks we ship are the Qt
	#    frameworks, which don't depend on any libraries that
	#    don't ship with the OS, and as it's hard to find the
	#    framework libraries under $pkglib without getting
	#    non-framework files);
	#
	#    filter out all lines that don't contain "compatibility" to
	#    remove lines of output that don't correspond to dependencies;
	#
	#    use cut to extract the library name from the output;
	#
	#    replace "@rpath/libssh" with "/usr/local/lib/libssh" so that
	#    it isn't excluded from subsequent filtering.
	#    libssh, for some reason, has its "install name" set to
	#    @rpath/libssh.4.dylib, rather than /usr/local/lib/libssh.4.dylib,
	#    when built by tools/macos-setup.sh;
	#
	#    replace "\tlibbrotli" with "\t/usr/local/lib/libbrotli" so that
	#    it isn't excluded from subsequent filtering.
	#    libbrotli, for some reason, doesn't have a path prefix in its
	#    "install name" when built by tools/macos-setup.sh;
	#
	#    strip out system libraries, as we don't bundle them with
	#    Wireshark;
	#
	#    eliminate duplicates.
	#
	libs="$(
		# shellcheck disable=SC2086
		otool -L $bundle_binary_list "$pkglib"/*.dylib 2>/dev/null \
		| grep -F compatibility \
		| cut -d\( -f1 \
		| sed '1,$s;^	@rpath/libssh;	/usr/local/lib/libssh;' \
		| sed '1,$s;^	libbrotli;	/usr/local/lib/libbrotli;' \
		| grep -E -v "$exclude_prefixes" \
		| sort \
		| uniq \
		)"
	# shellcheck disable=SC2086
	install -m 644 -C -v $libs "$pkglib"
	(( a++ ))
	# shellcheck disable=SC2012
	nnfiles=$( ls "$pkglib" | wc -l )
	if (( nnfiles == nfiles )); then
		endl=false
	else
		nfiles=$nnfiles
	fi
done

# Strip libraries and executables if requested
#----------------------------------------------------------
if [ "$strip" = "true" ]; then
	echo -e "\\nStripping debugging symbols...\\n"
	strip -x "$pkglib"/*.dylib
	strip -ur "$bundle_binary_list"
fi

#
# This may not work on Qt 5.5.0 or 5.5.1:
# https://bugreports.qt.io/browse/QTBUG-47868
#
"@QT_MACDEPLOYQT_EXECUTABLE@" "$bundle" -no-strip -verbose=2 || exit 1

#
# The build process added to the Wireshark binary an rpath entry
# pointing to the directory containing the Qt frameworks; remove
# that entry from the Wireshark binary in the package.
#
/usr/bin/install_name_tool -delete_rpath "$qt_frameworks_dir" $pkgexec/Wireshark

# NOTE: we must rpathify *all* files, *including* Qt libraries etc.,
#
rpathify_file () {
	# Fix a given executable, library, or plugin to be relocatable
	if [ ! -f "$1" ]; then
		return 0;
	fi

	#
	# OK, what type of file is this?
	#
	if ! filetype=$( otool -hv "$1" | grep -E MH_MAGIC | awk '{print $5}' ; exit "${PIPESTATUS[0]}" ) ; then
		echo "Unable to rpathify $1 in $( pwd ): file type failed."
		exit 1
	fi

	case "$filetype" in

	EXECUTE|DYLIB|BUNDLE)
		#
		# Executable, library, or plugin.  (Plugins
		# can be either DYLIB or BUNDLE; shared
		# libraries are DYLIB.)
		#
		# For DYLIB and BUNDLE, fix the shared
		# library identification.
		#
		if [[ "$filetype" = "DYLIB" || "$filetype" = "BUNDLE" ]]; then
			echo "Changing shared library identification of $1"
			base=$( echo "$1" | awk -F/ '{print $NF}' )
			#
			# The library will end up in a directory in
			# the rpath; this is what we should change its
			# ID to.
			#
			to=@rpath/$base
			/usr/bin/install_name_tool -id "$to" "$1"

			#
			# If we're a library and we depend on something in
			# @executable_path/../Frameworks, replace that with
			# @rpath.
			#
			otool -L "$1" | grep @executable_path/../Frameworks | awk '{print $1}' | \
			while read -r dep_lib ; do
				base=$( echo "$dep_lib" | awk -F/ '{print $NF}' )
				to="@rpath/$base"
				echo "Changing reference to $dep_lib to $to in $1"
				/usr/bin/install_name_tool -change "$dep_lib" "$to" "$1"
			done
		fi

		#
		# Find our local rpaths and remove them.
		#
		otool -l "$1" | grep -A2 LC_RPATH \
			| awk '$1=="path" && $2 !~ /^@/ {print $2}' \
			| grep -E -v "$exclude_prefixes" | \
		while read -r lc_rpath ; do
			echo "Stripping LC_RPATH $lc_rpath from $1"
			install_name_tool -delete_rpath "$lc_rpath" "$1"
		done

		#
		# Add -Wl,-rpath,@executable_path/../Frameworks
		# to the rpath, so it'll find the bundled
		# frameworks and libraries if they're referred
		# to by @rpath/, rather than having a wrapper
		# script tweak DYLD_LIBRARY_PATH.
		#
		if [[ "$filetype" = "EXECUTE" ]]; then
			if [ -d ../Frameworks ] ; then
				framework_path=../Frameworks
			elif [ -d ../../Frameworks ] ; then
				framework_path=../../Frameworks
			else
				echo "Unable to find relative path to Frameworks for $1 from $( pwd )"
				exit 1
			fi

			echo "Adding @executable_path/$framework_path to rpath of $1"
			/usr/bin/install_name_tool -add_rpath @executable_path/$framework_path "$1"
		fi

		#
		# Show the minimum supported version of macOS
		# for each executable or library
		#
		if [[ "$filetype" = "EXECUTE" || "$filetype" = "DYLIB" ]] && [[ "$VERSION" -ge "7" ]] ; then
			echo "Minimum macOS version for $1:"
			otool -l "$1" | grep -A3 LC_VERSION_MIN_MACOSX
		fi

		#
		# Get the list of dynamic libraries on which this
		# file depends, and select only the libraries that
		# are in $LIBPREFIX, as those are the only ones
		# that we'll be shipping in the app bundle; the
		# other libraries are system-supplied or supplied
		# as part of X11, will be expected to be on the
		# system on which the bundle will be installed,
		# and should be referred to by their full pathnames.
		#
		libs="$(
			otool -L "$1" \
			| grep -F compatibility \
			| cut -d\( -f1 \
			| grep -E -v "$exclude_prefixes" \
			| sort \
			| uniq \
			)"

		for lib in $libs; do
			#
			# Get the file name of the library.
			#
			base=$( echo "$lib" | awk -F/ '{print $NF}' )
			#
			# The library will end up in a directory in
			# the rpath; this is what we should change its
			# file name to.
			#
			to=@rpath/$base
			#
			# Change the reference to that library.
			#
			echo "Changing reference to $lib to $to in $1"
			/usr/bin/install_name_tool -change "$lib" "$to" "$1"
		done
		;;
	esac
}

rpathify_dir () {
	#
	# Make sure we *have* that directory
	#
	if [ -d "$1" ]; then
		(cd "$1" || exit 1
		echo "rpathifying $1"
		#
		# Make sure we *have* files to fix
		#
		# shellcheck disable=SC2086
		files=$( ls $2 2>/dev/null )
		if [ -n "$files" ]; then
			for file in $files; do
				rpathify_file "$file" "$( pwd )"
			done
		else
			echo "no files found in $1"
		fi
		)
		rf_ret=$?
		if [ $rf_ret -ne 0 ] ; then exit $rf_ret ; fi
	fi
}

rpathify_files () {
	#
	# Fix bundle deps
	#
	rpathify_dir "$pkglib" "*.dylib"
	rpathify_dir "$pkgexec" "*"
	for plugindir in "$pkgplugin"/*
	do
		rpathify_dir "$plugindir" "*"
	done

	rpathify_dir "$pkgexec/extcap" "*"
}

if [ ${#LIBPREFIX} -ge "6" ]; then
	# If the LIBPREFIX path is long enough to allow
	# path rewriting, then do this.
	# 6 is the length of @rpath, which replaces LIBPREFIX.
	rpathify_files
else
	echo "Could not rewrite dylib paths for bundled libraries.  This requires" >&2
	echo "the support libraries to be installed in a PREFIX of at least 6 characters in length." >&2
	echo "" >&2
	exit 1

fi

dsymify_file () {
	echo "    $1"
	dsymutil --minimize "$1"
}

echo "Dsymifying executables:"
if [ -z "$bundle_binary_list" ] ; then
	echo "No executables specified for dsymifying."
	exit 1
fi
for binary in $bundle_binary_list ; do
	if [ -e "$binary" ];then
		dsymify_file "$binary"
	fi
done

echo "Dsymifying frameworks:"
for framework in "$pkglib"/*.framework/Versions/*/* ; do
	if [ -f "$framework" ];then
		dsymify_file "$framework"
	fi
done

echo "Dsymifying libraries:"
for library in "$pkglib"/*.dylib ; do
	#
	# Squelch warnings, in case the .o files from building
	# support libraries aren't around any more.
	#
	dsymify_file "$library" | grep -E -v 'unable to open object file'
done

echo "Dsymifying plugins:"
for plugin in "$pkgplugin"/*/*.so ; do
	dsymify_file "$plugin"
done

codesign_file () {
	# https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html
	# https://developer.apple.com/library/archive/technotes/tn2206/_index.html
	# https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/resolving_common_notarization_issues?language=objc
	#
	# XXX Do we need to add the com.apple.security.cs.allow-unsigned-executable-memory
	# entitlement for Lua?
	# https://developer.apple.com/documentation/security/hardened_runtime_entitlements?language=objc
	codesign \
		--sign "Developer ID Application: $CODE_SIGN_IDENTITY" \
		--options runtime \
		--entitlements "@CMAKE_SOURCE_DIR@/packaging/macosx/entitlements.plist" \
		--timestamp \
		--verbose \
		"$1"
}

if [ -n "$CODE_SIGN_IDENTITY" ] ; then
	security find-identity -v -s "$CODE_SIGN_IDENTITY" -p codesigning

	# The Code Signing Guide says:
	#
	# "While you use the --deep option for verification to mimic what Gatekeeper does,
	# it is not recommended for signing. During signing, if you have nested code, and
	# if you are signing manually, you sign nested code in stages (as Xcode does
	# automatically), starting with the most deeply embedded components first. You
	# then sign code at the next level of hierarchy, and so on. You work your way
	# outward, finally signing the top level entity that contains all the others.
	# Signing all the components in one shot with --deep is for emergency repairs and
	# temporary adjustments only. Note that signing with the combination --deep
	# --force will forcibly re-sign all code in a bundle."

	echo "Signing frameworks"
	for framework in "$pkglib"/*.framework/Versions/* ; do
		codesign_file "$framework"
	done

	echo "Signing libraries"
	for library in "$pkglib"/*.dylib ; do
		codesign_file "$library"
	done

	plugin_list=$( find $bundle/Contents/PlugIns -type f -name "*.dylib" -o -name "*.so" )
	echo "Signing plugins"
	for plugin in $plugin_list ; do
		codesign_file "$plugin"
	done

	echo "Signing secondary executables"
	if [ -z "$secondary_binary_list" ] ; then
		echo "No executables specified for code signing."
		exit 1
	fi
	for binary in $secondary_binary_list ; do
		if [ -e "$binary" ];then
			codesign_file "$binary"
		fi
	done

	echo "Signing primary executable"
	codesign_file "$pkgexec/Wireshark"

	echo "Signing $bundle"
	codesign_file "$bundle"

	# Code Signing Guide, "Testing Conformance with Command Line Tools"
	codesign --verify --deep --strict --verbose=2 "$bundle" || exit 1
	spctl --assess --type exec --verbose=2 "$bundle" || exit 1
else
	echo "Code signing not performed (no identity)"
fi

exit 0