Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
build: cross compile linux by ReenigneArcher · Pull Request #2020 · LizardByte/Sunshine · GitHub
Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' build: cross compile linux by ReenigneArcher · Pull Request #2020 · LizardByte/Sunshine · GitHub
Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' build: cross compile linux by ReenigneArcher · Pull Request #2020 · LizardByte/Sunshine · GitHub
Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' build: cross compile linux by ReenigneArcher · Pull Request #2020 · LizardByte/Sunshine · GitHub
Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' build: cross compile linux by ReenigneArcher · Pull Request #2020 · LizardByte/Sunshine · GitHub
Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' build: cross compile linux by ReenigneArcher · Pull Request #2020 · LizardByte/Sunshine · GitHub
Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); build: cross compile linux by ReenigneArcher · Pull Request #2020 · LizardByte/Sunshine · GitHub
Skip to content

build: cross compile linux - #2020

Closed
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux
Closed

build: cross compile linux#2020
ReenigneArcher wants to merge 1 commit into
masterfrom
build-cross-compile-linux

Conversation

@ReenigneArcher

@ReenigneArcherReenigneArcher commented Jan 14, 2024

Copy link
Copy Markdown
Member

Description

Draft PR based on #2018

Todo:

  • Ensure Fedora 39 image builds successfully
  • Migrate changes to Fedora 38
  • Cross compile Debian/Ubuntu
  • Document cross compiling process
  • Update artifact names in docs if changed

Screenshot

Issues Fixed or Closed

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

Branch Updates

LizardByte requires that branches be up-to-date before merging. This means that after any PR is merged, this branch
must be updated before it can be merged. You must also
Allow edits from maintainers.

  • I want maintainers to keep my branch updated

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 9913644 to 91f286aCompareJanuary 14, 2024 14:23
@ReenigneArcherReenigneArcher mentioned this pull request Jan 14, 2024
Comment threaddocker/fedora-39.dockerfile Outdated
Comment threaddocker/fedora-39.dockerfile Outdated
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 6 times, most recently from 9683060 to e3a7510CompareJanuary 14, 2024 20:25
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

@chewi I created some toolchain files. I figured this is the better approach long term, and they should be able to be re-used for people building outside of our docker images.

The aarch64 image builds successfully! https://github.com/LizardByte/Sunshine/actions/runs/7521536314/job/20472490455

