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 "$@"