Skip to content
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

Android linking #1502

Merged
merged 7 commits into from
May 24, 2023
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
2 changes: 2 additions & 0 deletions docs/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@
items:
- name: App manifest
href: android/manifest.md
- name: Linking
href: android/linking.md
- name: iOS
items:
- name: Apple account management
Expand Down
2 changes: 2 additions & 0 deletions docs/android/deployment/publish-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Additional build parameters can be specified on the command line, if they aren't
| `-p:AndroidSigningKeyPass` | The password of the key within the keystore file. This is the value provided to `keytool` when creating the keystore file and you're asked to enter the keystore password. This is because the default keystore type assumes that the key password and keystore password are identical. This property also supports `env:` and `file:` prefixes that can be used to specify an environment variable or file that contains the password. These options provide a way to prevent the password from appearing in build logs. |
| `-p:AndroidSigningKeyStore` | The filename of the keystore file created by `keytool`. This is the `keytool -keystore` value used when creating the keystore. |
| `-p:AndroidSigningStorePass` | The password for the keystore file. This is the value provided to `keytool` when creating the keystore file and you're asked to enter the keystore password. This is because the default keystore type assumes that the keystore password and key password are identical. This property also supports `env:` and `file:` prefixes that can be used to specify an environment variable or file that contains the password. These options provide a way to prevent the password from appearing in build logs. |
| `-p:PublishTrimmed` | A boolean value that indicates whether the app should be trimmed. The default value is `true` for release builds. |

You should use the same password as the values of the `AndroidSigningKeyPass` and `AndroidSigningStorePass` parameters.

Expand Down Expand Up @@ -131,6 +132,7 @@ An alternative to specifying build parameters on the command line is to specify
| `<AndroidSigningKeyPass>` | The password of the key within the keystore file. This is the value provided to `keytool` when creating the keystore file and you're asked to enter the keystore password. This is because the default keystore type assumes that the key password and keystore password are identical. This property also supports `env:` and `file:` prefixes that can be used to specify an environment variable or file that contains the password. These options provide a way to prevent the password from appearing in build logs. |
| `<AndroidSigningKeyStore>` | The filename of the keystore file created by `keytool`. This is the `keytool -keystore` value used when creating the keystore. |
| `<AndroidSigningStorePass>` | The password for the keystore file. This is the value provided to `keytool` when creating the keystore file and you're asked to enter the keystore password. This is because the default keystore type assumes that the keystore password and key password are identical. This property also supports `env:` and `file:` prefixes that can be used to specify an environment variable or file that contains the password. These options provide a way to prevent the password from appearing in build logs. |
| `<PublishTrimmed>` | A boolean value that indicates whether the app should be trimmed. The default value is `true` for release builds. |

For a full list of build properties, see [Build properties](/xamarin/android/deploy-test/building-apps/build-properties).

Expand Down
53 changes: 53 additions & 0 deletions docs/android/linking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: "Linking a .NET MAUI Android app"
description: "Learn about the .NET Android linker, which is used to eliminate unused code from a .NET MAUI Android app in order to reduce its size."
ms.date: 04/23/2023
no-loc: [ ILLink ]
---

# Linking a .NET MAUI Android app

When it builds your app, .NET Multi-platform App UI (.NET MAUI) can use a linker called *ILLink* to reduce the overall size of the app. ILLink reduces the size by analyzing the intermediate code produced by the compiler. It removes unused methods, properties, fields, events, structs, and classes to produce an app that contains only code and assembly dependencies that are necessary to run the app.

## Linker behavior

The linker enables you to trim your .NET MAUI Android apps. When trimming is enabled, the linker leaves your assemblies untouched and reduces the size of the SDK assemblies by removing types and members that your app doesn't use.

Linker behavior can be configured for each build configuration of your app. By default, trimming is disabled for debug builds and enabled for release builds.

> [!WARNING]
> Enabling the linker for your app's debug configuration may hinder your debugging experience, as it may remove property accessors that enable you to inspect the state of your objects.

<!-- markdownlint-disable MD025 -->
# [Visual Studio](#tab/vs)
<!-- markdownlint-enable MD025 -->

1. In **Solution Explorer** right-click on your .NET MAUI app project and select **Properties**. Then, navigate to the **Android > Options** tab and ensure that trimming is enabled for the release build configuration:

:::image type="content" source="media/linking/vs.png" alt-text="Screenshot of the linker behavior for Android in Visual Studio.":::

<!-- markdownlint-disable MD025 -->
# [Visual Studio for Mac](#tab/vsmac)
<!-- markdownlint-enable MD025 -->

1. In the **Solution Window**, right-click on your .NET MAUI app project and select **Properties**.
1. In the **Project Properties** window, select the **Build > Android > Linker** tab.
1. In the **Project Properties** window, ensure the **Configuration** drop-down is set to **Release** and set the **Linker Behavior** drop-down to your desired linker behavior:

- *Don't link*. Disabling linking ensures assemblies aren't modified.
- *Link SDK assemblies only*. In this mode, the linker leaves your assemblies untouched and reduces the size of the SDK assemblies by removing types and members that your app doesn't use.
- *Link all assemblies*. When it links all assemblies, the linker performs additional optimizations to make your app as small as possible. It modifies the intermediate code for your source code, which may break your app if you use features using an approach that the linker's static analysis can't detect. In these cases, you may need to make adjustments to your source code to make your app work correctly.

:::image type="content" source="media/linking/vsmac.png" alt-text="Screenshot of the linker behavior for Android in Visual Studio for Mac.":::

> [!WARNING]
> Linking all assemblies isn't recommended, and can require significant additional work to ensure that your app still works.

1. In the **Project Properties** window, click the **OK** button.

> [!NOTE]
> Visual Studio for Mac sets the `AndroidLinkMode` MSBuild property in your app's project file. `<AndroidLinkMode>None</AndroidLinkMode>` maps to `<PublishTrimmed>false</PublishTrimmed>` and `<AndroidLinkMode>SdkOnly</AndroidLinkMode>` maps to `<PublishTrimmed>true</PublishTrimmed>`.

----

[!INCLUDE [Control the linker](../includes/linker-control.md)]
Binary file added docs/android/media/linking/vs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/android/media/linking/vsmac.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
ms.topic: include
ms.date: 04/11/2023
ms.date: 05/23/2023
---

## Preserve code

When you use the linker, it sometimes remove code that you might have called dynamically, even indirectly. You can instruct the linker to preserve members by annotating them with the [`DynamicDependency`](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) attribute. This attribute can be used to express a dependency on either a type and subset of members, or at specific members.

> [!IMPORTANT]
> Every member that isn't statically linked by the app is subject to be removed.
> Every member in the BCL that can't be statically determined to be used by the app is subject to be removed.

The [`DynamicDependency`](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) attribute can be applied to constructors, fields, and methods:

Expand Down Expand Up @@ -44,7 +44,7 @@ The type and member strings use a variation of the C# documentation comment ID s

## Preserve assemblies

It's possible to specify assemblies that should be excluded from the linking process, while allowing other assemblies to be linked.
It's possible to specify assemblies that should be excluded from the linking process, while allowing other assemblies to be linked. This approach can be useful when you can't easily use the [`DynamicDependency`](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) attribute, or don't control the code that's being linked away.

When it links all assemblies, you can tell the linker to skip an assembly by setting the `TrimmerRootAssembly` MSBuild property in an `<ItemGroup>` tag in the project file:

Expand Down Expand Up @@ -92,23 +92,50 @@ When an assembly, type, or member is listed in the XML, the default action is pr

## Mark an assembly as linker safe

