Skip to content

[dotnet] Set ApplicationTitle correctly for desktop apps. Fixes #20615. #22913

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 30, 2025
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
1 change: 0 additions & 1 deletion dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,6 @@
<_IntermediateNativeLibraryDir>$(IntermediateOutputPath)nativelibraries/</_IntermediateNativeLibraryDir>
<_IntermediateFrameworksDir>$(DeviceSpecificIntermediateOutputPath)frameworks</_IntermediateFrameworksDir>
<_IntermediateDecompressionDir>$([MSBuild]::EnsureTrailingSlash('$(DeviceSpecificIntermediateOutputPath)decompressed'))</_IntermediateDecompressionDir>
<_NativeExecutableName>$(_AppBundleName)</_NativeExecutableName>
<_NativeExecutablePublishDir Condition="'$(_PlatformName)' == 'iOS' Or '$(_PlatformName)' == 'tvOS'">$(_RelativeAppBundlePath)\</_NativeExecutablePublishDir>
<_NativeExecutablePublishDir Condition="'$(_PlatformName)' == 'macOS' Or '$(_PlatformName)' == 'MacCatalyst'">$(_RelativeAppBundlePath)\Contents\MacOS\</_NativeExecutablePublishDir>
<_AppBundleFrameworksDir Condition="'$(_PlatformName)' == 'iOS' Or '$(_PlatformName)' == 'tvOS'">$(_RelativeAppBundlePath)\Frameworks\</_AppBundleFrameworksDir>
Expand Down
5 changes: 4 additions & 1 deletion msbuild/Xamarin.MacDev.Tasks/Tasks/CompileAppManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public class CompileAppManifest : XamarinTask, IHasProjectDir, IHasResourcePrefi
[Required]
public string AssemblyName { get; set; } = String.Empty;

[Required]
public string BundleExecutable { get; set; } = "";

[Required]
[Output] // This is required to create an empty file on Windows for the Input/Outputs check.
public ITaskItem? CompiledAppManifest { get; set; }
Expand Down Expand Up @@ -133,7 +136,7 @@ public override bool Execute ()
plist.SetIfNotPresent (ManifestKeys.CFBundleInfoDictionaryVersion, "6.0");
plist.SetIfNotPresent (ManifestKeys.CFBundlePackageType, IsAppExtension ? "XPC!" : "APPL");
plist.SetIfNotPresent (ManifestKeys.CFBundleSignature, "????");
plist.SetIfNotPresent (ManifestKeys.CFBundleExecutable, AssemblyName);
plist.SetIfNotPresent (ManifestKeys.CFBundleExecutable, BundleExecutable);
plist.SetIfNotPresent (ManifestKeys.CFBundleName, AppBundleName);

if (GenerateApplicationManifest && !string.IsNullOrEmpty (ApplicationTitle))
Expand Down
6 changes: 5 additions & 1 deletion msbuild/Xamarin.Shared/Xamarin.Shared.props
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ Copyright (C) 2020 Microsoft. All rights reserved.
<!-- Accept 'UseInterpreter' as an alternative for 'MtouchInterpreter', so that we have the same variable name as Android -->
<MtouchInterpreter Condition="'$(MtouchInterpreter)' == '' And '$(UseInterpreter)' == 'True'">all</MtouchInterpreter>

<_AppBundleName>$(AssemblyName)</_AppBundleName>
<_AppBundleName Condition="'$(_AppBundleName)' == '' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), 10.0)) And ('$(_PlatformName)' == 'macOS' Or '$(_PlatformName)' == 'MacCatalyst')">$(ApplicationTitle)</_AppBundleName>
<_AppBundleName Condition="'$(_AppBundleName)' == ''">$(AssemblyName)</_AppBundleName>

