-
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 3 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,20 @@ | ||
| # 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, MAUI participates through `$(GetApplicationArtifactsDependsOn)` and updates existing `@(ApplicationArtifact)` items with MAUI project metadata after the platform SDK `GetApplicationArtifacts` target has run `Build` and platform-produced items exist. | ||
|
|
||
| The MAUI metadata enrichment target adds these metadata values when the matching project properties are set: | ||
|
|
||
| - `ApplicationId` | ||
| - `ApplicationIdGuid` | ||
| - `ApplicationName`, mapped from `ApplicationTitle` | ||
| - `ApplicationTitle` | ||
| - `ApplicationDisplayVersion` | ||
| - `ApplicationVersion` | ||
|
|
||
| `GetApplicationArtifacts` and `Publish` remain platform-owned result paths. Platform SDK `GetApplicationArtifacts` depends on `Build`, then executes targets appended to `$(GetApplicationArtifactsDependsOn)` before returning `@(ApplicationArtifact)` items. `Publish` uses the same post-`Build` extension path before returning items. Replacing `$(GetApplicationArtifactsDependsOn)` must not bypass platform build or platform artifact population; platform SDKs keep `Build` outside that extensibility property. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,6 +115,115 @@ 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 getArtifactsFile = Path.Combine(projectDir, "get-application-artifacts.txt"); | ||
| var publishArtifactsFile = Path.Combine(projectDir, "publish-application-artifacts.txt"); | ||
| var dependsOnFile = Path.Combine(projectDir, "get-application-artifacts-depends-on.txt"); | ||
|
|
||
| Assert.True(DotnetInternal.New("maui", projectDir, DotNetCurrent, output: _output), | ||
| $"Unable to create template maui. Check test output for errors."); | ||
|
|
||
| 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="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" /> | ||
|
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. This import is rooted at the generated test project's MSBuildThisFileDirectory and walks up three directories to find src/Workload. That happens to work for local fallback TestDirectory under the repo's bin/test-dir, but in Azure Pipelines TestEnvironment.GetTestDirectoryRoot() uses AGENT_TEMPDIRECTORY/test-dir, so ......\src does not point at the MAUI checkout and the new Build integration test cannot import Microsoft.Maui.Sdk.After.targets. The current CI failures in the Build integration lanes are consistent with this PR-introduced test path issue. (found by: 5, 4)
Collaborator
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. [major] Build & MSBuild — This import is resolved relative to the generated test project. In CI TestDirectory is under
Collaborator
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. ❌ Error — This import is resolved relative to the generated temp test project via
Collaborator
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. ❌ Error — This import resolves from the generated temp project directory, not the MAUI repo root. In CI
Collaborator
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. ❌ [critical] Build & MSBuild — Import path is broken on CI and on Linux/macOS
Resolve the targets file from C# using
Collaborator
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 generated
Collaborator
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. [critical] Build & MSBuild — This import resolves from
Collaborator
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. [major] Build & MSBuild / integration test correctness — This import resolves relative to the temporary test project directory, not the MAUI repo root. In CI |
||
| <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> | ||
| </ApplicationArtifact> | ||
| <ApplicationArtifact Include="$(MSBuildProjectDirectory)/artifacts/platform/apple/MyArtifactApp.app"> | ||
| <PackageFormat>app</PackageFormat> | ||
| <IsDirectory>true</IsDirectory> | ||
| <PlatformName>iOS</PlatformName> | ||
| <BundleIdentifier>com.example.platform</BundleIdentifier> | ||
| </ApplicationArtifact> | ||
| </ItemGroup> | ||
| </Target> | ||
| <Target Name="WriteGetApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata"> | ||
|
Collaborator
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. [moderate] Regression Prevention — The test target depends on _AddMauiApplicationArtifactMetadata directly, so it validates the private target body rather than the GetApplicationArtifactsDependsOn extension path this PR adds. A regression where the hook is not actually invoked by GetApplicationArtifacts/Publish could still pass because lines 157 and 167 call the target manually. Seed the item, invoke the targets through $(GetApplicationArtifactsDependsOn)/the public artifacts target, and assert the metadata after that path runs.
Collaborator
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.
Collaborator
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.
Collaborator
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.
Collaborator
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. [major] Regression Prevention/Test Coverage — The test directly depends on the private |
||
| <WriteLinesToFile | ||
| File="$(MSBuildProjectDirectory)/get-application-artifacts-depends-on.txt" | ||
| Lines="$(GetApplicationArtifactsDependsOn)" | ||
| Overwrite="true" /> | ||
| <WriteLinesToFile | ||
| File="$(MSBuildProjectDirectory)/get-application-artifacts.txt" | ||
| Lines="@(ApplicationArtifact->'%(Filename)%(Extension)|%(PackageFormat)|%(ApplicationTitle)|%(ApplicationName)|%(ApplicationId)|%(ApplicationIdGuid)|%(ApplicationDisplayVersion)|%(ApplicationVersion)|%(Signed)|%(PackageId)|%(PlatformName)|%(BundleIdentifier)')" | ||
| Overwrite="true" /> | ||
| </Target> | ||
| <Target Name="WritePublishApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata"> | ||
|
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. Both WriteGetApplicationArtifactsMetadata and WritePublishApplicationArtifactsMetadata depend directly on SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata, so they exercise the metadata target in isolation rather than through a real GetApplicationArtifacts/Publish invocation; the actual wiring is only checked indirectly by the string Assert.Contains on the depends-on file (line 178). The Publish variant therefore adds no coverage beyond the Get variant. This is understandable while the platform GetApplicationArtifacts target is unavailable in net11, but consider collapsing the duplicate target (or asserting the real wiring) once the platform producers land. |
||
| <WriteLinesToFile | ||
| File="$(MSBuildProjectDirectory)/publish-application-artifacts.txt" | ||
| Lines="@(ApplicationArtifact->'%(Filename)%(Extension)|%(PackageFormat)|%(ApplicationTitle)|%(ApplicationName)|%(ApplicationId)|%(ApplicationIdGuid)|%(ApplicationDisplayVersion)|%(ApplicationVersion)|%(Signed)|%(PackageId)|%(PlatformName)|%(BundleIdentifier)')" | ||
| Overwrite="true" /> | ||
| </Target> | ||
| </Project> | ||
| """); | ||
|
|
||
| Assert.True(DotnetInternal.Build(projectFile, "Debug", target: "WriteGetApplicationArtifactsMetadata", framework: $"{DotNetCurrent}-android", properties: BuildProps, output: _output), | ||
| $"Project {Path.GetFileName(projectFile)} failed to write GetApplicationArtifacts metadata. Check test output/attachments for errors."); | ||
| Assert.Contains("_AddMauiApplicationArtifactMetadata", File.ReadAllText(dependsOnFile), StringComparison.Ordinal); | ||
| AssertApplicationArtifactMetadata(File.ReadAllLines(getArtifactsFile)); | ||
|
|
||
| Assert.True(DotnetInternal.Build(projectFile, "Debug", target: "WritePublishApplicationArtifactsMetadata", framework: $"{DotNetCurrent}-android", properties: BuildProps, output: _output), | ||
|
Collaborator
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 publish assertion builds a custom target with the same direct dependency on |
||
| $"Project {Path.GetFileName(projectFile)} failed to write Publish metadata. Check test output/attachments for errors."); | ||
| AssertApplicationArtifactMetadata(File.ReadAllLines(publishArtifactsFile)); | ||
|
|
||
| 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", | ||
| platformName: "", | ||
| bundleIdentifier: ""); | ||
|
|
||
| AssertArtifact( | ||
| artifactLines.Single(line => line.StartsWith("MyArtifactApp.app|app|", StringComparison.Ordinal)), | ||
| "MyArtifactApp.app", | ||
| "app", | ||
| signed: "", | ||
| packageId: "", | ||
| platformName: "iOS", | ||
| bundleIdentifier: "com.example.platform"); | ||
| } | ||
|
|
||
| static void AssertArtifact(string artifactLine, string fileName, string packageFormat, string signed, string packageId, string platformName, string bundleIdentifier) | ||
| { | ||
| 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(platformName, metadata[10]); | ||
| Assert.Equal(bundleIdentifier, metadata[11]); | ||
| } | ||
| } | ||
|
|
||
| [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,13 @@ | |
| <ProjectCapability Include="MauiEssentials" Condition=" '$(UseMaui)' == 'true' or '$(UseMauiEssentials)' == 'true' " /> | ||
| </ItemGroup> | ||
|
|
||
| <PropertyGroup> | ||
| <GetApplicationArtifactsDependsOn> | ||
|
Collaborator
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. 💡 [minor] Build & MSBuild — Consider guarding the dependency-chain extension The property group appends |
||
| $(GetApplicationArtifactsDependsOn); | ||
| _AddMauiApplicationArtifactMetadata | ||
| </GetApplicationArtifactsDependsOn> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- SingleProject-specific features --> | ||
| <ItemGroup Condition=" '$(SingleProject)' == 'true' "> | ||
| <ProjectCapability Include="Msix" /> | ||
|
|
@@ -30,4 +37,17 @@ | |
| <AndroidManifest Condition=" Exists('Platforms\Android\AndroidManifest.xml') ">Platforms\Android\AndroidManifest.xml</AndroidManifest> | ||
| </PropertyGroup> | ||
|
|
||
| <Target Name="_AddMauiApplicationArtifactMetadata" Condition="'@(ApplicationArtifact)' != ''"> | ||
| <ItemGroup> | ||
| <ApplicationArtifact Update="@(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> | ||
| </ItemGroup> | ||
| </Target> | ||
|
|
||
| </Project> | ||
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.