If you have a library in your project, or you're a developer of a reusable library and you want the linker to treat your assembly as linkable, you can mark the assembly as linker safe with the [`AssemblyMetadata`](xref:System.Reflection.AssemblyMetadataAttribute) attribute:
If you have a library in your project, or you're a developer of a reusable library and you want the linker to treat your assembly as linkable, you can mark the assembly as linker safe by adding the `IsTrimmable` MSBuild property to the project file for the assembly:

```xml
<PropertyGroup>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
```

This marks your assembly as "trimmable" and enables trim warnings for that project. Being "trimmable" means your assembly is considered compatible with trimming and should have no trim warnings when the assembly is built. When used in a trimmed app, the assembly's unused members will be removed in the final output.

Setting the `IsTrimmable` MSBuild property to `true` in your project file inserts the [`AssemblyMetadata`](xref:System.Reflection.AssemblyMetadataAttribute) attribute into your assembly:

```csharp
[assembly: AssemblyMetadata("IsTrimmable", "True")]
```

Alternatively, you can set `<IsTrimmable>true</IsTrimmable>` in a `<PropertyGroup>` tag in the project file for the assembly. This marks your assembly as "trimmable" and enable trim warnings for that project. Being "trimmable" means your assembly is considered compatible with trimming and should have no trim warnings when the assembly is built. When used in a trimmed app, the assembly's unused members will be removed in the final output.
Alternatively, you can add the [`AssemblyMetadata`](xref:System.Reflection.AssemblyMetadataAttribute) attribute into your assembly without having added the `IsTrimmable` MSBuild property to the project file for your assembly.

> [!NOTE]
> If the `IsTrimmable` MSBuild property is set for an assembly, this overrides the `IsTrimmable` attribute. This enables you to opt an assembly into trimming even if it doesn't have the attribute, or to disable trimming of an assembly that has the attribute.
> If the `IsTrimmable` MSBuild property is set for an assembly, this overrides the `AssemblyMetadata("IsTrimmable", "True")` attribute. This enables you to opt an assembly into trimming even if it doesn't have the attribute, or to disable trimming of an assembly that has the attribute.

### Suppress analysis warnings

When the linker is enabled, it will remove IL that's not statically reachable. Apps that uses reflection or other patterns that create dynamic dependencies may be broken as a result. To warn about such patterns, when marking an assembly as linker safe library authors should set the `SuppressTrimAnalysisWarnings` MSBuild property to `false`:

## Disable linking
```xml
<PropertyGroup>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
```

This will include warnings about the entire app, including your own code, library code, and SDK code.

### Show detailed warnings

To disable linking, set `<MtouchLink>None</MtouchLink>` in a `<PropertyGroup>` tag in the project file, or set the linker behavior to **Don't link** in Visual Studio.
Trim analysis produces at most one warning for each assembly that comes from a `PackageReference`, indicating that the assembly's internals aren't compatible with trimming. When marking an assembly as linker safe, library authors should enable individual warnings for all assemblies by setting the `TrimmerSingleWarn` MSBuild property to `false`:

```xml
<PropertyGroup>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
```

> [!WARNING]
> Setting `<PublishTrimmed>false</PublishTrimmed>` in a `<PropertyGroup>` tag in the project file for the assembly doesn't disable linking for .NET MAUI apps on iOS and Mac Catalyst.
This will show all detailed warnings, instead of collapsing them to a single warning per assembly.

## See also

Expand Down
2 changes: 1 addition & 1 deletion docs/ios/linking.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ no-loc: [ ILLink ]

----

[!INCLUDE [Control the linker](../macios/includes/linker-control.md)]
[!INCLUDE [Control the linker](../includes/linker-control.md)]
2 changes: 1 addition & 1 deletion docs/mac-catalyst/linking.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ To configure linker behavior in Visual Studio for Mac:

1. In the **Project Properties** window, click the **OK** button.

[!INCLUDE [Control the linker](../macios/includes/linker-control.md)]
[!INCLUDE [Control the linker](../includes/linker-control.md)]