mirror of https://github.com/jkjoy/sunpeiwen.git
346 lines
9.1 KiB
JavaScript
346 lines
9.1 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
const { execFileSync } = require('child_process');
|
||
|
const { readFileSync, statSync } = require('fs');
|
||
|
const { win32: path } = require('path');
|
||
|
|
||
|
const VS_VERSIONS_MODERN = new Map([
|
||
|
[15, {
|
||
|
year: 2017,
|
||
|
msbuild: path.join('MSBuild', '15.0', 'Bin', 'MSBuild.exe'),
|
||
|
toolset: 'v141',
|
||
|
}],
|
||
|
[16, {
|
||
|
year: 2019,
|
||
|
msbuild: path.join('MSBuild', 'Current', 'Bin', 'MSBuild.exe'),
|
||
|
toolset: 'v142',
|
||
|
}],
|
||
|
[17, {
|
||
|
year: 2022,
|
||
|
msbuild:
|
||
|
(process.arch === 'x64'
|
||
|
? path.join('MSBuild', 'Current', 'Bin', 'amd64', 'MSBuild.exe')
|
||
|
: path.join('MSBuild', 'Current', 'Bin', 'MSBuild.exe')),
|
||
|
toolset: 'v143',
|
||
|
}],
|
||
|
]);
|
||
|
const PACKAGES = {
|
||
|
msbuild: /^Microsoft[.]VisualStudio[.]VC[.]MSBuild[.](?:v\d+[.])?Base$/i,
|
||
|
vctools: /^Microsoft[.]VisualStudio[.]Component[.]VC[.]Tools[.]x86[.]x64$/i,
|
||
|
express: /^Microsoft[.]VisualStudio[.]WDExpress$/i,
|
||
|
winsdk:
|
||
|
/^Microsoft[.]VisualStudio[.]Component[.]Windows(81|10|11)SDK(?:[.](\d+)(?:[.]Desktop.*)?)?$/,
|
||
|
};
|
||
|
const SDK_REG = 'HKLM\\Software\\Microsoft\\Microsoft SDKs\\Windows';
|
||
|
const SDK32_REG =
|
||
|
'HKLM\\Software\\Wow6432Node\\Microsoft\\Microsoft SDKs\\Windows';
|
||
|
|
||
|
function checkRequiredPackages(packages) {
|
||
|
if (!Array.isArray(packages))
|
||
|
return false;
|
||
|
|
||
|
let foundMSBuild = false;
|
||
|
let foundVCTools = false;
|
||
|
let foundExpress = false;
|
||
|
for (const pkg of packages) {
|
||
|
if (!foundMSBuild && PACKAGES.msbuild.test(pkg))
|
||
|
foundMSBuild = true;
|
||
|
else if (!foundVCTools && PACKAGES.vctools.test(pkg))
|
||
|
foundVCTools = true;
|
||
|
else if (!foundExpress && PACKAGES.express.test(pkg))
|
||
|
foundExpress = true;
|
||
|
|
||
|
if (foundMSBuild && (foundVCTools || foundExpress))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sorts newest to oldest
|
||
|
function versionStringCompare(a, b) {
|
||
|
const splitA = a.split('.');
|
||
|
const splitB = b.split('.');
|
||
|
const len = Math.min(splitA.length, splitB.length);
|
||
|
for (let i = 0; i < len; ++i) {
|
||
|
const nA = parseInt(splitA[i], 10);
|
||
|
const nB = parseInt(splitB[i], 10);
|
||
|
if (nA > nB)
|
||
|
return -1;
|
||
|
if (nA < nB)
|
||
|
return 1;
|
||
|
}
|
||
|
if (splitA.length > splitB.length)
|
||
|
return -1;
|
||
|
else if (splitA.length < splitB.length)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
function sdkVersionCompare(a, b) {
|
||
|
return versionStringCompare(a.fullVersion, b.fullVersion);
|
||
|
}
|
||
|
|
||
|
function getSDKPaths(fullVer) {
|
||
|
if (typeof fullVer !== 'string' || !fullVer)
|
||
|
return;
|
||
|
try {
|
||
|
const arch = (process.arch === 'ia32' ? 'x86' : 'x64');
|
||
|
const shortVer = `v${/^\d+[.]\d+/.exec(fullVer)[0]}`;
|
||
|
let verPath = getRegValue(`${SDK_REG}\\${shortVer}`, 'InstallationFolder');
|
||
|
if (!verPath)
|
||
|
verPath = getRegValue(`${SDK32_REG}\\${shortVer}`, 'InstallationFolder');
|
||
|
if (!verPath)
|
||
|
return;
|
||
|
|
||
|
// Includes
|
||
|
const includePaths = [];
|
||
|
for (const type of ['shared', 'um', 'ucrt']) {
|
||
|
const testPath = path.resolve(verPath, 'Include', fullVer, type);
|
||
|
statSync(testPath);
|
||
|
includePaths.push(testPath);
|
||
|
}
|
||
|
|
||
|
// Libraries
|
||
|
const libPaths = [];
|
||
|
for (const type of ['um', 'ucrt']) {
|
||
|
const testPath = path.resolve(verPath, 'Lib', fullVer, type, arch);
|
||
|
statSync(testPath);
|
||
|
libPaths.push(testPath);
|
||
|
}
|
||
|
|
||
|
return { includePaths, libPaths };
|
||
|
} catch {}
|
||
|
}
|
||
|
|
||
|
const execOpts = {
|
||
|
encoding: 'utf8',
|
||
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||
|
windowsHide: true,
|
||
|
};
|
||
|
|
||
|
function findModernVS() {
|
||
|
const versions = [];
|
||
|
const ps = path.join(
|
||
|
process.env.SystemRoot,
|
||
|
'System32',
|
||
|
'WindowsPowerShell',
|
||
|
'v1.0',
|
||
|
'powershell.exe'
|
||
|
);
|
||
|
const cs = path.resolve(__dirname, '..', 'deps', 'Find-VisualStudio.cs');
|
||
|
const args = [
|
||
|
'-ExecutionPolicy',
|
||
|
'Unrestricted',
|
||
|
'-NoProfile',
|
||
|
'-Command',
|
||
|
`&{Add-Type -Path '${cs}';[VisualStudioConfiguration.Main]::PrintJson()}`
|
||
|
];
|
||
|
try {
|
||
|
const out = execFileSync(ps, args, execOpts);
|
||
|
const info = JSON.parse(out);
|
||
|
if (Array.isArray(info)) {
|
||
|
for (const vs of info) {
|
||
|
const vsPath = path.resolve(vs.path);
|
||
|
let vsVer = /^(?<major>\d+)[.](?<minor>\d+)[.]/.exec(vs.version);
|
||
|
if (!vsVer)
|
||
|
continue;
|
||
|
vsVer = {
|
||
|
full: vs.version,
|
||
|
major: +vsVer.groups.major,
|
||
|
minor: +vsVer.groups.minor,
|
||
|
};
|
||
|
const verInfo = VS_VERSIONS_MODERN.get(vsVer.major);
|
||
|
if (verInfo === undefined)
|
||
|
continue;
|
||
|
if (!checkRequiredPackages(vs.packages))
|
||
|
continue;
|
||
|
const vsSDKs = [];
|
||
|
for (const pkg of vs.packages) {
|
||
|
let fullVersion;
|
||
|
let version;
|
||
|
const m = PACKAGES.winsdk.exec(pkg);
|
||
|
if (!m)
|
||
|
continue;
|
||
|
const sdk = m[1];
|
||
|
switch (sdk) {
|
||
|
case '81':
|
||
|
fullVersion = version = '8.1';
|
||
|
break;
|
||
|
case '10':
|
||
|
case '11': {
|
||
|
if (m[2] === undefined)
|
||
|
continue;
|
||
|
const sdkVer = parseInt(m[2], 10);
|
||
|
if (!isFinite(sdkVer) || sdkVer < 0)
|
||
|
continue;
|
||
|
fullVersion = `10.0.${sdkVer}.0`;
|
||
|
version = '10.0';
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
const paths = getSDKPaths(fullVersion);
|
||
|
if (!paths)
|
||
|
continue;
|
||
|
vsSDKs.push({
|
||
|
version,
|
||
|
fullVersion,
|
||
|
...paths,
|
||
|
});
|
||
|
}
|
||
|
if (vsSDKs.length === 0)
|
||
|
continue;
|
||
|
let clPath;
|
||
|
const includePaths = [];
|
||
|
const libPaths = [];
|
||
|
try {
|
||
|
let vcVerFile;
|
||
|
let clVer;
|
||
|
try {
|
||
|
vcVerFile = path.join(
|
||
|
vsPath,
|
||
|
'VC',
|
||
|
'Auxiliary',
|
||
|
'Build',
|
||
|
`Microsoft.VCToolsVersion.${verInfo.toolset}.default.txt`
|
||
|
);
|
||
|
clVer = readFileSync(vcVerFile, { encoding: 'utf8' }).trim();
|
||
|
} catch {}
|
||
|
if (!clVer) {
|
||
|
vcVerFile = path.join(
|
||
|
vsPath,
|
||
|
'VC',
|
||
|
'Auxiliary',
|
||
|
'Build',
|
||
|
'Microsoft.VCToolsVersion.default.txt'
|
||
|
);
|
||
|
clVer = readFileSync(vcVerFile, { encoding: 'utf8' }).trim();
|
||
|
}
|
||
|
const arch = (process.arch === 'ia32' ? 'x86' : 'x64');
|
||
|
let testPath = path.join(
|
||
|
vsPath,
|
||
|
'VC',
|
||
|
'Tools',
|
||
|
'MSVC',
|
||
|
clVer,
|
||
|
'bin',
|
||
|
`Host${arch}`,
|
||
|
arch,
|
||
|
'cl.exe'
|
||
|
);
|
||
|
statSync(testPath);
|
||
|
clPath = testPath;
|
||
|
|
||
|
testPath = path.join(
|
||
|
vsPath,
|
||
|
'VC',
|
||
|
'Tools',
|
||
|
'MSVC',
|
||
|
clVer,
|
||
|
'include'
|
||
|
);
|
||
|
statSync(testPath);
|
||
|
includePaths.push(testPath);
|
||
|
|
||
|
testPath = path.join(
|
||
|
vsPath,
|
||
|
'VC',
|
||
|
'Tools',
|
||
|
'MSVC',
|
||
|
clVer,
|
||
|
'lib',
|
||
|
arch
|
||
|
);
|
||
|
statSync(testPath);
|
||
|
libPaths.push(testPath);
|
||
|
} catch {
|
||
|
continue;
|
||
|
}
|
||
|
vsSDKs.sort(sdkVersionCompare);
|
||
|
versions.push({
|
||
|
path: vsPath,
|
||
|
version: vsVer,
|
||
|
sdks: vsSDKs,
|
||
|
...verInfo,
|
||
|
msbuild: path.join(vsPath, verInfo.msbuild),
|
||
|
cl: clPath,
|
||
|
includePaths,
|
||
|
libPaths,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
} catch {}
|
||
|
return versions;
|
||
|
}
|
||
|
|
||
|
const VS_VERSIONS_OLDER = [
|
||
|
{
|
||
|
version: { full: '12.0', major: 12, minor: 0 },
|
||
|
year: 2013,
|
||
|
toolset: 'v120',
|
||
|
},
|
||
|
{
|
||
|
version: { full: '14.0', major: 14, minor: 0 },
|
||
|
year: 2015,
|
||
|
toolset: 'v140',
|
||
|
},
|
||
|
];
|
||
|
|
||
|
const VC_REG = 'HKLM\\Software\\Microsoft\\VisualStudio\\SxS\\VC7';
|
||
|
const VC32_REG =
|
||
|
'HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7';
|
||
|
const MSBUILD_REG = 'HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions';
|
||
|
|
||
|
function getRegValue(key, value, use32) {
|
||
|
const extraArgs = (use32 ? [ '/reg:32' ] : []);
|
||
|
const regexp = new RegExp(`^\\s+${value}\\s+REG_\\w+\\s+(\\S.*)$`, 'im');
|
||
|
const reg = path.join(process.env.SystemRoot, 'System32', 'reg.exe');
|
||
|
const args = [ 'query', key, '/v', value, ...extraArgs ];
|
||
|
|
||
|
try {
|
||
|
const out = execFileSync(reg, args, execOpts);
|
||
|
const m = regexp.exec(out);
|
||
|
if (m)
|
||
|
return m[1];
|
||
|
} catch {}
|
||
|
}
|
||
|
|
||
|
function findOlderVS() {
|
||
|
const versions = [];
|
||
|
try {
|
||
|
for (const vs of VS_VERSIONS_OLDER) {
|
||
|
let vsPath = getRegValue(VC_REG, vs.version.full);
|
||
|
if (!vsPath)
|
||
|
vsPath = getRegValue(VC32_REG, vs.version.full);
|
||
|
if (!vsPath)
|
||
|
continue;
|
||
|
vsPath = path.resolve(vsPath, '..');
|
||
|
|
||
|
const msbuildPath = getRegValue(
|
||
|
`${MSBUILD_REG}\\${vs.version.full}`,
|
||
|
'MSBuildToolsPath',
|
||
|
(process.arch === 'ia32')
|
||
|
);
|
||
|
if (!msbuildPath)
|
||
|
continue;
|
||
|
versions.push({
|
||
|
path: vsPath,
|
||
|
...vs,
|
||
|
msbuild: path.join(msbuildPath, 'MSBuild.exe'),
|
||
|
cl: path.join(vsPath, 'VC', 'bin', 'cl.exe'),
|
||
|
includePaths: [path.join(vsPath, 'VC', 'include')],
|
||
|
libPaths: [path.join(vsPath, 'VC', 'lib')],
|
||
|
sdks: [],
|
||
|
});
|
||
|
}
|
||
|
} catch {}
|
||
|
return versions;
|
||
|
}
|
||
|
|
||
|
module.exports = () => {
|
||
|
const versions = findModernVS().concat(findOlderVS());
|
||
|
// Sorts newest to oldest
|
||
|
versions.sort((a, b) => {
|
||
|
return versionStringCompare(a.version.full, b.version.full);
|
||
|
});
|
||
|
return versions;
|
||
|
};
|