Skip to content
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
27 changes: 27 additions & 0 deletions msbuild/Xamarin.Localization.MSBuild/MSBStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1428,4 +1428,29 @@
<data name="E7101" xml:space="preserve">
<value>Unknown property '{0}' with value '{1}'.</value>
</data>

<data name="E7102" xml:space="preserve">
<value>Invalid value '{0}' for the entitlement '{1}' of type '{2}' specified in the CustomEntitlements item group. Expected no value at all.</value>
<comment>
Don't translate: CustomEntitlements (name of option in project file)
</comment>
</data>

<data name="E7103" xml:space="preserve">
<value>Invalid value '{0}' for the entitlement '{1}' of type '{2}' specified in the CustomEntitlements item group. Expected 'true' or 'false'.</value>
<comment>
Don't translate:
* CustomEntitlements (name of option in project file)
* 'true', 'false'
</comment>
</data>

<data name="E7104" xml:space="preserve">
<value>Unknown type '{0}' for the entitlement '{1}' specified in the CustomEntitlements item group. Expected 'Remove', 'Boolean', 'String', or 'StringArray'.</value>
<comment>
Don't translate:
* CustomEntitlements (name of option in project file)
* 'Remove', 'Boolean', 'String', 'StringArray'
</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public abstract class CompileEntitlementsTaskBase : XamarinTask
[Required]
public ITaskItem? CompiledEntitlements { get; set; }

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

public bool Debug { get; set; }

public string Entitlements { get; set; } = string.Empty;
Expand Down Expand Up @@ -250,6 +252,64 @@ PDictionary MergeEntitlementDictionary (PDictionary dict, MobileProvision? profi
return result;
}

void AddCustomEntitlements (PDictionary dict)
{
if (CustomEntitlements is null)
return;

// Process any custom entitlements from the 'CustomEntitlements' item group. These are applied last, and will override anything else.
// Possible values:
// <ItemGroup>
// <CustomEntitlements Include="name.of.entitlement" Type="Boolean" Value="true" /> <!-- value can be 'false' too (case doesn't matter) -->
// <CustomEntitlements Include="name.of.entitlement" Type="String" Value="stringvalue" />
// <CustomEntitlements Include="name.of.entitlement" Type="StringArray" Value="a;b" /> <!-- array of strings, separated by semicolon -->
// <CustomEntitlements Include="name.of.entitlement" Type="StringArray" Value="a😁b" ArraySeparator="😁" /> <!-- array of strings, separated by 😁 -->
// <CustomEntitlements Include="name.of.entitlement" Type="Remove" /> <!-- This will remove the corresponding entitlement -->
// </ItemGroup>

foreach (var item in CustomEntitlements) {
var entitlement = item.ItemSpec;
var type = item.GetMetadata ("Type");
var value = item.GetMetadata ("Value");
switch (type.ToLowerInvariant ()) {
case "remove":
if (!string.IsNullOrEmpty (value))
Log.LogError (MSBStrings.E7102, /* Invalid value '{0}' for the entitlement '{1}' of type '{2}' specified in the CustomEntitlements item group. Expected no value at all. */ value, entitlement, type);
dict.Remove (entitlement);
break;
case "boolean":
bool booleanValue;
if (string.Equals (value, "true", StringComparison.OrdinalIgnoreCase)) {
booleanValue = true;
} else if (string.Equals (value, "false", StringComparison.OrdinalIgnoreCase)) {
booleanValue = false;
Comment on lines +281 to +285

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use https://docs.microsoft.com/en-us/dotnet/api/system.boolean.tryparse?view=net-6.0 look like it supports several representations for bool:

// The example displays the following output:
//       Unable to parse '<null>'.
//       Unable to parse ''.
//       'True' --> True
//       'False' --> False
//       'true' --> True
//       'false' --> False
//       '    true    ' --> True
//       Unable to parse '0'.
//       Unable to parse '1'.
//       Unable to parse '-1'.
//       Unable to parse 'string'.

} else {
Log.LogError (MSBStrings.E7103, /* "Invalid value '{0}' for the entitlement '{1}' of type '{2}' specified in the CustomEntitlements item group. Expected 'true' or 'false'." */ value, entitlement, type);
continue;
}

dict [entitlement] = new PBoolean (booleanValue);
break;
case "string":
dict [entitlement] = new PString (value ?? string.Empty);
break;
case "stringarray":
var arraySeparator = item.GetMetadata ("ArraySeparator");
if (string.IsNullOrEmpty (arraySeparator))
arraySeparator = ";";
var arrayContent = value.Split (new string[] { arraySeparator }, StringSplitOptions.None);
var parray = new PArray ();
foreach (var element in arrayContent)
parray.Add (new PString (element));
dict [entitlement] = parray;
break;
default:
Log.LogError (MSBStrings.E7104, /* "Unknown type '{0}' for the entitlement '{1}' specified in the CustomEntitlements item group. Expected 'Remove', 'Boolean', 'String', or 'StringArray'." */ type, entitlement);
break;
}
}
}

