mirror of https://github.com/coder/coder.git
feat: add windows amd64 installer (#4719)
This commit is contained in:
parent
7d04bf2abe
commit
067362cf31
|
@ -65,13 +65,14 @@ jobs:
|
|||
restore-keys: |
|
||||
js-${{ runner.os }}-
|
||||
|
||||
- name: Install nsis and zstd
|
||||
run: sudo apt-get install -y nsis zstd
|
||||
|
||||
- name: Install nfpm
|
||||
run: |
|
||||
set -euo pipefail
|
||||
wget -O /tmp/nfpm.deb https://github.com/goreleaser/nfpm/releases/download/v2.18.1/nfpm_amd64.deb
|
||||
sudo dpkg -i /tmp/nfpm.deb
|
||||
- name: Install zstd
|
||||
run: sudo apt-get install -y zstd
|
||||
|
||||
- name: Install rcodesign
|
||||
run: |
|
||||
|
@ -107,6 +108,7 @@ jobs:
|
|||
make -j \
|
||||
build/coder_"$version"_linux_{amd64,armv7,arm64}.{tar.gz,apk,deb,rpm} \
|
||||
build/coder_"$version"_{darwin,windows}_{amd64,arm64}.zip \
|
||||
build/coder_"$version"_windows_amd64_installer.exe \
|
||||
build/coder_helm_"$version".tgz
|
||||
env:
|
||||
CODER_SIGN_DARWIN: "1"
|
||||
|
@ -155,6 +157,7 @@ jobs:
|
|||
run: |
|
||||
./scripts/publish_release.sh \
|
||||
${{ (github.event.inputs.dry_run || github.event.inputs.snapshot) && '--dry-run' }} \
|
||||
./build/*_installer.exe \
|
||||
./build/*.zip \
|
||||
./build/*.tar.gz \
|
||||
./build/*.tgz \
|
||||
|
|
7
Makefile
7
Makefile
|
@ -252,6 +252,13 @@ $(CODER_ALL_PACKAGES): $(CODER_PACKAGE_DEPS)
|
|||
--output "$@" \
|
||||
"build/coder_$(VERSION)_$${os}_$${arch}"
|
||||
|
||||
# This task builds a Windows amd64 installer. Depends on makensis.
|
||||
build/coder_$(VERSION)_windows_amd64_installer.exe: build/coder_$(VERSION)_windows_amd64.exe
|
||||
./scripts/build_windows_installer.sh \
|
||||
--version "$(VERSION)" \
|
||||
--output "$@" \
|
||||
"$<"
|
||||
|
||||
# Redirect from version-less Docker image targets to the versioned ones.
|
||||
#
|
||||
# Called like this:
|
||||
|
|
|
@ -32,7 +32,10 @@ Coder creates remote development machines so your team can develop from anywhere
|
|||
> **Note**:
|
||||
> Coder is in a beta state. [Report issues here](https://github.com/coder/coder/issues/new).
|
||||
|
||||
The easiest way to install Coder is to use our [install script](https://github.com/coder/coder/blob/main/install.sh) for Linux and macOS.
|
||||
The easiest way to install Coder is to use our
|
||||
[install script](https://github.com/coder/coder/blob/main/install.sh) for Linux
|
||||
and macOS. For Windows, use the latest `..._installer.exe` file from GitHub
|
||||
Releases.
|
||||
|
||||
To install, run:
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script packages a Windows installer for Coder containing the given
|
||||
# binary.
|
||||
#
|
||||
# Usage: ./build_windows_installer.sh --output "path/to/installer.exe" [--version 1.2.3] [--agpl] path/to/binary.exe
|
||||
#
|
||||
# Only amd64 binaries are supported.
|
||||
#
|
||||
# If no version is specified, defaults to the version from ./version.sh.
|
||||
#
|
||||
# If the --agpl parameter is specified, only the AGPL license is included in the
|
||||
# installer.
|
||||
|
||||
set -euo pipefail
|
||||
# shellcheck source=scripts/lib.sh
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
agpl="${CODER_BUILD_AGPL:-0}"
|
||||
output_path=""
|
||||
version=""
|
||||
|
||||
args="$(getopt -o "" -l agpl,output:,version: -- "$@")"
|
||||
eval set -- "$args"
|
||||
while true; do
|
||||
case "$1" in
|
||||
--agpl)
|
||||
agpl=1
|
||||
shift
|
||||
;;
|
||||
--output)
|
||||
mkdir -p "$(dirname "$2")"
|
||||
output_path="$(realpath "$2")"
|
||||
shift 2
|
||||
;;
|
||||
--version)
|
||||
version="$2"
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
error "Unrecognized option: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$output_path" == "" ]]; then
|
||||
error "--output is a required parameter"
|
||||
fi
|
||||
|
||||
if [[ "$#" != 1 ]]; then
|
||||
error "Exactly one argument must be provided to this script, $# were supplied"
|
||||
fi
|
||||
if [[ ! -f "$1" ]]; then
|
||||
error "File '$1' does not exist or is not a regular file"
|
||||
fi
|
||||
input_file="$(realpath "$1")"
|
||||
|
||||
version="${version#v}"
|
||||
if [[ "$version" == "" ]]; then
|
||||
version="$(execrelative ./version.sh)"
|
||||
fi
|
||||
|
||||
# Remove the "v" prefix and ensure the version is in the format X.X.X.X for
|
||||
# makensis.
|
||||
nsis_version="${version//-*/}"
|
||||
nsis_version+=".$(date -u +%Y%m%d%H%M)"
|
||||
|
||||
# Check dependencies
|
||||
dependencies makensis
|
||||
|
||||
# Make a temporary dir where all source files intended to be in the installer
|
||||
# will be hardlinked/copied to.
|
||||
cdroot
|
||||
temp_dir="$(TMPDIR="$(dirname "$input_file")" mktemp -d)"
|
||||
mkdir -p "$temp_dir/bin"
|
||||
ln "$input_file" "$temp_dir/bin/coder.exe"
|
||||
cp "$(realpath scripts/win-installer/installer.nsi)" "$temp_dir/installer.nsi"
|
||||
cp "$(realpath scripts/win-installer/path.nsh)" "$temp_dir/path.nsh"
|
||||
cp "$(realpath scripts/win-installer/coder.ico)" "$temp_dir/coder.ico"
|
||||
cp "$(realpath scripts/win-installer/banner.bmp)" "$temp_dir/banner.bmp"
|
||||
|
||||
# Craft a license document by combining the AGPL license and optionally the
|
||||
# enterprise license.
|
||||
license_path="$temp_dir/license.txt"
|
||||
|
||||
if [[ "$agpl" == 0 ]]; then
|
||||
cat <<-EOF >"$license_path"
|
||||
This distribution of Coder includes some enterprise-licensed code which is not
|
||||
licensed under the AGPL license:
|
||||
|
||||
$(sed 's/^/ /' "$(realpath LICENSE.enterprise)")
|
||||
|
||||
|
||||
|
||||
The non-enterprise code in this distribution is licensed under the AGPL license:
|
||||
|
||||
$(sed 's/^/ /' "$(realpath LICENSE)")
|
||||
EOF
|
||||
else
|
||||
cat <<-EOF >"$license_path"
|
||||
This distribution of Coder is free software and is licensed under the AGPL
|
||||
license:
|
||||
|
||||
$(sed 's/^/ /' "$(realpath LICENSE)")
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Run makensis to build the installer.
|
||||
pushd "$temp_dir"
|
||||
makensis \
|
||||
-V4 \
|
||||
-DCODER_VERSION="$version" \
|
||||
-DCODER_NSIS_VERSION="$nsis_version" \
|
||||
-DCODER_YEAR="$(date +%Y)" \
|
||||
installer.nsi
|
||||
popd
|
||||
|
||||
# Copy the installer to the output path.
|
||||
cp "$temp_dir/installer.exe" "$output_path"
|
||||
|
||||
rm -rf "$temp_dir"
|
|
@ -79,7 +79,7 @@ if [[ "$arch" == "arm" ]] || [[ "$arch" == "armv7" ]]; then
|
|||
fi
|
||||
|
||||
# Make temporary dir where all source files intended to be in the package will
|
||||
# be hardlinked from.
|
||||
# be hardlinked to.
|
||||
cdroot
|
||||
temp_dir="$(TMPDIR="$(dirname "$input_file")" mktemp -d)"
|
||||
ln "$input_file" "$temp_dir/coder"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
Binary file not shown.
After Width: | Height: | Size: 422 KiB |
|
@ -0,0 +1,133 @@
|
|||
# This NSIS installer script was taken from the following webpage and heavily
|
||||
# adapted to Coder's needs:
|
||||
# https://www.conjur.org/blog/building-a-windows-installer-from-a-linux-ci-pipeline/
|
||||
|
||||
Unicode true
|
||||
|
||||
!define APP_NAME "Coder"
|
||||
!define COMP_NAME "Coder Technologies, Inc."
|
||||
!define VERSION "${CODER_NSIS_VERSION}"
|
||||
!define COPYRIGHT "Copyright (c) ${CODER_YEAR} Coder Technologies, Inc."
|
||||
!define DESCRIPTION "Remote development environments on your infrastructure provisioned with Terraform"
|
||||
!define INSTALLER_NAME "installer.exe"
|
||||
!define MAIN_APP_EXE "coder.exe"
|
||||
!define MAIN_APP_EXE_PATH "bin\coder.exe"
|
||||
!define ICON "coder.ico"
|
||||
!define BANNER "banner.bmp"
|
||||
!define LICENSE_TXT "license.txt"
|
||||
|
||||
!define INSTALL_DIR "$PROGRAMFILES64\${APP_NAME}"
|
||||
!define INSTALL_TYPE "SetShellVarContext all" # this means install for all users
|
||||
!define REG_ROOT "HKLM"
|
||||
!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}"
|
||||
!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
|
||||
|
||||
######################################################################
|
||||
|
||||
VIProductVersion "${VERSION}"
|
||||
VIAddVersionKey "ProductName" "${APP_NAME}"
|
||||
VIAddVersionKey "CompanyName" "${COMP_NAME}"
|
||||
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
|
||||
VIAddVersionKey "FileDescription" "${DESCRIPTION}"
|
||||
VIAddVersionKey "FileVersion" "${VERSION}"
|
||||
|
||||
######################################################################
|
||||
|
||||
SetCompressor /SOLID Lzma
|
||||
Name "${APP_NAME}"
|
||||
Caption "${APP_NAME}"
|
||||
OutFile "${INSTALLER_NAME}"
|
||||
BrandingText "${APP_NAME} v${CODER_VERSION}"
|
||||
InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" "Path"
|
||||
InstallDir "${INSTALL_DIR}"
|
||||
|
||||
######################################################################
|
||||
|
||||
!define MUI_ICON "${ICON}"
|
||||
!define MUI_UNICON "${ICON}"
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "${BANNER}"
|
||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${BANNER}"
|
||||
|
||||
######################################################################
|
||||
|
||||
!include "MUI2.nsh"
|
||||
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_UNABORTWARNING
|
||||
|
||||
!define MUI_WELCOMEPAGE_TEXT "Setup will guide you through the installation of Coder v${CODER_VERSION}.$\r$\n$\r$\nClick Next to continue."
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
|
||||
!insertmacro MUI_PAGE_LICENSE "${LICENSE_TXT}"
|
||||
|
||||
!insertmacro MUI_PAGE_COMPONENTS
|
||||
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
!define MUI_FINISHPAGE_TEXT "Coder v${CODER_VERSION} has been installed on your computer.$\r$\n$\r$\nIf you added Coder to your PATH, you can use Coder by opening a command prompt or PowerShell and running `coder`. You may have to sign out and sign back in for `coder` to be available.$\r$\n$\r$\nClick Finish to close Setup."
|
||||
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_UNPAGE_FINISH
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
######################################################################
|
||||
|
||||
!include ".\path.nsh"
|
||||
|
||||
Section "Coder CLI" SecInstall
|
||||
SectionIn RO # mark this section as required
|
||||
|
||||
${INSTALL_TYPE}
|
||||
|
||||
SetOverwrite ifnewer
|
||||
SetOutPath "$INSTDIR"
|
||||
File /r "bin"
|
||||
File "${LICENSE_TXT}"
|
||||
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE_PATH}"
|
||||
WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "Path" "$INSTDIR"
|
||||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "${APP_NAME}"
|
||||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe"
|
||||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayIcon" "$INSTDIR\${MAIN_APP_EXE_PATH}"
|
||||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}"
|
||||
SectionEnd
|
||||
|
||||
Section "Add to PATH" SecAddToPath
|
||||
Push "$INSTDIR\bin"
|
||||
Call AddToPath
|
||||
SectionEnd
|
||||
|
||||
######################################################################
|
||||
|
||||
Section Uninstall
|
||||
${INSTALL_TYPE}
|
||||
|
||||
RmDir /r "$INSTDIR"
|
||||
DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
|
||||
DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
|
||||
|
||||
Push "$INSTDIR\bin"
|
||||
Call un.RemoveFromPath
|
||||
SectionEnd
|
||||
|
||||
######################################################################
|
||||
|
||||
LangString DESC_SecInstall ${LANG_ENGLISH} "Install the Coder command-line interface (coder.exe) for all users."
|
||||
LangString DESC_SecAddToPath ${LANG_ENGLISH} "Add coder.exe to the PATH for all users. This enables `coder` to be used directly from a command prompt or PowerShell."
|
||||
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecAddToPath} $(DESC_SecAddToPath)
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
|
@ -0,0 +1,198 @@
|
|||
# PATH utilities. Taken from:
|
||||
# https://www.smartmontools.org/browser/trunk/smartmontools/os_win32/installer.nsi?rev=5310#L689
|
||||
|
||||
; os_win32/installer.nsi - smartmontools install NSIS script
|
||||
;
|
||||
; Home page of code is: https://www.smartmontools.org
|
||||
;
|
||||
; Copyright (C) 2006-22 Christian Franke
|
||||
;
|
||||
; SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
;--------------------------------------------------------------------
|
||||
; Path functions
|
||||
;
|
||||
; Based on example from:
|
||||
; http://nsis.sourceforge.net/Path_Manipulation
|
||||
;
|
||||
|
||||
|
||||
!include "WinMessages.nsh"
|
||||
|
||||
; Registry Entry for environment (NT4,2000,XP)
|
||||
; All users:
|
||||
!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
; Current user only:
|
||||
;!define Environ 'HKCU "Environment"'
|
||||
|
||||
|
||||
; AddToPath - Appends dir to PATH
|
||||
; (does not work on Win9x/ME)
|
||||
;
|
||||
; Usage:
|
||||
; Push "dir"
|
||||
; Call AddToPath
|
||||
|
||||
Function AddToPath
|
||||
Exch $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
Push $4
|
||||
|
||||
; NSIS ReadRegStr returns empty string on string overflow
|
||||
; Native calls are used here to check actual length of PATH
|
||||
|
||||
; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3)
|
||||
System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4"
|
||||
IntCmp $4 0 0 done done
|
||||
; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
|
||||
; RegCloseKey($3)
|
||||
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
|
||||
System::Call "advapi32::RegCloseKey(i $3)"
|
||||
|
||||
${If} $4 = 234 ; ERROR_MORE_DATA
|
||||
DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}"
|
||||
MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}" /SD IDOK
|
||||
Goto done
|
||||
${EndIf}
|
||||
|
||||
${If} $4 <> 0 ; NO_ERROR
|
||||
${If} $4 <> 2 ; ERROR_FILE_NOT_FOUND
|
||||
DetailPrint "AddToPath: unexpected error code $4"
|
||||
Goto done
|
||||
${EndIf}
|
||||
StrCpy $1 ""
|
||||
${EndIf}
|
||||
|
||||
; Check if already in PATH
|
||||
Push "$1;"
|
||||
Push "$0;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" 0 done
|
||||
Push "$1;"
|
||||
Push "$0\;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" 0 done
|
||||
|
||||
; Prevent NSIS string overflow
|
||||
StrLen $2 $0
|
||||
StrLen $3 $1
|
||||
IntOp $2 $2 + $3
|
||||
IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
|
||||
${If} $2 > ${NSIS_MAX_STRLEN}
|
||||
DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}"
|
||||
MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}." /SD IDOK
|
||||
Goto done
|
||||
${EndIf}
|
||||
|
||||
; Append dir to PATH
|
||||
DetailPrint "Add to PATH: $0"
|
||||
StrCpy $2 $1 1 -1
|
||||
${If} $2 == ";"
|
||||
StrCpy $1 $1 -1 ; remove trailing ';'
|
||||
${EndIf}
|
||||
${If} $1 != "" ; no leading ';'
|
||||
StrCpy $0 "$1;$0"
|
||||
${EndIf}
|
||||
WriteRegExpandStr ${Environ} "PATH" $0
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
done:
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
FunctionEnd
|
||||
|
||||
|
||||
; RemoveFromPath - Removes dir from PATH
|
||||
;
|
||||
; Usage:
|
||||
; Push "dir"
|
||||
; Call RemoveFromPath
|
||||
|
||||
Function un.RemoveFromPath
|
||||
Exch $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
Push $4
|
||||
Push $5
|
||||
Push $6
|
||||
|
||||
ReadRegStr $1 ${Environ} "PATH"
|
||||
StrCpy $5 $1 1 -1
|
||||
${If} $5 != ";"
|
||||
StrCpy $1 "$1;" ; ensure trailing ';'
|
||||
${EndIf}
|
||||
Push $1
|
||||
Push "$0;"
|
||||
Call un.StrStr
|
||||
Pop $2 ; pos of our dir
|
||||
StrCmp $2 "" done
|
||||
|
||||
DetailPrint "Remove from PATH: $0"
|
||||
StrLen $3 "$0;"
|
||||
StrLen $4 $2
|
||||
StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove
|
||||
StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
|
||||
StrCpy $3 "$5$6"
|
||||
StrCpy $5 $3 1 -1
|
||||
${If} $5 == ";"
|
||||
StrCpy $3 $3 -1 ; remove trailing ';'
|
||||
${EndIf}
|
||||
WriteRegExpandStr ${Environ} "PATH" $3
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
done:
|
||||
Pop $6
|
||||
Pop $5
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
FunctionEnd
|
||||
|
||||
|
||||
; StrStr - find substring in a string
|
||||
;
|
||||
; Usage:
|
||||
; Push "this is some string"
|
||||
; Push "some"
|
||||
; Call StrStr
|
||||
; Pop $0 ; "some string"
|
||||
|
||||
!macro StrStr un
|
||||
Function ${un}StrStr
|
||||
Exch $R1 ; $R1=substring, stack=[old$R1,string,...]
|
||||
Exch ; stack=[string,old$R1,...]
|
||||
Exch $R2 ; $R2=string, stack=[old$R2,old$R1,...]
|
||||
Push $R3
|
||||
Push $R4
|
||||
Push $R5
|
||||
StrLen $R3 $R1
|
||||
StrCpy $R4 0
|
||||
; $R1=substring, $R2=string, $R3=strlen(substring)
|
||||
; $R4=count, $R5=tmp
|
||||
${Do}
|
||||
StrCpy $R5 $R2 $R3 $R4
|
||||
${IfThen} $R5 == $R1 ${|} ${ExitDo} ${|}
|
||||
${IfThen} $R5 == "" ${|} ${ExitDo} ${|}
|
||||
IntOp $R4 $R4 + 1
|
||||
${Loop}
|
||||
StrCpy $R1 $R2 "" $R4
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Exch $R1 ; $R1=old$R1, stack=[result,...]
|
||||
FunctionEnd
|
||||
!macroend
|
||||
!insertmacro StrStr ""
|
||||
!insertmacro StrStr "un."
|
Loading…
Reference in New Issue