mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-12 18:57:51 -04:00
440 lines
12 KiB
Bash
440 lines
12 KiB
Bash
#!/bin/sh
|
|
|
|
set -u
|
|
|
|
err() {
|
|
echo "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
# NOTICE: If you change anything here, please make the same changes in self_install.rs
|
|
usage() {
|
|
cat <<EOF
|
|
Install the SpacetimeDB CLI
|
|
|
|
Usage: spacetime-install[EXE] [OPTIONS]
|
|
|
|
Options:
|
|
--root-dir <ROOT_DIR>
|
|
The directory to locally install SpacetimeDB into. If unspecified,
|
|
uses platform defaults
|
|
|
|
-y, --yes
|
|
Skip the confirmation dialog
|
|
|
|
-h, --help
|
|
Print help
|
|
EOF
|
|
}
|
|
|
|
SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/latest/download}"
|
|
|
|
main() {
|
|
# First make sure all config is valid, we don't want to end up with
|
|
# a partial install.
|
|
local _downloader
|
|
if check_cmd curl; then
|
|
_downloader=curl
|
|
elif check_cmd wget; then
|
|
_downloader=wget
|
|
else
|
|
err "need 'curl' or 'wget' (command not found)"
|
|
fi
|
|
need_cmd chmod
|
|
need_cmd rm
|
|
need_cmd rmdir
|
|
need_cmd mktemp
|
|
|
|
# Detect the OS and arch
|
|
get_architecture || return 1
|
|
local _host="$RETVAL"
|
|
[ -z "$_host" ] && err "failed to get architecture"
|
|
|
|
local _ext=""
|
|
case "$_host" in
|
|
*windows*) _ext=".exe" ;;
|
|
esac
|
|
|
|
# We can now install the binary
|
|
local _tmpdir
|
|
_tmpdir="$(ensure mktemp -d)" || exit 1
|
|
local _download_file="$_tmpdir/spacetime-install$_ext"
|
|
|
|
check_for_help_flag "$@"
|
|
|
|
# Define the latest SpacetimeDB download url
|
|
local _url="$SPACETIME_DOWNLOAD_ROOT/spacetimedb-update-$_host$_ext"
|
|
echo "Downloading installer..."
|
|
if [ "$_downloader" = curl ]; then
|
|
local _httpstatus
|
|
if ! _httpstatus="$(curl -sSfL -w '%{http_code}' --progress-bar "$_url" -o "$_download_file")"; then
|
|
if [ "$_httpstatus" = 404 ]; then
|
|
err "It seems like we may not have prebuilt binaries for your platform."
|
|
fi
|
|
exit 1
|
|
fi
|
|
elif [ "$_downloader" = wget ]; then
|
|
wget "$_url" -O "$_download_file" || exit 1
|
|
fi
|
|
|
|
ensure chmod u+x "$_download_file"
|
|
if [ ! -x "$_download_file" ]; then
|
|
printf '%s\n' "Cannot execute $_download_file (likely because of mounting /tmp as noexec)." 1>&2
|
|
printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./spacetime-install${_ext}." 1>&2
|
|
exit 1
|
|
fi
|
|
|
|
"$_download_file" "$@"
|
|
local _retval=$?
|
|
|
|
rm -f "$_download_file"
|
|
rmdir "$_tmpdir"
|
|
|
|
return "$_retval"
|
|
}
|
|
|
|
check_for_help_flag() {
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
OPTIND=1
|
|
if [ "${arg%%--*}" = "" ]; then
|
|
# Long option (other than --help);
|
|
# don't attempt to interpret it.
|
|
continue
|
|
fi
|
|
while getopts :hy sub_arg "$arg"; do
|
|
case "$sub_arg" in
|
|
h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
done
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
|
|
# The following functions are from rustup-init.sh of the Rust project:
|
|
# get_architecture, get_endianness, need_cmd, check_cmd, ensure
|
|
# Licensed under the MIT license:
|
|
## Copyright (c) 2016 The Rust Project Developers
|
|
##
|
|
## Permission is hereby granted, free of charge, to any
|
|
## person obtaining a copy of this software and associated
|
|
## documentation files (the "Software"), to deal in the
|
|
## Software without restriction, including without
|
|
## limitation the rights to use, copy, modify, merge,
|
|
## publish, distribute, sublicense, and/or sell copies of
|
|
## the Software, and to permit persons to whom the Software
|
|
## is furnished to do so, subject to the following
|
|
## conditions:
|
|
##
|
|
## The above copyright notice and this permission notice
|
|
## shall be included in all copies or substantial portions
|
|
## of the Software.
|
|
##
|
|
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
## ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
## TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
## PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
## SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
## CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
## IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
## DEALINGS IN THE SOFTWARE.
|
|
get_architecture() {
|
|
local _ostype _cputype _bitness _arch _clibtype
|
|
_ostype="$(uname -s)"
|
|
_cputype="$(uname -m)"
|
|
_clibtype="gnu"
|
|
|
|
if [ "$_ostype" = Linux ]; then
|
|
if [ "$(uname -o)" = Android ]; then
|
|
_ostype=Android
|
|
fi
|
|
if ldd --version 2>&1 | grep -q 'musl'; then
|
|
_clibtype="musl"
|
|
fi
|
|
fi
|
|
|
|
if [ "$_ostype" = Darwin ]; then
|
|
# Darwin `uname -m` can lie due to Rosetta shenanigans. If you manage to
|
|
# invoke a native shell binary and then a native uname binary, you can
|
|
# get the real answer, but that's hard to ensure, so instead we use
|
|
# `sysctl` (which doesn't lie) to check for the actual architecture.
|
|
if [ "$_cputype" = i386 ]; then
|
|
# Handling i386 compatibility mode in older macOS versions (<10.15)
|
|
# running on x86_64-based Macs.
|
|
# Starting from 10.15, macOS explicitly bans all i386 binaries from running.
|
|
# See: <https://support.apple.com/en-us/HT208436>
|
|
|
|
# Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code.
|
|
if sysctl hw.optional.x86_64 2> /dev/null || true | grep -q ': 1'; then
|
|
_cputype=x86_64
|
|
fi
|
|
elif [ "$_cputype" = x86_64 ]; then
|
|
# Handling x86-64 compatibility mode (a.k.a. Rosetta 2)
|
|
# in newer macOS versions (>=11) running on arm64-based Macs.
|
|
# Rosetta 2 is built exclusively for x86-64 and cannot run i386 binaries.
|
|
|
|
# Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code.
|
|
if sysctl hw.optional.arm64 2> /dev/null || true | grep -q ': 1'; then
|
|
_cputype=arm64
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ "$_ostype" = SunOS ]; then
|
|
# Both Solaris and illumos presently announce as "SunOS" in "uname -s"
|
|
# so use "uname -o" to disambiguate. We use the full path to the
|
|
# system uname in case the user has coreutils uname first in PATH,
|
|
# which has historically sometimes printed the wrong value here.
|
|
if [ "$(/usr/bin/uname -o)" = illumos ]; then
|
|
_ostype=illumos
|
|
fi
|
|
|
|
# illumos systems have multi-arch userlands, and "uname -m" reports the
|
|
# machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86
|
|
# systems. Check for the native (widest) instruction set on the
|
|
# running kernel:
|
|
if [ "$_cputype" = i86pc ]; then
|
|
_cputype="$(isainfo -n)"
|
|
fi
|
|
fi
|
|
|
|
case "$_ostype" in
|
|
|
|
Android)
|
|
_ostype=linux-android
|
|
;;
|
|
|
|
Linux)
|
|
check_proc
|
|
_ostype=unknown-linux-$_clibtype
|
|
_bitness=$(get_bitness)
|
|
;;
|
|
|
|
FreeBSD)
|
|
_ostype=unknown-freebsd
|
|
;;
|
|
|
|
NetBSD)
|
|
_ostype=unknown-netbsd
|
|
;;
|
|
|
|
DragonFly)
|
|
_ostype=unknown-dragonfly
|
|
;;
|
|
|
|
Darwin)
|
|
_ostype=apple-darwin
|
|
;;
|
|
|
|
illumos)
|
|
_ostype=unknown-illumos
|
|
;;
|
|
|
|
MINGW* | MSYS* | CYGWIN* | Windows_NT)
|
|
_ostype=pc-windows-gnu
|
|
;;
|
|
|
|
*)
|
|
err "unrecognized OS type: $_ostype"
|
|
;;
|
|
|
|
esac
|
|
|
|
case "$_cputype" in
|
|
|
|
i386 | i486 | i686 | i786 | x86)
|
|
_cputype=i686
|
|
;;
|
|
|
|
xscale | arm)
|
|
_cputype=arm
|
|
if [ "$_ostype" = "linux-android" ]; then
|
|
_ostype=linux-androideabi
|
|
fi
|
|
;;
|
|
|
|
armv6l)
|
|
_cputype=arm
|
|
if [ "$_ostype" = "linux-android" ]; then
|
|
_ostype=linux-androideabi
|
|
else
|
|
_ostype="${_ostype}eabihf"
|
|
fi
|
|
;;
|
|
|
|
armv7l | armv8l)
|
|
_cputype=armv7
|
|
if [ "$_ostype" = "linux-android" ]; then
|
|
_ostype=linux-androideabi
|
|
else
|
|
_ostype="${_ostype}eabihf"
|
|
fi
|
|
;;
|
|
|
|
aarch64 | arm64)
|
|
_cputype=aarch64
|
|
;;
|
|
|
|
x86_64 | x86-64 | x64 | amd64)
|
|
_cputype=x86_64
|
|
;;
|
|
|
|
mips)
|
|
_cputype=$(get_endianness mips '' el)
|
|
;;
|
|
|
|
mips64)
|
|
if [ "$_bitness" -eq 64 ]; then
|
|
# only n64 ABI is supported for now
|
|
_ostype="${_ostype}abi64"
|
|
_cputype=$(get_endianness mips64 '' el)
|
|
fi
|
|
;;
|
|
|
|
ppc)
|
|
_cputype=powerpc
|
|
;;
|
|
|
|
ppc64)
|
|
_cputype=powerpc64
|
|
;;
|
|
|
|
ppc64le)
|
|
_cputype=powerpc64le
|
|
;;
|
|
|
|
s390x)
|
|
_cputype=s390x
|
|
;;
|
|
riscv64)
|
|
_cputype=riscv64gc
|
|
;;
|
|
loongarch64)
|
|
_cputype=loongarch64
|
|
ensure_loongarch_uapi
|
|
;;
|
|
*)
|
|
err "unknown CPU type: $_cputype"
|
|
|
|
esac
|
|
|
|
# Detect 64-bit linux with 32-bit userland
|
|
if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then
|
|
case $_cputype in
|
|
x86_64)
|
|
_cputype=i686
|
|
;;
|
|
mips64)
|
|
_cputype=$(get_endianness mips '' el)
|
|
;;
|
|
powerpc64)
|
|
_cputype=powerpc
|
|
;;
|
|
aarch64)
|
|
_cputype=armv7
|
|
if [ "$_ostype" = "linux-android" ]; then
|
|
_ostype=linux-androideabi
|
|
else
|
|
_ostype="${_ostype}eabihf"
|
|
fi
|
|
;;
|
|
riscv64gc)
|
|
err "riscv64 with 32-bit userland unsupported"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Detect armv7 but without the CPU features Rust needs in that build,
|
|
# and fall back to arm.
|
|
# See https://github.com/rust-lang/rustup.rs/issues/587.
|
|
if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then
|
|
if ensure grep '^Features' /proc/cpuinfo | grep -E -q -v 'neon|simd'; then
|
|
# At least one processor does not have NEON (which is asimd on armv8+).
|
|
_cputype=arm
|
|
fi
|
|
fi
|
|
|
|
_arch="${_cputype}-${_ostype}"
|
|
|
|
RETVAL="$_arch"
|
|
}
|
|
|
|
check_proc() {
|
|
# Check for /proc by looking for the /proc/self/exe link
|
|
# This is only run on Linux
|
|
if ! test -L /proc/self/exe ; then
|
|
err "fatal: Unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc."
|
|
fi
|
|
}
|
|
|
|
get_bitness() {
|
|
need_cmd head
|
|
# Architecture detection without dependencies beyond coreutils.
|
|
# ELF files start out "\x7fELF", and the following byte is
|
|
# 0x01 for 32-bit and
|
|
# 0x02 for 64-bit.
|
|
# The printf builtin on some shells like dash only supports octal
|
|
# escape sequences, so we use those.
|
|
local _current_exe_head
|
|
_current_exe_head=$(head -c 5 /proc/self/exe )
|
|
if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then
|
|
echo 32
|
|
elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then
|
|
echo 64
|
|
else
|
|
err "unknown platform bitness"
|
|
fi
|
|
}
|
|
|
|
get_endianness() {
|
|
local cputype=$1
|
|
local suffix_eb=$2
|
|
local suffix_el=$3
|
|
|
|
# detect endianness without od/hexdump, like get_bitness() does.
|
|
need_cmd head
|
|
need_cmd tail
|
|
|
|
local _current_exe_endianness
|
|
_current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)"
|
|
if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then
|
|
echo "${cputype}${suffix_el}"
|
|
elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then
|
|
echo "${cputype}${suffix_eb}"
|
|
else
|
|
err "unknown platform endianness"
|
|
fi
|
|
}
|
|
|
|
need_cmd() {
|
|
if ! check_cmd "$1"; then
|
|
err "need '$1' (command not found)"
|
|
fi
|
|
}
|
|
|
|
check_cmd() {
|
|
command -v "$1" > /dev/null 2>&1
|
|
}
|
|
|
|
# Run a command that should never fail. If the command fails execution
|
|
# will immediately terminate with an error showing the failing
|
|
# command.
|
|
ensure() {
|
|
if ! "$@"; then err "command failed: $*"; fi
|
|
}
|
|
|
|
main "$@" || exit 1
|