Skip to content

Commit 9858956

Browse files
committed
Add support to cache VS components
1 parent 7d4e338 commit 9858956

10 files changed

Lines changed: 295 additions & 4 deletions

extension/BuildPhpExtension/BuildPhpExtension.psd1

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,24 @@
9595
'Get-PhpSdk',
9696
'Get-PhpSrc',
9797
'Get-TempFiles',
98-
'Get-VsVersionHelper',
98+
'Get-VsCacheInfo',
99+
'Get-VsInstallPath',
99100
'Get-VsVersion',
101+
'Get-VsVersionHelper',
100102
'Invoke-Build',
101103
'Invoke-CleanupTempFiles',
104+
'Invoke-RestoreVsToolsetCache',
105+
'Invoke-SaveVsToolsetCache',
102106
'Invoke-Tests',
107+
'Restore-VsToolsetFromCache',
103108
'Set-AmqpTestEnvironment',
104109
'Set-GAGroup',
105110
'Set-ImagickTestEnvironment',
106111
'Set-NetSecurityProtocolType',
107112
'Set-Oci819TestEnvironment',
108113
'Set-PdoOciTestEnvironment',
109114
'Set-XdebugTestEnvironment',
115+
'Sync-VsToolsetCache',
110116

111117
# Public functions
112118
'Invoke-PhpBuildExtension'

extension/BuildPhpExtension/private/Add-VS.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function Add-Vs {
3535
Get-File -Url $vsWhereUrl -OutFile $vswherePath
3636
}
3737

38-
$instances = & $vswherePath -products '*' -format json 2> $null | ConvertFrom-Json
38+
$instances = & $vswherePath -latest -products '*' -format json 2> $null | ConvertFrom-Json
3939
$vsInst = $instances | Select-Object -First 1
4040