static bool AreEqual (byte[] x, byte[] y)
{
if (x.Length != y.Length)
Expand Down Expand Up @@ -356,6 +416,8 @@ protected virtual PDictionary GetCompiledEntitlements (MobileProvision? profile,
break;
}

AddCustomEntitlements (entitlements);

return entitlements;
}

Expand Down
6 changes: 6 additions & 0 deletions msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -645,12 +645,18 @@ Copyright (C) 2018 Microsoft. All rights reserved.
Condition="'$(_RequireCodeSigning)' == 'true' Or '$(CodesignEntitlements)' != ''"
DependsOnTargets="$(_CompileEntitlementsDependsOn)"
Outputs="$(DeviceSpecificIntermediateOutputPath)Entitlements.xcent">
<!-- Automatically add the 'allow-jit' entitlement for desktop release builds in .NET -->
<ItemGroup Condition="'$(Configuration)' == 'Release' And ('$(_PlatformName)' == 'macOS' Or '$(_PlatformName)' == 'MacCatalyst') And '$(UsingAppleNETSdk)' == 'true'">
<!-- We need to compare the result of AnyHaveMetadataValue to 'true', because if it's empty, the return value is not a boolean, it's an empty string -->
<CustomEntitlements Condition="'@(CustomEntitlements->AnyHaveMetadataValue('Identity','com.apple.security.cs.allow-jit'))' != 'true'" Include="com.apple.security.cs.allow-jit" Type="Boolean" Value="true" />
</ItemGroup>
<CompileEntitlements
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true'"
AppBundleDir="$(AppBundleDir)"
AppIdentifier="$(_AppIdentifier)"
BundleIdentifier="$(_BundleIdentifier)"
CustomEntitlements="@(CustomEntitlements)"
Entitlements="$(CodesignEntitlements)"
CompiledEntitlements="$(DeviceSpecificIntermediateOutputPath)Entitlements.xcent"
IsAppExtension="$(IsAppExtension)"
Expand Down
19 changes: 19 additions & 0 deletions tests/dotnet/Entitlements/AppDelegate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Runtime.InteropServices;

using Foundation;

namespace MySimpleApp
{
public class Program
{
static int Main (string[] args)
{
GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly

Console.WriteLine (Environment.GetEnvironmentVariable ("MAGIC_WORD"));

return args.Length;
}
}
}
7 changes: 7 additions & 0 deletions tests/dotnet/Entitlements/MacCatalyst/Entitlements.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
6 changes: 6 additions & 0 deletions tests/dotnet/Entitlements/MacCatalyst/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>
1 change: 1 addition & 0 deletions tests/dotnet/Entitlements/MacCatalyst/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
2 changes: 2 additions & 0 deletions tests/dotnet/Entitlements/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
TOP=../../..
include $(TOP)/tests/common/shared-dotnet-test.mk
7 changes: 7 additions & 0 deletions tests/dotnet/Entitlements/iOS/Entitlements.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-ios</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
6 changes: 6 additions & 0 deletions tests/dotnet/Entitlements/iOS/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>
1 change: 1 addition & 0 deletions tests/dotnet/Entitlements/iOS/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
7 changes: 7 additions & 0 deletions tests/dotnet/Entitlements/macOS/Entitlements.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-macos</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
6 changes: 6 additions & 0 deletions tests/dotnet/Entitlements/macOS/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>
1 change: 1 addition & 0 deletions tests/dotnet/Entitlements/macOS/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
13 changes: 13 additions & 0 deletions tests/dotnet/Entitlements/shared.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<OutputType>Exe</OutputType>
<ApplicationId>Entitlements</ApplicationId>
</PropertyGroup>

