Index: uspace/app/bdsh/meson.build
===================================================================
--- uspace/app/bdsh/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/app/bdsh/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -45,3 +45,3 @@
 
 # TODO: install this file somewhere sane
-install_data('demo.txt', install_dir: '/')
+installed_data += { 'name': 'demo.txt', 'dir': '/' }
Index: uspace/app/modplay/meson.build
===================================================================
--- uspace/app/modplay/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/app/modplay/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -4,4 +4,4 @@
 if install_nonessential_data
 	# TODO: install this file somewhere sane
-	install_data('demo.xm', install_dir: '/')
+	installed_data += { 'name': 'demo.xm', 'dir': '/' }
 endif
Index: uspace/app/viewer/meson.build
===================================================================
--- uspace/app/viewer/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/app/viewer/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -4,4 +4,4 @@
 if install_nonessential_data
 	# TODO: install this file somewhere sane
-	install_data('logo.tga', install_dir: '/')
+	installed_data += { 'name': 'logo.tga', 'dir': '/' }
 endif
Index: uspace/app/wavplay/meson.build
===================================================================
--- uspace/app/wavplay/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/app/wavplay/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -9,4 +9,4 @@
 if install_nonessential_data
 	# TODO: install this file somewhere sane
-	install_data('demo.wav', install_dir: '/')
+	installed_data += { 'name': 'demo.wav', 'dir': '/' }
 endif
Index: uspace/drv/bus/isa/meson.build
===================================================================
--- uspace/drv/bus/isa/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/drv/bus/isa/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -1,3 +1,6 @@
 src = files('i8237.c', 'isa.c')
 
-install_data('ebus.dev', 'isa.dev', install_dir: 'drv' / 'isa')
+installed_data += [
+	{ 'name': 'ebus.dev', 'dir': 'drv/isa' },
+	{ 'name': 'isa.dev', 'dir': 'drv/isa' },
+]
Index: uspace/drv/nic/ar9271/meson.build
===================================================================
--- uspace/drv/nic/ar9271/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/drv/nic/ar9271/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -8,3 +8,3 @@
 )
 
-install_data('ar9271.fw', install_dir: 'drv' / 'ar9271')
+installed_data += { 'name': 'ar9271.fw', 'dir': 'drv/ar9271' }
Index: uspace/lib/meson.build
===================================================================
--- uspace/lib/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/lib/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -63,4 +63,8 @@
 testfiles = []
 
+# Text of the install script.
+uspace_lib_install_script_text = []
+uspace_lib_install_depends = []
+
 foreach l : libs
 	# Variables that might be defined in library meson files:
Index: uspace/meson.build
===================================================================
--- uspace/meson.build	(revision 1ef3213bed44afc575804996e7f9e120ec63db73)
+++ uspace/meson.build	(revision 5852a5a4c4f6ed1865f99f5fbde2f51505fc8953)
@@ -12,4 +12,6 @@
 subdir('drv')
 
+install_files = []
+install_deps = []
 dirs = []
 
@@ -41,5 +43,7 @@
 	# Install driver metadata.
 	if not CONFIG_BAREBONE or rd_essential.contains('drv' / drv)
-		install_data('drv' / drv / _basename + '.ma', install_dir: 'drv' / _basename)
+		_src = meson.current_source_dir() / 'drv' / drv / _basename + '.ma'
+		_dstdir = 'drv' / _basename
+		install_files += [[ _dstdir, _src, _basename + '.ma' ]]
 	endif
 endforeach
@@ -55,4 +59,5 @@
 	link_args = []
 	language = 'c'
+	installed_data = []
 
 	subdir(appdirs.get('subdir'))
@@ -62,4 +67,13 @@
 
 	install = not CONFIG_BAREBONE or rd_essential.contains(dir)
+
+	if install
+		# Install data files, if any.
+		foreach _f : installed_data
+			_dstdir = _f.get('dir')
+			_src = meson.current_source_dir() / dir / _f.get('name')
+			install_files += [[ _dstdir, _src, _f.get('name') ]]
+		endforeach
+	endif
 
 	# basename/dirname will be is useful later.
@@ -133,6 +147,4 @@
 endforeach
 
-install_script_text = []
-
 foreach tst : bin_targets
 	_ldargs = tst.get('link_args')
@@ -145,21 +157,4 @@
 	_build_name = _full_install_path.underscorify()
 	_full_build_name = meson.current_build_dir() / _build_name
-
-	if _install
-		# Due to certain quirks of our build, executables need to be built with a different name than what they are installed with.
-		# Meson doesn't support renaming installed files (at least not as of mid-2019) so we do it here manually.
-
-		install_script_text += [
-			'mkdir -p ${MESON_INSTALL_DESTDIR_PREFIX}"@0@"'.format(_install_dir),
-			'cp "@0@" ${MESON_INSTALL_DESTDIR_PREFIX}"@1@"'.format(_full_build_name, _full_install_path),
-		]
-
-		if install_debug_files
-			install_script_text += [
-				'cp "@0@".disasm ${MESON_INSTALL_DESTDIR_PREFIX}debug/"@1@".disasm'.format(_full_build_name, _full_install_path),
-				'cp "@0@".map ${MESON_INSTALL_DESTDIR_PREFIX}debug/"@1@".map'.format(_full_build_name, _full_install_path)
-			]
-		endif
-	endif
 
 	if link_map
@@ -176,26 +171,49 @@
 		link_args: arch_uspace_c_args + arch_uspace_link_args + _ldargs,
 		implicit_include_directories: false,
-		install: false,
-		#install_dir: _install_dir,
 		build_by_default: true,
 	)
 
 	if disassemble
-		custom_target(_build_name + '.disasm',
+		_disasm = custom_target(_build_name + '.disasm',
 			command: [ objdump, '-S', '@INPUT@' ],
 			input: _bin,
 			output: _build_name + '.disasm',
 			capture: true,
-			install: false,
-			#install_dir: 'debug' / _install_dir,
 			build_by_default: true,
 		)
 	endif
-endforeach
+
+	if _install
+		# Due to certain quirks of our build, executables need to be built with a different name than what they are installed with.
+		# Meson doesn't support renaming installed files (at least not as of mid-2019) so we do it manually.
+
+		install_files += [[ _install_dir, _full_build_name, _basename ]]
+		install_deps += [ _bin ]
+
+		if install_debug_files
+			if disassemble
+				install_files += [[ 'debug' / _install_dir, _full_build_name + '.disasm', _basename + '.disasm' ]]
+				install_deps += [ _disasm ]
+			endif
+			if link_map
+				install_files += [[ 'debug' / _install_dir, _full_build_name + '.map', _basename + '.map' ]]
+			endif
+		endif
+	endif
+endforeach
+
+uspace_bin_install_script_text = []
+
+foreach f : install_files
+	_cmd = 'mkdir -p "${MESON_INSTALL_DESTDIR_PREFIX}@0@" && cp -L -T "@1@" "${MESON_INSTALL_DESTDIR_PREFIX}@0@/@2@"'
+	uspace_bin_install_script_text += _cmd.format(f[0], f[1], f[2])
+endforeach
+
+uspace_install_script_text = uspace_lib_install_script_text + uspace_bin_install_script_text
 
 # Emit and register the install script.
 
 configure_file(
-	configuration: { 'text' : '\n'.join(install_script_text) },
+	configuration: { 'text' : '\n'.join(uspace_install_script_text) },
 	input: 'install.sh.in',
 	output: 'install.sh',
