Proton/configure.sh
Arkadiusz Hiler 692ef0aa1f build: Do as much as possible inside of the SDK container.
Parts of the rules, including the magical ones created via
make/rules-*.mk, are executed inside of the container via SHELL
override, and parts are executed on the host side.

This makes reasoning about and debugging the rules much harder than it
should be. It also requirs the users to have certain programs installed
on the host in addition to docker/podman.

With this change `make` will act as a simple pass through to inside of
the container for the most part.

One notable exception is installation which still happens the host side.
2023-02-14 16:07:44 +02:00

308 lines
9.3 KiB
Bash
Executable File

#!/bin/bash
set -eu
SRCDIR="$(dirname "$0")"
DEFAULT_BUILD_NAME="proton-localbuild" # If no --build-name specified
# Output helpers
COLOR_ERR=""
COLOR_STAT=""
COLOR_INFO=""
COLOR_CMD=""
COLOR_CLEAR=""
if [[ $(tput colors 2>/dev/null || echo 0) -gt 0 ]]; then
COLOR_ERR=$'\e[31;1m'
COLOR_STAT=$'\e[32;1m'
COLOR_INFO=$'\e[30;1m'
COLOR_CMD=$'\e[93;1m'
COLOR_CLEAR=$'\e[0m'
fi
sh_quote() {
local quoted
quoted="$(printf '%q ' "$@")"; [[ $# -eq 0 ]] || echo "${quoted:0:-1}";
}
err() { echo >&2 "${COLOR_ERR}!!${COLOR_CLEAR} $*"; }
stat() { echo >&2 "${COLOR_STAT}::${COLOR_CLEAR} $*"; }
info() { echo >&2 "${COLOR_INFO}::${COLOR_CLEAR} $*"; }
showcmd() { echo >&2 "+ ${COLOR_CMD}$(sh_quote "$@")${COLOR_CLEAR}"; }
die() { err "$@"; exit 1; }
finish() { stat "$@"; exit 0; }
cmd() { showcmd "$@"; "$@"; }
#
# Dependency Checks
#
MISSING_DEPENDENCIES=0
dependency_command() {
local COMMAND=$1
shift
if ! command -v "$COMMAND" &> /dev/null; then
err "Couldn't find command '$COMMAND'. Please install ${@:-$COMMAND}."
MISSING_DEPENDENCIES=1
fi
}
CONTAINER_MOUNT_OPTS=""
check_container_engine() {
stat "Trying $1."
if ! cmd $1 run --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" \
--rm $2 \
stat --format "%u" /test/permission_check 2>&1)"
rm permission_check
if [[ $inner_uid == *"Permission denied"* ]]; then
err "The container cannot access files. Are you using SELinux?"
die "Please read README.md and check your $1 setup works."
elif [ "$inner_uid" -eq 0 ]; then
# namespace maps the user as root or the build is performed as host's root
ROOTLESS_CONTAINER=1
elif [ "$inner_uid" -eq "$(id -u)" ]; then
ROOTLESS_CONTAINER=0
else
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
}
#
# Configure
#
THIS_COMMAND="$0 $*" # For printing, not evaling
MAKEFILE="./Makefile"
# This is not rigorous. Do not use this for untrusted input. Do not. If you need a version of
# this for untrusted input, rethink the path that got you here.
function escape_for_make() {
local escape="$1"
escape="${escape//\\/\\\\}" # '\' -> '\\'
escape="${escape//#/\\#}" # '#' -> '\#'
escape="${escape//\$/\$\$}" # '$' -> '$$'
escape="${escape// /\\ }" # ' ' -> '\ '
echo "$escape"
}
function configure() {
local steamrt_image="$arg_protonsdk_image"
local srcdir
srcdir="$(dirname "$0")"
# nothing specified, getting the default value from the Makefile to test the
# container engine
if [[ -z $steamrt_image ]]; then
steamrt_image="$(sed -n 's/STEAMRT_IMAGE ?= //p' $SRCDIR/Makefile.in)"
fi
# Build name
local build_name="$arg_build_name"
if [[ -n $build_name ]]; then
info "Configuring with build name: $build_name"
else
build_name="$DEFAULT_BUILD_NAME"
info "No build name specified, using default: $build_name"
fi
dependency_command make "GNU Make"
if [ "$MISSING_DEPENDENCIES" -ne 0 ]; then
die "Missing dependencies, cannot continue."
fi
if [[ -n "$arg_relabel_volumes" ]]; then
CONTAINER_MOUNT_OPTS=:Z
fi
if [[ -n "$arg_container_engine" ]]; then
check_container_engine "$arg_container_engine" "$steamrt_image" || die "Specified container engine \"$arg_container_engine\" doesn't work"
else
stat "Trying to find usable container engine."
if check_container_engine docker "$steamrt_image"; then
arg_container_engine="docker"
elif check_container_engine podman "$steamrt_image"; then
arg_container_engine="podman"
else
die "${arg_container_engine:-Container engine discovery} has failed. Please fix your setup."
fi
fi
stat "Using $arg_container_engine."
## Write out config
# Don't die after this point or we'll have rather unhelpfully deleted the Makefile
[[ ! -e "$MAKEFILE" ]] || rm "$MAKEFILE"
{
# Config
echo "# Generated by: $THIS_COMMAND"
echo ""
echo "SRCDIR := $(escape_for_make "$srcdir")"
echo "BUILD_NAME := $(escape_for_make "$build_name")"
# SteamRT was specified, baking it into the Makefile
if [[ -n $arg_protonsdk_image ]]; then
echo "STEAMRT_IMAGE := $(escape_for_make "$arg_protonsdk_image")"
fi
echo "ROOTLESS_CONTAINER := $ROOTLESS_CONTAINER"
echo "CONTAINER_ENGINE := $arg_container_engine"
if [[ -n "$arg_docker_opts" ]]; then
echo "DOCKER_OPTS := $arg_docker_opts"
fi
if [[ -n "$CONTAINER_MOUNT_OPTS" ]]; then
echo "CONTAINER_MOUNT_OPTS := $CONTAINER_MOUNT_OPTS"
fi
if [[ -n "$arg_enable_ccache" ]]; then
echo "ENABLE_CCACHE := 1"
fi
# Include base
echo ""
echo "include \$(SRCDIR)/Makefile.in"
} >> "$MAKEFILE"
stat "Created $MAKEFILE, now run make to build."
stat " See README.md for make targets and instructions"
}
#
# Parse arguments
#
arg_protonsdk_image=""
arg_build_name=""
arg_container_engine=""
arg_docker_opts=""
arg_relabel_volumes=""
arg_enable_ccache=""
arg_help=""
invalid_args=""
function parse_args() {
local arg;
local val;
local val_used;
local val_passed;
while [[ $# -gt 0 ]]; do
arg="$1"
val=''
val_used=''
val_passed=''
if [[ -z $arg ]]; then # Sanity
err "Unexpected empty argument"
return 1
elif [[ ${arg:0:2} != '--' ]]; then
err "Unexpected positional argument ($1)"
return 1
fi
# Looks like an argument does it have a --foo=bar value?
if [[ ${arg%=*} != "$arg" ]]; then
val="${arg#*=}"
arg="${arg%=*}"
val_passed=1
else
# Otherwise for args that want a value, assume "--arg val" form
val="${2:-}"
fi
# The args
if [[ $arg = --help || $arg = --usage ]]; then
arg_help=1
elif [[ $arg = --build-name ]]; then
arg_build_name="$val"
val_used=1
elif [[ $arg = --container-engine ]]; then
arg_container_engine="$val"
val_used=1
elif [[ $arg = --docker-opts ]]; then
arg_docker_opts="$val"
val_used=1
elif [[ $arg = --relabel-volumes ]]; then
arg_relabel_volumes="1"
elif [[ $arg = --enable-ccache ]]; then
arg_enable_ccache="1"
elif [[ $arg = --proton-sdk-image ]]; then
val_used=1
arg_protonsdk_image="$val"
else
err "Unrecognized option $arg"
return 1
fi
# Check if this arg used the value and shouldn't have or vice-versa
if [[ -n $val_used && -z $val_passed ]]; then
# "--arg val" form, used $2 as the value.
# Don't allow this if it looked like "--arg --val"
if [[ ${val#--} != "$val" ]]; then
err "Ambiguous format for argument with value \"$arg $val\""
err " (use $arg=$val or $arg='' $val)"
return 1
fi
# Error if this was the last positional argument but expected $val
if [[ $# -le 1 ]]; then
err "$arg takes a parameter, but none given"
return 1
fi
shift # consume val
elif [[ -z $val_used && -n $val_passed ]]; then
# Didn't use a value, but passed in --arg=val form
err "$arg does not take a parameter"
return 1
fi
shift # consume arg
done
}
usage() {
"$1" "Usage: $0 { --no-proton-sdk | --proton-sdk-image=<image> --steam-runtime=<name> }"
"$1" " Generate a Makefile for building Proton. May be run from another directory to create"
"$1" " out-of-tree build directories (e.g. mkdir mybuild && cd mybuild && ../configure.sh)"
"$1" ""
"$1" " Options"
"$1" " --help / --usage Show this help text and exit"
"$1" ""
"$1" " --build-name=<name> Set the name of the build that displays when used in Steam"
"$1" ""
"$1" " --container-engine=<engine> Which Docker-compatible container engine to use,"
"$1" " e.g. podman. Tries to do autodiscovery when not specified."
"$1" ""
"$1" " --docker-opts='<options>' Extra options to pass to Docker when invoking the runtime."
"$1" ""
"$1" " --relabel-volumes Bind-mounted volumes will be relabeled. Use with caution."
"$1" ""
"$1" " --enable-ccache Mount \$CCACHE_DIR or \$HOME/.ccache inside of the container and use ccache for the build."
"$1" ""
"$1" " Steam Runtime"
"$1" " Proton builds that are to be installed & run under the steam client must be built with"
"$1" " the Steam Runtime SDK to ensure compatibility. See README.md for more information."
"$1" ""
"$1" " --proton-sdk-image=<image> Automatically invoke the Steam Runtime SDK in <image>"
"$1" " for build steps that must be run in an SDK"
"$1" " environment. See README.md for instructions to"
"$1" " create this image."
"$1" " --steam-runtime=soldier Name of the steam runtime release to build for (soldier, scout)."
"$1" ""
"$1" " --no-proton-sdk Do not automatically invoke any runtime SDK as part of the build."
"$1" " Build steps may still be manually run in a runtime environment."
exit 1;
}
parse_args "$@" || usage err
[[ -z $arg_help ]] || usage info
configure