Add Linux Arm64 support

This commit is contained in:
Caelan Sayler
2024-06-14 22:36:16 +01:00
parent d6c4974d3d
commit a8e40defb8
11 changed files with 119 additions and 61 deletions

View File

@@ -10,6 +10,7 @@ indent_style = space
# ReSharper properties
resharper_csharp_wrap_after_invocation_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_indent_raw_literal_string = indent
# MSBuild files
[*.{csproj,targets,props}]
@@ -138,6 +139,19 @@ dotnet_diagnostic.il3000.severity = error
dotnet_diagnostic.il3001.severity = error
dotnet_diagnostic.il3002.severity = error
dotnet_diagnostic.il3003.severity = error
# Resharper
resharper_place_accessorholder_attribute_on_same_line = false
resharper_place_accessor_attribute_on_same_line = false
resharper_place_event_attribute_on_same_line = false
resharper_place_field_attribute_on_same_line = false
resharper_place_method_attribute_on_same_line = false
resharper_place_property_attribute_on_same_line = false
resharper_place_record_field_attribute_on_same_line = false
resharper_place_type_attribute_on_same_line = false
csharp_max_attribute_length_for_same_line = 0
###############################
# VB Coding Conventions #
###############################

View File

@@ -17,16 +17,15 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dotnet/nbgv@master
with:
setAllVars: true
- uses: Swatinem/rust-cache@v2
with:
key: "rust-test-${{ matrix.os }}"
workspaces: "src/Rust -> target"
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Install NBGV
run: dotnet tool install -g nbgv
if: startsWith(matrix.os, 'macos-')
continue-on-error: true
- name: Test Rust
working-directory: src/Rust
run: cargo llvm-cov ${{ matrix.rust_flags }} --cobertura --output-path ../../test/coverage.rust.${{ matrix.os }}.xml
@@ -42,6 +41,9 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dotnet/nbgv@master
with:
setAllVars: true
- uses: Swatinem/rust-cache@v2
with:
key: "rust-build-windows"
@@ -70,20 +72,33 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dotnet/nbgv@master
id: nbgv
with:
setAllVars: true
- uses: Swatinem/rust-cache@v2
with:
key: "rust-build-linux"
workspaces: "src/Rust -> target"
- name: Build Rust
- name: Build Rust (x64)
working-directory: src/Rust
run: |
cargo build --release --target x86_64-unknown-linux-gnu
cp ./target/x86_64-unknown-linux-gnu/release/update ./target/release/UpdateNix
cp ./target/x86_64-unknown-linux-gnu/release/update ./target/release/UpdateNix_x64
- name: Build Rust (arm64)
working-directory: src/Rust
env:
CROSS_NuGetPackageVersion: ${{ steps.nbgv.outputs.NuGetPackageVersion }}
run: |
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo binstall cross --no-confirm
cross build --release --target aarch64-unknown-linux-gnu
cp ./target/aarch64-unknown-linux-gnu/release/update ./target/release/UpdateNix_arm64
- name: Upload Rust Build Artifacts
uses: actions/upload-artifact@v4
with:
name: rust-ubuntu-latest
path: src/Rust/target/release/UpdateNix
path: src/Rust/target/release/UpdateNix*
- name: Cancel workflow if failed
uses: andymckay/cancel-action@0.4
if: ${{ failure() }}
@@ -97,13 +112,13 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dotnet/nbgv@master
with:
setAllVars: true
- uses: Swatinem/rust-cache@v2
with:
key: "rust-build-${{ matrix.os }}"
workspaces: "src/Rust -> target"
- name: Install NBGV
run: dotnet tool install -g nbgv
continue-on-error: true
- name: Build Rust
working-directory: src/Rust
run: |
@@ -170,10 +185,6 @@ jobs:
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Install NBGV
run: dotnet tool install -g nbgv
if: ${{ matrix.os == 'macos-latest' }}
continue-on-error: true
- name: Install FUSE
run: |
sudo add-apt-repository universe

View File

@@ -43,7 +43,8 @@
<None Include="..\Rust\target\release\setup.exe" Pack="true" PackagePath="vendor" />
<None Include="..\Rust\target\release\stub.exe" Pack="true" PackagePath="vendor" />
<None Include="..\Rust\target\release\UpdateMac" Pack="true" PackagePath="vendor" />
<None Include="..\Rust\target\release\UpdateNix" Pack="true" PackagePath="vendor" />
<None Include="..\Rust\target\release\UpdateNix_x64" Pack="true" PackagePath="vendor" />
<None Include="..\Rust\target\release\UpdateNix_arm64" Pack="true" PackagePath="vendor" />
</ItemGroup>
</Target>

5
src/Rust/Cross.toml Normal file
View File

@@ -0,0 +1,5 @@
[target.aarch64-unknown-linux-gnu]
pre-build = [
"dpkg --add-architecture $CROSS_DEB_ARCH",
"apt-get update && apt-get install -y libssl-dev:$CROSS_DEB_ARCH",
]

