diff --git a/.ci/license-header.sh b/.ci/license-header.sh index f438d59dac..784c6bac5a 100755 --- a/.ci/license-header.sh +++ b/.ci/license-header.sh @@ -4,7 +4,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later # specify full path if dupes may exist -EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder" +EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder" # license header constants, please change when needed :)))) YEAR=2025 diff --git a/.gitignore b/.gitignore index 0886224d8d..d070d94681 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # Build directory /[Bb]uild*/ doc-build/ +out/ AppDir/ uruntime @@ -59,3 +60,4 @@ eden-windows-msvc artifacts *.AppImage* /install* +vulkansdk*.exe diff --git a/docs/Build.md b/docs/Build.md index 52a671ab1e..0bb70ee322 100644 --- a/docs/Build.md +++ b/docs/Build.md @@ -26,7 +26,7 @@ Android has a completely different build process than other platforms. See its [ If the configure phase fails, see the `Troubleshooting` section below. Usually, as long as you followed the dependencies guide, the defaults *should* successfully configure and build. -### Qt Creator +### Option A: Qt Creator This is the recommended GUI method for Linux, macOS, and Windows. @@ -46,39 +46,51 @@ Hit "Configure Project", then wait for CMake to finish configuring (may take a w -### Command Line - -This is recommended for *BSD, Solaris, Linux, and MSYS2. MSVC is possible, but not recommended. +### Option B: Command Line
Click to Open -Note that CMake must be in your PATH, and you must be in the cloned Eden directory. On Windows, you must also set up a Visual C++ development environment. This can be done by running `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat` in the same terminal. - -Recommended generators: - +> [!WARNING] +>For all systems: +>- *CMake* **MUST** be in your PATH (and also *ninja*, if you are using it as ``) +>- You *MUST* be in the cloned *Eden* directory +> +>On Windows: +> - It's recommended to install **[Ninja](https://ninja-build.org/)** +> - You must load **Visual C++ development environment**, this can be done by running our convenience script: +> - `tools/windows/load-msvc-env.ps1` (for PowerShell 5+) +> - `tools/windows/load-msvc-env.sh` (for MSYS2, Git Bash, etc) + +Available ``: - MSYS2: `MSYS Makefiles` -- MSVC: Install **[ninja](https://ninja-build.org/)** and use `Ninja`, OR use `Visual Studio 17 2022` +- MSVC: `Ninja` (preferred) or `Visual Studio 17 2022` - macOS: `Ninja` (preferred) or `Xcode` - Others: `Ninja` (preferred) or `UNIX Makefiles` -BUILD_TYPE should usually be `Release` or `RelWithDebInfo` (debug symbols--compiled executable will be large). If you are using a debugger and annoyed with stuff getting optimized out, try `Debug`. +Available ``: +- `Release` (default) +- `RelWithDebInfo` (debug symbols--compiled executable will be large) +- `Debug` (if you are using a debugger and annoyed with stuff getting optimized out) + +Caveat for Debug Builds: +- If you're building with CCache, you will need to add the environment variable `CL` with the `/FS` flag ([Reference](https://learn.microsoft.com/pt-br/cpp/build/reference/fs-force-synchronous-pdb-writes?view=msvc-170)) Also see the [Options](Options.md) page for additional CMake options. ```sh -cmake -S . -B build -G "GENERATOR" -DCMAKE_BUILD_TYPE= -DYUZU_TESTS=OFF +cmake -S . -B build -G "" -DCMAKE_BUILD_TYPE= -DYUZU_TESTS=OFF ``` If you are on Windows and prefer to use Clang: ```sh -cmake -S . -B build -G "GENERATOR" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl +cmake -S . -B build -G "" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl ```
-### [CLion](https://www.jetbrains.com/clion/) +### Option C: [CLion](https://www.jetbrains.com/clion/)
Click to Open @@ -133,13 +145,13 @@ Many platforms have quirks, bugs, and other fun stuff that may cause issues when ## Building & Running -### Qt Creator +### On Qt Creator Simply hit Ctrl+B, or the "hammer" icon in the bottom left. To run, hit the "play" icon, or Ctrl+R. -### Command Line +### On Command Line -If you are not on Windows and are using the `UNIX Makefiles` generator, you must also add `-j$(nproc)` to this command. +If you are using the `UNIX Makefiles` or `Visual Studio 17 2022` as ``, you should also add `--parallel` for faster build times. ``` cmake --build build @@ -157,4 +169,4 @@ Some platforms have convenience scripts provided for building. - **[Linux](scripts/Linux.md)** - **[Windows](scripts/Windows.md)** -macOS scripts will come soon. \ No newline at end of file +macOS scripts will come soon. diff --git a/docs/Deps.md b/docs/Deps.md index 15ffff28a8..05764341ec 100644 --- a/docs/Deps.md +++ b/docs/Deps.md @@ -3,10 +3,13 @@ To build Eden, you MUST have a C++ compiler. * On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+ - GCC 12 also requires Clang 14+ -* On Windows, this is either: - - **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option), - - clang-cl - can be downloaded from the MSVC installer, - - or **[MSYS2](https://www.msys2.org)** +* On Windows, we support: + - **[MSVC](https://visualstudio.microsoft.com/downloads/)** (default) + - It's STRONGLY RECOMMENDED to use the **Community** option and **Visual Studio 2022** + - You need to install: **[Desktop development with C++](https://learn.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=msvc-170)** + - **[clang-cl](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-180)** + - You need to install: **C++ Clang tools for Windows** + - **[MSYS2](https://www.msys2.org)** (experimental) * On macOS, this is Apple Clang - This can be installed with `xcode-select --install` @@ -15,20 +18,23 @@ The following additional tools are also required: * **[CMake](https://www.cmake.org/)** 3.22+ - already included with the Android SDK * **[Git](https://git-scm.com/)** for version control - **[Windows installer](https://gitforwindows.org)** +* **[Python3](https://www.python.org/downloads/)** 3.10+ - necessary to download external repositories * On Windows, you must install the **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** as well - - *A convenience script to install the latest SDK is provided in `.ci/windows/install-vulkan-sdk.ps1`* + - *A convenience script to install the latest SDK is provided in:* + - `tools/windows/install-vulkan-sdk.ps1` (for PowerShell 5+) + - `tools/windows/install-vulkan-sdk.sh` (for MSYS2, Git Bash, etc) -If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the recommended IDE for building) +If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the **RECOMMENDED** IDE for building) * On Linux, *BSD and macOS, this can be done by the package manager - If you wish to use Qt Creator, append `qtcreator` or `qt-creator` to the commands seen below. -* MSVC/clang-cl users on Windows must install through the [official installer](https://www.qt.io/download-qt-installer-oss) +* MSVC/clang-cl users on Windows must install through the official [Qt](https://www.qt.io/download-qt-installer-oss) installer * Linux and macOS users may choose to use the installer as well. * MSYS2 can also install Qt 6 via the package manager If you are on Windows, a convenience script to install MSVC, MSYS2, Qt, all necessary packages for MSYS2, and set up a zsh environment with useful keybinds and aliases can be found [here](https://git.crueter.xyz/scripts/windev). - For help setting up Qt Creator, run `./install.sh -h qtcreator` -If you are on Windows and NOT building with MSYS2, you may go [back home](Build.md) and continue. +If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue. ## Externals The following are handled by Eden's externals: diff --git a/.ci/windows/install-vulkan-sdk.ps1 b/tools/windows/install-vulkan-sdk.ps1 similarity index 79% rename from .ci/windows/install-vulkan-sdk.ps1 rename to tools/windows/install-vulkan-sdk.ps1 index 4c5274d1b7..072b531201 100755 --- a/.ci/windows/install-vulkan-sdk.ps1 +++ b/tools/windows/install-vulkan-sdk.ps1 @@ -1,25 +1,36 @@ +# 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 -if (-not ([bool](net session 2>$null))) { +try { + net session 1>$null 2>$null +} catch { Write-Host "This script must be run with administrator privileges!" Exit 1 } -$VulkanSDKVer = "1.4.321.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" -$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer" $Arguments = "--root `"$VULKAN_SDK`" --accept-licenses --default-answer --confirm-command install" echo "Installing Vulkan SDK $VulkanSDKVer" @@ -36,4 +47,4 @@ 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 -} \ No newline at end of file +} 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 +# 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 </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' &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[@]}" &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 "$?"'