Browse Source

Improved extraction handling

pull/55/head
Ionite 1 year ago
parent
commit
bb385d1d1a
No known key found for this signature in database
  1. 40
      StabilityMatrix.Avalonia/Assets.cs
  2. 9
      StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs
  3. 41
      StabilityMatrix.Avalonia/Models/AvaloniaResource.cs
  4. 56
      StabilityMatrix.Core/Helper/ArchiveHelper.cs
  5. 22
      StabilityMatrix.Core/Helper/Compat.cs

40
StabilityMatrix.Avalonia/Assets.cs

@ -3,6 +3,7 @@ using System.IO;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Platform; using Avalonia.Platform;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper;
namespace StabilityMatrix.Avalonia; namespace StabilityMatrix.Avalonia;
@ -17,39 +18,18 @@ internal static class Assets
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")] [SupportedOSPlatform("linux")]
public static Uri SevenZipExecutable public static AvaloniaResource SevenZipExecutable => Compat.Switch(
{ (PlatformKind.Windows,
get new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/win-x64/7za.exe")),
{ (PlatformKind.Linux | PlatformKind.X64,
if (Compat.IsWindows) new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs",
{ UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute)));
return new Uri("avares://StabilityMatrix.Avalonia/Assets/win-x64/7za.exe");
}
if (Compat.Platform.HasFlag(PlatformKind.Linux | PlatformKind.X64))
{
return new Uri("avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs");
}
throw new PlatformNotSupportedException();
}
}
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")] [SupportedOSPlatform("linux")]
public static Uri SevenZipLicense public static AvaloniaResource SevenZipLicense => Compat.Switch(
{ (PlatformKind.Windows, new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/win-x64/7za - LICENSE.txt")),
get (PlatformKind.Linux | PlatformKind.X64, new AvaloniaResource("avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs - LICENSE.txt")));
{
if (Compat.IsWindows)
{
return new Uri("avares://StabilityMatrix.Avalonia/Assets/win-x64/7za - LICENSE.txt");
}
if (Compat.Platform.HasFlag(PlatformKind.Linux | PlatformKind.X64))
{
return new Uri("avares://StabilityMatrix.Avalonia/Assets/linux-x64/7zzs - LICENSE.txt");
}
throw new PlatformNotSupportedException();
}
}
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")] [SupportedOSPlatform("linux")]

9
StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs

@ -5,6 +5,7 @@ using System.Runtime.Versioning;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities;
using NLog; using NLog;
using StabilityMatrix.Core.Exceptions; using StabilityMatrix.Core.Exceptions;
using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper;
@ -57,11 +58,11 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper
}; };
progress?.Report(new ProgressReport(0, message: "Unpacking resources", isIndeterminate: true)); progress?.Report(new ProgressReport(0, message: "Unpacking resources", isIndeterminate: true));
Directory.CreateDirectory(AssetsDir); Directory.CreateDirectory(AssetsDir);
foreach (var (assetUri, extractTo) in assets) foreach (var (asset, extractDir) in assets)
{ {
await Assets.ExtractAsset(assetUri, extractTo); await asset.ExtractTo(extractDir);
} }
progress?.Report(new ProgressReport(1, message: "Unpacking resources", isIndeterminate: false)); progress?.Report(new ProgressReport(1, message: "Unpacking resources", isIndeterminate: false));
@ -112,7 +113,7 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper
await PythonDir.DeleteAsync(true); await PythonDir.DeleteAsync(true);
} }
progress?.Report(new ProgressReport(0, "Installing Python", isIndeterminate: true)); progress?.Report(new ProgressReport(0, "Installing Python", isIndeterminate: true));
await ArchiveHelper.ExtractManaged(downloadPath, PythonDir); await ArchiveHelper.Extract7ZAuto(downloadPath, PythonDir);
// For Linux, move the inner 'python' folder up to the root PythonDir // For Linux, move the inner 'python' folder up to the root PythonDir
if (Compat.IsLinux) if (Compat.IsLinux)

41
StabilityMatrix.Avalonia/Models/AvaloniaResource.cs