<!-- If resources are pre-compiled/processed before being embedded in libraries, or if they're stored as-is -->
<BundleOriginalResources Condition="'$(BundleOriginalResources)' == '' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), 10.0))">true</BundleOriginalResources>
Expand All @@ -238,6 +239,9 @@ Copyright (C) 2020 Microsoft. All rights reserved.
<_BundleOriginalResources Condition="'$(OutputType)' == 'Library' And '$(IsAppExtension)' != 'true' And '$(BundleOriginalResources)' == 'true'">true</_BundleOriginalResources>

<EnableProfiler Condition="'$(EnableProfiler)' == '' And '$(_BundlerDebug)' == 'true'">true</EnableProfiler>

<!-- Set the name of the native executable -->
<_NativeExecutableName Condition="'$(_NativeExecutableName)' == ''">$(AssemblyName)</_NativeExecutableName>
</PropertyGroup>

<PropertyGroup Condition="'$(IsBindingProject)' == 'true'">
Expand Down
1 change: 1 addition & 0 deletions msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
AppBundleName="$(_AppBundleName)"
AppManifest="$(AppBundleManifest)"
AssemblyName="$(AssemblyName)"
BundleExecutable="$(_NativeExecutableName)"
CompiledAppManifest="$(_TemporaryAppManifest)"
Debug="$(_BundlerDebug)"
DefaultSdkVersion="$(_SdkVersion)"
Expand Down
17 changes: 17 additions & 0 deletions tests/dotnet/Spaced App/AppDelegate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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;
}
}
}
1 change: 1 addition & 0 deletions tests/dotnet/Spaced App/MacCatalyst/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/Spaced App/MacCatalyst/Spaced App.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>
2 changes: 2 additions & 0 deletions tests/dotnet/Spaced App/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
TOP=../../..
include $(TOP)/tests/common/shared-dotnet-test.mk
1 change: 1 addition & 0 deletions tests/dotnet/Spaced App/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/Spaced App/iOS/Spaced App.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>
1 change: 1 addition & 0 deletions tests/dotnet/Spaced App/macOS/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/Spaced App/macOS/Spaced App.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>
20 changes: 20 additions & 0 deletions tests/dotnet/Spaced App/shared.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<OutputType>Exe</OutputType>

<ApplicationTitle>Spaced App Title</ApplicationTitle>
<ApplicationId>com.xamarin.spacedapp</ApplicationId>
<ApplicationVersion>1.0</ApplicationVersion>

<UseInterpreter>true</UseInterpreter> <!-- speeds up testing -->
<ExcludeTouchUnitReference>true</ExcludeTouchUnitReference>
<ExcludeNUnitLiteReference>true</ExcludeNUnitLiteReference>
</PropertyGroup>

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

<ItemGroup>
<Compile Include="../*.cs" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions tests/dotnet/Spaced App/shared.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TOP=../../../..
TESTNAME=Spaced App
include $(TOP)/tests/common/shared-dotnet.mk
1 change: 1 addition & 0 deletions tests/dotnet/Spaced App/tvOS/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/Spaced App/tvOS/Spaced App.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>
40 changes: 40 additions & 0 deletions tests/dotnet/UnitTests/ProjectTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3215,6 +3215,46 @@ static HashSet<string> GetLinkedWithFrameworks (string path)
return rv;
}

[Test]
[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64")]
[TestCase (ApplePlatform.iOS, "ios-arm64")]
[TestCase (ApplePlatform.TVOS, "tvos-arm64")]
[TestCase (ApplePlatform.MacOSX, "osx-arm64")]
public void SpacedAppTitle (ApplePlatform platform, string runtimeIdentifiers)
{
var project = "Spaced App";
var title = "Spaced App Title";

Configuration.IgnoreIfIgnoredPlatform (platform);
Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);

var applicationTitle = (platform.IsDesktop () && Version.Parse (Configuration.DotNetTfm.Replace ("net", "")).Major >= 10) ? title : project;
var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, applicationTitle: applicationTitle);
Clean (project_path);
var properties = GetDefaultProperties (runtimeIdentifiers);
var rv = DotNet.AssertBuild (project_path, properties);
var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray ();

