Skip to content

Commit f953e5d

Browse files
committed
[msbuild] Sign simulator apps by default. Fixes #18469.
Fixes #18469.
1 parent ed404de commit f953e5d

File tree

8 files changed

+133
-104
lines changed

8 files changed

+133
-104
lines changed

msbuild/Xamarin.MacDev.Tasks/Tasks/Codesign.cs

+17-8
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class Codesign : XamarinParallelTask, ITaskCallback, ICancelableTask {
4545
public string Keychain { get; set; } = string.Empty;
4646

4747
[Required]
48-
public ITaskItem [] Resources { get; set; } = Array.Empty<ITaskItem> ();
48+
public ITaskItem? [] Resources { get; set; } = Array.Empty<ITaskItem> ();
4949

5050
// Can also be specified per resource using the 'CodesignResourceRules' metadata
5151
public string ResourceRules { get; set; } = string.Empty;
@@ -127,9 +127,12 @@ bool Validate (SignInfo info)
127127
}
128128

129129
// 'sortedItems' is sorted by length of path, longest first.
130-
bool NeedsCodesign (ITaskItem [] sortedItems, int index, string stampFileContents)
130+
bool NeedsCodesign (ITaskItem? [] sortedItems, int index, string stampFileContents)
131131
{
132132
var item = sortedItems [index];
133+
if (item is null)
134+
return false;
135+
133136
var stampFile = GetCodesignStampFile (item);
134137
if (!File.Exists (stampFile)) {
135138
Log.LogMessage (MessageImportance.Low, "The stamp file '{0}' does not exist, so the item '{1}' needs to be codesigned.", stampFile, item.ItemSpec);
@@ -150,10 +153,11 @@ bool NeedsCodesign (ITaskItem [] sortedItems, int index, string stampFileContent
150153
var resolvedStampFile = Path.GetFullPath (PathUtils.ResolveSymbolicLinks (stampFile));
151154

152155
for (var i = 0; i < index; i++) {
153-
if (sortedItems [i] is null)
156+
var sortedItem = sortedItems [i];
157+
if (sortedItem is null)
154158
continue; // this item does not need to be signed
155-
if (sortedItems [i].ItemSpec.StartsWith (itemPath, StringComparison.OrdinalIgnoreCase)) {
156-
Log.LogMessage (MessageImportance.Low, "The item '{0}' contains '{1}', which must be signed, which means that the item must be signed too.", item.ItemSpec, sortedItems [i].ItemSpec);
159+
if (sortedItem.ItemSpec.StartsWith (itemPath, StringComparison.OrdinalIgnoreCase)) {
160+
Log.LogMessage (MessageImportance.Low, "The item '{0}' contains '{1}', which must be signed, which means that the item must be signed too.", item.ItemSpec, sortedItem.ItemSpec);
157161
return true; // there's an item inside this directory that needs to be signed, so this directory must be signed too
158162
}
159163
}
@@ -402,7 +406,7 @@ bool ExecuteUnsafe ()
402406
// All this makes it easier to sort and split the input files into buckets that can be codesigned together,
403407
// while also not codesigning directories before files inside them.
404408
foreach (var res in resourcesToSign) {
405-
var path = res.ItemSpec;
409+
var path = res!.ItemSpec;
406410
var parent = Path.GetDirectoryName (path);
407411

408412
// so do not don't sign `A.framework/A`, sign `A.framework` which will always sign the *bundle*
@@ -416,17 +420,22 @@ bool ExecuteUnsafe ()
416420
}
417421

418422
// first sort all the items by path length, longest path first.
419-
resourcesToSign = resourcesToSign.OrderBy (v => v.ItemSpec.Length).Reverse ().ToArray ();
423+
resourcesToSign = resourcesToSign.OrderBy (v => v!.ItemSpec.Length).Reverse ().ToArray ();
420424

421425
// remove items that are up-to-date
422426
var itemsToSign = new List<SignInfo> ();
423427
for (var i = 0; i < resourcesToSign.Length; i++) {
424428
var item = resourcesToSign [i];
429+
if (item is null)
430+
continue;
425431
var info = new SignInfo (item);
426432
if (!Validate (info))
427433
continue;
428-
if (NeedsCodesign (resourcesToSign, i, info.GetStampFileContents (this)))
434+
if (NeedsCodesign (resourcesToSign, i, info.GetStampFileContents (this))) {
429435
itemsToSign.Add (info);
436+
} else {
437+
resourcesToSign [i] = new TaskItem ("");
438+
}
430439
}
431440

432441
if (Log.HasLoggedErrors)

msbuild/Xamarin.MacDev.Tasks/Tasks/CompileEntitlements.cs

+41-18
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,22 @@ protected string EntitlementBundlePath {
137137
}
138138
}
139139

140+
bool IsDeviceOrDesktop {
141+
get {
142+
switch (Platform) {
143+
case ApplePlatform.iOS:
144+
case ApplePlatform.TVOS:
145+
case ApplePlatform.WatchOS:
146+
return !SdkIsSimulator;
147+
case ApplePlatform.MacOSX:
148+
case ApplePlatform.MacCatalyst:
149+
return true;
150+
default:
151+
throw new InvalidOperationException (string.Format (MSBStrings.InvalidPlatform, Platform));
152+
}
153+
}
154+
}
155+
140156
PString MergeEntitlementString (PString pstr, MobileProvision? profile, bool expandWildcards, string? key)
141157
{
142158
string TeamIdentifierPrefix;
@@ -145,7 +161,7 @@ PString MergeEntitlementString (PString pstr, MobileProvision? profile, bool exp
145161
if (string.IsNullOrEmpty (pstr.Value))
146162
return (PString) pstr.Clone ();
147163

148-
if (profile is null) {
164+
if (profile is null && IsDeviceOrDesktop) {
149165
if (!warnedTeamIdentifierPrefix && pstr.Value.Contains ("$(TeamIdentifierPrefix)")) {
150166
Log.LogWarning (null, null, null, Entitlements, 0, 0, 0, 0, MSBStrings.W0108b /* Cannot expand $(TeamIdentifierPrefix) in Entitlements.plist without a provisioning profile for key '{0}' with value '{1}' */, key, pstr.Value);
151167
warnedTeamIdentifierPrefix = true;
@@ -455,7 +471,7 @@ public override bool Execute ()
455471
MobileProvision? profile;
456472
PDictionary template;
457473
PDictionary compiled;
458-
PDictionary archived;
474+
PDictionary? archived = null;
459475
string path;
460476

461477
switch (SdkPlatform) {
@@ -509,7 +525,26 @@ public override bool Execute ()
509525

510526
ValidateAppEntitlements (profile, compiled);
511527

512-
archived = GetArchivedExpandedEntitlements (template, compiled);
528+
Directory.CreateDirectory (Path.GetDirectoryName (CompiledEntitlements!.ItemSpec));
529+
530+
if (SdkIsSimulator) {
531+
var simulatedEntitlements = compiled;
532+
var simulatedXcent = Path.ChangeExtension (CompiledEntitlements.ItemSpec, "").TrimEnd ('.') + "-Simulated.xcent";
533+
try {
534+
WriteXcent (simulatedEntitlements, simulatedXcent);
535+
} catch (Exception ex) {
536+
Log.LogError (MSBStrings.E0114, simulatedXcent, ex.Message);
537+
return false;
538+
}
539+
540+
EntitlementsInExecutable = new TaskItem (simulatedXcent);
541+
542+
// No matter what, I've only been able to make Xcode apply a single entitlement to simulator builds: com.apple.security.get-task-allow
543+
compiled = new PDictionary ();
544+
compiled.Add ("com.apple.security.get-task-allow", new PBoolean (true));
545+
} else {
546+
archived = GetArchivedExpandedEntitlements (template, compiled);
547+
}
513548

514549
try {
515550
Directory.CreateDirectory (Path.GetDirectoryName (CompiledEntitlements!.ItemSpec));
@@ -519,22 +554,10 @@ public override bool Execute ()
519554
return false;
520555
}
521556

522-
SaveArchivedExpandedEntitlements (archived);
523-
524-
/* The path to the entitlements must be resolved to the full path, because we might want to reference it from a containing project that just references this project,
525-
* and in that case it becomes a bit complicated to resolve to a full path on disk when building remotely from Windows. Instead just resolve to a full path here,
526-
* and use that from now on. This has to be done from a task, so that we get the full path on the mac when executed remotely from Windows. */
527-
var compiledEntitlementsFullPath = new TaskItem (Path.GetFullPath (CompiledEntitlements!.ItemSpec));
557+
if (archived is not null)
558+
SaveArchivedExpandedEntitlements (archived);
528559

529-
if (Platform == Utils.ApplePlatform.MacCatalyst) {
530-
EntitlementsInSignature = compiledEntitlementsFullPath;
531-
} else if (SdkIsSimulator) {
532-
if (compiled.Count > 0) {
533-
EntitlementsInExecutable = compiledEntitlementsFullPath;
534-
}
535-
} else {
536-
EntitlementsInSignature = compiledEntitlementsFullPath;
537-
}
560+
EntitlementsInSignature = CompiledEntitlements;
538561

539562
return !Log.HasLoggedErrors;
540563
}

msbuild/Xamarin.MacDev.Tasks/Tasks/DetectSigningIdentity.cs

+1-56
Original file line numberDiff line numberDiff line change
@@ -608,62 +608,7 @@ bool ExecuteImpl ()
608608
return !Log.HasLoggedErrors;
609609
}
610610
} else {
611-
// Framework is either iOS or tvOS
612-
if (SdkIsSimulator) {
613-
if (AppleSdkSettings.XcodeVersion.Major >= 8 && RequireProvisioningProfile) {
614-
// Note: Starting with Xcode 8.0, we need to codesign iOS Simulator builds that enable Entitlements
615-
// in order for them to run. The "-" key is a special value allowed by the codesign utility that
616-
// allows us to get away with not having an actual codesign key.
617-
DetectedCodeSigningKey = "-";
618-
619-
if (!IsAutoCodeSignProfile (ProvisioningProfile)) {
620-
identity.Profile = MobileProvisionIndex.GetMobileProvision (platform, ProvisioningProfile);
621-
622-
if (identity.Profile is null) {
623-
Log.LogError (MSBStrings.E0140, PlatformName, ProvisioningProfile);
624-
return false;
625-
}
626-
627-
identity.AppId = ConstructValidAppId (identity.Profile, identity.BundleId);
628-
if (identity.AppId is null) {
629-
Log.LogError (MSBStrings.E0141, identity.BundleId, ProvisioningProfile);
630-
return false;
631-
}
632-
633-
provisioningProfileName = identity.Profile.Name;
634-
635-
DetectedProvisioningProfile = identity.Profile.Uuid;
636-
DetectedDistributionType = identity.Profile.DistributionType.ToString ();
637-
} else {
638-
certs = new X509Certificate2 [0];
639-
640-
if ((profiles = GetProvisioningProfiles (platform, type, identity, certs)) is null)
641-
return false;
642-
643-
if ((pairs = GetCodeSignIdentityPairs (profiles, certs)) is null)
644-
return false;
645-
646-
var match = GetBestMatch (pairs, identity);
647-
identity.Profile = match.Profile;
648-
identity.AppId = match.AppId;
649-
650-
if (identity.Profile is not null) {
651-
DetectedDistributionType = identity.Profile.DistributionType.ToString ();
652-
DetectedProvisioningProfile = identity.Profile.Uuid;
653-
provisioningProfileName = identity.Profile.Name;
654-
}
655-
656-
DetectedAppId = identity.AppId;
657-
}
658-
} else {
659-
// Note: Do not codesign. Codesigning seems to break the iOS Simulator in older versions of Xcode.
660-
DetectedCodeSigningKey = null;
661-
}
662-
663-
return !Log.HasLoggedErrors;
664-
}
665-
666-
if (!SdkIsSimulator && !RequireCodeSigning) {
611+
if (SdkIsSimulator || !RequireCodeSigning) {
667612
// The "-" key is a special value allowed by the codesign utility that
668613
// allows us to get away with not having an actual codesign key.
669614
DetectedCodeSigningKey = "-";

msbuild/Xamarin.MacDev.Tasks/Tasks/LinkNativeCode.cs

+30-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ bool ExecuteUnsafe ()
202202
foreach (var obj in ObjectFiles)
203203
arguments.Add (Path.GetFullPath (obj.ItemSpec));
204204

205-
arguments.AddRange (GetEmbedEntitlementsInExecutableLinkerFlags (EntitlementsInExecutable));
205+
arguments.AddRange (GetEmbedEntitlementsWithDerInExecutableLinkerFlags (EntitlementsInExecutable));
206206

207207
arguments.Add ("-o");
208208
arguments.Add (Path.GetFullPath (OutputFile));
@@ -242,6 +242,20 @@ bool ExecuteUnsafe ()
242242
return !Log.HasLoggedErrors;
243243
}
244244

245+
IEnumerable<string> GetEmbedEntitlementsWithDerInExecutableLinkerFlags (string entitlements)
246+
{
247+
var rv = GetEmbedEntitlementsInExecutableLinkerFlags (entitlements).ToList ();
248+
if (rv.Count > 0) {
249+
rv.AddRange (new string [] {
250+
"-Xlinker", "-sectcreate",
251+
"-Xlinker", "__TEXT",
252+
"-Xlinker", "__ents_der",
253+
"-Xlinker", ConvertEntitlementsToDerEntitlements (Path.GetFullPath (entitlements)),
254+
});
255+
}
256+
return rv;
257+
}
258+
245259
public static string [] GetEmbedEntitlementsInExecutableLinkerFlags (string entitlements)
246260
{
247261
if (string.IsNullOrEmpty (entitlements))
@@ -258,6 +272,21 @@ public static string [] GetEmbedEntitlementsInExecutableLinkerFlags (string enti
258272
};
259273
}
260274

275+
string ConvertEntitlementsToDerEntitlements (string entitlements)
276+
{
277+
var derEntitlements = entitlements + ".der";
278+
var arguments = new List<string> () {
279+
"derq",
280+
"query",
281+
"-f", "xml",
282+
"-i", entitlements,
283+
"-o", derEntitlements,
284+
"--raw",
285+
};
286+
ExecuteAsync ("xcrun", arguments, sdkDevPath: SdkDevPath).Wait ();
287+
return derEntitlements;
288+
}
289+
261290
static bool EntitlementsRequireLinkerFlags (string path)
262291
{
263292
try {

msbuild/Xamarin.Shared/Xamarin.Shared.props

+3-5
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,12 @@ Copyright (C) 2020 Microsoft. All rights reserved.
135135
</PropertyGroup>
136136

137137
<!-- RequireCodeSigning -->
138-
<!-- iOS/watchOS/tvOS is simple: device builds require code signing, simulator builds do not. This is a big lie, for some simulator builds need to be signed, but the _DetectCodeSigning task handles those cases. -->
138+
<!-- iOS/watchOS/tvOS is simple: device builds require code signing, simulator builds technically don't even though some important features won't work unless the app is signed (launch screen won't show for instance) -->
139139
<PropertyGroup Condition="'$(_PlatformName)' != 'macOS' And '$(_PlatformName)' != 'MacCatalyst'">
140140
<!-- Make it possible to override the default logic by setting EnableCodeSigning -->
141141
<_RequireCodeSigning Condition="'$(_RequireCodeSigning)' == ''">$(EnableCodeSigning)</_RequireCodeSigning>
142-
<!-- Device builds must be signed -->
143-
<_RequireCodeSigning Condition="'$(_RequireCodeSigning)' == '' And '$(ComputedPlatform)' == 'iPhone'">true</_RequireCodeSigning>
144-
<!-- Otherwise code signing is disabled by default (simulator builds)-->
145-
<_RequireCodeSigning Condition="'$(_RequireCodeSigning)' == ''">false</_RequireCodeSigning>
142+
<!-- Device builds must be signed, and some features won't work in the simulator if the app isn't signed (launch screen for instance), so default to always sign -->
143+
<_RequireCodeSigning Condition="'$(_RequireCodeSigning)' == ''">true</_RequireCodeSigning>
146144
</PropertyGroup>
147145
<!-- macOS is a bit more complicated:
148146
* 'EnableCodeSigning' specifies whether the app is signed or not, and this defaults to false if it's not set.

tests/dotnet/UnitTests/BundleStructureTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ public enum CodeSignature {
595595
[Test]
596596
// Debug
597597
[TestCase (ApplePlatform.iOS, "ios-arm64", CodeSignature.All, "Debug")]
598-
[TestCase (ApplePlatform.iOS, "iossimulator-x64", CodeSignature.Frameworks, "Debug")]
598+
[TestCase (ApplePlatform.iOS, "iossimulator-x64", CodeSignature.All, "Debug")]
599599
[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64", CodeSignature.All, "Debug")]
600600
[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64;maccatalyst-arm64", CodeSignature.All, "Debug")]
601601
[TestCase (ApplePlatform.MacOSX, "osx-x64", CodeSignature.Frameworks, "Debug")]

tests/monotouch-test/dotnet/shared.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
<!-- Don't remove native symbols, because it makes debugging native crashes harder -->
1717
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
1818

19+
<CodesignEntitlements Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'iOS' ">$(MonoTouchTestDirectory)\Entitlements.plist</CodesignEntitlements>
20+
<CodesignEntitlements Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tvOS'">$(MonoTouchTestDirectory)\Entitlements.plist</CodesignEntitlements>
21+
1922
<DefineConstants Condition="'$(Configuration)' == 'Debug'">$(DefineConstants);DEBUG</DefineConstants>
2023

2124
<!-- warning CA1422: This call site is reachable on: '...': we use APIs that aren't available on a certain OS platform all the time (in some cases to verify any broken behavior), so ignore such warnings -->

0 commit comments

Comments
 (0)