microsoft / WindowsAppSDK

The Windows App SDK empowers all Windows desktop apps with modern Windows UI, APIs, and platform features, including back-compat support, shipped via NuGet.
https://docs.microsoft.com/windows/apps/windows-app-sdk/
MIT License
3.72k stars 308 forks source link

AnyCPU support #893

Open dotMorten opened 3 years ago

dotMorten commented 3 years ago

Describe the bug I'm trying to use MRTCore in a WPF application. I reference ProjectReunion package, and compile and I'm presented with this error:

1>C:\Users\mn.nuget\packages\microsoft.projectreunion.winui\0.8.0-preview\buildTransitive\Microsoft.WinUI.targets(15,5): error : This version of WinUI does not support AnyCPU. Either set Platform or PlatformTarget to one of the following: x86, x64, or arm64.

I'm in no way interested in using WinUI here, but the current nuget dependency forces me to get WinUI included, including all the types etc. IMHO this is backwards, and instead Microsoft.ProjectReunion.WinUI you be referencing Microsoft.ProjectReunion. The error above is extremely confusing as I am not interested in or explicitly referencing the WinUI extension.

Sure I can avoid using AnyCPU, but that's not so much the point (and I just followed the sample app config which has AnyCPU as well: https://github.com/microsoft/Project-Reunion-Samples/blob/main/MrtCore/winforms_unpackaged_app/winforms_unpackaged_app.csproj). Also why can't you do AnyCPU? Other .NET3+ apps can handle deploying multiple architecture runtimes in an AnyCPU app.

Steps to reproduce the bug Create a WPF Application and add reunion:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>

    <TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
    <UseWPF>true</UseWPF>
    <Platforms>AnyCPU;x86;x64</Platforms>
    <RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.ProjectReunion" Version="0.8.0-preview" />
  </ItemGroup>
</Project>

Compile for AnyCPU

I get the app packages the same runtime dependencies, but it creates confusing unrelated errors, and exposes a set of APIs that I never intended to reference. I'm only interested in MRTCore which is packaged with the "base" reunion package, yet I'm getting DWrite, InteractiveExperiences and WinUI throw in as well.

Expected behavior WinUI, DWrite and InteractiveExperiences package is not included. Intellisense doesn't autocomplete those APIs either.

Version Info

NuGet package version: 0.8.0-preview

Windows 10 version Saw the problem?
Insider Build (xxxxx)
May 2020 Update (19041)
November 2019 Update (18363)
May 2019 Update (18362)
October 2018 Update (17763)

Additional context

riverar commented 3 years ago

NuGet dependencies are including unrelated packages

You referenced the Project Reunion package which includes related packages:

<dependencies>
  <dependency id="Microsoft.ProjectReunion.Foundation" version="0.8.0-preview" />
  <dependency id="Microsoft.ProjectReunion.DWrite" version="0.8.0-preview" />
  <dependency id="Microsoft.ProjectReunion.WinUI" version="0.8.0-preview" />
  <dependency id="Microsoft.ProjectReunion.InteractiveExperiences" version="0.8.0-preview" />
</dependencies>

WinUI target throws error : This version of WinUI does not support AnyCPU. Either set Platform or PlatformTarget to one of the following: x86, x64, or arm64

Dynamic dependencies and other components do not support AnyCPU. Not sure why the sample ships with that though (bug?).

The error above is extremely confusing as I am not interested in or explicitly referencing the WinUI extension.

Well, you added that dependency (see above). But if you add the Foundation package by itself, the WinUI targets do still get evaluated, which I agree is very confusing.

dotMorten commented 3 years ago

Dynamic dependencies and other components do not support AnyCPU. Not sure why the sample ships with that though (bug?).

.NET will deploy all the architecture runtimes when compiling for AnyCPU and dynamically load the correct one, so yes that should work just fine. That's why when you compile for AnyCPU you get a \runtimes\win10-[arch]\ for each supported architecture, but when compiling for a specific runtime identifier, that folder gets dropped and the dependencies gets placed in the root folder instead. Why should reunion work any different?

riverar commented 3 years ago

.NET will deploy all the architecture runtimes when compiling for AnyCPU and dynamically load the correct one, so yes that should work just fine. That's why when you compile for AnyCPU you get a \runtimes\win10-[arch]\ for each supported architecture, but when compiling for a specific runtime identifier, that folder gets dropped and the dependencies gets placed in the root folder instead. Why should reunion work any different?

Not sure what behavior you're referring to but compiling the WPF app here as AnyCPU will result in a single ILONLY image.

dotMorten commented 3 years ago

When you build for any cpu in a .NET 3.1+ project, if a nuget dependency includes runtimes for several different architectures (and/or platforms) they are deployed to a subfolder for each, and .NET automatically knows where to find them based on current platform and architecture at runtime. For instance your output folder might have this:

image

Sure when you go to publish, you pick a specific architecture, but a standard new .NET Core project defaults to AnyCPU when you hit F5 after creating it - so this is just as much about simplifying the developer experience.

andrewleader commented 3 years ago

Thanks for pointing this out Morten, @AdamBraden is investigating the AnyCPU topic.

AdamBraden commented 3 years ago

Yes we need to fix the nuget packages to correctly place the native dependencies of our components.

Build/F5 of AnyCPU results in a lot of binaries being deployed, but apparently that's the way it is for .NET.

asklar commented 3 years ago

Duplicate of #922 ?

mattleibow commented 2 years ago

FYI: When using an Any CPU app head, then I still have to add this.

<Target Name="BinPlaceBootstrapDll" />
DrusTheAxe commented 2 years ago

Dupe of #1217 ?

dotMorten commented 2 years ago

@DrusTheAxe No this is different. The other one is about being able to build architecture agnostic class libs and is a regression that holds up control libraries like ours and the toolkit. This one is about following the pattern of the other .net libs where when building for anycpu, all runtimes are deployed and at runtime the right native libs are loaded and will run. That creates a much better and simpler first run experience for .net developers (typically picking platform and architecture doesn't come into play until publish time).

mattleibow commented 1 month ago

Just adding some of the things that I found and am working around in maui. The Windows App SDK does already support AnyCPU since this is the default mode for .NET "Core", however there are targets that do not allow it and artificially restrict it.

There is a target WindowsAppSDKSelfContainedVerifyConfiguration that just throws an error. I work around this in my targets by replacing it with a dummy target:

<Target Name="WindowsAppSDKSelfContainedVerifyConfiguration">
</Target>

There is a GetExtractMicrosoftWindowsAppSDKMsixFilesInputs target that is supposed to extract things, but this fails if the Platform is not set. I work around this by injecting a target that runs right after it to replace the values of NativePlatform:

<Target Name="_MAUIAfter_GetExtractMicrosoftWindowsAppSDKMsixFilesInputs"
        AfterTargets="GetExtractMicrosoftWindowsAppSDKMsixFilesInputs"
        Condition="'$(NativePlatform)' == 'AnyCPU'">
  <PropertyGroup>
    <NativePlatform>Invalid</NativePlatform>
    <NativePlatform Condition="'$(Platform)' == 'x86'">x86</NativePlatform>
    <NativePlatform Condition="'$(Platform)' == 'Win32'">x86</NativePlatform>
    <NativePlatform Condition="'$(Platform)' == 'x64'">x64</NativePlatform>
    <NativePlatform Condition="'$(Platform)' == 'arm'">arm</NativePlatform>
    <NativePlatform Condition="'$(Platform)' == 'arm64'">arm64</NativePlatform>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Platform)' == 'AnyCPU' or '$(Platform)' == 'Any CPU'">
    <NativePlatform>neutral</NativePlatform>
    <NativePlatform Condition="'$(RuntimeIdentifier)' == 'win10-x86' or '$(RuntimeIdentifier)' == 'win-x86'">x86</NativePlatform>
    <NativePlatform Condition="'$(RuntimeIdentifier)' == 'win10-x64' or '$(RuntimeIdentifier)' == 'win-x64'">x64</NativePlatform>
    <NativePlatform Condition="'$(RuntimeIdentifier)' == 'win10-arm' or '$(RuntimeIdentifier)' == 'win-arm'">arm</NativePlatform>
    <NativePlatform Condition="'$(RuntimeIdentifier)' == 'win10-arm64' or '$(RuntimeIdentifier)' == 'win-arm64'">arm64</NativePlatform>
  </PropertyGroup>
  <ItemGroup>
    <MicrosoftWindowsAppSDKMsix Include="$([MSBuild]::NormalizeDirectory('$(MicrosoftWindowsAppSDKPackageDir)','tools\Msix\win10-$(NativePlatform)'))Microsoft.WindowsAppRuntime.?.?.Msix"/>
    <MicrosoftWindowsAppSDKMsix Include="$([MSBuild]::NormalizeDirectory('$(MicrosoftWindowsAppSDKPackageDir)','tools\Msix\win10-$(NativePlatform)'))Microsoft.WindowsAppRuntime.?.?-*.Msix"/>
  </ItemGroup>