4141
$componentArgs = $Config.components | ForEach-Object { '--add'; $_ }
@@ -83,4 +83,4 @@ function Add-Vs {
8383
}
8484
end {
8585
}
86-
}
86+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
function Get-VsCacheInfo {
2+
<#
3+
.SYNOPSIS
4+
Check if VS components need to be installed and set GitHub Actions outputs for caching.
5+
.PARAMETER PhpVersion
6+
PHP Version
7+
#>
8+
[OutputType()]
9+
param (
10+
[Parameter(Mandatory = $true, Position=0, HelpMessage='PHP Version')]
11+
[ValidateNotNull()]
12+
[ValidateLength(1, [int]::MaxValue)]
13+
[string] $PhpVersion
14+
)
15+
begin {
16+
$jsonPath = [System.IO.Path]::Combine($PSScriptRoot, '..\config\vs.json')
17+
}
18+
process {
19+
$VsConfig = Get-Content -Path $jsonPath -Raw | ConvertFrom-Json
20+
$majorMinor = if ($PhpVersion -eq 'master') { 'master' } else { $PhpVersion.Substring(0, 3) }
21+
$VsVersion = $VsConfig.php.$majorMinor
22+
$cacheRoot = if ([string]::IsNullOrWhiteSpace($env:RUNNER_TEMP)) { [System.IO.Path]::GetTempPath() } else { $env:RUNNER_TEMP }
23+
$cachePath = [System.IO.Path]::Combine($cacheRoot, 'vs-components', $VsVersion)
24+
$vsInstallPath = ''
25+
$needsInstall = $true
26+
$vsInstallPath = Get-VsInstallPath
27+
if (-not [string]::IsNullOrWhiteSpace($vsInstallPath)) {
28+
try {
29+
Get-VsVersionHelper -VsVersion $VsVersion -VsConfig $VsConfig | Out-Null
30+
$needsInstall = $false
31+
} catch {
32+
}
33+
}
34+
$components = $VsConfig.vs.$VsVersion.components -join ','
35+
$bytes = [System.Text.Encoding]::UTF8.GetBytes($components)
36+
$hash = [System.Security.Cryptography.SHA256]::Create().ComputeHash($bytes)
37+
$componentHash = ([System.BitConverter]::ToString($hash).Replace('-', '').Substring(0, 16)).ToLower()
38+
$cachePath = Join-Path $cachePath $componentHash
39+
"needs-vs-install=$($needsInstall.ToString().ToLower())" >> $env:GITHUB_OUTPUT
40+
"vs-version=$VsVersion" >> $env:GITHUB_OUTPUT
41+
"vs-install-path=$vsInstallPath" >> $env:GITHUB_OUTPUT
42+
"vs-cache-path=$cachePath" >> $env:GITHUB_OUTPUT
43+
"vs-cache-key-prefix=vs-components-$VsVersion-$env:RUNNER_OS-$componentHash" >> $env:GITHUB_OUTPUT
44+
}
45+
end {
46+
}
47+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function Get-VsInstallPath {
2+
<#
3+
.SYNOPSIS
4+
Get the latest Visual Studio installation path.
5+
#>
6+
[OutputType([string])]
7+
param ()
8+
begin {
9+
}
10+
process {
11+
$installerDir = Join-Path "${env:ProgramFiles(x86)}\Microsoft Visual Studio" 'Installer'
12+
$vswherePath = Join-Path $installerDir 'vswhere.exe'
13+
if (-not (Test-Path $vswherePath)) {
14+
return ''
15+
}
16+
17+
$vsInstallPath = & $vswherePath -latest -products * -property installationPath 2>$null | Select-Object -First 1
18+
if ($null -eq $vsInstallPath) {
19+
return ''
20+
}
21+
22+
return $vsInstallPath.Trim()
23+
}
24+
end {
25+
}
26+
}

extension/BuildPhpExtension/private/Get-VsVersion.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ function Get-VsVersion {
2727
Add-Vs -VsVersion $VsVersion -VsConfig $VsConfig
2828
$selectedToolset = Get-VsVersionHelper -VsVersion $VsVersion -VsConfig $VsConfig
2929
}
30+
3031
return [PSCustomObject]@{
3132
vs = $VsVersion
3233
toolset = $selectedToolset
3334
}
3435
}
3536
end {
3637
}
37-
}
38+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
function Invoke-RestoreVsToolsetCache {
2+
<#
3+
.SYNOPSIS
4+
Restore the cached Visual Studio toolset into the active installation.
5+
.PARAMETER CachePath
6+
Cache staging path.
7+
.PARAMETER VsInstallPath
8+
Visual Studio installation path.
9+
#>
10+
[OutputType()]
11+
param (
12+
[Parameter(Mandatory = $true, Position=0, HelpMessage='Cache staging path')]
13+
[ValidateNotNull()]
14+
[ValidateLength(1, [int]::MaxValue)]
15+
[string] $CachePath,
16+
[Parameter(Mandatory = $false, Position=1, HelpMessage='Visual Studio installation path')]
17+
[string] $VsInstallPath = ''
18+
)
19+
begin {
20+
}
21+
process {
22+
if ([string]::IsNullOrWhiteSpace($VsInstallPath)) {
23+
$VsInstallPath = Get-VsInstallPath
24+
}
25+
26+
if ([string]::IsNullOrWhiteSpace($VsInstallPath)) {
27+
throw "Visual Studio installation path is not available."
28+
}
29+
30+
$restored = Restore-VsToolsetFromCache -VsInstallPath $VsInstallPath -CachePath $CachePath
31+
if (-not $restored) {
32+
throw "Failed to restore the cached Visual Studio toolset."
33+
}
34+
}
35+
end {
36+
}
37+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
function Invoke-SaveVsToolsetCache {
2+
<#
3+
.SYNOPSIS
4+
Stage the selected Visual Studio toolset for GitHub Actions caching.
5+
.PARAMETER PhpVersion
6+
PHP Version.
7+
.PARAMETER CachePath
8+
Cache staging path.
9+
#>
10+
[OutputType()]
11+
param (
12+
[Parameter(Mandatory = $true, Position=0, HelpMessage='PHP Version')]
13+
[ValidateNotNull()]
14+
[ValidateLength(1, [int]::MaxValue)]
15+
[string] $PhpVersion,
16+
[Parameter(Mandatory = $true, Position=1, HelpMessage='Cache staging path')]
17+
[ValidateNotNull()]
18+
[ValidateLength(1, [int]::MaxValue)]
19+
[string] $CachePath
20+
)
21+
begin {
22+
}
23+
process {
24+
$vsInstallPath = Get-VsInstallPath
25+
if ([string]::IsNullOrWhiteSpace($vsInstallPath)) {
26+
throw "Visual Studio installation path is not available."
27+
}
28+
29+
$vsData = Get-VsVersion -PhpVersion $PhpVersion
30+
Sync-VsToolsetCache -VsInstallPath $vsInstallPath -CachePath $CachePath -Toolset $vsData.toolset
31+
}
32+
end {
33+
}
34+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
function Restore-VsToolsetFromCache {
2+
<#
3+
.SYNOPSIS
4+
Restore a cached Visual Studio toolset into the active VS installation.
5+
.PARAMETER VsInstallPath
6+
Visual Studio installation path.
7+
.PARAMETER CachePath
8+
Cache staging path.
9+
#>
10+
[OutputType([bool])]
11+
param (
12+
[Parameter(Mandatory = $true, Position=0, HelpMessage='Visual Studio installation path')]
13+
[ValidateNotNull()]
14+
[ValidateLength(1, [int]::MaxValue)]
15+
[string] $VsInstallPath,
16+
[Parameter(Mandatory = $true, Position=1, HelpMessage='Cache staging path')]
17+
[ValidateNotNull()]
18+
[ValidateLength(1, [int]::MaxValue)]
19+
[string] $CachePath
20+
)
21+
begin {
22+
}
23+
process {
24+
$metadataPath = Join-Path $CachePath 'toolset.txt'
25+
if (-not (Test-Path $metadataPath)) {
26+
return $false
27+
}
28+
29+
$toolset = (Get-Content -Path $metadataPath -Raw).Trim()
30+
if ([string]::IsNullOrWhiteSpace($toolset)) {
31+
return $false
32+
}
33+
34+
$sourcePath = Join-Path (Join-Path $CachePath 'toolset') $toolset
35+
if (-not (Test-Path $sourcePath)) {
36+
return $false
37+
}
38+
39+
$msvcDirectory = Join-Path $VsInstallPath 'VC\Tools\MSVC'
40+
if (-not (Test-Path $msvcDirectory)) {
41+
New-Item -Path $msvcDirectory -ItemType Directory -Force | Out-Null
42+
}
43+
44+
$destinationPath = Join-Path $msvcDirectory $toolset
45+
if (-not (Test-Path $destinationPath)) {
46+
Copy-Item -Path $sourcePath -Destination $msvcDirectory -Recurse -Force
47+
}
48+
49+
return (Test-Path $destinationPath)
50+
}
51+
end {
52+
}
53+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
function Sync-VsToolsetCache {
2+
<#
3+
.SYNOPSIS
4+
Stage a Visual Studio toolset for GitHub Actions caching.
5+
.PARAMETER VsInstallPath
6+
Visual Studio installation path.
7+
.PARAMETER CachePath
8+
Cache staging path.
9+
.PARAMETER Toolset
10+
Toolset version directory name.
11+
#>
12+
[OutputType()]
13+
param (
14+
[Parameter(Mandatory = $true, Position=0, HelpMessage='Visual Studio installation path')]
15+
[ValidateNotNull()]
16+
[ValidateLength(1, [int]::MaxValue)]
17+
[string] $VsInstallPath,
18+
[Parameter(Mandatory = $true, Position=1, HelpMessage='Cache staging path')]
19+
[ValidateNotNull()]
20+
[ValidateLength(1, [int]::MaxValue)]
21+
[string] $CachePath,
22+
[Parameter(Mandatory = $true, Position=2, HelpMessage='Toolset directory')]
23+
[ValidateNotNull()]
24+
[ValidateLength(1, [int]::MaxValue)]
25+
[string] $Toolset
26+
)
27+
begin {
28+
}
29+
process {
30+
$sourcePath = Join-Path (Join-Path $VsInstallPath 'VC\Tools\MSVC') $Toolset
31+
if (-not (Test-Path $sourcePath)) {
32+
return
33+
}
34+
35+
if (-not (Test-Path $CachePath)) {
36+
New-Item -Path $CachePath -ItemType Directory -Force | Out-Null
37+
}
38+
39+
$toolsetCacheDirectory = Join-Path $CachePath 'toolset'
40+
if (Test-Path $toolsetCacheDirectory) {
41+
Remove-Item -Path $toolsetCacheDirectory -Recurse -Force
42+
}
43+
New-Item -Path $toolsetCacheDirectory -ItemType Directory -Force | Out-Null
44+
45+
Copy-Item -Path $sourcePath -Destination $toolsetCacheDirectory -Recurse -Force
46+
Set-Content -Path (Join-Path $CachePath 'toolset.txt') -Value $Toolset -NoNewline
47+
}
48+
end {
49+
}
50+
}

extension/action.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,28 @@ runs:
6161
- name: Checkout
6262
uses: actions/checkout@v5
6363

64+
- name: Check VS components
65+
id: vs-check
66+
shell: pwsh
67+
run: |
68+
Import-Module ${{ github.action_path }}\BuildPhpExtension -Force
69+
Get-VsCacheInfo -PhpVersion ${{ inputs.php-version }}
70+
71+
- name: Restore VS components cache
72+
id: vs-cache
73+
if: steps.vs-check.outputs.needs-vs-install == 'true'
74+
uses: actions/cache/restore@v4
75+
with:
76+
path: ${{ steps.vs-check.outputs.vs-cache-path }}
77+
key: ${{ steps.vs-check.outputs.vs-cache-key-prefix }}
78+
79+
- name: Restore VS toolset
80+
if: steps.vs-cache.outputs.cache-hit == 'true'
81+
shell: pwsh
82+
run: |
83+
Import-Module ${{ github.action_path }}\BuildPhpExtension -Force
84+
Invoke-RestoreVsToolsetCache -CachePath "${{ steps.vs-check.outputs.vs-cache-path }}" -VsInstallPath "${{ steps.vs-check.outputs.vs-install-path }}"
85+
6486
- name: Build PHP Extension
6587
id: build
6688
shell: pwsh
@@ -85,6 +107,21 @@ runs:
85107
-Arch ${{inputs.arch}} `
86108
-Ts ${{inputs.ts}}
87109
110+
- name: Stage VS toolset cache
111+
if: steps.vs-check.outputs.needs-vs-install == 'true' && steps.vs-cache.outputs.cache-hit != 'true'
112+
shell: pwsh
113+
run: |
114+
Import-Module ${{ github.action_path }}\BuildPhpExtension -Force
115+
Invoke-SaveVsToolsetCache -PhpVersion ${{ inputs.php-version }} -CachePath "${{ steps.vs-check.outputs.vs-cache-path }}"
116+
117+
- name: Save VS components cache
118+
if: steps.vs-check.outputs.needs-vs-install == 'true' && steps.vs-cache.outputs.cache-hit != 'true'
119+
continue-on-error: true
120+
uses: actions/cache/save@v4
121+
with:
122+
path: ${{ steps.vs-check.outputs.vs-cache-path }}
123+
key: ${{ steps.vs-cache.outputs.cache-primary-key }}
124+
88125
- name: Upload the build artifact
89126
uses: actions/upload-artifact@v5
90127
with:

0 commit comments

Comments
 (0)