Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[msbuild] Port Metal and MetalLib to subclass XamarinTask. Fixes #21437. #21439

Merged
merged 2 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/build-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,15 @@ Example:
```

This property was introduced in .NET 9.

## MetalLibPath

The full path to the `metallib` tool (the Metal Linker).

The default behavior is to use `xcrun metallib`.

## MetalPath

The full path to the Metal compiler.

The default behavior is to use `xcrun metal`.
112 changes: 43 additions & 69 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/Metal.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand All @@ -11,139 +12,112 @@
using Xamarin.Localization.MSBuild;
using Xamarin.Messaging.Build.Client;

// Disable until we get around to enable + fix any issues.
#nullable disable

namespace Xamarin.MacDev.Tasks {
public class Metal : XamarinToolTask {
public class Metal : XamarinTask {
CancellationTokenSource? cancellationTokenSource;

#region Inputs

[Required]
public string IntermediateOutputPath { get; set; }
public string IntermediateOutputPath { get; set; } = string.Empty;

public string MetalPath { get; set; } = string.Empty;

[Required]
public string MinimumOSVersion { get; set; }
public string MinimumOSVersion { get; set; } = string.Empty;

[Required]
public string ProjectDir { get; set; }
public string ProjectDir { get; set; } = string.Empty;

[Required]
public string ResourcePrefix { get; set; }
public string ResourcePrefix { get; set; } = string.Empty;

[Required]
public string SdkDevPath { get; set; }
public string SdkDevPath { get; set; } = string.Empty;

[Required]
public string SdkVersion { get; set; }
public string SdkVersion { get; set; } = string.Empty;

[Required]
public bool SdkIsSimulator { get; set; }

[Required]
public string SdkRoot { get; set; }
public string SdkRoot { get; set; } = string.Empty;

[Required]
public ITaskItem SourceFile { get; set; }
public ITaskItem? SourceFile { get; set; }

#endregion

[Output]
public ITaskItem OutputFile { get; set; }

string DevicePlatformBinDir {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
return AppleSdkSettings.XcodeVersion.Major >= 11
? Path.Combine (SdkDevPath, "Toolchains", "XcodeDefault.xctoolchain", "usr", "bin")
: Path.Combine (SdkDevPath, "Platforms", "iPhoneOS.platform", "usr", "bin");
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
return AppleSdkSettings.XcodeVersion.Major >= 10
? Path.Combine (SdkDevPath, "Toolchains", "XcodeDefault.xctoolchain", "usr", "bin")
: Path.Combine (SdkDevPath, "Platforms", "MacOSX.platform", "usr", "bin");
default:
throw new InvalidOperationException (string.Format (MSBStrings.InvalidPlatform, Platform));
}
}
}
public ITaskItem? OutputFile { get; set; }

protected override string ToolName {
get { return "metal"; }
}

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 (DevicePlatformBinDir, ToolExe);

return File.Exists (path) ? path : ToolExe;
if (string.IsNullOrEmpty (toolPathOverride)) {
arguments.Insert (0, toolName);
return "xcrun";
}
return toolPathOverride;
}

public override bool Execute ()
{
if (ShouldExecuteRemotely ())
return new TaskRunner (SessionId, BuildEngine4).RunAsync (this).Result;

if (AppleSdkSettings.XcodeVersion.Major >= 11)
EnvironmentVariables = EnvironmentVariables.CopyAndAdd ($"SDKROOT={SdkRoot}");
return base.Execute ();
}
var env = new Dictionary<string, string?> {
{ "SDKROOT", SdkRoot },
};

protected override string GenerateCommandLineCommands ()
{
var prefixes = BundleResource.SplitResourcePrefixes (ResourcePrefix);
var intermediate = Path.Combine (IntermediateOutputPath, ToolName);
var logicalName = BundleResource.GetLogicalName (ProjectDir, prefixes, SourceFile, !string.IsNullOrEmpty (SessionId));
var intermediate = Path.Combine (IntermediateOutputPath, MetalPath);
var logicalName = BundleResource.GetLogicalName (ProjectDir, prefixes, SourceFile!, !string.IsNullOrEmpty (SessionId));
var path = Path.Combine (intermediate, logicalName);
var args = new CommandLineArgumentBuilder ();
var args = new List<string> ();
var dir = Path.GetDirectoryName (path);

if (!Directory.Exists (dir))
Directory.CreateDirectory (dir);
Directory.CreateDirectory (dir);

OutputFile = new TaskItem (Path.ChangeExtension (path, ".air"));
OutputFile.SetMetadata ("LogicalName", Path.ChangeExtension (logicalName, ".air"));

args.Add ("-arch", "air64");
var executable = GetExecutable (args, "metal", MetalPath);

args.Add ("-arch");
args.Add ("air64");
args.Add ("-emit-llvm");
args.Add ("-c");
args.Add ("-gline-tables-only");
args.Add ("-ffast-math");

args.Add ("-serialize-diagnostics");
args.AddQuoted (Path.ChangeExtension (path, ".dia"));
args.Add (Path.ChangeExtension (path, ".dia"));

args.Add ("-o");
args.AddQuoted (Path.ChangeExtension (path, ".air"));
args.Add (Path.ChangeExtension (path, ".air"));

if (Platform == ApplePlatform.MacCatalyst) {
args.Add ($"-target");
args.Add ($"air64-apple-ios{MinimumOSVersion}-macabi");
} else {
args.Add (PlatformFrameworkHelper.GetMinimumVersionArgument (TargetFrameworkMoniker, SdkIsSimulator, MinimumOSVersion));
}
args.AddQuoted (SourceFile.ItemSpec);
args.Add (SourceFile!.ItemSpec);

return args.ToString ();
}
cancellationTokenSource = new CancellationTokenSource ();
ExecuteAsync (Log, executable, args, environment: env, cancellationToken: cancellationTokenSource.Token).Wait ();

protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance)
{
// TODO: do proper parsing of error messages and such
Log.LogMessage (messageImportance, "{0}", singleLine);
return !Log.HasLoggedErrors;
}

public override void Cancel ()
public void Cancel ()
{
if (ShouldExecuteRemotely ())
if (ShouldExecuteRemotely ()) {
BuildConnection.CancelAsync (BuildEngine4).Wait ();

base.Cancel ();
} else {
cancellationTokenSource?.Cancel ();
}
}
}
}
103 changes: 36 additions & 67 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/MetalLib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand All @@ -10,78 +11,35 @@
using Xamarin.Messaging.Build.Client;
using Xamarin.Utils;

// Disable until we get around to enable + fix any issues.
#nullable disable

namespace Xamarin.MacDev.Tasks {
public class MetalLib : XamarinToolTask, ITaskCallback {
public class MetalLib : XamarinTask, ITaskCallback {
CancellationTokenSource? cancellationTokenSource;

#region Inputs

[Required]
public ITaskItem [] Items { get; set; }
public ITaskItem [] Items { get; set; } = Array.Empty<ITaskItem> ();

public string MetalLibPath { get; set; } = string.Empty;

[Required]
public string OutputLibrary { get; set; }
public string OutputLibrary { get; set; } = string.Empty;

[Required]
public string SdkDevPath { get; set; }
public string SdkDevPath { get; set; } = string.Empty;

[Required]
public string SdkRoot { get; set; }
public string SdkRoot { get; set; } = string.Empty;

#endregion

string DevicePlatformBinDir {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
return AppleSdkSettings.XcodeVersion.Major >= 11
? Path.Combine (SdkDevPath, "Toolchains", "XcodeDefault.xctoolchain", "usr", "bin")
: Path.Combine (SdkDevPath, "Platforms", "iPhoneOS.platform", "usr", "bin");
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
return AppleSdkSettings.XcodeVersion.Major >= 10
? Path.Combine (SdkDevPath, "Toolchains", "XcodeDefault.xctoolchain", "usr", "bin")
: Path.Combine (SdkDevPath, "Platforms", "MacOSX.platform", "usr", "bin");
default:
throw new InvalidOperationException (string.Format (MSBStrings.InvalidPlatform, Platform));
}
}
}

protected override string ToolName {
get { return "metallib"; }
}

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 (DevicePlatformBinDir, ToolExe);

return File.Exists (path) ? path : ToolExe;
}

protected override string GenerateCommandLineCommands ()
{
var args = new CommandLineArgumentBuilder ();

args.Add ("-o");
args.AddQuoted (OutputLibrary);

foreach (var item in Items)
args.AddQuoted (item.ItemSpec);

return args.ToString ();
}

protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance)
{
// TODO: do proper parsing of error messages and such
Log.LogMessage (messageImportance, "{0}", singleLine);
if (string.IsNullOrEmpty (toolPathOverride)) {
arguments.Insert (0, toolName);
return "xcrun";
}
return toolPathOverride;
}

public override bool Execute ()
Expand All @@ -90,14 +48,24 @@ public override bool Execute ()
return new TaskRunner (SessionId, BuildEngine4).RunAsync (this).Result;

var dir = Path.GetDirectoryName (OutputLibrary);
Directory.CreateDirectory (dir);

if (!Directory.Exists (dir))
Directory.CreateDirectory (dir);
var env = new Dictionary<string, string?> {
{ "SDKROOT", SdkRoot },
};

if (AppleSdkSettings.XcodeVersion.Major >= 11)
EnvironmentVariables = EnvironmentVariables.CopyAndAdd ($"SDKROOT={SdkRoot}");
var args = new List<string> ();
args.Add ("-o");
args.Add (OutputLibrary);
foreach (var item in Items)
args.Add (item.ItemSpec);

var executable = GetExecutable (args, "metallib", MetalLibPath);

cancellationTokenSource = new CancellationTokenSource ();
ExecuteAsync (Log, executable, args, environment: env, cancellationToken: cancellationTokenSource.Token).Wait ();

return base.Execute ();
return !Log.HasLoggedErrors;
}

public bool ShouldCopyToBuildServer (ITaskItem item) => false;
Expand All @@ -106,12 +74,13 @@ public override bool Execute ()

public IEnumerable<ITaskItem> GetAdditionalItemsToBeCopied () => Enumerable.Empty<ITaskItem> ();

public override void Cancel ()
public void Cancel ()
{
base.Cancel ();

if (ShouldExecuteRemotely ())
if (ShouldExecuteRemotely ()) {
BuildConnection.CancelAsync (BuildEngine4).Wait ();
} else {
cancellationTokenSource?.Cancel ();
}
}
}
}
5 changes: 3 additions & 2 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;

using Microsoft.Build.Framework;
using Microsoft.Build.Tasks;
Expand Down Expand Up @@ -120,15 +121,15 @@ protected System.Threading.Tasks.Task<Execution> ExecuteAsync (string fileName,
return ExecuteAsync (Log, fileName, arguments, sdkDevPath, environment, mergeOutput, showErrorIfFailure, workingDirectory);
}

internal protected static async System.Threading.Tasks.Task<Execution> ExecuteAsync (TaskLoggingHelper log, string fileName, IList<string> arguments, string? sdkDevPath = null, Dictionary<string, string?>? environment = null, bool mergeOutput = true, bool showErrorIfFailure = true, string? workingDirectory = null)
internal protected static async System.Threading.Tasks.Task<Execution> ExecuteAsync (TaskLoggingHelper log, string fileName, IList<string> arguments, string? sdkDevPath = null, Dictionary<string, string?>? environment = null, bool mergeOutput = true, bool showErrorIfFailure = true, string? workingDirectory = null, CancellationToken? cancellationToken = null)
{
// Create a new dictionary if we're given one, to make sure we don't change the caller's dictionary.
var launchEnvironment = environment is null ? new Dictionary<string, string?> () : new Dictionary<string, string?> (environment);
if (!string.IsNullOrEmpty (sdkDevPath))
launchEnvironment ["DEVELOPER_DIR"] = sdkDevPath;

log.LogMessage (MessageImportance.Normal, MSBStrings.M0001, fileName, StringUtils.FormatArguments (arguments));
var rv = await Execution.RunAsync (fileName, arguments, environment: launchEnvironment, mergeOutput: mergeOutput, workingDirectory: workingDirectory);
var rv = await Execution.RunAsync (fileName, arguments, environment: launchEnvironment, mergeOutput: mergeOutput, workingDirectory: workingDirectory, cancellationToken: cancellationToken);
log.LogMessage (rv.ExitCode == 0 ? MessageImportance.Low : MessageImportance.High, MSBStrings.M0002, fileName, rv.ExitCode);

// Show the output
Expand Down
2 changes: 2 additions & 0 deletions msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true' and '%(Metal.Identity)' != ''"
IntermediateOutputPath="$(DeviceSpecificIntermediateOutputPath)"
MetalPath="$(MetalPath)"
MinimumOSVersion="$(_MinimumOSVersion)"
ProjectDir="$(MSBuildProjectDirectory)"
TargetFrameworkMoniker="$(_ComputedTargetFrameworkMoniker)"
Expand All @@ -1417,6 +1418,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true'"
Items="@(_SmeltedMetal)"
MetalLibPath="$(MetalLibPath)"
SdkDevPath="$(_SdkDevPath)"
SdkRoot="$(_SdkRoot)"
OutputLibrary="$(_AppResourcesPath)default.metallib">
Expand Down
Loading