Last active
August 4, 2024 21:07
-
-
Save awakecoding/c9171965001df09f02f44edec5439ade to your computer and use it in GitHub Desktop.
ReadyToRun.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
param( | |
[Parameter(Mandatory = $true, Position = 0)] | |
[string] $InputPath, | |
[string] $OutputPath, | |
[ValidateSet("x64", "arm64")] | |
[string] $TargetArch = "x64", | |
[ValidateSet("windows", "linux", "osx")] | |
[string] $TargetOS = "windows", | |
[Parameter(Mandatory = $true)] | |
[string] $RuntimeConfigFile, | |
[string[]] $IncludeFilter, | |
[switch] $UseCache, | |
[switch] $PrintCommands | |
) | |
function Find-CrossGen2Exe | |
{ | |
$NugetPackagesPath = "$Env:USERPROFILE\.nuget\packages" | |
$NugetPackageName = "Microsoft.NETCore.App.Crossgen2.win-x64" | |
$NugetPackageCache = Join-Path $NugetPackagesPath ($NugetPackageName.ToLower()) | |
$CrossGen2Tool = Get-Item "$NugetPackageCache\*\tools\crossgen2.exe" | Sort-Object -Descending | Select-Object -First 1 | |
$CrossGen2Tool.FullName | |
} | |
function Test-DotNetAssembly | |
{ | |
param ( | |
[Parameter(Mandatory = $true, Position = 0)] | |
[string] $FilePath | |
) | |
try { | |
[System.Reflection.AssemblyName]::GetAssemblyName($FilePath) | Out-Null | |
return $true | |
} catch { | |
return $false | |
} | |
} | |
function Get-RuntimeAssemblyPaths | |
{ | |
param ( | |
[Parameter(Mandatory = $true)] | |
[string] $RuntimeConfigPath | |
) | |
$DotNetRootShared = "$Env:ProgramFiles\dotnet\shared" | |
$RuntimeConfig = Get-Content $RuntimeConfigPath -Raw | ConvertFrom-Json | |
$FrameworkAssemblyPaths = @() | |
$Runtimeconfig.runtimeOptions.frameworks | ForEach-Object { | |
$FrameworkName = $_.Name | |
$Version = [Version] $_.version | |
$VersionFilter = "$($Version.Major).$($Version.Minor).*" | |
$FrameworkAssemblyPath = Get-Item "$DotNetRootShared\$FrameworkName\$VersionFilter" | Sort-Object -Descending | Select-Object -First 1 | |
$FrameworkAssemblyPaths += $FrameworkAssemblyPath.FullName | |
} | |
$FrameworkAssemblyPaths | |
} | |
function Get-ApplicationBasePath | |
{ | |
param ( | |
[Parameter(Mandatory = $true)] | |
[string] $RuntimeConfigPath | |
) | |
[System.IO.Path]::GetDirectoryName($RuntimeConfigPath) | |
} | |
function Get-ReferencedAssemblies | |
{ | |
param ( | |
[Parameter(Mandatory = $true)] | |
[string] $AssemblyPath, | |
[Parameter(Mandatory = $true)] | |
[string] $ApplicationBasePath | |
) | |
$Assembly = [System.Reflection.Assembly]::LoadFile($AssemblyPath) | |
$Assembly.GetReferencedAssemblies() | |
| ForEach-Object { Join-Path $ApplicationBasePath ($_.Name + ".dll") } | |
| Where-Object { Test-Path -Path $_ -PathType Leaf } | |
} | |
if ([string]::IsNullOrEmpty($OutputPath)) { | |
$OutputPath = $InputPath | |
$OutputIsInput = $true | |
} | |
$RuntimeConfigPath = Join-Path $InputPath $RuntimeConfigFile | |
if (-Not $IncludeFilter) { | |
$IncludeFilter = @('*') | |
} | |
if (-Not $OutputIsInput) { | |
#Remove-Item -Path $OutputPath -ErrorAction SilentlyContinue -Recurse -Force | Out-Null | |
#Copy-Item -Path $InputPath -Destination $OutputPath -Recurse | |
& robocopy.exe $InputPath $OutputPath /E /DCOPY:DA /COPY:DAT /MT:16 /R:3 /W:5 /IS /NFL /NDL /NS /NC /NP | |
} | |
$TargetRID = ($TargetOS -Replace "windows", "win") + "-" + $TargetArch | |
$ReadyToRunInclude = Get-ChildItem -Path "$InputPath/*.dll" -Include $IncludeFilter | |
$ApplicationBasePath = Get-ApplicationBasePath -RuntimeConfigPath $RuntimeConfigPath | |
$FrameworkAssemblyPaths = Get-RuntimeAssemblyPaths -RuntimeConfigPath $RuntimeConfigPath | |
$FrameworkAssemblies = $FrameworkAssemblyPaths | |
| ForEach-Object { Get-Item "$_/*.dll" } | |
| Select-Object -ExpandProperty FullName | |
| Where-Object { Test-DotNetAssembly $_ } | |
$SystemReferenceArgs = @() | |
$FrameworkAssemblies | ForEach-Object { | |
$SystemReferenceArgs += "-r`:`"$_`"" | |
} | |
$CrossGen2Exe = Find-CrossGen2Exe | |
Write-Host "Using $CrossGen2Exe" | |
if (Test-Path Env:R2R_CACHE_PATH) { | |
$R2RCachePath = $Env:R2R_CACHE_PATH | |
} else { | |
$R2RCachePath = Join-Path (Get-Location) "r2r-cache" | |
} | |
if ($UseCache) { | |
New-Item -Path $R2RCachePath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null | |
} | |
$StartTime = [datetime]::Now | |
$TotalSizeDifference = 0 | |
$CacheHitCount = 0 | |
$AssemblyCount = $ReadyToRunInclude.Count | |
$ReadyToRunInclude | ForEach-Object { | |
$InputFile = $AssemblyPath = $_.FullName | |
$OutputFile = Join-Path $OutputPath $_.Name | |
if ($OutputIsInput) { | |
$OutputFile = [System.IO.Path]::ChangeExtension($OutputFile, ".ni.dll") | |
} | |
$InputFileName = [System.IO.Path]::GetFileName($InputFile) | |
$InputFileHash = (Get-FileHash $InputFile SHA256).Hash.ToLower() | |
# <r2r-cache>/<TargetRID>/<InputFileName>/<InputFileHash>/ | |
$CacheFileDir = Join-Path $R2RCachePath $TargetRID $InputFileName $InputFileHash | |
$CacheFilePath = Join-Path $CacheFileDir $InputFileName | |
$CacheFileDeps = $CacheFilePath + ".deps" | |
$ReferencedAssemblies = Get-ReferencedAssemblies -AssemblyPath $AssemblyPath -ApplicationBasePath $ApplicationBasePath | |
$Dependencies = "" | |
foreach ($ReferencedAssembly in $ReferencedAssemblies) { | |
$FileName = [System.IO.Path]::GetFileName($ReferencedAssembly) | |
$FileHash = (Get-FileHash $ReferencedAssembly SHA256).Hash.ToLower() | |
$Dependencies += "$FileHash $FileName`n" | |
} | |
$Dependencies = $Dependencies.TrimEnd("`n") | |
$sha256 = [System.Security.Cryptography.SHA256]::Create() | |
$hashBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Dependencies)) | |
$InputDepsHash = ([BitConverter]::ToString($hashBytes) -Replace '-', '').ToLower() | |
$ReferenceArgs = @() | |
$ReferenceArgs += $SystemReferenceArgs | |
$ReferencedAssemblies | ForEach-Object { | |
$ReferenceArgs += "-r:`"$_`"" | |
} | |
$CrossGenArgs = @("--targetos:$TargetOS", "--targetarch:$TargetArch") | |
$CrossGenArgs += @("-O") | |
$CrossGenArgs += $ReferenceArgs | |
$CrossGenArgs += "--out:`"$OutputFile`"" | |
$CrossGenArgs += $InputFile | |
if ($PrintCommands) { | |
Write-Host "crossgen2.exe" $CrossGenArgs -Separator "`r`n" | |
} | |
$CacheHit = $false | |
if ($UseCache) { | |
if ((Test-Path -Path $CacheFilePath -PathType Leaf) -and (Test-Path -Path $CacheFileDeps -PathType Leaf)) { | |
$CachedDepsHash = (Get-FileHash $CacheFileDeps SHA256).Hash.ToLower() | |
if ($InputDepsHash -eq $CachedDepsHash) { | |
$CacheHit = $true | |
$CacheHitCount++ | |
} | |
} | |
} | |
if ($CacheHit) { | |
Copy-Item $CacheFilePath $OutputFile -Force | |
} else { | |
$ResponseFilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "crossgen2_args.rsp") | |
$CrossGenArgs | Out-File -FilePath $ResponseFilePath -Encoding Utf8NoBOM | |
Start-Process -FilePath $CrossGen2Exe -ArgumentList "@$ResponseFilePath" -NoNewWindow -Wait | |
Remove-Item $ResponseFilePath | |
} | |
$InputFileSize = (Get-Item $InputFile).Length | |
$OutputFileSize = (Get-Item $OutputFile).Length | |
$SizeDifference = $OutputFileSize - $InputFileSize | |
$SizePercentage = [math]::Round(($SizeDifference / $InputFileSize) * 100, 2) | |
$TotalSizeDifference += $SizeDifference | |
Write-Output "$($_.Name)`: $SizePercentage`% larger" | |
if ($OutputIsInput) { | |
Remove-Item $InputFile | Out-Null | |
Move-Item $OutputFile $InputFile | |
$OutputFile = $InputFile | |
} | |
if ($UseCache) { | |
New-Item -Path $CacheFileDir -ItemType Directory -ErrorAction SilentlyContinue | Out-Null | |
Copy-Item $OutputFile $CacheFilePath -Force | |
Set-Content -Path $CacheFileDeps -Value $Dependencies -Encoding Utf8NoBOM -NoNewLine -Force | |
} | |
} | |
$TotalSizeDiffMB = [math]::Round($TotalSizeDifference / 1000 / 1000, 2) | |
Write-Output "ReadyToRun output is $TotalSizeDiffMB MB larger" | |
$EndTime = [datetime]::Now | |
$TotalTimeSeconds = [math]::Round(($EndTime - $StartTime).TotalSeconds, 2) | |
Write-Output "Total generation time: $TotalTimeSeconds seconds" | |
if ($UseCache) { | |
$CacheHitPercentage = [math]::Round(($CacheHitCount / $AssemblyCount) * 100, 2) | |
Write-Output "Cache hits: $CacheHitCount ($CacheHitPercentage%)" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment