Expose application artifacts#25723
Conversation
Add public MSBuild item groups for final MaciOS build and publish artifacts so custom targets and CI can consume app bundles, IPA files, PKG installers, and Xcode archives with metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Collapse MaciOS artifact and published artifact item groups into ApplePackageOutput, matching AndroidPackageOutput naming and reducing metadata to the package contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make ApplePackageOutput Signed reflect app code signing only, and expose separate PackageSigned metadata for .pkg installer package signing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop Signed and PackageSigned from ApplePackageOutput since Apple builds do not emit parallel signed and unsigned package outputs that need to be distinguished. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop AppBundlePath from ApplePackageOutput so the public contract focuses on the final artifact item identity and stable package metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add direct GetApplePackageOutputs coverage using MSBuild item query output for IPA, PKG, app bundle, and xcarchive artifacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename the public artifact item group and query target to ApplicationArtifact and GetApplicationArtifacts so the surface can be shared across platforms. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add GetApplicationArtifactsDependsOn so later SDKs such as MAUI can enrich ApplicationArtifact metadata before GetApplicationArtifacts returns items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Validate that GetApplicationArtifactsDependsOn extension targets can observe platform-produced ApplicationArtifact items before adding metadata, and verify both app and package artifacts receive the augmented metadata. Restore the injected sample project content after the test to avoid leaving dirty files on failure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Route Publish through GetApplicationArtifacts after _PrePublish so targets appended with GetApplicationArtifactsDependsOn can augment ApplicationArtifact metadata before Publish returns items. Add publish-target coverage that validates a MAUI-like extension sees platform artifacts before updating metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Document that GetApplicationArtifactsDependsOn extension targets should update platform-produced ApplicationArtifact items when adding metadata, while only adding new items for additional artifacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make Build a mandatory GetApplicationArtifacts dependency instead of part of the overridable GetApplicationArtifactsDependsOn property. Extension targets still run after platform artifacts are produced, and tests now overwrite the property to prove Build cannot be removed while metadata augmentation still applies to GetApplicationArtifacts and Publish. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update ApplicationArtifact target-result examples to use dotnet build with -getTargetResult for GetApplicationArtifacts and Publish. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve conflict in Xamarin.Shared.targets by keeping the ApplicationArtifact query target and the new PrepareAssemblies targets from main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Android equivalent: dotnet/android#11674 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…ationArtifact) Adding the GetApplicationArtifacts target (with Returns="@(ApplicationArtifact)") to Xamarin.Shared.targets regressed app-extension embedding: a plain 'dotnet build' of an app that references an app extension stopped copying the .appex into the container app's PlugIns folder, failing DotNetProjectTest.BuildProjectsWithExtensions on iOS, tvOS and macOS. The cause is an MSBuild behavior: once any target in a .targets file uses the 'Returns' attribute, MSBuild no longer treats the 'Outputs' attribute of that file's other targets as their return value - those targets return an empty set to <MSBuild> callers. GetAppExtensionBundlePath and BuildAndGetAppExtensionBundlePath relied on 'Outputs="@(_AppExtensionBundlePath)"' being returned (they are invoked cross-project by _ResolveAppExtensionReferences / _ResolveWatchAppReferences, which capture TargetOutputs into _ResolvedAppExtensionReferences). After GetApplicationArtifacts introduced the first 'Returns' in the file, those two targets returned nothing, so _ResolvedAppExtensionReferences was empty and _CopyAppExtensionsToBundle copied nothing. Fix by giving both targets an explicit Returns equal to their Outputs, restoring their previous return value, and add a comment documenting the pitfall. The @(ApplicationArtifact) feature (GetApplicationArtifacts / Publish) is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
✅ [PR Build #ee63ecc] Build passed (Detect API changes) ✅Pipeline on Agent |
This comment has been minimized.
This comment has been minimized.
✅ [PR Build #ee63ecc] Build passed (Build packages) ✅Pipeline on Agent |
✅ API diff for current PR / commitNET (empty diffs)✅ API diff vs stableNET (empty diffs)ℹ️ Generator diffGenerator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes) Pipeline on Agent |
✅ [PR Build #ee63ecc] Build passed (Build macOS tests) ✅Pipeline on Agent |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
🚀 [CI Build #ee63ecc] Test results 🚀Test results✅ All tests passed on VSTS: test results. 🎉 All 220 tests passed 🎉 Tests counts✅ assembly-processing: All 1 tests passed. Html Report (VSDrops) Download macOS tests✅ Tests on macOS Monterey (12): All 5 tests passed. Html Report (VSDrops) Download Linux Build VerificationPipeline on Agent |
| var result = DotNet.AssertBuild (project_path, properties, timeout: TimeSpan.FromMinutes (15)); | ||
|
|
||
| // The .ipa is built on the paired Mac and copied back to this Windows machine, so it must be | ||
| // surfaced in @(ApplicationArtifact) with the local (Windows) path. The .app bundle stays on the | ||
| // Mac, so it's intentionally not surfaced as an artifact on Windows. | ||
| var ipaPath = Path.Combine (appPath, "..", $"{project}.ipa"); | ||
| Assert.That (ipaPath, Does.Exist, "ipa creation"); | ||
| AssertApplicationArtifact (result.BinLogPath, ipaPath, platform, "ipa", isDirectory: false); | ||
| } | ||
|
|
||
| [Test] | ||
| [Category ("RemoteWindows")] | ||
| [TestCase (ApplePlatform.iOS, "ios-arm64")] | ||
| public void ArchiveOnRemoteWindowsTest (ApplePlatform platform, string runtimeIdentifiers) | ||
| { | ||
| var project = "MySimpleApp"; | ||
| var configuration = "Release"; | ||
| Configuration.IgnoreIfIgnoredPlatform (platform); | ||
| Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers); | ||
| Configuration.IgnoreIfNotOnWindows (); | ||
|
|
||
| var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration); | ||
| Clean (project_path); | ||
| var properties = GetDefaultProperties (runtimeIdentifiers); | ||
| properties ["ArchiveOnBuild"] = "true"; | ||
| properties ["Configuration"] = configuration; | ||
|
|
||
| var result = DotNet.AssertBuild (project_path, properties, timeout: TimeSpan.FromMinutes (20)); |
There was a problem hiding this comment.
NDB, and this could be fixed in a follow-up PR if desired: building remotely is slow, so would it be possible to merge these two tests into one so that we only build one test app remotely? Setting both ArchiveOnBuild + BuildIpa seems like it could accomplish that.
There was a problem hiding this comment.
Created #25769 to track merging the two remote Windows tests into a single build.
Apple platform builds and publishes now expose their final outputs through a shared
@(ApplicationArtifact)MSBuild item group. This gives custom targets, tooling, and CI a stable, structured way to discover the produced.app,.ipa,.pkg, and.xcarchiveoutputs instead of parsing individual scalar properties ($(AppBundleDir),$(IpaPackagePath),$(PkgPackagePath),$(ArchiveDir), …). The item group name is platform-neutral, leaving room for other SDKs to reuse the same contract.What's exposed
@(ApplicationArtifact)item per final artifact, whose item identity is the absolute path to the artifact.PackageFormat—app,ipa,pkg, orxcarchiveIsDirectory—truefor.app/.xcarchive,falsefor.ipa/.pkgPlatformName—iOS,tvOS,macOS, orMacCatalystBundleIdentifier— the resolved app bundle identifierCodesign, the IPA afterCreateIpa, the PKG after_CreateInstaller, and the Xcode archive afterArchive— so artifacts gated byBuildIpa,CreatePackage, orArchiveOnBuildare surfaced when those are enabled.How to query
GetApplicationArtifactsbuilds the project and returns@(ApplicationArtifact);Publishreturns the same group (and enablesBuildIpa/CreatePackageas appropriate):Extensibility
GetApplicationArtifactsDependsOnis a post-build extension point so later SDK layers (for example .NET MAUI) can enrich the metadata on the platform-created items, or add items for additional artifacts.Buildis a hard-coded dependency ofGetApplicationArtifacts, outside the overridable property, so overwritingGetApplicationArtifactsDependsOncan never skip platform artifact population. Platform metadata stays limited to the four well-known values above; extension targets own anything richer and should update the platform-created items rather than duplicate them.Windows (Pair to Mac)
On Windows the
.ipa/.xcarchiveare produced on the paired Mac and only copied back afterwards, so the collectors are anchored on the publicCreateIpa/Archivewrapper targets (which complete after the copy-back) instead of the internal_ZipIpa/_CoreArchive. The archive collector prefers the local$(ArchiveDir)and falls back to the copied-back$(ArchivePath). The.appstays on the Mac during a remote build, so it is intentionally not surfaced there (guarded byIsRemoteBuild); surfacing the Windows.appand theIsAppDistributionIPA is left for a follow-up.App-extension fix
Adding a
Returnsattribute (onGetApplicationArtifacts) surfaced a subtle MSBuild behavior: once any target in a.targetsfile usesReturns, that file's other targets stop returning theirOutputsto<MSBuild>callers. That silently emptied the legacyGetAppExtensionBundlePath/BuildAndGetAppExtensionBundlePathgetters, so app extensions were no longer embedded into their container app'sPlugInsfolder. Both targets now declareReturnsexplicitly (equal to their previousOutputs), restoring extension embedding while keeping the new feature.Tests
MySimpleAppWithArtifactMetadatatest project (rather than mutating a shared one) exercises theGetApplicationArtifactsDependsOnextension/override behavior so the tests can run in parallel.PostBuildTestitem-query tests for.app,.ipa,.pkg, and.xcarchive, extension-added metadata, publish-target augmentation, an overwrittenGetApplicationArtifactsDependsOn, and the target dependency graph — asserted both from the binlog and via-getItem.[Category("RemoteWindows")]tests validating that the.ipaand.xcarchiveare surfaced after Pair-to-Mac copy-back.Docs
docs/building-apps/build-items.md—@(ApplicationArtifact)and its metadata.docs/building-apps/build-properties.md—GetApplicationArtifactsDependsOn.docs/building-apps/build-targets.md— theGetApplicationArtifactscontract and-getItemquery examples.Validation
xmllint --noout msbuild/Xamarin.Shared/Xamarin.Shared.targets msbuild/Xamarin.Shared/Xamarin.iOS.Common.targetsgit diff --checkmake all install) and ran the@(ApplicationArtifact)query tests and the app-extension build tests for iOS/tvOS/macOS: artifacts are populated with absolute paths and correct metadata, and app extensions are embedded into their container app.