diff --git a/appveyor.yml b/appveyor.yml index ef2731951..03ffdca9d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,39 +1,40 @@ os: Visual Studio 2015 # Clone directly into GOPATH. -clone_folder: c:\gopath\src\github.com\ethereum\go-ethereum +clone_folder: C:\gopath\src\github.com\ethereum\go-ethereum clone_depth: 5 version: "{branch}.{build}" environment: global: - GOPATH: c:\gopath + GOPATH: C:\gopath CC: gcc.exe matrix: - GETH_ARCH: amd64 MSYS2_ARCH: x86_64 MSYS2_BITS: 64 MSYSTEM: MINGW64 - PATH: C:\msys64\mingw64\bin\;%PATH% + PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH% - GETH_ARCH: 386 MSYS2_ARCH: i686 MSYS2_BITS: 32 MSYSTEM: MINGW32 - PATH: C:\msys64\mingw32\bin\;%PATH% + PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH% install: - - rmdir c:\go /s /q + - rmdir C:\go /s /q - appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.3.windows-amd64.zip - 7z x go1.7.3.windows-amd64.zip -y -oC:\ > NUL - go version - gcc --version build_script: - - go run build\\ci.go install -arch %GETH_ARCH% + - go run build\ci.go install -arch %GETH_ARCH% after_build: - - go run build\\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds + - go run build\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds + - go run build\ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds test_script: - set GOARCH=%GETH_ARCH% - set CGO_ENABLED=1 - - go run build\\ci.go test -vet -coverage + - go run build\ci.go test -vet -coverage diff --git a/build/ci.go b/build/ci.go index 2c6e918a6..f5ef54b75 100644 --- a/build/ci.go +++ b/build/ci.go @@ -28,6 +28,7 @@ Available commands are: archive [-arch architecture] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts importkeys -- imports signing keys from env debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package + nsis -- creates a Windows NSIS installer xgo [ options ] -- cross builds according to options For all commands, -n prevents execution of external programs (dry run mode). @@ -122,6 +123,8 @@ func main() { doArchive(os.Args[2:]) case "debsrc": doDebianSource(os.Args[2:]) + case "nsis": + doWindowsInstaller(os.Args[2:]) case "xgo": doXgo(os.Args[2:]) default: @@ -429,7 +432,7 @@ func makeWorkdir(wdflag string) string { if wdflag != "" { err = os.MkdirAll(wdflag, 0744) } else { - wdflag, err = ioutil.TempDir("", "eth-deb-build-") + wdflag, err = ioutil.TempDir("", "geth-build-") } if err != nil { log.Fatal(err) @@ -559,6 +562,76 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) { return pkgdir } +// Windows installer + +func doWindowsInstaller(cmdline []string) { + // Parse the flags and make skip installer generation on PRs + var ( + arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging") + signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`) + upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`) + workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) + ) + flag.CommandLine.Parse(cmdline) + *workdir = makeWorkdir(*workdir) + env := build.Env() + maybeSkipArchive(env) + + // Aggregate binaries that are included in the installer + var ( + devTools []string + allTools []string + gethTool string + ) + for _, file := range allToolsArchiveFiles { + if file == "COPYING" { // license, copied later + continue + } + allTools = append(allTools, filepath.Base(file)) + if filepath.Base(file) == "geth.exe" { + gethTool = file + } else { + devTools = append(devTools, file) + } + } + + // Render NSIS scripts: Installer NSIS contains two installer sections, + // first section contains the geth binary, second section holds the dev tools. + templateData := map[string]interface{}{ + "License": "COPYING", + "Geth": gethTool, + "DevTools": devTools, + } + build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "geth.nsi"), 0644, nil) + build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData) + build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools) + build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil) + build.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll", 0755) + build.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING", 0755) + + // Build the installer. This assumes that all the needed files have been previously + // built (don't mix building and packaging to keep cross compilation complexity to a + // minimum). + version := strings.Split(build.VERSION(), ".") + if env.Commit != "" { + version[2] += "-" + env.Commit[:8] + } + installer, _ := filepath.Abs("geth-" + archiveBasename(*arch, env) + ".exe") + build.MustRunCommand("makensis.exe", + "/DOUTPUTFILE="+installer, + "/DMAJORVERSION="+version[0], + "/DMINORVERSION="+version[1], + "/DBUILDVERSION="+version[2], + "/DARCH="+*arch, + filepath.Join(*workdir, "geth.nsi"), + ) + + // Sign and publish installer. + if err := archiveUpload(installer, *upload, *signer); err != nil { + log.Fatal(err) + } +} + // Cross compilation func doXgo(cmdline []string) { diff --git a/build/nsis.envvarupdate.nsh b/build/nsis.envvarupdate.nsh new file mode 100644 index 000000000..9c3ecbe33 --- /dev/null +++ b/build/nsis.envvarupdate.nsh @@ -0,0 +1,327 @@ +/** + * EnvVarUpdate.nsh + * : Environmental Variables: append, prepend, and remove entries + * + * WARNING: If you use StrFunc.nsh header then include it before this file + * with all required definitions. This is to avoid conflicts + * + * Usage: + * ${EnvVarUpdate} "ResultVar" "EnvVarName" "Action" "RegLoc" "PathString" + * + * Credits: + * Version 1.0 + * * Cal Turney (turnec2) + * * Amir Szekely (KiCHiK) and e-circ for developing the forerunners of this + * function: AddToPath, un.RemoveFromPath, AddToEnvVar, un.RemoveFromEnvVar, + * WriteEnvStr, and un.DeleteEnvStr + * * Diego Pedroso (deguix) for StrTok + * * Kevin English (kenglish_hi) for StrContains + * * Hendri Adriaens (Smile2Me), Diego Pedroso (deguix), and Dan Fuhry + * (dandaman32) for StrReplace + * + * Version 1.1 (compatibility with StrFunc.nsh) + * * techtonik + * + * http://nsis.sourceforge.net/Environmental_Variables:_append%2C_prepend%2C_and_remove_entries + * + */ + + +!ifndef ENVVARUPDATE_FUNCTION +!define ENVVARUPDATE_FUNCTION +!verbose push +!verbose 3 +!include "LogicLib.nsh" +!include "WinMessages.NSH" +!include "StrFunc.nsh" + +; ---- Fix for conflict if StrFunc.nsh is already includes in main file ----------------------- +!macro _IncludeStrFunction StrFuncName + !ifndef ${StrFuncName}_INCLUDED + ${${StrFuncName}} + !endif + !ifndef Un${StrFuncName}_INCLUDED + ${Un${StrFuncName}} + !endif + !define un.${StrFuncName} "${Un${StrFuncName}}" +!macroend + +!insertmacro _IncludeStrFunction StrTok +!insertmacro _IncludeStrFunction StrStr +!insertmacro _IncludeStrFunction StrRep + +; ---------------------------------- Macro Definitions ---------------------------------------- +!macro _EnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString + Push "${EnvVarName}" + Push "${Action}" + Push "${RegLoc}" + Push "${PathString}" + Call EnvVarUpdate + Pop "${ResultVar}" +!macroend +!define EnvVarUpdate '!insertmacro "_EnvVarUpdateConstructor"' + +!macro _unEnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString + Push "${EnvVarName}" + Push "${Action}" + Push "${RegLoc}" + Push "${PathString}" + Call un.EnvVarUpdate + Pop "${ResultVar}" +!macroend +!define un.EnvVarUpdate '!insertmacro "_unEnvVarUpdateConstructor"' +; ---------------------------------- Macro Definitions end------------------------------------- + +;----------------------------------- EnvVarUpdate start---------------------------------------- +!define hklm_all_users 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' +!define hkcu_current_user 'HKCU "Environment"' + +!macro EnvVarUpdate UN + +Function ${UN}EnvVarUpdate + + Push $0 + Exch 4 + Exch $1 + Exch 3 + Exch $2 + Exch 2 + Exch $3 + Exch + Exch $4 + Push $5 + Push $6 + Push $7 + Push $8 + Push $9 + Push $R0 + + /* After this point: + ------------------------- + $0 = ResultVar (returned) + $1 = EnvVarName (input) + $2 = Action (input) + $3 = RegLoc (input) + $4 = PathString (input) + $5 = Orig EnvVar (read from registry) + $6 = Len of $0 (temp) + $7 = tempstr1 (temp) + $8 = Entry counter (temp) + $9 = tempstr2 (temp) + $R0 = tempChar (temp) */ + + ; Step 1: Read contents of EnvVarName from RegLoc + ; + ; Check for empty EnvVarName + ${If} $1 == "" + SetErrors + DetailPrint "ERROR: EnvVarName is blank" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ; Check for valid Action + ${If} $2 != "A" + ${AndIf} $2 != "P" + ${AndIf} $2 != "R" + SetErrors + DetailPrint "ERROR: Invalid Action - must be A, P, or R" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ${If} $3 == HKLM + ReadRegStr $5 ${hklm_all_users} $1 ; Get EnvVarName from all users into $5 + ${ElseIf} $3 == HKCU + ReadRegStr $5 ${hkcu_current_user} $1 ; Read EnvVarName from current user into $5 + ${Else} + SetErrors + DetailPrint 'ERROR: Action is [$3] but must be "HKLM" or HKCU"' + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ; Check for empty PathString + ${If} $4 == "" + SetErrors + DetailPrint "ERROR: PathString is blank" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ; Make sure we've got some work to do + ${If} $5 == "" + ${AndIf} $2 == "R" + SetErrors + DetailPrint "$1 is empty - Nothing to remove" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ; Step 2: Scrub EnvVar + ; + StrCpy $0 $5 ; Copy the contents to $0 + ; Remove spaces around semicolons (NOTE: spaces before the 1st entry or + ; after the last one are not removed here but instead in Step 3) + ${If} $0 != "" ; If EnvVar is not empty ... + ${Do} + ${${UN}StrStr} $7 $0 " ;" + ${If} $7 == "" + ${ExitDo} + ${EndIf} + ${${UN}StrRep} $0 $0 " ;" ";" ; Remove ';' + ${Loop} + ${Do} + ${${UN}StrStr} $7 $0 "; " + ${If} $7 == "" + ${ExitDo} + ${EndIf} + ${${UN}StrRep} $0 $0 "; " ";" ; Remove ';' + ${Loop} + ${Do} + ${${UN}StrStr} $7 $0 ";;" + ${If} $7 == "" + ${ExitDo} + ${EndIf} + ${${UN}StrRep} $0 $0 ";;" ";" + ${Loop} + + ; Remove a leading or trailing semicolon from EnvVar + StrCpy $7 $0 1 0 + ${If} $7 == ";" + StrCpy $0 $0 "" 1 ; Change ';' to '' + ${EndIf} + StrLen $6 $0 + IntOp $6 $6 - 1 + StrCpy $7 $0 1 $6 + ${If} $7 == ";" + StrCpy $0 $0 $6 ; Change ';' to '' + ${EndIf} + ; DetailPrint "Scrubbed $1: [$0]" ; Uncomment to debug + ${EndIf} + + /* Step 3. Remove all instances of the target path/string (even if "A" or "P") + $6 = bool flag (1 = found and removed PathString) + $7 = a string (e.g. path) delimited by semicolon(s) + $8 = entry counter starting at 0 + $9 = copy of $0 + $R0 = tempChar */ + + ${If} $5 != "" ; If EnvVar is not empty ... + StrCpy $9 $0 + StrCpy $0 "" + StrCpy $8 0 + StrCpy $6 0 + + ${Do} + ${${UN}StrTok} $7 $9 ";" $8 "0" ; $7 = next entry, $8 = entry counter + + ${If} $7 == "" ; If we've run out of entries, + ${ExitDo} ; were done + ${EndIf} ; + + ; Remove leading and trailing spaces from this entry (critical step for Action=Remove) + ${Do} + StrCpy $R0 $7 1 + ${If} $R0 != " " + ${ExitDo} + ${EndIf} + StrCpy $7 $7 "" 1 ; Remove leading space + ${Loop} + ${Do} + StrCpy $R0 $7 1 -1 + ${If} $R0 != " " + ${ExitDo} + ${EndIf} + StrCpy $7 $7 -1 ; Remove trailing space + ${Loop} + ${If} $7 == $4 ; If string matches, remove it by not appending it + StrCpy $6 1 ; Set 'found' flag + ${ElseIf} $7 != $4 ; If string does NOT match + ${AndIf} $0 == "" ; and the 1st string being added to $0, + StrCpy $0 $7 ; copy it to $0 without a prepended semicolon + ${ElseIf} $7 != $4 ; If string does NOT match + ${AndIf} $0 != "" ; and this is NOT the 1st string to be added to $0, + StrCpy $0 $0;$7 ; append path to $0 with a prepended semicolon + ${EndIf} ; + + IntOp $8 $8 + 1 ; Bump counter + ${Loop} ; Check for duplicates until we run out of paths + ${EndIf} + + ; Step 4: Perform the requested Action + ; + ${If} $2 != "R" ; If Append or Prepend + ${If} $6 == 1 ; And if we found the target + DetailPrint "Target is already present in $1. It will be removed and" + ${EndIf} + ${If} $0 == "" ; If EnvVar is (now) empty + StrCpy $0 $4 ; just copy PathString to EnvVar + ${If} $6 == 0 ; If found flag is either 0 + ${OrIf} $6 == "" ; or blank (if EnvVarName is empty) + DetailPrint "$1 was empty and has been updated with the target" + ${EndIf} + ${ElseIf} $2 == "A" ; If Append (and EnvVar is not empty), + StrCpy $0 $0;$4 ; append PathString + ${If} $6 == 1 + DetailPrint "appended to $1" + ${Else} + DetailPrint "Target was appended to $1" + ${EndIf} + ${Else} ; If Prepend (and EnvVar is not empty), + StrCpy $0 $4;$0 ; prepend PathString + ${If} $6 == 1 + DetailPrint "prepended to $1" + ${Else} + DetailPrint "Target was prepended to $1" + ${EndIf} + ${EndIf} + ${Else} ; If Action = Remove + ${If} $6 == 1 ; and we found the target + DetailPrint "Target was found and removed from $1" + ${Else} + DetailPrint "Target was NOT found in $1 (nothing to remove)" + ${EndIf} + ${If} $0 == "" + DetailPrint "$1 is now empty" + ${EndIf} + ${EndIf} + + ; Step 5: Update the registry at RegLoc with the updated EnvVar and announce the change + ; + ClearErrors + ${If} $3 == HKLM + WriteRegExpandStr ${hklm_all_users} $1 $0 ; Write it in all users section + ${ElseIf} $3 == HKCU + WriteRegExpandStr ${hkcu_current_user} $1 $0 ; Write it to current user section + ${EndIf} + + IfErrors 0 +4 + MessageBox MB_OK|MB_ICONEXCLAMATION "Could not write updated $1 to $3" + DetailPrint "Could not write updated $1 to $3" + Goto EnvVarUpdate_Restore_Vars + + ; "Export" our change + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + EnvVarUpdate_Restore_Vars: + ; + ; Restore the user's variables and return ResultVar + Pop $R0 + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Push $0 ; Push my $0 (ResultVar) + Exch + Pop $0 ; Restore his $0 + +FunctionEnd + +!macroend ; EnvVarUpdate UN +!insertmacro EnvVarUpdate "" +!insertmacro EnvVarUpdate "un." +;----------------------------------- EnvVarUpdate end---------------------------------------- + +!verbose pop +!endif \ No newline at end of file diff --git a/build/nsis.geth.nsi b/build/nsis.geth.nsi new file mode 100644 index 000000000..dbeb9319c --- /dev/null +++ b/build/nsis.geth.nsi @@ -0,0 +1,65 @@ +# Builds a Windows installer with NSIS. +# It expects the following command line arguments: +# - OUTPUTFILE, filename of the installer (without extension) +# - MAJORVERSION, major build version +# - MINORVERSION, minor build version +# - BUILDVERSION, build id version +# +# The created installer executes the following steps: +# 1. install geth for all users +# 2. install optional development tools such as abigen +# 3. create an uninstaller +# 4. configures the Windows firewall for geth +# 5. create geth, attach and uninstall start menu entries +# 6. configures the registry that allows Windows to manage the package through its platform tools +# 7. adds the environment system wide variable ETHEREUM_SOCKET +# 8. adds the install directory to %PATH% +# +# Requirements: +# - NSIS, http://nsis.sourceforge.net/Main_Page +# - SFP, http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin (put dll in NSIS\Plugins\x86-ansi) +# +# based on: http://nsis.sourceforge.net/A_simple_installer_with_start_menu_shortcut_and_uninstaller +# +# TODO: +# - sign installer +CRCCheck on + +!define GROUPNAME "Ethereum" +!define APPNAME "Geth" +!define DESCRIPTION "Official Go implementation of the Ethereum protocol" +!addplugindir .\ + +# Require admin rights on NT6+ (When UAC is turned on) +RequestExecutionLevel admin + +# Use LZMA compression +SetCompressor /SOLID lzma + +!include LogicLib.nsh +!include EnvVarUpdate.nsh + +!macro VerifyUserIsAdmin +UserInfo::GetAccountType +pop $0 +${If} $0 != "admin" # Require admin rights on NT4+ + messageBox mb_iconstop "Administrator rights required!" + setErrorLevel 740 # ERROR_ELEVATION_REQUIRED + quit +${EndIf} +!macroend + +function .onInit + # make vars are global for all users since geth is installed global + setShellVarContext all + !insertmacro VerifyUserIsAdmin + + ${If} ${ARCH} == "amd64" + StrCpy $InstDir "$PROGRAMFILES64\${APPNAME}" + ${Else} + StrCpy $InstDir "$PROGRAMFILES32\${APPNAME}" + ${Endif} +functionEnd + +!include install.nsh +!include uninstall.nsh diff --git a/build/nsis.install.nsh b/build/nsis.install.nsh new file mode 100644 index 000000000..f9ad8e95e --- /dev/null +++ b/build/nsis.install.nsh @@ -0,0 +1,102 @@ +Name "geth ${MAJORVERSION}.${MINORVERSION}.${BUILDVERSION}" # VERSION variables set through command line arguments +InstallDir "$InstDir" +OutFile "${OUTPUTFILE}" # set through command line arguments + +# Links for "Add/Remove Programs" +!define HELPURL "https://github.com/ethereum/go-ethereum/issues" +!define UPDATEURL "https://github.com/ethereum/go-ethereum/releases" +!define ABOUTURL "https://github.com/ethereum/go-ethereum#ethereum-go" +!define /date NOW "%Y%m%d" + +PageEx license + LicenseData {{.License}} +PageExEnd + +# Install geth binary +Section "Geth" GETH_IDX + SetOutPath $INSTDIR + file {{.Geth}} + + # Create start menu launcher + createDirectory "$SMPROGRAMS\${APPNAME}" + createShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\geth.exe" "--fast" "--cache=512" + createShortCut "$SMPROGRAMS\${APPNAME}\Attach.lnk" "$INSTDIR\geth.exe" "attach" "" "" + createShortCut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "" "" + + # Firewall - remove rules (if exists) + SimpleFC::AdvRemoveRule "Geth incoming peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Geth outgoing peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Geth UDP discovery (UDP:30303)" + + # Firewall - add rules + SimpleFC::AdvAddRule "Geth incoming peers (TCP:30303)" "" 6 1 1 2147483647 1 "$INSTDIR\geth.exe" "" "" "Ethereum" 30303 "" "" "" + SimpleFC::AdvAddRule "Geth outgoing peers (TCP:30303)" "" 6 2 1 2147483647 1 "$INSTDIR\geth.exe" "" "" "Ethereum" "" 30303 "" "" + SimpleFC::AdvAddRule "Geth UDP discovery (UDP:30303)" "" 17 2 1 2147483647 1 "$INSTDIR\geth.exe" "" "" "Ethereum" "" 30303 "" "" + + # Set default IPC endpoint (https://github.com/ethereum/EIPs/issues/147) + ${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc" + ${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "A" "HKLM" "\\.\pipe\geth.ipc" + + # Add geth to PATH + ${EnvVarUpdate} $0 "PATH" "A" "HKLM" $INSTDIR +SectionEnd + +# Install optional develop tools. +Section /o "Development tools" DEV_TOOLS_IDX + SetOutPath $INSTDIR + {{range .DevTools}}file {{.}} + {{end}} +SectionEnd + +# Return on top of stack the total size (as DWORD) of the selected/installed sections. +Var GetInstalledSize.total +Function GetInstalledSize + StrCpy $GetInstalledSize.total 0 + + ${if} ${SectionIsSelected} ${GETH_IDX} + SectionGetSize ${GETH_IDX} $0 + IntOp $GetInstalledSize.total $GetInstalledSize.total + $0 + ${endif} + + ${if} ${SectionIsSelected} ${DEV_TOOLS_IDX} + SectionGetSize ${DEV_TOOLS_IDX} $0 + IntOp $GetInstalledSize.total $GetInstalledSize.total + $0 + ${endif} + + IntFmt $GetInstalledSize.total "0x%08X" $GetInstalledSize.total + Push $GetInstalledSize.total +FunctionEnd + +# Write registry, Windows uses these values in various tools such as add/remove program. +# PowerShell: Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, InstallLocation, InstallDate | Format-Table –AutoSize +function .onInstSuccess + # Save information in registry in HKEY_LOCAL_MACHINE branch, Windows add/remove functionality depends on this + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "DisplayName" "${GROUPNAME} - ${APPNAME} - ${DESCRIPTION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "InstallLocation" "$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "InstallDate" "${NOW}" + # Wait for Alex + #WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "Publisher" "${GROUPNAME}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "HelpLink" "${HELPURL}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "URLUpdateInfo" "${UPDATEURL}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "URLInfoAbout" "${ABOUTURL}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "DisplayVersion" "${MAJORVERSION}.${MINORVERSION}.${BUILDVERSION}" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "VersionMajor" ${MAJORVERSION} + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "VersionMinor" ${MINORVERSION} + # There is no option for modifying or repairing the install + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "NoRepair" 1 + + Call GetInstalledSize + Pop $0 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "EstimatedSize" "$0" + + # Create uninstaller + writeUninstaller "$INSTDIR\uninstall.exe" +functionEnd + +Page components +Page directory +Page instfiles diff --git a/build/nsis.simplefc.dll b/build/nsis.simplefc.dll new file mode 100644 index 000000000..73b7d9634 Binary files /dev/null and b/build/nsis.simplefc.dll differ diff --git a/build/nsis.simplefc.source.zip b/build/nsis.simplefc.source.zip new file mode 100644 index 000000000..d7476022a Binary files /dev/null and b/build/nsis.simplefc.source.zip differ diff --git a/build/nsis.uninstall.nsh b/build/nsis.uninstall.nsh new file mode 100644 index 000000000..ea7d5e298 --- /dev/null +++ b/build/nsis.uninstall.nsh @@ -0,0 +1,32 @@ +Section "Uninstall" + # uninstall for all users + setShellVarContext all + + # Delete (optionally) installed files + {{range $}}Delete $INSTDIR\{{.}} + {{end}} + Delete $INSTDIR\uninstall.exe + + # Delete install directory + rmDir $INSTDIR + + # Delete start menu launcher + Delete "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" + Delete "$SMPROGRAMS\${APPNAME}\Attach.lnk" + Delete "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" + rmDir "$SMPROGRAMS\${APPNAME}" + + # Firewall - remove rules if exists + SimpleFC::AdvRemoveRule "Geth incoming peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Geth outgoing peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Geth UDP discovery (UDP:30303)" + + # Remove IPC endpoint (https://github.com/ethereum/EIPs/issues/147) + ${un.EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc" + + # Remove install directory from PATH + ${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" $INSTDIR + + # Cleanup registry (deletes all sub keys) + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" +SectionEnd diff --git a/internal/build/util.go b/internal/build/util.go index a821cd7f2..ce17ce220 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -20,6 +20,7 @@ import ( "bytes" "flag" "fmt" + "io" "io/ioutil" "log" "os" @@ -117,3 +118,25 @@ func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x log.Fatal(err) } } + +// CopyFile copies a file. +func CopyFile(dst, src string, mode os.FileMode) { + if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { + log.Fatal(err) + } + destFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + log.Fatal(err) + } + defer destFile.Close() + + srcFile, err := os.Open(src) + if err != nil { + log.Fatal(err) + } + defer srcFile.Close() + + if _, err := io.Copy(destFile, srcFile); err != nil { + log.Fatal(err) + } +}