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