</Target>

There is also a _GetProjectArchitecture that almost works with Any CPU, except that since .NET added/removed the support for the full RID graph, the conditions are no longer correct. I have also created a replacement target that will fix the values:

<Target Name="_GetProjectArchitecture" Returns="@(ProjectArchitecture)">
  <PropertyGroup>
    <_ProjectArchitectureOutput>Invalid</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(Platform)' == 'x86'">x86</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(Platform)' == 'Win32'">x86</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(Platform)' == 'x64'">x64</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(Platform)' == 'arm'">arm</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(Platform)' == 'arm64'">arm64</_ProjectArchitectureOutput>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Platform)' == 'AnyCPU' or '$(Platform)' == 'Any CPU'">
    <_ProjectArchitectureOutput>neutral</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(RuntimeIdentifier)' == 'win10-x86' or '$(RuntimeIdentifier)' == 'win-x86'">x86</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(RuntimeIdentifier)' == 'win10-x64' or '$(RuntimeIdentifier)' == 'win-x64'">x64</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(RuntimeIdentifier)' == 'win10-arm' or '$(RuntimeIdentifier)' == 'win-arm'">arm</_ProjectArchitectureOutput>
    <_ProjectArchitectureOutput Condition="'$(RuntimeIdentifier)' == 'win10-arm64' or '$(RuntimeIdentifier)' == 'win-arm64'">arm64</_ProjectArchitectureOutput>
  </PropertyGroup>
  <Error Condition="'$(UseAppHost)' == 'true' and '$(_ProjectArchitectureOutput)' == 'neutral' and '$(AllowNeutralPackageWithAppHost)' != 'true'"
    Text="Packaged .NET applications with an app host exe cannot be ProcessorArchitecture neutral. Please specify a RuntimeIdentifier or a Platform other than AnyCPU." />
  <ItemGroup>
    <ProjectArchitecture Include="$(_ProjectArchitectureOutput)" />
  </ItemGroup>
</Target>
mattleibow commented 1 month ago

This is maybe the same/similar issue to https://github.com/microsoft/WindowsAppSDK/issues/2684

mattleibow commented 1 month ago

@Scottj1s FYI

Scottj1s commented 1 month ago

@mattleibow thanks for reporting. The _GetProjectArchitecture target is fixed in WinAppSDK 1.6, available very soon. I'm really not sure why the WindowsAppSDKSelfContainedVerifyConfiguration target was added, given the more fine-grained check in _GetProjectArchitecture (which prevents packaging for Any CPU, not merely building it). I'll see if there's any resistance to just removing it. Your mods to GetExtractMicrosoftWindowsAppSDKMsixFilesInputs are appreciated - will include these.

lhak commented 1 week ago

At least in the 1.6-experimental1 release, the _GetProjectArchitecture target still does not support the new runtime identifiers.

Scottj1s commented 2 days ago

At least in the 1.6-experimental1 release, the _GetProjectArchitecture target still does not support the new runtime identifiers.

1.6 Experimental 2 includes the changes suggested by @mattleibow