xamarin-macios/msbuild/Xamarin.MacDev.Tasks.Core/Tasks/CodesignTaskBase.cs

242 строки
6.2 KiB
C#

using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Diagnostics;
using Parallel = System.Threading.Tasks.Parallel;
using ParallelOptions = System.Threading.Tasks.ParallelOptions;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Collections.Generic;
namespace Xamarin.MacDev.Tasks
{
public abstract class CodesignTaskBase : Task
{
const string ToolName = "codesign";
const string MacOSDirName = "MacOS";
const string CodeSignatureDirName = "_CodeSignature";
string toolExe;
#region Inputs
public string SessionId { get; set; }
[Required]
public string CodesignAllocate { get; set; }
public bool DisableTimestamp { get; set; }
public string Entitlements { get; set; }
public string Keychain { get; set; }
[Required]
public ITaskItem[] Resources { get; set; }
public string ResourceRules { get; set; }
[Required]
public string SigningKey { get; set; }
public string ExtraArgs { get; set; }
public bool IsAppExtension { get; set; }
public bool UseHardenedRuntime { get; set; }
public bool UseSecureTimestamp { get; set; }
public string ToolExe {
get { return toolExe ?? ToolName; }
set { toolExe = value; }
}
public string ToolPath { get; set; }
#endregion
#region Outputs
[Output]
public ITaskItem[] CodesignedFiles { get; set; }
#endregion
string GetFullPathToTool ()
{
if (!string.IsNullOrEmpty (ToolPath))
return Path.Combine (ToolPath, ToolExe);
var path = Path.Combine ("/usr/bin", ToolExe);
return File.Exists (path) ? path : ToolExe;
}
ProcessStartInfo GetProcessStartInfo (string tool, string args)
{
var startInfo = new ProcessStartInfo (tool, args);
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.EnvironmentVariables["CODESIGN_ALLOCATE"] = CodesignAllocate;
startInfo.CreateNoWindow = true;
return startInfo;
}
string GenerateCommandLineArguments (ITaskItem item)
{
var args = new CommandLineArgumentBuilder ();
args.Add ("-v");
args.Add ("--force");
if (IsAppExtension)
args.Add ("--deep");
if (UseHardenedRuntime)
args.Add ("-o runtime");
if (UseSecureTimestamp)
args.Add ("--timestamp");
else
args.Add ("--timestamp=none");
args.Add ("--sign");
args.AddQuoted (SigningKey);
if (!string.IsNullOrEmpty (Keychain)) {
args.Add ("--keychain");
args.AddQuoted (Path.GetFullPath (Keychain));
}
if (!string.IsNullOrEmpty (ResourceRules)) {
args.Add ("--resource-rules");
args.AddQuoted (Path.GetFullPath (ResourceRules));
}
if (!string.IsNullOrEmpty (Entitlements)) {
args.Add ("--entitlements");
args.AddQuoted (Path.GetFullPath (Entitlements));
}
if (DisableTimestamp)
args.Add ("--timestamp=none");
if (!string.IsNullOrEmpty (ExtraArgs))
args.Add (ExtraArgs);
args.AddQuoted (Path.GetFullPath (item.ItemSpec));
return args.ToString ();
}
void Codesign (ITaskItem item)
{
var startInfo = GetProcessStartInfo (GetFullPathToTool (), GenerateCommandLineArguments (item));
var messages = new StringBuilder ();
var errors = new StringBuilder ();
int exitCode;
try {
Log.LogMessage (MessageImportance.Normal, "Tool {0} execution started with arguments: {1}", startInfo.FileName, startInfo.Arguments);
using (var stdout = new StringWriter (messages)) {
using (var stderr = new StringWriter (errors)) {
using (var process = ProcessUtils.StartProcess (startInfo, stdout, stderr)) {
process.Wait ();
exitCode = process.Result;
}
}
Log.LogMessage (MessageImportance.Low, "Tool {0} execution finished (exit code = {1}).", startInfo.FileName, exitCode);
}
} catch (Exception ex) {
Log.LogError ("Error executing tool '{0}': {1}", startInfo.FileName, ex.Message);
return;
}
if (messages.Length > 0)
Log.LogMessage (MessageImportance.Normal, "{0}", messages.ToString ());
if (exitCode != 0) {
if (errors.Length > 0)
Log.LogError (null, null, null, item.ItemSpec, 0, 0, 0, 0, "{0}", errors);
else
Log.LogError (null, null, null, item.ItemSpec, 0, 0, 0, 0, "{0} failed.", startInfo.FileName);
}
}
public override bool Execute ()
{
if (Resources.Length == 0)
return true;
var codesignedFiles = new List<ITaskItem> ();
Parallel.ForEach (Resources, new ParallelOptions { MaxDegreeOfParallelism = Math.Max (Environment.ProcessorCount / 2, 1) }, (item) => {
Codesign (item);
var files = GetCodesignedFiles (item);
lock (codesignedFiles)
codesignedFiles.AddRange (files);
});
CodesignedFiles = codesignedFiles.ToArray ();
return !Log.HasLoggedErrors;
}
IEnumerable<ITaskItem> GetCodesignedFiles (ITaskItem item)
{
var codesignedFiles = new List<ITaskItem> ();
if (Directory.Exists (item.ItemSpec)) {
var codeSignaturePath = Path.Combine (item.ItemSpec, CodeSignatureDirName);
if (!Directory.Exists (codeSignaturePath))
return codesignedFiles;
codesignedFiles.AddRange (Directory.EnumerateFiles (codeSignaturePath).Select (x => new TaskItem (x)));
var extension = Path.GetExtension (item.ItemSpec);
if (extension == ".app" || extension == ".appex") {
var executableName = Path.GetFileName (item.ItemSpec);
var manifestPath = Path.Combine (item.ItemSpec, "Info.plist");
if (File.Exists(manifestPath)) {
var bundleExecutable = PDictionary.FromFile (manifestPath).GetCFBundleExecutable ();
if (!string.IsNullOrEmpty(bundleExecutable))
executableName = bundleExecutable;
}
var basePath = item.ItemSpec;
if (Directory.Exists (Path.Combine (basePath, MacOSDirName)))
basePath = Path.Combine (basePath, MacOSDirName);
var executablePath = Path.Combine (basePath, executableName);
if (File.Exists (executablePath))
codesignedFiles.Add (new TaskItem (executablePath));
}
} else if (File.Exists (item.ItemSpec)) {
codesignedFiles.Add (item);
var dirName = Path.GetDirectoryName (item.ItemSpec);
if (Path.GetExtension (dirName) == ".framework")
codesignedFiles.AddRange (Directory.EnumerateFiles (Path.Combine (dirName, CodeSignatureDirName)).Select (x => new TaskItem (x)));
}
return codesignedFiles;
}
}
}