View File

@@ -1,6 +1,7 @@
#![allow(unused_variables)]
use semver;
use std::process::Command;
use std::env;
#[cfg(target_os = "windows")]
extern crate winres;
@@ -12,9 +13,7 @@ fn main() {
#[cfg(target_os = "windows")]
delay_load();
let ver_output = Command::new("nbgv").args(&["get-version", "-v", "NuGetPackageVersion"]).output().expect("Failed to execute nbgv get-version");
let version = String::from_utf8(ver_output.stdout).expect("Unable to convert ngbv output to string");
let version = version.trim();
let version = get_package_version();
let ver = semver::Version::parse(&version).expect("Unable to parse ngbv output as semver version");
let ver: u64 = ver.major << 48 | ver.minor << 32 | ver.patch << 16;
let desc = format!("Velopack {}", version);
@@ -28,13 +27,32 @@ fn main() {
.set_version_info(winres::VersionInfo::FILEVERSION, ver)
.set("CompanyName", "Velopack")
.set("ProductName", "Velopack")
.set("ProductVersion", version)
.set("ProductVersion", &version)
.set("FileDescription", &desc)
.set("LegalCopyright", "Caelan Sayler (c) 2023, Velopack Ltd. (c) 2024")
.compile()
.unwrap();
}
fn get_package_version() -> String {
if let Ok(version) = env::var("NuGetPackageVersion") {
// NuGetPackageVersion is set, return it trimmed
return version.trim().to_string();
} else if let Ok(version) = env::var("NBGV_NuGetPackageVersion") {
// NBGV_NuGetPackageVersion is set, return it trimmed
return version.trim().to_string();
} else if let Ok(version) = env::var("CROSS_NuGetPackageVersion") {
// NBGV_NuGetPackageVersion is set, return it trimmed
return version.trim().to_string();
} else if env::var("CI").is_ok() {
// CI is set, NuGetPackageVersion should be set always in CI
panic!("Error: NuGetPackageVersion must be set in CI");
} else {
// CI is not set, return "v0.0.0-local"
return "0.0.0-local".to_string();
}
}
#[cfg(target_os = "windows")]
fn delay_load() {
delay_load_exe("update");

View File

@@ -1,5 +1,3 @@
use anyhow::Result;
#[derive(PartialEq, Debug, Clone, strum::IntoStaticStr)]
pub enum RuntimeArch {
X86,
@@ -86,7 +84,7 @@ type IsWow64Process2Fn = unsafe extern "system" fn(
) -> windows::Win32::Foundation::BOOL;
#[cfg(target_os = "windows")]
unsafe fn is_wow64_process2(handle: windows::Win32::Foundation::HANDLE) -> Result<u16> {
unsafe fn is_wow64_process2(handle: windows::Win32::Foundation::HANDLE) -> anyhow::Result<u16> {
use windows::Win32::Foundation::TRUE;
use windows::Win32::System::SystemInformation::IMAGE_FILE_MACHINE;

View File

@@ -35,18 +35,18 @@ public class LinuxPackCommandRunner : PackageBuilder<LinuxPackOptions>
: Options.Categories.TrimEnd(';');
File.WriteAllText(appRunPath, $$"""
#!/bin/sh
if [ ! -z "$APPIMAGE" ] && [ ! -z "$APPDIR" ]; then
MD5=$(echo -n "file://$APPIMAGE" | md5sum | cut -d' ' -f1)
cp "$APPDIR/{{iconFilename}}" "$HOME/.cache/thumbnails/normal/$MD5.png" >/dev/null 2>&1
cp "$APPDIR/{{iconFilename}}" "$HOME/.cache/thumbnails/large/$MD5.png" >/dev/null 2>&1
xdg-icon-resource forceupdate >/dev/null 2>&1
fi
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 | sed 's/\\s/ /g')
exec "${EXEC}" "$@"
""".Replace("\r", ""));
#!/bin/sh
if [ ! -z "$APPIMAGE" ] && [ ! -z "$APPDIR" ]; then
MD5=$(echo -n "file://$APPIMAGE" | md5sum | cut -d' ' -f1)
cp "$APPDIR/{{iconFilename}}" "$HOME/.cache/thumbnails/normal/$MD5.png" >/dev/null 2>&1
cp "$APPDIR/{{iconFilename}}" "$HOME/.cache/thumbnails/large/$MD5.png" >/dev/null 2>&1
xdg-icon-resource forceupdate >/dev/null 2>&1
fi
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 | sed 's/\\s/ /g')
exec "${EXEC}" "$@"
""".Replace("\r", ""));
Chmod.ChmodFileAsExecutable(appRunPath);
var mainExeName = Options.EntryExecutableName ?? Options.PackId;
@@ -59,23 +59,28 @@ exec "${EXEC}" "$@"
mainExeName = mainExeName.Replace(" ", "\\s");
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}
StartupWMClass={Options.PackId}
Categories={categories};
""".Replace("\r", ""));
[Desktop Entry]
Type=Application
Name={Options.PackTitle ?? Options.PackId}
Comment={Options.PackTitle ?? Options.PackId} {Options.PackVersion}
Icon={Options.PackId}
Exec={mainExeName}
StartupWMClass={Options.PackId}
Categories={categories};
""".Replace("\r", ""));
// copy existing app files
CopyFiles(new DirectoryInfo(packDir), bin, progress, true);
}
Options.TargetRuntime.Architecture = Options.TargetRuntime.HasArchitecture
? Options.TargetRuntime.Architecture
: GetMachineForBinary(MainExePath);
// velopack required files
File.WriteAllText(Path.Combine(bin.FullName, "sq.version"), GenerateNuspecContent());
File.Copy(HelperFile.GetUpdatePath(RuntimeOs.Linux), Path.Combine(bin.FullName, "UpdateNix"), true);
File.Copy(HelperFile.GetUpdatePath(Options.TargetRuntime, Log), Path.Combine(bin.FullName, "UpdateNix"), true);
progress(100);
return Task.FromResult(dir.FullName);
}
@@ -91,10 +96,7 @@ Categories={categories};
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);
AppImageTool.CreateLinuxAppImage(packDir, outputPath, Options.TargetRuntime.Architecture, Log);
PortablePackagePath = outputPath;
progress(100);
return Task.CompletedTask;

View File

@@ -32,7 +32,7 @@ public class OsxPackCommandRunner : PackageBuilder<OsxPackOptions>
var structure = new OsxStructureBuilder(dir.FullName);
var macosdir = structure.MacosDirectory;
File.WriteAllText(Path.Combine(macosdir, "sq.version"), GenerateNuspecContent());
File.Copy(HelperFile.GetUpdatePath(RuntimeOs.OSX), Path.Combine(macosdir, "UpdateMac"), true);
File.Copy(HelperFile.GetUpdatePath(Options.TargetRuntime, Log), Path.Combine(macosdir, "UpdateMac"), true);
foreach (var f in Directory.GetFiles(macosdir, "*", SearchOption.AllDirectories)) {
if (BinDetect.IsMachOImage(f)) {

View File

@@ -46,7 +46,7 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
packDir = dir.FullName;
var updatePath = Path.Combine(TempDir.FullName, "Update.exe");
File.Copy(HelperFile.GetUpdatePath(RuntimeOs.Windows), updatePath, true);
File.Copy(HelperFile.GetUpdatePath(Options.TargetRuntime, Log), updatePath, true);
// check for and delete clickonce manifest
var clickonceManifests = Directory.EnumerateFiles(packDir, "*.application")

View File

@@ -1,12 +1,13 @@
using System.Runtime.Versioning;
using Microsoft.Extensions.Logging;
namespace Velopack.Packaging;
public static class HelperFile
{
public static string GetUpdateExeName(RuntimeOs os)
private static string GetUpdateExeName(RID target, ILogger log)
{
switch (os) {
switch (target.BaseRID) {
case RuntimeOs.Windows:
return FindHelperFile("update.exe");
#if DEBUG
@@ -16,16 +17,25 @@ public static class HelperFile
return FindHelperFile("update");
#else
case RuntimeOs.Linux:
return FindHelperFile("UpdateNix");
if (!target.HasArchitecture) {
log.Warn("No architecture specified with --runtime, defaulting to x64. If this was not intended please specify via the --runtime parameter");
return FindHelperFile("UpdateNix_x64");
}
return target.Architecture switch {
RuntimeCpu.arm64 => FindHelperFile("UpdateNix_arm64"),
RuntimeCpu.x64 => FindHelperFile("UpdateNix_x64"),
_ => throw new PlatformNotSupportedException($"Update binary is not available for this platform ({target}).")
};
case RuntimeOs.OSX:
return FindHelperFile("UpdateMac");
#endif
default:
throw new PlatformNotSupportedException("Update binary is not available for this platform.");
}
throw new PlatformNotSupportedException($"Update binary is not available for this platform ({target}).");
}
public static string GetUpdatePath(RuntimeOs os) => FindHelperFile(GetUpdateExeName(os));
public static string GetUpdatePath(RID target, ILogger log) => FindHelperFile(GetUpdateExeName(target, log));
public static string GetZstdPath()
{
@@ -73,7 +83,7 @@ public static class HelperFile
}
}
private static List<string> _searchPaths = new List<string>();
private static readonly List<string> _searchPaths = new List<string>();
static HelperFile()
{

View File

@@ -67,7 +67,6 @@ public abstract class PackageBuilder<T> : ICommand<T>
var packId = options.PackId;
var packDirectory = options.PackDirectory;
var packVersion = options.PackVersion;
var semVer = SemanticVersion.Parse(packVersion);
// check that entry exe exists
var mainExeName = options.EntryExecutableName ?? options.PackId;