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

408 строки
11 KiB
C#
Исходник Обычный вид История

using System;
2016-04-21 16:40:25 +03:00
using System.IO;
using System.Collections.Generic;
using Microsoft.Build.Framework;
2020-03-14 00:46:28 +03:00
using Xamarin.Localization.MSBuild;
2016-04-21 16:40:25 +03:00
namespace Xamarin.MacDev.Tasks
{
public abstract class CompileEntitlementsTaskBase : XamarinTask
2016-04-21 16:40:25 +03:00
{
bool warnedTeamIdentifierPrefix;
bool warnedAppIdentifierPrefix;
#region Inputs
[Required]
public string AppBundleDir { get; set; }
[Required]
public string AppIdentifier { get; set; }
[Required]
public string BundleIdentifier { get; set; }
[Required]
public ITaskItem CompiledEntitlements { get; set; }
2016-04-21 16:40:25 +03:00
public bool Debug { get; set; }
2016-04-21 16:40:25 +03:00
public string Entitlements { get; set; }
[Required]
public bool IsAppExtension { get; set; }
public string ProvisioningProfile { get; set; }
[Required]
public string SdkDevPath { get; set; }
public bool SdkIsSimulator { get; set; }
[Required]
public string SdkPlatform { get; set; }
2016-04-21 16:40:25 +03:00
[Required]
public string SdkVersion { get; set; }
[Output]
public ITaskItem EntitlementsInExecutable { get; set; }
[Output]
public ITaskItem EntitlementsInSignature { get; set; }
2016-04-21 16:40:25 +03:00
#endregion
protected abstract string ApplicationIdentifierKey { get; }
protected abstract string DefaultEntitlementsPath { get; }
protected abstract HashSet<string> AllowedProvisioningKeys { get; }
protected abstract string EntitlementBundlePath { get; }
protected virtual bool MergeProfileEntitlements {
get { return true; }
}
PString MergeEntitlementString (PString pstr, MobileProvision profile, bool expandWildcards)
2016-04-21 16:40:25 +03:00
{
string TeamIdentifierPrefix;
string AppIdentifierPrefix;
if (string.IsNullOrEmpty (pstr.Value))
return (PString) pstr.Clone ();
if (profile == null) {
if (!warnedTeamIdentifierPrefix && pstr.Value.Contains ("$(TeamIdentifierPrefix)")) {
2020-03-14 00:46:28 +03:00
Log.LogWarning (null, null, null, Entitlements, 0, 0, 0, 0, MSBStrings.W0108);
2016-04-21 16:40:25 +03:00
warnedTeamIdentifierPrefix = true;
}
if (!warnedAppIdentifierPrefix && pstr.Value.Contains ("$(AppIdentifierPrefix)")) {
2020-03-14 00:46:28 +03:00
Log.LogWarning (null, null, null, Entitlements, 0, 0, 0, 0, MSBStrings.W0109);
2016-04-21 16:40:25 +03:00
warnedAppIdentifierPrefix = true;
}
}
if (profile != null && profile.ApplicationIdentifierPrefix.Count > 0)
AppIdentifierPrefix = profile.ApplicationIdentifierPrefix[0] + ".";
else
AppIdentifierPrefix = string.Empty;
if (profile != null && profile.TeamIdentifierPrefix.Count > 0)
TeamIdentifierPrefix = profile.TeamIdentifierPrefix[0] + ".";
else
TeamIdentifierPrefix = AppIdentifierPrefix;
var customTags = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase) {
{ "TeamIdentifierPrefix", TeamIdentifierPrefix },
{ "AppIdentifierPrefix", AppIdentifierPrefix },
{ "CFBundleIdentifier", BundleIdentifier },
};
var expanded = StringParserService.Parse (pstr.Value, customTags);
if (expandWildcards && expanded.IndexOf ('*') != -1) {
int asterisk = expanded.IndexOf ('*');
string prefix;
if (expanded.StartsWith (TeamIdentifierPrefix, StringComparison.Ordinal))
prefix = TeamIdentifierPrefix;
else if (expanded.StartsWith (AppIdentifierPrefix, StringComparison.Ordinal))
prefix = AppIdentifierPrefix;
else
prefix = string.Empty;
var baseBundleIdentifier = expanded.Substring (prefix.Length, asterisk - prefix.Length);
if (!BundleIdentifier.StartsWith (baseBundleIdentifier, StringComparison.Ordinal))
expanded = expanded.Replace ("*", BundleIdentifier);
else
expanded = prefix + BundleIdentifier;
}
2016-04-21 16:40:25 +03:00
return new PString (expanded);
}
PArray MergeEntitlementArray (PArray array, MobileProvision profile)
{
var result = new PArray ();
foreach (var item in array) {
PObject value;
if (item is PDictionary)
value = MergeEntitlementDictionary ((PDictionary) item, profile);
else if (item is PString)
value = MergeEntitlementString ((PString) item, profile, false);
2016-04-21 16:40:25 +03:00
else if (item is PArray)
value = MergeEntitlementArray ((PArray) item, profile);
else
value = item.Clone ();
if (value != null)
result.Add (value);
}
if (result.Count > 0)
return result;
return null;
}
PDictionary MergeEntitlementDictionary (PDictionary dict, MobileProvision profile)
{
var result = new PDictionary ();
foreach (var item in dict) {
PObject value = item.Value;
if (value is PDictionary)
value = MergeEntitlementDictionary ((PDictionary) value, profile);
else if (value is PString)
value = MergeEntitlementString ((PString) value, profile, false);
2016-04-21 16:40:25 +03:00
else if (value is PArray)
value = MergeEntitlementArray ((PArray) value, profile);
else
value = value.Clone ();
if (value != null)
result.Add (item.Key, value);
}
return result;
}
static bool AreEqual (byte[] x, byte[] y)
{
if (x.Length != y.Length)
return false;
for (int i = 0; i < x.Length; i++) {
if (x[i] != y[i])
return false;
}
return true;
}
2016-04-21 16:40:25 +03:00
static void WriteXcent (PObject doc, string path)
{
var buf = doc.ToByteArray (false);
using (var stream = new MemoryStream ()) {
2016-04-21 16:40:25 +03:00
stream.Write (buf, 0, buf.Length);
var src = stream.ToArray ();
bool save;
// Note: if the destination file already exists, only re-write it if the content will change
if (File.Exists (path)) {
var dest = File.ReadAllBytes (path);
save = !AreEqual (src, dest);
} else {
save = true;
}
if (save)
File.WriteAllBytes (path, src);
2016-04-21 16:40:25 +03:00
}
}
protected virtual PDictionary GetCompiledEntitlements (MobileProvision profile, PDictionary template)
{
var entitlements = new PDictionary ();
if (profile != null && MergeProfileEntitlements) {
// start off with the settings from the provisioning profile
foreach (var item in profile.Entitlements) {
if (!AllowedProvisioningKeys.Contains (item.Key))
continue;
var value = item.Value;
if (item.Key == "com.apple.developer.icloud-container-environment")
value = new PString ("Development");
else if (value is PDictionary)
value = MergeEntitlementDictionary ((PDictionary) value, profile);
else if (value is PString)
value = MergeEntitlementString ((PString) value, profile, item.Key == ApplicationIdentifierKey);
2016-04-21 16:40:25 +03:00
else if (value is PArray)
value = MergeEntitlementArray ((PArray) value, profile);
else
value = value.Clone ();
if (value != null)
entitlements.Add (item.Key, value);
}
}
// merge in the user's values
foreach (var item in template) {
var value = item.Value;
if (item.Key == "com.apple.developer.ubiquity-container-identifiers" ||
item.Key == "com.apple.developer.icloud-container-identifiers" ||
item.Key == "com.apple.developer.icloud-container-environment" ||
item.Key == "com.apple.developer.icloud-services") {
if (profile == null)
2020-03-14 00:46:28 +03:00
Log.LogWarning (null, null, null, Entitlements, 0, 0, 0, 0, MSBStrings.W0110, item.Key);
else if (!profile.Entitlements.ContainsKey (item.Key))
2020-03-14 00:46:28 +03:00
Log.LogWarning (null, null, null, Entitlements, 0, 0, 0, 0, MSBStrings.W0111, item.Key);
2016-04-21 16:40:25 +03:00
} else if (item.Key == ApplicationIdentifierKey) {
var str = value as PString;
// Ignore ONLY if it is empty, otherwise take the user's value
if (str == null || string.IsNullOrEmpty (str.Value))
continue;
}
if (value is PDictionary)
value = MergeEntitlementDictionary ((PDictionary) value, profile);
else if (value is PString)
value = MergeEntitlementString ((PString) value, profile, item.Key == ApplicationIdentifierKey);
2016-04-21 16:40:25 +03:00
else if (value is PArray)
value = MergeEntitlementArray ((PArray) value, profile);
else
value = value.Clone ();
if (value != null)
entitlements[item.Key] = value;
}
return entitlements;
}
static PDictionary GetArchivedExpandedEntitlements (PDictionary template, PDictionary compiled)
{
var allowed = new HashSet<string> ();
// the template (user-supplied Entitlements.plist file) is used to create a whitelist of keys
allowed.Add ("com.apple.developer.icloud-container-environment");
foreach (var item in template)
allowed.Add (item.Key);
// now we duplicate the allowed keys from the compiled xcent file
var archived = new PDictionary ();
foreach (var item in compiled) {
if (allowed.Contains (item.Key))
archived.Add (item.Key, item.Value.Clone ());
}
return archived;
}
protected virtual MobileProvision GetMobileProvision (MobileProvisionPlatform platform, string name)
2016-04-21 16:40:25 +03:00
{
return MobileProvisionIndex.GetMobileProvision (platform, name);
2016-04-21 16:40:25 +03:00
}
public override bool Execute ()
{
MobileProvisionPlatform platform;
2016-04-21 16:40:25 +03:00
MobileProvision profile;
PDictionary template;
PDictionary compiled;
PDictionary archived;
string path;
bool save;
2016-04-21 16:40:25 +03:00
switch (SdkPlatform) {
case "AppleTVSimulator":
case "AppleTVOS":
platform = MobileProvisionPlatform.tvOS;
break;
case "iPhoneSimulator":
case "WatchSimulator":
case "iPhoneOS":
case "WatchOS":
platform = MobileProvisionPlatform.iOS;
break;
case "MacOSX":
platform = MobileProvisionPlatform.MacOS;
break;
case "MacCatalyst":
platform = MobileProvisionPlatform.MacOS;
break;
default:
Log.LogError (MSBStrings.E0048, SdkPlatform);
return false;
}
2016-04-21 16:40:25 +03:00
if (!string.IsNullOrEmpty (ProvisioningProfile)) {
if ((profile = GetMobileProvision (platform, ProvisioningProfile)) == null) {
2020-03-14 00:46:28 +03:00
Log.LogError (MSBStrings.E0049, ProvisioningProfile);
2016-04-21 16:40:25 +03:00
return false;
}
} else {
profile = null;
}
if (!string.IsNullOrEmpty (Entitlements)) {
if (!File.Exists (Entitlements)) {
2020-03-14 00:46:28 +03:00
Log.LogError (MSBStrings.E0112, Entitlements);
2016-04-21 16:40:25 +03:00
return false;
}
path = Entitlements;
} else {
path = DefaultEntitlementsPath;
}
try {
template = PDictionary.FromFile (path);
} catch (Exception ex) {
2020-03-14 00:46:28 +03:00
Log.LogError (MSBStrings.E0113, path, ex.Message);
2016-04-21 16:40:25 +03:00
return false;
}
compiled = GetCompiledEntitlements (profile, template);
archived = GetArchivedExpandedEntitlements (template, compiled);
try {
Directory.CreateDirectory (Path.GetDirectoryName (CompiledEntitlements.ItemSpec));
WriteXcent (compiled, CompiledEntitlements.ItemSpec);
2016-04-21 16:40:25 +03:00
} catch (Exception ex) {
2020-03-14 00:46:28 +03:00
Log.LogError (MSBStrings.E0114, CompiledEntitlements, ex.Message);
2016-04-21 16:40:25 +03:00
return false;
}
path = Path.Combine (EntitlementBundlePath, "archived-expanded-entitlements.xcent");
if (File.Exists (path)) {
var plist = PDictionary.FromFile (path);
var src = archived.ToXml ();
var dest = plist.ToXml ();
save = src != dest;
} else {
save = true;
}
if (save) {
try {
archived.Save (path, true);
} catch (Exception ex) {
2020-03-14 00:46:28 +03:00
Log.LogError (MSBStrings.E0115, ex.Message);
return false;
}
2016-04-21 16:40:25 +03:00
}
if (SdkIsSimulator) {
if (compiled.Count > 0) {
EntitlementsInExecutable = CompiledEntitlements;
}
} else {
EntitlementsInSignature = CompiledEntitlements;
}
2016-04-21 16:40:25 +03:00
return !Log.HasLoggedErrors;
}
}
}