/
windows.js
417 lines (379 loc) Β· 14.8 KB
/
windows.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/**
* Detects the Windows Phone development environment and its dependencies.
*
* @module lib/wp8
*
* @copyright
* Copyright (c) 2009-2014 by Appcelerator, Inc. All Rights Reserved.
*
* @license
* Licensed under the terms of the Apache Public License
* Please see the LICENSE included with this distribution for details.
*/
var appc = require('node-appc'),
async = require('async'),
fs = require('fs'),
path = require('path'),
spawn = require('child_process').spawn,
__ = appc.i18n(__dirname).__,
mobilewebPackageJson = {},
cachedResults;
// need to find the mobile web platform and its package.json
(function findPackageJson(dir) {
if (dir != '/') {
var file = path.join(dir, 'mobileweb', 'package.json');
if (fs.existsSync(file)) {
mobilewebPackageJson = require(file);
} else {
findPackageJson(path.dirname(dir));
}
}
}(path.join(__dirname, '..', '..', '..')));
/**
* Detects current Windows Phone environment.
* @param {Object} config - The CLI config object
* @param {Object} opts - Detect options
* @param {Function} finished - Callback when detection is finished
*/
exports.detect = function detect(config, opts, finished) {
if (process.platform != 'win32') return finished();
opts || (opts = {});
if (cachedResults && !opts.bypassCache) return finished(cachedResults);
var results = {
issues: []
};
async.parallel({
'visualstudio': function (next) {
var keyRegExp = /.+\\(\d+\.\d)_config$/i,
valueRegExp = /\s(\w+)\s+(REG_\w+)\s+(.+)$/,
possibleVersions = {},
vsInfo = {};
// scan the registry to find some Visual Studio installs
async.parallel([
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\VisualStudio', // there should not be anything here because VS is currently 32-bit and we'll find it next
'HKEY_LOCAL_MACHINE\\Software\\Wow6432Node\\Microsoft\\VisualStudio', // this is where VS should be found because it's 32-bit
'HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio' // should be the same as the one above, but just to be safe
].map(function (keyPath) {
return function (next) {
appc.subprocess.run('reg', ['query', keyPath], function (code, out, err) {
if (!code) {
out.trim().split(/\r\n|\n/).forEach(function (configKey) {
configKey = configKey.trim();
var m = configKey.match(keyRegExp);
if (m) {
possibleVersions[configKey] = {
version: m[1],
configKey: configKey
};
}
});
}
next();
});
};
}), function () {
// if we didn't find any Visual Studios, then we're done
if (!Object.keys(possibleVersions).length) {
results.issues.push({
id: 'WINDOWS_VISUAL_STUDIO_NOT_INSTALLED',
type: 'error',
message: __('Microsoft Visual Studio not found.') + '\n' +
__('You will be unable to build Windows Phone or Windows Store apps.') + '\n' +
__('You can install it from %s.', '__http://appcelerator.com/visualstudio__')
});
return next();
}
// fetch Visual Studio install information
async.parallel(Object.keys(possibleVersions).map(function (configKey) {
return function (next) {
appc.subprocess.run('reg', ['query', configKey, '/v', '*'], function (code, out, err) {
if (!code) {
var ver = possibleVersions[configKey].version,
info = vsInfo[ver] = {
version: ver,
registryKey: configKey,
supported: appc.version.satisfies(ver, mobilewebPackageJson.vendorDependencies['visual studio'], true),
vcvarsall: null,
wpsdk: null,
selected: false
};
// get only the values we are interested in
out.trim().split(/\r\n|\n/).forEach(function (line) {
var parts = line.trim().split(' ').map(function (p) { return p.trim(); });
if (parts.length == 3) {
if (parts[0] == 'CLR Version') {
info.clrVersion = parts[2];
} else if (parts[0] == 'ShellFolder') {
info.path = parts[2];
}
}
});
// verify that this Visual Studio actually exists
if (info.path && fs.existsSync(info.path) && fs.existsSync(path.join(info.path, 'Common7', 'IDE', 'devenv.exe'))) {
// get the vcvarsall script
var vcvarsall = path.join(info.path, 'VC', 'vcvarsall.bat');
if (fs.existsSync(vcvarsall)) {
info.vcvarsall = vcvarsall;
}
// detect all Windows Phone SDKs
var wpsdkDir = path.join(info.path, 'VC', 'WPSDK');
fs.existsSync(wpsdkDir) && fs.readdirSync(wpsdkDir).forEach(function (ver) {
var vcvarsphone = path.join(wpsdkDir, ver, 'vcvarsphoneall.bat');
if (fs.existsSync(vcvarsphone) && /^wp\d+$/i.test(ver)) {
// we found a windows phone sdk!
var name = (parseInt(ver.replace(/^wp/i, '')) / 10).toFixed(1);
info.wpsdk || (info.wpsdk = {});
info.wpsdk[name] = {
vcvarsphone: vcvarsphone
};
}
});
}
}
if (info.vcvarsall) {
appc.subprocess.getRealName(info.vcvarsall, function (err, file) {
if (!err) {
info.vcvarsall = file
}
next();
});
} else {
next();
}
});
};
}), function () {
// double check if we didn't find any Visual Studios, then we're done
if (!Object.keys(vsInfo).length) {
results.issues.push({
id: 'WINDOWS_VISUAL_STUDIO_NOT_INSTALLED',
type: 'error',
message: __('Microsoft Visual Studio not found.') + '\n' +
__('You will be unable to build Windows Phone or Windows Store apps.') + '\n' +
__('You can install it from %s.', '__http://appcelerator.com/visualstudio__')
});
return next();
}
var preferredVersion = config.get('windows.visualstudio.selectedVersion');
vsInfo[preferredVersion && vsInfo[preferredVersion] ? preferredVersion : Object.keys(vsInfo).sort().pop()].selected = true;
next(null, vsInfo);
});
});
},
'windowsphone': function (next) {
var wpInfo = {};
async.parallel([
'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Microsoft SDKs\\WindowsPhone', // probably nothing here
'HKEY_LOCAL_MACHINE\\Software\\Wow6432Node\\Microsoft\\Microsoft SDKs\\WindowsPhone' // this is most likely where WPSDK will be found
].map(function (keyPath) {
return function (next) {
appc.subprocess.run('reg', ['query', keyPath], function (code, out, err) {
var keyRegExp = /.+\\(v\d+\.\d)$/;
if (!code) {
out.trim().split(/\r\n|\n/).forEach(function (key) {
key = key.trim();
var m = key.match(keyRegExp),
version = m[1].replace(/^v/, '');
if (m) {
wpInfo[version] = {
version: version,
registryKey: keyPath + '\\' + m[1],
supported: appc.version.satisfies(version, mobilewebPackageJson.vendorDependencies['windows phone sdk'], false), // no maybes
path: null,
deployCmd: null,
selected: false,
devices: null
};
}
});
}
next();
});
};
}), function () {
// check if we didn't find any Windows Phone SDKs, then we're done
if (!Object.keys(wpInfo).length) {
results.issues.push({
id: 'WINDOWS_PHONE_SDK_NOT_INSTALLED',
type: 'error',
message: __('Microsoft Windows Phone SDK not found.') + '\n' +
__('You will be unable to build Windows Phone apps.') + '\n' +
__('You can install it from %s.', '__http://appcelerator.com/windowsphone__')
});
return next();
}
// fetch Windows Phone SDK install information
async.parallel(Object.keys(wpInfo).map(function (ver) {
return function (next) {
appc.subprocess.run('reg', ['query', wpInfo[ver].registryKey + '\\Install Path', '/v', '*'], function (code, out, err) {
if (code) {
// bad key? either way, remove this version
delete wpInfo[ver];
} else {
// get only the values we are interested in
out.trim().split(/\r\n|\n/).forEach(function (line) {
var parts = line.trim().split(' ').map(function (p) { return p.trim(); });
if (parts.length == 3) {
if (parts[0] == 'Install Path') {
wpInfo[ver].path = parts[2];
var deployCmd = path.join(parts[2], 'Tools', 'XAP Deployment', 'XapDeployCmd.exe');
// check the old WP8 location
if (fs.existsSync(deployCmd)) {
wpInfo[ver].deployCmd = deployCmd;
// check the new WP8.1 location
} else if (fs.existsSync(deployCmd = path.join(parts[2], 'Tools', 'AppDeploy', 'AppDeployCmd.exe'))) {
wpInfo[ver].deployCmd = deployCmd;
}
}
}
});
}
next();
});
};
}), function () {
// double check if we didn't find any Windows Phone SDKs, then we're done
if (Object.keys(wpInfo).every(function (v) { return !wpInfo[v].path; })) {
results.issues.push({
id: 'WINDOWS_PHONE_SDK_NOT_INSTALLED',
type: 'error',
message: __('Microsoft Windows Phone SDK not found.') + '\n' +
__('You will be unable to build Windows Phone apps.') + '\n' +
__('You can install it from %s.', '__http://appcelerator.com/windowsphone__')
});
return next();
}
if (Object.keys(wpInfo).every(function (v) { return !wpInfo[v].deployCmd; })) {
results.issues.push({
id: 'WINDOWS_PHONE_SDK_MISSING_DEPLOY_CMD',
type: 'error',
message: __('Microsoft Windows Phone SDK is missing the deploy command.') + '\n' +
__('You will be unable to build Windows Phone apps.') + '\n' +
__('You can install it from %s.', '__http://appcelerator.com/windowsphone__')
});
return next();
}
var preferredVersion = config.get('windows.wpsdk.selectedVersion');
if (!wpInfo[preferredVersion] || !wpInfo[preferredVersion].supported) {
preferredVersion = Object.keys(wpInfo).sort().filter(function (v) { return wpInfo[v].supported; }).shift();
}
if (preferredVersion) {
wpInfo[preferredVersion].selected = true;
}
next(null, wpInfo);
});
});
}
}, function (err, result) {
appc.util.mix(results, result);
// now that we've detected Visual Studio, we can try to find MSBuild
var selectedVisualStudio = exports.getSelectedVisualStudio(results);
async.parallel({
'msbuild': function (next) {
if (!selectedVisualStudio) return next();
appc.subprocess.run('cmd', [ '/C', selectedVisualStudio.vcvarsall + ' && MSBuild /version' ], function (code, out, err) {
var msbuildInfo = null;
if (code) {
results.issues.push({
id: 'WINDOWS_MSBUILD_ERROR',
type: 'error',
message: __('Failed to run MSBuild.') + '\n' +
__('This is most likely due to Visual Studio cannot find a suitable .NET framework.') + '\n' +
__('Please install the latest .NET framework.')
});
} else {
var chunks = out.trim().split(/\r\n\r\n|\n\n/);
chunks.shift(); // strip off the first chunk
msbuildInfo = {
version: chunks.shift().split(/\r\n|\n/).pop().trim()
};
if (!appc.version.satisfies(msbuildInfo.version, mobilewebPackageJson.vendorDependencies['msbuild'])) {
results.issues.push({
id: 'WINDOWS_MSBUILD_TOO_OLD',
type: 'error',
message: __('The MSBuild version %s is too old.', msbuildInfo.version) + '\n' +
__("Titanium requires .NET MSBuild '%s'.", mobilewebPackageJson.vendorDependencies['msbuild']) + '\n' +
__('Please install the latest .NET framework.')
});
}
}
next(null, msbuildInfo);
});
},
'devices': function (next) {
if (!result.windowsphone) return next();
async.each(Object.keys(result.windowsphone), function (version, callback) {
var wpInfo = result.windowsphone[version];
if (!wpInfo.deployCmd) return callback();
appc.subprocess.run(wpInfo.deployCmd, '/EnumerateDevices', function (code, out, err) {
if (err) {
if (!results.issues.some(function (i) { return i.id == 'WINDOWS_PHONE_ENUMERATE_DEVICES_FAILED'; })) {
results.issues.push({
id: 'WINDOWS_PHONE_ENUMERATE_DEVICES_FAILED',
type: 'error',
message: __('Failed to enumerate Windows Phone devices.') + '\n' +
__('Ensure that the Windows Phone SDK is properly installed.')
});
}
} else {
var deviceRegExp = /^ ([0-9]*)\t\t(.*)$/;
wpInfo.devices = {};
out.trim().split(/\r\n|\n/).forEach(function (line) {
var m = line.match(deviceRegExp);
if (m) {
wpInfo.devices[m[1]] = m[2];
}
});
if (!results.issues.some(function (i) { return i.id == 'WINDOWS_PHONE_EMULATOR_NOT_INSTALLED'; }) && Object.keys(wpInfo.devices).filter(function (d) { return !/device/i.test(wpInfo.devices[d]); }).length == 0) {
results.issues.push({
id: 'WINDOWS_PHONE_EMULATOR_NOT_INSTALLED',
type: 'error',
message: __('Windows Phone Emulator is not installed.') + '\n' +
__('Ensure that the Windows Phone Emulator is properly installed.') + '\n' +
__('You must be running 64-bit Windows 8.1 Pro with Hyper-V support enabled.')
});
}
}
callback();
});
}, next);
},
'powershell': function (next) {
if (!selectedVisualStudio) return next();
var args = [
'&&',
config.get('windows.executables.powershell', 'powershell'),
'-command',
path.resolve(__dirname, '..', '..', '..', 'node_modules', 'titanium-sdk', 'bin', 'test_permissions.ps1')
],
child = spawn(selectedVisualStudio.vcvarsall, args, {
cwd: path.join(selectedVisualStudio.path, 'VC')
});
child.stdout.on('data', function () {});
child.stderr.on('data', function () {});
child.on('close', function (code) {
if (code) {
results.issues.push({
id: 'WINDOWS_PHONE_POWERSHELL_SCRIPTS_DISABLED',
type: 'error',
message: __('Executing PowerShell scripts is disabled.') + '\n' +
__('In order to build Windows Hybrid apps for the Windows Store (winstore), you must change the execution policy to allow PowerShell scripts.') + '\n' +
__('To enable PowerShell scripts, search __PowerShell__ in the __Start__ menu, right click the icon, select __Run as administrator__, then run:') + '\n' +
' __Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser__'
});
}
next(null, { enabled: !code });
});
}
}, function (err, result) {
finished(cachedResults = appc.util.mix(results, result));
});
});
};
exports.getSelectedVisualStudio = function getSelectedVisualStudio(env) {
if (env.visualstudio) {
return env.visualstudio[Object.keys(env.visualstudio).sort().filter(function (v) {
return env.visualstudio[v].selected;
}).pop()];
}
};