Some things to work out before bringing these changes to the other images.

  1. x86_64 doesn't build -> https://github.com/LizardByte/Sunshine/actions/runs/7521377865/job/20472144349#step:10:6817
  2. The second build step, is still slow installing the dependencies for Sunshine. Is there anything we can do about that... or does it have to remain on the end users target arch?
  3. Is there a standard directory to use instead of /mnt/cross? I think in debian it should be /usr/bin/{tuple}... e.g. /usr/bin/aarch64-linux-gnu (at least that's what we use for our ffmpeg cross compile build) https://github.com/LizardByte/build-deps/blob/bf3821f07bb814fcac96d22984e35a015ead2a80/.github/workflows/build-ffmpeg.yml#L320

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 150696f to 58f9177CompareJanuary 14, 2024 21:58
@chewi

Copy link
Copy Markdown
Contributor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.
  2. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.
  3. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. Don't try to do native builds this way. It adds unnecessary complexity. I'm not sure why it's failing in this case. It might be because the native toolchain doesn't support --sysroot, but I have found this approach to be problematic in general.

    1. 2 minutes is too slow? 😆 Most of that is probably in downloading the dependencies, and it shouldn't be much worse than it was for a native build. You could potentially build all the targets from one image, saving you from having to install some of the build dependencies (e.g. cmake) more than once. On the other hand, it may be desirable to keep these separated, and they'll run in parallel anyway. A more effective way to save time would be to store the image with the dependencies installed instead of recreating it every time. Then CI could focus on just building Sunshine itself.

    2. /mnt/cross is arbitrary, but you are confused. This is a directory. You will only have files under /usr/bin like aarch64-linux-gnu-gcc. Gentoo typically does cross-compile into /usr/{tuple} but it really doesn't make any difference here, as it's only relevant for the duration of the build, and I figured using a single path would make the code easier to read. You should only need to change this if you decide to build all the targets together. It's different with Debian/Ubuntu though. Those have multiarch support, which means that the package manager mixes the architectures together under the same tree, with the libraries in /usr/lib/{tuple} and the headers in /usr/include/{tuple}. I have that working locally now, except for the CUDA support, so I need a little more time.

Don't try to do native builds this way.

Okay, I'll try to add logic to handle this. I would like to keep the images multi arch though if possible.

2 minutes is too slow?

2 minutes would be a dream... It's actually almost 30... and it's pretty much all downloading deps.
image

it's only relevant for the duration of the build

Correct, except the value is in the toolchain file. And if this will provided to dev users I don't think they'll appreciate it being a non standard location.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 5 times, most recently from 2c6e0d8 to 26a90edCompareJanuary 15, 2024 02:13
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

x86_64 and aarch64 are building now

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 8 times, most recently from 7ffccdf to e258c97CompareJanuary 15, 2024 05:04
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 2 times, most recently from 438f04b to 2f7ae64CompareJanuary 15, 2024 05:41
Comment threaddocker/fedora-38.dockerfile
Comment threaddocker/fedora-39.dockerfile
@chewi

chewi commented Jan 15, 2024

Copy link
Copy Markdown
Contributor
  1. I see why it's slow. You're still emulating here! 😆 Everything except the final end user image creation should be done as amd64 without QEMU. I'm not sure how this works yet, but I can try to figure it out later if you're stuck.
  2. Providing pre-configured toolchain files isn't really that useful. Not only will the sysroot differ, but the tuples can differ too. Some distros use aarch64-linux-gnu, some use aarch64-unknown-linux-gnu, and there will be other variations too. Even the suffixes seem to vary, as Fedora uses gcc-ar and gcc-ranlib rather than just ar and ranlib for some reason. Those doing cross-compiling will be used to creating their own toolchain files and may have tooling in place to generate them. That is the case for Gentoo. Providing an "example" file wouldn't hurt though.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor
  1. I have one idea to add another stage. The stage will use the build platform and install Sunshine to a custom directory... Similar to /mnt/cross... Then the final stage can copy everything out of there.

  2. Good to know. I've only ever cross compiled ffmpeg and a lot of those libraries provide toolchain files. I'll throw a comment in there that it's only for reference.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from f2bdf91 to 2f7ae64CompareJanuary 15, 2024 17:07
@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I tried installing sunshine under build platform architecture, and copying it to the arch specific image, but it fails to copy.

# syntax=docker/dockerfile:1.4# artifacts: true# platforms: linux/amd64,linux/arm64/v8# platforms_pr: linux/amd64,linux/arm64/v8# no-cache-filters: sunshine-base,artifacts,sunshineARG BASE=fedora
ARG TAG=39
FROM --platform=$BUILDPLATFORM ${BASE}:${TAG} AS sunshine-base
ARG TARGETPLATFORM
ARG TAG
ENV TAG=${TAG}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# setup envWORKDIR /env
RUN <<_ENV
#!/bin/bash
set -e
case "${TARGETPLATFORM}" in
linux/amd64)
TARGETARCH=x86_64
echo GCC_FLAVOR="" >> ./env
echo "DNF=( dnf -y --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
linux/arm64)
TARGETARCH=aarch64
echo GCC_FLAVOR="-${TARGETARCH}-linux-gnu" >> ./env
echo "DNF=( dnf -y --installroot /mnt/cross --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
echo "DNF2=( dnf -y --installroot /mnt/install --releasever \"${TAG}\" --forcearch \"${TARGETARCH}\" )" >> ./env
;;
*)
echo "unsupported platform: ${TARGETPLATFORM}";
exit 1
;;
esac
echo TARGETARCH=${TARGETARCH} >> ./env
echo TUPLE=${TARGETARCH}-linux-gnu >> ./env
_ENV
FROM sunshine-base as sunshine-build
# reused args from baseARG TARGETPLATFORM
# args from ci workflowARG BRANCH
ARG BUILD_VERSION
ARG COMMIT
# note: BUILD_VERSION may be blankENV BRANCH=${BRANCH}
ENV BUILD_VERSION=${BUILD_VERSION}
ENV COMMIT=${COMMIT}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install build dependencies# hadolint ignore=DL3041RUN <<_DEPS_A
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
dnf -y update
dnf -y install \
cmake-3.27.* \
gcc"${GCC_FLAVOR}"-13.2.* \
gcc-c++"${GCC_FLAVOR}"-13.2.* \
git-core \
nodejs \
pkgconf-pkg-config \
rpm-build \
wayland-devel \
wget \
which
dnf clean all
_DEPS_A
# install host dependencies# hadolint ignore=DL3041RUN <<_DEPS_B
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# Initialize an array for packages
packages=(
boost-devel-1.81.0*
glibc-devel
libappindicator-gtk3-devel
libcap-devel
libcurl-devel
libdrm-devel
libevdev-devel
libnotify-devel
libstdc++-devel
libva-devel
libvdpau-devel
libX11-devel
libxcb-devel
libXcursor-devel
libXfixes-devel
libXi-devel
libXinerama-devel
libXrandr-devel
libXtst-devel
mesa-libGL-devel
miniupnpc-devel
numactl-devel
openssl-devel
opus-devel
pulseaudio-libs-devel
wayland-devel
)
# Conditionally include arch specific packages
if [[ "${TARGETARCH}" == 'x86_64' ]]; then
packages+=(intel-mediasdk-devel)
fi
"${DNF[@]}" install \
filesystem
# Install packages using the array"${DNF[@]}" --setopt=tsflags=noscripts install "${packages[@]}"# Clean up"${DNF[@]}" clean all
_DEPS_B
# todo - enable cuda once it's supported for gcc 13 and fedora 39## install cuda#WORKDIR /build/cuda## versions: https://developer.nvidia.com/cuda-toolkit-archive#ENV CUDA_VERSION="12.0.0"#ENV CUDA_BUILD="525.60.13"## hadolint ignore=SC3010#RUN <<_INSTALL_CUDA##!/bin/bash#set -e### shellcheck source=/dev/null#source /env/env#cuda_prefix="https://developer.download.nvidia.com/compute/cuda/"#cuda_suffix=""#if [[ "${TARGETARCH}" == 'aarch64' ]]; then# cuda_suffix="_sbsa"#fi#url="${cuda_prefix}${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux${cuda_suffix}.run"#echo "cuda url: ${url}"#wget "$url" --progress=bar:force:noscroll -q --show-progress -O ./cuda.run#chmod a+x ./cuda.run#./cuda.run --silent --toolkit --toolkitpath=/build/cuda --no-opengl-libs --no-man-page --no-drm#rm ./cuda.run#_INSTALL_CUDA# copy repositoryWORKDIR /build/sunshine/
COPY --link .. .
# setup build directoryWORKDIR /build/sunshine/build
# cmake and cpack# todo - add cmake argument back in for cuda support "-DCMAKE_CUDA_COMPILER:PATH=/build/cuda/bin/nvcc \"# todo - re-enable "DSUNSHINE_ENABLE_CUDA"RUN <<_MAKE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
# shellcheck disable=SC2086
if [[ "${TARGETARCH}" == 'aarch64' ]]; then
CXX_FLAG_1="$(echo /mnt/cross/usr/include/c++/[0-9]*/)"
CXX_FLAG_2="$(echo /mnt/cross/usr/include/c++/[0-9]*/${TUPLE%%-*}-*/)"
LD_FLAG="$(echo /mnt/cross/usr/lib/gcc/${TUPLE%%-*}-*/[0-9]*/)"
export \
CXXFLAGS="-isystem ${CXX_FLAG_1} -isystem ${CXX_FLAG_2}" \
LDFLAGS="-L${LD_FLAG}" \
PKG_CONFIG_LIBDIR=/mnt/cross/usr/lib64/pkgconfig:/mnt/cross/usr/share/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/mnt/cross \
PKG_CONFIG_SYSTEM_INCLUDE_PATH=/mnt/cross/usr/include \
PKG_CONFIG_SYSTEM_LIBRARY_PATH=/mnt/cross/usr/lib64
fi
TOOLCHAIN_OPTION=""
if [[ "${TARGETARCH}" != 'x86_64' ]]; then
TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=toolchain-${TUPLE}.cmake"
fi
cmake \
"$TOOLCHAIN_OPTION" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DSUNSHINE_ASSETS_DIR=share/sunshine \
-DSUNSHINE_EXECUTABLE_PATH=/usr/bin/sunshine \
-DSUNSHINE_ENABLE_WAYLAND=ON \
-DSUNSHINE_ENABLE_X11=ON \
-DSUNSHINE_ENABLE_DRM=ON \
-DSUNSHINE_ENABLE_CUDA=OFF \
/build/sunshine
make -j "$(nproc)"
cpack -G RPM
_MAKE
FROM scratch AS artifacts
ARG BASE
ARG TAG
ARG TARGETARCH
COPY --link --from=sunshine-build /build/sunshine/build/cpack_artifacts/Sunshine.rpm /sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm
FROM sunshine-base as sunshine-install
# copy deb from builderCOPY --link --from=artifacts /sunshine*.rpm /sunshine.rpm
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# install sunshine using custom DNF commandRUN <<_INSTALL_SUNSHINE
#!/bin/bash
set -e
# shellcheck source=/dev/null
source /env/env
"${DNF2[@]}" update
"${DNF2[@]}" install /sunshine.rpm
"${DNF2[@]}" clean all
rm -rf /var/cache/yum
_INSTALL_SUNSHINE
FROM ${BASE}:${TAG} AS sunshine
# copy installed files from sunshine-installCOPY --from=sunshine-install /mnt/install/ /usr/
# validate sunshine is installedRUN <<_VALIDATE_SUNSHINE
#!/bin/bash
set -e
# make sure sunshine is at /usr/bin/sunshine
if [[ ! -f /usr/bin/sunshine ]]; then
echo "sunshine not found at /usr/bin/sunshine"
exit 1
fi
# make sure the version command works
sunshine --version
_VALIDATE_SUNSHINE
# network setupEXPOSE 47984-47990/tcp
EXPOSE 48010
EXPOSE 47998-48000/udp
# setup userARG PGID=1000
ENV PGID=${PGID}
ARG PUID=1000
ENV PUID=${PUID}
ENV TZ="UTC"ARG UNAME=lizard
ENV UNAME=${UNAME}
ENV HOME=/home/$UNAME
# setup userRUN <<_SETUP_USER
#!/bin/bash
set -e
groupadd -f -g "${PGID}""${UNAME}"
useradd -lm -d ${HOME} -s /bin/bash -g "${PGID}" -u "${PUID}""${UNAME}"
mkdir -p ${HOME}/.config/sunshine
ln -s ${HOME}/.config/sunshine /config
chown -R ${UNAME} ${HOME}
_SETUP_USER
USER ${UNAME}
WORKDIR ${HOME}
# entrypointENTRYPOINT ["/usr/bin/sunshine"]

output snip

 => [sunshine-install 2/2] RUN <<_INSTALL_SUNSHINE (#!/bin/bash...) 119.3s => ERROR [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/ 0.0s ------
> [sunshine 2/5] COPY --from=sunshine-install /mnt/install/ /usr/:
------
cannot replace to directory /var/lib/docker/overlay2/s6q271u3bmdgrx1gx21bca2on/merged/usr/bin with file
Failed to deploy '<unknown> Dockerfile: docker/fedora-39.dockerfile': Image build failed with exit code 1.

@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch 3 times, most recently from 4f672eb to a79567eCompareJanuary 20, 2024 02:52
@chewi

chewi commented Jan 20, 2024

Copy link
Copy Markdown
Contributor

Ah, I kinda wish you'd held off with Debian as I'd been working on Ubuntu myself. I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall. Also take note of the RUN --mount=type=cache part, which is very useful for testing this locally, as it caches the downloaded packages even when the Dockerfile changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

I only had CUDA to fix. Please take a look for comparison. I've provided two different approaches for doing CUDA, but I think using Ubuntu's (or Debian's) own packages works best overall.

Yea, before I dug in I thought I could just use the run file for the other arch, but it seems to fail when running on x86_64... and it fails in a very weird way. It must completely crash the container or something.

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

I found this method. https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=arm64-sbsa&Compilation=Cross&Distribution=Ubuntu&target_version=22.04&target_type=deb_network_cross

But they don't list Debian like they do for the run files, and I'm not sure the Ubuntu packages will work for that.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Did you work out the platform stuff yet, where you only use QEMU at the end? I think you did, but I'm not sure.

No, I couldn't figure it out. This was my best attempt. #2020 (comment)

@chewi

Copy link
Copy Markdown
Contributor

I discovered the cross installation method last night, but didn't implement it yet. I'll probably not use the ubuntu provided packages as for 22.04 it is cuda 11.5 and I'd guess for 20.04 is even older.

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

I found this method.

That method is one of the two I submitted. If you look at the individual commits, you'll see it. I think you only need a few header files, so that may be a way to take this approach while still supporting Debian and ppc64el.

I'll take a look at your PR for reference, but would it be possible for you to put your changes into the existing docker files? That will make it easier to see the changes.

Will do.

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Does it matter? Sunshine still builds with the older ones regardless, right? Or does that prevent it from working with newer GPUs?

It builds, but with less capability. See here:

if(CMAKE_CUDA_COMPILER_VERSIONVERSION_GREATER_EQUAL 11.8)

@chewi

Copy link
Copy Markdown
Contributor

I've been working with the Fedora 39 Dockerfile you have in your branch. I saw your approach above, and I can't see why it fails, but I didn't try it yet because I thought I might be able to do it a different way.

I began with FROM against the target arch while bind mounting the build stage inside that. I had then hoped to use bubblewrap or even just plain chroot to "turn the stage inside out" by mounting the target system within the build stage and then "entering" the build stage to execute commands. Unfortunately, this just isn't feasible in Docker without additional privileges. I did manage to execute dnf from the build stage without turning it inside out, but although there's not much code involved, it feels rather hacky and brittle.

SHELL ["/tmp/build/bin/ld.so", "--library-path", "/tmp/build/lib64", "/tmp/build/bin/bash", "-c"]
RUN --mount=type=bind,from=artifacts,source=/sunshine-${BASE}-${TAG}-${TARGETARCH}.rpm,target=/tmp/sunshine.rpm \
--mount=type=bind,from=sunshine-build,source=/,target=/tmp/build <<_INSTALL_SUNSHINE
set -e
export PATH=/tmp/build/sbin:/tmp/build/bin
export LD_LIBRARY_PATH=/tmp/build/lib64
LDSO=$(echo /tmp/build/lib*/ld-*.so.*)
/tmp/build/bin/ld.so $(type -P ln) -s "${LDSO}" "${LDSO#/tmp/build}"
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts update
python3 $(type -P dnf) -y --forcearch aarch64 --setopt=tsflags=noscripts install /tmp/sunshine.rpm
_INSTALL_SUNSHINE
SHELL ["/bin/sh", "-c"]

I looked around to see what other people do. It seems that they either avoid doing RUN at all in the final stage, which is easier when you have a single static binary, or they only use it for quick tasks, much like you did.

I'll now try your approach or something like it, as it feels like the only sensible option at this point.

@chewi

Copy link
Copy Markdown
Contributor

I got it to work, but for some reason it installed development packages and came out huge. Will figure it out later.

Co-Authored-By: James Le Cuirot <chewi@aura-online.co.uk>
@ReenigneArcher
ReenigneArcherforce-pushed the build-cross-compile-linux branch from a79567e to 4e49a8dCompareMarch 30, 2024 19:19
@codecov

codecovBot commented Mar 30, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 6.10%. Comparing base (991fab9) to head (4e49a8d).
Report is 291 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2020 +/- ##
=========================================
- Coverage 6.11% 6.10% -0.01% 
=========================================
Files 85 85 Lines 18303 18303 Branches 8319 8319 =========================================
- Hits 1119 1118 -1 + Misses 15375 15374 -1 - Partials 1809 1811 +2 
FlagCoverage Δ
Linux4.11% <ø> (ø)
Windows1.51% <ø> (ø)
macOS-128.13% <ø> (-0.13%)⬇️
macOS-137.44% <ø> (+0.04%)⬆️
macOS-147.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@ReenigneArcher

Copy link
Copy Markdown
MemberAuthor

Closing this for now, might revisit it at a later point.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ReenigneArcher@chewi