1#!/usr/bin/env bash 2 3###################################################################### 4# 4) configure and build openzfs modules. This is run on the VMs. 5# 6# Usage: 7# 8# qemu-4-build-vm.sh OS [--enable-debug][--dkms][--patch-level NUM] 9# [--poweroff][--release][--repo][--tarball] 10# 11# OS: OS name like 'fedora41' 12# --enable-debug: Build RPMs with '--enable-debug' (for testing) 13# --dkms: Build DKMS RPMs as well 14# --patch-level NUM: Use a custom patch level number for packages. 15# --poweroff: Power-off the VM after building 16# --release Build zfs-release*.rpm as well 17# --repo After building everything, copy RPMs into /tmp/repo 18# in the ZFS RPM repository file structure. Also 19# copy tarballs if they were built. 20# --tarball: Also build a tarball of ZFS source 21###################################################################### 22 23ENABLE_DEBUG="" 24DKMS="" 25PATCH_LEVEL="" 26POWEROFF="" 27RELEASE="" 28REPO="" 29TARBALL="" 30while [[ $# -gt 0 ]]; do 31 case $1 in 32 --enable-debug) 33 ENABLE_DEBUG=1 34 shift 35 ;; 36 --dkms) 37 DKMS=1 38 shift 39 ;; 40 --patch-level) 41 PATCH_LEVEL=$2 42 shift 43 shift 44 ;; 45 --poweroff) 46 POWEROFF=1 47 shift 48 ;; 49 --release) 50 RELEASE=1 51 shift 52 ;; 53 --repo) 54 REPO=1 55 shift 56 ;; 57 --tarball) 58 TARBALL=1 59 shift 60 ;; 61 *) 62 OS=$1 63 shift 64 ;; 65 esac 66done 67 68set -eu 69 70function run() { 71 LOG="/var/tmp/build-stderr.txt" 72 echo "****************************************************" 73 echo "$(date) ($*)" 74 echo "****************************************************" 75 ($@ || echo $? > /tmp/rv) 3>&1 1>&2 2>&3 | stdbuf -eL -oL tee -a $LOG 76 if [ -f /tmp/rv ]; then 77 RV=$(cat /tmp/rv) 78 echo "****************************************************" 79 echo "exit with value=$RV ($*)" 80 echo "****************************************************" 81 echo 1 > /var/tmp/build-exitcode.txt 82 exit $RV 83 fi 84} 85 86# Look at the RPMs in the current directory and copy/move them to 87# /tmp/repo, using the directory structure we use for the ZFS RPM repos. 88# 89# For example: 90# /tmp/repo/epel-testing/9.5 91# /tmp/repo/epel-testing/9.5/SRPMS 92# /tmp/repo/epel-testing/9.5/SRPMS/zfs-2.3.99-1.el9.src.rpm 93# /tmp/repo/epel-testing/9.5/SRPMS/zfs-kmod-2.3.99-1.el9.src.rpm 94# /tmp/repo/epel-testing/9.5/kmod 95# /tmp/repo/epel-testing/9.5/kmod/x86_64 96# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug 97# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug/kmod-zfs-debuginfo-2.3.99-1.el9.x86_64.rpm 98# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug/libnvpair3-debuginfo-2.3.99-1.el9.x86_64.rpm 99# /tmp/repo/epel-testing/9.5/kmod/x86_64/debug/libuutil3-debuginfo-2.3.99-1.el9.x86_64.rpm 100# ... 101function copy_rpms_to_repo { 102 # Pick a RPM to query. It doesn't matter which one - we just want to extract 103 # the 'Build Host' value from it. 104 rpm=$(ls zfs-*.rpm | head -n 1) 105 106 # Get zfs version '2.2.99' 107 zfs_ver=$(rpm -qpi $rpm | awk '/Version/{print $3}') 108 109 # Get "2.1" or "2.2" 110 zfs_major=$(echo $zfs_ver | grep -Eo [0-9]+\.[0-9]+) 111 112 # Get 'almalinux9.5' or 'fedora41' type string 113 build_host=$(rpm -qpi $rpm | awk '/Build Host/{print $4}') 114 115 # Get '9.5' or '41' OS version 116 os_ver=$(echo $build_host | grep -Eo '[0-9\.]+$') 117 118 # Our ZFS version and OS name will determine which repo the RPMs 119 # will go in (regular or testing). Fedora always gets the newest 120 # releases, and Alma gets the older releases. 121 case $build_host in 122 almalinux*) 123 case $zfs_major in 124 2.2) 125 d="epel" 126 ;; 127 *) 128 d="epel-testing" 129 ;; 130 esac 131 ;; 132 fedora*) 133 d="fedora" 134 ;; 135 esac 136 137 prefix=/tmp/repo 138 dst="$prefix/$d/$os_ver" 139 140 # Special case: move zfs-release*.rpm out of the way first (if we built them). 141 # This will make filtering the other RPMs easier. 142 mkdir -p $dst 143 mv zfs-release*.rpm $dst || true 144 145 # Copy source RPMs 146 mkdir -p $dst/SRPMS 147 cp $(ls *.src.rpm) $dst/SRPMS/ 148 149 if [[ "$build_host" =~ "almalinux" ]] ; then 150 # Copy kmods+userspace 151 mkdir -p $dst/kmod/x86_64/debug 152 cp $(ls *.rpm | grep -Ev 'src.rpm|dkms|debuginfo') $dst/kmod/x86_64 153 cp *debuginfo*.rpm $dst/kmod/x86_64/debug 154 fi 155 156 if [ -n "$DKMS" ] ; then 157 # Copy dkms+userspace 158 mkdir -p $dst/x86_64 159 cp $(ls *.rpm | grep -Ev 'src.rpm|kmod|debuginfo') $dst/x86_64 160 fi 161 162 # Copy debug 163 mkdir -p $dst/x86_64/debug 164 cp $(ls *debuginfo*.rpm | grep -v kmod) $dst/x86_64/debug 165} 166 167function freebsd() { 168 extra="${1:-}" 169 170 export MAKE="gmake" 171 echo "##[group]Autogen.sh" 172 run ./autogen.sh 173 echo "##[endgroup]" 174 175 echo "##[group]Configure" 176 run ./configure \ 177 --prefix=/usr/local \ 178 --with-libintl-prefix=/usr/local \ 179 --enable-pyzfs \ 180 --enable-debuginfo $extra 181 echo "##[endgroup]" 182 183 echo "##[group]Build" 184 run gmake -j$(sysctl -n hw.ncpu) 185 echo "##[endgroup]" 186 187 echo "##[group]Install" 188 run sudo gmake install 189 echo "##[endgroup]" 190} 191 192function linux() { 193 extra="${1:-}" 194 195 echo "##[group]Autogen.sh" 196 run ./autogen.sh 197 echo "##[endgroup]" 198 199 echo "##[group]Configure" 200 run ./configure \ 201 --prefix=/usr \ 202 --enable-pyzfs \ 203 --enable-debuginfo $extra 204 echo "##[endgroup]" 205 206 echo "##[group]Build" 207 run make -j$(nproc) 208 echo "##[endgroup]" 209 210 echo "##[group]Install" 211 run sudo make install 212 echo "##[endgroup]" 213} 214 215function rpm_build_and_install() { 216 extra="${1:-}" 217 218 # Build RPMs with XZ compression by default (since gzip decompression is slow) 219 echo "%_binary_payload w7.xzdio" >> ~/.rpmmacros 220 221 echo "##[group]Autogen.sh" 222 run ./autogen.sh 223 echo "##[endgroup]" 224 225 if [ -n "$PATCH_LEVEL" ] ; then 226 sed -i -E 's/(Release:\s+)1/\1'$PATCH_LEVEL'/g' META 227 fi 228 229 echo "##[group]Configure" 230 run ./configure --enable-debuginfo $extra 231 echo "##[endgroup]" 232 233 echo "##[group]Build" 234 run make pkg-kmod pkg-utils 235 echo "##[endgroup]" 236 237 if [ -n "$DKMS" ] ; then 238 echo "##[group]DKMS" 239 make rpm-dkms 240 echo "##[endgroup]" 241 fi 242 243 if [ -n "$REPO" ] ; then 244 echo "Skipping install since we're only building RPMs and nothing else" 245 else 246 echo "##[group]Install" 247 run sudo dnf -y --nobest install $(ls *.rpm | grep -Ev 'dkms|src.rpm') 248 echo "##[endgroup]" 249 fi 250 251 # Optionally build the zfs-release.*.rpm 252 if [ -n "$RELEASE" ] ; then 253 echo "##[group]Release" 254 pushd ~ 255 sudo dnf -y install rpm-build || true 256 # Check out a sparse copy of zfsonlinux.github.com.git so we don't get 257 # all the binaries. We just need a few kilobytes of files to build RPMs. 258 git clone --depth 1 --no-checkout \ 259 https://github.com/zfsonlinux/zfsonlinux.github.com.git 260 261 cd zfsonlinux.github.com 262 git sparse-checkout set zfs-release 263 git checkout 264 cd zfs-release 265 266 mkdir -p ~/rpmbuild/{BUILDROOT,SPECS,RPMS,SRPMS,SOURCES,BUILD} 267 cp RPM-GPG-KEY-openzfs* *.repo ~/rpmbuild/SOURCES 268 cp zfs-release.spec ~/rpmbuild/SPECS/ 269 rpmbuild -ba ~/rpmbuild/SPECS/zfs-release.spec 270 271 # ZFS release RPMs are built. Copy them to the ~/zfs directory just to 272 # keep all the RPMs in the same place. 273 cp ~/rpmbuild/RPMS/noarch/*.rpm ~/zfs 274 cp ~/rpmbuild/SRPMS/*.rpm ~/zfs 275 276 popd 277 rm -fr ~/rpmbuild 278 echo "##[endgroup]" 279 fi 280 281 if [ -n "$REPO" ] ; then 282 echo "##[group]Repo" 283 copy_rpms_to_repo 284 echo "##[endgroup]" 285 fi 286} 287 288function deb_build_and_install() { 289 extra="${1:-}" 290 291 echo "##[group]Autogen.sh" 292 run ./autogen.sh 293 echo "##[endgroup]" 294 295 echo "##[group]Configure" 296 run ./configure \ 297 --prefix=/usr \ 298 --enable-pyzfs \ 299 --enable-debuginfo $extra 300 echo "##[endgroup]" 301 302 echo "##[group]Build" 303 run make native-deb-kmod native-deb-utils 304 echo "##[endgroup]" 305 306 echo "##[group]Install" 307 # Do kmod install. Note that when you build the native debs, the 308 # packages themselves are placed in parent directory '../' rather than 309 # in the source directory like the rpms are. 310 run sudo apt-get -y install $(find ../ | grep -E '\.deb$' \ 311 | grep -Ev 'dkms|dracut') 312 echo "##[endgroup]" 313} 314 315function build_tarball { 316 if [ -n "$REPO" ] ; then 317 ./autogen.sh 318 ./configure --with-config=srpm 319 make dist 320 mkdir -p /tmp/repo/releases 321 # The tarball name is based off of 'Version' field in the META file. 322 mv *.tar.gz /tmp/repo/releases/ 323 fi 324} 325 326# Debug: show kernel cmdline 327if [ -f /proc/cmdline ] ; then 328 cat /proc/cmdline || true 329fi 330 331# Set our hostname to our OS name and version number. Specifically, we set the 332# major and minor number so that when we query the Build Host field in the RPMs 333# we build, we can see what specific version of Fedora/Almalinux we were using 334# to build them. This is helpful for matching up KMOD versions. 335# 336# Examples: 337# 338# rhel8.10 339# almalinux9.5 340# fedora42 341source /etc/os-release 342 if which hostnamectl &> /dev/null ; then 343 # Fedora 42+ use hostnamectl 344 sudo hostnamectl set-hostname "$ID$VERSION_ID" 345 sudo hostnamectl set-hostname --pretty "$ID$VERSION_ID" 346else 347 sudo hostname "$ID$VERSION_ID" 348fi 349 350# save some sysinfo 351uname -a > /var/tmp/uname.txt 352 353cd $HOME/zfs 354export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" 355 356extra="" 357if [ -n "$ENABLE_DEBUG" ] ; then 358 extra="--enable-debug" 359fi 360 361# build 362case "$OS" in 363 freebsd*) 364 freebsd "$extra" 365 ;; 366 alma*|centos*) 367 rpm_build_and_install "--with-spec=redhat $extra" 368 ;; 369 fedora*) 370 rpm_build_and_install "$extra" 371 372 # Historically, we've always built the release tarballs on Fedora, since 373 # there was one instance long ago where we built them on CentOS 7, and they 374 # didn't work correctly for everyone. 375 if [ -n "$TARBALL" ] ; then 376 build_tarball 377 fi 378 ;; 379 debian*|ubuntu*) 380 deb_build_and_install "$extra" 381 ;; 382 *) 383 linux "$extra" 384 ;; 385esac 386 387 388# building the zfs module was ok 389echo 0 > /var/tmp/build-exitcode.txt 390 391# reset cloud-init configuration and poweroff 392if [ -n "$POWEROFF" ] ; then 393 sudo cloud-init clean --logs 394 sync && sleep 2 && sudo poweroff & 395fi 396exit 0 397