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