UNPKG

7.66 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const command_1 = require("@oclif/command");
4const qq = require("qqjs");
5const Tarballs = require("../../tarballs");
6class 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}
40PackWin.description = 'create windows installer from oclif CLI';
41PackWin.flags = {
42 root: command_1.flags.string({ char: 'r', description: 'path to oclif CLI root', default: '.', required: true }),
43};
44exports.default = PackWin;
45const scripts = {
46 cmd: (config) => `@echo off
47setlocal enableextensions
48
49set ${config.scopedEnvVarKey('BINPATH')}=%~dp0\\${config.bin}.cmd
50if 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
57basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")
58
59"$basedir/../client/bin/${config.bin}.cmd" "$@"
60ret=$?
61exit $ret
62`,
63 nsis: (config, arch) => `!include MUI2.nsh
64
65!define Version '${config.version.split('-')[0]}'
66Name "${config.name}"
67CRCCheck On
68InstallDirRegKey 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
79OutFile "installer.exe"
80VIProductVersion "\${VERSION}.0"
81VIAddVersionKey /LANG=\${LANG_ENGLISH} "ProductName" "${config.name}"
82VIAddVersionKey /LANG=\${LANG_ENGLISH} "Comments" "${config.pjson.homepage}"
83VIAddVersionKey /LANG=\${LANG_ENGLISH} "CompanyName" "${config.scopedEnvVar('AUTHOR') || config.pjson.author}"
84VIAddVersionKey /LANG=\${LANG_ENGLISH} "LegalCopyright" "${new Date().getFullYear()}"
85VIAddVersionKey /LANG=\${LANG_ENGLISH} "FileDescription" "${config.pjson.description}"
86VIAddVersionKey /LANG=\${LANG_ENGLISH} "FileVersion" "\${VERSION}.0"
87VIAddVersionKey /LANG=\${LANG_ENGLISH} "ProductVersion" "\${VERSION}.0"
88
89InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}"
90
91Section "${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}"
105SectionEnd
106
107Section "Set PATH to ${config.name}"
108 Push "$INSTDIR\\bin"
109 Call AddToPath
110SectionEnd
111
112Section "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
114SectionEnd
115
116Section "Uninstall"
117 Delete "$INSTDIR\\Uninstall.exe"
118 RMDir /r "$INSTDIR"
119 RMDir /r "$LOCALAPPDATA\\${config.dirname}"
120 DeleteRegKey /ifempty HKCU "Software\\${config.dirname}"
121SectionEnd
122
123!define Environ 'HKCU "Environment"'
124Function 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
185done:
186 Pop $4
187 Pop $3
188 Pop $2
189 Pop $1
190 Pop $0
191FunctionEnd
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
201Function 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
218done:
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,...]
225FunctionEnd
226`
227};