<Import Project="../../common/shared-dotnet.csproj" />

<ItemGroup>
<Compile Include="../*.cs" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions tests/dotnet/Entitlements/shared.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TOP=../../../..
TESTNAME=AutoDetectEntitlements
include $(TOP)/tests/common/shared-dotnet.mk
7 changes: 7 additions & 0 deletions tests/dotnet/Entitlements/tvOS/Entitlements.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-tvos</TargetFramework>
</PropertyGroup>
<Import Project="..\shared.csproj" />
</Project>
6 changes: 6 additions & 0 deletions tests/dotnet/Entitlements/tvOS/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>
1 change: 1 addition & 0 deletions tests/dotnet/Entitlements/tvOS/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../shared.mk
25 changes: 25 additions & 0 deletions tests/dotnet/UnitTests/ProjectTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -996,5 +996,30 @@ public void CustomAppBundleDir (ApplePlatform platform, string runtimeIdentifier
properties ["AppBundleDir"] = customAppBundleDir;
var result = DotNet.AssertBuild (project_path, properties);
}

[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64", "Release")]
[TestCase (ApplePlatform.MacOSX, "osx-arm64", "Debug")]
public void AutoAllowJitEntitlements (ApplePlatform platform, string runtimeIdentifiers, string configuration)
{
var project = "Entitlements";
Configuration.IgnoreIfIgnoredPlatform (platform);

var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration);
Clean (project_path);

var properties = GetDefaultProperties (runtimeIdentifiers);
properties ["Configuration"] = configuration;
DotNet.AssertBuild (project_path, properties);

var executable = GetNativeExecutable (platform, appPath);
var foundEntitlements = TryGetEntitlements (executable, out var entitlements);
if (configuration == "Release") {
Assert.IsTrue (foundEntitlements, "Found in Release");
Assert.IsTrue (entitlements!.Get<PBoolean>("com.apple.security.cs.allow-jit")?.Value, "Jit Allowed");
} else {
var jitNotAllowed = !foundEntitlements || !entitlements!.ContainsKey ("com.apple.security.cs.allow-jit");
Assert.True (jitNotAllowed, "Jit Not Allowed");
}
}
}
}
22 changes: 22 additions & 0 deletions tests/dotnet/UnitTests/TestBaseClass.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#nullable enable

using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

using Mono.Cecil;

using Xamarin.MacDev;
using Xamarin.Tests;

namespace Xamarin.Tests {
Expand Down Expand Up @@ -347,5 +349,25 @@ protected bool IsRuntimeIdentifierSigned (string runtimeIdentifiers)
}
return false;
}

protected bool TryGetEntitlements (string nativeExecutable, [NotNullWhen (true)] out PDictionary? entitlements)
{
var entitlementsPath = Path.Combine (Cache.CreateTemporaryDirectory (), "EntitlementsInBinary.plist");
var args = new string [] {
"--display",
"--entitlements",
entitlementsPath,
"--xml",
nativeExecutable
};
var rv = ExecutionHelper.Execute ("codesign", args, out var codesignOutput, TimeSpan.FromSeconds (15));
Assert.AreEqual (0, rv, $"'codesign {string.Join (" ", args)}' failed:\n{codesignOutput}");
if (File.Exists (entitlementsPath)) {
entitlements = PDictionary.FromFile(entitlementsPath);
return true;
}
entitlements = null;
return false;
}
}
}
Loading