mirror of
https://github.com/InsanusMokrassar/KSLog.git
synced 2026-11-02 09:42:56 +00:00
287 lines
11 KiB
Bash
Executable File
287 lines
11 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
#
|
|
# Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
|
#
|
|
|
|
# Possible environment variables:
|
|
# KOTLIN_CLI_DOWNLOAD_ROOT Maven repository to download the Kotlin CLI dist from.
|
|
# default: https://packages.jetbrains.team/maven/p/amper/amper
|
|
# KOTLIN_CLI_JRE_DOWNLOAD_ROOT Url prefix to download the JRE to run the Kotlin CLI
|
|
# default: https:/
|
|
# KOTLIN_CLI_BOOTSTRAP_CACHE_DIR Cache directory to store the extracted JRE and Kotlin CLI distribution
|
|
# KOTLIN_CLI_JAVA_HOME JRE to run the Kotlin CLI itself (optional, does not affect compilation)
|
|
# KOTLIN_CLI_JAVA_OPTIONS JVM options to pass to the JVM running the Kotlin CLI (does not affect the user's application)
|
|
# KOTLIN_CLI_NO_WELCOME_BANNER Disables the first-run welcome message if set to a non-empty value
|
|
|
|
set -e -u
|
|
|
|
# The version of the Kotlin Toolchain (and CLI) distribution to provision and use
|
|
kotlin_cli_version=0.13.0-dev-4444
|
|
# Establish chain of trust from here by specifying exact checksum of Kotlin Toolchain (and CLI) distribution to be run
|
|
kotlin_cli_sha256=13c2448146ac6dba0fa3a4e5d367dab73d8cfd7400bab348bebe905c165adecf
|
|
|
|
KOTLIN_CLI_DOWNLOAD_ROOT="${KOTLIN_CLI_DOWNLOAD_ROOT:-https://packages.jetbrains.team/maven/p/amper/amper}"
|
|
|
|
die() {
|
|
echo >&2
|
|
echo "$@" >&2
|
|
echo >&2
|
|
exit 1
|
|
}
|
|
|
|
# usage: check_sha SOURCE_MONIKER FILE SHA_CHECKSUM SHA_SIZE
|
|
# $1 SOURCE_MONIKER (e.g. url)
|
|
# $2 FILE
|
|
# $3 SHA hex string
|
|
# $4 SHA size in bits (256, 512, ...)
|
|
check_sha() {
|
|
sha_size=$4
|
|
if command -v shasum >/dev/null 2>&1; then
|
|
echo "$3 *$2" | shasum -a "$sha_size" --status -c || {
|
|
die "ERROR: Checksum mismatch for $2 (downloaded from $1): expected checksum $3 but got $(shasum --binary -a "$sha_size" "$2" | awk '{print $1}')"
|
|
}
|
|
return 0
|
|
fi
|
|
|
|
shaNsumCommand="sha${sha_size}sum"
|
|
if command -v "$shaNsumCommand" >/dev/null 2>&1; then
|
|
# discard the output as sha*sum may print redundant warnings in some versions
|
|
echo "$3 *$2" | $shaNsumCommand -w -c >/dev/null 2>&1 || {
|
|
die "ERROR: Checksum mismatch for $2 (downloaded from $1): expected checksum $3 but got $($shaNsumCommand "$2" | awk '{print $1}')"
|
|
}
|
|
return 0
|
|
fi
|
|
|
|
echo "Both 'shasum' and 'sha${sha_size}sum' utilities are missing. Please install one of them"
|
|
return 1
|
|
}
|
|
|
|
|
|
download_and_extract() {
|
|
moniker="$1"
|
|
file_url="$2"
|
|
file_sha="$3"
|
|
sha_size="$4"
|
|
cache_dir="$5"
|
|
extract_dir="$6"
|
|
show_banner_on_cache_miss="$7"
|
|
|
|
if [ -e "$extract_dir/.flag" ] && [ "$(cat "$extract_dir/.flag")" = "${file_sha}" ]; then
|
|
# Everything is up-to-date in $extract_dir, do nothing
|
|
return 0;
|
|
fi
|
|
|
|
mkdir -p "$cache_dir"
|
|
|
|
# Take a lock for the download of this file
|
|
short_sha=$(echo "$file_sha" | cut -c1-32) # cannot use the ${short_sha:0:32} syntax in regular /bin/sh
|
|
download_lock_file="$cache_dir/download-${short_sha}.lock"
|
|
process_lock_file="$cache_dir/download-${short_sha}.$$.lock"
|
|
echo $$ >"$process_lock_file"
|
|
while ! ln "$process_lock_file" "$download_lock_file" 2>/dev/null; do
|
|
lock_owner=$(cat "$download_lock_file" 2>/dev/null || true)
|
|
# We use `kill -0` instead of `ps -p` as the first one is more portable
|
|
if [ -n "$lock_owner" ] && kill -0 "$lock_owner" >/dev/null; then
|
|
echo "Another Kotlin CLI instance (pid $lock_owner) is downloading $moniker. Awaiting the result..."
|
|
sleep 1
|
|
elif [ -n "$lock_owner" ] && [ "$(cat "$download_lock_file" 2>/dev/null)" = "$lock_owner" ]; then
|
|
rm -f "$download_lock_file"
|
|
# We don't want to simply loop again here, because multiple concurrent processes may face this at the same time,
|
|
# which means the 'rm' command above from another script could delete our new valid lock file. Instead, we just
|
|
# ask the user to try again. This doesn't 100% eliminate the race, but the probability of issues is drastically
|
|
# reduced because it would involve 4 processes with perfect timing. We can revisit this later.
|
|
die "Another Kotlin CLI instance (pid $lock_owner) locked the download of $moniker, but is no longer running. The lock file is now removed, please try again."
|
|
fi
|
|
done
|
|
|
|
# shellcheck disable=SC2064
|
|
trap "rm -f \"$download_lock_file\"" EXIT
|
|
rm -f "$process_lock_file"
|
|
|
|
unlock_and_cleanup() {
|
|
rm -f "$download_lock_file"
|
|
trap - EXIT
|
|
return 0
|
|
}
|
|
|
|
if [ -e "$extract_dir/.flag" ] && [ "$(cat "$extract_dir/.flag")" = "${file_sha}" ]; then
|
|
# Everything is up-to-date in $extract_dir, just release the lock
|
|
unlock_and_cleanup
|
|
return 0;
|
|
fi
|
|
|
|
if [ "$show_banner_on_cache_miss" = "true" ] && [ -z "${KOTLIN_CLI_NO_WELCOME_BANNER:-}" ]; then
|
|
echo
|
|
cat <<EOF
|
|
Welcome to
|
|
|
|
@@@ @@@@ @@@ @@@
|
|
@@@ @@@@ @@@ @@@ @@@
|
|
@@@ #@@@" ,@@@ @@@
|
|
@@@ ,@@@% ,@@@@@@@, @@@@@@@@@@ @@@ @@@ @@@ ,@@@@@,
|
|
@@@ @@@@ @@@@@%"%@@@@@ @@@@@@@@@@ @@@ @@@ @@@@@@%%@@@@@
|
|
@@@@@@% @@@% %@@@ @@@ @@@ @@@ @@@@ %@@%
|
|
@@@ @@@@= #@@@ @@@# @@@ @@@ @@@ @@@ @@@
|
|
@@@ @@@@ #@@@ @@@# @@@ @@@ @@@ @@@ @@@
|
|
@@@ *@@@% @@@@ @@@@ @@@ @@@ @@@ @@@ @@@
|
|
@@@ %@@@= %@@@@*,*@@@@% @@@@### @@@ @@@ @@@ @@@
|
|
@@@ @@@@ "@@@@@@@" %@@@@@ @@@ @@@ @@@ @@@
|
|
EOF
|
|
echo
|
|
echo "This is the first run of the Kotlin CLI v$kotlin_cli_version, so we need to download the Kotlin Toolchain."
|
|
echo "Please give us a few seconds, subsequent runs will be faster."
|
|
echo
|
|
fi
|
|
|
|
echo "Downloading $moniker..."
|
|
|
|
temp_file="$cache_dir/download-file-$$.bin"
|
|
rm -f "$temp_file"
|
|
if command -v curl >/dev/null 2>&1; then
|
|
if [ -t 1 ]; then CURL_PROGRESS="--progress-bar"; else CURL_PROGRESS="--silent --show-error"; fi
|
|
# shellcheck disable=SC2086
|
|
curl $CURL_PROGRESS -L --fail --retry 5 --connect-timeout 30 --output "${temp_file}" "$file_url"
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
if [ -t 1 ]; then WGET_PROGRESS=""; else WGET_PROGRESS="-nv"; fi
|
|
wget $WGET_PROGRESS --tries=5 --connect-timeout=30 --read-timeout=120 -O "${temp_file}" "$file_url"
|
|
else
|
|
die "ERROR: Please install 'wget' or 'curl', as one of them is required to download $moniker"
|
|
fi
|
|
|
|
check_sha "$file_url" "$temp_file" "$file_sha" "$sha_size"
|
|
|
|
rm -rf "$extract_dir"
|
|
mkdir -p "$extract_dir"
|
|
|
|
case "$file_url" in
|
|
*".zip")
|
|
if command -v unzip >/dev/null 2>&1; then
|
|
unzip -q "$temp_file" -d "$extract_dir"
|
|
else
|
|
die "ERROR: Please install 'unzip', which is required to extract $moniker"
|
|
fi ;;
|
|
*)
|
|
if command -v tar >/dev/null 2>&1; then
|
|
tar -x -f "$temp_file" -C "$extract_dir"
|
|
else
|
|
die "ERROR: Please install 'tar', which is required to extract $moniker"
|
|
fi ;;
|
|
esac
|
|
|
|
rm -f "$temp_file"
|
|
|
|
echo "$file_sha" >"$extract_dir/.flag"
|
|
|
|
# Unlock and cleanup the lock file
|
|
unlock_and_cleanup
|
|
|
|
echo "Download complete."
|
|
echo
|
|
}
|
|
|
|
|
|
# ********** Project-local version detection **********
|
|
|
|
# 1. Search upwards for an executable `amper` file and/or `project.yaml`
|
|
# Sets wrapper_script to the found wrapper path, or empty string if not found.
|
|
find_project_context() {
|
|
wrapper_script=""
|
|
this_script="$(realpath "$0")"
|
|
project_dir=$(pwd)
|
|
while [ "$project_dir" != "/" ] && [ -n "$project_dir" ]; do
|
|
wrapper_candidate="$project_dir/kotlin"
|
|
if [ "$this_script" = "$wrapper_candidate" ]; then
|
|
# Found itself (local wrapper case), no need to update any version or search further.
|
|
return 1
|
|
fi
|
|
|
|
if [ -f "$wrapper_candidate" ] && [ -x "$wrapper_candidate" ]; then
|
|
# Found the wrapper — check that a project context exists alongside it
|
|
if [ -f "$project_dir/project.yaml" ] || [ -f "$project_dir/module.yaml" ]; then
|
|
wrapper_script="$wrapper_candidate"
|
|
return 0
|
|
else
|
|
echo "WARNING: Found wrapper script '$wrapper_candidate', but no project.yaml or module.yaml near it. Skipping." >&2
|
|
# Continue the search
|
|
fi
|
|
elif [ -f "$project_dir/project.yaml" ]; then
|
|
# Found project.yaml but no executable wrapper alongside it
|
|
echo "WARNING: Found a project.yaml in '$project_dir', but the wrapper script is missing; using Kotlin Toolchain v$kotlin_cli_version." >&2
|
|
return 1
|
|
fi
|
|
|
|
project_dir=$(dirname "$project_dir")
|
|
done
|
|
# Do not check root '/' - it's an unlikely candidate for a project
|
|
|
|
return 1
|
|
}
|
|
|
|
parse_project_context() {
|
|
# Parse kotlin_cli_version and kotlin_cli_sha256 from "$wrapper_script" without executing it.
|
|
parsed_kotlin_cli_version=$(
|
|
sed -n 's/^kotlin_cli_version=\([A-Za-z0-9._+-]\{1,\}\)[[:space:]]*$/\1/p' "$wrapper_script" \
|
|
| head -n 1
|
|
)
|
|
parsed_kotlin_cli_sha256=$(
|
|
sed -n 's/^kotlin_cli_sha256=\([0-9a-fA-F]\{64\}\)[[:space:]]*$/\1/p' "$wrapper_script" \
|
|
| head -n 1
|
|
)
|
|
|
|
if [ -z "$parsed_kotlin_cli_version" ]; then
|
|
echo "ERROR: Suspicious local wrapper script: failed to detect the distribution version in '$wrapper_script'" >&2
|
|
return 1
|
|
fi
|
|
if [ -z "$parsed_kotlin_cli_sha256" ]; then
|
|
echo "ERROR: Suspicious local wrapper script: failed to detect the distribution checksum in '$wrapper_script'" >&2
|
|
return 1
|
|
fi
|
|
|
|
# overwrite builtin values and proceed
|
|
kotlin_cli_version=$parsed_kotlin_cli_version
|
|
kotlin_cli_sha256=$parsed_kotlin_cli_sha256
|
|
return 0
|
|
}
|
|
|
|
if [ -z "${KOTLIN_CLI_WRAPPER_ALWAYS_USE_INTRINSIC_VERSION:-}" ]; then
|
|
find_project_context && parse_project_context
|
|
fi
|
|
|
|
# ********** System detection **********
|
|
|
|
kernelName=$(uname -s)
|
|
case "$kernelName" in
|
|
Darwin* )
|
|
default_kotlin_cli_cache_dir="$HOME/Library/Caches/JetBrains/Kotlin/cli"
|
|
;;
|
|
Linux* )
|
|
default_kotlin_cli_cache_dir="$HOME/.cache/JetBrains/Kotlin/cli"
|
|
;;
|
|
CYGWIN* | MSYS* | MINGW* )
|
|
if command -v cygpath >/dev/null 2>&1; then
|
|
default_kotlin_cli_cache_dir=$(cygpath -u "$LOCALAPPDATA\JetBrains\Kotlin\cli")
|
|
else
|
|
die "The 'cypath' command is not available, but the Kotlin CLI needs it. Use kotlin.bat instead, or try a Cygwin or MSYS environment."
|
|
fi
|
|
;;
|
|
*)
|
|
die "Unsupported platform $kernelName"
|
|
;;
|
|
esac
|
|
|
|
kotlin_cli_cache_dir="${KOTLIN_CLI_BOOTSTRAP_CACHE_DIR:-$default_kotlin_cli_cache_dir}"
|
|
|
|
# ********** Provision the Kotlin Toolchain distribution **********
|
|
|
|
kotlin_cli_url="$KOTLIN_CLI_DOWNLOAD_ROOT/org/jetbrains/kotlin/kotlin-cli/$kotlin_cli_version/kotlin-cli-$kotlin_cli_version-dist.tgz"
|
|
kotlin_cli_target_dir="$kotlin_cli_cache_dir/kotlin-cli-$kotlin_cli_version"
|
|
download_and_extract "Kotlin Toolchain distribution v$kotlin_cli_version" "$kotlin_cli_url" "$kotlin_cli_sha256" 256 "$kotlin_cli_cache_dir" "$kotlin_cli_target_dir" "true"
|
|
|
|
# ********** Launch the Kotlin CLI **********
|
|
|
|
launcher_script="$kotlin_cli_target_dir/bin/launcher.sh"
|
|
|
|
KOTLIN_CLI_WRAPPER_PATH="$(realpath "$0")" \
|
|
exec /bin/sh "$launcher_script" "$@"
|