From 8046395c134e44472fc2e4ed7544e2ce74de5862 Mon Sep 17 00:00:00 2001
From: Rolf Bjarne Kvinge <rolf@xamarin.com>
Date: Thu, 28 Nov 2024 23:52:42 +0100
Subject: [PATCH] [msbuild] Port the CreateAssetPack task to subclass
 XamarinTask.

This has a few advantages:

* We simplify and unify more of our code.
* We have more control over the error reporting / logging behavior.

Additionally:

* Use 'xcrun' to invoke 'zip' (partial fix for #3931).
* Allow for overriding the path to the command-line tool in question.
* Add support for cancellation.
---
 .../Tasks/CreateAssetPack.cs                  | 64 ++++++++++---------
 .../Xamarin.Shared/Xamarin.iOS.Common.targets |  3 +-
 2 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateAssetPack.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateAssetPack.cs
index 93455e67571d..5ac931ea6ffa 100644
--- a/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateAssetPack.cs
+++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateAssetPack.cs
@@ -1,7 +1,11 @@
 using System;
+using System.Collections.Generic;
 using Microsoft.Build.Utilities;
 using Microsoft.Build.Framework;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
 using Xamarin.MacDev;
 using Xamarin.MacDev.Tasks;
 using Xamarin.Messaging.Build.Client;
@@ -9,7 +13,9 @@
 #nullable enable
 
 namespace Xamarin.MacDev.Tasks {
-	public class CreateAssetPack : XamarinToolTask {
+	public class CreateAssetPack : XamarinTask, ICancelableTask {
+		CancellationTokenSource? cancellationTokenSource;
+
 		#region Inputs
 
 		[Required]
@@ -18,34 +24,32 @@ public class CreateAssetPack : XamarinToolTask {
 		[Required]
 		public ITaskItem? Source { get; set; }
 
-		#endregion
+		public string ZipPath { get; set; } = string.Empty;
 
-		protected override string ToolName {
-			get { return "zip"; }
-		}
+		#endregion
 
-		protected override string GenerateFullPathToTool ()
+		static string GetExecutable (List<string> arguments, string toolName, string toolPathOverride)
 		{
-			if (!string.IsNullOrEmpty (ToolPath))
-				return Path.Combine (ToolPath, ToolExe);
-
-			var path = Path.Combine ("/usr/bin", ToolExe);
-
-			return File.Exists (path) ? path : ToolExe;
+			if (string.IsNullOrEmpty (toolPathOverride)) {
+				arguments.Insert (0, toolName);
+				return "xcrun";
+			}
+			return toolPathOverride;
 		}
 
-		protected override string GetWorkingDirectory ()
+		protected string GetWorkingDirectory ()
 		{
 			return Source!.GetMetadata ("FullPath");
 		}
 
-		protected override string GenerateCommandLineCommands ()
+		List<string> GenerateCommandLineCommands ()
 		{
-			var args = new CommandLineArgumentBuilder ();
+			var args = new List<string> ();
 
-			args.Add ("-r", "-y");
-			args.AddQuoted (OutputFile!.GetMetadata ("FullPath"));
-			args.AddQuoted ("META-INF");
+			args.Add ("-r");
+			args.Add ("-y");
+			args.Add (OutputFile!.GetMetadata ("FullPath"));
+			args.Add ("META-INF");
 
 			long size = 0;
 			int count = 0;
@@ -64,18 +68,13 @@ protected override string GenerateCommandLineCommands ()
 					size += info.Length;
 				}
 
-				args.AddQuoted (Path.GetFileName (path));
+				args.Add (Path.GetFileName (path));
 				count++;
 			}
 
 			SaveMetaFile (count, size);
 
-			return args.ToString ();
-		}
-
-		protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance)
-		{
-			Log.LogMessage (messageImportance, "{0}", singleLine);
+			return args;
 		}
 
 		public override bool Execute ()
@@ -87,7 +86,11 @@ public override bool Execute ()
 			if (File.Exists (OutputFile!.ItemSpec))
 				File.Delete (OutputFile.ItemSpec);
 
-			return base.Execute ();
+			var args = GenerateCommandLineCommands ();
+			var executable = GetExecutable (args, "zip", ZipPath);
+			cancellationTokenSource = new CancellationTokenSource ();
+			ExecuteAsync (Log, executable, args, workingDirectory: GetWorkingDirectory (), cancellationToken: cancellationTokenSource.Token).Wait ();
+			return !Log.HasLoggedErrors;
 		}
 
 		void SaveMetaFile (int count, long size)
@@ -104,12 +107,13 @@ void SaveMetaFile (int count, long size)
 			meta.Save (Path.Combine (Source.ItemSpec, "META-INF", "com.apple.ZipMetadata.plist"), true, true);
 		}
 
-		public override void Cancel ()
+		public void Cancel ()
 		{
-			if (ShouldExecuteRemotely ())
+			if (ShouldExecuteRemotely ()) {
 				BuildConnection.CancelAsync (BuildEngine4).Wait ();
-
-			base.Cancel ();
+			} else {
+				cancellationTokenSource?.Cancel ();
+			}
 		}
 	}
 }
diff --git a/msbuild/Xamarin.Shared/Xamarin.iOS.Common.targets b/msbuild/Xamarin.Shared/Xamarin.iOS.Common.targets
index 1882a6b12833..d2483014ceb6 100644
--- a/msbuild/Xamarin.Shared/Xamarin.iOS.Common.targets
+++ b/msbuild/Xamarin.Shared/Xamarin.iOS.Common.targets
@@ -513,10 +513,9 @@ Copyright (C) 2013-2016 Xamarin. All rights reserved.
 		<CreateAssetPack
 			SessionId="$(BuildSessionId)"
 			Condition="'$(IsMacEnabled)' == 'true' And '$(EmbedOnDemandResources)' == 'false'"
-			ToolExe="$(ZipExe)"
-			ToolPath="$(ZipPath)"
 			Source="%(_AssetPack.Identity)"
 			OutputFile="$(IpaPackageDir)OnDemandResources\%(_AssetPack.DirectoryName)"
+			ZipPath="$(ZipPath)"
 			/>
 
 		<Copy