@echo off @rem @rem Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. @rem @rem Possible environment variables: @rem KOTLIN_CLI_DOWNLOAD_ROOT Maven repository to download the Kotlin CLI dist from @rem default: https://packages.jetbrains.team/maven/p/amper/amper @rem KOTLIN_CLI_JRE_DOWNLOAD_ROOT Url prefix to download the Kotlin CLI JRE from. @rem default: https:/ @rem KOTLIN_CLI_BOOTSTRAP_CACHE_DIR Cache directory to store the extracted JRE and Kotlin CLI distribution @rem KOTLIN_CLI_JAVA_HOME JRE to run the Kotlin CLI itself (optional, does not affect compilation) @rem KOTLIN_CLI_JAVA_OPTIONS JVM options to pass to the JVM running the Kotlin CLI (does not affect the user's application) @rem KOTLIN_CLI_NO_WELCOME_BANNER Disables the first-run welcome message if set to a non-empty value setlocal @rem The version of the Kotlin Toolchain distribution to provision and use set kotlin_cli_version=0.13.0-dev-4444 @rem Establish chain of trust from here by specifying the exact checksum of the Kotlin Toolchain distribution to be run set kotlin_cli_sha256=13c2448146ac6dba0fa3a4e5d367dab73d8cfd7400bab348bebe905c165adecf if not defined KOTLIN_CLI_DOWNLOAD_ROOT set KOTLIN_CLI_DOWNLOAD_ROOT=https://packages.jetbrains.team/maven/p/amper/amper if not defined KOTLIN_CLI_BOOTSTRAP_CACHE_DIR set KOTLIN_CLI_BOOTSTRAP_CACHE_DIR=%LOCALAPPDATA%\JetBrains\Kotlin\cli @rem remove trailing \ if present if [%KOTLIN_CLI_BOOTSTRAP_CACHE_DIR:~-1%] EQU [\] set KOTLIN_CLI_BOOTSTRAP_CACHE_DIR=%KOTLIN_CLI_BOOTSTRAP_CACHE_DIR:~0,-1% goto :after_function_declarations REM ********** Download and extract any zip or .tar.gz archive ********** :download_and_extract setlocal set moniker=%~1 set url=%~2 set target_dir=%~3 set sha=%~4 set sha_size=%~5 set show_banner_on_cache_miss=%~6 setlocal enableDelayedExpansion set flag_file=%target_dir%\.flag if exist "%flag_file%" ( set /p current_flag=<"%flag_file%" if "!current_flag!" == "%sha%" exit /b ) set NL=^ @rem two empty lines required above for the NL character @rem We have to build the welcome banner here as an env var because we @rem can't pass a multiline string through the single line powershell set welcome_banner=!NL! ^ Welcome to !NL! ^ !NL! ^ @@@ @@@@ @@@ @@@ !NL! ^ @@@ @@@@ @@@ @@@ @@@ !NL! ^ @@@ #@@@^" ,@@@ @@@ !NL! ^ @@@ ,@@@%% ,@@@@@@@, @@@@@@@@@@ @@@ @@@ @@@ ,@@@@@, !NL! ^ @@@ @@@@ @@@@@%%^"%%@@@@@ @@@@@@@@@@ @@@ @@@ @@@@@@%%%%@@@@@ !NL! ^ @@@@@@%% @@@%% %%@@@ @@@ @@@ @@@ @@@@ %%@@%% !NL! ^ @@@ @@@@= #@@@ @@@# @@@ @@@ @@@ @@@ @@@ !NL! ^ @@@ @@@@ #@@@ @@@# @@@ @@@ @@@ @@@ @@@ !NL! ^ @@@ *@@@%% @@@@ @@@@ @@@ @@@ @@@ @@@ @@@ !NL! ^ @@@ %%@@@= %%@@@@*,*@@@@%% @@@@### @@@ @@@ @@@ @@@ !NL! ^ @@@ @@@@ ^"@@@@@@@^" %%@@@@@ @@@ @@@ @@@ @@@ !NL! @rem This multiline string is actually passed as a single line to powershell, meaning #-comments are not possible. @rem So here are a few comments about the code below: @rem - we need to support both .zip and .tar.gz archives (for the Kotlin Toolchain distribution and the JRE) @rem - tar should be present in all Windows machines since 2018 (and usable from both cmd and powershell) @rem - tar requires the destination dir to exist @rem - We use (New-Object Net.WebClient).DownloadFile instead of Invoke-WebRequest for performance. See the issue @rem https://github.com/PowerShell/PowerShell/issues/16914, which is still not fixed in Windows PowerShell 5.1 @rem - DownloadFile requires the directories in the destination file's path to exist set download_and_extract_ps1= ^ Set-StrictMode -Version 3.0; ^ $ErrorActionPreference = 'Stop'; ^ ^ $createdNew = $false; ^ $lock = New-Object System.Threading.Mutex($true, ('Global\kotlin-cli-bootstrap.' + '%target_dir%'.GetHashCode().ToString()), [ref]$createdNew); ^ if (-not $createdNew) { ^ Write-Host 'Another Kotlin CLI instance is bootstrapping. Waiting for our turn...'; ^ [void]$lock.WaitOne(); ^ } ^ ^ try { ^ if ((Get-Content '%flag_file%' -ErrorAction Ignore) -ne '%sha%') { ^ if (('%show_banner_on_cache_miss%' -eq 'true') -and [string]::IsNullOrEmpty('%KOTLIN_CLI_NO_WELCOME_BANNER%')) { ^ Write-Host \"$env:welcome_banner\"; ^ Write-Host ''; ^ Write-Host 'This is the first run of the Kotlin CLI v%kotlin_cli_version%, so we need to download the Kotlin Toolchain.'; ^ Write-Host 'Please give us a few seconds now, subsequent runs will be faster.'; ^ Write-Host ''; ^ } ^ $temp_file = '%KOTLIN_CLI_BOOTSTRAP_CACHE_DIR%\' + [System.IO.Path]::GetRandomFileName(); ^ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^ Write-Host 'Downloading %moniker%...'; ^ [void](New-Item '%KOTLIN_CLI_BOOTSTRAP_CACHE_DIR%' -ItemType Directory -Force); ^ if (Get-Command curl.exe -errorAction SilentlyContinue) { ^ curl.exe -L --silent --show-error --fail --output $temp_file '%url%'; ^ } else { ^ (New-Object Net.WebClient).DownloadFile('%url%', $temp_file); ^ } ^ ^ $actualSha = (Get-FileHash -Algorithm SHA%sha_size% -Path $temp_file).Hash.ToString(); ^ if ($actualSha -ne '%sha%') { ^ $writeErr = if ($Host.Name -eq 'ConsoleHost') { [Console]::Error.WriteLine } else { $host.ui.WriteErrorLine } ^ $writeErr.Invoke(\"ERROR: Checksum mismatch for $temp_file (downloaded from %url%): expected checksum %sha% but got $actualSha\"); ^ exit 1; ^ } ^ ^ if (Test-Path '%target_dir%') { ^ Remove-Item '%target_dir%' -Recurse; ^ } ^ if ($temp_file -like '*.zip') { ^ Add-Type -A 'System.IO.Compression.FileSystem'; ^ [IO.Compression.ZipFile]::ExtractToDirectory($temp_file, '%target_dir%'); ^ } else { ^ [void](New-Item '%target_dir%' -ItemType Directory -Force); ^ tar -xzf $temp_file -C '%target_dir%'; ^ } ^ Remove-Item $temp_file; ^ ^ Set-Content '%flag_file%' -Value '%sha%'; ^ Write-Host 'Download complete.'; ^ Write-Host ''; ^ } ^ } ^ finally { ^ $lock.ReleaseMutex(); ^ } rem We reset the PSModulePath in case this batch script was called from PowerShell Core rem See https://github.com/PowerShell/PowerShell/issues/18108#issuecomment-2269703022 set PSModulePath= set powershell=%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe "%powershell%" -NonInteractive -NoProfile -NoLogo -Command %download_and_extract_ps1% if errorlevel 1 exit /b 1 exit /b 0 :find_project_context @rem Search upwards for a kotlin.bat wrapper file and/or project.yaml @rem Sets wrapper_script to the found wrapper path, or empty string if not found. @rem Returns errorlevel 0 if a valid wrapper (that is not this script itself) was found, 1 otherwise. set wrapper_script= set this_script=%~f0 set project_dir=%CD% :find_loop set wrapper_candidate=%project_dir%\kotlin.bat if "%this_script%"=="%wrapper_candidate%" ( @rem Found itself (local wrapper case), no need to update any version or search further. exit /b 1 ) if exist "%wrapper_candidate%" ( @rem Found a wrapper — check that a project context exists alongside it if exist "%project_dir%\project.yaml" ( set wrapper_script=%wrapper_candidate% exit /b 0 ) if exist "%project_dir%\module.yaml" ( set wrapper_script=%wrapper_candidate% exit /b 0 ) echo WARNING: Found wrapper script '%wrapper_candidate%', but no project.yaml or module.yaml near it. Skipping. >&2 @rem Continue the search goto :find_next_parent ) if exist "%project_dir%\project.yaml" ( @rem Found project.yaml but no 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 exit /b 1 ) :find_next_parent @rem Move to parent directory for %%P in ("%project_dir%\..") do set parent_dir=%%~fP if "%parent_dir%"=="%project_dir%" ( @rem Reached the root, stop searching exit /b 1 ) set project_dir=%parent_dir% goto :find_loop :parse_project_context @rem Parse kotlin_cli_version and kotlin_cli_sha256 from the found wrapper_script without executing it. set parsed_kotlin_cli_version= set parsed_kotlin_cli_sha256= for /f "tokens=2 delims==" %%A in ('findstr /r /c:"^set kotlin_cli_version=[A-Za-z0-9._+-]*$" "%wrapper_script%"') do ( if not defined parsed_kotlin_cli_version set parsed_kotlin_cli_version=%%A ) for /f "tokens=2 delims==" %%A in ('findstr /r /c:"^set kotlin_cli_sha256=[0-9a-fA-F]*$" "%wrapper_script%"') do ( if not defined parsed_kotlin_cli_sha256 set parsed_kotlin_cli_sha256=%%A ) if not defined parsed_kotlin_cli_version ( echo ERROR: Suspicious local wrapper script: failed to detect the distribution version in '%wrapper_script%' >&2 exit /b 1 ) if not defined parsed_kotlin_cli_sha256 ( echo ERROR: Suspicious local wrapper script: failed to detect the distribution checksum in '%wrapper_script%' >&2 exit /b 1 ) @rem Overwrite builtin values and proceed set kotlin_cli_version=%parsed_kotlin_cli_version% set kotlin_cli_sha256=%parsed_kotlin_cli_sha256% exit /b 0 :fail echo ERROR: Kotlin CLI bootstrap failed, see errors above exit /b 1 :after_function_declarations REM ********** Project-local version detection ********** if defined KOTLIN_CLI_WRAPPER_ALWAYS_USE_INTRINSIC_VERSION goto :after_local_version_detection call :find_project_context if errorlevel 1 goto :after_local_version_detection call :parse_project_context if errorlevel 1 goto fail :after_local_version_detection REM ********** Provision the Kotlin Toolchain distribution ********** set kotlin_cli_url=%KOTLIN_CLI_DOWNLOAD_ROOT%/org/jetbrains/kotlin/kotlin-cli/%kotlin_cli_version%/kotlin-cli-%kotlin_cli_version%-dist.tgz set kotlin_cli_target_dir=%KOTLIN_CLI_BOOTSTRAP_CACHE_DIR%\kotlin-cli-%kotlin_cli_version% call :download_and_extract "Kotlin Toolchain distribution v%kotlin_cli_version%" "%kotlin_cli_url%" "%kotlin_cli_target_dir%" "%kotlin_cli_sha256%" "256" "true" if errorlevel 1 goto fail REM ********** Launch the Kotlin CLI ********** rem Determine the correct busybox binary based on architecture if "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( set busybox_exe=%kotlin_cli_target_dir%\bin\busybox64a.exe ) else if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( set busybox_exe=%kotlin_cli_target_dir%\bin\busybox64u.exe ) else ( echo Unsupported architecture %PROCESSOR_ARCHITECTURE% >&2 goto fail ) rem We use busybox here because it doesn't reinterpret the user-passed command-line arguments (that we pass via %*). rem Also this way we can use the unified launcher script (.sh) set KOTLIN_CLI_WRAPPER_PATH=%~f0 rem The '& call' after the app run is to avoid the "Terminate batch job (Y/N)?" prompt "%busybox_exe%" sh "%kotlin_cli_target_dir%\bin\launcher.sh" %* & call :exitWithErrorLevel :exitWithErrorLevel @rem We use "%COMSPEC%" /d /c exit so that and/or operators work properly when calling kotlin.bat directly from a script @rem without the `call` command. With a plain `exit /B %ERRORLEVEL%`, using `kotlin.bat && echo success` would print @rem 'success' even in case of failure. Consumers would have to use `call kotlin.bat && echo success` for it to work.` "%COMSPEC%" /d /c exit %ERRORLEVEL%