UNPKG

19.6 kBJavaScriptView Raw
1/*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing,
13 software distributed under the License is distributed on an
14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 KIND, either express or implied. See the License for the
16 specific language governing permissions and limitations
17 under the License.
18*/
19
20var Q = require('q');
21var os = require('os');
22var path = require('path');
23var shell = require('shelljs');
24var spawn = require('cordova-common').superspawn.spawn;
25var CordovaError = require('cordova-common').CordovaError;
26
27var ConfigParser, MSBuildTools, Version;
28try {
29 ConfigParser = require('../../template/cordova/lib/ConfigParser');
30 MSBuildTools = require('../../template/cordova/lib/MSBuildTools');
31 Version = require('../../template/cordova/lib/Version');
32} catch (ex) {
33 // If previous import fails, we're probably running this script
34 // from installed platform and the module location is different.
35 ConfigParser = require('./ConfigParser');
36 MSBuildTools = require('./MSBuildTools');
37 Version = require('./Version');
38}
39
40// The constant for VS2013 Upd2 PackageVersion. See MSDN for
41// reference: https://msdn.microsoft.com/en-us/library/bb164659(v=vs.120).aspx
42var VS2013_UPDATE2_RC = new Version(12, 0, 30324);
43var REQUIRED_VERSIONS = {
44 '8.1': {
45 os: '6.3',
46 msbuild: '12.0',
47 visualstudio: '12.0',
48 windowssdk: '8.1',
49 phonesdk: '8.1'
50 },
51 '10.0': {
52 // Note that Windows 10 target is also supported on Windows 7, so this should look
53 // like '6.1 || >=6.3', but due to Version module restricted functionality we handle
54 // this case separately in checkOS function below.
55 os: '6.3',
56 msbuild: '14.0',
57 visualstudio: '14.0',
58 windowssdk: '10.0',
59 phonesdk: '10.0'
60 }
61};
62
63function getMinimalRequiredVersionFor (requirement, windowsTargetVersion, windowsPhoneTargetVersion) {
64 if (windowsTargetVersion === '8' || windowsTargetVersion === '8.0') {
65 throw new CordovaError('windows8 platform is deprecated. To use windows-target-version=8.0 you may downgrade to cordova-windows@4.');
66 }
67
68 if (windowsPhoneTargetVersion === '8' || windowsPhoneTargetVersion === '8.0') {
69 throw new CordovaError('8.0 is not a valid version for windows-phone-target-version (use the wp8 Cordova platform instead)');
70 }
71 var windowsReqVersion = Version.tryParse(REQUIRED_VERSIONS[windowsTargetVersion][requirement]);
72 var phoneReqVersion = Version.tryParse(REQUIRED_VERSIONS[windowsPhoneTargetVersion][requirement]);
73
74 // If we're searching for Windows SDK, we're not
75 // interested in Phone's version and and vice versa.
76 if (requirement === 'windowssdk') return windowsReqVersion;
77 if (requirement === 'phonesdk') return phoneReqVersion;
78
79 // If both windowsReqVersion and phoneReqVersion is valid Versions, choose the max one
80 if (windowsReqVersion && phoneReqVersion) {
81 return windowsReqVersion.gt(phoneReqVersion)
82 ? windowsReqVersion
83 : phoneReqVersion;
84 }
85
86 // Otherwise return that one which is defined and valid
87 return windowsReqVersion || phoneReqVersion;
88}
89
90function getHighestAppropriateVersion (versions, requiredVersion) {
91 return versions.map(function (version) {
92 return Version.tryParse(version);
93 })
94 .sort(Version.comparer)
95 .filter(function (toolVersion) {
96 return toolVersion.gte(requiredVersion);
97 }).reverse()[0];
98}
99
100/**
101 * Return Version object for current Windows version. User 'ver' binary or
102 * os.release() in case of errors.
103 *
104 * @return {Version} Version information for current OS.
105 */
106function getWindowsVersion () {
107 return spawn('ver').then(function (output) {
108 var match = /\[Version (.*)\]\s*$/.exec(output);
109 return Version.fromString(match[1]);
110 }).fail(function () {
111 return Version.fromString(os.release());
112 });
113}
114
115/**
116 * Lists all Visual Studio versions insalled. For VS 2013 if it present, also
117 * checks if Update 2 is installed.
118 *
119 * @return {String[]} List of installed Visual Studio versions.
120 */
121function getInstalledVSVersions () {
122 // Query all keys with Install value equal to 1, then filter out
123 // those, which are not related to VS itself
124 return spawn('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\DevDiv\\vs\\Servicing', '/s', '/v', 'Install', '/f', '1', '/d', '/e', '/reg:32'])
125 .fail(function () { return ''; })
126 .then(function (output) {
127 return output.split('\n')
128 .reduce(function (installedVersions, line) {
129 var match = /(\d+\.\d+)\\(ultimate|professional|premium|community)/.exec(line);
130 if (match && match[1] && installedVersions.indexOf(match[1]) === -1) { installedVersions.push(match[1]); }
131 return installedVersions;
132 }, []);
133 }).then(function (installedVersions) {
134 // If there is no VS2013 installed, the we have nothing to do
135 if (installedVersions.indexOf('12.0') === -1) return installedVersions;
136
137 // special case for VS 2013. We need to check if VS2013 update 2 is installed
138 return spawn('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\Updates\\Microsoft Visual Studio 2013\\vsupdate_KB2829760', '/v', 'PackageVersion', '/reg:32'])
139 .then(function (output) {
140 var updateVer = Version.fromString(/PackageVersion\s+REG_SZ\s+(.*)/i.exec(output)[1]);
141 // if update version is lover than Update2, reject the promise
142 if (VS2013_UPDATE2_RC.gte(updateVer)) return Q.reject();
143 return installedVersions;
144 }).fail(function () {
145 // if we got any errors on previous steps, we're assuming that
146 // required VS update is not installed.
147 installedVersions.splice(installedVersions.indexOf('12.0'), 1);
148 return installedVersions;
149 });
150 })
151 .then(function (installedVersions) {
152 var willowVersions = MSBuildTools.getWillowInstallations().map(function (installation) {
153 return installation.version;
154 });
155 return installedVersions.concat(willowVersions);
156 });
157}
158
159/**
160 * Gets list of installed Windows SDKs
161 *
162 * @return {Version[]} List of installed SDKs' versions
163 */
164function getInstalledWindowsSdks () {
165 var installedSdks = [];
166 return spawn('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows', '/s', '/v', 'InstallationFolder', '/reg:32'])
167 .fail(function () { return ''; })
168 .then(function (output) {
169 var re = /\\Microsoft SDKs\\Windows\\v(\d+\.\d+)\s*InstallationFolder\s+REG_SZ\s+(.*)/gim;
170 var match;
171 while ((match = re.exec(output))) {
172 var sdkPath = match[2];
173 // Verify that SDKs is really installed by checking SDKManifest file at SDK root
174 if (shell.test('-e', path.join(sdkPath, 'SDKManifest.xml'))) {
175 installedSdks.push(Version.tryParse(match[1]));
176 }
177 }
178 }).thenResolve(installedSdks);
179}
180
181/**
182 * Gets list of installed Windows Phone SDKs. Separately searches for 8.1 Phone
183 * SDK and Windows 10 SDK, because the latter is needed for both Windows and
184 * Windows Phone applications.
185 *
186 * @return {Version[]} List of installed Phone SDKs' versions.
187 */
188function getInstalledPhoneSdks () {
189 var installedSdks = [];
190 return spawn('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows Phone\\v8.1', '/v', 'InstallationFolder', '/reg:32'])
191 .fail(function () { return ''; })
192 .then(function (output) {
193 var match = /\\Microsoft SDKs\\Windows Phone\\v(\d+\.\d+)\s*InstallationFolder\s+REG_SZ\s+(.*)/gim.exec(output);
194 if (match && shell.test('-e', path.join(match[2], 'SDKManifest.xml'))) {
195 installedSdks.push(Version.tryParse(match[1]));
196 }
197 }).then(function () {
198 return spawn('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0', '/v', 'InstallationFolder', '/reg:32']);
199 }).fail(function () {
200 return '';
201 }).then(function (output) {
202 var match = /\\Microsoft SDKs\\Windows\\v(\d+\.\d+)\s*InstallationFolder\s+REG_SZ\s+(.*)/gim.exec(output);
203 if (match && shell.test('-e', path.join(match[2], 'SDKManifest.xml'))) {
204 installedSdks.push(Version.tryParse(match[1]));
205 }
206 }).thenResolve(installedSdks);
207}
208
209/**
210 * Shortens version string or Version object by leaving only first two segments
211 * (major and minor).
212 * @param {String|Version} version The version identifier. Either Version
213 * object or string that looks like "12.5.6"
214 * @return {String} Shortened version, or undefined if provided
215 * parameter is not a valid version
216 */
217function shortenVersion (version) {
218 return /^(\d+(?:\.\d+)?)/.exec(version.toString())[1];
219}
220
221function mapWindowsVersionToName (version) {
222 var map = {
223 '6.2': 'Windows 8',
224 '6.3': 'Windows 8.1',
225 '10.0': 'Windows 10'
226 };
227 var majorMinor = shortenVersion(version);
228 return map[majorMinor];
229}
230
231function mapVSVersionToName (version) {
232 var map = {
233 '11.0': '2012 Express for Windows',
234 '12.0': '2013 Express for Windows Update2',
235 '14.0': '2015 Community'
236 };
237 var majorMinor = shortenVersion(version);
238 return map[majorMinor];
239}
240
241/**
242 * Check if current OS is supports building windows platform
243 * @return {Promise} Promise either fullfilled or rejected with error message.
244 */
245var checkOS = function (windowsTargetVersion, windowsPhoneTargetVersion) {
246 if (process.platform !== 'win32') {
247 // Build Universal windows apps available for windows platform only, so we reject on others platforms
248 return Q.reject('Cordova tooling for Windows requires Windows OS to build project');
249 }
250
251 return getWindowsVersion().then(function (actualVersion) {
252 var requiredOsVersion = getMinimalRequiredVersionFor('os', windowsTargetVersion, windowsPhoneTargetVersion);
253 if ((actualVersion.gte(requiredOsVersion)) ||
254 // Special case for Windows 10/Phone 10 targets which can be built on Windows 7 (version 6.1)
255 (actualVersion.major === 6 && actualVersion.minor === 1 && getConfig().getWindowsTargetVersion() === '10.0')) {
256 return mapWindowsVersionToName(actualVersion);
257 }
258
259 return Q.reject('Current Windows version doesn\'t support building this project. ' +
260 'Consider upgrading your OS to ' + mapWindowsVersionToName(requiredOsVersion));
261 });
262};
263
264/**
265 * Checks if MSBuild tools is available.
266 * @return {Promise} Promise either fullfilled with MSBuild version
267 * or rejected with error message.
268 */
269var checkMSBuild = function (windowsTargetVersion, windowsPhoneTargetVersion) {
270 return MSBuildTools.findAllAvailableVersions()
271 .then(function (msbuildToolsVersions) {
272 // console.log('msbuildToolsVersions', msbuildToolsVersions);
273 var msbuildRequiredVersion = getMinimalRequiredVersionFor('msbuild', windowsTargetVersion, windowsPhoneTargetVersion);
274 // console.log('msbuildRequiredVersion', msbuildRequiredVersion);
275 msbuildToolsVersions = msbuildToolsVersions.map(function (msbuildToolsVersion) {
276 return msbuildToolsVersion.version;
277 });
278 // console.log('msbuildToolsVersions', msbuildToolsVersions);
279
280 var appropriateVersion = getHighestAppropriateVersion(msbuildToolsVersions, msbuildRequiredVersion);
281 // console.log('appropriateVersion', appropriateVersion);
282 return appropriateVersion
283 ? shortenVersion(appropriateVersion)
284 : Q.reject('MSBuild tools v.' + shortenVersion(msbuildRequiredVersion) + ' not found. ' +
285 'Please install Visual Studio ' + mapVSVersionToName(getMinimalRequiredVersionFor('visualstudio', windowsTargetVersion, windowsPhoneTargetVersion)) +
286 ' or higher from https://www.visualstudio.com/downloads/download-visual-studio-vs');
287 });
288};
289
290var checkVS = function (windowsTargetVersion, windowsPhoneTargetVersion) {
291 var vsRequiredVersion = getMinimalRequiredVersionFor('visualstudio', windowsTargetVersion, windowsPhoneTargetVersion);
292
293 if (process.env.VSINSTALLDIR) {
294 return Q('(user-specified via VSINSTALLDIR)');
295 }
296 return getInstalledVSVersions()
297 .then(function (installedVersions) {
298 var appropriateVersion = getHighestAppropriateVersion(installedVersions, vsRequiredVersion);
299 return appropriateVersion
300 ? shortenVersion(appropriateVersion)
301 : Q.reject('Required version of Visual Studio not found. Please install Visual Studio ' +
302 mapVSVersionToName(vsRequiredVersion) +
303 ' or higher from https://www.visualstudio.com/downloads/download-visual-studio-vs');
304 });
305};
306
307var checkWinSdk = function (windowsTargetVersion, windowsPhoneTargetVersion) {
308 return getInstalledWindowsSdks()
309 .then(function (installedSdks) {
310 var requiredVersion = getMinimalRequiredVersionFor('windowssdk', windowsTargetVersion, windowsPhoneTargetVersion);
311 var hasSdkInstalled = installedSdks.some(function (installedSdk) {
312 return installedSdk.eq(requiredVersion);
313 });
314 if (!hasSdkInstalled) {
315 return Q.reject('Windows SDK not found. Ensure that you have installed ' +
316 'Windows ' + shortenVersion(requiredVersion) + ' SDK along with Visual Studio or install ' +
317 'Windows ' + shortenVersion(requiredVersion) + ' SDK separately from ' +
318 'https://dev.windows.com/en-us/downloads');
319 }
320
321 return shortenVersion(requiredVersion);
322 });
323};
324
325var checkPhoneSdk = function (windowsTargetVersion, windowsPhoneTargetVersion) {
326 var requiredVersion = getMinimalRequiredVersionFor('phonesdk', windowsTargetVersion, windowsPhoneTargetVersion);
327 return getInstalledPhoneSdks()
328 .then(function (installedSdks) {
329 var hasSdkInstalled = installedSdks.some(function (installedSdk) {
330 return installedSdk.eq(requiredVersion);
331 });
332
333 return hasSdkInstalled
334 ? shortenVersion(requiredVersion)
335 : Q.reject();
336 })
337 .fail(function () {
338 return Q.reject('Windows Phone SDK not found. Ensure that you have installed ' +
339 'Windows Phone ' + shortenVersion(requiredVersion) + ' SDK along with Visual Studio or install ' +
340 'Windows Phone ' + shortenVersion(requiredVersion) + ' SDK separately from ' +
341 'https://dev.windows.com/develop/download-phone-sdk');
342 });
343};
344
345module.exports.run = function () {
346 return checkOS('10.0', '10.0').then(function () {
347 return MSBuildTools.findAvailableVersion();
348 });
349};
350
351/** Checks if Windows SDK required to build the target_platform is present
352 * @param {String} target_platorm Target platform ('8.1' or '10.0')
353 */
354module.exports.isWinSDKPresent = function (target_platform) {
355 return checkWinSdk(target_platform, '8.1');
356};
357
358// Checks if min SDK required to build Windows Phone 8.1 project is present
359module.exports.isPhoneSDKPresent = function () {
360 return checkPhoneSdk('8.1', '8.1');
361};
362
363/**
364 * Object that represents one of requirements for current platform.
365 * @param {String} id The unique identifier for this requirements.
366 * @param {String} name The name of requirements. Human-readable field.
367 * @param {Boolean} isFatal Marks the requirement as fatal. If such requirement will fail
368 * next requirements' checks will be skipped.
369 */
370var Requirement = function (id, name, isFatal) {
371 this.id = id;
372 this.name = name;
373 this.installed = false;
374 this.metadata = {};
375 this.isFatal = isFatal || false;
376};
377
378var requirements = [
379 new Requirement('os', 'Windows OS', true),
380 new Requirement('msbuild', 'MSBuild Tools'),
381 new Requirement('visualstudio', 'Visual Studio'),
382 new Requirement('windowssdk', 'Windows SDK'),
383 new Requirement('phonesdk', 'Windows Phone SDK')
384];
385
386// Define list of checks needs to be performed
387var checkFns = [checkOS, checkMSBuild, checkVS, checkWinSdk, checkPhoneSdk];
388
389var config = null;
390function getConfig () {
391 try {
392 config = config || new ConfigParser(path.join(__dirname, '../../config.xml'));
393 return Q(config);
394 } catch (e) {
395 // try again to cover case of being called from command line
396 try {
397 config = config || new ConfigParser(path.join(__dirname, '../../template/config.xml'));
398 return Q(config);
399 } catch (e) {
400 // yeah, really no config.xml
401 return Q.reject(new CordovaError('Can\'t check requirements for Windows platform.' +
402 'The config.xml file is either missing or malformed.'));
403 }
404 }
405}
406
407/**
408 * Methods that runs all checks one by one and returns a result of checks
409 * as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method.
410 * @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
411 */
412module.exports.check_all = function () {
413 var result = [];
414 var fatalIsHit = false;
415
416 // Then execute requirement checks one-by-one
417 return checkFns.reduce(function (promise, checkFn, idx) {
418 return promise.then(function () {
419 // If fatal requirement is failed,
420 // we don't need to check others
421 if (fatalIsHit) return Q();
422 var requirement = requirements[idx];
423 return getConfig()
424 .then(function (config) {
425 return checkFn(config.getWindowsTargetVersion(), config.getWindowsPhoneTargetVersion())
426 .then(function (version) {
427 requirement.installed = true;
428 requirement.metadata.version = version;
429 result.push(requirement);
430 }).catch(function (err) {
431 if (requirement.isFatal) fatalIsHit = true;
432 requirement.metadata.reason = err;
433 result.push(requirement);
434 });
435 });
436 });
437 }, Q())
438 .then(function () {
439 // When chain is completed, return requirements array to upstream API
440 return result;
441 });
442};
443
444module.exports.help = function () {
445 console.log('Usage: check_reqs or node check_reqs');
446};