diff --git a/Directory.Build.BeforeCommonTargets.targets b/Directory.Build.BeforeCommonTargets.targets index 8339d8e3ca0c..6395f6c74dc6 100644 --- a/Directory.Build.BeforeCommonTargets.targets +++ b/Directory.Build.BeforeCommonTargets.targets @@ -15,6 +15,7 @@ @@ -30,6 +30,9 @@ $(MSBuildProjectName.EndsWith('.Test')) OR $(MSBuildProjectName.EndsWith('.FunctionalTest')) ) ">true false + true + true + true true false + 99.9 + + true + full + true + true + + true + + diff --git a/eng/testing/linker/SupportFiles/Directory.Build.targets b/eng/testing/linker/SupportFiles/Directory.Build.targets new file mode 100644 index 000000000000..a2a18b071459 --- /dev/null +++ b/eng/testing/linker/SupportFiles/Directory.Build.targets @@ -0,0 +1,25 @@ + + + + + true + + + + + + + + + + + + + + + diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template new file mode 100644 index 000000000000..59333742ae1c --- /dev/null +++ b/eng/testing/linker/project.csproj.template @@ -0,0 +1,22 @@ + + + + {TargetFramework} + Exe + {RuntimeIdentifier} + {PublishAot} + {MicrosoftNETCoreAppRuntimeVersion} + {RepoRoot} + <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) + {AdditionalProperties} + + + + {RuntimeHostConfigurationOptions} + + + + {AdditionalProjectReferences} + + + diff --git a/eng/testing/linker/trimmingTests.props b/eng/testing/linker/trimmingTests.props new file mode 100644 index 000000000000..4158daec9957 --- /dev/null +++ b/eng/testing/linker/trimmingTests.props @@ -0,0 +1,20 @@ + + + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'trimmingTests')) + $([MSBuild]::NormalizeDirectory('$(TrimmingTestDir)', 'projects')) + $(MSBuildThisFileDirectory)project.csproj.template + + + false + false + + + + + $(DefaultNetCoreTargetFramework) + + + true + + + diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets new file mode 100644 index 000000000000..5f5fcb99736a --- /dev/null +++ b/eng/testing/linker/trimmingTests.targets @@ -0,0 +1,132 @@ + + + + + + + + $(TrimmingTestDir) + + + + + + + + + + + + $([MSBuild]::NormalizeDirectory('$(TrimmingTestProjectsDir)', '$(MSBuildProjectName)', '%(Filename)', '$(PackageRID)')) + $(TargetRuntimeIdentifier) + $(DefaultNetCoreTargetFramework) + $(DefaultNetCoreTargetFramework)-%(TestConsoleAppSourceFiles.TargetOS) + + + + %(ProjectDir)project.csproj + $([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '%(TargetFramework)', '%(TestRuntimeIdentifier)', 'publish', 'project')) + $([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '%(TargetFramework)', '%(TestRuntimeIdentifier)', 'publish')) + + + + + + %(FullPath) + + + + + + + + <_projectDir>%(TestConsoleApps.ProjectDir)\ + <_projectFile>%(TestConsoleApps.ProjectFile) + <_projectSourceFile>%(TestConsoleApps.ProjectCompileItems) + + + + <_additionalProjectReferenceTemp Include="$(AdditionalProjectReferences)" /> + <_additionalProjectReference Include="<ProjectReference Include="$(LibrariesProjectRoot)%(_additionalProjectReferenceTemp.Identity)\src\%(_additionalProjectReferenceTemp.Identity).csproj" SkipUseReferenceAssembly="true" />" /> + + + + <_additionalProjectReferencesString>@(_additionalProjectReference, '%0a') + + + + <_additionalProjectSourceFiles Include="%(TestConsoleApps.AdditionalSourceFiles)" /> + + + + <_switchesAsItems Include="%(TestConsoleApps.DisabledFeatureSwitches)" Value="false" /> + <_switchesAsItems Include="%(TestConsoleApps.EnabledFeatureSwitches)" Value="true" /> + + <_propertiesAsItems Include="%(TestConsoleApps.DisabledProperties)" Value="false" /> + <_propertiesAsItems Include="%(TestConsoleApps.EnabledProperties)" Value="true" /> + + + + <_runtimeHostConfigurationOptionsString>@(_switchesAsItems->'<RuntimeHostConfigurationOption Include="%(Identity)" Value="%(Value)" Trim="true" />', '%0a ') + <_additionalPropertiesString>@(_propertiesAsItems->'<%(Identity)>%(Value)</%(Identity)>', '%0a ') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.NativeAotTests/Microsoft.AspNetCore.NativeAotTests.proj b/src/DefaultBuilder/test/Microsoft.AspNetCore.NativeAotTests/Microsoft.AspNetCore.NativeAotTests.proj new file mode 100644 index 000000000000..b17fb2b6f5c3 --- /dev/null +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.NativeAotTests/Microsoft.AspNetCore.NativeAotTests.proj @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.NativeAotTests/UseStartupThrowsForStructContainersTest.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.NativeAotTests/UseStartupThrowsForStructContainersTest.cs new file mode 100644 index 000000000000..ef5951c9cbb3 --- /dev/null +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.NativeAotTests/UseStartupThrowsForStructContainersTest.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; + +int classTestResult = RunClassTest(); +if (classTestResult != 100) +{ + return classTestResult; +} + +return RunStructTest(); + +static int RunClassTest() +{ + var builder = Host.CreateDefaultBuilder(); + builder.UseServiceProviderFactory(new MyContainerClassFactory()); + + builder.ConfigureWebHost(webBuilder => + { + webBuilder.UseStartup(typeof(MyStartupWithClass)); + }); + + builder.Build(); + + if (!MyStartupWithClass.ConfigureServicesCalled) + { + return -1; + } + if (!MyStartupWithClass.ConfigureContainerCalled) + { + return -2; + } + + return 100; +} + +static int RunStructTest() +{ + var builder = Host.CreateDefaultBuilder(); + builder.UseServiceProviderFactory(new MyContainerStructFactory()); + + builder.ConfigureWebHost(webBuilder => + { + webBuilder.UseStartup(typeof(MyStartupWithStruct)); + }); + + try + { + builder.Build(); + return -3; + } + catch (InvalidOperationException e) + { + if (!e.Message.StartsWith("A ValueType TContainerBuilder isn't supported with AOT", StringComparison.Ordinal)) + { + return -4; + } + } + + if (!MyStartupWithStruct.ConfigureServicesCalled) + { + return -5; + } + + // ConfigureContainer should not have been called, since the exception should have been raised + if (MyStartupWithStruct.ConfigureContainerCalled) + { + return -6; + } + + return 100; +} + +public class MyStartupWithClass +{ + public static bool ConfigureServicesCalled; + public static bool ConfigureContainerCalled; + + public void ConfigureServices(IServiceCollection _) => ConfigureServicesCalled = true; + public void ConfigureContainer(MyContainerClass _) => ConfigureContainerCalled = true; + public void Configure(IApplicationBuilder _) { } +} + +public class MyContainerClassFactory : IServiceProviderFactory +{ + public MyContainerClass CreateBuilder(IServiceCollection services) => new MyContainerClass(services); + + public IServiceProvider CreateServiceProvider(MyContainerClass containerBuilder) + { + containerBuilder.Build(); + return containerBuilder; + } +} + +public class MyContainerClass : IServiceProvider +{ + private IServiceProvider _inner; + private IServiceCollection _services; + + public MyContainerClass(IServiceCollection services) => _services = services; + public void Build() => _inner = _services.BuildServiceProvider(); + public object GetService(Type serviceType) => _inner.GetService(serviceType); +} + +public class MyStartupWithStruct +{ + public static bool ConfigureServicesCalled; + public static bool ConfigureContainerCalled; + + public void ConfigureServices(IServiceCollection _) => ConfigureServicesCalled = true; + public void ConfigureContainer(MyContainerStruct _) => ConfigureContainerCalled = true; + public void Configure(IApplicationBuilder _) { } +} + +public class MyContainerStructFactory : IServiceProviderFactory +{ + public MyContainerStruct CreateBuilder(IServiceCollection services) => new MyContainerStruct(services); + + public IServiceProvider CreateServiceProvider(MyContainerStruct containerBuilder) + { + containerBuilder.Build(); + return containerBuilder; + } +} + +public struct MyContainerStruct : IServiceProvider +{ + private IServiceProvider _inner; + private IServiceCollection _services; + + public MyContainerStruct(IServiceCollection services) => _services = services; + public void Build() => _inner = _services.BuildServiceProvider(); + public object GetService(Type serviceType) => _inner.GetService(serviceType); +} diff --git a/src/Hosting/Hosting/src/Infrastructure/ISupportsStartup.cs b/src/Hosting/Hosting/src/Infrastructure/ISupportsStartup.cs index cf4dedf93e6c..6ee6cce59e22 100644 --- a/src/Hosting/Hosting/src/Infrastructure/ISupportsStartup.cs +++ b/src/Hosting/Hosting/src/Infrastructure/ISupportsStartup.cs @@ -40,6 +40,6 @@ public interface ISupportsStartup /// /// A delegate that specifies a factory for the startup class. /// The . - /// When using the IL linker, all public methods of are preserved. This should match the Startup type directly (and not a base type). + /// When in a trimmed app, all public methods of are preserved. This should match the Startup type directly (and not a base type). IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TStartup>(Func startupFactory); } diff --git a/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs b/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs index 050b0c5f2ef8..af693bc07cea 100644 --- a/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs +++ b/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs @@ -86,7 +86,7 @@ public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action /// The to configure. /// A delegate that specifies a factory for the startup class. /// The . - /// When using the il linker, all public methods of are preserved. This should match the Startup type directly (and not a base type). + /// When in a trimmed app, all public methods of are preserved. This should match the Startup type directly (and not a base type). public static IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TStartup>(this IWebHostBuilder hostBuilder, Func startupFactory) where TStartup : class { ArgumentNullException.ThrowIfNull(startupFactory);