From b704571a2568ddb3621e86aad2ad67f81c8f6767 Mon Sep 17 00:00:00 2001 From: Nat Brown Date: Sat, 22 Aug 2026 21:40:59 -0400 Subject: [PATCH 1/5] configure.sh: avoid bash 4 only string operations ${var:0:-1} (negative substring length) and ${var,,} (lowercase expansion) are bash 4 features. macOS still ships bash 3.2, where both are syntax errors, so configure.sh cannot run there at all. Replace them with equivalents that work on both. Signed-off-by: Nat Brown --- configure.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.sh b/configure.sh index 60b6df4367e..fc2c59bbb6d 100755 --- a/configure.sh +++ b/configure.sh @@ -21,7 +21,7 @@ fi sh_quote() { local quoted - quoted="$(printf '%q ' "$@")"; [[ $# -eq 0 ]] || echo "${quoted:0:-1}"; + quoted="$(printf '%q ' "$@")"; [[ $# -eq 0 ]] || echo "${quoted}" | sed 's/ $//'; } err() { echo >&2 "${COLOR_ERR}!!${COLOR_CLEAR} $*"; } stat() { echo >&2 "${COLOR_STAT}::${COLOR_CLEAR} $*"; } @@ -115,7 +115,7 @@ function configure() { info "No build name specified, using default: $build_name" fi - if [[ ${build_name,,} == *proton* ]]; then + if echo "$build_name" | tr '[:upper:]' '[:lower:]' | grep -q 'proton'; then internal_tool_name=${build_name} else internal_tool_name=${build_name}-proton From 68ab18446b4d1851146840ade6bf5cbab8f0cf4a Mon Sep 17 00:00:00 2001 From: Nat Brown Date: Sat, 22 Aug 2026 21:40:59 -0400 Subject: [PATCH 2/5] configure.sh: select the container platform from the target architecture --target-arch selects the SDK image but nothing tells the container engine which platform to run it as, so it only works when the host is already native for the target. Building arm64 on an x86_64 host, or x86_64 on an arm64 host, either fails or silently depends on the engine guessing. Derive the platform from the target architecture and pass --platform when the host is not native for it, both when probing the engine and in the options used for the build itself. Same-architecture builds pass nothing, as before. Signed-off-by: Nat Brown --- configure.sh | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/configure.sh b/configure.sh index fc2c59bbb6d..5edf5f56691 100755 --- a/configure.sh +++ b/configure.sh @@ -48,15 +48,42 @@ dependency_command() { CONTAINER_MOUNT_OPTS="" +# The container platform follows the *target* architecture, not the host's: an +# arm64 build needs an arm64 container even on an x86_64 host, and an x86_64 +# build needs an amd64 container on an arm64 host. --platform is only passed +# when the host is not already native for it, so same-arch builds are unchanged. +CONTAINER_PLATFORM="" +TARGET_PLATFORM="" +HOST_PLATFORM="" + +platform_of_arch() { + case "$1" in + arm64|aarch64) echo "linux/arm64" ;; + *) echo "linux/amd64" ;; + esac +} + +set_container_platform() { + TARGET_PLATFORM="$(platform_of_arch "$1")" + HOST_PLATFORM="$(platform_of_arch "$(uname -m)")" + if [[ "$TARGET_PLATFORM" != "$HOST_PLATFORM" ]]; then + CONTAINER_PLATFORM="--platform $TARGET_PLATFORM" + info "Host is $HOST_PLATFORM, target needs $TARGET_PLATFORM: using '$CONTAINER_PLATFORM'" + else + CONTAINER_PLATFORM="" + fi +} + check_container_engine() { + local platform="$CONTAINER_PLATFORM" stat "Trying $1." - if ! cmd $1 run --rm $2; then + if ! cmd $1 run $platform --rm $2; then info "$1 is unable to run the container." return 1 fi touch permission_check - local inner_uid="$($1 run -v "$(pwd):/test$CONTAINER_MOUNT_OPTS" \ + local inner_uid="$($1 run $platform -v "$(pwd):/test$CONTAINER_MOUNT_OPTS" \ --rm $2 \ stat --format "%u" /test/permission_check 2>&1)" rm permission_check @@ -127,6 +154,14 @@ function configure() { fi info "Build targetting: $target_arch" + set_container_platform "$target_arch" + + # a non-native target needs its platform pinned for the build itself, not just + # for the engine probe above + if [[ -n "$CONTAINER_PLATFORM" ]]; then + arg_docker_opts="${arg_docker_opts} ${CONTAINER_PLATFORM}" + fi + # nothing specified, getting the default value from the Makefile to test the # container engine if [[ -z $steamrt_image ]]; then From 1a40f43b29ab434ed0f42fcfbe1d8cc0b5ae3b9b Mon Sep 17 00:00:00 2001 From: Nat Brown Date: Sat, 22 Aug 2026 21:41:33 -0400 Subject: [PATCH 3/5] configure.sh: support macOS hosts Three things a macOS host needs that a Linux host does not: - Rosetta. An x86_64 container on Apple Silicon must be translated by Rosetta rather than QEMU or the build does not work; check for it, but only when we are actually running an amd64 container on an arm64 host. - /etc/machine-id. Neither Docker nor Podman provide one on macOS and Wine requires it, so generate one and bind mount it. - MAKE. macOS resolves make to a path inside Xcode which does not exist in the container, so record an explicit /usr/bin/make in the generated Makefile. Also fail early if kern.maxfiles is too low; the default is well below what rsync and the source setup stages need, and the resulting failures are opaque. Signed-off-by: Nat Brown --- configure.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/configure.sh b/configure.sh index 5edf5f56691..a582acfadaf 100755 --- a/configure.sh +++ b/configure.sh @@ -103,6 +103,16 @@ check_container_engine() { err "File owner's UID doesn't map to 0 or $(id -u) in the container." die "Don't know how to map permissions. Please check your $1 setup." fi + + # Only x86_64-on-Apple-Silicon goes through Rosetta; a native arm64 container + # must not be required to have it. + if [ "$(uname)" = "Darwin" ] && [[ "$HOST_PLATFORM" == "linux/arm64" ]] \ + && [[ "$TARGET_PLATFORM" == "linux/amd64" ]]; then + if ! $1 run $platform --rm $2 \ + bash -c 'ls -ahl /proc/$$/exe 2>/dev/null | grep -q "rosetta"'; then + die "macOS: The container needs to be using Rosetta instruction emulation." + fi + fi } # @@ -193,6 +203,27 @@ function configure() { stat "Using $arg_container_engine." + if [ "$(uname)" = "Darwin" ]; then + # the default file handle limit is far below what rsync and the source + # setup stages need, and the resulting failures are opaque + if [ "$(sysctl -n kern.maxfiles)" -lt 100000 ]; then + die "macOS: system file handle limit too low. See README.md for instructions." + fi + + # neither Docker nor Podman provide a machine-id on macOS, and Wine needs one + machine_id_file="$(pwd)/etc/machine-id" + if [ ! -s "$machine_id_file" ]; then + mkdir -p "$(pwd)/etc" + uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]' > "$machine_id_file" + chmod 444 "$machine_id_file" + info "macOS: generated container machine-id: $(cat "$machine_id_file")" + fi + arg_docker_opts="${arg_docker_opts} -v $(pwd)/etc/machine-id:/etc/machine-id" + + # macOS resolves make to a path inside Xcode, which does not exist in the container + arg_make_override="/usr/bin/make" + fi + ## Write out config # Don't die after this point or we'll have rather unhelpfully deleted the Makefile [[ ! -e "$MAKEFILE" ]] || rm "$MAKEFILE" @@ -206,6 +237,10 @@ function configure() { echo "TARGET_ARCH := $(escape_for_make "$target_arch")" echo "INTERNAL_TOOL_NAME := $(escape_for_make "$internal_tool_name")" + if [[ -n "$arg_make_override" ]]; then + echo "MAKE := $(escape_for_make "$arg_make_override")" + fi + # SteamRT was specified, baking it into the Makefile if [[ -n $arg_protonsdk_image ]]; then echo "STEAMRT_IMAGE := $(escape_for_make "$arg_protonsdk_image")" @@ -241,6 +276,7 @@ arg_build_name="" arg_target_arch="" arg_container_engine="" arg_docker_opts="" +arg_make_override="" arg_relabel_volumes="" arg_enable_ccache="" arg_help="" From 7f194d1bb7b97f857c147ceb24be3298db873f7c Mon Sep 17 00:00:00 2001 From: Nat Brown Date: Sat, 22 Aug 2026 21:41:33 -0400 Subject: [PATCH 4/5] Makefile.in: add a container-shell target test-container proves the container runs, but there is no supported way to get a shell inside it with the same mounts and working directory the build uses. That is the quickest way to reproduce a failing build step by hand. Signed-off-by: Nat Brown --- Makefile | 5 +++++ Makefile.in | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/Makefile b/Makefile index 55ff219d06c..75967dda6a4 100644 --- a/Makefile +++ b/Makefile @@ -96,6 +96,11 @@ help: @echo " dxvk - Rebuild DXVK and copy it into $(BUILD_ROOT)/." @echo " lsteamclient - Rebuild the Steam client wrapper and copy it into $(BUILD_ROOT)/." @echo "" + @echo "Container targets:" + @echo " test-container - Tests whether the container environment is functional" + @echo " container-shell - Opens an interactive bash shell in the container environment." + @echo " Useful for evaluating new build steps." + @echo "" @echo "Examples:" @echo " make install - Build Proton and install into this user's Steam installation," @echo " with the current Proton branch name as the tool's name." diff --git a/Makefile.in b/Makefile.in index 5e477c7f99c..587e4b649d2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1516,6 +1516,13 @@ test-container: get-steamrt-image: echo $(STEAMRT_IMAGE) +.PHONY: container-shell +container-shell: + @echo >&2 ":: Entering interactive container shell" + $(CONTAINER_ENGINE) run --rm -v $(SRC):$(SRC)$(CONTAINER_MOUNT_OPTS) -v $(OBJ):$(OBJ)$(CONTAINER_MOUNT_OPTS) \ + -w $(OBJ) -e MAKEFLAGS \ + $(DOCKER_OPTS) -it $(STEAMRT_IMAGE) /bin/bash + STEAM_DIR := $(HOME)/.steam/root .PHONY: install install: all @@ -1534,6 +1541,7 @@ targets: $(info make deploy - create a build ready to be uploaded to steamworks in deploy/) $(info make redist - create an easily sharable proton redistributable in redist/) $(info make module=xyz module - build the selected wine dll) + $(info make container-shell - launch an interactive shell in the container environment) .PHONY: default From 52eef9ab8f4e07b7e07b0e3f1dd58a0c15ae307b Mon Sep 17 00:00:00 2001 From: Nat Brown Date: Sat, 22 Aug 2026 21:41:51 -0400 Subject: [PATCH 5/5] README: document building on a macOS host Covers the host prerequisites enforced by configure.sh: Rosetta 2 on Apple Silicon, the raised file handle limit, and the fact that Proton itself does not run on macOS. Signed-off-by: Nat Brown --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 5d4d2e9aa6a..158759b6677 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,22 @@ dependencies on the host side. ## Preparing the build environment You need either a Docker or a Podman setup which Proton's build system uses + +NOTE: A few considerations when building Proton on a macOS host: + +1. Proton does not run on macOS, but a Mac can build Proton for a Steam Deck or + for Linux Steam. +2. On Apple Silicon the container runtime must be configured to use Rosetta 2 + for x86-64 emulation -- the build does not work under QEMU. See the + [Docker](https://www.docker.com/blog/docker-desktop-4-25/) or + [Podman](https://podman-desktop.io/docs/podman/rosetta) instructions. Note + that Rosetta only handles x86-64; 32-bit x86 binaries still run under QEMU. +3. macOS default file handle limits are far too low for rsync and the source + setup stages. Raise them with + `sudo sysctl -w kern.maxfiles=1048576; sudo sysctl -w kern.maxfilesperproc=1048576` + and reboot. The change must be global to be picked up by container runtimes + and background daemons at boot. + internally. You should never need to use either container engine manually unless working on those parts of build system directly.