diff --git a/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml index 17314a9c01..dca90bf22d 100644 --- a/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml +++ b/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml @@ -255,8 +255,6 @@ steps: - task: CopyFiles@2 displayName: Stage WinRT.Host.Shim condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release')) - enabled: false - continueOnError: True inputs: SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.Host.Shim\bin\$(BuildConfiguration)\net10.0 Contents: | @@ -295,6 +293,16 @@ steps: cswinrtprojectiongen.pdb TargetFolder: $(StagingFolder)\net10.0\native + - task: CopyFiles@2 + displayName: Stage cswinrtwinmdgen + condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'arm64')), eq(variables['BuildConfiguration'], 'release')) + inputs: + SourceFolder: $(Build.SourcesDirectory)\src\WinRT.WinMD.Generator\bin\$(BuildConfiguration)\net10.0\win-$(BuildPlatform)\publish + Contents: | + cswinrtwinmdgen.exe + cswinrtwinmdgen.pdb + TargetFolder: $(StagingFolder)\net10.0\native + - task: CopyFiles@2 displayName: Stage WinRT.Generator.Tasks condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release')) diff --git a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml index 12dcc49074..1722e98bf6 100644 --- a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml +++ b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml @@ -73,6 +73,7 @@ jobs: net10.0/native/cswinrtinteropgen.exe; net10.0/native/cswinrtimplgen.exe; net10.0/native/cswinrtprojectiongen.exe; + net10.0/native/cswinrtwinmdgen.exe; search_root: $(StagingFolder) - task: onebranch.pipeline.signing@1 diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml index 22fe1c4931..c2c5b3c943 100644 --- a/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml +++ b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml @@ -95,7 +95,7 @@ steps: command: pack searchPatternPack: nuget/Microsoft.Windows.CsWinRT.nuspec configurationToPack: Release - buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\release_x86\native\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\release_x86\native\WindowsRuntime.Internal.winmd;net10_runtime=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.dll;net10_runtime_xml=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.xml;source_generator=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll.mui;cswinrtinteropgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtinteropgen.exe;cswinrtinteropgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtinteropgen.exe;cswinrtimplgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtimplgen.exe;cswinrtimplgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtimplgen.exe;cswinrtprojectiongen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtprojectiongen.exe;cswinrtprojectiongen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtprojectiongen.exe;run_cswinrt_generator_task=$(Build.SourcesDirectory)\release_x86\netstandard2.0\WinRT.Generator.Tasks.dll;branch=$(Build.SourceBranchName);commit=$(Build.SourceVersion) + buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\release_x86\native\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\release_x86\native\WindowsRuntime.Internal.winmd;net10_runtime=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.dll;net10_runtime_xml=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.xml;source_generator=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll.mui;cswinrtinteropgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtinteropgen.exe;cswinrtinteropgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtinteropgen.exe;cswinrtimplgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtimplgen.exe;cswinrtimplgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtimplgen.exe;cswinrtprojectiongen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtprojectiongen.exe;cswinrtprojectiongen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtprojectiongen.exe;cswinrtwinmdgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtwinmdgen.exe;cswinrtwinmdgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtwinmdgen.exe;run_cswinrt_generator_task=$(Build.SourcesDirectory)\release_x86\netstandard2.0\WinRT.Generator.Tasks.dll;branch=$(Build.SourceBranchName);commit=$(Build.SourceVersion) packDestination: $(ob_outputDirectory)\packages - ${{ if eq(parameters.IsGitHub, false) }}: diff --git a/build/AzurePipelineTemplates/CsWinRT-Test-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-Test-Steps.yml index 01a55b79f3..e9a909b82f 100644 --- a/build/AzurePipelineTemplates/CsWinRT-Test-Steps.yml +++ b/build/AzurePipelineTemplates/CsWinRT-Test-Steps.yml @@ -70,6 +70,18 @@ steps: dir _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin\AuthoringConsumptionTest.exe --gtest_output=xml:AUTHORINGTEST-$(Build.BuildNumber).xml +# Run Multi-Component Authoring Consumption Tests + - task: CmdLine@2 + displayName: Run Multi-Component Authoring Consumption Tests + enabled: true + condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64'))) + continueOnError: True + inputs: + workingDirectory: $(Build.SourcesDirectory)\src + script: | + dir _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest2\bin + _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest2\bin\AuthoringConsumptionTest2.exe --gtest_output=xml:AUTHORINGTEST2-$(Build.BuildNumber).xml + # Run WUX Tests - task: CmdLine@2 displayName: Run WUX Tests diff --git a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets index 0dd2544db8..1d416861cb 100644 --- a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets +++ b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets @@ -68,7 +68,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> - + WinRT.Host.Shim.dll PreserveNewest @@ -172,6 +172,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. true $(TargetFramework) + + $(WindowsSdkPackageVersion) @@ -294,7 +297,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> - + true lib\$(TargetFramework) diff --git a/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets b/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets index 2dfc975f16..f2cff4f636 100644 --- a/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets +++ b/nuget/Microsoft.Windows.CsWinRT.CsWinRTGen.targets @@ -45,6 +45,11 @@ Copyright (C) Microsoft Corporation. All rights reserved. High true + + false + <_CsWinRTGeneratorInteropAssemblyName>WinRT.Interop <_CsWinRTGeneratorInteropAssemblyFileName>$(_CsWinRTGeneratorInteropAssemblyName).dll @@ -350,8 +355,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. StandardErrorImportance="$(CsWinRTGeneratorStandardErrorImportance)" LogStandardErrorAsError="$(CsWinRTGeneratorLogStandardErrorAsError)" /> - - + + WinRT.Projection .NETCoreApp @@ -408,6 +414,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. AssemblyName="WinRT.Component" CsWinRTToolsDirectory="$(CsWinRTMergedProjectionEffectiveToolsDirectory)" CsWinRTToolsArchitecture="$(CsWinRTToolsArchitecture)" + EmitEntryPointInitializer="$(CsWinRTEmitEntryPointInitializer)" StandardOutputImportance="$(CsWinRTGeneratorStandardOutputImportance)" StandardErrorImportance="$(CsWinRTGeneratorStandardErrorImportance)" LogStandardErrorAsError="$(CsWinRTGeneratorLogStandardErrorAsError)" /> diff --git a/nuget/Microsoft.Windows.CsWinRT.Embedded.targets b/nuget/Microsoft.Windows.CsWinRT.Embedded.targets deleted file mode 100644 index ff8015f1f1..0000000000 --- a/nuget/Microsoft.Windows.CsWinRT.Embedded.targets +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - $(DefineConstants);EMBED - - true - $(CsWinRTPath)embedded\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/nuget/Microsoft.Windows.CsWinRT.IIDOptimizer.targets b/nuget/Microsoft.Windows.CsWinRT.IIDOptimizer.targets deleted file mode 100644 index 640a3a4b7e..0000000000 --- a/nuget/Microsoft.Windows.CsWinRT.IIDOptimizer.targets +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - $(CsWinRTPath)build\tools\IIDOptimizer\ - - @(BuiltProjectOutputGroupKeyOutput->'%(Identity)') - - $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', 'IIDOptimizer')) - - @(ReferencePathWithRefAssemblies->'--refs %(Identity)', ' ') - - ---target -$(CsWinRTIIDOptimizerTargetAssembly) ---outdir -$(IIDOptimizerInterimDir) -$(GuidPatchTargetAssemblyReferences) - - - - - - - $(IIDOptimizerInterimDir)cswinrt_iidoptimizer.rsp - "$(CsWinRTIIDOptimizerPath)IIDOptimizer.exe" %40"$(CsWinRTIIDOptimizerResponseFile)" - - - - - - - - - - - - - - - - - - - - - - - - - - - $([System.IO.Directory]::GetParent($(CsWinRTIIDOptimizerTargetAssembly))) - - - - - - - - \ No newline at end of file diff --git a/nuget/Microsoft.Windows.CsWinRT.Native.targets b/nuget/Microsoft.Windows.CsWinRT.Native.targets deleted file mode 100644 index 6542a26038..0000000000 --- a/nuget/Microsoft.Windows.CsWinRT.Native.targets +++ /dev/null @@ -1,133 +0,0 @@ - - - - - <_CsWinRTNativeConsumerIntermediateDir>$(IntDir)cswinrt\ - - - - - - - - <_CsWinRTComponentProjectPaths Include="%(_ResolvedProjectReferencePaths.MSBuildSourceProjectFile)" - Condition="'%(_ResolvedProjectReferencePaths.CsWinRTComponent)' == 'true'" /> - - - - - <_CsWinRTComponentTargetFramework>%(_ResolvedProjectReferencePaths.CsWinRTComponentTargetFramework) - <_CsWinRTComponentTargetFramework Condition="'$(_CsWinRTComponentTargetFramework)' == ''">net10.0-windows10.0.26100.1 - - - - - - - - - - <_CsWinRTHasComponentReferences Condition="'@(_CsWinRTComponentProjectPaths)' != ''">true - - - - - - - <_CsWinRTTempProjectDir>$(_CsWinRTNativeConsumerIntermediateDir) - <_CsWinRTTempProjectPath>$(_CsWinRTTempProjectDir)CsWinRT.NativeConsumer.csproj - - <_CsWinRTTempProjectTFMDir>$(_CsWinRTComponentTargetFramework) - - - - - - <_CsWinRTTempProjectLines Include="<Project Sdk="Microsoft.NET.Sdk">" /> - <_CsWinRTTempProjectLines Include=" <PropertyGroup>" /> - <_CsWinRTTempProjectLines Include=" <TargetFramework>$(_CsWinRTComponentTargetFramework)</TargetFramework>" /> - <_CsWinRTTempProjectLines Include=" <Platforms>x64%3Bx86%3BARM64</Platforms>" /> - <_CsWinRTTempProjectLines Include=" <CsWinRTBuildForNativeConsumer>true</CsWinRTBuildForNativeConsumer>" /> - <_CsWinRTTempProjectLines Include=" <CsWinRTGenerateInteropAssembly>true</CsWinRTGenerateInteropAssembly>" /> - <_CsWinRTTempProjectLines Include=" <CsWinRTGenerateProjection>false</CsWinRTGenerateProjection>" /> - <_CsWinRTTempProjectLines Include=" <EnableDefaultItems>false</EnableDefaultItems>" /> - <_CsWinRTTempProjectLines Include=" <GenerateAssemblyInfo>false</GenerateAssemblyInfo>" /> - <_CsWinRTTempProjectLines Include=" <ProduceReferenceAssembly>false</ProduceReferenceAssembly>" /> - <_CsWinRTTempProjectLines Include=" <NoWarn>$(NoWarn)%3BNETSDK1130</NoWarn>" /> - <_CsWinRTTempProjectLines Include=" </PropertyGroup>" /> - <_CsWinRTTempProjectLines Include=" <ItemGroup>" /> - <_CsWinRTTempProjectLines Include=" <ProjectReference Include="%(_CsWinRTComponentProjectPaths.Identity)" />" /> - <_CsWinRTTempProjectLines Include=" </ItemGroup>" /> - <_CsWinRTTempProjectLines Include="</Project>" /> - - - - - - - - - - - - <_CsWinRTTempProjectIntermediateDir>$(_CsWinRTTempProjectDir)obj\$(Configuration)\$(_CsWinRTTempProjectTFMDir)\ - - - - <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Component.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Component.dll')" /> - <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Interop.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Interop.dll')" /> - <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Projection.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Projection.dll')" /> - <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Projection.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Projection.dll')" /> - <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Xaml.Projection.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Xaml.Projection.dll')" /> - - - - - - - diff --git a/nuget/Microsoft.Windows.CsWinRT.nuspec b/nuget/Microsoft.Windows.CsWinRT.nuspec index 1b9f18d74a..738ef0f911 100644 --- a/nuget/Microsoft.Windows.CsWinRT.nuspec +++ b/nuget/Microsoft.Windows.CsWinRT.nuspec @@ -25,10 +25,12 @@ - + + + - + @@ -37,7 +39,7 @@ - + + $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..\metadata\WindowsRuntime.Internal.winmd')) + + @@ -128,22 +144,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. - - - - $(CsWinRTPath)lib\net10.0 - $(CsWinRTPath)runtimes\**\native - - - - - - - - - - - @@ -151,6 +151,23 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + + + + + + @@ -267,7 +284,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. @(CsWinRTIncludeItems->'-include %(Identity)', ' ') - $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..\metadata\WindowsRuntime.Internal.winmd')) -input $(CsWinRTInteropMetadata) -include WindowsRuntime.Internal diff --git a/nuget/native/Microsoft.Windows.CsWinRT.targets b/nuget/native/Microsoft.Windows.CsWinRT.targets new file mode 100644 index 0000000000..6fad7ca266 --- /dev/null +++ b/nuget/native/Microsoft.Windows.CsWinRT.targets @@ -0,0 +1,262 @@ + + + + + <_CsWinRTNativeConsumerIntermediateDir>$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntDir)', 'cswinrt')) + + + normal + + + <_CsWinRTNormalizedPlatform Condition="'$(Platform)' == 'Win32'">x86 + <_CsWinRTNormalizedPlatform Condition="'$(_CsWinRTNormalizedPlatform)' == ''">$(Platform) + + + + + + + + <_CsWinRTComponentProjectPaths Include="%(_ResolvedProjectReferencePaths.MSBuildSourceProjectFile)" + Condition="'%(_ResolvedProjectReferencePaths.CsWinRTComponent)' == 'true'" /> + + + <_CsWinRTComponentTargetFrameworkValues Include="%(_ResolvedProjectReferencePaths.CsWinRTComponentTargetFramework)" + Condition="'%(_ResolvedProjectReferencePaths.CsWinRTComponent)' == 'true' and '%(_ResolvedProjectReferencePaths.CsWinRTComponentTargetFramework)' != ''" /> + <_CsWinRTComponentTargetFrameworkValues Include="%(CsWinRTNativeComponent.CsWinRTComponentTargetFramework)" + Condition="'%(CsWinRTNativeComponent.CsWinRTComponentTargetFramework)' != ''" /> + + + <_CsWinRTComponentSdkPackageVersionValues Include="%(_ResolvedProjectReferencePaths.CsWinRTComponentWindowsSdkPackageVersion)" + Condition="'%(_ResolvedProjectReferencePaths.CsWinRTComponent)' == 'true' and '%(_ResolvedProjectReferencePaths.CsWinRTComponentWindowsSdkPackageVersion)' != ''" /> + <_CsWinRTComponentSdkPackageVersionValues Include="%(CsWinRTNativeComponent.CsWinRTComponentWindowsSdkPackageVersion)" + Condition="'%(CsWinRTNativeComponent.CsWinRTComponentWindowsSdkPackageVersion)' != ''" /> + + + + <_CsWinRTHasComponentReferences Condition="'@(_CsWinRTComponentProjectPaths)' != '' or '@(CsWinRTNativeComponent)' != ''">true + + + <_CsWinRTDistinctComponentTargetFrameworks>@(_CsWinRTComponentTargetFrameworkValues->Distinct(), ';') + + + <_CsWinRTComponentTargetFramework Condition="'$(CsWinRTComponentTargetFrameworkOverride)' != ''">$(CsWinRTComponentTargetFrameworkOverride) + <_CsWinRTComponentTargetFramework Condition="'$(_CsWinRTComponentTargetFramework)' == '' and '$(_CsWinRTDistinctComponentTargetFrameworks)' != '' and !$(_CsWinRTDistinctComponentTargetFrameworks.Contains(';'))">$(_CsWinRTDistinctComponentTargetFrameworks) + + + + + + + + + + + <_CsWinRTAggregatorWindowsSdkPackageVersion Condition="'$(CsWinRTNativeConsumerWindowsSdkPackageVersion)' != ''">$(CsWinRTNativeConsumerWindowsSdkPackageVersion) + <_CsWinRTAggregatorWindowsSdkPackageVersion Condition="'$(_CsWinRTAggregatorWindowsSdkPackageVersion)' == ''">@(_CsWinRTComponentSdkPackageVersionValues->Distinct()) + + + + + <_CsWinRTAggregatorInputs Include="@(_ResolvedProjectReferencePaths)" + Condition="'%(_ResolvedProjectReferencePaths.CsWinRTComponent)' == 'true'" /> + <_CsWinRTAggregatorInputs Include="@(CsWinRTNativeComponent)" /> + <_CsWinRTAggregatorInputs Include="$(MSBuildThisFileFullPath)" /> + + + + + + + + + + + + + + <_CsWinRTTempProjectDir>$(_CsWinRTNativeConsumerIntermediateDir) + <_CsWinRTTempProjectPath>$(_CsWinRTTempProjectDir)CsWinRT.NativeConsumer.csproj + <_CsWinRTTempProjectTFMDir>$(_CsWinRTComponentTargetFramework) + + + <_CsWinRTTempProjectBaseIntermediateDir>$(_CsWinRTTempProjectDir)obj\ + <_CsWinRTTempProjectIntermediateConfigDir>$(_CsWinRTTempProjectBaseIntermediateDir)$(Configuration)\ + <_CsWinRTTempProjectIntermediateDir>$(_CsWinRTTempProjectIntermediateConfigDir)$(_CsWinRTTempProjectTFMDir)\ + + + <_CsWinRTPropsForAggregator>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..\Microsoft.Windows.CsWinRT.props')) + <_CsWinRTTargetsForAggregator>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..\Microsoft.Windows.CsWinRT.targets')) + + + + + + <_CsWinRTTempProjectLines Include="<Project>" /> + <_CsWinRTTempProjectLines Include=" <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />" /> + <_CsWinRTTempProjectLines Include=" <Import Project="$(_CsWinRTPropsForAggregator)" />" /> + <_CsWinRTTempProjectLines Include=" <PropertyGroup>" /> + <_CsWinRTTempProjectLines Include=" <TargetFramework>$(_CsWinRTComponentTargetFramework)</TargetFramework>" /> + <_CsWinRTTempProjectLines Include=" <Platforms>x64%3Bx86%3BARM64</Platforms>" /> + <_CsWinRTTempProjectLines Condition="'$(_CsWinRTAggregatorWindowsSdkPackageVersion)' != ''" + Include=" <WindowsSdkPackageVersion>$(_CsWinRTAggregatorWindowsSdkPackageVersion)</WindowsSdkPackageVersion>" /> + + <_CsWinRTTempProjectLines Include=" <BaseIntermediateOutputPath>$(_CsWinRTTempProjectBaseIntermediateDir)</BaseIntermediateOutputPath>" /> + <_CsWinRTTempProjectLines Include=" <IntermediateOutputPath>$(_CsWinRTTempProjectIntermediateConfigDir)</IntermediateOutputPath>" /> + <_CsWinRTTempProjectLines Include=" <CsWinRTBuildForNativeConsumer>true</CsWinRTBuildForNativeConsumer>" /> + <_CsWinRTTempProjectLines Include=" <CsWinRTEmitEntryPointInitializer>true</CsWinRTEmitEntryPointInitializer>" /> + <_CsWinRTTempProjectLines Include=" <CsWinRTGenerateInteropAssembly>true</CsWinRTGenerateInteropAssembly>" /> + <_CsWinRTTempProjectLines Include=" <CsWinRTGenerateProjection>false</CsWinRTGenerateProjection>" /> + <_CsWinRTTempProjectLines Include=" <EnableDefaultItems>false</EnableDefaultItems>" /> + <_CsWinRTTempProjectLines Include=" <GenerateAssemblyInfo>false</GenerateAssemblyInfo>" /> + <_CsWinRTTempProjectLines Include=" <ProduceReferenceAssembly>false</ProduceReferenceAssembly>" /> + <_CsWinRTTempProjectLines Include=" </PropertyGroup>" /> + <_CsWinRTTempProjectLines Include=" <ItemGroup>" /> + + <_CsWinRTTempProjectLines Condition="'@(_CsWinRTComponentProjectPaths)' != ''" + Include=" <ProjectReference Include="%(_CsWinRTComponentProjectPaths.Identity)" />" /> + + <_CsWinRTTempProjectLines Condition="'@(CsWinRTNativeComponent)' != ''" + Include=" <Reference Include="%(CsWinRTNativeComponent.Filename)">" /> + <_CsWinRTTempProjectLines Condition="'@(CsWinRTNativeComponent)' != ''" + Include=" <HintPath>%(CsWinRTNativeComponent.Identity)</HintPath>" /> + <_CsWinRTTempProjectLines Condition="'@(CsWinRTNativeComponent)' != ''" + Include=" <CsWinRTComponent>true</CsWinRTComponent>" /> + <_CsWinRTTempProjectLines Condition="'@(CsWinRTNativeComponent)' != ''" + Include=" <Private>true</Private>" /> + <_CsWinRTTempProjectLines Condition="'@(CsWinRTNativeComponent)' != ''" + Include=" </Reference>" /> + <_CsWinRTTempProjectLines Include=" </ItemGroup>" /> + <_CsWinRTTempProjectLines Include=" <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />" /> + <_CsWinRTTempProjectLines Include=" <Import Project="$(_CsWinRTTargetsForAggregator)" />" /> + <_CsWinRTTempProjectLines Include="</Project>" /> + + + + + + + + + + <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Component.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Component.dll')" /> + <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Interop.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Interop.dll')" /> + <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Projection.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Projection.dll')" /> + <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Projection.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Projection.dll')" /> + <_CsWinRTGeneratedDlls Include="$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Xaml.Projection.dll" Condition="Exists('$(_CsWinRTTempProjectIntermediateDir)WinRT.Sdk.Xaml.Projection.dll')" /> + + + + + + + + + + + <_CsWinRTHostDllPath>$(MSBuildThisFileDirectory)..\..\hosting\$(_CsWinRTNormalizedPlatform)\native\WinRT.Host.dll + <_CsWinRTHostMuiPath>$(MSBuildThisFileDirectory)..\..\hosting\$(_CsWinRTNormalizedPlatform)\native\en-US\WinRT.Host.dll.mui + + + + <_CsWinRTHostingItemsToReplace Include="@(ReferenceCopyLocalPaths)" + Condition="'%(Filename)%(Extension)' == 'WinRT.Host.dll' or '%(Filename)%(Extension)' == 'WinRT.Host.dll.mui'" /> + + + + + + + + diff --git a/nuget/readme.md b/nuget/readme.md index 392c45f1af..094a256562 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -84,6 +84,62 @@ If a Windows SDK is installed, $(WindowsSDKVersion) is defined when building fro The C#/WinRT package can be used both to consume WinRT types, and to produce them (via CsWinRTComponent). It is also possible to combine these settings and do both. For example, a developer might want to *produce* a library that's implemented in terms of another WinRT runtime class (*consuming* it). +## Consuming CsWinRT components from a native (C++) app + +When a native (C++) `.vcxproj` references one or more managed CsWinRT components, the CsWinRT package's +`build/native/Microsoft.Windows.CsWinRT.targets` walks the component references and synthesizes a temporary +aggregator project. That aggregator runs the cswinrt projection, interop and component pipeline once across +the union of all referenced components, producing a single deduplicated `WinRT.Interop.dll` plus +`WinRT.Component.dll`, `WinRT.Projection.dll` and `WinRT.Sdk.Projection.dll`. The merged hosting bundle is +copied to the consumer's output directory next to `WinRT.Host.dll` / `WinRT.Host.Shim.dll`. + +Component references are picked up from two sources, treated as equivalent inputs: + +* `` items whose `CsWinRTComponent` metadata is `true`. Components built with + `CsWinRTComponent=true` automatically expose this metadata. +* `@(CsWinRTNativeComponent)` items contributed by component-package targets files (see below). + +Consumer-side properties: + +| Property | Description | +|-|-| +| `CsWinRTDisableNativeComponentInterop` | Set to `true` to disable aggregator generation. | +| `CsWinRTComponentTargetFrameworkOverride` | TFM to use for the aggregator project. Required when referenced components disagree on TFM, or when no referenced component supplies a TFM. | +| `CsWinRTNativeConsumerWindowsSdkPackageVersion` | `WindowsSdkPackageVersion` to pass to the aggregator. Falls back to a value picked from any component reference, then to the SDK default. | +| `CsWinRTSkipNativeHostingAssetsOverride` | Set to `true` to opt out of the arch-correct replacement of `WinRT.Host.dll` / `.mui` in the consumer's reference closure. | + +### Distributing a CsWinRT component for native consumers + +A CsWinRT component is built with `CsWinRTComponent=true` in its `.csproj`. The build produces a managed +`{AssemblyName}.dll` plus a `.winmd`. Two distribution shapes are supported: + +#### Option 1 — Ship the managed component .dll (JIT hosting, supports merged multi-component aggregation) + +Native consumers reference the managed component and the consumer's own `WinRT.Host.dll`/`WinRT.Host.Shim.dll` +load it at runtime. Component packages opt in by shipping a small `build/native/{PackageId}.targets` that +contributes the component .dll to `@(CsWinRTNativeComponent)` and its `.winmd` to `@(CsWinRTInputs)`: + +```xml + + + + + net10.0-windows10.0.26100.1 + + + + +``` + +The `Identity` is the path to the component .dll. `CsWinRTComponentTargetFramework` is required and is used +by the consumer to derive the aggregator's target framework. + +#### Option 2 — AOT-publish the component and ship the resulting native binary + +The component author runs `dotnet publish -p:PublishAot=true` on the component themselves and distributes +the resulting native dll. Native consumers load that binary directly through whatever activation mechanism +they prefer; they do not go through the aggregator. + ## Troubleshooting The MSBuild verbosity level maps to MSBuild message importance as follows: diff --git a/src/Authoring/WinRT.Host/WinRT.Host.cpp b/src/Authoring/WinRT.Host/WinRT.Host.cpp index 82566f5995..aa738ad080 100644 --- a/src/Authoring/WinRT.Host/WinRT.Host.cpp +++ b/src/Authoring/WinRT.Host/WinRT.Host.cpp @@ -376,16 +376,6 @@ void GetActivationFactory(void* hstr_class_id, void** activation_factory) init_runtime(host_module.wstring().c_str(), host_config.c_str()); - // If no explicit target assembly mapping found, probe for it by naming convention - if (target_path.empty()) - { - target_path = probe_for_target_assembly(host_module, class_id); - if (target_path.empty() || !std::filesystem::exists(target_path)) - { - winrt::throw_hresult(HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND)); - } - } - // Load shim (managed portion of host) and retrieve get_activation_factory pointer if (::get_activation_factory == nullptr) { @@ -395,11 +385,57 @@ void GetActivationFactory(void* hstr_class_id, void** activation_factory) shim_path.wstring().c_str(), L"WinRT.Host.Shim, WinRT.Host.Shim", L"GetActivationFactory", - L"WinRT.Host.Shim+GetActivationFactoryDelegate, WinRT.Host.Shim", + L"WinRT.Host.Shim+GetActivationFactoryDelegate, WinRT.Host.Shim", nullptr, (void**)&::get_activation_factory)); } + // If no explicit target assembly mapping was supplied via runtimeconfig, prefer a deployed + // 'WinRT.Component.dll'. This is the merged-projection / merged-activation dll produced by + // the aggregator's projection generator for native-consumer scenarios with one or more + // CsWinRT components. Its merged 'ABI.WinRT.Component.ManagedExports.GetActivationFactory' + // dispatches across each input component. A 'CLASS_E_CLASSNOTAVAILABLE' from the shim + // means the merged dll didn't aggregate that runtime class, so we fall through to the + // existing per-component prefix probe below. Any other failing HRESULT is fatal. + if (target_path.empty()) + { + auto winrt_component_path = host_module; + winrt_component_path.replace_filename(L"WinRT.Component.dll"); + + if (std::filesystem::exists(winrt_component_path)) + { + winrt::hstring hstr_winrt_component_path(winrt_component_path.c_str()); + *activation_factory = nullptr; + + HRESULT hr = ::get_activation_factory( + winrt::get_abi(hstr_winrt_component_path), hstr_class_id, activation_factory); + + if (SUCCEEDED(hr)) + { + return; + } + + if (hr != CLASS_E_CLASSNOTAVAILABLE) + { + check_hostfxr_hresult(hr); + } + + // 'WinRT.Component.dll' did not aggregate this runtime class; fall through + // to the existing per-component probing logic below. + *activation_factory = nullptr; + } + } + + // If no explicit target assembly mapping found, probe for it by naming convention + if (target_path.empty()) + { + target_path = probe_for_target_assembly(host_module, class_id); + if (target_path.empty() || !std::filesystem::exists(target_path)) + { + winrt::throw_hresult(HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND)); + } + } + // Load target assembly and get managed runtime class activation factory winrt::hstring hstr_target_path(target_path.c_str()); check_hostfxr_hresult(::get_activation_factory( diff --git a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs index 0fc02a3df8..cb09452714 100644 --- a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs +++ b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs @@ -40,10 +40,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return options.GlobalOptions.GetCsWinRTUseWindowsUIXamlProjections(); }); - // Get whether the project is being built for a native consumer - IncrementalValueProvider isBuildForNativeConsumer = context.AnalyzerConfigOptionsProvider.Select(static (options, token) => + // Get whether the current project is itself a component + IncrementalValueProvider isComponent = context.AnalyzerConfigOptionsProvider.Select(static (options, token) => { - return options.GlobalOptions.GetBuildForNativeConsumer(); + return options.GlobalOptions.GetCsWinRTComponent(); }); // Get whether the current project is a library published with Native AOT @@ -52,11 +52,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Combine(isPublishAot) .Select(static (flags, token) => flags.Left && flags.Right); - // Get whether the generator should actually run or not + // Get whether the generator should actually run or not. + // The gate fires for any unit that ships TypeMap entries: an executable, a NativeAOT-published library, + // or a Windows Runtime component (the component .dll itself is the deployable when consumed natively). IncrementalValueProvider isGeneratorEnabled = isOutputTypeExe .Combine(isPublishAotLibrary) - .Combine(isBuildForNativeConsumer) + .Combine(isComponent) .Select(static (flags, token) => flags.Left.Left || flags.Left.Right || flags.Right); // Gather all PE references from the current compilation @@ -80,12 +82,16 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Select(static (name, _) => name!) .Collect(); - // Combine all matching assembly names (reference + component) and filter out the Windows SDK ones - IncrementalValueProvider> filteredAssemblyNames = + // Collect non-Windows-SDK reference assembly names. These feed the merged projection (WinRT.Projection.dll). + IncrementalValueProvider> nonSdkReferenceAssemblyNames = referenceAssemblyNames .Where(static name => name is not null and not "Microsoft.Windows.SDK.NET" and not "Microsoft.Windows.UI.Xaml") .Select(static (name, _) => name!) - .Collect() + .Collect(); + + // Combine reference and component assembly names for the per-assembly '[TypeMapAssemblyTarget]' entries. + IncrementalValueProvider> filteredAssemblyNames = + nonSdkReferenceAssemblyNames .Combine(collectedComponentAssemblyNames) .Select(static (pair, token) => pair.Left.AddRange(pair.Right)); @@ -94,9 +100,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) filteredAssemblyNames .Select(static (names, token) => names.Sort(StringComparer.Ordinal).AsEquatableArray()); - // Whether the merged projection will be generated + // Whether the merged projection will be generated. Only non-Windows-SDK reference assemblies feed + // WinRT.Projection.dll; component assemblies are projected into WinRT.Component.dll instead. IncrementalValueProvider hasMergedProjection = - filteredAssemblyNames + nonSdkReferenceAssemblyNames .Select(static (assemblyNames, token) => !assemblyNames.IsDefaultOrEmpty); // Generate the attributes for all matching assemblies @@ -108,12 +115,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // Also generate the '[TypeMapAssemblyTarget]' entry for the merged projection context.RegisterImplementationSourceOutput(hasMergedProjection, Execute.EmitMergedProjectionTypeMapAssemblyTargetAttributes); - // Get whether the current project is a component - IncrementalValueProvider isComponent = context.AnalyzerConfigOptionsProvider.Select(static (options, token) => - { - return options.GlobalOptions.GetCsWinRTComponent(); - }); - // Whether any component assemblies are referenced, or the current project is itself a component IncrementalValueProvider hasComponentAssembly = collectedComponentAssemblyNames diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 056a8ddd2e..5a10c6a6d5 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -21,9 +21,13 @@ + + + @@ -103,7 +107,7 @@ diff --git a/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.vcxproj b/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.vcxproj index 8d55f37bf5..d4f5a2ab0e 100644 --- a/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.vcxproj +++ b/src/Tests/AuthoringConsumptionTest/AuthoringConsumptionTest.vcxproj @@ -99,7 +99,7 @@ {41e2a272-150f-42f5-ad40-047aad9088a0} - TargetFramework=net10.0 + TargetFramework=net10.0-windows10.0.26100.1 - + - - - <_CsWinRTComponentProjectPath>..\AuthoringTest\AuthoringTest.csproj - <_CsWinRTComponentOutputDir>..\AuthoringTest\bin\$(_WinMDPlatform)\$(Configuration)\net10.0\ - <_CsWinRTComponentIntermediateDir>..\AuthoringTest\obj\$(_WinMDPlatform)\$(Configuration)\net10.0\ - - - - - - - <_CsWinRTJitGeneratedDlls Include="$(_CsWinRTComponentIntermediateDir)WinRT.Component.dll" Condition="Exists('$(_CsWinRTComponentIntermediateDir)WinRT.Component.dll')" /> - <_CsWinRTJitGeneratedDlls Include="$(_CsWinRTComponentIntermediateDir)WinRT.Interop.dll" Condition="Exists('$(_CsWinRTComponentIntermediateDir)WinRT.Interop.dll')" /> - <_CsWinRTJitGeneratedDlls Include="$(_CsWinRTComponentIntermediateDir)WinRT.Projection.dll" Condition="Exists('$(_CsWinRTComponentIntermediateDir)WinRT.Projection.dll')" /> - <_CsWinRTJitGeneratedDlls Include="$(_CsWinRTComponentIntermediateDir)WinRT.Sdk.Projection.dll" Condition="Exists('$(_CsWinRTComponentIntermediateDir)WinRT.Sdk.Projection.dll')" /> - <_CsWinRTJitGeneratedDlls Include="$(_CsWinRTComponentIntermediateDir)WinRT.Sdk.Xaml.Projection.dll" Condition="Exists('$(_CsWinRTComponentIntermediateDir)WinRT.Sdk.Xaml.Projection.dll')" /> - - - - ..\AuthoringTest\bin\$(_WinMDPlatform)\$(Configuration)\net10.0\win-$(Platform)\AuthoringTest.winmd + ..\AuthoringTest\bin\$(_WinMDPlatform)\$(Configuration)\net10.0-windows10.0.26100.1\win-$(Platform)\AuthoringTest.winmd true - + - - + + diff --git a/src/Tests/AuthoringConsumptionTest2.AOT/AuthoringConsumptionTest2.AOT.csproj b/src/Tests/AuthoringConsumptionTest2.AOT/AuthoringConsumptionTest2.AOT.csproj new file mode 100644 index 0000000000..6d39f286da --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2.AOT/AuthoringConsumptionTest2.AOT.csproj @@ -0,0 +1,35 @@ + + + + net10.0-windows10.0.26100.1 + x64 + win-x64 + + true + + true + Shared + true + Exe + true + true + + + + + + + + + + + + + all + + + + + + + diff --git a/src/Tests/AuthoringConsumptionTest2.AOT/Directory.Build.props b/src/Tests/AuthoringConsumptionTest2.AOT/Directory.Build.props new file mode 100644 index 0000000000..6a86d69f2e --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2.AOT/Directory.Build.props @@ -0,0 +1,9 @@ + + + + true + + + + + diff --git a/src/Tests/AuthoringConsumptionTest2.AOT/EntryPoint.cs b/src/Tests/AuthoringConsumptionTest2.AOT/EntryPoint.cs new file mode 100644 index 0000000000..ae24a069c0 --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2.AOT/EntryPoint.cs @@ -0,0 +1,8 @@ +[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows")] + +internal static class Program +{ + static void Main() + { + } +} diff --git a/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.AOT.exe.manifest b/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.AOT.exe.manifest new file mode 100644 index 0000000000..33eb965487 --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.AOT.exe.manifest @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.exe.manifest b/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.exe.manifest new file mode 100644 index 0000000000..e4fbe38c96 --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.exe.manifest @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.vcxproj b/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.vcxproj new file mode 100644 index 0000000000..cf00d96d7d --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/AuthoringConsumptionTest2.vcxproj @@ -0,0 +1,247 @@ + + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8a4b1f2a-9c2e-4d3a-bf68-2a4f5b7c8d9e} + Win32Proj + AuthoringConsumptionTest2 + Application + v143 + v142 + Unicode + <_WinMDPlatform>$(Platform) + <_WinMDPlatform Condition="'$(Platform)' == 'Win32'">x86 + false + true + + + + + + + + + + + + + + Create + + + + + + + + true + + + + + + {ffa9a78b-f53f-43ee-af87-24a80f4c330a} + TargetFramework=net10.0 + + + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA} + TargetFramework=net10.0 + + + {0bb8f82d-874e-45aa-bca3-20ce0562164a} + TargetFramework=net10.0 + + + {7e33bcb7-19c5-4061-981d-ba695322708a} + + + {25244ced-966e-45f2-9711-1f51e951ff89} + TargetFramework=net10.0 + + + TargetFramework=net10.0-windows10.0.26100.1 + + + TargetFramework=net10.0-windows10.0.26100.1 + + + + + + TargetFramework=net10.0-windows10.0.26100.1 + RuntimeIdentifier=win-$(Platform) + false + false + + + TargetFramework=net10.0-windows10.0.26100.1 + RuntimeIdentifier=win-$(Platform) + false + false + + + + AuthoringConsumptionTest2.AOT + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\$(NativeLibraryProjectName)'))\ + $(NativeLibraryProjectDirectory)$(NativeLibraryProjectName).csproj + $(NativeLibraryProjectDirectory)bin\$(Platform)\$(Configuration)\net10.0-windows10.0.26100.1\win-$(Platform)\publish\ + $(NativeLibraryPublishFolder)$(NativeLibraryProjectName).dll + + + + + + + + + + + ..\AuthoringTest3\bin\$(_WinMDPlatform)\$(Configuration)\net10.0-windows10.0.26100.1\AuthoringTest3.winmd + true + + + ..\AuthoringTest2\bin\$(_WinMDPlatform)\$(Configuration)\net10.0-windows10.0.26100.1\AuthoringTest2.winmd + true + + + + + + ..\AuthoringTest3\bin\$(_WinMDPlatform)\$(Configuration)\net10.0-windows10.0.26100.1\AuthoringTest3.winmd + true + AuthoringConsumptionTest2.AOT.dll + + + ..\AuthoringTest2\bin\$(_WinMDPlatform)\$(Configuration)\net10.0-windows10.0.26100.1\AuthoringTest2.winmd + true + AuthoringConsumptionTest2.AOT.dll + + + + + + + + + + + + + + + + + + + + + + + Use + pch.h + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + + + true + Console + + + + + Use + pch.h + MaxSpeed + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + Level3 + + + true + true + true + Console + + + + + Use + pch.h + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + + + true + Console + + + + + Use + pch.h + MaxSpeed + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + Level3 + + + true + true + true + Console + + + + + + + diff --git a/src/Tests/AuthoringConsumptionTest2/Directory.Build.targets b/src/Tests/AuthoringConsumptionTest2/Directory.Build.targets new file mode 100644 index 0000000000..855c176a63 --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/Directory.Build.targets @@ -0,0 +1,16 @@ + + + + $(MSBuildProjectDirectory)/DoNotImport_MsAppxPackageTargets.targets + CopyTestAssets;$(PrepareForRunDependsOn) + + + + + + + + + diff --git a/src/Tests/AuthoringConsumptionTest2/DoNotImport_MsAppxPackageTargets.targets b/src/Tests/AuthoringConsumptionTest2/DoNotImport_MsAppxPackageTargets.targets new file mode 100644 index 0000000000..74f0916e0a --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/DoNotImport_MsAppxPackageTargets.targets @@ -0,0 +1,3 @@ + + + diff --git a/src/Tests/AuthoringConsumptionTest2/WinRT.Host.runtimeconfig.json b/src/Tests/AuthoringConsumptionTest2/WinRT.Host.runtimeconfig.json new file mode 100644 index 0000000000..88b184e831 --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/WinRT.Host.runtimeconfig.json @@ -0,0 +1,10 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "rollForward": "LatestMinor", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + } + } +} diff --git a/src/Tests/AuthoringConsumptionTest2/packages.config b/src/Tests/AuthoringConsumptionTest2/packages.config new file mode 100644 index 0000000000..0470fd6c7d --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/packages.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/Tests/AuthoringConsumptionTest2/pch.cpp b/src/Tests/AuthoringConsumptionTest2/pch.cpp new file mode 100644 index 0000000000..1d9f38c57d --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/Tests/AuthoringConsumptionTest2/pch.h b/src/Tests/AuthoringConsumptionTest2/pch.h new file mode 100644 index 0000000000..ce364f2e67 --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/pch.h @@ -0,0 +1,22 @@ +#pragma once + +// Undefine GetCurrentTime macro to prevent +// conflict with Storyboard::GetCurrentTime +#undef GetCurrentTime + +#include +#include +#include + +#pragma push_macro("X86") +#pragma push_macro("X64") +#undef X86 +#undef X64 +#include "winrt/Windows.System.h" +#pragma pop_macro("X64") +#pragma pop_macro("X86") + +#include +#include + +#include "gtest/gtest.h" diff --git a/src/Tests/AuthoringConsumptionTest2/test.cpp b/src/Tests/AuthoringConsumptionTest2/test.cpp new file mode 100644 index 0000000000..242dfc49a0 --- /dev/null +++ b/src/Tests/AuthoringConsumptionTest2/test.cpp @@ -0,0 +1,67 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; + +// Activation tests across two CsWinRT components aggregated into one merged AOT host. + +TEST(MultiComponent, CalculatorStatics) +{ + EXPECT_EQ(AuthoringTest3::Calculator::GetDefaultFactor(), 1); + EXPECT_EQ(AuthoringTest3::Calculator::GetDefaultNumber(), 2); +} + +TEST(MultiComponent, GreeterMethods) +{ + AuthoringTest2::Greeter greeter; + EXPECT_EQ(greeter.Greet(L"world"), hstring(L"Hello, world!")); + EXPECT_EQ(greeter.Add(2, 3), 5); +} + +TEST(MultiComponent, BothComponentsActivateInOneProcess) +{ + AuthoringTest3::Calculator first; + AuthoringTest2::Greeter second; + + EXPECT_EQ(first.GetFactor(), 1); + EXPECT_EQ(second.Add(10, 20), 30); +} + +// Generic instantiations from both components flow through the merged interop closure. +// If per-component interop generation had run independently, type-map registration would +// fail at publish time or these calls would fail at runtime. + +TEST(MultiComponent, GenericCollectionsFromBothComponents) +{ + AuthoringTest2::Greeter greeter; + auto numbers = greeter.GetNumbers(); + ASSERT_EQ(numbers.Size(), 6u); + EXPECT_EQ(numbers.GetAt(0), 1); + EXPECT_EQ(numbers.GetAt(5), 13); + + AuthoringTest3::Calculator calculator; + auto bools = calculator.GetBools(); + EXPECT_GT(bools.Size(), 0u); + + auto uris = calculator.GetUris(); + EXPECT_GT(uris.Size(), 0u); +} + +TEST(MultiComponent, GenericMapFromComponent2) +{ + AuthoringTest2::Greeter greeter; + auto counts = greeter.GetCounts(); + + ASSERT_EQ(counts.Size(), 3u); + EXPECT_EQ(counts.Lookup(L"alpha"), 1); + EXPECT_EQ(counts.Lookup(L"beta"), 2); + EXPECT_EQ(counts.Lookup(L"gamma"), 3); +} + +int main(int argc, char** argv) +{ + init_apartment(); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/Tests/AuthoringTest/AuthoringTest.csproj b/src/Tests/AuthoringTest/AuthoringTest.csproj index 24afdfea4b..7a5f332d59 100644 --- a/src/Tests/AuthoringTest/AuthoringTest.csproj +++ b/src/Tests/AuthoringTest/AuthoringTest.csproj @@ -1,7 +1,7 @@ - net10.0 + net10.0-windows10.0.26100.1 x64;x86 true true @@ -41,7 +41,10 @@ - + + + compile;runtime + all diff --git a/src/Tests/AuthoringTest/Module.cs b/src/Tests/AuthoringTest/Module.cs index 1bc22ba3d2..c909bfc470 100644 --- a/src/Tests/AuthoringTest/Module.cs +++ b/src/Tests/AuthoringTest/Module.cs @@ -4,22 +4,6 @@ namespace AuthoringTest; -// CsWinRT makes use of the .NET typemap to register all the projected types. -// As part of this, .NET uses TypeMapAssemblyTarget to discover the assemblies with the type map. -// But this needs to be on the launching executable for it to discover them by default or use the -// RuntimeHostConfiguration which isn't available on current builds. Due to this, -// we manually set the entry assembly which allows .NET to discover it. -internal static class ProjectionTypesInitializer -{ -#pragma warning disable CA2255 // The 'ModuleInitializer' attribute should not be used in libraries - [System.Runtime.CompilerServices.ModuleInitializer] -#pragma warning restore CA2255 // The 'ModuleInitializer' attribute should not be used in libraries - internal static void InitializeProjectionTypes() - { - Assembly.SetEntryAssembly(typeof(ProjectionTypesInitializer).Assembly); - } -} - internal class Program { static void Main(string[] args) diff --git a/src/Tests/AuthoringTest2/AuthoringTest2.csproj b/src/Tests/AuthoringTest2/AuthoringTest2.csproj new file mode 100644 index 0000000000..3de3658513 --- /dev/null +++ b/src/Tests/AuthoringTest2/AuthoringTest2.csproj @@ -0,0 +1,18 @@ + + + + net10.0-windows10.0.26100.1 + x64;x86 + true + true + 10.0.18362.0 + win-x86;win-x64;win-arm64 + + + + + + + + + diff --git a/src/Tests/AuthoringTest2/Directory.Build.props b/src/Tests/AuthoringTest2/Directory.Build.props new file mode 100644 index 0000000000..6a86d69f2e --- /dev/null +++ b/src/Tests/AuthoringTest2/Directory.Build.props @@ -0,0 +1,9 @@ + + + + true + + + + + diff --git a/src/Tests/AuthoringTest2/Greeter.cs b/src/Tests/AuthoringTest2/Greeter.cs new file mode 100644 index 0000000000..2895b53c78 --- /dev/null +++ b/src/Tests/AuthoringTest2/Greeter.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +namespace AuthoringTest2; + +public sealed class Greeter +{ + public string Greet(string name) + { + return $"Hello, {name}!"; + } + + public int Add(int a, int b) + { + return a + b; + } + + // Generic collection returns; exercise the merged interop's marshalling closure. + public IList GetNumbers() + { + return new List { 1, 2, 3, 5, 8, 13 }; + } + + public IDictionary GetCounts() + { + return new Dictionary + { + { "alpha", 1 }, + { "beta", 2 }, + { "gamma", 3 }, + }; + } +} diff --git a/src/Tests/AuthoringTest2/Module.cs b/src/Tests/AuthoringTest2/Module.cs new file mode 100644 index 0000000000..56fd158d77 --- /dev/null +++ b/src/Tests/AuthoringTest2/Module.cs @@ -0,0 +1,12 @@ +using System.Reflection; + +[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows")] + +namespace AuthoringTest2; + +internal class Program +{ + static void Main(string[] args) + { + } +} diff --git a/src/Tests/AuthoringTest3/AuthoringTest3.csproj b/src/Tests/AuthoringTest3/AuthoringTest3.csproj new file mode 100644 index 0000000000..3de3658513 --- /dev/null +++ b/src/Tests/AuthoringTest3/AuthoringTest3.csproj @@ -0,0 +1,18 @@ + + + + net10.0-windows10.0.26100.1 + x64;x86 + true + true + 10.0.18362.0 + win-x86;win-x64;win-arm64 + + + + + + + + + diff --git a/src/Tests/AuthoringTest3/Calculator.cs b/src/Tests/AuthoringTest3/Calculator.cs new file mode 100644 index 0000000000..0bfb64c716 --- /dev/null +++ b/src/Tests/AuthoringTest3/Calculator.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace AuthoringTest3; + +public sealed class Calculator +{ + public static int GetDefaultFactor() => 1; + public static int GetDefaultNumber() => 2; + + public int GetFactor() => 1; + + public int Multiply(int a, int b) => a * b; + + // Generic collection returns; exercise the merged interop's marshalling closure. + public IList GetBools() + { + return new List { true, false, true }; + } + + public IList GetUris() + { + return new List + { + new Uri("https://example.com/a"), + new Uri("https://example.com/b"), + }; + } +} diff --git a/src/Tests/AuthoringTest3/Directory.Build.props b/src/Tests/AuthoringTest3/Directory.Build.props new file mode 100644 index 0000000000..6a86d69f2e --- /dev/null +++ b/src/Tests/AuthoringTest3/Directory.Build.props @@ -0,0 +1,9 @@ + + + + true + + + + + diff --git a/src/Tests/AuthoringTest3/Module.cs b/src/Tests/AuthoringTest3/Module.cs new file mode 100644 index 0000000000..2a778079f1 --- /dev/null +++ b/src/Tests/AuthoringTest3/Module.cs @@ -0,0 +1,12 @@ +using System.Reflection; + +[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows")] + +namespace AuthoringTest3; + +internal class Program +{ + static void Main(string[] args) + { + } +} diff --git a/src/WinRT.Generator.Tasks/RunCsWinRTMergedProjectionGenerator.cs b/src/WinRT.Generator.Tasks/RunCsWinRTMergedProjectionGenerator.cs index 307304eda4..15c9eb4224 100644 --- a/src/WinRT.Generator.Tasks/RunCsWinRTMergedProjectionGenerator.cs +++ b/src/WinRT.Generator.Tasks/RunCsWinRTMergedProjectionGenerator.cs @@ -86,6 +86,13 @@ public sealed class RunCsWinRTMergedProjectionGenerator : ToolTask /// public bool WindowsUIXamlProjection { get; set; } + /// + /// Gets or sets whether to emit the 'ProjectionTypesInitializer' module initializer + /// (Assembly.SetEntryAssembly) into 'WinRT.Component.dll'. Only needed under JIT to + /// enable TypeMap discovery; AOT uses a separate exe-project workaround. + /// + public bool EmitEntryPointInitializer { get; set; } + /// protected override string ToolName => "cswinrtprojectiongen.exe"; @@ -208,6 +215,11 @@ protected override string GenerateResponseFileCommands() AppendResponseFileCommand(args, "--windows-ui-xaml-projection", "true"); } + if (EmitEntryPointInitializer) + { + AppendResponseFileCommand(args, "--emit-entry-point-initializer", "true"); + } + // Add any additional arguments that are not statically known foreach (ITaskItem additionalArgument in AdditionalArguments ?? []) { diff --git a/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs index ca9c572266..960098b3cd 100644 --- a/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs +++ b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.Generate.cs @@ -45,11 +45,22 @@ private static ProjectionGeneratorProcessingState ProcessReferences(ProjectionGe { args.Token.ThrowIfCancellationRequested(); - GenerateRspFile(args, out string outputFolder, out string rspFile, out HashSet projectionReferenceAssemblies, out bool hasTypesToProject); + GenerateRspFile( + args, + out string outputFolder, + out string rspFile, + out HashSet projectionReferenceAssemblies, + out bool hasTypesToProject, + out List componentAssemblyNames); string[] referencesWithoutProjections = [.. args.ReferenceAssemblyPaths.Where(r => !projectionReferenceAssemblies.Contains(r))]; - return new ProjectionGeneratorProcessingState(outputFolder, rspFile, referencesWithoutProjections, hasTypesToProject); + return new ProjectionGeneratorProcessingState( + outputFolder, + rspFile, + referencesWithoutProjections, + hasTypesToProject, + componentAssemblyNames); } /// @@ -111,12 +122,14 @@ private static void GenerateSources(ProjectionGeneratorArgs args, ProjectionGene /// The generated response file for running cswinrt.exe. /// The projection reference assemblies which were used to generate the response file. /// Whether any types were found to include in the projection. + /// Sorted simple names of input [WindowsRuntimeComponentAssembly] references (populated only in component mode). private static void GenerateRspFile( ProjectionGeneratorArgs args, out string outputFolder, out string rspFile, out HashSet projectionReferenceAssemblies, - out bool hasTypesToProject) + out bool hasTypesToProject, + out List componentAssemblyNames) { args.Token.ThrowIfCancellationRequested(); @@ -124,6 +137,7 @@ private static void GenerateRspFile( rspFile = Path.Combine(outputFolder, "ProjectionGenerator.rsp"); projectionReferenceAssemblies = []; hasTypesToProject = false; + componentAssemblyNames = []; using StreamWriter fileStream = new(rspFile); @@ -144,7 +158,7 @@ private static void GenerateRspFile( if (isComponentMode) { // Collect the names of all component assemblies from the references - HashSet componentAssemblyNames = []; + HashSet componentAssemblyNameSet = []; foreach (string refPath in args.ReferenceAssemblyPaths) { @@ -157,16 +171,20 @@ private static void GenerateRspFile( if (IsComponentAssembly(refModule) && refModule.Assembly?.Name is Utf8String name) { - _ = componentAssemblyNames.Add(name.Value); + _ = componentAssemblyNameSet.Add(name.Value); } } + // Sort for stable codegen output + componentAssemblyNames = [.. componentAssemblyNameSet]; + componentAssemblyNames.Sort(StringComparer.Ordinal); + // Scan WinMD files matching component assembly names (e.g. 'MyComponent.winmd') foreach (string winmdPath in args.WinMDPaths) { string winmdFileName = Path.GetFileNameWithoutExtension(winmdPath); - if (!componentAssemblyNames.Contains(winmdFileName)) + if (!componentAssemblyNameSet.Contains(winmdFileName)) { continue; } @@ -254,6 +272,8 @@ private static void GenerateRspFile( if (isWindowsSdkMode && projectionReferenceAssemblies.Count == 0) { WriteWindowsSdkFilters(fileStream, args.WindowsUIXamlProjection); + + hasTypesToProject = true; } // If we're not in Windows SDK mode, we exclude the Windows namespace to avoid diff --git a/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.WinRTComponentSources.cs b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.WinRTComponentSources.cs new file mode 100644 index 0000000000..027cda92b6 --- /dev/null +++ b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.WinRTComponentSources.cs @@ -0,0 +1,282 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Text; +using AsmResolver.DotNet; + +namespace WindowsRuntime.ProjectionGenerator.Generation; + +/// +internal partial class ProjectionGenerator +{ + /// + /// Component-mode only. Emits the supporting source files that make WinRT.Component.dll + /// the merged aggregator dll: (a) a module initializer that designates this assembly as the + /// entry assembly for .NET TypeMap discovery, (b) a [TypeMapAssemblyTarget] union + /// covering each component plus shared infrastructure assemblies, and (c) a merged + /// ABI.WinRT.Component.ManagedExports.GetActivationFactory that the + /// WinRT.Host.Shim reflects on. + /// + /// + /// These emissions write directly into + /// (the same folder cswinrt.exe targets), so they are picked up automatically by the + /// Roslyn compile step in . + /// + private static void EmitWinRTComponentSources(ProjectionGeneratorArgs args, ProjectionGeneratorProcessingState processingState) + { + // Only emit these files when producing 'WinRT.Component.dll'. + if (args.AssemblyName != "WinRT.Component") + { + return; + } + + // Nothing to merge if there are no input components. cswinrt.exe -component would + // not have produced any 'ABI.{ComponentName}.ManagedExports' types either. + if (processingState.ComponentAssemblyNames.Count == 0) + { + return; + } + + if (args.EmitEntryPointInitializer) + { + WriteProjectionTypesInitializer(processingState); + } + + WriteTypeMapAssemblyTargets(args, processingState); + WriteMergedManagedExports(processingState); + } + + /// + /// Emits a [ModuleInitializer]-driven SetEntryAssembly in WinRT.Component.dll. + /// This designates WinRT.Component.dll as the entry assembly the .NET runtime uses + /// to root [TypeMapAssemblyTarget] discovery. Idempotent: skipped if the host has + /// already set an entry assembly. Per-component dlls intentionally do not emit a similar + /// initializer; consumers using a single component dll directly must call + /// Assembly.SetEntryAssembly themselves if they need TypeMap discovery rooted there. + /// + private static void WriteProjectionTypesInitializer(ProjectionGeneratorProcessingState processingState) + { + const string source = """ + // + #pragma warning disable + + namespace ABI.WinRT.Component; + + /// + /// Designates WinRT.Component.dll as the entry assembly for the hosted .NET + /// runtime so that [TypeMapAssemblyTarget] discovery (rooted at the entry + /// assembly) sees the union assembly attributes emitted on this dll. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal static class ProjectionTypesInitializer + { + #pragma warning disable CA2255 // The 'ModuleInitializer' attribute should not be used in libraries + [global::System.Runtime.CompilerServices.ModuleInitializer] + #pragma warning restore CA2255 + internal static void InitializeProjectionTypes() + { + if (global::System.Reflection.Assembly.GetEntryAssembly() is null) + { + global::System.Reflection.Assembly.SetEntryAssembly(typeof(ProjectionTypesInitializer).Assembly); + } + } + } + """; + + File.WriteAllText(Path.Combine(processingState.SourcesFolder, "ProjectionTypesInitializer.g.cs"), source); + } + + /// + /// Emits the union [TypeMapAssemblyTarget] assembly attributes on + /// WinRT.Component.dll. The union covers each input component, the shared CsWinRT + /// infrastructure assemblies (WinRT.Interop, WinRT.Runtime, + /// WinRT.Sdk.Projection), and (when applicable) WinRT.Sdk.Xaml.Projection + /// and WinRT.Projection. + /// + /// + /// Only assemblies that will actually be deployed alongside WinRT.Component.dll are + /// included. The .NET runtime fails with when it tries + /// to enumerate a target assembly that doesn't exist, which would in turn fail the static + /// constructor of any type that depends on the type map (e.g. WindowsRuntimeComWrappers). + /// + private static void WriteTypeMapAssemblyTargets(ProjectionGeneratorArgs args, ProjectionGeneratorProcessingState processingState) + { + // Build the list of target assembly simple names. Order is deterministic for stable + // codegen output. The runtime treats the order as irrelevant for discovery. + System.Collections.Generic.List targetAssemblyNames = + [ + "WinRT.Interop", + "WinRT.Runtime", + "WinRT.Sdk.Projection", + ]; + + if (args.WindowsUIXamlProjection) + { + targetAssemblyNames.Add("WinRT.Sdk.Xaml.Projection"); + } + + // 'WinRT.Projection.dll' is generated only when there are non-Windows-SDK, non-component + // Windows Runtime reference assemblies in scope (i.e. third-party projection references). + // Only include it in the union when that condition holds; otherwise the runtime would + // fail with FileNotFoundException trying to enumerate a non-existent assembly. + if (HasMergedProjectionReferences(args)) + { + targetAssemblyNames.Add("WinRT.Projection"); + } + + // Per-component targets + targetAssemblyNames.AddRange(processingState.ComponentAssemblyNames); + + StringBuilder sb = new(); + _ = sb.AppendLine("// "); + _ = sb.AppendLine("#pragma warning disable"); + _ = sb.AppendLine(); + + foreach (string targetTypeMapGroup in (string[]) + [ + "global::WindowsRuntime.InteropServices.WindowsRuntimeComWrappersTypeMapGroup", + "global::WindowsRuntime.InteropServices.WindowsRuntimeMetadataTypeMapGroup", + "global::WindowsRuntime.InteropServices.DynamicInterfaceCastableImplementationTypeMapGroup", + ]) + { + foreach (string targetAssembly in targetAssemblyNames) + { + _ = sb.AppendLine($"[assembly: global::System.Runtime.InteropServices.TypeMapAssemblyTarget<{targetTypeMapGroup}>(\"{targetAssembly}\")]"); + } + + _ = sb.AppendLine(); + } + + File.WriteAllText(Path.Combine(processingState.SourcesFolder, "TypeMapAssemblyTargets.g.cs"), sb.ToString()); + } + + /// + /// Returns true if there is at least one non-Windows-SDK, non-component Windows Runtime + /// reference assembly in scope - i.e. the same condition the merged projection target + /// uses to determine whether WinRT.Projection.dll needs to be generated. + /// + private static bool HasMergedProjectionReferences(ProjectionGeneratorArgs args) + { + string[] resolverPaths = [.. System.Linq.Enumerable.Where(args.ReferenceAssemblyPaths, + p => !p.EndsWith(".winmd", StringComparison.OrdinalIgnoreCase))]; + + PathAssemblyResolver resolver = new(resolverPaths); + + foreach (string refPath in args.ReferenceAssemblyPaths) + { + if (refPath.EndsWith(".winmd", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + ModuleDefinition refModule; + + try + { + refModule = ModuleDefinition.FromFile(refPath, resolver.ReaderParameters); + } + catch + { + continue; + } + + // Skip non-projection references (only WinRT projection .dll-s are interesting) + if (!IsReferenceAssembly(refModule) || !IsWindowsRuntimeReferenceAssembly(refModule)) + { + continue; + } + + // Windows SDK projections (Microsoft.Windows.SDK.NET / Microsoft.Windows.UI.Xaml) + // go into 'WinRT.Sdk.Projection.dll' / 'WinRT.Sdk.Xaml.Projection.dll', not 'WinRT.Projection.dll'. + if (IsWindowsSdkAssembly(refModule)) + { + continue; + } + + // Component reference projections also don't go into 'WinRT.Projection.dll' (they + // are listed separately in the per-component target group above). + if (IsComponentAssembly(refModule)) + { + continue; + } + + // A third-party Windows Runtime reference projection - 'WinRT.Projection.dll' will + // be generated by '_RunCsWinRTMergedProjectionGenerator' for these. + return true; + } + + return false; + } + + /// + /// Emits ABI.WinRT.Component.ManagedExports.GetActivationFactory, a merged + /// dispatcher that walks each per-component ABI.{ComponentName}.ManagedExports.GetActivationFactory + /// (already emitted by cswinrt.exe in -component mode) and returns the first + /// non-null factory. + /// + /// + /// This type is what WinRT.Host.Shim.GetActivationFactory reflects on when the + /// loaded target assembly is WinRT.Component.dll (the moduleName the shim + /// computes from the dll filename is "WinRT.Component", so it looks up + /// ABI.WinRT.Component.ManagedExports). + /// + private static void WriteMergedManagedExports(ProjectionGeneratorProcessingState processingState) + { + StringBuilder dispatchCases = new(); + + foreach (string componentAssemblyName in processingState.ComponentAssemblyNames) + { + _ = dispatchCases.AppendLine($$""" + + factory = global::ABI.{{componentAssemblyName}}.ManagedExports.GetActivationFactory(activatableClassId); + if (factory is not null) + { + return factory; + } + """); + } + + string source = $$""" + // + #pragma warning disable + + namespace ABI.WinRT.Component; + + /// + /// Merged managed activation entry point for WinRT.Component.dll. Reflectively + /// invoked by WinRT.Host.Shim.GetActivationFactory when the manifest-free + /// activation flow loads WinRT.Component.dll; iterates per-component + /// ABI.{ComponentName}.ManagedExports.GetActivationFactory entries. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public static unsafe class ManagedExports + { + /// + /// Retrieves the activation factory for the requested runtime class by + /// dispatching to each input component's ManagedExports in turn. + /// + /// The runtime class identifier. + /// A pointer to the factory, or null if no component handles the class. + public static void* GetActivationFactory(global::System.ReadOnlySpan activatableClassId) + { + void* factory; + {{dispatchCases.ToString().TrimEnd()}} + + return null; + } + + /// + /// Reflection-friendly overload used by managed runtime hosts (e.g. WinRT.Host.Shim). + /// + public static nint GetActivationFactory(string activatableClassId) + { + return (nint)GetActivationFactory(global::System.MemoryExtensions.AsSpan(activatableClassId)); + } + } + """; + + File.WriteAllText(Path.Combine(processingState.SourcesFolder, "MergedManagedExports.g.cs"), source); + } +} diff --git a/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.cs b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.cs index 2119605ee2..7ec3baff8e 100644 --- a/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.cs +++ b/src/WinRT.Projection.Generator/Generation/ProjectionGenerator.cs @@ -80,6 +80,21 @@ public static void Run([Argument] string responseFilePath, CancellationToken tok args.Token.ThrowIfCancellationRequested(); + // In component mode (i.e. producing 'WinRT.Component.dll'), emit the supporting + // source files alongside cswinrt.exe's output so the merged dll plays the entry-assembly + // and merged-activation roles (TypeMap union, SetEntryAssembly module init, merged + // 'ABI.WinRT.Component.ManagedExports.GetActivationFactory', and AOT native export). + try + { + EmitWinRTComponentSources(args, processingState); + } + catch (Exception e) when (!e.IsWellKnown) + { + throw new UnhandledProjectionGeneratorException("winrt-component-sources", e); + } + + args.Token.ThrowIfCancellationRequested(); + // Invoke Roslyn to compile the generated sources into 'WinRT.Projection.dll' try { diff --git a/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.Parsing.cs b/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.Parsing.cs index 7f90150ca2..35c928d1c3 100644 --- a/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.Parsing.cs +++ b/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.Parsing.cs @@ -82,6 +82,7 @@ public static ProjectionGeneratorArgs ParseFromResponseFile(string path, Cancell AssemblyName = GetOptionalStringArgument(argsMap, nameof(AssemblyName), "WinRT.Projection"), WindowsSdkOnly = GetOptionalBoolArgument(argsMap, nameof(WindowsSdkOnly)), WindowsUIXamlProjection = GetOptionalBoolArgument(argsMap, nameof(WindowsUIXamlProjection)), + EmitEntryPointInitializer = GetOptionalBoolArgument(argsMap, nameof(EmitEntryPointInitializer)), Token = token }; } diff --git a/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.cs b/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.cs index 2b8e4e6f2b..2a1e814b7b 100644 --- a/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.cs +++ b/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorArgs.cs @@ -54,6 +54,16 @@ internal sealed partial class ProjectionGeneratorArgs [CommandLineArgumentName("--windows-ui-xaml-projection")] public bool WindowsUIXamlProjection { get; init; } + /// + /// Gets whether to emit the ProjectionTypesInitializer module initializer + /// (calls Assembly.SetEntryAssembly) into WinRT.Component.dll. Only + /// needed under JIT to enable [TypeMapAssemblyTarget] discovery at the merged + /// component dll; AOT uses a separate exe-project workaround. Will become + /// unnecessary once the TypeMappingEntryAssembly MSBuild property is available. + /// + [CommandLineArgumentName("--emit-entry-point-initializer")] + public bool EmitEntryPointInitializer { get; init; } + /// Gets the token for the operation. public required CancellationToken Token { get; init; } } \ No newline at end of file diff --git a/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorProcessingState.cs b/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorProcessingState.cs index 6d570ac04f..3422657346 100644 --- a/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorProcessingState.cs +++ b/src/WinRT.Projection.Generator/Generation/ProjectionGeneratorProcessingState.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; + namespace WindowsRuntime.ProjectionGenerator.Generation; /// @@ -10,7 +12,13 @@ namespace WindowsRuntime.ProjectionGenerator.Generation; /// The path to the generated response file for CsWinRT. /// The reference assembly paths excluding projection assemblies. /// Whether any types were found to project. -internal sealed class ProjectionGeneratorProcessingState(string sourcesFolder, string rspFilePath, string[] referencesWithoutProjections, bool hasTypesToProject = true) +/// Sorted simple names of all input [WindowsRuntimeComponentAssembly] references (component-mode only). +internal sealed class ProjectionGeneratorProcessingState( + string sourcesFolder, + string rspFilePath, + string[] referencesWithoutProjections, + bool hasTypesToProject = true, + IReadOnlyList? componentAssemblyNames = null) { /// /// Gets the path to the folder where sources will be generated. @@ -32,4 +40,11 @@ internal sealed class ProjectionGeneratorProcessingState(string sourcesFolder, s /// and emit phases should be skipped (no DLL will be produced). /// public bool HasTypesToProject { get; } = hasTypesToProject; + + /// + /// Gets the simple names of all input assemblies marked with + /// [WindowsRuntimeComponentAssembly]. Empty unless this is a component-mode run + /// (i.e. producing WinRT.Component.dll). + /// + public IReadOnlyList ComponentAssemblyNames { get; } = componentAssemblyNames ?? []; } diff --git a/src/build.cmd b/src/build.cmd index 35d7b9ad16..d61ac8ccd8 100644 --- a/src/build.cmd +++ b/src/build.cmd @@ -228,6 +228,25 @@ if ErrorLevel 1 ( rem exit /b !ErrorLevel! ) +if /I "%cswinrt_platform%"=="x64" ( + call :exec %this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\AuthoringConsumptionTest2\bin\AuthoringConsumptionTest2.exe --gtest_output=xml:%this_dir%authoringtest2_%cswinrt_version_string%.xml + if ErrorLevel 1 ( + echo. + rem Not skipping due to known issue. + rem echo ERROR: Multi-component authoring test failed, skipping NuGet pack + rem exit /b !ErrorLevel! + ) +) +if /I "%cswinrt_platform%"=="x86" ( + call :exec %this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\AuthoringConsumptionTest2\bin\AuthoringConsumptionTest2.exe --gtest_output=xml:%this_dir%authoringtest2_%cswinrt_version_string%.xml + if ErrorLevel 1 ( + echo. + rem Not skipping due to known issue. + rem echo ERROR: Multi-component authoring test failed, skipping NuGet pack + rem exit /b !ErrorLevel! + ) +) + :functionaltest rem Run functional tests if "%run_functional_tests%" EQU "true" ( @@ -262,11 +281,12 @@ set winrt_shim=%this_dir%Authoring\WinRT.Host.Shim\bin\%cswinrt_configuration%\n set cswinrtinteropgen_%cswinrt_platform%=%this_dir%WinRT.Interop.Generator\bin\%cswinrt_configuration%\net10.0\win-%cswinrt_platform%\publish\cswinrtinteropgen.exe set cswinrtimplgen_%cswinrt_platform%=%this_dir%WinRT.Impl.Generator\bin\%cswinrt_configuration%\net10.0\win-%cswinrt_platform%\publish\cswinrtimplgen.exe set cswinrtprojectiongen_%cswinrt_platform%=%this_dir%WinRT.Projection.Generator\bin\%cswinrt_configuration%\net10.0\win-%cswinrt_platform%\publish\cswinrtprojectiongen.exe +set cswinrtwinmdgen_%cswinrt_platform%=%this_dir%WinRT.WinMD.Generator\bin\%cswinrt_configuration%\net10.0\win-%cswinrt_platform%\publish\cswinrtwinmdgen.exe set run_cswinrt_generator_task=%this_dir%WinRT.Generator.Tasks\bin\%cswinrt_configuration%\netstandard2.0\WinRT.Generator.Tasks.dll rem Now call pack echo Creating nuget package -call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties cswinrt_exe=%cswinrt_exe%;interop_winmd=%interop_winmd%;net10_runtime=%net10_runtime%;net10_runtime_xml=%net10_runtime_xml%;source_generator=%source_generator%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_host_resource_x86=%winrt_host_resource_x86%;winrt_host_resource_x64=%winrt_host_resource_x64%;winrt_host_resource_arm=%winrt_host_resource_arm%;winrt_host_resource_arm64=%winrt_host_resource_arm64%;winrt_shim=%winrt_shim%;cswinrtinteropgen_x64=%cswinrtinteropgen_x64%;cswinrtinteropgen_arm64=%cswinrtinteropgen_arm64%;cswinrtimplgen_x64=%cswinrtimplgen_x64%;cswinrtimplgen_arm64=%cswinrtimplgen_arm64%;cswinrtprojectiongen_x64=%cswinrtprojectiongen_x64%;cswinrtprojectiongen_arm64=%cswinrtprojectiongen_arm64%;run_cswinrt_generator_task=%run_cswinrt_generator_task%; -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis +call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties cswinrt_exe=%cswinrt_exe%;interop_winmd=%interop_winmd%;net10_runtime=%net10_runtime%;net10_runtime_xml=%net10_runtime_xml%;source_generator=%source_generator%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_host_resource_x86=%winrt_host_resource_x86%;winrt_host_resource_x64=%winrt_host_resource_x64%;winrt_host_resource_arm=%winrt_host_resource_arm%;winrt_host_resource_arm64=%winrt_host_resource_arm64%;winrt_shim=%winrt_shim%;cswinrtinteropgen_x64=%cswinrtinteropgen_x64%;cswinrtinteropgen_arm64=%cswinrtinteropgen_arm64%;cswinrtimplgen_x64=%cswinrtimplgen_x64%;cswinrtimplgen_arm64=%cswinrtimplgen_arm64%;cswinrtprojectiongen_x64=%cswinrtprojectiongen_x64%;cswinrtprojectiongen_arm64=%cswinrtprojectiongen_arm64%;cswinrtwinmdgen_x64=%cswinrtwinmdgen_x64%;cswinrtwinmdgen_arm64=%cswinrtwinmdgen_arm64%;run_cswinrt_generator_task=%run_cswinrt_generator_task%; -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis goto :eof :exec diff --git a/src/cswinrt.slnx b/src/cswinrt.slnx index 8c2dc47d6d..81a6f2d1c1 100644 --- a/src/cswinrt.slnx +++ b/src/cswinrt.slnx @@ -79,9 +79,8 @@ + - - @@ -175,6 +174,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -183,6 +203,22 @@ + + + + + + + + + + + + + + + +