var infoPlistPath = GetInfoPListPath (platform, appPath);
var infoPlist = PDictionary.FromFile (infoPlistPath)!;
Assert.AreEqual ("com.xamarin.spacedapp", infoPlist.GetString ("CFBundleIdentifier").Value, "CFBundleIdentifier");
Assert.AreEqual ("Spaced App Title", infoPlist.GetString ("CFBundleDisplayName").Value, "CFBundleDisplayName");

var appName = Path.GetFileNameWithoutExtension (appPath);
switch (platform) {
case ApplePlatform.MacCatalyst:
case ApplePlatform.MacOSX:
Assert.That (appName, Is.EqualTo (applicationTitle), "Dock Name");
break;
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
Assert.That (appName, Is.EqualTo (project), "App Name");
break;
default:
throw new NotImplementedException ();
}
}

[Test]
[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-x64", "13.1")]
[TestCase (ApplePlatform.iOS, "ios-arm64", "10.0")]
Expand Down
10 changes: 6 additions & 4 deletions tests/dotnet/UnitTests/TestBaseClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,17 @@ protected static void SetRuntimeIdentifiers (Dictionary<string, string> properti
properties [multiRid] = runtimeIdentifiers;
}

protected static string GetProjectPath (string project, string runtimeIdentifiers, ApplePlatform platform, out string appPath, string? subdir = null, string configuration = "Debug", string? netVersion = null)
protected static string GetProjectPath (string project, string runtimeIdentifiers, ApplePlatform platform, out string appPath, string? subdir = null, string configuration = "Debug", string? netVersion = null, string? applicationTitle = null)
{
return GetProjectPath (project, null, runtimeIdentifiers, platform, out appPath, configuration, netVersion);
return GetProjectPath (project, null, runtimeIdentifiers, platform, out appPath, configuration, netVersion, applicationTitle);
}

protected static string GetProjectPath (string project, string? subdir, string runtimeIdentifiers, ApplePlatform platform, out string appPath, string configuration = "Debug", string? netVersion = null)
protected static string GetProjectPath (string project, string? subdir, string runtimeIdentifiers, ApplePlatform platform, out string appPath, string configuration = "Debug", string? netVersion = null, string? applicationTitle = null)
{
var rv = GetProjectPath (project, subdir, platform);
appPath = Path.Combine (GetOutputPath (project, subdir, runtimeIdentifiers, platform, configuration, netVersion), project + ".app");
if (applicationTitle is null)
applicationTitle = project;
appPath = Path.Combine (GetOutputPath (project, subdir, runtimeIdentifiers, platform, configuration, netVersion), applicationTitle + ".app");
return rv;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ protected virtual void ConfigureTask ()
Task.CompiledAppManifest = new TaskItem (Path.Combine (Cache.CreateTemporaryDirectory (), "AppBundlePath", "Info.plist"));
Task.AssemblyName = assemblyName;
Task.AppManifest = new TaskItem (CreateTempFile ("foo.plist"));
Task.BundleExecutable = assemblyName;
Task.MinSupportedOSPlatformVersion = "10.0";
Task.SupportedOSPlatformVersion = "15.0";
Task.SdkVersion = "10.0";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public override void BundleExecutable ()
{
base.BundleExecutable ();
// Adding ".app" to the assembly name isn't allowed because iOS may fail to launch the app.
Task.AssemblyName = "AssemblyName.app";
Task.BundleExecutable = "AssemblyName.app";
Assert.IsFalse (Task.Execute (), "#1");
}

Expand Down
14 changes: 14 additions & 0 deletions tools/common/ApplePlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,19 @@ public static ApplePlatform Parse (string platform)
throw new System.InvalidOperationException ($"Unknown platform: {platform}");
}
}

public static bool IsDesktop (this ApplePlatform @this)
{
switch (@this) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
return false;
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
return true;
default:
throw new System.InvalidOperationException ($"Unknown platform: {@this}");
}
}
}
}
Loading