blob: b54ed050131e7f4e0f9e859cff9454b49b1d802e [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var Q = require('q');
var path = require('path');
var fs = require('fs');
var shell = require('shelljs');
var Version = require('./Version');
var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
function MSBuildTools (version, path) {
this.version = version;
this.path = path;
}
MSBuildTools.prototype.buildProject = function (projFile, buildType, buildarch, buildFlags) {
events.emit('log', 'Building project: ' + projFile);
events.emit('log', '\tConfiguration : ' + buildType);
events.emit('log', '\tPlatform : ' + buildarch);
var checkWinSDK = function (target_platform) {
return require('./check_reqs').isWinSDKPresent(target_platform);
};
var checkPhoneSDK = function () {
return require('./check_reqs').isPhoneSDKPresent();
};
var args = ['/clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal', '/nologo',
'/p:Configuration=' + buildType,
'/p:Platform=' + buildarch];
if (buildFlags) {
args = args.concat(buildFlags);
}
var that = this;
var promise;
// Check if SDK required to build the respective platform is present. If not present, return with corresponding error, else call msbuild.
if (projFile.indexOf('CordovaApp.Phone.jsproj') > -1) {
promise = checkPhoneSDK();
} else if (projFile.indexOf('CordovaApp.Windows.jsproj') > -1) {
promise = checkWinSDK('8.1');
} else {
promise = checkWinSDK('10.0');
}
return promise.then(function () {
return spawn(path.join(that.path, 'msbuild'), [projFile].concat(args), { stdio: 'inherit' });
});
};
// returns full path to msbuild tools required to build the project and tools version
module.exports.findAvailableVersion = function () {
var versions = ['15.0', '14.0', '12.0', '4.0'];
return Q.all(versions.map(checkMSBuildVersion)).then(function (versions) {
// select first msbuild version available, and resolve promise with it
var msbuildTools = versions[0] || versions[1] || versions[2] || versions[3];
return msbuildTools ? Q.resolve(msbuildTools) : Q.reject('MSBuild tools not found');
});
};
function findAllAvailableVersionsFallBack () {
var versions = ['15.0', '14.0', '12.0', '4.0'];
events.emit('verbose', 'Searching for available MSBuild versions...');
return Q.all(versions.map(checkMSBuildVersion)).then(function (unprocessedResults) {
return unprocessedResults.filter(function (item) {
return !!item;
});
});
}
module.exports.findAllAvailableVersions = function () {
// CB-11548 use VSINSTALLDIR environment if defined to find MSBuild. If VSINSTALLDIR
// is not specified or doesn't contain the MSBuild path we are looking for - fall back
// to default discovery mechanism.
if (process.env.VSINSTALLDIR) {
var msBuildPath = path.join(process.env.VSINSTALLDIR, 'MSBuild/15.0/Bin');
return module.exports.getMSBuildToolsAt(msBuildPath)
.then(function (msBuildTools) {
return [msBuildTools];
}).catch(findAllAvailableVersionsFallBack);
}
return findAllAvailableVersionsFallBack();
};
/**
* Gets MSBuildTools instance for user-specified location
*
* @param {String} location FS location where to search for MSBuild
* @returns Promise<MSBuildTools> The MSBuildTools instance at specified location
*/
module.exports.getMSBuildToolsAt = function (location) {
var msbuildExe = path.resolve(location, 'msbuild');
// TODO: can we account on these params availability and printed version format?
return spawn(msbuildExe, ['-version', '-nologo'])
.then(function (output) {
// MSBuild prints its' version as 14.0.25123.0, so we pick only first 2 segments
var version = output.match(/^(\d+\.\d+)/)[1];
return new MSBuildTools(version, location);
});
};
function checkMSBuildVersion (version) {
// first, check if we have a VS 2017+ with such a version
var correspondingWillow = module.exports.getWillowInstallations().filter(function (inst) {
return inst.version === version;
})[0];
if (correspondingWillow) {
var toolsPath = path.join(correspondingWillow.path, 'MSBuild', version, 'Bin');
if (shell.test('-e', toolsPath)) {
return module.exports.getMSBuildToolsAt(toolsPath);
}
}
return spawn('reg', ['query', 'HKLM\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\' + version, '/v', 'MSBuildToolsPath'])
.then(function (output) {
// fetch msbuild path from 'reg' output
var toolsPath = /MSBuildToolsPath\s+REG_SZ\s+(.*)/i.exec(output);
if (toolsPath) {
toolsPath = toolsPath[1];
// CB-9565: Windows 10 invokes .NET Native compiler, which only runs on x86 arch,
// so if we're running an x64 Node, make sure to use x86 tools.
if ((version === '15.0' || version === '14.0') && toolsPath.indexOf('amd64') > -1) {
toolsPath = path.resolve(toolsPath, '..');
}
events.emit('verbose', 'Found MSBuild v' + version + ' at ' + toolsPath);
return new MSBuildTools(version, toolsPath);
}
}).catch(function (err) { /* eslint handle-callback-err : 0 */
// if 'reg' exits with error, assume that registry key not found
});
}
/// returns an array of available UAP Versions
module.exports.getAvailableUAPVersions = function () {
var programFilesFolder = process.env['ProgramFiles(x86)'] || process.env['ProgramFiles'];
// No Program Files folder found, so we won't be able to find UAP SDK
if (!programFilesFolder) return [];
var uapFolderPath = path.join(programFilesFolder, 'Windows Kits', '10', 'Platforms', 'UAP');
if (!shell.test('-e', uapFolderPath)) {
return []; // No UAP SDK exists on this machine
}
var result = [];
shell.ls(uapFolderPath).filter(function (uapDir) {
return shell.test('-d', path.join(uapFolderPath, uapDir));
}).map(function (folder) {
return Version.tryParse(folder);
}).forEach(function (version, index) {
if (version) {
result.push(version);
}
});
return result;
};
/**
* Lists all VS 2017+ instances dirs in ProgramData
*
* @return {String[]} List of paths to all VS2017+ instances
*/
function getWillowProgDataPaths () {
if (!process.env.systemdrive) {
// running on linux/osx?
return [];
}
var instancesRoot = path.join(process.env.systemdrive, 'ProgramData/Microsoft/VisualStudio/Packages/_Instances');
if (!shell.test('-d', instancesRoot)) {
// can't seem to find VS instances dir, return empty result
return [];
}
return fs.readdirSync(instancesRoot).map(function (file) {
var instanceDir = path.join(instancesRoot, file);
if (shell.test('-d', instanceDir)) {
return instanceDir;
}
}).filter(function (progDataPath) {
// make sure state.json exists
return shell.test('-e', path.join(progDataPath, 'state.json'));
});
}
/**
* Lists all installed VS 2017+ versions
*
* @return {Object[]} List of all VS 2017+ versions
*/
module.exports.getWillowInstallations = function () {
var progDataPaths = getWillowProgDataPaths();
var installations = [];
progDataPaths.forEach(function (progDataPath) {
try {
var stateJsonPath = path.join(progDataPath, 'state.json');
var fileContents = fs.readFileSync(stateJsonPath, 'utf-8');
var state = JSON.parse(fileContents);
// get only major and minor version
var version = state.product.version.match(/^(\d+\.\d+)/)[1];
installations.push({ version: version, path: state.installationPath });
} catch (err) {
// something's wrong, skip this one
}
});
return installations;
};