@ -0,0 +1,41 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Platform;
using StabilityMatrix.Core.Helper;
namespace StabilityMatrix.Avalonia.Models;
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public readonly record struct AvaloniaResource(
Uri UriPath,
UnixFileMode WriteUnixFileMode = UnixFileMode.None)
{
public string FileName => UriPath.Segments[^1];
public AvaloniaResource(string uriPath, UnixFileMode writeUnixFileMode = UnixFileMode.None)
: this(new Uri(uriPath), writeUnixFileMode)
{
}
/// <summary>
/// Extracts this resource to the output directory.
/// </summary>
public async Task ExtractTo(string outputDir, bool overwrite = true)
{
var targetPath = Path.Combine(outputDir, FileName);
if (File.Exists(targetPath) && !overwrite)
{
return;
}
var stream = AssetLoader.Open(UriPath);
await using var fileStream = File.Create(targetPath);
await stream.CopyToAsync(fileStream);
// Write permissions
if (!Compat.IsWindows && Compat.IsUnix && WriteUnixFileMode != UnixFileMode.None)
{
File.SetUnixFileMode(targetPath, WriteUnixFileMode);
}
}
}

56
StabilityMatrix.Core/Helper/ArchiveHelper.cs

@ -122,6 +122,58 @@ public static partial class ArchiveHelper
var compressed = ulong.Parse(matches[1].Value); var compressed = ulong.Parse(matches[1].Value);
return new ArchiveInfo(size, compressed); return new ArchiveInfo(size, compressed);
} }
/// <summary>
/// Extracts a zipped tar (i.e. '.tar.gz') archive.
/// First extracts the zipped tar, then extracts the tar and removes the tar.
/// </summary>
/// <param name="archivePath"></param>
/// <param name="extractDirectory"></param>
/// <returns></returns>
public static async Task<ArchiveInfo> Extract7ZTar(string archivePath, string extractDirectory)
{
if (!archivePath.EndsWith(".tar.gz"))
{
throw new ArgumentException("Archive must be a zipped tar.");
}
// Extract the tar.gz to tar
await Extract7Z(archivePath, extractDirectory);
// Extract the tar
var tarPath = Path.Combine(extractDirectory, Path.GetFileNameWithoutExtension(archivePath) + ".tar");
if (!File.Exists(tarPath))
{
throw new FileNotFoundException("Tar file not found.", tarPath);
}
try
{
return await Extract7Z(tarPath, extractDirectory);
}
finally
{
// Remove the tar
if (File.Exists(tarPath))
{
File.Delete(tarPath);
}
}
}
/// <summary>
/// Extracts with auto handling of tar.gz files.
/// </summary>
public static async Task<ArchiveInfo> Extract7ZAuto(string archivePath, string extractDirectory)
{
if (archivePath.EndsWith(".tar.gz"))
{
return await Extract7ZTar(archivePath, extractDirectory);
}
else
{
return await Extract7Z(archivePath, extractDirectory);
}
}
/// <summary> /// <summary>
/// Extract an archive to the output directory. /// Extract an archive to the output directory.
@ -212,6 +264,8 @@ public static partial class ArchiveHelper
var entry = reader.Entry; var entry = reader.Entry;
var outputPath = Path.Combine(outputDirectory, entry.Key); var outputPath = Path.Combine(outputDirectory, entry.Key);
Logger.Debug($"Extracting key: {entry.Key.ToRepr()}");
if (entry.IsDirectory) if (entry.IsDirectory)
{ {
if (!Directory.Exists(outputPath)) if (!Directory.Exists(outputPath))
@ -255,7 +309,7 @@ public static partial class ArchiveHelper
// Delete path if exists // Delete path if exists
File.Delete(outputPath); File.Delete(outputPath);
File.CreateSymbolicLink(outputPath, entry.LinkTarget); File.CreateSymbolicLink(outputPath, entry.LinkTarget);
return; continue;
} }
catch (IOException e) catch (IOException e)
{ {

22
StabilityMatrix.Core/Helper/Compat.cs

@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.FileInterfaces;
namespace StabilityMatrix.Core.Helper; namespace StabilityMatrix.Core.Helper;
@ -104,6 +105,27 @@ public static class Compat
AppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); AppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
AppDataHome = AppData + AppName; AppDataHome = AppData + AppName;
} }
/// <summary>
/// Generic function to return different objects based on platform flags.
/// Parameters are checked in sequence with Compat.Platform.HasFlag,
/// the first match is returned.
/// </summary>
/// <exception cref="PlatformNotSupportedException">Thrown when no targets match</exception>
public static T Switch<T>(params (PlatformKind platform, T target)[] targets)
{
foreach (var (platform, target) in targets)
{
if (Platform.HasFlag(platform))
{
return target;
}
}
throw new PlatformNotSupportedException(
$"Platform {Platform.ToString()} is not in supported targets: " +
$"{string.Join(", ", targets.Select(t => t.platform.ToString()))}");
}
/// <summary> /// <summary>
/// Get the current application executable name. /// Get the current application executable name.

Loading…
Cancel
Save