aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorCaio Oliveira <caiooliveirafarias0@gmail.com>2025-11-15 17:53:13 +0100
committercrueter <crueter@eden-emu.dev>2025-11-15 17:53:13 +0100
commit7e730a121bfa0539cbb132b0b0e19eb182840581 (patch)
treee1ca44a5d23e4f46b94caaaa97178d22a66b866d /tools
parent680384f7b831189bc55715373086fa5f0f060af1 (diff)
[tools] MSVC environment loader, POSIX vulkan/MSVC install scripts (#2993)
* also move install-vulkan-sdk.ps1 to tools Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2993 Reviewed-by: crueter <crueter@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev> Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com> Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Diffstat (limited to 'tools')
-rwxr-xr-xtools/windows/install-vulkan-sdk.ps150
-rw-r--r--tools/windows/install-vulkan-sdk.sh36
-rw-r--r--tools/windows/load-msvc-env.ps142
-rw-r--r--tools/windows/load-msvc-env.sh24
-rw-r--r--tools/windows/vcvarsall.sh433
5 files changed, 585 insertions, 0 deletions
diff --git a/tools/windows/install-vulkan-sdk.ps1 b/tools/windows/install-vulkan-sdk.ps1
new file mode 100755
index 0000000000..072b531201
--- /dev/null
+++ b/tools/windows/install-vulkan-sdk.ps1
@@ -0,0 +1,50 @@
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+$ErrorActionPreference = "Stop"
+
+# Check if running as administrator
+try {
+ net session 1>$null 2>$null
+} catch {
+ Write-Host "This script must be run with administrator privileges!"
+ Exit 1
+}
+
+$VulkanSDKVer = "1.4.328.1"
+$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer"
+$ExeFile = "vulkansdk-windows-X64-$VulkanSDKVer.exe"
+$Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile"
+$Destination = "./$ExeFile"
+
+# Check if Vulkan SDK is already installed
+if (Test-Path $VULKAN_SDK) {
+ Write-Host "-- Vulkan SDK already installed at $VULKAN_SDK"
+ return
+}
+
+echo "Downloading Vulkan SDK $VulkanSDKVer from $Uri"
+$WebClient = New-Object System.Net.WebClient
+$WebClient.DownloadFile($Uri, $Destination)
+echo "Finished downloading $ExeFile"
+
+$Arguments = "--root `"$VULKAN_SDK`" --accept-licenses --default-answer --confirm-command install"
+
+echo "Installing Vulkan SDK $VulkanSDKVer"
+$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments
+$ExitCode = $InstallProcess.ExitCode
+
+if ($ExitCode -ne 0) {
+ echo "Error installing Vulkan SDK $VulkanSDKVer (Error: $ExitCode)"
+ Exit $ExitCode
+}
+
+echo "Finished installing Vulkan SDK $VulkanSDKVer"
+
+if ("$env:GITHUB_ACTIONS" -eq "true") {
+ echo "VULKAN_SDK=$VULKAN_SDK" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+ echo "$VULKAN_SDK/Bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+}
diff --git a/tools/windows/install-vulkan-sdk.sh b/tools/windows/install-vulkan-sdk.sh
new file mode 100644
index 0000000000..cc2bcf2c50
--- /dev/null
+++ b/tools/windows/install-vulkan-sdk.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/sh
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+: "${VULKAN_SDK_VER:=1.4.328.1}"
+: "${VULKAN_ROOT:=C:/VulkanSDK/$VULKAN_SDK_VER}"
+EXE_FILE="vulkansdk-windows-X64-$VULKAN_SDK_VER.exe"
+URI="https://sdk.lunarg.com/sdk/download/$VULKAN_SDK_VER/windows/$EXE_FILE"
+VULKAN_ROOT_UNIX=$(cygpath -u "$VULKAN_ROOT")
+
+# Check if Vulkan SDK is already installed
+if [ -d "$VULKAN_ROOT_UNIX" ]; then
+ echo "-- Vulkan SDK already installed at $VULKAN_ROOT_UNIX"
+ exit 0
+fi
+
+echo "Downloading Vulkan SDK $VULKAN_SDK_VER from $URI"
+[ ! -f "./$EXE_FILE" ] && curl -L -o "./$EXE_FILE" "$URI"
+chmod +x "./$EXE_FILE"
+echo "Finished downloading $EXE_FILE"
+
+echo "Installing Vulkan SDK $VULKAN_SDK_VER..."
+if net session > /dev/null 2>&1; then
+ ./$EXE_FILE --root "$VULKAN_ROOT" --accept-licenses --default-answer --confirm-command install
+else
+ echo "This script must be run with administrator privileges!"
+ exit 1
+fi
+
+echo "Finished installing Vulkan SDK $VULKAN_SDK_VER"
+
+# GitHub Actions integration
+if [ \"${GITHUB_ACTIONS:-false}\" = \"true\" ]; then
+ echo \"VULKAN_SDK=$VULKAN_ROOT\" >> \"$GITHUB_ENV\"
+ echo \"$VULKAN_ROOT/bin\" >> \"$GITHUB_PATH\"
+fi
diff --git a/tools/windows/load-msvc-env.ps1 b/tools/windows/load-msvc-env.ps1
new file mode 100644
index 0000000000..8b98101bdb
--- /dev/null
+++ b/tools/windows/load-msvc-env.ps1
@@ -0,0 +1,42 @@
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+$osArch = $env:PROCESSOR_ARCHITECTURE
+
+switch ($osArch) {
+ "AMD64" { $arch = "x64" }
+ "ARM64" { $arch = "arm64" }
+ default {
+ Write-Error "load-msvc-env.ps1: Unsupported architecture: $osArch"
+ exit 1
+ }
+}
+
+$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
+
+if (!(Test-Path $vswhere)) {
+ Write-Error "load-msvc-env.ps1: vswhere not found"
+ exit 1
+}
+
+$vs = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
+
+if (-not $vs) {
+ Write-Error "load-msvc-env.ps1: Visual Studio (with Desktop development with C++) not found"
+ exit 1
+}
+
+$bat = "$vs\VC\Auxiliary\Build\vcvarsall.bat"
+
+if (!(Test-Path $bat)) {
+ Write-Error "load-msvc-env.ps1: (vcvarsall.bat) not found"
+ exit 1
+}
+
+cmd /c "`"$bat`" $arch && set" | ForEach-Object {
+ if ($_ -match "^(.*?)=(.*)$") {
+ [Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
+ }
+}
+
+Write-Host "load-msvc-env.ps1: MSVC environment loaded for $arch ($vs)"
diff --git a/tools/windows/load-msvc-env.sh b/tools/windows/load-msvc-env.sh
new file mode 100644
index 0000000000..008621f7e4
--- /dev/null
+++ b/tools/windows/load-msvc-env.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/bash
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+ARCH_RAW="$PROCESSOR_ARCHITECTURE"
+
+case "$ARCH_RAW" in
+ AMD64) ARCH="x64" ;;
+ ARM64) ARCH="arm64" ;;
+ *) echo "load-msvc-env.sh: Unsupported architecture: $ARCH_RAW"; exit 1 ;;
+esac
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+VCVARS_BASH="$SCRIPT_DIR/vcvarsall.sh"
+
+if [ ! -f "$VCVARS_BASH" ]; then
+ echo "load-msvc-env.sh: vcvarsall.sh not found in $SCRIPT_DIR"
+ #exit 1
+fi
+chmod +x "$VCVARS_BASH"
+
+eval "$("$VCVARS_BASH" "$ARCH")"
+
+echo "MSVC environment loaded for $ARCH with vcvars-bash script" \ No newline at end of file
diff --git a/tools/windows/vcvarsall.sh b/tools/windows/vcvarsall.sh
new file mode 100644
index 0000000000..e6270730d7
--- /dev/null
+++ b/tools/windows/vcvarsall.sh
@@ -0,0 +1,433 @@
+#!/usr/bin/env bash
+#
+# SPDX-FileCopyrightText: Copyright 2023 Nathan Poirier <nathan@poirier.io>
+# SPDX-License-Identifier: MIT License
+#
+# Source: https://github.com/nathan818fr/vcvars-bash
+#
+set -Eeuo pipefail
+shopt -s inherit_errexit
+
+declare -r VERSION='2025-07-09.1'
+
+function detect_platform() {
+ case "${OSTYPE:-}" in
+ cygwin* | msys* | win32)
+ declare -gr bash_platform='win_cyg'
+ ;;
+ *)
+ if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
+ declare -gr bash_platform='win_wsl'
+ else
+ printf 'error: Unsupported platform (%s)\n' "${OSTYPE:-}" >&2
+ printf 'hint: This script only supports Bash on Windows (Git Bash, WSL, etc.)\n' >&2
+ return 1
+ fi
+ ;;
+ esac
+}
+
+function detect_mode() {
+ # Detect the mode depending on the name called
+ if [[ "$(basename -- "$0")" == vcvarsrun* ]]; then
+ declare -gr script_mode='run' # vcvarsrun.sh
+ else
+ declare -gr script_mode='default' # vcvarsall.sh
+ fi
+}
+
+function print_usage() {
+ case "$script_mode" in
+ default)
+ cat <<EOF
+Usage: eval "\$(vcvarsall.sh [vcvarsall.bat arguments])"
+Script version: $VERSION
+
+Load MSVC environment variables using vcvarsall.bat and export them.
+The script writes a list of export commands to stdout, to be evaluated by a
+POSIX-compliant shell.
+
+Example: eval "\$(vcvarsall.sh x86)"
+EOF
+ ;;
+ run)
+ cat <<EOF
+Usage: vcvarsrun.sh [vcvarsall.bat arguments] -- command [arguments...]
+Script version: $VERSION
+
+Load MSVC environment variables using vcvarsall.bat and execute a command with
+them.
+
+Example: vcvarsrun.sh x64 -- cl /nologo /EHsc /Fe:hello.exe hello.cpp
+EOF
+ ;;
+ esac
+ cat <<EOF
+
+Environment variables:
+ VSINSTALLDIR
+ The path to the Visual Studio installation directory.
+ Example: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community"
+ Default: The latest Visual Studio installation directory found by
+ vswhere.exe
+
+ VSWHEREPATH
+ The path to the vswhere.exe executable.
+ Example: "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe"
+ Default: Automatically detected (in the PATH or well-known locations)
+
+ VSWHEREARGS
+ The arguments to pass to vswhere.exe.
+ This value is always automatically prefixed with "-latest -property installationPath".
+ Default: "-products *"
+EOF
+ local vcvarsall
+ vcvarsall=$(find_vcvarsall)
+ printf '\nvcvarsall.bat arguments: %s\n' "$({ cmd "$(cmdesc "$vcvarsall")" -help </dev/null 2>/dev/null || true; } | fix_crlf | sed '/^Syntax:/d; s/^ vcvarsall.bat //')"
+}
+
+function main() {
+ detect_platform
+ detect_mode
+
+ # Parse arguments
+ if [[ $# -eq 0 ]]; then
+ print_usage >&2
+ return 1
+ fi
+
+ local arg vcvarsall_args=()
+ for arg in "$@"; do
+ shift
+ if [[ "$arg" == '--' ]]; then
+ if [[ "$script_mode" == 'default' ]]; then
+ printf 'error: Unexpected argument: --\n' >&2
+ printf 'hint: Use vcvarsrun to run a command\n' >&2
+ return 1
+ fi
+ break
+ fi
+ vcvarsall_args+=("$(cmdesc "$arg")")
+ done
+
+ if [[ "$script_mode" == 'run' && $# -eq 0 ]]; then
+ printf 'error: No command specified\n' >&2
+ return 1
+ fi
+
+ # Get MSVC environment variables from vcvarsall.bat
+ local vcvarsall vcvarsall_env
+ vcvarsall=$(find_vcvarsall)
+ vcvarsall_env=$({ cmd "$(cmdesc "$vcvarsall")" "${vcvarsall_args[@]}" '&&' 'set' </dev/null || true; } | fix_crlf)
+
+ # Filter MSVC environment variables and export them.
+ # The list of variables to export was based on a comparison between a clean environment and the vcvarsall.bat
+ # environment (on different MSVC versions, tools and architectures).
+ #
+ # Windows environment variables are case-insensitive while Unix-like environment variables are case-sensitive, so:
+ # - we always use uppercase names to prevent duplicates environment variables on the Unix-like side
+ # - we also ensure that only the first occurrence of a variable is exported (see below)
+ #
+ # While Windows environment variables are case-insensitive, it is possible to have duplicates in some edge cases.
+ # e.g. using Git Bash:
+ # export xxx=1; export XXX=2; export xXx=3; cmd.exe //c set XxX=4 '&&' set
+ # will output:
+ # XXX=4
+ # xXx=3
+ # xxx=1
+
+ declare -A seen_vars
+ function export_env() {
+ local name=${1^^}
+ local value=$2
+ if [[ ! "$name" =~ ^[A-Z0-9_]+$ ]]; then return; fi
+ if [[ -n "${seen_vars[$name]:-}" ]]; then return; fi
+ seen_vars[$name]=1
+ if [[ "$script_mode" == 'run' ]]; then
+ export "${name}=${value}"
+ else
+ printf "export %s='%s'\n" "$name" "${value//\'/\'\\\'\'}"
+ fi
+ }
+
+ local name value initialized=false
+ while IFS='=' read -r name value; do
+ if [[ "$initialized" == 'false' ]]; then
+ if [[ -n "$value" ]]; then name+="=$value"; fi
+ if [[ "$name" == *' Environment initialized for: '* ]]; then initialized=true; fi
+ printf '%s\n' "$name" >&2
+ continue
+ fi
+
+ case "${name^^}" in
+ LIB | LIBPATH | INCLUDE | EXTERNAL_INCLUDE | COMMANDPROMPTTYPE | DEVENVDIR | EXTENSIONSDKDIR | FRAMEWORK* | \
+ PLATFORM | PREFERREDTOOLARCHITECTURE | UCRT* | UNIVERSALCRTSDK* | VCIDE* | VCINSTALL* | VCPKG* | VCTOOLS* | \
+ VSCMD* | VSINSTALL* | VS[0-9]* | VISUALSTUDIO* | WINDOWSLIB* | WINDOWSSDK*)
+ export_env "$name" "$value"
+ ;;
+ PATH)
+ # PATH is a special case, requiring special handling
+ local new_paths
+ new_paths=$(pathlist_win_to_unix "$value") # Convert to unix-style path list
+ new_paths=$(pathlist_normalize "${PATH}:${new_paths}") # Prepend the current PATH
+ export_env 'WINDOWS_PATH' "$value"
+ export_env 'PATH' "$new_paths"
+ ;;
+ esac
+ done <<<"$vcvarsall_env"
+
+ if [[ "$initialized" == 'false' ]]; then
+ printf 'error: vcvarsall.bat failed' >&2
+ return 1
+ fi
+
+ # Execute command if needed
+ if [[ "$script_mode" == 'run' ]]; then
+ exec "$@"
+ fi
+}
+
+# Locate vcvarsall.bat
+# Inputs:
+# VSINSTALLDIR: The path to the Visual Studio installation directory (optional)
+# VSWHEREPATH: The path to the vswhere.exe executable (optional)
+# VSWHEREARGS: The arguments to pass to vswhere.exe (optional)
+# Outputs:
+# stdout: The windows-style path to vcvarsall.bat
+function find_vcvarsall() {
+ local vsinstalldir
+ if [[ -n "${VSINSTALLDIR:-}" ]]; then
+ vsinstalldir="$VSINSTALLDIR"
+ else
+ local vswhere
+ if [[ -n "${VSWHEREPATH:-}" ]]; then
+ vswhere=$(unixpath "$VSWHEREPATH")
+ else
+ vswhere=$(command -v 'vswhere' 2>/dev/null || unixpath 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe')
+ fi
+
+ local vswhereargs=(-latest -property installationPath)
+ if [[ -n "${VSWHEREARGS:-}" ]]; then
+ parse_args_simple "$VSWHEREARGS"
+ vswhereargs+=("${result[@]}")
+ else
+ vswhereargs+=(-products '*')
+ fi
+
+ if [[ "${VCVARSBASH_DEBUG:-}" == 1 ]]; then
+ printf 'debug: vswhere path: %s\n' "$vswhere" >&2
+ printf 'debug: vswhere args:\n' >&2
+ printf 'debug: [ %s ]\n' "${vswhereargs[@]}" >&2
+ fi
+
+ vsinstalldir=$("$vswhere" "${vswhereargs[@]}" </dev/null | fix_crlf)
+ if [[ -z "$vsinstalldir" ]]; then
+ printf 'error: vswhere returned an empty installation path\n' >&2
+ return 1
+ fi
+ fi
+
+ printf '%s\n' "$(winpath "$vsinstalldir")\\VC\\Auxiliary\\Build\\vcvarsall.bat"
+}
+
+# Split command arguments into an array, with a simple logic
+# - Arguments are separated by whitespace (space, tab, newline, carriage return)
+# - To include whitespace in an argument, it must be enclosed in double quotes (")
+# - When inside a double-quoted argument, quotes can be escaped by doubling them ("")
+# Inputs:
+# $1: The string to parse
+# Outputs:
+# result: An array of parsed arguments
+function parse_args_simple() {
+ declare -g result=()
+ local str="$1 " # add a trailing space to simplify the last argument handling
+ local args=()
+ local current_type=none # none = waiting for next arg, normal = inside normal arg, quoted = inside quoted arg
+ local current_value=''
+ local i=0 len=${#str}
+ while ((i < len)); do
+ local c=${str:i:1}
+ case "$current_type" in
+ none)
+ case "$c" in
+ ' ' | $'\t' | $'\n' | $'\r')
+ # Ignore whitespace
+ ;;
+ '"')
+ # Start a quoted argument
+ current_type=quoted
+ current_value=''
+ ;;
+ *)
+ # Start a normal argument
+ current_type=normal
+ current_value="$c"
+ ;;
+ esac
+ ;;
+ normal)
+ case "$c" in
+ ' ' | $'\t' | $'\n' | $'\r')
+ # End of normal argument, add it to the list
+ args+=("$current_value")
+ current_type=none
+ current_value=''
+ ;;
+ *)
+ # Continue building the normal argument
+ current_value+="$c"
+ ;;
+ esac
+ ;;
+ quoted)
+ case "$c" in
+ '"')
+ if [[ "${str:i+1:1}" != '"' ]]; then
+ # End of quoted argument, add it to the list
+ args+=("$current_value")
+ current_type=none
+ current_value=''
+ else
+ # Escaped quote, add a single quote to the current value
+ current_value+='"'
+ ((++i)) # Skip the next quote
+ fi
+ ;;
+ *)
+ # Continue building the quoted argument
+ current_value+="$c"
+ ;;
+ esac
+ ;;
+ esac
+ ((++i))
+ done
+ if [[ "$current_type" != none ]]; then
+ printf 'error: Unfinished %s argument: %s\n' "$current_type" "$current_value" >&2
+ return 1
+ fi
+ declare -g result=("${args[@]}")
+}
+
+# Run a command with cmd.exe
+# Inputs:
+# $@: The command string to run (use cmdesc to escape arguments when needed)
+# Outputs:
+# stdout: The cmd.exe standard output
+# stderr: The cmd.exe error output
+function cmd() {
+ # This seems to work fine on all supported platforms
+ # (even with all the weird path and argument conversions on MSYS-like)
+ MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' cmd.exe /s /c " ; $* "
+}
+
+# Escape a cmd.exe command argument
+# Inputs:
+# $1: The argument to escape
+# Outputs:
+# stdout: The escaped argument
+function cmdesc() {
+ # shellcheck disable=SC2001
+ sed 's/[^0-9A-Za-z]/^\0/g' <<<"$1"
+}
+
+# Convert path to an absolute unix-style path
+# Inputs:
+# $1: The path to convert
+# Outputs:
+# stdout: The converted path
+function unixpath() {
+ local path=$1
+ case "$bash_platform" in
+ win_wsl)
+ case "$path" in
+ [a-zA-Z]:\\* | [a-zA-Z]:/* | \\\\* | //*)
+ # Convert windows path using wslpath (unix mode, absolute path)
+ wslpath -u -a -- "$path"
+ ;;
+ *)
+ # Convert unix path using realpath
+ realpath -m -- "$path"
+ ;;
+ esac
+ ;;
+ *)
+ cygpath -u -a -- "$path"
+ ;;
+ esac
+}
+
+# Convert path to an absolute windows-style path
+# Inputs:
+# $1: The path to convert
+# Outputs:
+# stdout: The converted path
+function winpath() {
+ local path=$1
+ case "$bash_platform" in
+ win_wsl)
+ case "$path" in
+ [a-zA-Z]:\\* | [a-zA-Z]:/* | \\\\* | //*)
+ # Already a windows path
+ printf '%s' "$path"
+ ;;
+ *)
+ # Convert using wslpath (windows mode, absolute path)
+ wslpath -w -a -- "$path"
+ ;;
+ esac
+ ;;
+ *)
+ # Convert using cygpath (windows mode, absolute path, long form)
+ cygpath -w -a -l -- "$path"
+ ;;
+ esac
+}
+
+# Convert a windows-style path list to a unix-style path list
+# Inputs:
+# $1: The windows-style path list to convert
+# Outputs:
+# stdout: The converted unix-style path list
+function pathlist_win_to_unix() {
+ local win_paths=$1
+
+ local path_dir first=true
+ while IFS= read -r -d';' path_dir; do
+ if [[ -z "$path_dir" ]]; then continue; fi
+
+ if [[ "$first" == 'true' ]]; then first=false; else printf ':'; fi
+ printf '%s' "$(unixpath "$path_dir")"
+ done <<<"${win_paths};"
+}
+
+# Normalize a unix-style path list, removing duplicates and empty entries
+# Inputs:
+# $1: The list to normalize
+# Outputs:
+# stdout: The normalized path list
+function pathlist_normalize() {
+ local unix_paths=$1
+
+ declare -A seen_paths
+ local path_dir first=true
+ while IFS= read -r -d ':' path_dir; do
+ if [[ -z "$path_dir" ]]; then continue; fi
+ if [[ -n "${seen_paths[$path_dir]:-}" ]]; then continue; fi
+ seen_paths[$path_dir]=1
+
+ if [[ "$first" == 'true' ]]; then first=false; else printf ':'; fi
+ printf '%s' "$path_dir"
+ done <<<"${unix_paths}:"
+}
+
+# Convert CRLF to LF
+# Inputs:
+# stdin: The input to convert
+# Outputs:
+# stdout: The converted input
+function fix_crlf() {
+ sed 's/\r$//'
+}
+
+eval 'main "$@";exit "$?"'