From 9af781f41937e5ce8d42b914bf94dbbb1e289e89 Mon Sep 17 00:00:00 2001 From: halx99 Date: Wed, 27 Dec 2023 22:55:35 +0800 Subject: [PATCH] Improve make-pkg.ps1 Add a hack inject dotnet runtime 6.0+ to support create cross platform zip on windows. --- .gitattributes | 2 +- tools/ci/make-pkg.ps1 | 223 +++++++++++++++++++++++++++++++++--------- 2 files changed, 180 insertions(+), 45 deletions(-) diff --git a/.gitattributes b/.gitattributes index ccb61a7574..506d6e2146 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Auto detect text files and perform LF normalization -* text=auto +* text=auto eol=lf # Custom for Visual Studio *.cs diff=csharp *.sln merge=union diff --git a/tools/ci/make-pkg.ps1 b/tools/ci/make-pkg.ps1 index ef86aca766..d6e142a3e5 100644 --- a/tools/ci/make-pkg.ps1 +++ b/tools/ci/make-pkg.ps1 @@ -1,12 +1,10 @@ param( - $version = $null + $version = $null ) -Write-Host "Creating package $pkg_file_path ..." - $AX_ROOT = (Resolve-Path $PSScriptRoot/../..).Path -if(!$version -or ($version -eq 'auto')) { +if (!$version -or ($version -eq 'auto')) { $axver_file = (Resolve-Path $AX_ROOT/core/axmolver.h.in).Path $axver_content = $(Get-Content -Path $axver_file) function parse_axver($part) { @@ -16,7 +14,7 @@ if(!$version -or ($version -eq 'auto')) { $axver = "$(parse_axver 'MAJOR').$(parse_axver 'MINOR').$(parse_axver 'PATCH')" $git_prog = (Get-Command 'git' -ErrorAction SilentlyContinue).Source - if($git_prog) { + if ($git_prog) { $branchName = $(git -C $AX_ROOT branch --show-current) if ($branchName -eq 'dev') { $commitHash = $(git -C $AX_ROOT rev-parse --short=7 HEAD) @@ -51,29 +49,34 @@ $excludes = @( $pkg_file_name = "axmol-$version.zip" $pkg_file_path = $(Join-Path $AX_ROOT $pkg_file_name) +Write-Host "Creating package $pkg_file_path ..." + $compress_args = @{ - Path = $AX_ROOT + Path = $AX_ROOT CompressionLevel = 'Optimal' - DestinationPath = $pkg_file_path + DestinationPath = $pkg_file_path RelativeBasePath = $AX_ROOT - Exclude = $excludes - Prefix = "axmol-$version" + Exclude = $excludes + Prefix = "axmol-$version" } # Compress-Archive @compress -PassThru function Compress-ArchiveEx() { param( - $Path, - $CompressionLevel = 'Optimal', - $DestinationPath, - $Exclude, - $Prefix = '', - $RelativeBasePath = '', - [switch]$Force + $Path, + $CompressionLevel = 'Optimal', + $DestinationPath, + $Exclude, + $Prefix = '', + $RelativeBasePath = '', + [switch]$Force ) - if($RelativeBasePath) { + $Script:S_IFREG = 0x8000 + # $S_IFDIR = 0x4000 + + if ($RelativeBasePath) { Push-Location $RelativeBasePath } @@ -85,69 +88,201 @@ function Compress-ArchiveEx() { Add-Type -AssemblyName System.IO.Compression Add-Type -AssemblyName System.IO.Compression.FileSystem } - $archive = [System.IO.Compression.ZipFile]::Open($DestinationPath, [System.IO.Compression.ZipArchiveMode]::Create) + $pwsh_ver = $PSVersionTable.PSVersion.ToString() + if (([System.Version]$pwsh_ver -ge [System.Version]'7.0.0.0') -and $IsWindows) { + + if (-not ([System.Management.Automation.PSTypeName]'UnixFileStream').Type) { + Add-Type -TypeDefinition @" +// A hack to create unix style .zip on windows +// refers: +// - https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipVersion.cs#L24 +// - https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs#L529C26-L529C50 + +using System.Text; +using System.IO; +using System.IO.Compression; + +public class MyZipFile : ZipArchive +{ + public UnixFileStream Stream { get; set; } + public MyZipFile(UnixFileStream stream, ZipArchiveMode mode, bool leaveOpen) : base(stream, mode, leaveOpen) + { + Stream = stream; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + Stream.IsDisposing = true; + base.Dispose(disposing); + } +} + +public class UnixFileStream : FileStream +{ + internal enum ZipVersionMadeByPlatform : byte + { + Windows = 0, + Unix = 3 + } + + // public const uint DirectoryFileHeaderSignatureConstant = 0x02014B50; + // public const uint LocalFileHeaderSignatureConstant = 0x04034B50; + int m_hints = -1; + int m_hints2 = -1; + + public UnixFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) : base(path, mode, access, share, bufferSize, useAsync) + { + } + + public override void WriteByte(byte value) + { + if (m_hints2 != -1) ++m_hints; + if (IsDisposing) + { + if (m_hints != -1) ++m_hints; + + if (m_hints == 2) + { // hint: CurrentZipPlatform: hack set to unix + value = (byte)ZipVersionMadeByPlatform.Unix; + } + } + base.WriteByte(value); + } + + public override void Write(byte[] array, int offset, int count) + { + if (IsDisposing) + { // hint: entryHeaderSignature + if ((count == 4 && array[0] == 0x50 && array[1] == 0x4b && array[2] == 0x01 && array[3] == 0x02) || m_hints != -1) + ++m_hints; + + if (m_hints == 17) // hint: filepath + { + var path = Encoding.UTF8.GetString(array); + array = Encoding.UTF8.GetBytes(path.Replace('\\', '/')); + m_hints = -1; + } + } + + if ((count == 4 && array[0] == 0x50 && array[1] == 0x4b && array[2] == 0x03 && array[3] == 0x04) || m_hints2 != -1) + ++m_hints2; + + if (m_hints2 == 10) { + var path = Encoding.UTF8.GetString(array); + array = Encoding.UTF8.GetBytes(path.Replace('\\', '/')); + m_hints2 = -1; + } + + base.Write(array, offset, count); + } + + public bool IsDisposing { set; get; } = false; + + public static ZipArchive CreateUnixZipFile(string archiveFileName) + { + var fs = new UnixFileStream(archiveFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 0x1000, useAsync: false); + try + { + return new MyZipFile(fs, ZipArchiveMode.Create, leaveOpen: false); + } + catch + { + fs.Dispose(); + throw; + } + } +} +"@ + } + + $archive = [UnixFileStream]::CreateUnixZipFile($DestinationPath) + } + else { + $archive = [System.IO.Compression.ZipFile]::Open($DestinationPath, [System.IO.Compression.ZipArchiveMode]::Create) + } $compressionLevelValue = @{ - 'Optimal' = [System.IO.Compression.CompressionLevel]::Optimal - 'Fastest' = [System.IO.Compression.CompressionLevel]::Fastest + 'Optimal' = [System.IO.Compression.CompressionLevel]::Optimal + 'Fastest' = [System.IO.Compression.CompressionLevel]::Fastest 'NoCompression' = [System.IO.Compression.CompressionLevel]::NoCompression }[$CompressionLevel] [array]$Excludes = $Exclude [array]$Paths = $Path $_is_exclude = { - param($uxpath) - foreach($exclude in $Excludes) { - if($uxpath -like $exclude) { - return $true - } - } - return $false + param($uxpath) + foreach ($exclude in $Excludes) { + if ($uxpath -like $exclude) { + return $true + } + } + return $false } $Script:total = 0 $_zip_add = { param($archive, $path, $compressionLevel, $prefix) - if(!$path.LinkType) { + if (!$path.LinkType) { # -RelativeBasePath add in powershell 7.4 which github ci is 7.2 not support $rname = $(Resolve-Path -Path $path -Relative).Replace('\', '/') if ($rname.StartsWith('./')) { $rname = $rname.TrimStart('./') } $excluded = (&$_is_exclude -uxpath $rname) - if(!$excluded) { - if($prefix) { $rname = Join-Path $prefix $rname } - + if (!$excluded) { if (!$path.PSIsContainer) { - ++$Script:total - $zentry = $archive.CreateEntry($rname) + Write-Host "a $rname" + # preserve unix file permissions mode + # refer https://github.com/PowerShell/Microsoft.PowerShell.Archive/pull/146/files + $uxmode = $null if ($path.UnixStat) { - # when run on unix, set permissions same with origin file - # refer https://github.com/PowerShell/Microsoft.PowerShell.Archive/pull/146/files - $zentry.ExternalAttributes = ((0x8000 -bor $path.UnixStat.Mode) -shl 16) + $uxmode = $path.UnixStat.Mode + } + else { + $fileext = Split-Path $rname -Extension + if (!$fileext -or $rname.EndsWith('.sh')) { + $filestatus = $(git -C $AX_ROOT ls-files -s $rname) + if ($filestatus) { + $uxmode = [Convert]::ToInt32($filestatus.Split(' ')[0], 8) + } + } } + if (!$uxmode) { + # default unix file permissions + $uxmode = [Convert]::ToInt32('100644', 8) + } + + if ($prefix) { + $rname = Join-Path $prefix $rname + } + $zentry = $archive.CreateEntry($rname) + $zentry.ExternalAttributes = (($Script:S_IFREG -bor $uxmode) -shl 16) $zentryWriter = New-Object -TypeName System.IO.BinaryWriter $zentry.Open() $zentryWriter.Write([System.IO.File]::ReadAllBytes($path)) $zentryWriter.Flush() $zentryWriter.Close() - } else { + + ++$Script:total + } + else { $sub_paths = Get-ChildItem $path - foreach($sub_path in $sub_paths) { + foreach ($sub_path in $sub_paths) { &$_zip_add $archive $sub_path $compressionLevel $prefix } } } else { - Write-Host "x` $path" + Write-Host "x $rname" } } else { - Write-Host "x $path, LinkType=$($Path.LinkType)" + Write-Host "x $rname, LinkType=$($Path.LinkType)" } } # write entries with relative paths as names foreach ($path in $Paths) { - if($path.GetType() -eq [string]) { + if ($path.GetType() -eq [string]) { $path = Get-Item $path } &$_zip_add $archive $path $compressionLevelValue $Prefix @@ -156,7 +291,7 @@ function Compress-ArchiveEx() { # release zip file $archive.Dispose() - if($RelativeBasePath) { + if ($RelativeBasePath) { Pop-Location } @@ -172,10 +307,10 @@ Write-Host "Create package $pkg_file_path done, ${total} files found, MD5: $md5_ Pop-Location -if($env:GITHUB_ACTIONS -eq 'true') { +if ($env:GITHUB_ACTIONS -eq 'true') { $release_note = Join-Path $AX_ROOT "release_note_draft.txt" [System.IO.File]::WriteAllText($release_note, "## MD5 Hash of the release artifacts`n - ``${pkg_file_name}``: $md5_digest") echo "release_tag=v$version" >> ${env:GITHUB_OUTPUT} echo "release_pkg=$pkg_file_name" >> ${env:GITHUB_OUTPUT} echo "release_note=$release_note" >> ${env:GITHUB_OUTPUT} -} +} \ No newline at end of file