diff --git a/README.md b/README.md index e8b3176d4..e46d56fdd 100644 --- a/README.md +++ b/README.md @@ -209,12 +209,43 @@ This installs the latest prerelease build, as opposed to the latest released ver 3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows. 4. Run `c3c.exe`. +#### Installing on Windows with the install script + +Open a PowerShell terminal (you may need to run it as an administrator) and run the following command: +```bash +iwr -useb https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.ps1 | iex +``` +The script will inform you once the installation is successful and add the `~/.c3` directory to your PATH, which will allow you to run the c3c command from any location. + +You can choose another version with option `C3_VERSION`. +For example, you can force the installation of the 0.7.4 version: +```bash +$env:C3_VERSION='0.7.4'; powershell -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.ps1 | iex" +``` + +If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows. + + #### Installing on Debian with precompiled binaries 1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz) (debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux-debug.tar.gz)) 2. Unpack executable and standard lib. 3. Run `./c3c`. +#### Installing on Debian with the install script + +Open a terminal and run the following command: +```bash +curl -fsSL https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.sh | bash +``` +The C3 compiler will be installed, and the script will also update your ~/.bashrc to include `~/.c3` in your PATH, allowing you to invoke the c3c command from anywhere. You might need to restart your terminal or source your shell for the changes to take effect. + +You can choose another version with option `C3_VERSION`. +For example, you can force the installation of the 0.7.4 version: +```bash +curl -fsSL https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.sh | C3_VERSION=0.7.4 bash +``` + #### Installing on Ubuntu with precompiled binaries 1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz) (debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20-debug.tar.gz)) @@ -296,7 +327,6 @@ You can access `c3c` via [flake.nix](./flake.nix), which will contain the latest } ``` - ### Installing on Gentoo `c3c` is available in the [Gentoo GURU overlay](https://wiki.gentoo.org/wiki/Project:GURU). diff --git a/install/install.ps1 b/install/install.ps1 new file mode 100644 index 000000000..b4b79bc57 --- /dev/null +++ b/install/install.ps1 @@ -0,0 +1,189 @@ +<# +.SYNOPSIS + C3 install script. +.DESCRIPTION + This script installs C3 on Windows from the command line. +.PARAMETER C3Version + Specifies the version of C3 to install. + Default is 'latest'. Can also be set via environment variable 'C3_VERSION'. +.PARAMETER C3Home + Specifies C3's installation directory. + Default is '$Env:USERPROFILE\.c3'. Can also be set via environment variable 'C3_HOME'. +.PARAMETER NoPathUpdate + If specified, the script will not modify the PATH environment variable. +.PARAMETER C3Repourl + Specifies the repository URL of C3. + Default is 'https://github.com/c3lang/c3c'. Can also be set via environment variable 'C3_REPOURL'. +.LINK + https://c3-lang.org/ +.LINK + https://github.com/c3lang/c3c +#> + +# Script parameters with defaults +param ( + [string] $C3Version = 'latest', + [string] $C3Home = "$Env:USERPROFILE\.c3", + [switch] $NoPathUpdate, + [string] $C3Repourl = 'https://github.com/c3lang/c3c' +) + +# Enable strict mode for better error handling +Set-StrictMode -Version Latest + +# Function to broadcast environment variable changes to Windows system +function Publish-Env { + # Add P/Invoke type if it does not exist + if (-not ("Win32.NativeMethods" -as [Type])) { + Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @" +[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] +public static extern IntPtr SendMessageTimeout( + IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, + uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); +"@ + } + + # Constants for broadcasting environment changes + $HWND_BROADCAST = [IntPtr] 0xffff + $WM_SETTINGCHANGE = 0x1a + $result = [UIntPtr]::Zero + + # Broadcast the message to all windows + [Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST, + $WM_SETTINGCHANGE, + [UIntPtr]::Zero, + "Environment", + 2, + 5000, + [ref] $result + ) | Out-Null +} + +# Function to write or update an environment variable in the registry +function Write-Env { + param( + [String] $name, + [String] $val, + [Switch] $global + ) + + # Determine the registry key based on scope (user or system) + $RegisterKey = if ($global) { + Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' + } else { + Get-Item -Path 'HKCU:' + } + + $EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $true) + + # If value is null, delete the variable + if ($null -eq $val) { + $EnvRegisterKey.DeleteValue($name) + } else { + # Determine the correct registry value type + $RegistryValueKind = if ($val.Contains('%')) { + [Microsoft.Win32.RegistryValueKind]::ExpandString + } elseif ($EnvRegisterKey.GetValue($name)) { + $EnvRegisterKey.GetValueKind($name) + } else { + [Microsoft.Win32.RegistryValueKind]::String + } + $EnvRegisterKey.SetValue($name, $val, $RegistryValueKind) + } + + # Broadcast the change to the system + Publish-Env +} + +# Function to get an environment variable from the registry +function Get-Env { + param( + [String] $name, + [Switch] $global + ) + + # Determine registry key based on scope + $RegisterKey = if ($global) { + Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' + } else { + Get-Item -Path 'HKCU:' + } + + $EnvRegisterKey = $RegisterKey.OpenSubKey('Environment') + $RegistryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames + + # Retrieve the value without expanding environment variables + $EnvRegisterKey.GetValue($name, $null, $RegistryValueOption) +} + +# Override defaults if environment variables exist +if ($Env:C3_VERSION) { $C3Version = $Env:C3_VERSION } +if ($Env:C3_HOME) { $C3Home = $Env:C3_HOME } +if ($Env:C3_NO_PATH_UPDATE) { $NoPathUpdate = $true } +if ($Env:C3_REPOURL) { $C3Repourl = $Env:C3_REPOURL -replace '/$', '' } + +# Set binary name +$BINARY = "c3-windows" + +# Determine the download URL based on version +if ($C3Version -eq 'latest') { + $DOWNLOAD_URL = "$C3Repourl/releases/latest/download/$BINARY.zip" +} else { + # Ensure version starts with 'v' + $C3Version = "v" + ($C3Version -replace '^v', '') + $DOWNLOAD_URL = "$C3Repourl/releases/download/$C3Version/$BINARY.zip" +} + +$BinDir = $C3Home + +Write-Host "This script will automatically download and install C3 ($C3Version) for you." +Write-Host "Getting it from this url: $DOWNLOAD_URL" +Write-Host "The binary will be installed into '$BinDir'" + +# Create temporary file for download +$TEMP_FILE = [System.IO.Path]::GetTempFileName() + +try { + # Download the binary + Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $TEMP_FILE + + # Remove previous installation if it exists + if (Test-Path -Path $BinDir) { + Remove-Item -Path $BinDir -Recurse -Force | Out-Null + } + + # Rename temp file to .zip + $ZIP_FILE = $TEMP_FILE + ".zip" + Rename-Item -Path $TEMP_FILE -NewName $ZIP_FILE + + # Extract downloaded zip + Expand-Archive -Path $ZIP_FILE -DestinationPath $Env:USERPROFILE -Force + + # Rename extracted folder to target installation directory + Rename-Item -Path "$Env:USERPROFILE/c3-windows-Release" -NewName $BinDir +} catch { + Write-Host "Error: '$DOWNLOAD_URL' is not available or failed to download" + exit 1 +} finally { + # Cleanup temporary zip file + Remove-Item -Path $ZIP_FILE +} + +# Update PATH environment variable if requested +if (!$NoPathUpdate) { + $PATH = Get-Env 'PATH' + if ($PATH -notlike "*$BinDir*") { + Write-Output "Adding $BinDir to PATH" + + # Persist PATH for future sessions + Write-Env -name 'PATH' -val "$BinDir;$PATH" + + # Update PATH for current session + $Env:PATH = "$BinDir;$PATH" + Write-Output "You may need to restart your shell" + } else { + Write-Output "$BinDir is already in PATH" + } +} else { + Write-Output "You may need to update your PATH manually to use c3" +} diff --git a/install/install.sh b/install/install.sh new file mode 100644 index 000000000..f10cbd005 --- /dev/null +++ b/install/install.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash +set -euo pipefail # Exit on error, unset variables, and fail pipelines on any error + +__wrap__() { + # Version of C3 to install (default: latest) + VERSION="${C3_VERSION:-latest}" + # Installation directory (default: ~/.c3) + C3_HOME="${C3_HOME:-$HOME/.c3}" + # Expand '~' if present + C3_HOME="${C3_HOME/#\~/$HOME}" + BIN_DIR="$C3_HOME" + # C3 compiler repository URL + REPO="c3lang/c3c" + REPOURL="${C3_REPOURL:-https://github.com/$REPO}" + + detect_platform() { + # Detects the operating system + local os_type + os_type="$(uname -s | tr '[:upper:]' '[:lower:]')" + + case "$os_type" in + darwin) # macOS + echo "macos" + ;; + msys*|mingw*|cygwin*) # Windows (Git Bash / MSYS / Cygwin) + IS_MSYS=true + echo "windows" + ;; + *) + echo $os_type + ;; + esac + } + + # Determine platform string + PLATFORM="$(detect_platform)" + + # File extension for the archive (ZIP for Windows, TAR.GZ for others) + EXT=".tar.gz" + BINARY="c3-${PLATFORM}" + if [[ "${IS_MSYS:-false}" == true ]]; then + EXT=".zip" + fi + + # Determine the download URL (latest release or specific version) + if [[ "$VERSION" == "latest" ]]; then + URL="${REPOURL%/}/releases/latest/download/${BINARY}${EXT}" + else + URL="${REPOURL%/}/releases/download/v${VERSION#v}/${BINARY}${EXT}" + fi + + # Temporary file for the downloaded archive + TEMP_FILE="$(mktemp "${TMPDIR:-/tmp}/.C3_install.XXXXXXXX")" + trap 'rm -f "$TEMP_FILE"' EXIT # Ensure temp file is deleted on exit + + download_file() { + # Download the archive using curl or wget + # Check that the curl version is not 8.8.0, which is broken for --write-out + # https://github.com/curl/curl/issues/13845 + if command -v curl >/dev/null && [[ "$(curl --version | awk 'NR==1{print $2}')" != "8.8.0" ]]; then + curl -SL "$URL" -o "$TEMP_FILE" + elif command -v wget >/dev/null; then + wget -O "$TEMP_FILE" "$URL" + else + echo "Error: curl or wget is required." >&2 + exit 1 + fi + } + + echo "Downloading C3 ($VERSION) from $URL..." + download_file + + # Remove existing installation and extract the new one + rm -rf "$BIN_DIR" + if [[ "$EXT" == ".zip" ]]; then + unzip "$TEMP_FILE" -d "$HOME" + else + tar -xzf "$TEMP_FILE" -C "$HOME" + fi + + # Move extracted folder to installation directory + mv "$HOME/c3" "$BIN_DIR" + chmod +x "$BIN_DIR/c3c" # Ensure compiler binary is executable + echo "✅ Installation completed in $BIN_DIR" + + # Update PATH unless suppressed by environment variable + if [ -n "${C3_NO_PATH_UPDATE:-}" ]; then + echo "No path update because C3_NO_PATH_UPDATE is set" + else + update_shell() { + FILE="$1" + LINE="$2" + + # Create shell config file if missing + if [ ! -f "$FILE" ]; then + touch "$FILE" + fi + + # Add the PATH line if not already present + if ! grep -Fxq "$LINE" "$FILE"; then + echo "Updating '${FILE}'" + echo "$LINE" >>"$FILE" + echo "Please restart or source your shell." + fi + } + + # Detect the current shell and add C3 to its PATH + case "$(basename "${SHELL-}")" in + bash) + # Default to bashrc as that is used in non login shells instead of the profile. + LINE="export PATH=\"${BIN_DIR}:\$PATH\"" + update_shell ~/.bashrc "$LINE" + ;; + fish) + LINE="fish_add_path ${BIN_DIR}" + update_shell ~/.config/fish/config.fish "$LINE" + ;; + zsh) + LINE="export PATH=\"${BIN_DIR}:\$PATH\"" + update_shell ~/.zshrc "$LINE" + ;; + tcsh) + LINE="set path = ( ${BIN_DIR} \$path )" + update_shell ~/.tcshrc "$LINE" + ;; + '') + echo "warn: Could not detect shell type." >&2 + echo " Please permanently add '${BIN_DIR}' to your \$PATH to enable the 'c3c' command." >&2 + ;; + *) + echo "warn: Could not update shell $(basename "$SHELL")" >&2 + echo " Please permanently add '${BIN_DIR}' to your \$PATH to enable the 'c3c' command." >&2 + ;; + esac + fi +} +__wrap__