UNPKG

gulp-msbuild

Version:

msbuild plugin for gulp. Inspired by grunt-msbuild.

169 lines (136 loc) 6.21 kB
import { execSync } from 'child_process'; import * as path from 'path'; import * as fs from 'fs'; import { MSBUILD_VERSIONS } from './constants'; import { MSBuildOptions } from './msbuild-options'; import { ArchitectureService } from '../utilities/ArchitectureService'; export class MSBuildFinder { private buildIs64Bit: boolean; private version: string; private windir: string; public constructor(options: MSBuildOptions) { this.buildIs64Bit = ArchitectureService.Architecture === 'x64'; this.version = options.toolsVersion ?? 'auto' this.windir = options.windir ?? 'C:\Windows'; } public findVersion(): (string | null)[] | null { if (this.version == 'auto') return this.getLatestAvailableVersion(); const parsedNumber = parseFloat(this.version); if (isNaN(parsedNumber)) throw new Error('version unknown'); // Pre-v12: MSBuild ships with the .NET Framework and does not require vswhere if (parsedNumber < 12) { const frameworkPath = this.concatPreV12(this.version); if (fs.existsSync(frameworkPath)) { return [frameworkPath, this.version]; } return null; } const vswherePath = path.join(process.env['ProgramFiles(x86)'] || '', 'Microsoft Visual Studio', 'Installer', 'vswhere.exe'); if (!fs.existsSync(vswherePath)) { console.error('vswhere.exe not found'); return null; } try { const installationsBuffer = execSync(`"${vswherePath}" -products * -requires Microsoft.Component.MSBuild -format json`); const installations = JSON.parse(installationsBuffer.toString()); const major = (Number)(this.version.split('.')[0]); if (major >= 16) { // For v16+, match by the major part of installationVersion rather than // relying on the installation path containing a particular substring. // This correctly handles VS 18 (and any future VS version) regardless // of the year/name used in the installation directory. const matchingInstallation = installations.find((installation: any) => { const installMajor = parseInt( (installation.installationVersion || '').split('.')[0] || '0' ); return installMajor === major; }); if (!matchingInstallation) { return [null, this.version]; } const msbuildPath = path.join( matchingInstallation.installationPath, 'MSBuild', 'Current', 'Bin', this.getx64_dir(), 'MSBuild.exe' ); return [fs.existsSync(msbuildPath) ? msbuildPath : null, this.version]; } // v12–v15: use path-based detection via vswhere installation paths const installationPaths = installations.map( (installation: any) => this.concatCorrectPath(installation.installationPath) ) as string[]; const installedPath = this.getInstalledVersion(installationPaths); return [ installedPath, this.version ] } catch (error: any) { console.error(`Error executing vswhere: ${error.message}`); return null; } } concatCorrectPath(installationPath: string): string { const major = (Number)(this.version.split('.')[0]); if (major >= 16) { return this.concatV16AndAbove(installationPath, this.version); } else if (major >= 12 && major <= 15) { return this.concatV15AndBelow(installationPath, this.version); } return this.concatPreV12(this.version); } concatPreV12(version: string): string { let toolVersion = MSBUILD_VERSIONS[version]; const framework = this.buildIs64Bit ? 'Framework64' : 'Framework'; return path.join(this.windir, 'Microsoft.Net', framework, toolVersion, 'MSBuild.exe'); } concatV15AndBelow(installationPath: string, version: string): string { return path.join(installationPath, 'MSBuild', version, 'Bin', this.getx64_dir(), 'MSBuild.exe'); } concatV16AndAbove(installationPath: string, version: string): string { if (!installationPath.includes(MSBUILD_VERSIONS[version])) return ''; return path.join(installationPath, 'MSBuild', 'Current', 'Bin', this.getx64_dir(), 'MSBuild.exe'); } getInstalledVersion(installationPaths: string[]): string | null { let installedPath: string | null = null; for (let i = 0; i < installationPaths.length; i++) { const installationPath = installationPaths[i]; if (installationPath == '') continue; if (fs.existsSync(installationPath)) { installedPath = installationPath; break; } } return installedPath; } getLatestAvailableVersion(): (string | null)[] | null { let installationPaths: string[] = []; let versions: string[] = []; for(const key in MSBUILD_VERSIONS) { this.version = key; const installationPath = this.findVersion(); if (installationPath != null) { if (installationPath[0] != null) { installationPaths.push(installationPath[0]); versions.push(key) } } } if (installationPaths.length === 0) return null; return [ installationPaths[installationPaths.length -1], versions[versions.length -1] ]; } getx64_dir() { return this.buildIs64Bit ? 'amd64' : ''; } }