xref: /linux/tools/perf/scripts/install-build-deps.sh (revision 1331bb51069bd9ddde63a22d493d773801ea3bfc)
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# Currently supported distros, each validated in a fresh container:
17#
18#   - Fedora, on dnf, validated on Fedora 44, in a toolbx container:
19#
20#           toolbox create fedora:44
21#           toolbox enter fedora:44
22#
23#   - Ubuntu, on apt-get, validated on Ubuntu 26.04, in a distrobox
24#     container:
25#
26#           distrobox create --image ubuntu:26.04
27#           distrobox enter ubuntu-26-04
28#
29#     The apt-get install runs with a noninteractive debconf frontend:
30#     default-jdk, needed by the jvmti feature tests, pulls in tzdata,
31#     which prompts for the timezone on a terminal and would block the
32#     install, e.g. in a container shared with an interactive session.
33#
34# Running inside a container keeps the host system unmodified.
35#
36# Debian, which shares the Ubuntu package mapping, is the next planned
37# distro to be enabled once that mapping is validated on a Debian
38# release (trixie), as is RHEL support, once its (largely similar to
39# Fedora) package mapping is validated there.
40#
41# Usage: install-build-deps.sh [OPTIONS]
42#
43# Options:
44#    --list       list the packages that would be installed, then exit
45#    --dry-run    show the install command that would be run, without
46#                 running it
47#    --distro ID  force a distro: fedora, ubuntu (default: auto-detect)
48#    -h, --help   print this help message
49#
50# Requires root (or passwordless sudo) to actually install packages.
51
52set -u
53
54DISTRO=""
55
56help() {
57	cat <<EOF
58Usage: $(basename "$0") [--list] [--dry-run] [--distro ID] [-h|--help]
59
60Install the development packages needed to build tools/perf.
61
62The package set is derived from the feature tests in tools/build/feature/,
63mapping each one with an external dependency to the devel package providing
64it, so the corresponding feature gets enabled on a build.
65
66Options:
67    --list      list the packages that would be installed, then exit
68    --dry-run   show the install command that would be run, without running it
69    --distro ID force a distro: fedora, ubuntu
70    -h, --help  print this help message
71
72Distros supported: Fedora (dnf), validated in a toolbx container on
73Fedora 44, and Ubuntu (apt-get), validated in a distrobox container on
74Ubuntu 26.04.
75EOF
76	exit 0
77}
78
79# ---------------------------------------------------------------------
80# Return the Fedora package(s) providing the devel requirements of a
81# feature test in tools/build/feature/.  Multiple packages are separated
82# by spaces; an empty result means the test has no external devel
83# dependency (pure toolchain/glibc, or no equivalent Fedora package).
84#
85# The mapping was built by checking, for each feature test, which Fedora
86# devel package provides the headers the test compiles against.
87# ---------------------------------------------------------------------
88fedora_pkg_for() {
89	local feat="$1"
90	case "$feat" in
91	libelf|libelf-getphdrnum|libelf-gelf_getnote|libelf-getshdrstrndx)
92		echo "elfutils-libelf-devel"
93		;;
94	libelf-zstd)
95		echo "elfutils-libelf-devel libzstd-devel"
96		;;
97	libdw)
98		echo "elfutils-devel"
99		;;
100	# test-libdebuginfod.c includes <elfutils/debuginfod.h>, which is
101	# provided by elfutils-debuginfod-client-devel, not elfutils-devel.
102	libdebuginfod)
103		echo "elfutils-debuginfod-client-devel"
104		;;
105	libnuma|numa_num_possible_cpus)
106		echo "numactl-devel"
107		;;
108	libzstd)
109		echo "libzstd-devel"
110		;;
111	zlib)
112		echo "zlib-devel"
113		;;
114	lzma)
115		echo "xz-devel"
116		;;
117	libslang)
118		echo "slang-devel"
119		;;
120	libcapstone)
121		echo "capstone-devel"
122		;;
123	libpython)
124		echo "python3-devel"
125		;;
126	libtraceevent)
127		echo "libtraceevent-devel"
128		;;
129	cxa-demangle)
130		echo "libstdc++-devel"
131		;;
132	libbpf)
133		echo "libbpf-devel"
134		;;
135	babeltrace2-ctf-writer)
136		echo "libbabeltrace2-devel"
137		;;
138	libopenssl)
139		echo "openssl-devel"
140		;;
141	libpfm4)
142		echo "libpfm-devel"
143		;;
144	sdt)
145		echo "systemtap-sdt-devel"
146		;;
147	clang-bpf-co-re)
148		echo "clang-devel"
149		;;
150	llvm|llvm-perf)
151		echo "llvm-devel"
152		;;
153	jvmti|jvmti-cmlr)
154		echo "java-latest-openjdk-devel"
155		;;
156	esac
157	# Pure toolchain/libc features (backtrace, eventfd, fortify-source,
158	# gettid, glibc, hello, reallocarray, pthread-*, stackprotector-all,
159	# timerfd, scandirat, sched_getcpu, setns, file-handle,
160	# bionic...) need no external package: their
161	# requirements are covered by the base packages below.  Features that
162	# perf's own build does not check (libcap, libcheck, libcpupower,
163	# libtracefs, whose feature tests exist for rtla/bpftool/rv) and the
164	# opt-in features, which a default build does not enable: the libbfd
165	# disassembler family (libbfd, libbfd-threadsafe, libbfd-liberty,
166	# disassembler-*, cplus-demangle), only linked on BUILD_NONDISTRO
167	# builds and deprecated in favor of capstone, GTK2, LIBPERL and
168	# LIBUNWIND support (ifdef GTK2 / ifdef LIBPERL / LIBUNWIND=1),
169	# and CoreSight (ifdef CORESIGHT), are deliberately not mapped.
170	# libaio is not mapped either: its
171	# test uses the POSIX AIO API (aio.h, aio_*, -lrt), provided by
172	# glibc headers (pulled in by the libc headers package) in the
173	# base set, not the native libaio.h/io_submit API that
174	# libaio-devel provides.
175}
176
177# ---------------------------------------------------------------------
178# Return the Debian/Ubuntu package(s) providing the devel requirements
179# of a feature test in tools/build/feature/.  This mapping is shared by
180# Debian and Ubuntu, whose package names for these devel packages match,
181# and is validated on Ubuntu 26.04; Debian (trixie) will be enabled
182# once the same mapping is validated on it.
183# ---------------------------------------------------------------------
184debian_pkg_for() {
185	local feat="$1"
186	case "$feat" in
187	libelf|libelf-getphdrnum|libelf-gelf_getnote|libelf-getshdrstrndx)
188		echo "libelf-dev"
189		;;
190	libelf-zstd)
191		echo "libelf-dev libzstd-dev"
192		;;
193	libdw)
194		echo "libdw-dev"
195		;;
196	libdebuginfod)
197		echo "libdebuginfod-dev"
198		;;
199	libnuma|numa_num_possible_cpus)
200		echo "libnuma-dev"
201		;;
202	libzstd)
203		echo "libzstd-dev"
204		;;
205	zlib)
206		echo "zlib1g-dev"
207		;;
208	lzma)
209		echo "liblzma-dev"
210		;;
211	libslang)
212		echo "libslang2-dev"
213		;;
214	libcapstone)
215		echo "libcapstone-dev"
216		;;
217	libpython)
218		echo "python3-dev"
219		;;
220	libtraceevent)
221		echo "libtraceevent-dev"
222		;;
223	# The cxa-demangle feature test links against libstdc++ builtin
224	# demangling, so on Debian-derived distros it is covered by the
225	# g++ base package below, which pulls in libstdc++-*-dev.
226	cxa-demangle)
227		;;
228	libbpf)
229		echo "libbpf-dev"
230		;;
231	babeltrace2-ctf-writer)
232		echo "libbabeltrace2-dev"
233		;;
234	libopenssl)
235		echo "libssl-dev"
236		;;
237	libpfm4)
238		echo "libpfm4-dev"
239		;;
240	sdt)
241		echo "systemtap-sdt-dev"
242		;;
243	# test-clang-bpf-co-re.c invokes the clang binary (not libclang-cpp),
244	# so clang suffices; llvm-dev brings llvm-config, needed by the
245	# llvm/llvm-perf tests below.
246	clang-bpf-co-re)
247		echo "clang llvm-dev"
248		;;
249	llvm|llvm-perf)
250		echo "llvm-dev"
251		;;
252	jvmti|jvmti-cmlr)
253		echo "default-jdk"
254		;;
255	esac
256	# Same rationale as in fedora_pkg_for() above for unmapped tests.
257}
258
259# Base packages needed by any perf build, regardless of feature tests:
260# compiler, libc headers, flex/bison for the parser, kernel headers
261# for UAPI headers with no in-tree copy, e.g. <linux/capability.h>, and
262# gcc-c++ (dnf) / g++ (apt) is needed by the C++-based feature tests
263# (cxa-demangle, llvm, llvm-perf), compiled with $(CXX), and
264# pulls in libstdc++-devel / libstdc++-*-dev.
265# python3-setuptools is needed to build the python binding (perf's
266# util/setup.py uses it; without it binding is skipped with a warning).
267# rust is not a header-based feature test: test-rust.bin just checks
268# "$(RUSTC) --version" (tools/build/feature/Makefile), so it is mapped
269# here like the other toolchain packages.
270fedora_base_pkgs="gcc gcc-c++ make flex bison glibc-devel kernel-headers python3-setuptools rust"
271# pkg-config mirrors the pkgconf-pkg-config package that Fedora
272# installs by default, needed by the babeltrace2 feature test and
273# libopenssl's pkg-config checks.
274debian_base_pkgs="gcc g++ make pkg-config flex bison libc6-dev linux-libc-dev python3-setuptools rustc"
275
276# ---------------------------------------------------------------------
277# Distro detection
278# ---------------------------------------------------------------------
279detect_distro() {
280	if [ -n "$DISTRO" ]; then
281		echo "$DISTRO"
282		return
283	fi
284	local id
285	id=$( . /etc/os-release 2>/dev/null && echo "${ID:-}" )
286	case "$id" in
287	fedora)			echo "fedora" ;;
288	ubuntu)			echo "ubuntu" ;;
289	# Debian shares the Ubuntu package mapping, but it is only validated
290	# on Ubuntu so far, so don't auto-detect it yet.
291	debian)			echo "" ;;
292	# RHEL and its derivatives share most Fedora package names, but the
293	# mapping is only validated on Fedora, so don't auto-detect them.
294	rhel|centos|rocky|alma|ol) echo "" ;;
295	*)			echo "" ;;
296	esac
297}
298
299# ---------------------------------------------------------------------
300# Feature test enumeration: mirror of the tests checked during a build,
301# from the actual test-*.c / test-*.cpp sources in tools/build/feature/.
302# ---------------------------------------------------------------------
303feature_tests() {
304	local srcdir="$1"
305	local f
306	for f in "$srcdir"/tools/build/feature/test-*.c "$srcdir"/tools/build/feature/test-*.cpp; do
307		[ -e "$f" ] || continue
308		basename "$f" | sed -e 's/^test-//' -e 's/\.c$//' -e 's/\.cpp$//'
309	done
310}
311
312# ---------------------------------------------------------------------
313# Assemble the unique package list.  Since the full feature set is mapped
314# unconditionally, this installs the complete devel environment; installing
315# an already-present package is a no-op for both dnf and apt-get, making
316# this idempotent.
317# ---------------------------------------------------------------------
318package_set() {
319	local distro="$1" srcdir="$2"
320	local feat pkg pkgs
321	case "$distro" in
322	fedora)	pkgs="$fedora_base_pkgs" ;;
323	ubuntu)	pkgs="$debian_base_pkgs" ;;
324	esac
325
326	for feat in $(feature_tests "$srcdir"); do
327		if [ "$distro" = "fedora" ]; then
328			pkg=$(fedora_pkg_for "$feat")
329		else
330			pkg=$(debian_pkg_for "$feat")
331		fi
332		[ -n "$pkg" ] || continue
333		for pkg in $pkg; do
334			case " $pkgs " in
335			*" $pkg "*) ;;
336			*) pkgs="$pkgs $pkg" ;;
337			esac
338		done
339	done
340	echo "$pkgs"
341}
342
343# ---------------------------------------------------------------------
344# The install command proper for each supported package manager, plus
345# the command massaged for --dry-run.
346# ---------------------------------------------------------------------
347install_cmd() {
348	local distro="$1"; shift
349	case "$distro" in
350	fedora)
351		echo "dnf install -y $*"
352		;;
353	ubuntu)
354		# a fresh container has no package index, so update first; run
355		# with a noninteractive debconf frontend: default-jdk, used by
356		# the jvmti feature tests, pulls in tzdata, which prompts for
357		# the timezone on a terminal and would block the install.  The
358		# env var prefix works with the 'sh -c' invocation below, and
359		# shows up in --dry-run.
360		echo "DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y $*"
361		;;
362	esac
363}
364
365main() {
366	local action="install"
367	# Package accumulation happens in package_set(), which, together
368	# with the *_pkg_for() helpers, declares the per-feature 'pkg'
369	# locals; main() holds no per-feature package state, just the
370	# distro-wide list assembled by package_set().
371	local srcdir distro pkgs cmd
372
373	while [ $# -gt 0 ]; do
374		case "$1" in
375		--list)		action="list"; shift ;;
376		--dry-run)	action="dry-run"; shift ;;
377		--distro)
378			[ $# -ge 2 ] || {
379				echo "error: --distro requires an argument (fedora, rhel, ubuntu, debian)" >&2
380				exit 1
381			}
382			DISTRO="$2"; shift 2 ;;
383		-h|--help)	help ;;
384		*)		echo "error: unknown argument: $1" >&2; exit 1 ;;
385		esac
386	done
387
388	srcdir=$(cd "$(dirname "$0")/../../.." && pwd)
389	distro=$(detect_distro)
390	case "$distro" in
391	fedora|ubuntu) ;;
392	*)
393		echo "error: unsupported distro (got '$distro'); the package mapping is not validated on other distros." >&2
394		echo "Supported and validated: Fedora 44 (toolbx container), Ubuntu 26.04 (distrobox container)." >&2
395		exit 1
396		;;
397	esac
398
399	pkgs=$(package_set "$distro" "$srcdir")
400
401	case "$action" in
402	list)
403		echo "$pkgs" | tr ' ' '\n' | grep -v '^$' | sort
404		exit 0
405		;;
406	dry-run)
407		install_cmd "$distro" $pkgs
408		exit 0
409		;;
410	esac
411
412	echo "The following packages will be installed to enable perf features:"
413	echo "$pkgs" | tr ' ' '\n' | grep -v '^$' | sort | sed 's/^/  /'
414	echo
415	cmd=$(install_cmd "$distro" $pkgs)
416	if [ "$(id -u)" -eq 0 ]; then
417		sh -c "$cmd"
418	else
419		sudo sh -c "$cmd"
420	fi || {
421		echo "error: the install command failed, see the output above" >&2
422		exit 1
423	}
424}
425
426main "$@"
427