diff --git a/docs/core/deploying/prepare-libraries-for-trimming.md b/docs/core/deploying/prepare-libraries-for-trimming.md
new file mode 100644
index 0000000000000..9066614debf10
--- /dev/null
+++ b/docs/core/deploying/prepare-libraries-for-trimming.md
@@ -0,0 +1,246 @@
+---
+title: Prepare .NET libraries for trimming
+description: Learn how to prepare .NET libraries for trimming.
+author: sbomer
+ms.author: svbomer
+ms.date: 04/16/2021
+---
+
+# Prepare .NET libraries for trimming
+
+The .NET SDK makes it possible to reduce the size of self-contained apps by [trimming](trim-self-contained.md), removing unused code from the app and its dependencies. Not all code is compatible with trimming, so .NET 6 provides trim analysis [warnings](trimming-options.md#analysis-warnings) to detect patterns that may break trimmed apps. This document describes how to prepare libraries for trimming with the aid of these warnings, including recommendations for fixing some common cases.
+
+## Trim warnings in apps
+
+In .NET 6+, when publishing an app, the `PublishTrimmed` project file element will produce trim analysis warnings for patterns that are not statically understood to be compatible with trimming, including patterns in your code and in dependencies.
+
+You will encounter detailed warnings originating from your own code and `ProjectReference` dependencies. You may also see warnings like `warning IL2104: Assembly 'SomeAssembly' produced trim warnings` for `PackageReference` libraries. This warning means that the library contained patterns which are not guaranteed to work in the context of the trimmed app, and may result in a broken app. Consider contacting the author to see if the library can be annotated for trimming.
+
+To resolve warnings originating from the app code, see [resolving trim warnings](#resolve-trim-warnings). If you are interested in making your own `ProjectReference` libraries trim friendly, follow the instructions to [enable library trim warnings](#enable-library-trim-warnings).
+
+If your app only uses parts of a library that are compatible with trimming, consider [enabling trimming](trimming-options.md#trim-additional-assemblies) of this library if it is not already being trimmed. This will only produce warnings if your app uses problematic parts of the library. (You can also [show detailed warnings](trimming-options.md#show-detailed-warnings) for the library to see which parts of it are problematic.)
+
+## Enable library trim warnings
+
+These instructions show how to enable and resolve static analysis warnings to prepare a library for trimming. Follow these steps if you are authoring a library and either want to proactively make your library trimmable, or have been contacted by app authors who encountered trim warnings from your library.
+
+Ensure you are using the .NET 6 SDK for these steps. They will not work correctly in previous versions.
+
+## Enable Roslyn analyzer
+
+Set `true` (in .NET 6+) in your library project. This will not have any effect on the output, but it will enable trim analysis during build via a Roslyn analyzer.
+
+The Roslyn analyzer is useful for a fast feedback cycle with IDE integration, but is currently incomplete. It doesn't cover all trim analysis warnings, but the set of patterns it understands will improve over time to give more complete coverage. The Roslyn analyzer also isn't able to analyze the implementations of reference assemblies that you depend on. It is important to follow the next steps to ensure that your library is fully compatible with trimming.
+
+### Show all warnings
+
+To show all analysis warnings for your library, including warnings about dependencies, create a separate app project like the following that references your library, and publish it with `PublishTrimmed`.
+
+The extra step of creating an app project just to get complete warnings for a library is necessary because the implementations of dependencies are not generally available during `dotnet build`, and reference assemblies don't contain enough information to determine whether they are compatible with trimming. Publishing a self-contained app ensures that the library is analyzed in a context where its dependencies are available, so that you are alerted if your library uses any code from dependencies that could break a trimmed app.
+
+```xml
+
+
+
+ Exe
+ net6.0
+
+ linux-x64
+ true
+
+ link
+
+
+
+
+
+
+
+
+
+```
+
+```dotnetcli
+dotnet publish -c Release
+```
+
+- `TrimmerRootAssembly` ensures that every part of the library is analyzed. This is necessary in case the library has `[AssemblyMetadata("IsTrimmable", "True")]`, which would otherwise let trimming remove the unused library without analyzing it.
+
+- `link` ensures that only used parts of dependencies are analyzed. Without this option, you would see warnings originating from _any_ part of a dependency that doesn't set `[AssemblyMetadata("IsTrimmable", "True")]`, including parts that are unused by your library.
+
+You can also follow the same pattern for multiple libraries, adding them all to the same project as `ProjectReference` and `TrimmerRootAssembly` item to see trim analysis warnings for more than one library at a time, but note that this will warn about dependencies if _any_ of the root libraries use a trim-unfriendly API in a dependency. To see warnings that have to do with only a particular library, reference that library only.
+
+> [!NOTE]
+> The analysis results depend on the implementation details of your dependencies. If you update to a new version of a dependency, this may introduce analysis warnings if the new version added non-understood reflection patterns, even if there were no API changes. In other words, introducing trim analysis warnings to a library is a breaking change when the library is used with `PublishTrimmed`.
+
+## Resolve trim warnings
+
+The above steps will produce warnings about code that may cause problems when used in a trimmed app. Here are a few examples of the most common kinds of warnings you may encounter, with recommendations for fixing them.
+
+### RequiresUnreferencedCode
+
+```csharp
+using System.Diagnostics.CodeAnalysis;
+
+public class MyLibrary
+{
+ public static void Method()
+ {
+ // warning IL2026 : MyLibrary.Method: Using method 'MyLibrary.DynamicBehavior' which has
+ // 'RequiresUnreferencedCodeAttribute' can break functionality
+ // when trimming application code.
+ DynamicBehavior();
+ }
+
+ [RequiresUnreferencedCode("DynamicBehavior is incompatible with trimming.")]
+ static void DynamicBehavior()
+ {
+ }
+}
+```
+
+This means the library calls a method which has explicitly been annotated as incompatible with trimming, using [`RequiresUnreferencedCodeAttribute`](
+https://docs.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.requiresunreferencedcodeattribute?view=net-5.0&preserve-view=true). To get rid of the warning, consider whether `Method` needs to call `DynamicBehavior` to do its job. If so, annotate the caller `Method` with `RequiresUnreferencedCode` as well; this will "bubble up" the warning so that callers of `Method` get a warning instead:
+
+```csharp
+// Warn for calls to Method, but not for Method's call to DynamicBehavior.
+[RequiresUnreferencedCode("Calls DynamicBehavior.")]
+public static void Method()
+{
+ DynamicBehavior(); // OK. Doesn't warn now.
+}
+```
+
+Once you have "bubbled up" the attribute all the way to public APIs (so that these warnings are produced only for public methods, if at all), you are done. Apps which call your library will now get warnings if they call those public APIs, but these will no longer produce warnings like `IL2104: Assembly 'MyLibrary' produced trim warnings`.
+
+### DynamicallyAccessedMembers
+
+```csharp
+using System.Diagnostics.CodeAnalysis;
+
+public class MyLibrary
+{
+ static void UseMethods(Type type)
+ {
+ // warning IL2070: MyLibrary.UseMethods(Type): 'this' argument does not satisfy
+ // 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethods()'.
+ // The parameter 't' of method 'MyLibrary.UseMethods(Type)' does not have matching annotations.
+ foreach (var method in type.GetMethods())
+ {
+ // ...
+ }
+ }
+}
+```
+
+Here, `UseMethods` is calling a reflection method which has a [`DynamicallyAccessedMembers`](https://docs.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.dynamicallyaccessedmembersattribute?view=net-5.0&preserve-view=true) requirement. The requirement states that the type's public methods are available. In this case, you can fix this by adding the same requirement to the parameter of `UseMethods`.
+
+```csharp
+static void UseMethods(
+ // State the requirement in the UseMethods parameter.
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
+ Type type)
+{
+ // ...
+}
+```
+
+Now any calls to `UseMethods` will produce warnings if they pass in values which don't satisfy the `PublicMethods` requirement. Like with `RequiresUnreferencedCode`, once you have bubbled up such warnings to public APIs, you are done.
+
+Here is another example where an unknown `Type` flows into the annotated method parameter, this time from a field:
+
+```csharp
+static Type type;
+
+static void UseMethodsHelper()
+{
+ // warning IL2077: MyLibrary.UseMethodsHelper(Type): 'type' argument does not satisfy
+ // 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'MyLibrary.UseMethods(Type)'.
+ // The field 'System.Type MyLibrary::type' does not have matching annotations.
+ UseMethods(type);
+}
+```
+
+Similarly, here the problem is that the field `type` is passed into a parameter with these requinements. You can fix it by adding `DynamicallyAccessedMembers` to the field. This will warn about code that assigns incompatible values to the field instead. Sometimes this process will continue until a public API is annotated, and other times it will end when a concrete type flows into a location with these requirements. For example:
+
+```csharp
+[DynamicallyAccessedMembers(DynamicallyAccessedMembers.PublicMethods)]
+static Type type;
+
+static void InitializeTypeField()
+{
+ MyLibrary.type = typeof(System.Tuple);
+}
+```
+
+In this case the trim analysis will simply keep public methods of `System.Tuple`, and will not produce further warnings.
+
+## Recommendations
+
+In general, try to avoid reflection if possible. When using reflection, limit it in scope so that it is reachable only from a small part of the library.
+
+- Avoid using non-understood patterns in places like static constructors that will result in the warning propagating to all members of the class.
+- Avoid annotating virtual methods or interface methods, which will require all overrides to have matching annotations.
+- In some cases, you will be able to mechanically propagate warnings through your code without issues. Sometimes this will result in much of your public API being annotated with `RequiresUnreferencedCode`, which is the right thing to do if the library indeed behaves in ways that can't be understood statically by the trim analysis.
+- In other cases, you might discover that your code uses patterns which can't be expressed in terms of the `DynamicallyAccessedMembers` attributes, even if it only uses reflection to operate on statically-known types. In these cases, you may need to reorganize some of your code to make it follow an analyzable pattern.
+- Sometimes the existing design of an API will render it mostly trim-incompatible, and you may need to find other ways to accomplish what it is doing. A common example is reflection-based serializers. In these cases, consider adopting other technology like source generators to produce code that is more easily statically analyzed.
+
+## Resolve warnings for non-analyzable patterns
+
+You should prefer resolving warnings by expressing the intent of your code using `RequiresUnreferencedCode` and `DynamicallyAccessedMembers` when possible. However, in some cases you may be interested in enabling trimming of a library that uses patterns which can't be expressed with those attributes, or without refactoring existing code. This section describes additional advanced ways to resolve trim analysis warnings.
+
+> [!WARNING]
+> These techniques might break your code if used incorrectly.
+
+When suppressing warnings, you are responsible for guaranteeing the trim compatibility of your code based on invariants that you know to be true by inspection. Be very careful with these annotations, because if they are incorrect, or if invariants of your code change, they might end up hiding real issues.
+
+### UnconditionalSuppressMessage
+
+If the intent of your code can't be expressed with the annotations, but you know that the warning doesn't represent a real issue at runtime, you can suppress the warnings using [`UnconditionalSuppressMessageAttribute`](https://docs.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.unconditionalsuppressmessageattribute?view=net-5.0&preserve-view=true). This is similar to `SuppressMessageAttribute`, but it is persisted in IL and respected during trim analysis. For example:
+
+```csharp
+class TypeCollection
+{
+ Type[] types;u
+
+ // Ensure that only types with ctors are stored in the array
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ public Type this[int i]
+ {
+ // warning IL2063: TypeCollection.Item.get: Value returned from method 'TypeCollection.Item.get'
+ // can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements.
+ get => types[i];
+ set => types[i] = value;
+ }
+}
+
+class TypeCreator
+{
+ TypeCollection types;
+
+ public void CreateType(int i)
+ {
+ types[i] = typeof(TypeWithConstructor);
+ Activator.CreateInstance(types[i]); // No warning!
+ }
+}
+
+class TypeWithConstructor
+{
+}
+```
+
+Here, the indexer property has been annotated so that the returned `Type` meets the requirements of `CreateInstance`. This already ensures that the `TypeWithConstructor` constructor is kept, and that the call to `CreateInstance` doesn't warn. Furthermore, the indexer setter annotation ensures that any types stored in the `Type[]` have a constructor. However, the analysis isn't able to see this, and still produces a warning for the getter, because it doesn't know that the returned type has its constructor preserved.
+
+If you are sure that the requirements are met, you can silence this warning by adding `UnconditionalSuppressMessage` to the getter:
+
+```csharp
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+public Type this[int i]
+{
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063",
+ Justification = "The list only contains types stored through the annotated setter.")]
+ get => types[i];
+ set => types[i] = value;
+}
+```
diff --git a/docs/core/deploying/trimming-options.md b/docs/core/deploying/trimming-options.md
index 53f453e7238b4..4e40ef6cdef83 100644
--- a/docs/core/deploying/trimming-options.md
+++ b/docs/core/deploying/trimming-options.md
@@ -9,27 +9,47 @@ ms.date: 08/25/2020
The following MSBuild properties and items influence the behavior of [trimmed self-contained deployments](trim-self-contained.md). Some of the options mention `ILLink`, which is the name of the underlying tool that implements trimming. More information about the underlying tool can be found at the [Linker documentation](https://github.com/mono/linker/tree/master/docs).
+Trimming with `PublishTrimmed` was introduced in .NET Core 3.0. The other options are available only in .NET 5 and above.
+
## Enable trimming
- `true`
Enable trimming during publish, with the default settings defined by the SDK.
-When using `Microsoft.NET.Sdk`, this will perform assembly-level trimming of the framework assemblies from the netcoreapp runtime pack. Application code and non-framework libraries are not trimmed. Other SDKs may define different defaults.
+This will trim any assemblies which have been configured for trimming. With `Microsoft.NET.Sdk` in .NET 6, this includes the any assemblies with `[AssemblyMetadata("IsTrimmable", "True")]`, which is the case for framework assemblies. In .NET 5, framework assemblies from the netcoreapp runtime pack are configured for trimming via `` MSBuild metadata. Other SDKs may define different defaults.
## Trimming granularity
-The following granularity settings control how aggressively unused IL is discarded. This can be set as a property, or as metadata on an [individual assembly](#trimmed-assemblies).
+The following granularity settings control how aggressively unused IL is discarded. This can be set as a property affecting all trimmer input assemblies, or as metadata on an [individual assembly](#trimmed-assemblies) which overrides the property setting.
+
+- `link`
+
+ Enable member-level trimming, which removes unused members from types. This is the default in .NET 6+.
- `copyused`
Enable assembly-level trimming, which will keep an entire assembly if any part of it is used (in a statically understood way).
-- `link`
+Assemblies with `true` metadata but no explicit `TrimMode` will use the global `TrimMode`. The default `TrimMode` for `Microsoft.NET.Sdk` is `link` in .NET 6+, and `copyused` in previous versions.
+
+## Trim additional assemblies
+
+In .NET 6+, `PublishTrimmed` trims assemblies with the assembly-level attribute
- Enable member-level trimming, which removes unused members from types.
+```csharp
+[AssemblyMetadata("IsTrimmable", "True")]
+```
+
+which includes the framework libraries. In .NET 6+, you can also opt in to trimming for a library without this attribute, specifying the assembly by name (without the `.dll` extension).
+
+```xml
+
+
+
+```
-Assemblies with `true` metadata but no explicit `TrimMode` will use the global `TrimMode`. The default `TrimMode` for `Microsoft.NET.Sdk` is `copyused`.
+This is equivalent to setting MSBuild metadata `true` for the assembly in `ManagedAssemblyToLink` (see below).
## Trimmed assemblies
@@ -46,6 +66,8 @@ When publishing a trimmed app, the SDK computes an `ItemGroup` called `ManagedAs
```
+You can also use this to override the trimming behavior specified by the library author, by setting `false` for an assembly with `[AssemblyMetadata("IsTrimmable", "True"])`.
+
Do not add or remove items to/from `ManagedAssemblyToLink`, because the SDK computes this set during publish and expects it not to change. The supported metadata is:
- `true`
@@ -56,6 +78,10 @@ Do not add or remove items to/from `ManagedAssemblyToLink`, because the SDK comp
Control the [trimming granularity](#trimming-granularity) of this assembly. This takes precedence over the global `TrimMode`. Setting `TrimMode` on an assembly implies `true`.
+- `True` or `False`
+
+ Control whether to show [single warnings](#show-detailed-warnings) for this assembly.
+
## Root assemblies
All assemblies that do not have `true` are considered roots for the analysis, which means that they and all of their statically understood dependencies will be kept. Additional assemblies may be "rooted" by name (without the `.dll` extension):
@@ -98,6 +124,14 @@ Trimming will remove IL that is not statically reachable. Apps that use reflecti
This will include warnings about the entire app, including your own code, library code, and framework code.
+## Roslyn analyzer
+
+Setting `PublishTrimmed` in .NET 6+ will also enable a Roslyn analyzer that shows a _limited_ set of analysis warnings. The analyzer may also be enabled or disabled independently of `PublishTrimmed`.
+
+- `true`
+
+ Enable a Roslyn analyzer for a subset of trim analysis warnings.
+
## Warning versions
Trim analysis respects the [`AnalysisLevel`](../project-sdk/msbuild-props.md#analysislevel) property that controls the version of analysis warnings across the SDK. There is another property that controls the version of trim analysis warnings independently (similar to `WarningLevel` for the compiler):
@@ -114,7 +148,17 @@ Individual [warning codes](https://github.com/mono/linker/blob/master/docs/error
Don't treat ILLink warnings as errors. This may be useful to avoid turning trim analysis warnings into errors when treating compiler warnings as errors globally.
-## Removing symbols
+## Show detailed warnings
+
+In .NET 6+, trim analysis will produce at most one warning for each assembly that comes from a `PackageReference`, indicating that the assembly's internals are not compatible with trimming. You can also show individual warnings for all assemblies:
+
+- `false`
+
+ Show all detailed warnings, instead of collapsing them to a single warning per assembly.
+
+The defaults show detailed warnings for the project assembly and `ProjectReference`s. `` can also be set as metadata on an [individual assembly](#trimmed-assemblies) to control the warning behavior for that assembly only.
+
+## Remove symbols
Symbols will normally be trimmed to match the trimmed assemblies. You can also remove all symbols:
@@ -130,7 +174,7 @@ Several feature areas of the framework libraries come with linker directives tha
- `false`
- Remove code that enables better debugging experiences. This will also [remove symbols](#removing-symbols).
+ Remove code that enables better debugging experiences. This will also [remove symbols](#remove-symbols).
- `false`
diff --git a/docs/fundamentals/toc.yml b/docs/fundamentals/toc.yml
index de70ce684eac0..9474c4095086a 100644
--- a/docs/fundamentals/toc.yml
+++ b/docs/fundamentals/toc.yml
@@ -1367,6 +1367,8 @@ items:
href: ../core/deploying/trim-self-contained.md
- name: Options
href: ../core/deploying/trimming-options.md
+ - name: Trimming libraries
+ href: ../core/deploying/prepare-libraries-for-trimming.md
- name: Runtime package store
href: ../core/deploying/runtime-store.md
- name: Runtime Identifier (RID) catalog