Last active
January 23, 2025 18:46
-
-
Save mavaddat/f021e09f8c2d492e19959a9978b42544 to your computer and use it in GitHub Desktop.
Download and install xmllint for Windows
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
#Requires -Modules PSParseHTML | |
function Install-XmlLint { | |
[CmdletBinding(SupportsShouldProcess)] | |
param ( | |
[Parameter(ValueFromPipelineByPropertyName)] | |
[string] | |
$InstallPath = (Join-Path -Path "$env:LOCALAPPDATA\Programs" -ChildPath xmllint), | |
[switch] | |
$Force | |
) | |
Write-Verbose 'Starting Install-XmlLint' | |
$baseUrl = 'http://xmlsoft.org/sources/win32' | |
if ([System.Environment]::Is64BitOperatingSystem) { | |
$baseUrl += '/64bit' | |
$fileSuffix = '_64' | |
} | |
Write-Verbose "Base URL: $baseUrl" | |
Write-Verbose "File Suffix: $fileSuffix" | |
$sort += '?C=M;O=D' # Order by date, descending | |
$libs = 'iconv', 'libxml2', 'libxml', 'xmlsec', 'zlib' | |
$httpClient = [System.Net.Http.HttpClient]::new() | |
$xmllintSite = Invoke-RestMethod -Method Get -Uri ($baseUrl + $sort) | ConvertFrom-HTML -Engine AngleSharp | |
$downloadLinks = $xmllintSite.QuerySelectorAll('body > table > tbody > tr > td > a') | |
Write-Verbose "Found $($downloadLinks.Text -match "($($libs -join '|')).*$fileSuffix") of $($libs.Count) download links" | |
@' | |
Microsoft.Bcl.AsyncInterfaces (>= 8.0.0) | |
System.Buffers (>= 4.6.0) | |
System.Memory (>= 4.6.0) | |
System.Text.Encoding.CodePages (>= 8.0.0) | |
ZstdSharp.Port (>= 0.8.4) | |
SharpCompress (>= 0.1) | |
'@ | Select-String -Pattern '\S+(?= \(.*\))' -AllMatches | Select-Object -ExpandProperty Matches | ForEach-Object { | |
(Get-Item (Join-Path (Split-Path (Get-Package $_.Value).Source) lib/netstandard*) | | |
Sort-Object { [version] ($_.Name -replace '^netstandard') })[-1] | | |
Get-ChildItem -Filter *.dll -Recurse | | |
ForEach-Object { | |
Write-Verbose "Adding '$($_.Name)' assembly into PowerShell session" | |
Add-Type -LiteralPath $_.FullName } | |
} | |
$extractOptions = [SharpCompress.Common.ExtractionOptions]::new() | |
$extractOptions.ExtractFullPath = $true | |
$extractOptions.Overwrite = $true | |
if (Test-Path -Path $InstallPath -PathType Container) { | |
Push-Location -Path $InstallPath | |
} | |
else { | |
New-Item -Path $InstallPath -ItemType Directory | Push-Location | |
} | |
foreach ($lib in $libs) { | |
$downloadLink = $downloadLinks | Where-Object -FilterScript { $_.Text -imatch "${lib}.*${fileSuffix}" } | Select-Object -First 1 -ExpandProperty PathName | |
Write-Verbose -Message "Downloading '$downloadLink'" | |
$downloadLink = $baseUrl + $downloadLink | |
# Use HttpClient to download the file | |
$httpClient = [System.Net.Http.HttpClient]::new() | |
$response = $httpClient.GetAsync($downloadLink).Result | |
$response.EnsureSuccessStatusCode() | Format-Table -AutoSize -Wrap | Out-String | Write-Debug | |
$bytes = $response.Content.ReadAsByteArrayAsync().Result | |
Write-Verbose "Downloaded $($bytes.Length) bytes" | |
# Use SharpCompress to extract the 7z files | |
$readerOptions = [SharpCompress.Readers.ReaderOptions]::new() | |
$readerOptions.ArchiveEncoding = [SharpCompress.Common.ArchiveEncoding]::new() | |
$readerOptions.ArchiveEncoding.Default = [System.Text.Encoding]::GetEncoding('utf-8') | |
try { | |
$sevenZipStream = [System.IO.MemoryStream]::new($bytes) | |
$archive = [SharpCompress.Archives.SevenZip.SevenZipArchive]::Open($sevenZipStream, $readerOptions) | |
foreach ($entry in $archive.Entries) { | |
if ($entry.IsDirectory -or -not $entry.Key.StartsWith('bin/')) { | |
Write-Debug "Skipping entry: $($entry.Key)" | |
continue | |
} | |
Write-Verbose "Extracting entry: $($entry.Key)" | |
try { | |
$entryStream = $entry.OpenEntryStream() | |
} | |
catch { | |
Write-Verbose "Skipping entry without a stream: $($entry.Key)" | |
continue | |
} | |
$outputPath = [System.IO.Path]::Combine("$env:TEMP\${lib}", $entry.Key) | |
$outputDir = [System.IO.Path]::GetDirectoryName($outputPath) | |
if (-not (Test-Path -Path $outputDir)) { | |
New-Item -Path $outputDir -ItemType Directory | Out-Null | |
} | |
$fileStream = [System.IO.File]::Create($outputPath) | |
try { | |
$entryStream.CopyTo($fileStream) | |
} | |
finally { | |
$fileStream.Close() | |
$entryStream.Close() | |
} | |
} | |
} | |
catch { | |
Write-Error -Message "Failed to open the 7z archive for '${lib}': $($_.Exception)" | |
} | |
# Copy all the files in the bin folder of each downloaded library to the xmllint folder | |
Get-ChildItem -Path "$env:TEMP\${lib}\bin" | Move-Item -Destination $InstallPath -Force:$Force | |
Write-Verbose "Copied files for $lib to $InstallPath" | |
} | |
$xmlLintExe = Get-Item -Path .\xmllint.exe | |
Write-Verbose "Completed Install-XmlLint: '$($xmlLintExe.FullName)'" | |
Pop-Location | |
} | |
if ($MyInvocation.InvocationName -eq '.') { | |
# Install the required packages, if they're not already installed | |
@' | |
Microsoft.Bcl.AsyncInterfaces (>= 8.0.0) | |
System.Buffers (>= 4.6.0) | |
System.Memory (>= 4.6.0) | |
System.Text.Encoding.CodePages (>= 8.0.0) | |
ZstdSharp.Port (>= 0.8.4) | |
SharpCompress (>= 0.1) | |
'@ | Select-String -Pattern '\S+(?= \(.*\))' -AllMatches | Select-Object -ExpandProperty Matches | ForEach-Object { | |
$packageName = $_.Value | |
if (-not(Get-Package -Name $packageName -Scope CurrentUser)) { | |
Write-Verbose -Message "Installing '$packageName'" -Verbose | |
Install-Package -Name $packageName -Scope CurrentUser -Source NuGet | |
} | |
} | |
# Install xmllint into the default location | |
Install-XmlLint -Force -Verbose -Confirm:$false | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@rinilnath , I have updated the code and tested it. You can either download the cmdlet and import it as a module
Or, use the
Invoke-Expression
on the remote string to run the script: