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
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# No 'set -e': the script uses explicit checks for its error paths, and
# is also run via '$(SHELL) .../install-build-deps.sh' from the make
# target in tools/perf/Makefile.perf, where a shebang option would be
# ignored anyway, so direct and make-driven runs behave the same.
#
# Install the development packages needed to build tools/perf.
#
# The package set is derived from the feature tests in tools/build/feature/:
# each feature test that perf may compile is mapped to the devel package
# that provides the headers/libraries it checks, so that a subsequent
# 'make -C tools/perf' build enables the corresponding perf features.
#
# Currently supported distros, each validated in a fresh container:
#
# - Fedora, on dnf, validated on Fedora 44, in a toolbx container:
#
# toolbox create fedora:44
# toolbox enter fedora:44
#
# - Ubuntu, on apt-get, validated on Ubuntu 26.04, in a distrobox
# container:
#
# distrobox create --image ubuntu:26.04
# distrobox enter ubuntu-26-04
#
# The apt-get install runs with a noninteractive debconf frontend:
# default-jdk, needed by the jvmti feature tests, pulls in tzdata,
# which prompts for the timezone on a terminal and would block the
# install, e.g. in a container shared with an interactive session.
#
# - Debian, on apt-get, reusing the Ubuntu mapping, validated on
# Debian 13 (trixie), in a distrobox container:
#
# distrobox create --image debian:trixie
# distrobox enter debian-trixie
#
# - RHEL and its variants (RHEL, CentOS Stream, Rocky Linux, AlmaLinux,
# Oracle Linux), on dnf (RHEL 8 and later), validated on CentOS
# Stream 10, in a distrobox container:
#
# distrobox create --image quay.io/centos/centos:stream10
# distrobox enter centos-stream10
#
# The RHEL family reuses most Fedora package names, with these
# differences, handled by probing the enabled repos:
#
# - zlib.h comes from zlib-ng-compat-devel on the RHEL 10 family,
# zlib-devel on RHEL 9 and earlier;
# - the JDK devel package is versioned, there is no
# java-latest-openjdk-devel: java-21-openjdk-devel on the RHEL
# 10 family, java-17-openjdk-devel on RHEL 9, java-11-openjdk-
# devel on RHEL 8;
# - libbpf-devel and capstone-devel are in the CRB repo on RHEL
# and CentOS Stream 10 ('dnf config-manager --set-enabled crb')
# and in EPEL on RHEL 9 and earlier;
# - libbabeltrace2-devel is not packaged on the RHEL 10 family.
#
# On the RHEL family, packages not available on the enabled repos
# are skipped and listed at the end of the run, so that a distro
# with CRB/EPEL enabled gets the full set, and a distro without
# them still installs what it can.
#
# Running inside a container keeps the host system unmodified.
#
# Usage: install-build-deps.sh [OPTIONS]
#
# Options:
# --list list the packages that would be installed, then exit
# --dry-run show the install command that would be run, without
# running it
# --distro ID force a distro: fedora, rhel, ubuntu, debian
# (default: auto-detect)
# -h, --help print this help message
#
# Requires root (or passwordless sudo) to actually install packages.
set -u
DISTRO=""
help() {
cat <<EOF
Usage: $(basename "$0") [--list] [--dry-run] [--distro ID] [-h|--help]
Install the development packages needed to build tools/perf.
The package set is derived from the feature tests in tools/build/feature/,
mapping each one with an external dependency to the devel package providing
it, so the corresponding feature gets enabled on a build.
Options:
--list list the packages that would be installed, then exit
--dry-run show the install command that would be run, without running it
--distro ID force a distro: fedora, rhel, ubuntu, debian
-h, --help print this help message
Distros supported: Fedora (dnf), validated in a toolbx container on
Fedora 44, Ubuntu (apt-get), validated in a distrobox container on
Ubuntu 26.04, Debian (apt-get), reusing the Ubuntu mapping, validated
in a distrobox container on Debian 13 (trixie), and the RHEL family
(dnf, RHEL 8 and later), validated on CentOS Stream 10 in a distrobox
container. On the RHEL family, packages not available on the enabled
repos are skipped and listed at the end; some there, e.g.
libbpf-devel and capstone-devel, come from the CRB repo on RHEL and
CentOS Stream 10 ('dnf config-manager --set-enabled crb') and from
EPEL on RHEL 9 and earlier.
EOF
exit 0
}
# ---------------------------------------------------------------------
# Return the Fedora package(s) providing the devel requirements of a
# feature test in tools/build/feature/. Multiple packages are separated
# by spaces; an empty result means the test has no external devel
# dependency (pure toolchain/glibc, or no equivalent Fedora package).
#
# The mapping was built by checking, for each feature test, which Fedora
# devel package provides the headers the test compiles against.
# ---------------------------------------------------------------------
fedora_pkg_for() {
local feat="$1"
case "$feat" in
libelf|libelf-getphdrnum|libelf-gelf_getnote|libelf-getshdrstrndx)
echo "elfutils-libelf-devel"
;;
libelf-zstd)
echo "elfutils-libelf-devel libzstd-devel"
;;
libdw)
echo "elfutils-devel"
;;
# test-libdebuginfod.c includes <elfutils/debuginfod.h>, which is
# provided by elfutils-debuginfod-client-devel, not elfutils-devel.
libdebuginfod)
echo "elfutils-debuginfod-client-devel"
;;
libnuma|numa_num_possible_cpus)
echo "numactl-devel"
;;
libzstd)
echo "libzstd-devel"
;;
zlib)
echo "zlib-devel"
;;
lzma)
echo "xz-devel"
;;
libslang)
echo "slang-devel"
;;
libcapstone)
echo "capstone-devel"
;;
libpython)
echo "python3-devel"
;;
libtraceevent)
echo "libtraceevent-devel"
;;
cxa-demangle)
echo "libstdc++-devel"
;;
libbpf)
echo "libbpf-devel"
;;
babeltrace2-ctf-writer)
echo "libbabeltrace2-devel"
;;
libopenssl)
echo "openssl-devel"
;;
libpfm4)
echo "libpfm-devel"
;;
sdt)
echo "systemtap-sdt-devel"
;;
clang-bpf-co-re)
echo "clang-devel"
;;
llvm|llvm-perf)
echo "llvm-devel"
;;
jvmti|jvmti-cmlr)
echo "java-latest-openjdk-devel"
;;
esac
# Pure toolchain/libc features (backtrace, eventfd, fortify-source,
# gettid, glibc, hello, reallocarray, pthread-*, stackprotector-all,
# timerfd, scandirat, sched_getcpu, setns, file-handle,
# bionic...) need no external package: their
# requirements are covered by the base packages below. Features that
# perf's own build does not check (libcap, libcheck, libcpupower,
# libtracefs, whose feature tests exist for rtla/bpftool/rv) and the
# opt-in features, which a default build does not enable: the libbfd
# disassembler family (libbfd, libbfd-threadsafe, libbfd-liberty,
# disassembler-*, cplus-demangle), only linked on BUILD_NONDISTRO
# builds and deprecated in favor of capstone, GTK2, LIBPERL and
# LIBUNWIND support (ifdef GTK2 / ifdef LIBPERL / LIBUNWIND=1),
# and CoreSight (ifdef CORESIGHT), are deliberately not mapped.
# libaio is not mapped either: its
# test uses the POSIX AIO API (aio.h, aio_*, -lrt), provided by
# glibc headers (pulled in by the libc headers package) in the
# base set, not the native libaio.h/io_submit API that
# libaio-devel provides.
}
# ---------------------------------------------------------------------
# Check whether the first argument is provided by the enabled repos, or
# is already installed, returning 0 if so and printing nothing.
#
# dnf repoquery exits 0 even when the pattern does not match anything,
# so availability is judged by the output being non-empty, not by the
# exit code.
# ---------------------------------------------------------------------
pkg_available() {
local p out
for p in "$@"; do
out=$(dnf repoquery --quiet --available "$p" 2>/dev/null)
[ -n "$out" ] || out=$(dnf repoquery --quiet --installed "$p" 2>/dev/null)
[ -n "$out" ] && return 0
done
return 1
}
# ---------------------------------------------------------------------
# Return the first of its arguments that pkg_available() accepts.
# ---------------------------------------------------------------------
pick_available() {
local p
for p in "$@"; do
if pkg_available "$p"; then
echo "$p"
return 0
fi
done
return 1
}
# ---------------------------------------------------------------------
# Return the RHEL-family package(s) providing the devel requirements of
# a feature test. This family reuses the Fedora mapping, whose names
# apply to it as well, except for the features whose Fedora package
# does not exist there, kept in the case below.
#
# Whether the resulting package is actually installable is checked in
# package_set(), as some of these names live in the CRB or EPEL repos,
# which may not be enabled: on the RHEL 10 family libbpf-devel and
# capstone-devel come from CRB, and libbabeltrace2-devel is not
# packaged at all.
# ---------------------------------------------------------------------
rhel_pkg_for() {
local feat="$1"
case "$feat" in
zlib)
# zlib-ng-compat-devel on the RHEL 10 family, zlib-devel on
# RHEL 9 and earlier. If neither is on the enabled repos,
# fall back to the first candidate so that package_set()
# reports it as missing instead of silently dropping the
# feature from the end-of-run note.
pick_available zlib-devel zlib-ng-compat-devel || echo "zlib-devel"
;;
jvmti|jvmti-cmlr)
# No java-latest-openjdk-devel on the RHEL family: the JDK
# devel package is versioned per release.
pick_available java-latest-openjdk-devel java-21-openjdk-devel java-17-openjdk-devel java-11-openjdk-devel || echo "java-latest-openjdk-devel"
;;
*)
fedora_pkg_for "$feat"
;;
esac
}
# ---------------------------------------------------------------------
# Return the Debian/Ubuntu package(s) providing the devel requirements
# of a feature test in tools/build/feature/. This mapping is shared by
# Debian and Ubuntu, whose package names for these devel packages match,
# and is validated on Ubuntu 26.04 and Debian 13 (trixie).
# ---------------------------------------------------------------------
debian_pkg_for() {
local feat="$1"
case "$feat" in
libelf|libelf-getphdrnum|libelf-gelf_getnote|libelf-getshdrstrndx)
echo "libelf-dev"
;;
libelf-zstd)
echo "libelf-dev libzstd-dev"
;;
libdw)
echo "libdw-dev"
;;
libdebuginfod)
echo "libdebuginfod-dev"
;;
libnuma|numa_num_possible_cpus)
echo "libnuma-dev"
;;
libzstd)
echo "libzstd-dev"
;;
zlib)
echo "zlib1g-dev"
;;
lzma)
echo "liblzma-dev"
;;
libslang)
echo "libslang2-dev"
;;
libcapstone)
echo "libcapstone-dev"
;;
libpython)
echo "python3-dev"
;;
libtraceevent)
echo "libtraceevent-dev"
;;
# The cxa-demangle feature test links against libstdc++ builtin
# demangling, so on Debian-derived distros it is covered by the
# g++ base package below, which pulls in libstdc++-*-dev.
cxa-demangle)
;;
libbpf)
echo "libbpf-dev"
;;
babeltrace2-ctf-writer)
echo "libbabeltrace2-dev"
;;
libopenssl)
echo "libssl-dev"
;;
libpfm4)
echo "libpfm4-dev"
;;
sdt)
echo "systemtap-sdt-dev"
;;
# test-clang-bpf-co-re.c invokes the clang binary (not libclang-cpp),
# so clang suffices; llvm-dev brings llvm-config, needed by the
# llvm/llvm-perf tests below.
clang-bpf-co-re)
echo "clang llvm-dev"
;;
llvm|llvm-perf)
echo "llvm-dev"
;;
jvmti|jvmti-cmlr)
echo "default-jdk"
;;
esac
# Same rationale as in fedora_pkg_for() above for unmapped tests.
}
# Base packages needed by any perf build, regardless of feature tests:
# compiler, libc headers, flex/bison for the parser, kernel headers
# for UAPI headers with no in-tree copy, e.g. <linux/capability.h>, and
# gcc-c++ (dnf) / g++ (apt) is needed by the C++-based feature tests
# (cxa-demangle, llvm, llvm-perf), compiled with $(CXX), and
# pulls in libstdc++-devel / libstdc++-*-dev.
# python3-setuptools is needed to build the python binding (perf's
# util/setup.py uses it; without it binding is skipped with a warning).
# rust is not a header-based feature test: test-rust.bin just checks
# "$(RUSTC) --version" (tools/build/feature/Makefile), so it is mapped
# here like the other toolchain packages.
fedora_base_pkgs="gcc gcc-c++ make flex bison glibc-devel kernel-headers python3-setuptools rust"
# pkg-config mirrors the pkgconf-pkg-config package that Fedora
# installs by default, needed by the babeltrace2 feature test and
# libopenssl's pkg-config checks.
debian_base_pkgs="gcc g++ make pkg-config flex bison libc6-dev linux-libc-dev python3-setuptools rustc"
# pkg-config: like Fedora, on pkgconf since Fedora 26 / RHEL 8, with
# 'pkgconfig' live only as a virtual Provides of the pkgconf-pkg-config
# subpackage. List it explicitly because, unlike Fedora, a minimal
# RHEL-family container may not have it preinstalled.
rhel_base_pkgs="gcc gcc-c++ make flex bison glibc-devel kernel-headers pkgconf-pkg-config python3-setuptools rust"
# ---------------------------------------------------------------------
# Distro detection
# ---------------------------------------------------------------------
detect_distro() {
if [ -n "$DISTRO" ]; then
echo "$DISTRO"
return
fi
local id
id=$( . /etc/os-release 2>/dev/null && echo "${ID:-}" )
case "$id" in
fedora) echo "fedora" ;;
rhel|centos|rocky|almalinux|ol)
# All share the Fedora package names (with the exceptions
# handled by rhel_pkg_for()) and dnf.
echo "rhel" ;;
ubuntu) echo "ubuntu" ;;
debian) echo "debian" ;;
*) echo "" ;;
esac
}
# ---------------------------------------------------------------------
# Feature test enumeration: mirror of the tests checked during a build,
# from the actual test-*.c / test-*.cpp sources in tools/build/feature/.
# ---------------------------------------------------------------------
feature_tests() {
local srcdir="$1"
local f
for f in "$srcdir"/tools/build/feature/test-*.c "$srcdir"/tools/build/feature/test-*.cpp; do
[ -e "$f" ] || continue
basename "$f" | sed -e 's/^test-//' -e 's/\.c$//' -e 's/\.cpp$//'
done
}
# ---------------------------------------------------------------------
# Assemble the unique package list. Since the full feature set is mapped
# unconditionally, this installs the complete devel environment; installing
# an already-present package is a no-op for both dnf and apt-get, making
# this idempotent. On the RHEL family, packages not available on the
# enabled repos are skipped and echoed on stderr, reported at the end of
# the run by the caller (package_set() runs in a subshell, so a variable
# would not survive it).
# ---------------------------------------------------------------------
package_set() {
local distro="$1" srcdir="$2"
local feat pkg pkgs base_pkgs missing_pkgs=""
case "$distro" in
fedora) pkgs="$fedora_base_pkgs" ;;
rhel) pkgs="$rhel_base_pkgs" ;;
ubuntu|debian) pkgs="$debian_base_pkgs" ;;
esac
# The base set must be checked against the enabled repos as well:
# e.g. 'rust' is only shipped as the rust-toolset AppStream module
# on RHEL 8 and 9, where dnf would abort the whole transaction
# with "No match for argument: rust" instead of skipping it, so
# unavailable base packages are skipped and noted like the mapped
# ones below.
if [ "$distro" = "rhel" ]; then
base_pkgs="$pkgs"
pkgs=""
for pkg in $base_pkgs; do
if pkg_available "$pkg"; then
pkgs="$pkgs $pkg"
else
missing_pkgs="$missing_pkgs $pkg"
fi
done
fi
for feat in $(feature_tests "$srcdir"); do
case "$distro" in
fedora) pkg=$(fedora_pkg_for "$feat") ;;
rhel) pkg=$(rhel_pkg_for "$feat") ;;
*) pkg=$(debian_pkg_for "$feat") ;;
esac
# An empty result means the feature test has no external devel
# dependency (see the unmapped list in fedora_pkg_for()), so
# there is nothing to install and nothing to report as missing:
# packages that are genuinely unavailable on the RHEL family
# surface through rhel_pkg_for() falling back to its first
# candidate, so that the pkg_available() check below catches
# them and adds them to missing_pkgs.
[ -n "$pkg" ] || continue
for pkg in $pkg; do
if [ "$distro" = "rhel" ] && ! pkg_available "$pkg"; then
missing_pkgs="$missing_pkgs $pkg"
continue
fi
case " $pkgs " in
*" $pkg "*) ;;
*) pkgs="$pkgs $pkg" ;;
esac
done
done
[ -n "$missing_pkgs" ] && echo "$missing_pkgs" >&2
echo "$pkgs"
}
# ---------------------------------------------------------------------
# Report the RHEL-family packages that were skipped because the enabled
# repos do not provide them.
# ---------------------------------------------------------------------
note_missing() {
[ -n "$1" ] || return 0
echo "Note: these packages are not available on the enabled repos" >&2
echo "and were not installed, so the features they enable will stay" >&2
echo "off in a perf build:" >&2
echo "$1" | tr ' ' '\n' | grep -v '^$' | sort -u | sed 's/^/ /' >&2
echo >&2
}
# ---------------------------------------------------------------------
# The install command proper for each supported package manager, plus
# the command massaged for --dry-run.
# ---------------------------------------------------------------------
install_cmd() {
local distro="$1"; shift
case "$distro" in
fedora|rhel)
echo "dnf install -y $*"
;;
ubuntu|debian)
# a fresh container has no package index, so update first; run
# with a noninteractive debconf frontend: default-jdk, used by
# the jvmti feature tests, pulls in tzdata, which prompts for
# the timezone on a terminal and would block the install. The
# env var prefix works with the 'sh -c' invocation below, and
# shows up in --dry-run.
echo "DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y $*"
;;
esac
}
main() {
local action="install"
# Package accumulation happens in package_set(), which, together
# with the *_pkg_for() helpers, declares the per-feature 'pkg'
# locals; main() holds no per-feature package state, just the
# distro-wide list assembled by package_set().
local srcdir distro pkgs cmd missing_file missing_pkgs
while [ $# -gt 0 ]; do
case "$1" in
--list) action="list"; shift ;;
--dry-run) action="dry-run"; shift ;;
--distro)
[ $# -ge 2 ] || {
echo "error: --distro requires an argument (fedora, rhel, ubuntu, debian)" >&2
exit 1
}
DISTRO="$2"; shift 2 ;;
-h|--help) help ;;
*) echo "error: unknown argument: $1" >&2; exit 1 ;;
esac
done
srcdir=$(cd "$(dirname "$0")/../../.." && pwd)
distro=$(detect_distro)
case "$distro" in
fedora|rhel|ubuntu|debian) ;;
*)
echo "error: unsupported distro (got '$distro'); the package mapping is not validated on other distros." >&2
echo "Supported and validated: Fedora 44 (toolbx container), Ubuntu 26.04 and Debian 13 (distrobox containers), and CentOS Stream 10 (distrobox container)." >&2
exit 1
;;
esac
# The RHEL family mapping probes the enabled repos with dnf
# repoquery and installs with dnf, so reject the yum-only members of
# the family (RHEL 7 and earlier, e.g. Oracle Linux 7) with a clear
# error instead of silently judging every package as unavailable.
if [ "$distro" = "rhel" ] && ! command -v dnf >/dev/null 2>&1; then
echo "error: dnf not found, the RHEL family package mapping requires dnf (RHEL 8 and later)." >&2
exit 1
fi
missing_file=$(mktemp) || {
echo "error: cannot create a temporary file (mktemp failed)" >&2
exit 1
}
# package_set() runs in a subshell, doing dnf repoquery loops that
# may be interrupted: a Ctrl-C during those loops hits these traps,
# turning SIGINT/SIGTERM into an exit that triggers the EXIT trap,
# removing the temp file, so the script terminates instead of
# resuming after the interrupted command, leaving no file behind.
trap 'rm -f "$missing_file"' EXIT
trap 'exit 1' INT TERM
pkgs=$(package_set "$distro" "$srcdir" 2>"$missing_file")
missing_pkgs=$(cat "$missing_file")
rm -f "$missing_file"
trap - EXIT INT TERM
case "$action" in
list)
echo "$pkgs" | tr ' ' '\n' | grep -v '^$' | sort
note_missing "$missing_pkgs"
exit 0
;;
dry-run)
install_cmd "$distro" $pkgs
note_missing "$missing_pkgs"
exit 0
;;
esac
echo "The following packages will be installed to enable perf features:"
echo "$pkgs" | tr ' ' '\n' | grep -v '^$' | sort | sed 's/^/ /'
echo
cmd=$(install_cmd "$distro" $pkgs)
if [ "$(id -u)" -eq 0 ]; then
sh -c "$cmd"
else
sudo sh -c "$cmd"
fi || {
echo "error: the install command failed, see the output above" >&2
exit 1
}
note_missing "$missing_pkgs"
}
main "$@"
|