diff --git a/.ci/pack.sh b/.ci/pack.sh index f795ba9c4..cd3371fd0 100755 --- a/.ci/pack.sh +++ b/.ci/pack.sh @@ -6,8 +6,9 @@ GITREV="`git show -s --format='%h'`" REV_NAME="citra-$OS-$TARGET-$GITDATE-$GITREV" # Determine the name of the release being built. -if [[ "$GITHUB_REF_NAME" =~ ^canary- ]] || [[ "$GITHUB_REF_NAME" =~ ^nightly- ]]; then - RELEASE_NAME=$(echo $GITHUB_REF_NAME | cut -d- -f1) +if [ "$GITHUB_REF_TYPE" = "tag" ]; then + RELEASE_NAME=citra-$GITHUB_REF_NAME + REV_NAME="citra-$GITHUB_REF_NAME-$OS-$TARGET" else RELEASE_NAME=head fi @@ -61,7 +62,10 @@ function pack_artifacts() { fi } -if [ -n "$UNPACKED" ]; then +if [ "$OS" = "windows" ] && [ "$GITHUB_REF_TYPE" = "tag" ]; then + # Move the installer to the artifacts directory + mv src/installer/bin/*.exe artifacts/ +elif [ -n "$UNPACKED" ]; then # Copy the artifacts to be uploaded unpacked. for ARTIFACT in build/bundle/*; do FILENAME=$(basename "$ARTIFACT") diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0215464fa..c28bcc1b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -168,10 +168,23 @@ jobs: pacboy: >- toolchain:p ccache:p cmake:p ninja:p spirv-tools:p qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p + - name: Install NSIS + run: | + Invoke-WebRequest https://deac-riga.dl.sourceforge.net/project/nsis/NSIS%203/3.10/nsis-3.10-setup.exe?viasf=1 -OutFile C:\WINDOWS\Temp\nsis-3.10-setup.exe + Invoke-Expression "& C:\WINDOWS\Temp\nsis-3.10-setup.exe \S" + shell: pwsh - name: Disable line ending translation run: git config --global core.autocrlf input - name: Build run: ./.ci/windows.sh + - name: Generate installer + if: ${{ github.ref_type == 'tag' }} + run: | + cd src\installer + "C:\Program Files (x86)\NSIS\makensis.exe" /DPRODUCT_VARIANT=${{ matrix.target }} /DPRODUCT_VERSION=${{ github.ref_name }} citra.nsi + mkdir bin + move /y *.exe bin\ + shell: cmd - name: Pack run: ./.ci/pack.sh - name: Upload diff --git a/.gitignore b/.gitignore index 62291f014..924e47cfb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ doc-build/ build-*/ +# NSIS built executables +src/installer/*.exe + # Generated source files src/common/scm_rev.cpp .travis.descriptor.json diff --git a/src/installer/citra.nsi b/src/installer/citra.nsi new file mode 100644 index 000000000..ea8fc653c --- /dev/null +++ b/src/installer/citra.nsi @@ -0,0 +1,229 @@ +; Copyright Dolphin Emulator Project / Lime3DS Emulator Project +; Licensed under GPLv2 or any later version +; Refer to the license.txt file included. + +; Usage: +; get the latest nsis: https://nsis.sourceforge.io/Download +; probably also want vscode extension: https://marketplace.visualstudio.com/items?itemName=idleberg.nsis +; makensis /DPRODUCT_VERSION= /DPRODUCT_VARIANT= + +; Require /DPRODUCT_VERSION= to makensis. +!ifndef PRODUCT_VERSION + !error "PRODUCT_VERSION must be defined" +!endif + +; Require /DPRODUCT_VARIANT= to makensis. +!ifndef PRODUCT_VARIANT + !error "PRODUCT_VARIANT must be defined" +!endif + +!define PRODUCT_NAME "Citra" +!define PRODUCT_PUBLISHER "Citra Emulator Developers" +!define PRODUCT_WEB_SITE "https://citra-emu.org/" +!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\${PRODUCT_NAME}.exe" +!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" + +!define BINARY_SOURCE_DIR "..\..\build\bundle" + +Name "${PRODUCT_NAME}" +OutFile "citra-${PRODUCT_VERSION}-windows-${PRODUCT_VARIANT}.exe" +SetCompressor /SOLID lzma +ShowInstDetails show +ShowUnInstDetails show + +; Setup MultiUser support: +; If launched without ability to elevate, user will not see any extra options. +; If user has ability to elevate, they can choose to install system-wide, with default to CurrentUser. +!define MULTIUSER_EXECUTIONLEVEL Highest +!define MULTIUSER_INSTALLMODE_INSTDIR "${PRODUCT_NAME}" +!define MULTIUSER_MUI +!define MULTIUSER_INSTALLMODE_COMMANDLINE +!define MULTIUSER_USE_PROGRAMFILES64 +!include "MultiUser.nsh" + +!include "MUI2.nsh" +; Custom page plugin +!include "nsDialogs.nsh" + +; MUI Settings +!define MUI_ICON "../../dist/citra.ico" +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" + +; License page +!insertmacro MUI_PAGE_LICENSE "..\..\license.txt" +; All/Current user selection page +!insertmacro MULTIUSER_PAGE_INSTALLMODE +; Desktop Shortcut page +Page custom desktopShortcutPageCreate desktopShortcutPageLeave +; Directory page +!insertmacro MUI_PAGE_DIRECTORY +; Instfiles page +!insertmacro MUI_PAGE_INSTFILES +; Finish page +!insertmacro MUI_PAGE_FINISH + +; Uninstaller pages +!insertmacro MUI_UNPAGE_INSTFILES + +; Variables +Var DisplayName +Var DesktopShortcutPageDialog +Var DesktopShortcutCheckbox +Var DesktopShortcut + +; Language files +!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "SimpChinese" +!insertmacro MUI_LANGUAGE "TradChinese" +!insertmacro MUI_LANGUAGE "Danish" +!insertmacro MUI_LANGUAGE "Dutch" +!insertmacro MUI_LANGUAGE "Finnish" +!insertmacro MUI_LANGUAGE "French" +!insertmacro MUI_LANGUAGE "German" +!insertmacro MUI_LANGUAGE "Greek" +!insertmacro MUI_LANGUAGE "Hungarian" +!insertmacro MUI_LANGUAGE "Indonesian" +!insertmacro MUI_LANGUAGE "Italian" +!insertmacro MUI_LANGUAGE "Japanese" +!insertmacro MUI_LANGUAGE "Korean" +!insertmacro MUI_LANGUAGE "Lithuanian" +!insertmacro MUI_LANGUAGE "Norwegian" +!insertmacro MUI_LANGUAGE "Polish" +!insertmacro MUI_LANGUAGE "PortugueseBR" +!insertmacro MUI_LANGUAGE "Romanian" +!insertmacro MUI_LANGUAGE "Spanish" +!insertmacro MUI_LANGUAGE "Turkish" +!insertmacro MUI_LANGUAGE "Vietnamese" + +; MUI end ------ + +!include "WinVer.nsh" +; Declare the installer itself as win10/win11 compatible, so WinVer.nsh works correctly. +ManifestSupportedOS {8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a} + +Function .onInit + StrCpy $DesktopShortcut 1 + !insertmacro MULTIUSER_INIT + + ; Keep in sync with build_info.txt + !define MIN_WIN10_VERSION 1703 + ${IfNot} ${AtLeastwin10} + ${OrIfNot} ${AtLeastWaaS} ${MIN_WIN10_VERSION} + MessageBox MB_OK "At least Windows 10 version ${MIN_WIN10_VERSION} is required." + Abort + ${EndIf} + + !insertmacro MUI_LANGDLL_DISPLAY +FunctionEnd + +Function un.onInit + !insertmacro MULTIUSER_UNINIT +FunctionEnd + +!macro UPDATE_DISPLAYNAME + ${If} $MultiUser.InstallMode == "CurrentUser" + StrCpy $DisplayName "$(^Name) (User)" + ${Else} + StrCpy $DisplayName "$(^Name)" + ${EndIf} +!macroend + +Function desktopShortcutPageCreate + !insertmacro MUI_HEADER_TEXT "Create Desktop Shortcut" "Would you like to create a desktop shortcut?" + nsDialogs::Create 1018 + Pop $DesktopShortcutPageDialog + ${If} $DesktopShortcutPageDialog == error + Abort + ${EndIf} + + ${NSD_CreateCheckbox} 0u 0u 100% 12u "Create a desktop shortcut" + Pop $DesktopShortcutCheckbox + ${NSD_SetState} $DesktopShortcutCheckbox $DesktopShortcut + + nsDialogs::Show +FunctionEnd + +Function desktopShortcutPageLeave + ${NSD_GetState} $DesktopShortcutCheckbox $DesktopShortcut +FunctionEnd + +Section "Base" + ExecWait '"$INSTDIR\uninst.exe" /S _?=$INSTDIR' + + SectionIn RO + + SetOutPath "$INSTDIR" + + ; The binplaced build output will be included verbatim. + File /r "${BINARY_SOURCE_DIR}\*" + + !insertmacro UPDATE_DISPLAYNAME + + ; Create start menu and desktop shortcuts + ; This needs to be done after Dolphin.exe is copied + CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}" + CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\$DisplayName.lnk" "$INSTDIR\citra.exe" + ${If} $DesktopShortcut == 1 + CreateShortCut "$DESKTOP\$DisplayName.lnk" "$INSTDIR\citra.exe" + ${EndIf} + + ; ?? + SetOutPath "$TEMP" +SectionEnd + +Section -AdditionalIcons + ; Create start menu shortcut for the uninstaller + CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall $DisplayName.lnk" "$INSTDIR\uninst.exe" "/$MultiUser.InstallMode" +SectionEnd + +!include "FileFunc.nsh" + +Section -Post + WriteUninstaller "$INSTDIR\uninst.exe" + + WriteRegStr SHCTX "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\citra.exe" + + ; Write metadata for add/remove programs applet + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "DisplayName" "$DisplayName" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe /$MultiUser.InstallMode" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\citra.exe" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR" + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD SHCTX "${PRODUCT_UNINST_KEY}" "EstimatedSize" "$0" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "Comments" "3DS emulator based on Citra" +SectionEnd + +Section Uninstall + !insertmacro UPDATE_DISPLAYNAME + + Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall $DisplayName.lnk" + + Delete "$DESKTOP\$DisplayName.lnk" + Delete "$SMPROGRAMS\${PRODUCT_NAME}\$DisplayName.lnk" + RMDir "$SMPROGRAMS\${PRODUCT_NAME}" + + ; Be a bit careful to not delete files a user may have put into the install directory. + Delete "$INSTDIR\*.dll" + Delete "$INSTDIR\citra.exe" + Delete "$INSTDIR\citra-room.exe" + Delete "$INSTDIR\license.txt" + Delete "$INSTDIR\qt.conf" + Delete "$INSTDIR\README.md" + Delete "$INSTDIR\uninst.exe" + RMDir /r "$INSTDIR\dist" + RMDir /r "$INSTDIR\plugins" + RMDir /r "$INSTDIR\scripting" + ; This should never be distributed via the installer, but just in case it is + Delete "$INSTDIR\tests.exe" + ; Delete the installation directory if there are no files left + RMDir "$INSTDIR" + + DeleteRegKey SHCTX "${PRODUCT_UNINST_KEY}" + DeleteRegKey SHCTX "${PRODUCT_DIR_REGKEY}" + + SetAutoClose true +SectionEnd