diff --git a/.ado/VSComponentList.ps1 b/.ado/VSComponentList.ps1 index bd27aca3c19..842aae5825d 100644 --- a/.ado/VSComponentList.ps1 +++ b/.ado/VSComponentList.ps1 @@ -13,11 +13,12 @@ Invoke-WebRequest -Uri 'https://download.visualstudio.microsoft.com/download/pr/ $p = Start-Process -PassThru $dir\vs_enterprise.exe -RedirectStandardError $dir\err -RedirectStandardOutput $dir\out -ArgumentList "export --installpath `"$installationPath`" --quiet --config $vsconfig" $p.WaitForExit() $x = [Datetime]::Now.AddSeconds(60) -while (!(Test-Path $vsconfig) -and ([datetime]::Now -lt $x)) + +do { - Sleep 5 Write-Host "Waiting for vsconfig file..." -} + Sleep 5 +} while (!(Test-Path $vsconfig) -and ([datetime]::Now -lt $x)) Get-Content $dir\err Get-Content $dir\out diff --git a/.ado/publish.yml b/.ado/publish.yml index 7647c4281f6..10bb4aaf930 100644 --- a/.ado/publish.yml +++ b/.ado/publish.yml @@ -149,7 +149,6 @@ jobs: - template: templates/build-rnw.yml parameters: project: vnext/ReactWindows-Universal.sln - vsComponents: $(VsComponents) - template: templates/publish-build-artifacts-for-nuget.yml parameters: diff --git a/.ado/templates/build-rnw.yml b/.ado/templates/build-rnw.yml index 2a0260822fd..52e468f533a 100644 --- a/.ado/templates/build-rnw.yml +++ b/.ado/templates/build-rnw.yml @@ -13,14 +13,16 @@ parameters: yarnBuildCmd: build # Visual Studio Installer - vsInstallerUri: $(VsInstallerUri) vsComponents: '' + listVsComponents: false + installVsComponents: false steps: - template: prepare-env.yml parameters: - vsInstallerUri: ${{ parameters.vsInstallerUri }} vsComponents: ${{ parameters.vsComponents }} + listVsComponents: ${{ parameters.listVsComponents }} + installVsComponents: ${{ parameters.installVsComponents }} yarnBuildCmd: ${{ parameters.yarnBuildCmd }} debug: ${{ parameters.debug }} diff --git a/.ado/templates/e2e-test-job.yml b/.ado/templates/e2e-test-job.yml index a222ac53ee0..3f12627159d 100644 --- a/.ado/templates/e2e-test-job.yml +++ b/.ado/templates/e2e-test-job.yml @@ -43,9 +43,6 @@ jobs: Get-WmiObject Win32_LogicalDisk - template: prepare-env.yml - parameters: - vsComponents: $(VsComponents) - yarnBuildCmd: build - task: NuGetCommand@2 displayName: NuGet restore - ReactUWPTestApp diff --git a/.ado/templates/prepare-env.yml b/.ado/templates/prepare-env.yml index b425c1670a6..fcc37dac7a7 100644 --- a/.ado/templates/prepare-env.yml +++ b/.ado/templates/prepare-env.yml @@ -5,8 +5,9 @@ parameters: debug: false # Visual Studio Installer - vsInstallerUri: $(VsInstallerUri) vsComponents: '' + listVsComponents: false + installVsComponents: false steps: - task: PowerShell@2 @@ -45,16 +46,23 @@ steps: parameters: sdkVersion: $(Win10Version) + - task: PowerShell@2 + displayName: List Visual Studio Components + inputs: + targetType: filePath + filePath: $(Build.SourcesDirectory)/.ado/VSComponentList.ps1 + condition: and(succeeded(), ${{ parameters.listVsComponents }}) + - task: PowerShell@2 displayName: Install Visual Studio dependencies inputs: targetType: filePath filePath: $(Build.SourcesDirectory)/vnext/Scripts/Tfs/Install-VsFeatures.ps1 arguments: - -InstallerUri ${{ parameters.vsInstallerUri }} -Components ${{ parameters.vsComponents }} -Collect:$${{ parameters.debug }} - condition: and(ne('${{ parameters.vsComponents }}', ''), eq(variables['VmImage'], 'windows-2019')) # Note that this is evaluated at parsing, so parameters.vsComponents is never empty + -Cleanup:$true + condition: and(succeeded(), ${{ parameters.installVsComponents }}) - task: PowerShell@2 displayName: List disksize after prepare-env diff --git a/.ado/templates/react-native-init.yml b/.ado/templates/react-native-init.yml index 8778fb8377c..2687f1a0d6d 100644 --- a/.ado/templates/react-native-init.yml +++ b/.ado/templates/react-native-init.yml @@ -6,7 +6,8 @@ parameters: configuration: experimentalNugetDependency: false vsComponents: '' - vsInstallerUri: $(VsInstallerUri) + listVsComponents: false + installVsComponents: false steps: - checkout: self # self represents the repo where the initial Pipelines YAML file was found @@ -96,15 +97,22 @@ steps: parameters: sdkVersion: $(Win10Version) + - task: PowerShell@2 + displayName: List Visual Studio Components + inputs: + targetType: filePath + filePath: $(Build.SourcesDirectory)/.ado/VSComponentList.ps1 + condition: and(succeeded(), ${{ parameters.listVsComponents }}) + - task: PowerShell@2 displayName: Install Visual Studio dependencies inputs: targetType: filePath filePath: $(Build.SourcesDirectory)/vnext/Scripts/Tfs/Install-VsFeatures.ps1 arguments: - -InstallerUri ${{ parameters.vsInstallerUri }} -Components ${{ parameters.vsComponents }} - condition: and(ne('${{ parameters.vsComponents }}', ''), eq(variables['VmImage'], 'windows-2019')) # Note that this is evaluated at parsing, so parameters.vsComponents is never empty + -Cleanup:$true + condition: and(succeeded(), ${{ parameters.installVsComponents }}) - task: VSBuild@1 displayName: VSBuild - testcli diff --git a/.ado/variables/msbuild.yml b/.ado/variables/msbuild.yml index e3294856312..ff72852ca88 100644 --- a/.ado/variables/msbuild.yml +++ b/.ado/variables/msbuild.yml @@ -3,5 +3,4 @@ variables: MSBuildPreferredToolArchitecture: x64 MSBuildPlatformToolset: v142 TargetPlatformVersion: 10.0.18362.0 - Win10Version: 18362 - VsInstallerUri: 'https://download.visualstudio.microsoft.com/download/pr/c4fef23e-cc45-4836-9544-70e213134bc8/1ee5717e9a1e05015756dff77eb27d554a79a6db91f2716d836df368381af9a1/vs_Enterprise.exe' \ No newline at end of file + Win10Version: 18362 \ No newline at end of file diff --git a/.ado/variables/vs2019.yml b/.ado/variables/vs2019.yml index 858fdcf0204..242f8ec272d 100644 --- a/.ado/variables/vs2019.yml +++ b/.ado/variables/vs2019.yml @@ -1,6 +1,5 @@ variables: VmImage: windows-2019 - VsComponents: 'Microsoft.Component.MSBuild,Microsoft.VisualStudio.ComponentGroup.UWP.VC,Microsoft.VisualStudio.Component.VC.Tools.x86.x64,Microsoft.VisualStudio.Component.VC.Tools.ARM,Microsoft.VisualStudio.Component.VC.Tools.ARM64' MSBuildVersion: 16.0 GoogleTestAdapterPath: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\ked32bft.vu0' BaseIntDir: $(Agent.HomeDirectory)\BaseIntDir # redirect to C: diff --git a/.ado/windows-vs-pr.yml b/.ado/windows-vs-pr.yml index 2a7660a11f0..5c73df39094 100644 --- a/.ado/windows-vs-pr.yml +++ b/.ado/windows-vs-pr.yml @@ -73,7 +73,6 @@ jobs: parameters: yarnBuildCmd: build project: vnext/ReactWindows-Universal.sln - vsComponents: $(VsComponents) - task: VSTest@2 displayName: Run Universal Unit Tests @@ -149,9 +148,6 @@ jobs: submodules: false - template: templates/prepare-env.yml - parameters: - vsComponents: $(VsComponents) - yarnBuildCmd: build - task: NuGetCommand@2 displayName: NuGet restore - Playground @@ -252,9 +248,6 @@ jobs: submodules: false - template: templates/prepare-env.yml - parameters: - vsComponents: $(VsComponents) - yarnBuildCmd: build - task: NuGetCommand@2 displayName: NuGet restore - SampleApps @@ -508,8 +501,6 @@ jobs: configuration: $(configuration) platform: $(platform) version: $(reactNativeVersion) - vsComponents: $(VsComponents) - - job: CliInitExperimental displayName: Verify react-native init experimental @@ -528,7 +519,6 @@ jobs: configuration: Release platform: x64 version: $(reactNativeVersion) - vsComponents: $(VsComponents) experimentalNugetDependency: true - job: RNWExtraChecks diff --git a/change/react-native-windows-2020-04-07-12-18-49-reduceinstall.json b/change/react-native-windows-2020-04-07-12-18-49-reduceinstall.json new file mode 100644 index 00000000000..1bdbfdf483e --- /dev/null +++ b/change/react-native-windows-2020-04-07-12-18-49-reduceinstall.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Optimizing VS component installer to speed up CI builds", + "packageName": "react-native-windows", + "email": "jthysell@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-04-07T19:18:49.438Z" +} \ No newline at end of file diff --git a/vnext/Scripts/Tfs/Install-VSFeatures.ps1 b/vnext/Scripts/Tfs/Install-VSFeatures.ps1 index 790d32c7838..6a836bc1cc5 100644 --- a/vnext/Scripts/Tfs/Install-VSFeatures.ps1 +++ b/vnext/Scripts/Tfs/Install-VSFeatures.ps1 @@ -2,53 +2,92 @@ param ( [Parameter(Mandatory=$true)] [string[]] $Components, - [Parameter(Mandatory=$true)] - [uri] $InstallerUri, + [uri] $InstallerUri = "https://download.visualstudio.microsoft.com/download/pr/c4fef23e-cc45-4836-9544-70e213134bc8/1ee5717e9a1e05015756dff77eb27d554a79a6db91f2716d836df368381af9a1/vs_Enterprise.exe", [string] $VsInstaller = "${env:System_DefaultWorkingDirectory}\vs_Enterprise.exe", + [string] $VsInstallOutputDir = "${env:System_DefaultWorkingDirectory}\vs", + [System.IO.FileInfo] $VsInstallPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise", - [switch] $Collect -) + [System.IO.FileInfo] $VsInstallerPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer", + + [switch] $Collect = $false, -Invoke-WebRequest -Method Get ` - -Uri $InstallerUri ` - -OutFile $VsInstaller + [switch] $Cleanup = $false, + + [switch] $UseWebInstaller = $false +) $Components | ForEach-Object { $componentList += '--add', $_ } -$VsInstallOutputDir = "${env:System_DefaultWorkingDirectory}\vs" - -New-Item -ItemType directory -Path $VsInstallOutputDir - -Start-Process ` - -FilePath "$VsInstaller" ` - -ArgumentList ( ` - '--layout', "$VsInstallOutputDir", - '--wait', - '--norestart', - '--quiet' + ` - $componentList - ) ` - -Wait ` - -PassThru - -Start-Process ` - -FilePath "$VsInstallOutputDir\vs_Enterprise.exe" ` - -ArgumentList ( - 'modify', - '--installPath', "`"$VsInstallPath`"" , - '--wait', - '--quiet', - '--norestart' + ` - $componentList - ) ` - -Wait ` - -PassThru ` - -OutVariable returnCode +$LocalVsInstaller = "$VsInstallerPath\vs_installershell.exe" + +$UseWebInstaller = $UseWebInstaller -or -not (Test-Path -Path "$LocalVsInstaller") + +if ($UseWebInstaller) { + Write-Host "Downloading web installer..." + + Invoke-WebRequest -Method Get ` + -Uri $InstallerUri ` + -OutFile $VsInstaller + + New-Item -ItemType directory -Path $VsInstallOutputDir + + Write-Host "Running web installer to download requested components..." + + Start-Process ` + -FilePath "$VsInstaller" ` + -ArgumentList ( ` + '--layout', "$VsInstallOutputDir", + '--wait', + '--norestart', + '--quiet' + ` + $componentList + ) ` + -Wait ` + -PassThru + + Write-Host "Running downloaded VS installer to add requested components..." + + Start-Process ` + -FilePath "$VsInstallOutputDir\vs_Enterprise.exe" ` + -ArgumentList ( + 'modify', + '--installPath', "`"$VsInstallPath`"" , + '--wait', + '--norestart', + '--quiet' + ` + $componentList + ) ` + -Wait ` + -PassThru ` + -OutVariable returnCode + + if ($Cleanup) { + Write-Host "Cleaning up..." + + Remove-Item -Path $VsInstaller + Remove-Item -Path $VsInstallOutputDir -Recurse + } + +} else { + Write-Host "Running local installer to add requested components..." + + Start-Process ` + -FilePath "$LocalVsInstaller" ` + -ArgumentList ( + 'modify', + '--installPath', "`"$VsInstallPath`"" , + '--norestart', + '--quiet' + ` + $componentList + ) ` + -Wait ` + -OutVariable returnCode +} if ($Collect) { Invoke-WebRequest -Method Get ` diff --git a/vnext/local-cli/runWindows/utils/msbuildtools.js b/vnext/local-cli/runWindows/utils/msbuildtools.js index b4db1c6f1ef..00e8d616b5e 100644 --- a/vnext/local-cli/runWindows/utils/msbuildtools.js +++ b/vnext/local-cli/runWindows/utils/msbuildtools.js @@ -90,24 +90,34 @@ class MSBuildTools { } } -function VSWhere(requires, version, property) { +function VSWhere(requires, version, property, verbose) { // This path is maintained and VS has promised to keep it valid. const vsWherePath = path.join( process.env['ProgramFiles(x86)'] || process.env.ProgramFiles, '/Microsoft Visual Studio/Installer/vswhere.exe', ); + if (verbose) { + console.log('Looking for vswhere at: ' + vsWherePath); + } + // Check if vswhere is present and try to find MSBuild. if (fs.existsSync(vsWherePath)) { - const vsPath = child_process + if (verbose) { + console.log('Found vswhere.'); + } + const propertyValue = child_process .execSync( `"${vsWherePath}" -version [${version},${Number(version) + 1}) -products * -requires ${requires} -property ${property}`, ) .toString() .split(EOL)[0]; - return vsPath; + return propertyValue; } else { + if (verbose) { + console.log("Couldn't find vswhere, querying registry."); + } const query = `reg query HKLM\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\${version} /s /v MSBuildToolsPath`; let toolsPath = null; // Try to get the MSBuild path using registry @@ -116,6 +126,9 @@ function VSWhere(requires, version, property) { let toolsPathOutput = /MSBuildToolsPath\s+REG_SZ\s+(.*)/i.exec(output); if (toolsPathOutput) { let toolsPathOutputStr = toolsPathOutput[1]; + if (verbose) { + console.log('Query found: ' + toolsPathOutputStr); + } // Win10 on .NET Native uses x86 arch compiler, if using x64 Node, use x86 tools if (version === '16.0') { toolsPathOutputStr = path.resolve(toolsPathOutputStr, '..'); @@ -148,18 +161,30 @@ function checkMSBuildVersion(version, buildArch, verbose) { } // https://aka.ms/vs/workloads - const requires = [ - 'Microsoft.Component.MSBuild', - getVCToolsByArch(buildArch), - 'Microsoft.VisualStudio.ComponentGroup.UWP.VC', - ]; + const requires = ['Microsoft.Component.MSBuild', getVCToolsByArch(buildArch)]; + + const vsPath = VSWhere( + requires.join(' '), + version, + 'installationPath', + verbose, + ); + + if (verbose) { + console.log('VS path: ' + vsPath); + } - const vsPath = VSWhere(requires.join(' '), version, 'installationPath'); const installationVersion = VSWhere( requires.join(' '), version, 'installationVersion', + verbose, ); + + if (verbose) { + console.log('VS version: ' + installationVersion); + } + // VS 2019 changed path naming convention const vsVersion = version === '16.0' ? 'Current' : version; @@ -171,6 +196,10 @@ function checkMSBuildVersion(version, buildArch, verbose) { 'Bin/MSBuild.exe', ); + if (verbose) { + console.log('Looking for MSBuilt at: ' + msBuildPath); + } + toolsPath = fs.existsSync(msBuildPath) ? path.dirname(msBuildPath) : null; // We found something so return MSBuild Tools.