mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
fix.安装器AOT优化
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sty="using:FluentAvalonia.Styling"
|
||||
xmlns:theme="using:Avalonia.Themes.Fluent"
|
||||
xmlns:fi="using:FluentIcons.Avalonia"
|
||||
x:Class="LanDesktopPLONDS.Installer.App"
|
||||
RequestedThemeVariant="Default">
|
||||
@@ -69,7 +69,7 @@
|
||||
</Application.Resources>
|
||||
|
||||
<Application.Styles>
|
||||
<sty:FluentAvaloniaTheme />
|
||||
<theme:FluentTheme />
|
||||
<Style Selector="Window">
|
||||
<Setter Property="FontFamily" Value="{DynamicResource AppFontFamily}" />
|
||||
</Style>
|
||||
|
||||
45
LanDesktopPLONDS.installer/Compress-NativeLibrary.ps1
Normal file
45
LanDesktopPLONDS.installer/Compress-NativeLibrary.ps1
Normal file
@@ -0,0 +1,45 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $SourcePath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $DestinationPath
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$source = Get-Item -LiteralPath $SourcePath
|
||||
$destinationDirectory = Split-Path -Parent $DestinationPath
|
||||
New-Item -ItemType Directory -Path $destinationDirectory -Force | Out-Null
|
||||
|
||||
$existing = Get-Item -LiteralPath $DestinationPath -ErrorAction SilentlyContinue
|
||||
if ($existing -and $existing.LastWriteTimeUtc -ge $source.LastWriteTimeUtc -and $existing.Length -gt 0) {
|
||||
return
|
||||
}
|
||||
|
||||
$temporaryPath = "$DestinationPath.$PID.tmp"
|
||||
if (Test-Path -LiteralPath $temporaryPath) {
|
||||
Remove-Item -LiteralPath $temporaryPath -Force
|
||||
}
|
||||
|
||||
$inputStream = [System.IO.File]::OpenRead($source.FullName)
|
||||
try {
|
||||
$outputStream = [System.IO.File]::Create($temporaryPath)
|
||||
try {
|
||||
$gzipStream = New-Object System.IO.Compression.GZipStream($outputStream, [System.IO.Compression.CompressionMode]::Compress)
|
||||
try {
|
||||
$inputStream.CopyTo($gzipStream)
|
||||
}
|
||||
finally {
|
||||
$gzipStream.Dispose()
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$outputStream.Dispose()
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$inputStream.Dispose()
|
||||
}
|
||||
|
||||
Move-Item -LiteralPath $temporaryPath -Destination $DestinationPath -Force
|
||||
@@ -19,12 +19,52 @@
|
||||
<ItemGroup Condition="'$(PublishAot)' == 'true'">
|
||||
<TrimmerRootAssembly Include="Avalonia" />
|
||||
<TrimmerRootAssembly Include="Avalonia.Desktop" />
|
||||
<TrimmerRootAssembly Include="FluentAvalonia" />
|
||||
<TrimmerRootAssembly Include="Avalonia.Themes.Fluent" />
|
||||
<TrimmerRootAssembly Include="FluentIcons.Avalonia" />
|
||||
<TrimmerRootAssembly Include="LanDesktopPLONDS.installer" />
|
||||
<TrimmerRootAssembly Include="System.Text.Json" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target
|
||||
Name="PrepareInstallerEmbeddedNativeLibraries"
|
||||
BeforeTargets="AssignTargetPaths"
|
||||
Condition="'$(PublishAot)' == 'true' and '$(RuntimeIdentifier)' == 'win-x64'">
|
||||
<ItemGroup>
|
||||
<InstallerNativeLibrary
|
||||
Include="$(PkgAvalonia_Angle_Windows_Natives)\runtimes\win-x64\native\av_libglesv2.dll"
|
||||
CompressedName="av_libglesv2.dll.gz"
|
||||
Condition="Exists('$(PkgAvalonia_Angle_Windows_Natives)\runtimes\win-x64\native\av_libglesv2.dll')" />
|
||||
<InstallerNativeLibrary
|
||||
Include="$(PkgHarfBuzzSharp_NativeAssets_Win32)\runtimes\win-x64\native\libHarfBuzzSharp.dll"
|
||||
CompressedName="libHarfBuzzSharp.dll.gz"
|
||||
Condition="Exists('$(PkgHarfBuzzSharp_NativeAssets_Win32)\runtimes\win-x64\native\libHarfBuzzSharp.dll')" />
|
||||
<InstallerNativeLibrary
|
||||
Include="$(PkgSkiaSharp_NativeAssets_Win32)\runtimes\win-x64\native\libSkiaSharp.dll"
|
||||
CompressedName="libSkiaSharp.dll.gz"
|
||||
Condition="Exists('$(PkgSkiaSharp_NativeAssets_Win32)\runtimes\win-x64\native\libSkiaSharp.dll')" />
|
||||
</ItemGroup>
|
||||
|
||||
<Error
|
||||
Condition="'@(InstallerNativeLibrary)' == ''"
|
||||
Text="NativeAOT installer native libraries were not found. Restore the installer with -p:PublishAot=true -r win-x64 before publishing." />
|
||||
|
||||
<MakeDir Directories="$(IntermediateOutputPath)embedded-native\$(RuntimeIdentifier)\" />
|
||||
<Exec
|
||||
Command="powershell -NoProfile -ExecutionPolicy Bypass -File "$(MSBuildThisFileDirectory)Compress-NativeLibrary.ps1" -SourcePath "%(InstallerNativeLibrary.FullPath)" -DestinationPath "$(IntermediateOutputPath)embedded-native\$(RuntimeIdentifier)\%(InstallerNativeLibrary.CompressedName)"" />
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource
|
||||
Include="$(IntermediateOutputPath)embedded-native\$(RuntimeIdentifier)\av_libglesv2.dll.gz"
|
||||
LogicalName="LanDesktopPLONDS.Installer.NativeLibraries.av_libglesv2.dll.gz" />
|
||||
<EmbeddedResource
|
||||
Include="$(IntermediateOutputPath)embedded-native\$(RuntimeIdentifier)\libHarfBuzzSharp.dll.gz"
|
||||
LogicalName="LanDesktopPLONDS.Installer.NativeLibraries.libHarfBuzzSharp.dll.gz" />
|
||||
<EmbeddedResource
|
||||
Include="$(IntermediateOutputPath)embedded-native\$(RuntimeIdentifier)\libSkiaSharp.dll.gz"
|
||||
LogicalName="LanDesktopPLONDS.Installer.NativeLibraries.libSkiaSharp.dll.gz" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<PropertyGroup Condition="'$(PublishAot)' == 'true'">
|
||||
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
|
||||
<TrimmerSingleWarn>false</TrimmerSingleWarn>
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" />
|
||||
<PackageReference Include="Avalonia.Angle.Windows.Natives" GeneratePathProperty="true" PrivateAssets="all" />
|
||||
<PackageReference Include="Avalonia.Desktop" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" />
|
||||
<PackageReference Include="FluentAvaloniaUI" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" />
|
||||
<PackageReference Include="FluentIcons.Avalonia" />
|
||||
<PackageReference Include="HarfBuzzSharp.NativeAssets.Win32" GeneratePathProperty="true" PrivateAssets="all" />
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Win32" GeneratePathProperty="true" PrivateAssets="all" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
170
LanDesktopPLONDS.installer/NativeDependencyBootstrapper.cs
Normal file
170
LanDesktopPLONDS.installer/NativeDependencyBootstrapper.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Compression;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LanDesktopPLONDS.Installer;
|
||||
|
||||
internal static class NativeDependencyBootstrapper
|
||||
{
|
||||
private const string CacheRootEnvironmentVariable = "LANDESKTOPPLONDS_INSTALLER_NATIVE_CACHE";
|
||||
private const string ResourcePrefix = "LanDesktopPLONDS.Installer.NativeLibraries.";
|
||||
|
||||
private static readonly string[] NativeLibraryNames =
|
||||
[
|
||||
"av_libglesv2.dll",
|
||||
"libHarfBuzzSharp.dll",
|
||||
"libSkiaSharp.dll"
|
||||
];
|
||||
|
||||
public static void Prepare()
|
||||
{
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var nativeDirectory = GetNativeDirectory();
|
||||
Directory.CreateDirectory(nativeDirectory);
|
||||
|
||||
var extractedLibraries = new List<string>(NativeLibraryNames.Length);
|
||||
foreach (var libraryName in NativeLibraryNames)
|
||||
{
|
||||
extractedLibraries.Add(ExtractLibrary(nativeDirectory, libraryName));
|
||||
}
|
||||
|
||||
AddToProcessDllSearchPath(nativeDirectory);
|
||||
|
||||
foreach (var libraryPath in extractedLibraries)
|
||||
{
|
||||
NativeLibrary.Load(libraryPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetNativeDirectory()
|
||||
{
|
||||
var configuredCacheRoot = Environment.GetEnvironmentVariable(CacheRootEnvironmentVariable);
|
||||
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
var cacheRoot = !string.IsNullOrWhiteSpace(configuredCacheRoot)
|
||||
? configuredCacheRoot
|
||||
: string.IsNullOrWhiteSpace(localAppData)
|
||||
? Path.GetTempPath()
|
||||
: localAppData;
|
||||
|
||||
string? versionStamp = null;
|
||||
if (!string.IsNullOrWhiteSpace(Environment.ProcessPath))
|
||||
{
|
||||
versionStamp = FileVersionInfo.GetVersionInfo(Environment.ProcessPath).ProductVersion;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(versionStamp))
|
||||
{
|
||||
versionStamp = "dev";
|
||||
}
|
||||
|
||||
return Path.Combine(
|
||||
cacheRoot,
|
||||
"LanDesktopPLONDS",
|
||||
"Installer",
|
||||
"native",
|
||||
RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(),
|
||||
SanitizePathSegment(versionStamp));
|
||||
}
|
||||
|
||||
private static string ExtractLibrary(string nativeDirectory, string libraryName)
|
||||
{
|
||||
var resourceName = ResourcePrefix + libraryName + ".gz";
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
using var resource = assembly.GetManifestResourceStream(resourceName);
|
||||
if (resource is null)
|
||||
{
|
||||
var availableResources = string.Join(", ", assembly.GetManifestResourceNames());
|
||||
throw new FileNotFoundException(
|
||||
$"Missing embedded native installer library resource '{resourceName}'. Available resources: {availableResources}");
|
||||
}
|
||||
|
||||
var destinationPath = Path.Combine(nativeDirectory, libraryName);
|
||||
var temporaryPath = destinationPath + "." + Guid.NewGuid().ToString("N") + ".tmp";
|
||||
using (var gzip = new GZipStream(resource, CompressionMode.Decompress))
|
||||
using (var output = File.Create(temporaryPath))
|
||||
{
|
||||
gzip.CopyTo(output);
|
||||
}
|
||||
|
||||
if (File.Exists(destinationPath) && FilesEqual(destinationPath, temporaryPath))
|
||||
{
|
||||
File.Delete(temporaryPath);
|
||||
return destinationPath;
|
||||
}
|
||||
|
||||
File.Move(temporaryPath, destinationPath, overwrite: true);
|
||||
return destinationPath;
|
||||
}
|
||||
|
||||
private static void AddToProcessDllSearchPath(string nativeDirectory)
|
||||
{
|
||||
var currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
|
||||
if (!currentPath.Contains(nativeDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Environment.SetEnvironmentVariable("PATH", nativeDirectory + Path.PathSeparator + currentPath);
|
||||
}
|
||||
|
||||
if (!SetDllDirectory(nativeDirectory))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastPInvokeError(), "Failed to update the process native DLL search path.");
|
||||
}
|
||||
}
|
||||
|
||||
private static string SanitizePathSegment(string value)
|
||||
{
|
||||
foreach (var invalidChar in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
value = value.Replace(invalidChar, '_');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static bool FilesEqual(string leftPath, string rightPath)
|
||||
{
|
||||
var left = new FileInfo(leftPath);
|
||||
var right = new FileInfo(rightPath);
|
||||
if (left.Length != right.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using var leftStream = File.OpenRead(leftPath);
|
||||
using var rightStream = File.OpenRead(rightPath);
|
||||
var leftBuffer = new byte[81920];
|
||||
var rightBuffer = new byte[81920];
|
||||
|
||||
while (true)
|
||||
{
|
||||
var leftRead = leftStream.Read(leftBuffer, 0, leftBuffer.Length);
|
||||
var rightRead = rightStream.Read(rightBuffer, 0, rightBuffer.Length);
|
||||
if (leftRead != rightRead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (leftRead == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < leftRead; i++)
|
||||
{
|
||||
if (leftBuffer[i] != rightBuffer[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32", EntryPoint = "SetDllDirectoryW", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool SetDllDirectory(string pathName);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ public static class Program
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
NativeDependencyBootstrapper.Prepare();
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user