xref: /linux/tools/perf/scripts/install-build-deps.sh (revision 9f83597eb5716531ceb171aee6aab0115c130dac)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# No 'set -e': the script uses explicit checks for its error paths, and
5# is also run via '$(SHELL) .../install-build-deps.sh' from the make
6# target in tools/perf/Makefile.perf, where a shebang option would be
7# ignored anyway, so direct and make-driven runs behave the same.
8#
9# Install the development packages needed to build tools/perf.
10#
11# The package set is derived from the feature tests in tools/build/feature/:
12# each feature test that perf may compile is mapped to the devel package
13# that provides the headers/libraries it checks, so that a subsequent
14# 'make -C tools/perf' build enables the corresponding perf features.
15#
16# Supported distros, each mapping validated in a fresh container, so the
17# host system is not modified:
18#
19#   - Fedora, on dnf, validated on Fedora 44, in a toolbx container:
20#
21#           toolbox create fedora:44
22#           toolbox enter fedora:44
23#
24# Usage: install-build-deps.sh [OPTIONS]
25#
26# Options:
27#    --list       list the packages that would be installed, then exit
28#    --dry-run    show the install command that would be run, without
29#                 running it
30#    --distro ID  force a distro: fedora (default: auto-detect)
31#    -h, --help   print this help message
32#
33# Requires root (or passwordless sudo) to actually install packages.
34
35set -u
36
37DISTRO=""
38
39help() {
40	cat <<EOF
41Usage: $(basename "$0") [--list] [--dry-run] [--distro ID] [-h|--help]
42
43Install the development packages needed to build tools/perf.
44
45The package set is derived from the feature tests in tools/build/feature/,
46mapping each one with an external dependency to the devel package providing
47it, so the corresponding feature gets enabled on a build.
48
49Options:
50    --list      list the packages that would be installed, then exit
51    --dry-run   show the install command that would be run, without running it
52    --distro ID force a distro: fedora
53    -h, --help  print this help message
54
55Distro supported: Fedora (dnf), validated on a fresh Fedora 44 toolbx
56container.
57EOF
58	exit 0
59}
60
61# ---------------------------------------------------------------------
62# Return the Fedora package(s) providing the devel requirements of a
63# feature test in tools/build/feature/.  Multiple packages are separated
64# by spaces; an empty result means the test has no external devel
65# dependency (pure toolchain/glibc, or no equivalent Fedora package).
66#
67# The mapping was built by checking, for each feature test, which Fedora
68# devel package provides the headers the test compiles against.
69# ---------------------------------------------------------------------
70fedora_pkg_for() {
71	local feat="$1"
72	case "$feat" in
73	libelf|libelf-getphdrnum|libelf-gelf_getnote|libelf-getshdrstrndx)
74		echo "elfutils-libelf-devel"
75		;;
76	libelf-zstd)
77		echo "elfutils-libelf-devel libzstd-devel"
78		;;
79	libdw)
80		echo "elfutils-devel"
81		;;
82	# test-libdebuginfod.c includes <elfutils/debuginfod.h>, which is
83	# provided by elfutils-debuginfod-client-devel, not elfutils-devel.
84	libdebuginfod)
85		echo "elfutils-debuginfod-client-devel"
86		;;
87	libnuma|numa_num_possible_cpus)
88		echo "numactl-devel"
89		;;
90	libzstd)
91		echo "libzstd-devel"
92		;;
93	zlib)
94		echo "zlib-devel"
95		;;
96	lzma)
97		echo "xz-devel"
98		;;
99	libslang)
100		echo "slang-devel"
101		;;
102	libcapstone)
103		echo "capstone-devel"
104		;;
105	libpython)
106		echo "python3-devel"
107		;;
108	libtraceevent)
109		echo "libtraceevent-devel"
110		;;
111	cxa-demangle)
112		echo "libstdc++-devel"
113		;;
114	libbpf)
115		echo "libbpf-devel"
116		;;
117	babeltrace2-ctf-writer)
118		echo "libbabeltrace2-devel"
119		;;
120	libopenssl)
121		echo "openssl-devel"
122		;;
123	libpfm4)
124		echo "libpfm-devel"
125		;;
126	sdt)
127		echo "systemtap-sdt-devel"
128		;;
129	clang-bpf-co-re)
130		echo "clang-devel"
131		;;
132	llvm|llvm-perf)
133		echo "llvm-devel"
134		;;
135	jvmti|jvmti-cmlr)
136		echo "java-latest-openjdk-devel"
137		;;
138	esac
139	# Pure toolchain/libc features (backtrace, eventfd, fortify-source,
140	# gettid, glibc, hello, reallocarray, pthread-*, stackprotector-all,
141	# timerfd, scandirat, sched_getcpu, setns, file-handle,
142	# bionic...) need no external package: their
143	# requirements are covered by the base packages below.  Features that
144	# perf's own build does not check (libcap, libcheck, libcpupower,
145	# libtracefs, whose feature tests exist for rtla/bpftool/rv) and the
146	# opt-in features, which a default build does not enable: the libbfd
147	# disassembler family (libbfd, libbfd-threadsafe, libbfd-liberty,
148	# disassembler-*, cplus-demangle), only linked on BUILD_NONDISTRO
149	# builds and deprecated in favor of capstone, GTK2, LIBPERL and
150	# LIBUNWIND support (ifdef GTK2 / ifdef LIBPERL / LIBUNWIND=1),
151	# and CoreSight (ifdef CORESIGHT), are deliberately not mapped.
152	# libaio is not mapped either: its
153	# test uses the POSIX AIO API (aio.h, aio_*, -lrt), provided by
154	# glibc headers (pulled in by the glibc-devel base package), not
155	# the native libaio.h/io_submit API that libaio-devel provides.
156}
157
158# ---------------------------------------------------------------------
159# Distro detection
160# ---------------------------------------------------------------------
161detect_distro() {
162	if [ -n "$DISTRO" ]; then
163		echo "$DISTRO"
164		return
165	fi
166	local id
167	id=$( . /etc/os-release 2>/dev/null && echo "${ID:-}" )
168	case "$id" in
169	fedora)			echo "fedora" ;;
170	ubuntu)			echo "ubuntu" ;;
171	# RHEL and its derivatives share most Fedora package names, but the
172	# mapping is only validated on Fedora, so don't auto-detect them.
173	rhel|centos|rocky|alma|ol) echo "" ;;
174	*)			echo "" ;;
175	esac
176}
177
178# ---------------------------------------------------------------------
179# Feature test enumeration: mirror of the tests checked during a build,
180# from the actual test-*.c / test-*.cpp sources in tools/build/feature/.
181# ---------------------------------------------------------------------
182feature_tests() {
183	local srcdir="$1"
184	local f
185	for f in "$srcdir"/tools/build/feature/test-*.c "$srcdir"/tools/build/feature/test-*.cpp; do
186		[ -e "$f" ] || continue
187		basename "$f" | sed -e 's/^test-//' -e 's/\.c$//' -e 's/\.cpp$//'
188	done
189}
190
191# Base packages needed by any build, regardless of feature tests:
192# compiler, libc headers, flex/bison for the parser, kernel headers
193# for UAPI headers with no in-tree copy, e.g. <linux/capability.h>, and
194# gcc-c++ is needed by the C++-based feature tests (cxa-demangle, llvm,
195# llvm-perf), which are compiled with $(CXX), pulling in
196# libstdc++-devel.  python3-setuptools is needed to build the python
197# (perf's util/setup.py uses it); rust is checked by the rust feature
198# test (test-rust.bin just runs "$(RUSTC) --version").
199
200fedora_base_pkgs="gcc gcc-c++ make flex bison glibc-devel kernel-headers python3-setuptools rust"
201debian_base_pkgs="gcc g++ make flex bison libc6-dev linux-libc-dev python3-setuptools rustc"
202
203# ---------------------------------------------------------------------
204# Assemble the unique package list: the base toolchain plus, for each
205# feature test the distro's package mapping knows about, its package(s).
206# Installing an already-present package is a no-op for both dnf and
207# apt-get, making this idempotent.
208# ---------------------------------------------------------------------
209package_set() {
210	local distro="$1" srcdir="$2"
211	local feat pkg pkgs
212
213	case "$distro" in
214	fedora)	pkgs="$fedora_base_pkgs" ;;
215	ubuntu)	pkgs="$debian_base_pkgs" ;;
216	esac
217
218	if [ "$distro" = "fedora" ]; then
219		for feat in $(feature_tests "$srcdir"); do
220			pkg=$(fedora_pkg_for "$feat")
221			for pkg in $pkg; do
222				case " $pkgs " in
223				*" $pkg "*) ;;
224				*) pkgs="$pkgs $pkg" ;;
225				esac
226			done
227		done
228	fi
229	echo "$pkgs"
230}
231
232# ---------------------------------------------------------------------
233# The install command proper for each supported package manager, plus
234# the command massaged for --dry-run.
235# ---------------------------------------------------------------------
236install_cmd() {
237	local distro="$1"; shift
238	case "$distro" in
239	fedora)
240		echo "dnf install -y $*"
241		;;
242	ubuntu)
243		# a fresh container has no package index, so update first.
244		echo "apt-get update && apt-get install -y $*"
245		;;
246	esac
247}
248
249main() {
250	local action="install"
251	local srcdir distro pkgs cmd
252
253	while [ $# -gt 0 ]; do
254		case "$1" in
255		--list)		action="list"; shift ;;
256		--dry-run)	action="dry-run"; shift ;;
257		--distro)
258			[ $# -ge 2 ] || {
259				echo "error: --distro requires an argument (fedora, rhel, ubuntu, debian)" >&2
260				exit 1
261			}
262			DISTRO="$2"; shift 2 ;;
263		-h|--help)	help ;;
264		*)		echo "error: unknown argument: $1" >&2; exit 1 ;;
265		esac
266	done
267
268	srcdir=$(cd "$(dirname "$0")/../../.." && pwd)
269	distro=$(detect_distro)
270	case "$distro" in
271	fedora|ubuntu) ;;
272	*)
273		echo "error: unsupported distro (got '$distro'); the package mapping is not validated on other distros." >&2
274		echo "Supported and validated: Fedora 44 (fresh toolbx container)." >&2
275		exit 1
276		;;
277	esac
278
279	pkgs=$(package_set "$distro" "$srcdir")
280
281	case "$action" in
282	list)
283		echo "$pkgs" | tr ' ' '\n' | grep -v '^$' | sort
284		exit 0
285		;;
286	dry-run)
287		install_cmd "$distro" $pkgs
288		exit 0
289		;;
290	esac
291
292	echo "The following packages will be installed to enable perf features:"
293	echo "$pkgs" | tr ' ' '\n' | grep -v '^$' | sort | sed 's/^/  /'
294	echo
295	cmd=$(install_cmd "$distro" $pkgs)
296	if [ "$(id -u)" -eq 0 ]; then
297		sh -c "$cmd"
298	else
299		sudo sh -c "$cmd"
300	fi || {
301		echo "error: the install command failed, see the output above" >&2
302		exit 1
303	}
304}
305
306main "$@"