Skip to content

Commit 0c863e9

Browse files
authored
Use architecture-based flags for Windows EXE and macOS DMG packaging (#123)
* Use architecture options for Windows and macOS packaging * Fix architecture parsing and RID binding for project commands
1 parent e0ec167 commit 0c863e9

File tree

3 files changed

+109
-20
lines changed

3 files changed

+109
-20
lines changed

src/DotnetPackaging.Exe/ExePackagingService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ private static Result<string> DetermineRuntimeIdentifier(Maybe<string> rid)
335335
return Result.Success(RuntimeInformation.OSArchitecture == RuntimeArchitecture.Arm64 ? "win-arm64" : "win-x64");
336336
}
337337

338-
return Result.Failure<string>("--rid is required when building EXE on non-Windows hosts (e.g., win-x64/win-arm64).");
338+
return Result.Failure<string>("--arch is required when building EXE on non-Windows hosts (x64/arm64).");
339339
}
340340

341341
private async Task<Result<Maybe<string>>> TryResolveLocalStub(string rid)

src/DotnetPackaging.Tool/Program.cs

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using Zafiro.FileSystem.Core;
2323
using DotnetPackaging.Exe;
2424
using System.Threading;
25+
using RuntimeArchitecture = System.Runtime.InteropServices.Architecture;
2526

2627
namespace DotnetPackaging.Tool;
2728

@@ -171,9 +172,9 @@ await ExecuteWithLogging("dmg-verify", file.FullName, async logger =>
171172
{
172173
Description = "Path to the prebuilt stub (WinExe) to concatenate (optional if repo layout is present)"
173174
};
174-
var exRidTop = new Option<string?>("--rid")
175+
var exArchTop = new Option<string?>("--arch")
175176
{
176-
Description = "Runtime identifier for the stub (win-x64, win-arm64)"
177+
Description = "Target architecture for the stub (x64, arm64)"
177178
};
178179

179180
// Reuse metadata options
@@ -247,7 +248,7 @@ await ExecuteWithLogging("dmg-verify", file.FullName, async logger =>
247248
exeCommand.Add(exAppId);
248249
exeCommand.Add(exVendor);
249250
exeCommand.Add(exExecutableName);
250-
exeCommand.Add(exRidTop);
251+
exeCommand.Add(exArchTop);
251252

252253
exeCommand.SetAction(async parseResult =>
253254
{
@@ -256,11 +257,18 @@ await ExecuteWithLogging("dmg-verify", file.FullName, async logger =>
256257
var stub = parseResult.GetValue(stubPath);
257258
var opt = optionsBinder.Bind(parseResult);
258259
var vendorOpt = parseResult.GetValue(exVendor);
259-
var ridOpt = parseResult.GetValue(exRidTop);
260+
var archOpt = parseResult.GetValue(exArchTop);
261+
var ridResult = ResolveWindowsRid(archOpt, "EXE packaging");
262+
if (ridResult.IsFailure)
263+
{
264+
Console.Error.WriteLine(ridResult.Error);
265+
Environment.ExitCode = 1;
266+
return;
267+
}
260268
await ExecuteWithLogging("exe", outFile.FullName, async logger =>
261269
{
262270
var exeService = new ExePackagingService(logger);
263-
var result = await exeService.BuildFromDirectory(inDir, outFile, opt, vendorOpt, ridOpt, stub);
271+
var result = await exeService.BuildFromDirectory(inDir, outFile, opt, vendorOpt, ridResult.Value, stub);
264272
if (result.IsFailure)
265273
{
266274
logger.Error("EXE packaging failed: {Error}", result.Error);
@@ -279,9 +287,9 @@ await ExecuteWithLogging("exe", outFile.FullName, async logger =>
279287
Description = "Path to the .csproj file",
280288
Required = true
281289
};
282-
var exRid = new Option<string?>("--rid")
290+
var exArch = new Option<string?>("--arch")
283291
{
284-
Description = "Runtime identifier (e.g. win-x64, win-arm64)"
292+
Description = "Target architecture (x64, arm64)"
285293
};
286294
var exSelfContained = new Option<bool>("--self-contained")
287295
{
@@ -313,7 +321,7 @@ await ExecuteWithLogging("exe", outFile.FullName, async logger =>
313321

314322
var exFromProject = new Command("from-project") { Description = "Publish a .NET project and build a Windows self-extracting installer (.exe). If --stub is not provided, the tool downloads the appropriate stub from GitHub Releases." };
315323
exFromProject.Add(exProject);
316-
exFromProject.Add(exRid);
324+
exFromProject.Add(exArch);
317325
exFromProject.Add(exSelfContained);
318326
exFromProject.Add(exConfiguration);
319327
exFromProject.Add(exSingleFile);
@@ -324,7 +332,14 @@ await ExecuteWithLogging("exe", outFile.FullName, async logger =>
324332
exFromProject.SetAction(async parseResult =>
325333
{
326334
var prj = parseResult.GetValue(exProject)!;
327-
var ridVal = parseResult.GetValue(exRid);
335+
var archVal = parseResult.GetValue(exArch);
336+
var ridResult = ResolveWindowsRid(archVal, "EXE packaging");
337+
if (ridResult.IsFailure)
338+
{
339+
Console.Error.WriteLine(ridResult.Error);
340+
Environment.ExitCode = 1;
341+
return;
342+
}
328343
var sc = parseResult.GetValue(exSelfContained);
329344
var cfg = parseResult.GetValue(exConfiguration)!;
330345
var sf = parseResult.GetValue(exSingleFile);
@@ -336,7 +351,7 @@ await ExecuteWithLogging("exe", outFile.FullName, async logger =>
336351
await ExecuteWithLogging("exe-from-project", extrasOutput.FullName, async logger =>
337352
{
338353
var exeService = new ExePackagingService(logger);
339-
var result = await exeService.BuildFromProject(prj, ridVal, sc, cfg, sf, tr, extrasOutput, opt, vendorOpt, extrasStub);
354+
var result = await exeService.BuildFromProject(prj, ridResult.Value, sc, cfg, sf, tr, extrasOutput, opt, vendorOpt, extrasStub);
340355
if (result.IsFailure)
341356
{
342357
logger.Error("EXE from project packaging failed: {Error}", result.Error);
@@ -1493,13 +1508,13 @@ private static void AddRpmFromProjectSubcommand(Command rpmCommand)
14931508
fromProject.SetAction(async parseResult =>
14941509
{
14951510
var prj = parseResult.GetValue(project)!;
1496-
var ridVal = parseResult.GetValue(rid);
14971511
var sc = parseResult.GetValue(selfContained);
14981512
var cfg = parseResult.GetValue(configuration)!;
14991513
var sf = parseResult.GetValue(singleFile);
15001514
var tr = parseResult.GetValue(trimmed);
15011515
var outFile = parseResult.GetValue(output)!;
15021516
var opt = optionsBinder.Bind(parseResult);
1517+
var ridVal = parseResult.GetValue(rid);
15031518

15041519
if (string.IsNullOrWhiteSpace(ridVal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
15051520
{
@@ -1606,13 +1621,13 @@ private static void AddDebFromProjectSubcommand(Command debCommand)
16061621
fromProject.SetAction(async parseResult =>
16071622
{
16081623
var prj = parseResult.GetValue(project)!;
1609-
var ridVal = parseResult.GetValue(rid);
16101624
var sc = parseResult.GetValue(selfContained);
16111625
var cfg = parseResult.GetValue(configuration)!;
16121626
var sf = parseResult.GetValue(singleFile);
16131627
var tr = parseResult.GetValue(trimmed);
16141628
var outFile = parseResult.GetValue(output)!;
16151629
var opt = optionsBinder.Bind(parseResult);
1630+
var ridVal = parseResult.GetValue(rid);
16161631

16171632
if (string.IsNullOrWhiteSpace(ridVal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
16181633
{
@@ -1701,12 +1716,12 @@ await DotnetPackaging.Msix.Msix.FromDirectory(container, Maybe<Serilog.ILogger>.
17011716
fromProject.SetAction(async parseResult =>
17021717
{
17031718
var prj = parseResult.GetValue(project)!;
1704-
var ridVal = parseResult.GetValue(rid);
17051719
var sc = parseResult.GetValue(selfContained);
17061720
var cfg = parseResult.GetValue(configuration)!;
17071721
var sf = parseResult.GetValue(singleFile);
17081722
var tr = parseResult.GetValue(trimmed);
17091723
var outFile = parseResult.GetValue(outMsix)!;
1724+
var ridVal = parseResult.GetValue(rid);
17101725

17111726
if (string.IsNullOrWhiteSpace(ridVal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
17121727
{
@@ -1798,13 +1813,13 @@ private static void AddAppImageFromProjectSubcommand(Command appImageCommand)
17981813
fromProject.SetAction(async parseResult =>
17991814
{
18001815
var prj = parseResult.GetValue(project)!;
1801-
var ridVal = parseResult.GetValue(rid);
18021816
var sc = parseResult.GetValue(selfContained);
18031817
var cfg = parseResult.GetValue(configuration)!;
18041818
var sf = parseResult.GetValue(singleFile);
18051819
var tr = parseResult.GetValue(trimmed);
18061820
var outFile = parseResult.GetValue(output)!;
18071821
var opt = optionsBinder.Bind(parseResult);
1822+
var ridVal = parseResult.GetValue(rid);
18081823

18091824
if (string.IsNullOrWhiteSpace(ridVal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
18101825
{
@@ -1874,10 +1889,78 @@ private static Task CreateDmg(DirectoryInfo inputDir, FileInfo outputFile, Optio
18741889
return DotnetPackaging.Dmg.DmgIsoBuilder.Create(inputDir.FullName, outputFile.FullName, name);
18751890
}
18761891

1892+
private static Result<string> ResolveWindowsRid(string? architecture, string context)
1893+
{
1894+
return ResolveRidForPlatform(architecture, OSPlatform.Windows, "Windows", "win", context);
1895+
}
1896+
1897+
private static Result<string> ResolveMacRid(string? architecture)
1898+
{
1899+
return ResolveRidForPlatform(architecture, OSPlatform.OSX, "macOS", "osx", "DMG packaging");
1900+
}
1901+
1902+
private static Result<string> ResolveRidForPlatform(string? architecture, OSPlatform targetPlatform, string targetName, string ridPrefix, string context)
1903+
{
1904+
var architectureResult = DetermineArchitecture(architecture, targetPlatform, targetName, context);
1905+
if (architectureResult.IsFailure)
1906+
{
1907+
return Result.Failure<string>(architectureResult.Error);
1908+
}
1909+
1910+
return MapArchitectureToRid(architectureResult.Value, ridPrefix, context);
1911+
}
1912+
1913+
private static Result<RuntimeArchitecture> DetermineArchitecture(string? requested, OSPlatform targetPlatform, string targetName, string context)
1914+
{
1915+
if (!string.IsNullOrWhiteSpace(requested))
1916+
{
1917+
var parsed = ParseRuntimeArchitecture(requested);
1918+
if (parsed is RuntimeArchitecture.X64 or RuntimeArchitecture.Arm64)
1919+
{
1920+
return Result.Success(parsed.Value);
1921+
}
1922+
1923+
return Result.Failure<RuntimeArchitecture>($"Unsupported architecture '{requested}'. Use x64 or arm64.");
1924+
}
1925+
1926+
if (RuntimeInformation.IsOSPlatform(targetPlatform))
1927+
{
1928+
if (RuntimeInformation.OSArchitecture is RuntimeArchitecture.X64 or RuntimeArchitecture.Arm64)
1929+
{
1930+
return Result.Success(RuntimeInformation.OSArchitecture);
1931+
}
1932+
1933+
return Result.Failure<RuntimeArchitecture>($"{context} supports x64 or arm64. Detected architecture '{RuntimeInformation.OSArchitecture}' is not supported.");
1934+
}
1935+
1936+
return Result.Failure<RuntimeArchitecture>($"--arch is required when building {context} on non-{targetName} hosts (x64/arm64).");
1937+
}
1938+
1939+
private static Result<string> MapArchitectureToRid(RuntimeArchitecture architecture, string ridPrefix, string context)
1940+
{
1941+
return architecture switch
1942+
{
1943+
RuntimeArchitecture.X64 => Result.Success($"{ridPrefix}-x64"),
1944+
RuntimeArchitecture.Arm64 => Result.Success($"{ridPrefix}-arm64"),
1945+
_ => Result.Failure<string>($"{context} supports x64 or arm64 architectures only.")
1946+
};
1947+
}
1948+
1949+
private static RuntimeArchitecture? ParseRuntimeArchitecture(string value)
1950+
{
1951+
var v = value.Trim().ToLowerInvariant();
1952+
return v switch
1953+
{
1954+
"x86_64" or "amd64" or "x64" => RuntimeArchitecture.X64,
1955+
"aarch64" or "arm64" => RuntimeArchitecture.Arm64,
1956+
_ => null
1957+
};
1958+
}
1959+
18771960
private static void AddDmgFromProjectSubcommand(Command dmgCommand)
18781961
{
18791962
var project = new Option<FileInfo>("--project") { Description = "Path to the .csproj file", Required = true };
1880-
var rid = new Option<string?>("--rid") { Description = "Runtime identifier (e.g. osx-x64, osx-arm64)", Required = true };
1963+
var arch = new Option<string?>("--arch") { Description = "Target architecture (x64, arm64)" };
18811964
var selfContained = new Option<bool>("--self-contained") { Description = "Publish self-contained" };
18821965
selfContained.DefaultValueFactory = _ => true;
18831966
var configuration = new Option<string>("--configuration") { Description = "Build configuration" };
@@ -1909,7 +1992,7 @@ private static void AddDmgFromProjectSubcommand(Command dmgCommand)
19091992

19101993
var fromProject = new Command("from-project") { Description = "Publish a .NET project and build a .dmg from the published output (.app bundle auto-generated if missing). Experimental." };
19111994
fromProject.Add(project);
1912-
fromProject.Add(rid);
1995+
fromProject.Add(arch);
19131996
fromProject.Add(selfContained);
19141997
fromProject.Add(configuration);
19151998
fromProject.Add(singleFile);
@@ -1920,18 +2003,24 @@ private static void AddDmgFromProjectSubcommand(Command dmgCommand)
19202003
fromProject.SetAction(async parseResult =>
19212004
{
19222005
var prj = parseResult.GetValue(project)!;
1923-
var ridVal = parseResult.GetValue(rid);
19242006
var sc = parseResult.GetValue(selfContained);
19252007
var cfg = parseResult.GetValue(configuration)!;
19262008
var sf = parseResult.GetValue(singleFile);
19272009
var tr = parseResult.GetValue(trimmed);
19282010
var outFile = parseResult.GetValue(output)!;
19292011
var opt = optionsBinder.Bind(parseResult);
2012+
var ridResult = ResolveMacRid(parseResult.GetValue(arch));
2013+
if (ridResult.IsFailure)
2014+
{
2015+
Console.Error.WriteLine(ridResult.Error);
2016+
Environment.ExitCode = 1;
2017+
return;
2018+
}
19302019

19312020
var publisher = new DotnetPackaging.Publish.DotnetPublisher();
19322021
var req = new DotnetPackaging.Publish.ProjectPublishRequest(prj.FullName)
19332022
{
1934-
Rid = string.IsNullOrWhiteSpace(ridVal) ? Maybe<string>.None : Maybe<string>.From(ridVal!),
2023+
Rid = Maybe<string>.From(ridResult.Value),
19352024
SelfContained = sc,
19362025
Configuration = cfg,
19372026
SingleFile = sf,

src/DotnetPackaging.Tool/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"Exe": {
1212
"commandName": "Project",
13-
"commandLineArgs": "exe from-project --project --rid win-x64 \"F:\\Repos\\Zafiro.Avalonia\\samples\\TestApp\\TestApp.Desktop\\TestApp.Desktop.csproj\" --output C:\\Users\\JMN\\Desktop\\Setup.exe"
13+
"commandLineArgs": "exe from-project --project --arch x64 \"F:\\Repos\\Zafiro.Avalonia\\samples\\TestApp\\TestApp.Desktop\\TestApp.Desktop.csproj\" --output C:\\Users\\JMN\\Desktop\\Setup.exe"
1414
}
1515
}
1616
}

0 commit comments

Comments
 (0)