\documentclass{beamer} \usepackage{amsmath} \usepackage{graphicx} \usepackage{qrcode} \usepackage[normalem]{ulem} \title{Hot cross builds \\ Cross-compilation in pkgsrc} \author{Taylor R Campbell \\ \texttt{riastradh@NetBSD.org}} \date{BSDCan 2024 \\ Ottawa, Canada \\ May 31, 2024} \begin{document} \frame{\titlepage} \begin{frame} \frametitle{Hot cross builds: cross-compilation in pkgsrc} \centering \url{https://www.NetBSD.org/gallery/presentations/riastradh/bsdcan2024/pkgcross.pdf} \vspace{\baselineskip} \qrcode[height=2in]{https://www.NetBSD.org/gallery/presentations/riastradh/bsdcan2024/pkgcross.pdf} \end{frame} \begin{frame} \frametitle{pkgsrc: portable package build system} \begin{itemize} \item \texttt{https://pkgsrc.org/} \item Framework for building third-party software on Unix-like operating systems. \item ${>}26{,}000$ packages. \item Actively supported platforms: \begin{itemize} \item NetBSD (first platform, based on mid-'90s FreeBSD ports) \item Solaris/SmartOS/illumos \item Linux \item macOS \end{itemize} \item Other platforms with some support: \begin{itemize} \item FreeBSD/OpenBSD/DragonflyBSD/MidnightBSD \item MINIX 3 \item SCO OpenServer/UnixWare \item HP-UX \item QNX \end{itemize} \item Works unprivileged, so you can develop in your home directory on a server you don't administer. \end{itemize} \end{frame} \begin{frame} \frametitle{Anatomy of a pkgsrc package} \begin{itemize} \item \texttt{DESCR} -- Human-readable description. \item \texttt{Makefile} -- Machine-readable description. \begin{itemize} \item Tells where to download source code. \item Rules for how to configure, build, install. \item Etc. \end{itemize} \item \texttt{distinfo} -- Names, sizes, and hashes of source distribution. Provides cryptographic integrity check. \item \texttt{PLIST} -- Packing list: lists files installed by package. \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{pkgsrc example: \texttt{security/nettle}, part 1} \begin{center} \small \begin{verbatim} # $NetBSD: pkgcross.tex,v 1.2 2024/05/31 12:12:59 riastradh Exp $ DISTNAME= nettle-3.9.1 CATEGORIES= security MASTER_SITES= http://www.lysator.liu.se/~nisse/archive/ MASTER_SITES+= ftp://ftp.lysator.liu.se/pub/security/lsh/ MAINTAINER= pkgsrc-users@NetBSD.org HOMEPAGE= https://www.lysator.liu.se/~nisse/nettle/ COMMENT= Cryptographic library LICENSE= gnu-lgpl-v2.1 USE_LANGUAGES= c c99 USE_LIBTOOL= yes USE_TOOLS+= gm4 gmake GNU_CONFIGURE= yes SET_LIBDIR= yes CONFIGURE_ARGS+= --disable-openssl CONFIGURE_ARGS+= --disable-shared \end{verbatim} \end{center} \end{frame} \begin{frame}[fragile] \frametitle{pkgsrc example: \texttt{security/nettle}, part 2} \begin{center} \small \begin{verbatim} .include "../../mk/bsd.prefs.mk" .if ${USE_CROSS_COMPILE:tl} == "yes" CONFIGURE_ENV+= CC_FOR_BUILD=${NATIVE_CC:Q} .endif INFO_FILES= yes TEST_TARGET= check PKGCONFIG_OVERRIDE= hogweed.pc.in PKGCONFIG_OVERRIDE+= nettle.pc.in BUILDLINK_API_DEPENDS.gmp+= gmp>=6.0 .include "../../devel/gmp/buildlink3.mk" .include "../../mk/bsd.pkg.mk" \end{verbatim} \end{center} \end{frame} \begin{frame}[fragile] \frametitle{Building and installing a package\footnote{On NetBSD, can use base system's \texttt{make}, but everywhere else we bootstrap \texttt{devel/bmake} for pkgsrc.}} \begin{center} \footnotesize \begin{verbatim} # which socat socat not found # cd /usr/pkgsrc/net/socat # bmake install => Bootstrap dependency digest>=20211023: found digest-20220214 => Fetching socat-1.8.0.0.tar.gz ... => Checksum SHA512 OK for socat-1.8.0.0.tar.gz ===> Installing dependencies for socat-1.8.0.0 ... => Tool dependency checkperms>=1.1: found checkperms-1.12 => Full dependency readline>=6.0: found readline-8.2nb2 ... => Creating binary package .../socat-1.8.0.0.tgz ===> Installing binary package of socat-1.8.0.0 # which socat /usr/pkg/bin/socat \end{verbatim} \end{center} \end{frame} \begin{frame}[fragile] \frametitle{Binary packages: build once, install many times} \begin{itemize} \item Building from source is necessary: verify source, audit programs, modify, etc. \item Building from source is slow: run compiler on lots of source code. \item Do it once, save the result, install binary packages after. \begin{center} \begin{verbatim} builder$ cd /home/builder/pkgsrc/net/socat builder$ bmake package client# PKG_PATH=/nfs/builder/pkgsrc/packages client# export PKG_PATH client# pkg_add socat client# which socat /usr/pkg/bin/socat \end{verbatim} \end{center} \end{itemize} \end{frame} \begin{frame} \frametitle{Binary package bulk builds} \begin{itemize} \item NetBSD provides binary packages for NetBSD on many architectures\footnote{\texttt{https://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/}}. \item MNX Cloud provides binary packages for SmartOS, macOS, Linux, and NetBSD/amd64\footnote{\texttt{https://pkgsrc.smartos.org/}}. \item I build binary packages for my own machines. \item You can too! \end{itemize} \end{frame} \begin{frame} \frametitle{Cross-compiling NetBSD} \begin{itemize} \item Every NetBSD build is a cross-build. \item \texttt{build.sh tools} builds cross-toolchain. \item \texttt{build.sh kernel=GENERIC distribution} builds NetBSD with the cross-toolchain. \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Cross-compiling pkgsrc} \begin{itemize} \item Use NetBSD \texttt{build.sh tools distribution} to get started.\footnote{See \texttt{doc/HOWTO-use-crosscompile} for details.} \item \texttt{USE\_CROSS\_COMPILE=yes} \item \texttt{TOOLDIR=/usr/obj.evbppc/tooldir.NetBSD-10.0-amd64} \item \texttt{CROSS\_DESTDIR=/usr/obj.evbppc/destdir.evbppc} \item \texttt{CROSS\_MACHINE\_ARCH=powerpc}, \texttt{CROSS\_OPSYS=NetBSD}, \dots \end{itemize} \begin{center} \begin{verbatim} $ uname -m amd64 $ cd ~/pkgsrc/net/socat $ bmake package ... $ cd ~/pkgsrc/packages.NetBSD-10.0-powerpc/All $ pkg_info -Q MACHINE_ARCH socat-1.8.0.0.tgz powerpc \end{verbatim} \end{center} \end{frame} \begin{frame} \frametitle{Cross-build in homedir, install systemwide on target} \begin{itemize} \item \texttt{./bootstrap --prefix /home/builder/pkg --unprivileged \dots} \item set \texttt{CROSS\_LOCALBASE=/usr/pkg} in mk.conf \end{itemize} \end{frame} \begin{frame} \frametitle{Toolchain wrappers} \begin{itemize} \item pkgsrc creates symlink farms of toolchain wrappers for build: \begin{itemize} \item \texttt{cc}, \texttt{ld}, \texttt{as}, \dots \item \texttt{powerpc--netbsd-gcc}, \texttt{powerpc--netbsd-ld}, \texttt{powerpc--netbsd-as}, \dots \end{itemize} \item pkgsrc buildlink3 framework creates symlink farms of dependent header files and libraries for build isolation. \item Wrappers transform toolchain arguments: \begin{itemize} \item add \texttt{--sysroot=\$\{CROSS\_DESTDIR\}} \item ensure \texttt{-I} (build-time include path) and \texttt{-L} (build-time library path) point at buildlink3 symlink farms \item ensure \texttt{-Wl,-R} (run-time library path) points at installation prefix without \texttt{CROSS\_DESTDIR} \item replace \texttt{-ldl} by appropriate platform-specific dlfcn.h option \item other package-specific argument transformations \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{Dependencies} \begin{itemize} \item Some packages \textbf{depend} on other packages: \begin{itemize} \item \texttt{tor} program needs \texttt{libevent} library at run-time \begin{itemize} \item \texttt{net/tor} (\textbf{run-}) \textbf{depends} on \texttt{devel/libevent} \end{itemize} \item Compiler needs \texttt{event.h} when building \texttt{tor} program at compile-time \begin{itemize} \item \texttt{net/tor} also \textbf{build-depends} on \texttt{devel/libevent} \end{itemize} \item Building \texttt{libxcb} requires running \texttt{xsltproc} to turn XML into C header files at compile-time \begin{itemize} \item \texttt{x11/libxcb} \textbf{tool-depends} on \texttt{textproc/xsltproc} \end{itemize} \item {\scriptsize Also \textbf{bootstrap-depends}, like tool-depends but for parts of the pkgsrc infrastructure.} \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{Cross-compiling dependencies} \begin{itemize} \item Use Intel Xeon to build \texttt{x11/xterm}, run on your powerpc-based thin client. \item \texttt{x11/xterm} must be \emph{cross-built} for \texttt{MACHINE\_ARCH=powerpc}. \item \texttt{x11/xterm} depends on \texttt{x11/libxcb}\footnote{Via \texttt{x11/libX11}.}. \begin{itemize} \item \texttt{x11/libxcb} must be \emph{cross-built} for \texttt{MACHINE\_ARCH=powerpc}. \end{itemize} \item \texttt{x11/libxcb} \emph{tool-depends} on \texttt{textproc/xsltproc}. \begin{itemize} \item \texttt{textproc/libxsltproc} must be \emph{natively built} for \texttt{MACHINE\_ARCH=x86\_64}. \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{Build-depends vs tool-depends} \begin{itemize} \item Both build-depends and tool-depends need to exist at build-time. \item \emph{Build-depends} are cross-built and installed into \texttt{/usr/obj.evbppc/destdir.evbppc/usr/pkg/...} \begin{itemize} \item Example: C libraries, needed for linker. \end{itemize} \item \emph{Tool-depends} are natively built and installed into \texttt{/home/builder/pkg/...} (\texttt{\$\{TOOLBASE\}}) \begin{itemize} \item Example: \texttt{xsltproc}, cross-compiler. \item \texttt{TARGET\_MACHINE\_ARCH}, \texttt{TARGET\_OPSYS}, \dots, are set to cross-compilation target. \end{itemize} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Pointing builds at tool programs in dependencies} \begin{itemize} \item Package uses glib-mkenums at build-time, how to use it? \begin{verbatim} TOOL_DEPENDS+= \ glib2-tools>=0:../../devel/glib2-tools \end{verbatim} \item GNU Autoconf: \begin{verbatim} CONFIGURE_ARGS+= \ GLIB_MKENUMS=${TOOLBASE:Q}/bin/glib-mkenums \end{verbatim} \item Meson: \begin{verbatim} MESON_CROSS_BINARIES+= glib-mkenums MESON_CROSS_BINARY.glib-mkenums= \ ${TOOLBASE}/bin/glib-mkenums \end{verbatim} \item Similarly: Use \texttt{TOOL\_PYTHONBIN} at build-time, but bake \texttt{PYTHONBIN} into product for run-time Python. \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Meson: pkgsrc creates cross config for you} \small \begin{verbatim} [properties] sys_root = '/usr/obj.evbppc/destdir.evbppc' [host_machine] system = 'netbsd' cpu_family = 'ppc' cpu = 'powerpc' endian = 'big' [binaries] glib-genmarshal = '/home/builder/pkg/bin/glib-genmarshal' glib-mkenums = '/home/builder/pkg/bin/glib-mkenums' \end{verbatim} \end{frame} \begin{frame} \frametitle{Complications part 1: mixing up build-depends and tool-depends} \begin{itemize} \item Originally, pkgsrc had only build-depends---same as tool-depends for native builds. \begin{itemize} \item \texttt{x11/libxcb} build-depended on \texttt{textproc/xsltproc}. \end{itemize} \item Packages practically never need to set \texttt{BUILD\_DEPENDS} directly---only via buildlink3. \item Solution: We mass-changed \texttt{BUILD\_DEPENDS} to \texttt{TOOL\_DEPENDS} in package makefiles. \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Complications part 2: package builds tools internally} \begin{itemize} \item Some packages depend on external tools like \texttt{x11/libxcb} depends on \texttt{textproc/xsltproc}. \item Others use internal tools, like \texttt{security/nettle} above. \item These try to use \texttt{CC}, which may be \texttt{powerpc--netbsd-gcc} for cross-compilation. \item Can't run the result on x86! \item Solution: Set \texttt{CC\_FOR\_BUILD}, maybe patch package to use it instead. \begin{center} \begin{verbatim} .include "../../mk/bsd.prefs.mk" .if ${USE_CROSS_COMPILE:tl} == "yes" CONFIGURE_ENV+= CC_FOR_BUILD=${NATIVE_CC:Q} .endif \end{verbatim} \end{center} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Complications part $2'$: package runs its own build product} \begin{itemize} \item Some packages want to run a program they also install. \begin{itemize} \item \texttt{x11/gtk2} calls \texttt{gtk2-update-icon-cache}. \end{itemize} \item Need both native \emph{and} cross versions of the program! \item Solution: Have package tool-depend on itself and pass path to the natively built tool in the cross-build: \begin{center} \begin{verbatim} .include "../../mk/bsd.prefs.mk" .if ${USE_CROSS_COMPILE:tl} == "yes" TOOL_DEPENDS+= ${PKGNAME}:../../${PKGPATH} UPDATE_ICON_CACHE= \ ${TOOLBASE:Q}/bin/gtk2-update-icon-cache CONFIGURE_ENV+= \ GTK2_UPDATE_ICON_CACHE=${UPDATE_ICON_CACHE} .endif \end{verbatim} \end{center} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Complications part 3: file existence tests} \begin{itemize} \item Package wants to know whether \texttt{/dev/urandom} will exist when run. \item Uses GNU Autoconf to ask whether \texttt{/dev/urandom} exists \emph{now}, when built. \item Build machine and target system may be different! \item But we know \texttt{/dev/urandom} will exist. \item Solution: Tell configure up front: \begin{center} \begin{verbatim} .include "../../mk/bsd.prefs.mk" .if ${USE_CROSS_COMPILE:tl} == "yes" CONFIGURE_ENV.NetBSD+= ac_cv_file__dev_urandom=yes .endif \end{verbatim} \end{center} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Complications part $3'$: file existence tests in pkgsrc} \begin{itemize} \item From \texttt{x11/libdrm}: \begin{center}\small \begin{verbatim} .if !exists(/usr/include/sys/atomic.h) # libdrm won't find system atomic ops, use a package. . include "../../devel/libatomic_ops/buildlink3.mk" .endif \end{verbatim} \end{center} \item Solution: Don't look in \texttt{/usr/include} --- look in \texttt{/usr/obj.evbppc/destdir.evbppc}: \begin{center}\small \begin{verbatim} .include "../../mk/bsd.prefs.mk" .if !exists(${_CROSS_DESTDIR}/usr/include/sys/atomic.h) # libdrm won't find system atomic ops, use a package. . include "../../devel/libatomic_ops/buildlink3.mk" .endif \end{verbatim} \end{center} \end{itemize} \end{frame} \begin{frame} \frametitle{Complications part 4a: configure run-tests} \begin{itemize} \item Similar to file existence tests. \item Program wants to know \texttt{sizeof(long)} at compile-time. \item Compiles a test program to print it, runs test program. \item Can't do that if building on 64-bit amd64 for 32-bit powerpc! \item Solution: Binary search with compile-time assertions using cross-compiler. \item (Yes, seriously! GNU Autoconf supports this with \texttt{AC\_CHECK\_SIZEOF}.) \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Complications part 4b: configure run-tests} \begin{itemize} \item Some are harder to replace. \item Tell the answers up front, maybe with patches. \item From \texttt{shells/zsh}: \begin{center} \begin{verbatim} .include "../../mk/bsd.prefs.mk" .if ${USE_CROSS_COMPILE:tl} == "yes" .if ${OPSYS} == "NetBSD" CONFIGURE_ENV+= zsh_cv_shared_environ=yes CONFIGURE_ENV+= zsh_cv_shared_tgetent=yes CONFIGURE_ENV+= zsh_cv_shared_tigetstr=yes CONFIGURE_ENV+= zsh_cv_sys_dynamic_execsyms=yes .endif .endif \end{verbatim} \end{center} \end{itemize} \end{frame} \begin{frame} \frametitle{Complications part 5: problem children} \begin{itemize} \item Some packages go to great effort to resist cross-compilation. \begin{itemize} \item Perl \item \only<1>{Python}\only<2->{\sout{Python} \only<3->{(much better since 3.10)}} \item gobject-introspection \end{itemize} \item Workaround: just build on your powerpc thin client and ship binary packages back to x86 build machine to continue. \item (Solution: Chainsaws and rototillers. Fix the build systems!\footnote{It can be done for Perl: OpenWrt does it. If you would like to help adapt their approach to pkgsrc, talk to me!}) \end{itemize} \end{frame} \begin{frame} \frametitle{Related work} \begin{itemize} \item OpenWrt: cross-compiled packages for Linux-based network appliances. \begin{itemize} \item Linux-only. \item Not general-purpose package system. \item Much smaller than pkgsrc. \end{itemize} \item \texttt{distcc}: run pkgsrc on thin client, run compiler remotely on x86 build machine. \begin{itemize} \item Complex to set up: many moving parts (literally). \item Hard to parallelize. \item Compiler is a big part but not all of run-time---make(1) is a big part of pkgsrc cost. \end{itemize} \item FreeBSD ports: run native compiler in user-mode emulator. \begin{itemize} \item Many moving parts (figuratively). \item Emulators are slow. \item Less clean separation between host and target. \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{\only<1>{Future}\only<2->{\sout{Future} Past} work} \only<2->{(since AsiaBSDcon 2015)} \end{frame} \begin{frame} \frametitle{\sout{Future} Past work} \begin{itemize} \item Cross-OS compilation. Use SmartOS x86 cloud cluster to build for \texttt{MACHINE\_PLATFORM=NetBSD-7.0-powerpc}. \begin{itemize} \item<2-> Set both \texttt{CROSS\_MACHINE\_ARCH} and \texttt{CROSS\_OPSYS} in mk.conf. \item<3-> Still to fix: \texttt{USE\_TOOLS+= \dots:run}. pkgsrc doesn't distinguish host OS from target OS in \texttt{USE\_TOOLS}. \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{\sout{Future} Past work} \begin{itemize} \item User interface improvements. \begin{itemize} \item<2-> \only<2>{Can't do \texttt{bmake package MACHINE\_ARCH=powerpc} for stupid reasons.} \only<3->{\sout{Can't do \texttt{bmake package MACHINE\_ARCH=powerpc} for stupid reasons.}} \begin{itemize} \item<3-> \texttt{bmake package CROSS\_MACHINE\_ARCH=powerpc} \end{itemize} \item<2-> \only<2>{Setting up cross-compiling requires a manual step to work around broken GNU \texttt{libtool}.} \only<3->{\sout{Setting up cross-compiling requires a manual step to work around broken GNU \texttt{libtool}.}} \begin{itemize} \item<3-> Bug fixed! \end{itemize} \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{\sout{Future} \only<1>{Past}\only<2->{\sout{Past} Future} work} \begin{itemize} \item Bulk builds. \begin{itemize} \item \texttt{pbulk} doesn't understand build-depends vs tool-depends. \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{\sout{Future} Past work} \begin{itemize} \item Unprivileged builds for privileged installs. \begin{itemize} \item \only<1>{Native and cross packages must both point at \texttt{/usr/pkg}.} \only<2->{\sout{Native and cross packages must both point at \texttt{/usr/pkg}.}} \begin{itemize} \item<2-> \texttt{LOCALBASE=/home/builder/pkg} and \texttt{CROSS\_LOCALBASE=/usr/pkg} in the same mk.conf. \end{itemize} \item (Unprivileged builds for unprivileged installs work fine---not a problem with privileges, just with different paths.) \item<3-> Some remaining issues: \texttt{chown} tool, suid executables. \end{itemize} \end{itemize} \end{frame} \begin{frame} \frametitle{Now get cross-building!} Questions? \end{frame} \end{document}