aboutsummaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2016-11-09 05:55:39 +0800
committerFelix Lange <fjl@twurst.com>2016-11-09 05:55:39 +0800
commit9bc97a5785d3d350a084b46fc77a8439b8dc533b (patch)
tree1802db150e1fed6272e08dd17b85a8a188ee55c8 /build
parent6707f70b4a947a06fdcf564619e6c8220a6654ad (diff)
downloaddexon-9bc97a5785d3d350a084b46fc77a8439b8dc533b.tar.gz
dexon-9bc97a5785d3d350a084b46fc77a8439b8dc533b.tar.zst
dexon-9bc97a5785d3d350a084b46fc77a8439b8dc533b.zip
build: NSIS based Windows installer (#3240)
This commit adds support for creating Windows installers to ci.go
Diffstat (limited to 'build')
-rw-r--r--build/ci.go75
-rw-r--r--build/nsis.envvarupdate.nsh327
-rw-r--r--build/nsis.geth.nsi65
-rw-r--r--build/nsis.install.nsh102
-rw-r--r--build/nsis.simplefc.dllbin0 -> 179712 bytes
-rw-r--r--build/nsis.simplefc.source.zipbin0 -> 23209 bytes
-rw-r--r--build/nsis.uninstall.nsh32
7 files changed, 600 insertions, 1 deletions
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 '<space>;'
+ ${Loop}
+ ${Do}
+ ${${UN}StrStr} $7 $0 "; "
+ ${If} $7 == ""
+ ${ExitDo}
+ ${EndIf}
+ ${${UN}StrRep} $0 $0 "; " ";" ; Remove ';<space>'
+ ${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 ';<EnvVar>' to '<EnvVar>'
+ ${EndIf}
+ StrLen $6 $0
+ IntOp $6 $6 - 1
+ StrCpy $7 $0 1 $6
+ ${If} $7 == ";"
+ StrCpy $0 $0 $6 ; Change ';<EnvVar>' to '<EnvVar>'
+ ${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
--- /dev/null
+++ b/build/nsis.simplefc.dll
Binary files differ
diff --git a/build/nsis.simplefc.source.zip b/build/nsis.simplefc.source.zip
new file mode 100644
index 000000000..d7476022a
--- /dev/null
+++ b/build/nsis.simplefc.source.zip
Binary files 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