mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-02-05 11:30:07 +08:00
240 lines
9.9 KiB
PowerShell
240 lines
9.9 KiB
PowerShell
<#
|
|
Force the object from command or function result to array.
|
|
Sometimes when a command returns zero or one object, it becomes $null or the object.
|
|
Only when a command returns multiple objects, it becomes an array.
|
|
This function let you get an array anyway to make writing script easier.
|
|
#>
|
|
function ForceArray($nodes) {
|
|
if ($nodes -eq $null) {
|
|
return ,@()
|
|
} elseif (($nodes -is [System.Array]) -or ($nodes -is [System.Collections.ArrayList])) {
|
|
return $nodes
|
|
} else {
|
|
return ,@($nodes)
|
|
}
|
|
}
|
|
|
|
<#
|
|
Search all GacUI Xml Resource file names in the directory that contains GacUI.xml, which is specified by $FileName.
|
|
#>
|
|
function EnumerateResourceFiles([String] $FileName) {
|
|
Write-Host "Searching for all resource files ..."
|
|
|
|
<# Load GacUI.xml and find all path patterns that we want to exclude from our result #>
|
|
[Xml]$gacui_xml = Get-Content $FileName
|
|
$excludes = (ForceArray (Select-Xml -Xml $gacui_xml -XPath "//GacUI/Exclude/@Pattern")).Node.Value
|
|
$search_directory = Split-Path -Parent (Resolve-Path $FileName)
|
|
|
|
<# Enumerate all Xml file, if it is not excluded, and it matches <Resource><Folder name="GacGenConfig"/></Resource> #>
|
|
$resource_files = (Get-ChildItem $search_directory -Filter "*.xml" -Recurse | ForEach-Object {
|
|
$normalized_path = $_.FullName -replace '\\','/'
|
|
if (($excludes | Where-Object { $normalized_path.Contains($_) }).Length -eq 0) {
|
|
if ((Select-Xml -Path $_.FullName -XPath "//Resource/Folder[@name='GacGenConfig']") -ne $null) {
|
|
$_.FullName.Substring($search_directory.Length)
|
|
}
|
|
}
|
|
})
|
|
[System.IO.File]::WriteAllLines("$($FileName).log\ResourceFiles.txt", $resource_files)
|
|
}
|
|
|
|
<#
|
|
Call GacGen.exe to dump metadatas from resource files.
|
|
Input files is save in $($FileName).log\ResourceFiles.txt, which is generated by EnumerateResourceFiles function
|
|
Output files is specified in $ResourceDumpFiles(resource_file_name => dump_file_name)
|
|
#>
|
|
function DumpResourceFiles([String] $FileName, [HashTable]$ResourceDumpFiles) {
|
|
Write-Host "Dumping all resource files ..."
|
|
|
|
$search_directory = Split-Path -Parent (Resolve-Path $FileName)
|
|
Get-Content "$($FileName).log\ResourceFiles.txt" | ForEach-Object {
|
|
$input_file = Join-Path -Path $search_directory -ChildPath $_
|
|
$output_file = "$($FileName).log\$($_ -replace '\\','_')"
|
|
$ResourceDumpFiles[$input_file] = $output_file
|
|
}
|
|
|
|
$ResourceDumpFiles.Keys | ForEach-Object {
|
|
$input_file = $_
|
|
$output_file = $ResourceDumpFiles[$_]
|
|
Start-Process-And-Wait (,("$PSScriptRoot\GacGen.exe", "/D32 `"$($input_file)`" `"$($output_file)`"")) $true
|
|
if (-not (Test-Path -Path $output_file)) {
|
|
throw "Failed to dump GacUI Xml Resource File: " + $input_file
|
|
}
|
|
}
|
|
}
|
|
|
|
<#
|
|
Given a metadata dump, determine if the compile result of this resource file is outdated.
|
|
It collects last modify times of all input files and output files.
|
|
If any output files' time is eariler than any input files' time, it is outdated.
|
|
#>
|
|
function NeedBuild([Xml] $Dump) {
|
|
$input_files = (ForceArray (Select-Xml -Xml $Dump -XPath "//ResourceMetadata/Inputs/Input/@Path")).Node.Value
|
|
$output_files = (ForceArray (Select-Xml -Xml $Dump -XPath "//ResourceMetadata/Outputs/Output/@Path")).Node.Value
|
|
|
|
if (($output_files | Where-Object { -not [System.IO.File]::Exists($_) }) -ne $null) {
|
|
return $true
|
|
}
|
|
|
|
$input_file_times = ForceArray ($input_files | ForEach-Object {
|
|
[System.IO.FileInfo]::new($_).LastWriteTimeUtc
|
|
})
|
|
$output_file_times = ForceArray ($output_files | ForEach-Object {
|
|
[System.IO.FileInfo]::new($_).LastWriteTimeUtc
|
|
})
|
|
|
|
$outdated = $output_file_times | Where-Object {
|
|
$output = $_
|
|
$modifieds = $input_file_times | Where-Object {
|
|
return $_ -gt $output
|
|
}
|
|
return $modifieds -ne $null
|
|
}
|
|
|
|
return $outdated -ne $null
|
|
}
|
|
|
|
<#
|
|
Given all metadata dumps $ResourceDumps(resource_file_name => Xml dump),
|
|
generate $name_to_file_map(resource_name => resource_file_name),
|
|
and $name_to_dep_map(resource_name => all dependencies)
|
|
#>
|
|
function ExtractDeps([HashTable] $ResourceDumps, [HashTable] $name_to_file_map, [HashTable] $name_to_dep_map)
|
|
{
|
|
$ResourceDumps.Keys | ForEach-Object {
|
|
$xml = $ResourceDumps[$_]
|
|
$name = (Select-Xml -Xml $xml -XPath "//ResourceMetadata/ResourceMetadata/@Name").Node.Value
|
|
if ($name -ne "") {
|
|
$attrs = ForceArray (Select-Xml -Xml $xml -XPath "//ResourceMetadata/ResourceMetadata/Dependencies/Resource/@Name")
|
|
$deps = ForceArray $attrs.Node.Value
|
|
$name_to_file_map[$name] = $_
|
|
$name_to_dep_map[$name] = [System.Collections.ArrayList]::new($deps)
|
|
}
|
|
}
|
|
}
|
|
|
|
<#
|
|
Given all metadata dumps $ResourceDumps(resource_file_name => Xml dump),
|
|
write all resources that is outdated to $OutputFileName.
|
|
#>
|
|
function EnumerateBuildCandidates([HashTable] $ResourceDumps, [String] $OutputFileName) {
|
|
Write-Host "Finding resource files that need rebuild ..."
|
|
$direct_candidates = ForceArray ($ResourceDumps.Keys | Where-Object { NeedBuild $ResourceDumps[$_] })
|
|
|
|
$name_to_file_map = @{}
|
|
$name_to_dep_map = @{}
|
|
ExtractDeps $ResourceDumps $name_to_file_map $name_to_dep_map
|
|
|
|
$file_to_name_map = @{}
|
|
$name_to_file_map.Keys | ForEach-Object { $file_to_name_map[$name_to_file_map[$_]] = $_ }
|
|
|
|
<# Get all names of named resources that are outdated #>
|
|
$names = ForceArray ($direct_candidates | Where-Object { $file_to_name_map.ContainsKey($_) } | ForEach-Object { $file_to_name_map[$_] })
|
|
$names = [System.Collections.ArrayList]::new($names)
|
|
<# Get all names of named resources that are not outdated #>
|
|
$pool = ForceArray ($name_to_file_map.Keys | Where-Object { -not $names.Contains($_) })
|
|
$pool = [System.Collections.ArrayList]::new($pool)
|
|
|
|
<# Grow $names from $pool #>
|
|
while ($true)
|
|
{
|
|
<# If any resource in $pool depends on any resource in $name, it is moved from $pool to $names #>
|
|
$selection = ForceArray ($pool | Where-Object {
|
|
$deps = $name_to_dep_map[$_]
|
|
return (ForceArray ($deps | Where-Object { $names.Contains($_) })).Count -ne 0
|
|
})
|
|
if ($selection.Count -eq 0) { break }
|
|
|
|
$names.AddRange($selection)
|
|
$selection | ForEach-Object { $pool.Remove($_) }
|
|
}
|
|
|
|
<# List all anonymous resource files before named resource files #>
|
|
$anonymous_candidates = ForceArray ($direct_candidates | Where-Object { -not $name_to_file_map.ContainsValue($_) })
|
|
$named_candidates = ForceArray ($names | ForEach-Object { $name_to_file_map[$_] })
|
|
|
|
[System.IO.File]::WriteAllLines($OutputFileName, $anonymous_candidates + $named_candidates)
|
|
}
|
|
|
|
<#
|
|
Given all metadata dumps $ResourceDumps(resource_file_name => Xml dump),
|
|
Write all paths of anonymous resource files to $OutputFileName.
|
|
#>
|
|
function EnumerateAnonymousResources([HashTable] $ResourceDumps, [String] $OutputFileName) {
|
|
Write-Host "Finding anonymouse resource files ..."
|
|
$file_names = $ResourceDumps.Keys | Where-Object {
|
|
return (ForceArray (Select-Xml -Xml $ResourceDumps[$_] -XPath "//ResourceMetadata/ResourceMetadata/@Name"))[0].Node.Value -eq ""
|
|
} | Sort-Object
|
|
[System.IO.File]::WriteAllLines($OutputFileName, (ForceArray $file_names))
|
|
}
|
|
|
|
<#
|
|
Verify if $name_to_dep_name(resource_name => dependencies) contains any dependencies that are not in this map
|
|
#>
|
|
function ValidateDeps([HashTable] $name_to_dep_map)
|
|
{
|
|
$hasError = $false
|
|
$name_to_dep_map.Keys | ForEach-Object {
|
|
$key = $_
|
|
$name_to_dep_map[$key] | ForEach-Object {
|
|
if (-not $name_to_dep_map.ContainsKey($key)) {
|
|
$hasError = $true
|
|
Write-Host "Resource $($key) depends on $($_) but $($_) does not exist."
|
|
}
|
|
}
|
|
}
|
|
if ($hasError) { throw "Please check your metadata." }
|
|
}
|
|
|
|
<#
|
|
Sort all named resource files in partial order
|
|
#>
|
|
function SortDeps([HashTable] $name_to_dep_map)
|
|
{
|
|
$compile_order = [System.Collections.ArrayList]::new()
|
|
while ($name_to_dep_map.Count -gt 0) {
|
|
$selection = ForceArray ($name_to_dep_map.Keys | Where-Object { $name_to_dep_map[$_].Count -eq 0 })
|
|
if ($selection.Count -eq 0) {
|
|
Write-Host "Found circle dependency in the following resources:"
|
|
$name_to_dep_map.Keys | ForEach-Object { Write-Host " $($_)" }
|
|
$hasError = $true;
|
|
break
|
|
} else {
|
|
$compile_order.AddRange((ForceArray ($selection | Select-Object)))
|
|
$selection | ForEach-Object {
|
|
$ready = $_
|
|
$name_to_dep_map.Remove($ready)
|
|
$name_to_dep_map.Values | ForEach-Object { $_.Remove($ready) }
|
|
}
|
|
}
|
|
}
|
|
if ($hasError) { throw "Please check your metadata." }
|
|
return $compile_order
|
|
}
|
|
|
|
<#
|
|
Given all metadata dumps $ResourceDumps(resource_file_name => Xml dump),
|
|
write all paths of named resource files in the correct build order to $OutputNames,
|
|
with all "resource_name=>resource_file_path" to $OutputMapping.
|
|
The $OutputMapping will be consumed
|
|
GacGen.exe /P32 <resource-xml> <HERE>
|
|
GacGen.exe /P64 <resource-xml> <HERE>
|
|
as an optional parameter
|
|
#>
|
|
function EnumerateNamedResources([HashTable] $ResourceDumps, [String] $OutputNames, [String] $OutputMapping) {
|
|
Write-Host "Finding named resource files ..."
|
|
|
|
$name_to_file_map = @{}
|
|
$name_to_dep_map = @{}
|
|
ExtractDeps $ResourceDumps $name_to_file_map $name_to_dep_map
|
|
ValidateDeps $name_to_dep_map
|
|
$compile_order = ForceArray (SortDeps $name_to_dep_map)
|
|
|
|
$file_names = ForceArray ($compile_order | ForEach-Object {
|
|
return $name_to_file_map[$_]
|
|
})
|
|
$file_mapping = ForceArray ($name_to_file_map.Keys | ForEach-Object {
|
|
return "$($_)=>$($name_to_file_map[$_])"
|
|
} | Sort-Object)
|
|
[System.IO.File]::WriteAllLines($OutputNames, $file_names)
|
|
[System.IO.File]::WriteAllLines($OutputMapping, $file_mapping)
|
|
} |