-
Notifications
You must be signed in to change notification settings - Fork 2k
Add ApplicationArtifact support for Windows App SDK #35973
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: net11.0
Are you sure you want to change the base?
Changes from 4 commits
e8d9037
79e1239
45c2182
3caafdb
860afcb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # ApplicationArtifact metadata in .NET MAUI | ||
|
|
||
| `@(ApplicationArtifact)` is the shared public item group for final application artifacts. Platform SDKs own creating these items and their artifact identity, path, format, and platform-specific metadata: | ||
|
|
||
| - .NET for Android creates APK and AAB items. | ||
| - .NET for iOS, Mac Catalyst, tvOS, and macOS creates `.app`, `.ipa`, `.pkg`, and `.xcarchive` items. | ||
| - Other platforms should populate the same item group from their own build or publish pipeline. | ||
|
|
||
| MAUI does not rediscover platform package files and does not create a parallel MAUI-specific artifact item group. Instead, it defines default MAUI metadata for the shared `ApplicationArtifact` item type. Platform SDKs create the items, and the defaults supplement their platform-specific metadata. | ||
|
|
||
| MAUI supplies these metadata values when the matching project properties are set: | ||
|
|
||
| - `ApplicationId` | ||
| - `ApplicationIdGuid` | ||
| - `ApplicationName`, mapped from `ApplicationTitle` | ||
| - `ApplicationTitle` | ||
| - `ApplicationDisplayVersion` | ||
| - `ApplicationVersion` | ||
|
|
||
| The defaults are declared with an MSBuild `ItemDefinitionGroup`, so explicit metadata from a platform SDK takes precedence. The defaults also apply when a platform recreates an item, as Android does when changing artifact identities to published paths. | ||
|
|
||
| `GetApplicationArtifacts` and `Publish` remain platform-owned result paths. MAUI only supplies shared metadata and does not change which artifacts those targets produce or return. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,6 +115,130 @@ public void BuildsWithSpecialCharacters(string id, string projectName, string ex | |
| $"Project {Path.GetFileName(projectFile)} failed to build. Check test output/attachments for errors."); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ApplicationArtifactsAreEnrichedWithMauiMetadata() | ||
| { | ||
| SetTestIdentifier(nameof(ApplicationArtifactsAreEnrichedWithMauiMetadata)); | ||
| var projectDir = TestDirectory; | ||
| var projectFile = Path.Combine(projectDir, $"{Path.GetFileName(projectDir)}.csproj"); | ||
| var seededArtifactsFile = Path.Combine(projectDir, "seeded-application-artifacts.txt"); | ||
| var publishedArtifactsFile = Path.Combine(projectDir, "published-application-artifacts.txt"); | ||
| var mauiSdkAfterTargets = Path.Combine( | ||
| TestEnvironment.GetMauiDirectory(), | ||
| "src", | ||
| "Workload", | ||
| "Microsoft.Maui.Sdk", | ||
| "Sdk", | ||
| "Microsoft.Maui.Sdk.After.targets"); | ||
|
|
||
| Assert.True(DotnetInternal.New("maui", projectDir, DotNetCurrent, output: _output), | ||
| $"Unable to create template maui. Check test output for errors."); | ||
|
|
||
| var buildProps = BuildProps; | ||
| buildProps.Add($"MauiSdkAfterTargets=\"{mauiSdkAfterTargets}\""); | ||
|
|
||
| FileUtilities.ReplaceInFile(projectFile, | ||
| "</Project>", | ||
| """ | ||
| <PropertyGroup> | ||
| <ApplicationTitle>My Artifact App</ApplicationTitle> | ||
| <ApplicationId>com.example.artifacts</ApplicationId> | ||
| <ApplicationIdGuid>11111111-2222-3333-4444-555555555555</ApplicationIdGuid> | ||
| <ApplicationDisplayVersion>2.3</ApplicationDisplayVersion> | ||
| <ApplicationVersion>42</ApplicationVersion> | ||
| </PropertyGroup> | ||
| <Import Project="$(MauiSdkAfterTargets)" /> | ||
| <Target Name="SeedApplicationArtifacts"> | ||
| <ItemGroup> | ||
| <ApplicationArtifact Include="$(MSBuildProjectDirectory)/artifacts/platform/android/MyArtifactApp-Signed.apk"> | ||
| <PackageFormat>apk</PackageFormat> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The explicit |
||
| <Signed>true</Signed> | ||
| <PackageId>com.example.platform</PackageId> | ||
| <Abi>arm64-v8a</Abi> | ||
| </ApplicationArtifact> | ||
| <ApplicationArtifact Include="$(MSBuildProjectDirectory)/artifacts/platform/android/MyArtifactApp-Signed.aab"> | ||
| <PackageFormat>aab</PackageFormat> | ||
| <Signed>true</Signed> | ||
| <PackageId>com.example.platform</PackageId> | ||
| </ApplicationArtifact> | ||
| </ItemGroup> | ||
| </Target> | ||
| <Target Name="WriteSeededApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts"> | ||
| <WriteLinesToFile | ||
| File="$(MSBuildProjectDirectory)/seeded-application-artifacts.txt" | ||
| Lines="@(ApplicationArtifact->'%(Filename)%(Extension)|%(PackageFormat)|%(ApplicationTitle)|%(ApplicationName)|%(ApplicationId)|%(ApplicationIdGuid)|%(ApplicationDisplayVersion)|%(ApplicationVersion)|%(Signed)|%(PackageId)|%(Abi)')" | ||
| Overwrite="true" /> | ||
| </Target> | ||
| <Target Name="RecreatePublishedApplicationArtifacts" DependsOnTargets="SeedApplicationArtifacts"> | ||
| <ItemGroup> | ||
| <_ApplicationArtifactForPublish Include="@(ApplicationArtifact)" /> | ||
| <_ApplicationArtifactPublishCopy | ||
| Include="@(_ApplicationArtifactForPublish->'$(MSBuildProjectDirectory)/artifacts/publish/%(Filename)%(Extension)')"> | ||
| <PackageFormat>%(_ApplicationArtifactForPublish.PackageFormat)</PackageFormat> | ||
| <Signed>%(_ApplicationArtifactForPublish.Signed)</Signed> | ||
| <PackageId>%(_ApplicationArtifactForPublish.PackageId)</PackageId> | ||
| <Abi Condition="'%(_ApplicationArtifactForPublish.Abi)' != ''">%(_ApplicationArtifactForPublish.Abi)</Abi> | ||
| </_ApplicationArtifactPublishCopy> | ||
| <ApplicationArtifact Remove="@(ApplicationArtifact)" /> | ||
| <ApplicationArtifact Include="@(_ApplicationArtifactPublishCopy)" /> | ||
| </ItemGroup> | ||
| </Target> | ||
| <Target Name="WritePublishedApplicationArtifactsMetadata" DependsOnTargets="RecreatePublishedApplicationArtifacts"> | ||
| <WriteLinesToFile | ||
| File="$(MSBuildProjectDirectory)/published-application-artifacts.txt" | ||
| Lines="@(ApplicationArtifact->'%(Filename)%(Extension)|%(PackageFormat)|%(ApplicationTitle)|%(ApplicationName)|%(ApplicationId)|%(ApplicationIdGuid)|%(ApplicationDisplayVersion)|%(ApplicationVersion)|%(Signed)|%(PackageId)|%(Abi)')" | ||
| Overwrite="true" /> | ||
| </Target> | ||
| </Project> | ||
| """); | ||
|
|
||
| Assert.True(DotnetInternal.Build(projectFile, "Debug", target: "WriteSeededApplicationArtifactsMetadata", framework: $"{DotNetCurrent}-android", properties: buildProps, output: _output), | ||
| $"Project {Path.GetFileName(projectFile)} failed to write seeded ApplicationArtifact metadata. Check test output/attachments for errors."); | ||
| AssertApplicationArtifactMetadata(File.ReadAllLines(seededArtifactsFile)); | ||
|
|
||
| Assert.True(DotnetInternal.Build(projectFile, "Debug", target: "WritePublishedApplicationArtifactsMetadata", framework: $"{DotNetCurrent}-android", properties: buildProps, output: _output), | ||
| $"Project {Path.GetFileName(projectFile)} failed to write published ApplicationArtifact metadata. Check test output/attachments for errors."); | ||
| AssertApplicationArtifactMetadata(File.ReadAllLines(publishedArtifactsFile)); | ||
|
|
||
| static void AssertApplicationArtifactMetadata(string[] artifactLines) | ||
| { | ||
| Assert.Equal(2, artifactLines.Length); | ||
|
|
||
| AssertArtifact( | ||
| artifactLines.Single(line => line.StartsWith("MyArtifactApp-Signed.apk|apk|", StringComparison.Ordinal)), | ||
| "MyArtifactApp-Signed.apk", | ||
| "apk", | ||
| signed: "true", | ||
| packageId: "com.example.platform", | ||
| abi: "arm64-v8a"); | ||
|
|
||
| AssertArtifact( | ||
| artifactLines.Single(line => line.StartsWith("MyArtifactApp-Signed.aab|aab|", StringComparison.Ordinal)), | ||
| "MyArtifactApp-Signed.aab", | ||
| "aab", | ||
| signed: "true", | ||
| packageId: "com.example.platform", | ||
| abi: ""); | ||
| } | ||
|
|
||
| static void AssertArtifact(string artifactLine, string fileName, string packageFormat, string signed, string packageId, string abi) | ||
| { | ||
| var metadata = artifactLine.Split('|'); | ||
|
|
||
| Assert.Equal(fileName, metadata[0]); | ||
| Assert.Equal(packageFormat, metadata[1]); | ||
| Assert.Equal("My Artifact App", metadata[2]); | ||
| Assert.Equal("My Artifact App", metadata[3]); | ||
| Assert.Equal("com.example.artifacts", metadata[4]); | ||
| Assert.Equal("11111111-2222-3333-4444-555555555555", metadata[5]); | ||
| Assert.Equal("2.3", metadata[6]); | ||
| Assert.Equal("42", metadata[7]); | ||
| Assert.Equal(signed, metadata[8]); | ||
| Assert.Equal(packageId, metadata[9]); | ||
| Assert.Equal(abi, metadata[10]); | ||
| } | ||
| } | ||
|
|
||
| [Theory] | ||
| // Parameters: short name, target framework, build config, use pack target, additionalDotNetBuildParams | ||
| // [InlineData("maui", DotNetPrevious, "Debug", false, "")] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,17 @@ | |
| <ProjectCapability Include="MauiEssentials" Condition=" '$(UseMaui)' == 'true' or '$(UseMauiEssentials)' == 'true' " /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemDefinitionGroup> | ||
| <ApplicationArtifact> | ||
| <ApplicationId Condition="'$(ApplicationId)' != ''">$(ApplicationId)</ApplicationId> | ||
| <ApplicationIdGuid Condition="'$(ApplicationIdGuid)' != ''">$(ApplicationIdGuid)</ApplicationIdGuid> | ||
| <ApplicationName Condition="'$(ApplicationTitle)' != ''">$(ApplicationTitle)</ApplicationName> | ||
| <ApplicationTitle Condition="'$(ApplicationTitle)' != ''">$(ApplicationTitle)</ApplicationTitle> | ||
| <ApplicationDisplayVersion Condition="'$(ApplicationDisplayVersion)' != ''">$(ApplicationDisplayVersion)</ApplicationDisplayVersion> | ||
| <ApplicationVersion Condition="'$(ApplicationVersion)' != ''">$(ApplicationVersion)</ApplicationVersion> | ||
| </ApplicationArtifact> | ||
| </ItemDefinitionGroup> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this actually go in dotnet/android and dotnet/macios? What if you are an Uno or MonoGame project? ... then what about WindowsAppSDK?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think maybe the android/macios stuff can get pushed down into those repos - i'll make PR's for that. For windows I'm thinking maybe another package that could be added to any windowsappsdk app that achieves the same, though I'll check with someone on that team if they might consider just a PR to their repo directly to add this too. |
||
|
|
||
| <!-- SingleProject-specific features --> | ||
| <ItemGroup Condition=" '$(SingleProject)' == 'true' "> | ||
| <ProjectCapability Include="Msix" /> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The relative path
$(MSBuildThisFileDirectory)..\..\..\assumes the test directory is located within the MAUI repositorybin/folder. However, on Azure Pipelines CI,TestDirectoryis located underAGENT_TEMPDIRECTORY(e.g.,.../test-dir/ApplicationArtifactsAreEnrichedWithMauiMetadata/). As a result, traversing up 3 directories points toAGENT_TEMPDIRECTORY/src/Workload/...instead of the repository, causing MSB4019 missing import errors and failing the integration tests.