Skip to content

[illink][ios] Explicitly setting PublishTrimmed=true changes the default feature switch configuration #108269

@ivanpovazan

Description

@ivanpovazan

Description

As called out in: #108108 (comment) iOS apps built with PublishTrimmed=true set on a project level end up with different set of feature switches configured during the build.
For example consider the difference in the input to the trimmer with and without setting the property value explicitly:

--feature Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability false->true
--feature System.ComponentModel.DefaultValueAttribute.IsSupported ->false
--feature System.ComponentModel.Design.IDesignerHost.IsSupported ->false
--feature System.Resources.UseSystemResourceKeys true->false
--feature System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported false->true
--feature System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop ->false

Problem

The problem comes from the order of importing MSBuild targets and property evaluation:

  • if it is not set on the project level, we will use feature switch configuration as set in the Xamarin SDK early in Xamarin.Shared.Sdk.targets:
    https://github.com/xamarin/xamarin-macios/blob/ec50934897ef1b98c1cd73ca780cdf5a3750faaa/dotnet/targets/Xamarin.Shared.Sdk.targets#L121-L158
    The SDK later sets: PublishTrimmed to true so that we run the linker in:
    https://github.com/xamarin/xamarin-macios/blob/ec50934897ef1b98c1cd73ca780cdf5a3750faaa/dotnet/targets/Xamarin.Shared.Sdk.targets#L298

  • if it is set on project level, Microsoft.NET.ILLink.targets will set its own feature switch configuration in:

    <PropertyGroup Condition="'$(PublishTrimmed)' == 'true'">
    <StartupHookSupport Condition="'$(StartupHookSupport)' == ''">false</StartupHookSupport>
    <CustomResourceTypesSupport Condition="'$(CustomResourceTypesSupport)' == ''">false</CustomResourceTypesSupport>
    <EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization Condition="'$(EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization)' == ''">false</EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization>
    <EnableUnsafeBinaryFormatterSerialization Condition="'$(EnableUnsafeBinaryFormatterSerialization)' == ''">false</EnableUnsafeBinaryFormatterSerialization>
    <EnableUnsafeUTF7Encoding Condition="'$(EnableUnsafeUTF7Encoding)' == ''">false</EnableUnsafeUTF7Encoding>
    <BuiltInComInteropSupport Condition="'$(BuiltInComInteropSupport)' == ''">false</BuiltInComInteropSupport>
    <!-- GeneratedComInterface/ComImport interop requires built-in COM -->
    <EnableGeneratedComInterfaceComImportInterop Condition="'$(BuiltInComInteropSupport)' == 'false'">false</EnableGeneratedComInterfaceComImportInterop>
    <EnableGeneratedComInterfaceComImportInterop Condition="'$(EnableGeneratedComInterfaceComImportInterop)' == ''">false</EnableGeneratedComInterfaceComImportInterop>
    <AutoreleasePoolSupport Condition="'$(AutoreleasePoolSupport)' == ''">false</AutoreleasePoolSupport>
    <EnableCppCLIHostActivation Condition="'$(EnableCppCLIHostActivation)' == ''">false</EnableCppCLIHostActivation>
    <!-- C++/CLI activation requires native hosting -->
    <_EnableConsumingManagedCodeFromNativeHosting Condition="'$(EnableCppCLIHostActivation)' == 'true'">true</_EnableConsumingManagedCodeFromNativeHosting>
    <_EnableConsumingManagedCodeFromNativeHosting Condition="'$(_EnableConsumingManagedCodeFromNativeHosting)' == ''">false</_EnableConsumingManagedCodeFromNativeHosting>
    <VerifyDependencyInjectionOpenGenericServiceTrimmability Condition="'$(VerifyDependencyInjectionOpenGenericServiceTrimmability)' == ''">true</VerifyDependencyInjectionOpenGenericServiceTrimmability>
    <JsonSerializerIsReflectionEnabledByDefault Condition="'$(JsonSerializerIsReflectionEnabledByDefault)' == ''">false</JsonSerializerIsReflectionEnabledByDefault>
    <!-- Linux Bionic doesn't ship GSSAPI, so enable managed implementation -->
    <_UseManagedNtlm Condition="'$(_UseManagedNtlm)' == '' and $(RuntimeIdentifier.StartsWith('linux-bionic'))">true</_UseManagedNtlm>
    <!-- Trim managed NTLM on Linux when it's not explicitly requested -->
    <_UseManagedNtlm Condition="'$(_UseManagedNtlm)' == '' and $(RuntimeIdentifier.StartsWith('linux'))">false</_UseManagedNtlm>
    <_ComObjectDescriptorSupport Condition="'$(_ComObjectDescriptorSupport)' == ''">false</_ComObjectDescriptorSupport>
    <_DesignerHostSupport Condition="'$(_DesignerHostSupport)' == ''">false</_DesignerHostSupport>
    <_DefaultValueAttributeSupport Condition="'$(_DefaultValueAttributeSupport)' == ''">false</_DefaultValueAttributeSupport>
    <DynamicCodeSupport Condition="'$(DynamicCodeSupport)' == ''">true</DynamicCodeSupport>
    <UseSystemResourceKeys Condition="'$(UseSystemResourceKeys)' == ''">false</UseSystemResourceKeys>
    <_DataSetXmlSerializationSupport Condition="'$(_DataSetXmlSerializationSupport)' == ''">false</_DataSetXmlSerializationSupport>
    </PropertyGroup>
    as it is imported before Xamarin.Shared.Sdk.targets

    Screenshot 2024-09-25 at 22 37 44

    This is specifically problematic in case of for example: DynamicCodeSupport , which should not be true on iOS platforms if interpreter is not enabled.

Proposal

We should investigate setting the feature switch defaults (and possibly other trimmer setting) in an MSBuild target so that mobile SDKs can provide different defaults earlier in the build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-Tools-ILLink.NET linker development as well as trimming analyzersgood first issueIssue should be easy to implement, good for first-time contributorsos-iosApple iOS

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions