Files
velopack/src/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs
2024-02-02 12:26:31 +00:00

111 lines
4.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using ELFSharp.ELF;
using Microsoft.Extensions.Logging;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Unix.Commands;
[SupportedOSPlatform("linux")]
public class LinuxPackCommandRunner : PackageBuilder<LinuxPackOptions>
{
protected string PortablePackagePath { get; set; }
public LinuxPackCommandRunner(ILogger logger, IFancyConsole console)
: base(RuntimeOs.Linux, logger, console)
{
}
protected override Task<string> PreprocessPackDir(Action<int> progress, string packDir)
{
var dir = TempDir.CreateSubdirectory("PreprocessPackDir.AppDir");
var bin = dir.CreateSubdirectory("usr").CreateSubdirectory("bin");
if (Options.PackIsAppDir) {
Log.Info("Using provided .AppDir, will skip building new one.");
CopyFiles(new DirectoryInfo(Options.PackDirectory), dir, progress, true);
} else {
Log.Info("Building new .AppDir");
var appRunPath = Path.Combine(dir.FullName, "AppRun");
File.WriteAllText(appRunPath, """
#!/bin/sh
HERE="$(dirname "$(readlink -f "${0}")")"
export PATH="${HERE}"/usr/bin/:"${PATH}"
EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1)
exec "${EXEC}" $@
""");
Chmod.ChmodFileAsExecutable(appRunPath);
var mainExeName = Options.EntryExecutableName ?? Options.PackId;
var mainExePath = Path.Combine(packDir, mainExeName);
if (!File.Exists(mainExePath))
throw new Exception($"Could not find main executable at '{mainExePath}'. Please specify with --exeName.");
File.WriteAllText(Path.Combine(dir.FullName, Options.PackId + ".desktop"), $"""
[Desktop Entry]
Type=Application
Name={Options.PackTitle ?? Options.PackId}
Comment={Options.PackTitle ?? Options.PackId} {Options.PackVersion}
Icon={Options.PackId}
Exec={mainExeName}
Path=~
Categories=Development;
""");
// copy existing app files
CopyFiles(new DirectoryInfo(packDir), bin, progress, true);
// app icon
File.Copy(Options.Icon, Path.Combine(dir.FullName, Options.PackId + Path.GetExtension(Options.Icon)), true);
}
// velopack required files
File.WriteAllText(Path.Combine(bin.FullName, "sq.version"), GenerateNuspecContent());
File.Copy(HelperFile.GetUpdatePath(), Path.Combine(bin.FullName, "UpdateNix"), true);
progress(100);
return Task.FromResult(dir.FullName);
}
protected override Task CreatePortablePackage(Action<int> progress, string packDir, string outputPath)
{
progress(-1);
var machine = Options.TargetRuntime.HasArchitecture
? Options.TargetRuntime.Architecture
: GetMachineForBinary(MainExePath);
AppImageTool.CreateLinuxAppImage(packDir, outputPath, machine, Log);
PortablePackagePath = outputPath;
progress(100);
return Task.CompletedTask;
}
protected virtual RuntimeCpu GetMachineForBinary(string path)
{
var elf = ELFReader.Load(path);
var machine = elf.Machine switch {
Machine.AArch64 => RuntimeCpu.arm64,
Machine.AMD64 => RuntimeCpu.x64,
Machine.Intel386 => RuntimeCpu.x86,
_ => throw new Exception($"Unsupported ELF machine type '{elf.Machine}'.")
};
return machine;
}
protected override Task CreateReleasePackage(Action<int> progress, string packDir, string outputPath)
{
var dir = TempDir.CreateSubdirectory("CreateReleasePackage.Linux");
File.Copy(PortablePackagePath, Path.Combine(dir.FullName, Options.PackId + ".AppImage"), true);
return base.CreateReleasePackage(progress, dir.FullName, outputPath);
}
protected override Task<string> CreateDeltaPackage(Action<int> progress, string releasePkg, string prevReleasePkg, string outputPkg, DeltaMode mode)
{
progress(-1); // there is only one "file", so progress will not work
return base.CreateDeltaPackage(progress, releasePkg, prevReleasePkg, outputPkg, mode);
}
}