We may end up trying to codesign the same item multiple times when codesigning universal .NET apps. Avoid this by deduplicating items to codesign. Fixes https://github.com/xamarin/xamarin-macios/issues/14522.
This commit is contained in:
Родитель
2f099ea61d
Коммит
50192c9f96
|
@ -2636,5 +2636,32 @@ namespace Xamarin.Localization.MSBuild {
|
|||
return ResourceManager.GetString("W7093", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Code signing has been requested multiple times for '{0}', with different metadata. The metadata for one are: '{1}', while the metadata for the other are: '{2}'.
|
||||
/// </summary>
|
||||
public static string W7095 {
|
||||
get {
|
||||
return ResourceManager.GetString("W7095", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Code signing has been requested multiple times for '{0}', with different metadata. The metadata '{1}={2}' has been set for one item, but not the other..
|
||||
/// </summary>
|
||||
public static string W7096 {
|
||||
get {
|
||||
return ResourceManager.GetString("W7096", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Code signing has been requested multiple times for '{0}', with different metadata. The metadata '{1}' has been values for each item (once it's '{2}', another time it's '{3}')..
|
||||
/// </summary>
|
||||
public static string W7097 {
|
||||
get {
|
||||
return ResourceManager.GetString("W7097", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1395,4 +1395,16 @@
|
|||
<data name="E7094" xml:space="preserve">
|
||||
<value>The file or directory '{0}' is not a framework nor a file within a framework.</value>
|
||||
</data>
|
||||
|
||||
<data name="W7095" xml:space="preserve">
|
||||
<value>Code signing has been requested multiple times for '{0}', with different metadata. The metadata for one are: '{1}', while the metadata for the other are: '{2}'</value>
|
||||
</data>
|
||||
|
||||
<data name="W7096" xml:space="preserve">
|
||||
<value>Code signing has been requested multiple times for '{0}', with different metadata. The metadata '{1}={2}' has been set for one item, but not the other.</value>
|
||||
</data>
|
||||
|
||||
<data name="W7097" xml:space="preserve">
|
||||
<value>Code signing has been requested multiple times for '{0}', with different metadata. The metadata '{1}' has been values for each item (once it's '{2}', another time it's '{3}').</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -135,6 +135,36 @@ namespace Xamarin.MacDev.Tasks {
|
|||
output.Add (item);
|
||||
}
|
||||
|
||||
// We may be asked to sign the same item multiple times (in particular for universal .NET builds)
|
||||
// Here we de-duplicate (based on itemspec). We also verify that the metadata is the same between
|
||||
// all deduplicated items, and if not, we show a warning.
|
||||
var grouped = output.GroupBy (v => v.ItemSpec);
|
||||
foreach (var group in grouped) {
|
||||
if (group.Count () < 2)
|
||||
continue;
|
||||
|
||||
var all = group.ToArray ();
|
||||
var firstMetadata = all [0].CloneCustomMetadataToDictionary ();
|
||||
for (var i = 1; i < all.Length; i++) {
|
||||
var nextMetadata = all [i].CloneCustomMetadataToDictionary ();
|
||||
if (nextMetadata.Count != firstMetadata.Count) {
|
||||
Log.LogWarning (MSBStrings.W7095, /* Code signing has been requested multiple times for '{0}', with different metadata. The metadata for one are: '{1}', while the metadata for the other are: '{2}' */ group.Key, string.Join (", ", firstMetadata.Keys), string.Join (", ", nextMetadata.Keys));
|
||||
} else {
|
||||
foreach (var kvp in firstMetadata) {
|
||||
if (!nextMetadata.TryGetValue (kvp.Key, out var nextValue)) {
|
||||
Log.LogWarning (MSBStrings.W7096, /* Code signing has been requested multiple times for '{0}', with different metadata. The metadata '{1}={2}' has been set for one item, but not the other. */ group.Key, kvp.Key, kvp.Value);
|
||||
continue;
|
||||
}
|
||||
if (nextValue != kvp.Value) {
|
||||
Log.LogWarning (MSBStrings.W7097, /* Code signing has been requested multiple times for '{0}', with different metadata. The metadata '{1}' has been values for each item (once it's '{2}', another time it's '{3}'). */ group.Key, kvp.Key, kvp.Value, nextValue);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
output.Remove (all [i]);
|
||||
}
|
||||
}
|
||||
|
||||
OutputCodesignItems = output.ToArray ();
|
||||
|
||||
return !Log.HasLoggedErrors;
|
||||
|
|
|
@ -355,68 +355,220 @@ namespace Xamarin.MacDev.Tasks {
|
|||
task.NativeStripItems = nativeStripItems.ToArray ();
|
||||
task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform, isDotNet).ToString ();
|
||||
Assert.IsTrue (task.Execute (), "Execute");
|
||||
Assert.AreEqual (0, Engine.Logger.WarningsEvents.Count, "Warning Count");
|
||||
|
||||
var outputCodesignItems = task.OutputCodesignItems;
|
||||
Assert.That (outputCodesignItems.Select (v => v.ItemSpec), Is.Unique, "Uniqueness");
|
||||
|
||||
var failures = new List<string> ();
|
||||
var itemsFound = new List<ITaskItem> ();
|
||||
foreach (var info in infos) {
|
||||
var item = outputCodesignItems.SingleOrDefault (v => string.Equals (v.ItemSpec, info.ItemSpec, StringComparison.OrdinalIgnoreCase));
|
||||
info.CodesignItem = item;
|
||||
if (IsPlatform (info.SignedOn, platform)) {
|
||||
if (item is null) {
|
||||
failures.Add ($"Expected '{info.ItemSpec}' to be signed.");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (item is not null) {
|
||||
failures.Add ($"Did not expect '{info.ItemSpec}' to be signed.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (item is null)
|
||||
continue;
|
||||
itemsFound.Add (item);
|
||||
|
||||
foreach (var kvp in info.Metadata) {
|
||||
var metadata = item.GetMetadata (kvp.Key);
|
||||
if (metadata == string.Empty && kvp.Value != string.Empty) {
|
||||
failures.Add ($"Item '{info.ItemSpec}': Expected metadata '{kvp.Key}' not found (with value '{kvp.Value}').");
|
||||
} else if (!string.Equals (metadata, kvp.Value)) {
|
||||
failures.Add ($"Item '{info.ItemSpec}': Expected value '{kvp.Value}' for metadata '{kvp.Key}', but got '{metadata}' instead.\nExpected: {kvp.Value}\nActual: {metadata}");
|
||||
}
|
||||
}
|
||||
|
||||
var customMetadata = item.CopyCustomMetadata ();
|
||||
var unexpectedMetadata = customMetadata.Keys.ToHashSet ();
|
||||
unexpectedMetadata.ExceptWith (info.Metadata.Keys);
|
||||
unexpectedMetadata.Remove ("OriginalItemSpec");
|
||||
foreach (var unexpected in unexpectedMetadata) {
|
||||
if (string.IsNullOrEmpty (customMetadata [unexpected]))
|
||||
continue;
|
||||
failures.Add ($"Item '{info.ItemSpec}': Unexpected metadata '{unexpected}' with value '{customMetadata [unexpected]}'.");
|
||||
}
|
||||
}
|
||||
|
||||
var itemsNotFound = outputCodesignItems.Where (v => !itemsFound.Contains (v)).ToArray ();
|
||||
foreach (var itemNotFound in itemsNotFound) {
|
||||
failures.Add ($"Did not expect '{itemNotFound.ItemSpec}' to be signed.");
|
||||
}
|
||||
|
||||
if (failures.Count > 0) {
|
||||
Console.WriteLine ($"{failures.Count} failures");
|
||||
foreach (var f in failures)
|
||||
Console.WriteLine (f);
|
||||
Console.WriteLine ($"{failures.Count} failures");
|
||||
}
|
||||
Assert.That (failures, Is.Empty, "Failures");
|
||||
VerifyCodesigningResults (infos, task.OutputCodesignItems, platform);
|
||||
} finally {
|
||||
Environment.CurrentDirectory = currentDir;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase (ApplePlatform.iOS, true)]
|
||||
[TestCase (ApplePlatform.iOS, false)]
|
||||
[TestCase (ApplePlatform.TVOS, true)]
|
||||
[TestCase (ApplePlatform.TVOS, false)]
|
||||
[TestCase (ApplePlatform.WatchOS, false)]
|
||||
[TestCase (ApplePlatform.MacOSX, true)]
|
||||
[TestCase (ApplePlatform.MacOSX, false)]
|
||||
[TestCase (ApplePlatform.MacCatalyst, true)]
|
||||
public void Duplicated (ApplePlatform platform, bool isDotNet)
|
||||
{
|
||||
var tmpdir = Cache.CreateTemporaryDirectory ();
|
||||
|
||||
var currentDir = Environment.CurrentDirectory;
|
||||
try {
|
||||
Environment.CurrentDirectory = tmpdir;
|
||||
var codesignItems = new List<ITaskItem> ();
|
||||
var codesignBundle = new List<ITaskItem> ();
|
||||
|
||||
string codeSignatureSubdirectory = string.Empty;
|
||||
switch (platform) {
|
||||
case ApplePlatform.MacCatalyst:
|
||||
case ApplePlatform.MacOSX:
|
||||
codeSignatureSubdirectory = "Contents/";
|
||||
break;
|
||||
}
|
||||
|
||||
var bundleAppMetadata = new Dictionary<string, string> {
|
||||
{ "RequireCodeSigning", "true" },
|
||||
};
|
||||
|
||||
var createDumpMetadata = new Dictionary<string, string> {
|
||||
{ "RequireCodeSigning", "true" },
|
||||
};
|
||||
|
||||
codesignItems = new List<ITaskItem> {
|
||||
new TaskItem ("Bundle.app/Contents/MonoBundle/createdump", createDumpMetadata),
|
||||
new TaskItem ("Bundle.app/Contents/MonoBundle/createdump", createDumpMetadata),
|
||||
};
|
||||
|
||||
codesignBundle = new List<ITaskItem> {
|
||||
new TaskItem ("Bundle.app", bundleAppMetadata),
|
||||
};
|
||||
|
||||
var infos = new CodesignInfo [] {
|
||||
new CodesignInfo ("Bundle.app", Platforms.All, bundleAppMetadata.Set ("CodesignStampFile", $"Bundle.app/{codeSignatureSubdirectory}_CodeSignature/CodeResources")),
|
||||
new CodesignInfo ("Bundle.app/Contents/MonoBundle/createdump", Platforms.All, createDumpMetadata.Set ("CodesignStampFile", "codesign-stamp-path/Bundle.app/Contents/MonoBundle/createdump")),
|
||||
};
|
||||
|
||||
var allFiles = infos.Select (v => v.ItemSpec).ToArray ();
|
||||
Touch (tmpdir, allFiles);
|
||||
|
||||
var task = CreateTask<ComputeCodesignItems> ();
|
||||
task.AppBundleDir = "Bundle.app";
|
||||
task.CodesignBundle = codesignBundle.ToArray ();
|
||||
task.CodesignItems = codesignItems.ToArray ();
|
||||
task.CodesignStampPath = "codesign-stamp-path/";
|
||||
task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform, isDotNet).ToString ();
|
||||
Assert.IsTrue (task.Execute (), "Execute");
|
||||
Assert.AreEqual (0, Engine.Logger.WarningsEvents.Count, "Warning Count");
|
||||
|
||||
VerifyCodesigningResults (infos, task.OutputCodesignItems, platform);
|
||||
} finally {
|
||||
Environment.CurrentDirectory = currentDir;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase (ApplePlatform.iOS, true)]
|
||||
[TestCase (ApplePlatform.iOS, false)]
|
||||
[TestCase (ApplePlatform.TVOS, true)]
|
||||
[TestCase (ApplePlatform.TVOS, false)]
|
||||
[TestCase (ApplePlatform.WatchOS, false)]
|
||||
[TestCase (ApplePlatform.MacOSX, true)]
|
||||
[TestCase (ApplePlatform.MacOSX, false)]
|
||||
[TestCase (ApplePlatform.MacCatalyst, true)]
|
||||
public void DuplicatedWithDifferentMetadata (ApplePlatform platform, bool isDotNet)
|
||||
{
|
||||
var tmpdir = Cache.CreateTemporaryDirectory ();
|
||||
|
||||
var currentDir = Environment.CurrentDirectory;
|
||||
try {
|
||||
Environment.CurrentDirectory = tmpdir;
|
||||
var codesignItems = new List<ITaskItem> ();
|
||||
var codesignBundle = new List<ITaskItem> ();
|
||||
|
||||
string codeSignatureSubdirectory = string.Empty;
|
||||
switch (platform) {
|
||||
case ApplePlatform.MacCatalyst:
|
||||
case ApplePlatform.MacOSX:
|
||||
codeSignatureSubdirectory = "Contents/";
|
||||
break;
|
||||
}
|
||||
|
||||
var bundleAppMetadata = new Dictionary<string, string> {
|
||||
{ "RequireCodeSigning", "true" },
|
||||
};
|
||||
|
||||
var createDumpMetadata1 = new Dictionary<string, string> {
|
||||
{ "RequireCodeSigning", "true" },
|
||||
{ "OnlyIn1", "true" },
|
||||
{ "InOneAndTwoWithDifferentValues", "1" },
|
||||
};
|
||||
var createDumpMetadata2 = new Dictionary<string, string> {
|
||||
{ "RequireCodeSigning", "true" },
|
||||
{ "OnlyIn2", "true" },
|
||||
{ "InOneAndTwoWithDifferentValues", "2" },
|
||||
};
|
||||
var createDumpMetadata3 = new Dictionary<string, string> {
|
||||
{ "RequireCodeSigning", "true" },
|
||||
};
|
||||
|
||||
codesignItems = new List<ITaskItem> {
|
||||
new TaskItem ("Bundle.app/Contents/MonoBundle/createdump", createDumpMetadata1),
|
||||
new TaskItem ("Bundle.app/Contents/MonoBundle/createdump", createDumpMetadata2),
|
||||
new TaskItem ("Bundle.app/Contents/MonoBundle/createdump", createDumpMetadata3),
|
||||
};
|
||||
|
||||
codesignBundle = new List<ITaskItem> {
|
||||
new TaskItem ("Bundle.app", bundleAppMetadata),
|
||||
};
|
||||
|
||||
var infos = new CodesignInfo [] {
|
||||
new CodesignInfo ("Bundle.app", Platforms.All, bundleAppMetadata.Set ("CodesignStampFile", $"Bundle.app/{codeSignatureSubdirectory}_CodeSignature/CodeResources")),
|
||||
new CodesignInfo ("Bundle.app/Contents/MonoBundle/createdump", Platforms.All, createDumpMetadata1.Set ("CodesignStampFile", "codesign-stamp-path/Bundle.app/Contents/MonoBundle/createdump")),
|
||||
};
|
||||
|
||||
var allFiles = infos.Select (v => v.ItemSpec).ToArray ();
|
||||
Touch (tmpdir, allFiles);
|
||||
|
||||
var task = CreateTask<ComputeCodesignItems> ();
|
||||
task.AppBundleDir = "Bundle.app";
|
||||
task.CodesignBundle = codesignBundle.ToArray ();
|
||||
task.CodesignItems = codesignItems.ToArray ();
|
||||
task.CodesignStampPath = "codesign-stamp-path/";
|
||||
task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform, isDotNet).ToString ();
|
||||
Assert.IsTrue (task.Execute (), "Execute");
|
||||
Assert.AreEqual (3, Engine.Logger.WarningsEvents.Count, "Warning Count");
|
||||
Assert.AreEqual ("Code signing has been requested multiple times for 'Bundle.app/Contents/MonoBundle/createdump', with different metadata. The metadata 'OnlyIn1=true' has been set for one item, but not the other.", Engine.Logger.WarningsEvents [0].Message, "Message #0");
|
||||
Assert.AreEqual ("Code signing has been requested multiple times for 'Bundle.app/Contents/MonoBundle/createdump', with different metadata. The metadata 'InOneAndTwoWithDifferentValues' has been values for each item (once it's '1', another time it's '2').", Engine.Logger.WarningsEvents [1].Message, "Message #1");
|
||||
Assert.AreEqual ("Code signing has been requested multiple times for 'Bundle.app/Contents/MonoBundle/createdump', with different metadata. The metadata for one are: 'RequireCodeSigning, OnlyIn1, InOneAndTwoWithDifferentValues, CodesignStampFile', while the metadata for the other are: 'RequireCodeSigning, CodesignStampFile'", Engine.Logger.WarningsEvents [2].Message, "Message #2");
|
||||
|
||||
VerifyCodesigningResults (infos, task.OutputCodesignItems, platform);
|
||||
} finally {
|
||||
Environment.CurrentDirectory = currentDir;
|
||||
}
|
||||
}
|
||||
void VerifyCodesigningResults (CodesignInfo [] infos, ITaskItem[] outputCodesignItems, ApplePlatform platform)
|
||||
{
|
||||
Assert.That (outputCodesignItems.Select (v => v.ItemSpec), Is.Unique, "Uniqueness");
|
||||
|
||||
var failures = new List<string> ();
|
||||
var itemsFound = new List<ITaskItem> ();
|
||||
foreach (var info in infos) {
|
||||
var item = outputCodesignItems.SingleOrDefault (v => string.Equals (v.ItemSpec, info.ItemSpec, StringComparison.OrdinalIgnoreCase));
|
||||
info.CodesignItem = item;
|
||||
if (IsPlatform (info.SignedOn, platform)) {
|
||||
if (item is null) {
|
||||
failures.Add ($"Expected '{info.ItemSpec}' to be signed.");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (item is not null) {
|
||||
failures.Add ($"Did not expect '{info.ItemSpec}' to be signed.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (item is null)
|
||||
continue;
|
||||
itemsFound.Add (item);
|
||||
|
||||
foreach (var kvp in info.Metadata) {
|
||||
var metadata = item.GetMetadata (kvp.Key);
|
||||
if (metadata == string.Empty && kvp.Value != string.Empty) {
|
||||
failures.Add ($"Item '{info.ItemSpec}': Expected metadata '{kvp.Key}' not found (with value '{kvp.Value}').");
|
||||
} else if (!string.Equals (metadata, kvp.Value)) {
|
||||
failures.Add ($"Item '{info.ItemSpec}': Expected value '{kvp.Value}' for metadata '{kvp.Key}', but got '{metadata}' instead.\nExpected: {kvp.Value}\nActual: {metadata}");
|
||||
}
|
||||
}
|
||||
|
||||
var customMetadata = item.CopyCustomMetadata ();
|
||||
var unexpectedMetadata = customMetadata.Keys.ToHashSet ();
|
||||
unexpectedMetadata.ExceptWith (info.Metadata.Keys);
|
||||
unexpectedMetadata.Remove ("OriginalItemSpec");
|
||||
foreach (var unexpected in unexpectedMetadata) {
|
||||
if (string.IsNullOrEmpty (customMetadata [unexpected]))
|
||||
continue;
|
||||
failures.Add ($"Item '{info.ItemSpec}': Unexpected metadata '{unexpected}' with value '{customMetadata [unexpected]}'.");
|
||||
}
|
||||
}
|
||||
|
||||
var itemsNotFound = outputCodesignItems.Where (v => !itemsFound.Contains (v)).ToArray ();
|
||||
foreach (var itemNotFound in itemsNotFound) {
|
||||
failures.Add ($"Did not expect '{itemNotFound.ItemSpec}' to be signed.");
|
||||
}
|
||||
|
||||
if (failures.Count > 0) {
|
||||
Console.WriteLine ($"{failures.Count} failures");
|
||||
foreach (var f in failures)
|
||||
Console.WriteLine (f);
|
||||
Console.WriteLine ($"{failures.Count} failures");
|
||||
}
|
||||
Assert.That (failures, Is.Empty, "Failures");
|
||||
}
|
||||
|
||||
bool IsPlatform (Platforms platforms, ApplePlatform platform)
|
||||
{
|
||||
switch (platform) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче