]>
| Commit | Line | Data |
|---|---|---|
| 1 | #!/bin/bash | |
| 2 | # | |
| 3 | # setup-linux.sh -- install everything needed to build rsync on Ubuntu. | |
| 4 | # | |
| 5 | # Tested on Ubuntu 22.04.5 LTS and 26.04 LTS. Covers both build systems: | |
| 6 | # the autoconf one (./configure && make) and the CMake one (CMakeLists.txt), | |
| 7 | # plus the optional feature libraries and the manpage generator. | |
| 8 | # | |
| 9 | # Usage: | |
| 10 | # ./setup-linux.sh # install required + optional | |
| 11 | # ./setup-linux.sh --minimal # required only (no ACLs/xattrs/zstd/...) | |
| 12 | # ./setup-linux.sh --dry-run # show what would be installed | |
| 13 | # | |
| 14 | # Safe to re-run: apt skips anything already present. | |
| 15 | ||
| 16 | set -uo pipefail | |
| 17 | ||
| 18 | MINIMAL=0 | |
| 19 | DRY_RUN=0 | |
| 20 | ||
| 21 | for arg in "$@"; do | |
| 22 | case "$arg" in | |
| 23 | --minimal) MINIMAL=1 ;; | |
| 24 | --dry-run) DRY_RUN=1 ;; | |
| 25 | -h|--help) | |
| 26 | sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//' | |
| 27 | exit 0 ;; | |
| 28 | *) | |
| 29 | echo "setup-linux.sh: unknown option '$arg' (try --help)" >&2 | |
| 30 | exit 2 ;; | |
| 31 | esac | |
| 32 | done | |
| 33 | ||
| 34 | # ---------------------------------------------------------------- helpers | |
| 35 | ||
| 36 | red() { printf '\033[31m%s\033[0m\n' "$*"; } | |
| 37 | green() { printf '\033[32m%s\033[0m\n' "$*"; } | |
| 38 | bold() { printf '\033[1m%s\033[0m\n' "$*"; } | |
| 39 | ||
| 40 | die() { red "ERROR: $*"; exit 1; } | |
| 41 | ||
| 42 | # ------------------------------------------------------- sanity checks | |
| 43 | ||
| 44 | command -v apt-get >/dev/null 2>&1 \ | |
| 45 | || die "no apt-get found -- this script is for Debian/Ubuntu systems." | |
| 46 | ||
| 47 | DISTRO="unknown" | |
| 48 | RELEASE="unknown" | |
| 49 | if [ -r /etc/os-release ]; then | |
| 50 | # shellcheck disable=SC1091 # runtime file, not available to the linter | |
| 51 | . /etc/os-release | |
| 52 | DISTRO="${ID:-unknown}" | |
| 53 | RELEASE="${VERSION_ID:-unknown}" | |
| 54 | fi | |
| 55 | ||
| 56 | bold "rsync build dependencies" | |
| 57 | echo " distro ......... ${PRETTY_NAME:-$DISTRO $RELEASE}" | |
| 58 | echo " architecture ... $(uname -m)" | |
| 59 | ||
| 60 | case "$DISTRO" in | |
| 61 | ubuntu|debian|linuxmint|pop) ;; | |
| 62 | *) echo | |
| 63 | red "Warning: this script targets Ubuntu/Debian; '$DISTRO' is untested." | |
| 64 | echo "Continuing anyway -- package names may differ." | |
| 65 | ;; | |
| 66 | esac | |
| 67 | ||
| 68 | # apt needs root. Re-exec under sudo rather than sprinkling it around, so | |
| 69 | # that a single password prompt covers the whole run. | |
| 70 | if [ "$(id -u)" -ne 0 ] && [ "$DRY_RUN" -eq 0 ]; then | |
| 71 | command -v sudo >/dev/null 2>&1 \ | |
| 72 | || die "not running as root and sudo is not installed." | |
| 73 | echo | |
| 74 | echo "Re-running under sudo..." | |
| 75 | # Plain sudo, not "sudo -E": some sudoers configs refuse to preserve the | |
| 76 | # environment, and the exports below happen in the root instance anyway. | |
| 77 | exec sudo "$0" "$@" | |
| 78 | fi | |
| 79 | ||
| 80 | export DEBIAN_FRONTEND=noninteractive | |
| 81 | export NEEDRESTART_MODE=a # don't prompt about restarting services | |
| 82 | ||
| 83 | # ------------------------------------------------------------- packages | |
| 84 | ||
| 85 | # Needed to build rsync at all. | |
| 86 | REQUIRED=( | |
| 87 | build-essential # gcc, g++, make, libc headers | |
| 88 | gawk # rsync needs a modern awk for its code generators | |
| 89 | autoconf # ./configure is generated from configure.ac | |
| 90 | automake | |
| 91 | python3 # manpage + header generators, and the test suite | |
| 92 | cmake # the CMake build | |
| 93 | ninja-build # ...and its default generator | |
| 94 | git # version stamping (git-version.h) | |
| 95 | pkg-config | |
| 96 | ) | |
| 97 | ||
| 98 | # Optional: each one switches on an rsync feature. Missing ones only mean | |
| 99 | # a smaller feature set, never a failed build. | |
| 100 | OPTIONAL=( | |
| 101 | acl libacl1-dev # --acls (helper tools also used by the tests) | |
| 102 | attr libattr1-dev # --xattrs (likewise) | |
| 103 | libxxhash-dev # xxhash checksums (becomes the default) | |
| 104 | libzstd-dev # zstd compression (becomes the default) | |
| 105 | liblz4-dev # lz4 compression | |
| 106 | libssl-dev # OpenSSL MD4/MD5 | |
| 107 | zlib1g-dev # for cmake -DRSYNC_EXTERNAL_ZLIB=ON | |
| 108 | libpopt-dev # for ./configure --with-included-popt=no | |
| 109 | ) | |
| 110 | ||
| 111 | # Development tooling: not needed to build rsync, but useful when working on | |
| 112 | # its shell scripts -- including this one, which shellcheck keeps honest. | |
| 113 | DEVTOOLS=( | |
| 114 | shellcheck | |
| 115 | ) | |
| 116 | OPTIONAL+=("${DEVTOOLS[@]}") | |
| 117 | ||
| 118 | # The manpages need one of two python3 markdown libraries; upstream prefers | |
| 119 | # cmarkgfm. Pick whichever this release actually offers. | |
| 120 | MARKDOWN_PKG="" | |
| 121 | for p in python3-cmarkgfm python3-commonmark; do | |
| 122 | cand=$(apt-cache policy "$p" 2>/dev/null | awk '/Candidate:/{print $2}') | |
| 123 | if [ -n "$cand" ] && [ "$cand" != "(none)" ]; then | |
| 124 | MARKDOWN_PKG="$p" | |
| 125 | break | |
| 126 | fi | |
| 127 | done | |
| 128 | if [ -n "$MARKDOWN_PKG" ]; then | |
| 129 | OPTIONAL+=("$MARKDOWN_PKG") | |
| 130 | fi | |
| 131 | ||
| 132 | if [ "$MINIMAL" -eq 1 ]; then | |
| 133 | OPTIONAL=() | |
| 134 | fi | |
| 135 | ||
| 136 | if [ "$DRY_RUN" -eq 1 ]; then | |
| 137 | echo | |
| 138 | bold "Would install (required):" | |
| 139 | printf ' %s\n' "${REQUIRED[@]}" | |
| 140 | if [ ${#OPTIONAL[@]} -gt 0 ]; then | |
| 141 | bold "Would install (optional):" | |
| 142 | printf ' %s\n' "${OPTIONAL[@]}" | |
| 143 | fi | |
| 144 | exit 0 | |
| 145 | fi | |
| 146 | ||
| 147 | # ------------------------------------------------------------- install | |
| 148 | ||
| 149 | echo | |
| 150 | bold "Updating package lists..." | |
| 151 | if ! apt-get update -qq; then | |
| 152 | red "apt-get update failed -- continuing with the cached lists." | |
| 153 | fi | |
| 154 | ||
| 155 | echo | |
| 156 | bold "Installing required packages..." | |
| 157 | if ! apt-get install -y "${REQUIRED[@]}"; then | |
| 158 | # Fall back to one-at-a-time so the failing package is obvious. | |
| 159 | red "Bulk install failed; retrying individually to find the culprit..." | |
| 160 | FAILED=() | |
| 161 | for p in "${REQUIRED[@]}"; do | |
| 162 | apt-get install -y "$p" || FAILED+=("$p") | |
| 163 | done | |
| 164 | [ ${#FAILED[@]} -eq 0 ] || die "could not install: ${FAILED[*]}" | |
| 165 | fi | |
| 166 | ||
| 167 | OPT_FAILED=() | |
| 168 | if [ ${#OPTIONAL[@]} -gt 0 ]; then | |
| 169 | echo | |
| 170 | bold "Installing optional packages..." | |
| 171 | if ! apt-get install -y "${OPTIONAL[@]}"; then | |
| 172 | red "Bulk install failed; retrying individually..." | |
| 173 | for p in "${OPTIONAL[@]}"; do | |
| 174 | apt-get install -y "$p" || OPT_FAILED+=("$p") | |
| 175 | done | |
| 176 | fi | |
| 177 | fi | |
| 178 | ||
| 179 | # --------------------------------------------------------------- verify | |
| 180 | ||
| 181 | echo | |
| 182 | bold "Verifying the toolchain..." | |
| 183 | ||
| 184 | MISSING=() | |
| 185 | check_cmd() { | |
| 186 | if command -v "$1" >/dev/null 2>&1; then | |
| 187 | printf ' %-16s %s\n' "$1" "$(command -v "$1")" | |
| 188 | else | |
| 189 | printf ' %-16s %s\n' "$1" "MISSING" | |
| 190 | MISSING+=("$1") | |
| 191 | fi | |
| 192 | } | |
| 193 | ||
| 194 | for c in gcc g++ make gawk autoconf automake cmake ninja python3 git; do | |
| 195 | check_cmd "$c" | |
| 196 | done | |
| 197 | ||
| 198 | # Not required to build, so report it without failing the run. | |
| 199 | if command -v shellcheck >/dev/null 2>&1; then | |
| 200 | printf ' %-16s %s\n' "shellcheck" "$(command -v shellcheck)" | |
| 201 | else | |
| 202 | printf ' %-16s %s\n' "shellcheck" "not installed (optional)" | |
| 203 | fi | |
| 204 | ||
| 205 | echo | |
| 206 | bold "Verifying optional libraries..." | |
| 207 | check_header() { | |
| 208 | # $1 = human name, $2 = header path | |
| 209 | if [ -e "/usr/include/$2" ] || \ | |
| 210 | find /usr/include -maxdepth 3 -name "$(basename "$2")" -print -quit \ | |
| 211 | 2>/dev/null | grep -q .; then | |
| 212 | printf ' %-16s yes\n' "$1" | |
| 213 | else | |
| 214 | printf ' %-16s no\n' "$1" | |
| 215 | fi | |
| 216 | } | |
| 217 | check_header acl sys/acl.h | |
| 218 | check_header xattr sys/xattr.h | |
| 219 | check_header xxhash xxhash.h | |
| 220 | check_header zstd zstd.h | |
| 221 | check_header lz4 lz4.h | |
| 222 | check_header openssl openssl/md5.h | |
| 223 | check_header zlib zlib.h | |
| 224 | ||
| 225 | echo | |
| 226 | bold "Verifying the manpage generator..." | |
| 227 | MD_OK=0 | |
| 228 | for m in cmarkgfm commonmark; do | |
| 229 | if python3 -c "import $m" >/dev/null 2>&1; then | |
| 230 | echo " python3 module '$m' importable" | |
| 231 | MD_OK=1 | |
| 232 | break | |
| 233 | fi | |
| 234 | done | |
| 235 | if [ "$MD_OK" -eq 0 ]; then | |
| 236 | if [ "$MINIMAL" -eq 1 ]; then | |
| 237 | echo " skipped (--minimal); build with ./configure --disable-md2man" | |
| 238 | else | |
| 239 | red " neither cmarkgfm nor commonmark is importable." | |
| 240 | echo " Install one with: python3 -mpip install --user commonmark" | |
| 241 | echo " ...or build with: ./configure --disable-md2man" | |
| 242 | fi | |
| 243 | fi | |
| 244 | ||
| 245 | # ---------------------------------------------------------------- done | |
| 246 | ||
| 247 | echo | |
| 248 | if [ ${#MISSING[@]} -ne 0 ]; then | |
| 249 | red "Missing required tools: ${MISSING[*]}" | |
| 250 | exit 1 | |
| 251 | fi | |
| 252 | ||
| 253 | if [ ${#OPT_FAILED[@]} -ne 0 ]; then | |
| 254 | red "Optional packages that failed to install: ${OPT_FAILED[*]}" | |
| 255 | echo "The build will still work, with those features disabled." | |
| 256 | fi | |
| 257 | ||
| 258 | green "All required build dependencies are installed." | |
| 259 | cat <<'EOF' | |
| 260 | ||
| 261 | Build rsync with either build system: | |
| 262 | ||
| 263 | autoconf: ./configure && make | |
| 264 | CMake: cmake -B build -G Ninja && cmake --build build | |
| 265 | ||
| 266 | Run the test suite (needs the autoconf build's helper programs): | |
| 267 | ||
| 268 | make check | |
| 269 | EOF |