1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const command_1 = require("@oclif/command");
|
4 | const qq = require("qqjs");
|
5 | const Tarballs = require("../../tarballs");
|
6 | class PackWin extends command_1.Command {
|
7 | async run() {
|
8 | await this.checkForNSIS();
|
9 | const { flags } = this.parse(PackWin);
|
10 | const buildConfig = await Tarballs.buildConfig(flags.root);
|
11 | const { config } = buildConfig;
|
12 | await Tarballs.build(buildConfig, { platform: 'win32', pack: false });
|
13 | const arches = buildConfig.targets.filter(t => t.platform === 'win32').map(t => t.arch);
|
14 | for (let arch of arches) {
|
15 | const installerBase = qq.join(buildConfig.tmp, `windows-${arch}-installer`);
|
16 | await qq.write([installerBase, `bin/${config.bin}.cmd`], scripts.cmd(config));
|
17 | await qq.write([installerBase, `bin/${config.bin}`], scripts.sh(config));
|
18 | await qq.write([installerBase, `${config.bin}.nsi`], scripts.nsis(config, arch));
|
19 | await qq.mv(buildConfig.workspace({ platform: 'win32', arch }), [installerBase, 'client']);
|
20 | await qq.x(`makensis ${installerBase}/${config.bin}.nsi | grep -v "\\[compress\\]" | grep -v "^File: Descending to"`);
|
21 | const o = buildConfig.dist(`win/${config.bin}-v${buildConfig.version}-${arch}.exe`);
|
22 | await qq.mv([installerBase, 'installer.exe'], o);
|
23 | this.log(`built ${o}`);
|
24 | }
|
25 | }
|
26 | async checkForNSIS() {
|
27 | try {
|
28 | await qq.x('makensis', { stdio: [0, null, 2] });
|
29 | }
|
30 | catch (err) {
|
31 | if (err.code === 1)
|
32 | return;
|
33 | if (err.code === 127)
|
34 | this.error('install makensis');
|
35 | else
|
36 | throw err;
|
37 | }
|
38 | }
|
39 | }
|
40 | PackWin.description = 'create windows installer from oclif CLI';
|
41 | PackWin.flags = {
|
42 | root: command_1.flags.string({ char: 'r', description: 'path to oclif CLI root', default: '.', required: true }),
|
43 | };
|
44 | exports.default = PackWin;
|
45 | const scripts = {
|
46 | cmd: (config) => `@echo off
|
47 | setlocal enableextensions
|
48 |
|
49 | set ${config.scopedEnvVarKey('BINPATH')}=%~dp0\\${config.bin}.cmd
|
50 | if exist "%LOCALAPPDATA%\\${config.dirname}\\client\\bin\\${config.bin}.cmd" (
|
51 | "%LOCALAPPDATA%\\${config.dirname}\\client\\bin\\${config.bin}.cmd" %*
|
52 | ) else (
|
53 | "%~dp0\\..\\client\\bin\\node.exe" "%~dp0\\..\\client\\bin\\run" %*
|
54 | )
|
55 | `,
|
56 | sh: (config) => `#!/bin/sh
|
57 | basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")
|
58 |
|
59 | "$basedir/../client/bin/${config.bin}.cmd" "$@"
|
60 | ret=$?
|
61 | exit $ret
|
62 | `,
|
63 | nsis: (config, arch) => `!include MUI2.nsh
|
64 |
|
65 | !define Version '${config.version.split('-')[0]}'
|
66 | Name "${config.name}"
|
67 | CRCCheck On
|
68 | InstallDirRegKey HKCU "Software\\${config.name}" ""
|
69 |
|
70 | !insertmacro MUI_PAGE_COMPONENTS
|
71 | !insertmacro MUI_PAGE_DIRECTORY
|
72 | !insertmacro MUI_PAGE_INSTFILES
|
73 |
|
74 | !insertmacro MUI_UNPAGE_CONFIRM
|
75 | !insertmacro MUI_UNPAGE_INSTFILES
|
76 |
|
77 | !insertmacro MUI_LANGUAGE "English"
|
78 |
|
79 | OutFile "installer.exe"
|
80 | VIProductVersion "\${VERSION}.0"
|
81 | VIAddVersionKey /LANG=\${LANG_ENGLISH} "ProductName" "${config.name}"
|
82 | VIAddVersionKey /LANG=\${LANG_ENGLISH} "Comments" "${config.pjson.homepage}"
|
83 | VIAddVersionKey /LANG=\${LANG_ENGLISH} "CompanyName" "${config.scopedEnvVar('AUTHOR') || config.pjson.author}"
|
84 | VIAddVersionKey /LANG=\${LANG_ENGLISH} "LegalCopyright" "${new Date().getFullYear()}"
|
85 | VIAddVersionKey /LANG=\${LANG_ENGLISH} "FileDescription" "${config.pjson.description}"
|
86 | VIAddVersionKey /LANG=\${LANG_ENGLISH} "FileVersion" "\${VERSION}.0"
|
87 | VIAddVersionKey /LANG=\${LANG_ENGLISH} "ProductVersion" "\${VERSION}.0"
|
88 |
|
89 | InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}"
|
90 |
|
91 | Section "${config.name} CLI \${VERSION}"
|
92 | SetOutPath $INSTDIR
|
93 | File /r bin
|
94 | File /r client
|
95 | ExpandEnvStrings $0 "%COMSPEC%"
|
96 |
|
97 | WriteRegStr HKCU "Software\\${config.dirname}" "" $INSTDIR
|
98 | WriteUninstaller "$INSTDIR\\Uninstall.exe"
|
99 | WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${config.dirname}" \\
|
100 | "DisplayName" "${config.name}"
|
101 | WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${config.dirname}" \\
|
102 | "UninstallString" "$\\"$INSTDIR\\uninstall.exe$\\""
|
103 | WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${config.dirname}" \\
|
104 | "Publisher" "${config.scopedEnvVar('AUTHOR') || config.pjson.author}"
|
105 | SectionEnd
|
106 |
|
107 | Section "Set PATH to ${config.name}"
|
108 | Push "$INSTDIR\\bin"
|
109 | Call AddToPath
|
110 | SectionEnd
|
111 |
|
112 | Section "Add %LOCALAPPDATA%\\${config.dirname} to Windows Defender exclusions (highly recommended for performance!)"
|
113 | ExecShell "" '"$0"' "/C powershell -ExecutionPolicy Bypass -Command $\\"& {Add-MpPreference -ExclusionPath $\\"$LOCALAPPDATA\\${config.dirname}$\\"}$\\" -FFFeatureOff" SW_HIDE
|
114 | SectionEnd
|
115 |
|
116 | Section "Uninstall"
|
117 | Delete "$INSTDIR\\Uninstall.exe"
|
118 | RMDir /r "$INSTDIR"
|
119 | RMDir /r "$LOCALAPPDATA\\${config.dirname}"
|
120 | DeleteRegKey /ifempty HKCU "Software\\${config.dirname}"
|
121 | SectionEnd
|
122 |
|
123 | !define Environ 'HKCU "Environment"'
|
124 | Function AddToPath
|
125 | Exch $0
|
126 | Push $1
|
127 | Push $2
|
128 | Push $3
|
129 | Push $4
|
130 |
|
131 | ; NSIS ReadRegStr returns empty string on string overflow
|
132 | ; Native calls are used here to check actual length of PATH
|
133 |
|
134 | ; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3)
|
135 | System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4"
|
136 | IntCmp $4 0 0 done done
|
137 | ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
|
138 | ; RegCloseKey($3)
|
139 | System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i \${NSIS_MAX_STRLEN} r2) i.r4"
|
140 | System::Call "advapi32::RegCloseKey(i $3)"
|
141 |
|
142 | IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
|
143 | DetailPrint "AddToPath: original length $2 > \${NSIS_MAX_STRLEN}"
|
144 | MessageBox MB_OK "PATH not updated, original length $2 > \${NSIS_MAX_STRLEN}"
|
145 | Goto done
|
146 |
|
147 | IntCmp $4 0 +5 ; $4 != NO_ERROR
|
148 | IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
|
149 | DetailPrint "AddToPath: unexpected error code $4"
|
150 | Goto done
|
151 | StrCpy $1 ""
|
152 |
|
153 | ; Check if already in PATH
|
154 | Push "$1;"
|
155 | Push "$0;"
|
156 | Call StrStr
|
157 | Pop $2
|
158 | StrCmp $2 "" 0 done
|
159 | Push "$1;"
|
160 | Push "$0\\;"
|
161 | Call StrStr
|
162 | Pop $2
|
163 | StrCmp $2 "" 0 done
|
164 |
|
165 | ; Prevent NSIS string overflow
|
166 | StrLen $2 $0
|
167 | StrLen $3 $1
|
168 | IntOp $2 $2 + $3
|
169 | IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
|
170 | IntCmp $2 \${NSIS_MAX_STRLEN} +4 +4 0
|
171 | DetailPrint "AddToPath: new length $2 > \${NSIS_MAX_STRLEN}"
|
172 | MessageBox MB_OK "PATH not updated, new length $2 > \${NSIS_MAX_STRLEN}."
|
173 | Goto done
|
174 |
|
175 | ; Append dir to PATH
|
176 | DetailPrint "Add to PATH: $0"
|
177 | StrCpy $2 $1 1 -1
|
178 | StrCmp $2 ";" 0 +2
|
179 | StrCpy $1 $1 -1 ; remove trailing ';'
|
180 | StrCmp $1 "" +2 ; no leading ';'
|
181 | StrCpy $0 "$1;$0"
|
182 | WriteRegExpandStr \${Environ} "PATH" $0
|
183 | SendMessage \${HWND_BROADCAST} \${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
184 |
|
185 | done:
|
186 | Pop $4
|
187 | Pop $3
|
188 | Pop $2
|
189 | Pop $1
|
190 | Pop $0
|
191 | FunctionEnd
|
192 |
|
193 | ; StrStr - find substring in a string
|
194 | ;
|
195 | ; Usage:
|
196 | ; Push "this is some string"
|
197 | ; Push "some"
|
198 | ; Call StrStr
|
199 | ; Pop $0 ; "some string"
|
200 |
|
201 | Function StrStr
|
202 | Exch $R1 ; $R1=substring, stack=[old$R1,string,...]
|
203 | Exch ; stack=[string,old$R1,...]
|
204 | Exch $R2 ; $R2=string, stack=[old$R2,old$R1,...]
|
205 | Push $R3
|
206 | Push $R4
|
207 | Push $R5
|
208 | StrLen $R3 $R1
|
209 | StrCpy $R4 0
|
210 | ; $R1=substring, $R2=string, $R3=strlen(substring)
|
211 | ; $R4=count, $R5=tmp
|
212 | loop:
|
213 | StrCpy $R5 $R2 $R3 $R4
|
214 | StrCmp $R5 $R1 done
|
215 | StrCmp $R5 "" done
|
216 | IntOp $R4 $R4 + 1
|
217 | Goto loop
|
218 | done:
|
219 | StrCpy $R1 $R2 "" $R4
|
220 | Pop $R5
|
221 | Pop $R4
|
222 | Pop $R3
|
223 | Pop $R2
|
224 | Exch $R1 ; $R1=old$R1, stack=[result,...]
|
225 | FunctionEnd
|
226 | `
|
227 | };
|