Microsoft.Build.MsixPackaging — reusable MSBuild SDK for multi-app MSIX packaging#672
Open
shmuelie wants to merge 18 commits into
Open
Microsoft.Build.MsixPackaging — reusable MSBuild SDK for multi-app MSIX packaging#672shmuelie wants to merge 18 commits into
shmuelie wants to merge 18 commits into
Conversation
New MSBuild SDK that packages multiple .NET projects into a single sideloadable MSIX using per-project AppxFragment.xml manifest merging. Replaces WAP/DesktopBridge with a transparent, SDK-style workflow. Includes: - 3 compiled MSBuild tasks (netstandard2.0): - MergeAppxFragments: multi-section fragment merge + version/arch stamping - ValidateAppxManifest: XML well-formedness + required elements + duplicate IDs - FindWindowsSdkTool: locates MakeAppx/SignTool/MakePri from Windows SDK - Sdk.props: imports NoTargets, defines 19 configurable properties - Sdk.targets: BuildMsix orchestrator with 7-stage pipeline + 4 opt-in targets - VS Property Page (XAML Rule) with 10 properties across 4 categories - build/ files for traditional NuGet package consumption Ported from SEnglard Ideas/MsixPackagingSdk prototype and adapted to MSBuildSdks repo conventions (Microsoft.Build.* naming, central package management, signing, Nerdbank.GitVersioning). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
15 xunit tests covering the 3 compiled MSBuild tasks: - MergeAppxFragmentsTests: version validation, structured fragment detection, Identity attribute patching, indentation formatting - ValidateAppxManifestTests: valid manifest, missing Identity, duplicate Application IDs, malformed XML, missing file, invalid version - FindWindowsSdkToolTests: host architecture detection All tests pass across net472, net8.0, net9.0, and net10.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Minimal consumer project demonstrating the SDK: - SampleConsoleApp: trivial console app with AppxFragment.xml - SamplePackaging.msbuildproj: imports SDK from source tree - Package.base.appxmanifest: manifest template with fragment marker - Directory.Build.props: overrides task assembly path for local dev Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MergeAppxFragments.cs and ValidateAppxManifest.cs had StyleCop violations (missing file headers, using order, omitted braces, member ordering, blank lines) that were masked by incremental analyzer caching and only surfaced on a clean build. Rewrap to satisfy the analyzers with no behavioral change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… signing Replace the SDK's hand-rolled Windows SDK tool discovery and the raw Exec calls to MakeAppx/SignTool with the compiled MSBuild tasks from the Microsoft.Windows.SDK.BuildTools.MSIX package (namespace Microsoft.Build.Msix): - Tool discovery: FindWindowsSdkTool (custom C# task) -> WinAppSdkGetSdkFileFullPath, used to locate MakeAppx.exe, SignTool.exe, and MakePri.exe. - Packing: PackMsix Exec(makeappx /d) -> WinAppSdkMakeAppxPack, packing from a generated [Files] mapping file built from the layout directory. - Signing: SignMsix Exec(signtool) -> WinAppSdkSignAppxPackage (adds timestamp and Azure Code Signing / Key Vault support). Integration (no bundling): the package is injected as a package reference from Sdk.props with GeneratePathProperty and ExcludeAssets=build;buildTransitive, so only the task assembly is used and the package's heavyweight WinAppSDK pipeline is not imported. VersionOverride is used under Central Package Management so no central PackageVersion entry is required. The task assembly is resolved per runtime (net6.0 for Core, net472 for desktop MSBuild). A small RoslynCodeTaskFactory inline task resolves the latest installed Windows SDK version for TargetPlatformVersion (honoring MsixWindowsSdkVersion when set). Removes FindWindowsSdkTool and its unit test. New properties: MsixSdkBuildToolsVersion, MsixWindowsSdkVersion, MsixHashAlgorithmId. MsixToolArchitecture is now a no-op (architecture resolved by the package). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The sample packaging project was nested a level too deep, so its SDK imports, ProjectReference, and default Package.base.appxmanifest/Images paths did not resolve (the project was never built in the original change). Flatten it to samples/MsixPackaging/ to match the sibling manifest, images, and app project, and point the dev-time _MsixSdkTasksAssembly at the artifacts output. Verified end-to-end: the sample now publishes, merges fragments, validates, and packs a valid .msix via the new build-tools pipeline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Surface the timestamping, Azure Code Signing (Trusted Signing), and Azure Key Vault parameters that the WinAppSdkSignAppxPackage task already accepts: - MsixTimestampUrl / MsixTimestampDigestAlgorithm - MsixAzureCodeSigningEnabled + Dlib/Endpoint/AccountName/CertificateProfileName - MsixAzureKeyVaultEnabled + Dlib/Url/CertificateId SignMsix now runs when signing is enabled and either a certificate file is supplied or an Azure signing mode is enabled (Azure modes need no .pfx). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Before signing with a certificate file, run WinAppSdkValidateSigningCertificate and WinAppSdkValidatePublisherName so a mismatch between the manifest Publisher and the signing certificate fails the build early with a clear message. Gated by MsixValidateSigningCertificate (default true) and skipped for the Azure signing modes, which do not use a local certificate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When MsixSigningEnabled=true, MsixGenerateTestCertificate=true, and no certificate is supplied, generate a throwaway self-signed code-signing certificate whose subject matches the manifest Publisher, sign with it, and delete the temporary .pfx afterward. The certificate is created in memory via .NET CertificateRequest in a shipped PowerShell script (New-MsixTestCertificate.ps1) — nothing is written to the certificate store, so no store cleanup is required and it works under both Windows PowerShell and PowerShell 7. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Opt-in GenerateMsixSymbolPackage target (MsixSymbolPackageEnabled) collects the PDBs from the layout and produces a .msixsym via WinAppSdkGenerateAppxSymbolPackage for Partner Center crash analysis. Output defaults to the .msix path with a .msixsym extension and can be overridden with MsixSymbolPackageOutput. Runs after PackMsix in the build pipeline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Opt-in GenerateMsixAppInstaller target (MsixAppInstallerEnabled) writes an .appinstaller file for sideload auto-update. Identity (Name/Publisher/Version/ ProcessorArchitecture) is read from the merged manifest; the hosted package URL is derived from MsixAppInstallerUri or set explicitly via MsixAppInstallerPackageUri. Update cadence is controlled by MsixAppInstallerUpdateCheckHours (OnLaunch). References a MainBundle when MsixBundleEnabled=true, otherwise a MainPackage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Opt-in MsixBundleEnabled builds one MSIX per architecture in MsixBundlePlatforms (e.g. x64|x86|arm64) and combines them into a .msixbundle: - BundleMsix drives a per-architecture loop (target-batched) that re-runs the pack chain with MsixTargetArchitecture and a win-<arch> RuntimeIdentifier, producing one arch-stamped .msix each into a clean packages directory, then runs 'makeappx bundle'. - A representative architecture layout is copied to MsixLayoutDir so the signing, symbol, and App Installer targets can read identity and PDBs. - Downstream targets act on _MsixPrimaryPackage, which is the bundle in bundle mode and the .msix otherwise, so signing/App Installer work for both. Referenced apps must declare the target architectures in <RuntimeIdentifiers> so restore covers them. The default single-architecture pipeline is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Opt-in MsixStoreUploadEnabled wraps the .msixbundle and, when present, the .msixsym symbol package into a .msixupload container for Partner Center via WinAppSdkCreateAppStoreContainer. Requires MsixBundleEnabled (a .msixupload wraps a bundle); errors clearly otherwise. Runs at the end of the bundle pipeline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add Bundle and Distribution categories and surface the new opt-in properties in the Visual Studio Project Properties UI: test-certificate generation, certificate validation, and timestamping (Signing); bundle build, bundle platforms, and Store upload (Bundle); symbol package and App Installer (Distribution). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The sample build now exercises test-certificate signing, certificate validation, the symbol package, and the App Installer end-to-end. SampleConsoleApp declares <RuntimeIdentifiers> so the multi-architecture bundle and Store-upload paths can be enabled via documented opt-in properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add property tables for signing (test certificate, validation, timestamp, Azure) and distribution/bundling (symbol package, App Installer, bundle, Store upload), plus a Signing section and a Multi-architecture bundles & distribution section, and update the VS property page category listing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Sdk\New-MsixTestCertificate.ps1 helper triggers NuGet's legacy PowerShell-install-script validation (NU5110/NU5111), which the repo promotes to errors. The script is invoked by the SDK targets, not by NuGet install, so suppress those warnings for this package. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New SDK project: Microsoft.Build.MsixPackaging — a reusable MSBuild SDK that packages multiple .NET projects into a sideloadable MSIX (single package or multi-architecture bundle) using per-project
AppxFragment.xmlmanifest entries. Replaces WAP/DesktopBridge with a transparent, SDK-style workflow.SDK-tool discovery, packing, signing, symbol packaging, and Store packaging use the compiled MSBuild tasks in the
Microsoft.Windows.SDK.BuildTools.MSIXpackage, so the SDK relies on supported Windows SDK tooling.Consumer Experience
Pipeline
BuildMsixorchestrator with a conditional pipeline (opt-in stages no-op unless enabled):Single package (default): Publish -> Merge -> Validate -> CopyAssets -> ResourceIndex -> Pack -> Symbol -> Sign -> AppInstaller
Bundle (
MsixBundleEnabled): per-architecture Publish/Merge/Validate/CopyAssets/ResourceIndex/Pack ->makeappx bundle-> Symbol -> Sign -> AppInstaller -> StoreUploadVS dev-loop (layout mode): Publish -> Merge -> Validate -> CopyAssets -> ResourceIndex -> DeployLayout (skips Pack/Sign)
PublishToLayoutProjectReferencewithLayoutDir(per-architecture RID in bundle mode)dotnet publishMergeAppxManifestMergeAppxFragmentstaskValidateAppxManifestValidateAppxManifesttaskCopyMsixAssetsMsixContentitemsCopyGenerateResourceIndexMakePri.exefor.resw(auto/true/false)WinAppSdkGetSdkFileFullPath+ MakePriPackMsix.msixWinAppSdkMakeAppxPackBundleMsix.msixbundlemakeappx bundleGenerateMsixSymbolPackage.msixsymfrom the layout PDBsWinAppSdkGenerateAppxSymbolPackageSignMsix.msix/.msixbundle(cert file, test cert, timestamp, or Azure)WinAppSdkSignAppxPackage(+ validation)GenerateMsixAppInstaller.appinstallerfor sideload auto-updateCreateMsixUpload.msixuploadWinAppSdkCreateAppStoreContainerPlus opt-in targets:
CleanMsixLayout,InstallMsix,RegisterMsixLayout,UninstallMsix.Build tooling (Microsoft.Windows.SDK.BuildTools.MSIX)
The SDK uses the package's compiled tasks (namespace
Microsoft.Build.Msix): SDK-tool discovery (WinAppSdkGetSdkFileFullPath), packing (WinAppSdkMakeAppxPack), signing (WinAppSdkSignAppxPackage), publisher/certificate validation (WinAppSdkValidatePublisherName,WinAppSdkValidateSigningCertificate), symbol packaging (WinAppSdkGenerateAppxSymbolPackage), and Store container creation (WinAppSdkCreateAppStoreContainer).Integration (not bundled): the package is injected as a
PackageReferencefromSdk.propswithGeneratePathPropertyandExcludeAssets="build;buildTransitive", so only the task assembly is used and the package's heavyweight WinAppSDK pipeline is not imported. It is restored automatically (consumers do not add it manually).VersionOverrideis used under Central Package Management so no centralPackageVersionentry is required, and the task assembly is resolved per runtime (net6.0for Core,net472for desktop MSBuild). ARoslynCodeTaskFactoryinline task resolves the latest installed Windows SDK version forTargetPlatformVersion(honoringMsixWindowsSdkVersionwhen set).Key Features
MsixPackageVersion(4-part numeric);MsixContentitems for arbitrary content.msixsym) for Partner Center crash analysis.appinstaller) for sideload auto-update (references the bundle or the.msix).msixbundle) acrossMsixBundlePlatforms(e.g.x64|x86|arm64).msixupload) wrapping the bundle and symbol for Partner CenterAll signing, symbol, App Installer, bundle, and Store-upload features are opt-in and off by default; the default build produces a single unsigned
.msix.What's Included
SDK Project (
src/MsixPackaging/)Microsoft.Build.MsixPackaging.csproj— netstandard2.0, follows the Artifacts patternTasks/— 2 compiled MSBuild tasks (MergeAppxFragments,ValidateAppxManifest)Sdk/Sdk.props— NoTargets import, property defaults, build-tools package injection, VS property pageSdk/Sdk.targets—BuildMsixorchestrator and all targets, using the build-tools package tasksSdk/Microsoft.Build.MsixPackaging.PropertyPage.xaml— VS Project Properties pageSdk/New-MsixTestCertificate.ps1— generates an in-memory self-signed test certificatebuild/—.props/.targetsfor traditional NuGet package consumptionversion.json(1.0),README.mdUnit Tests (
src/MsixPackaging.UnitTests/)net472,net8.0,net9.0,net10.0MergeAppxFragmentsTests— version validation, fragment detection, attribute patching (version/architecture), indentationValidateAppxManifestTests— valid manifest, missing elements, duplicate IDs, malformed XML, invalid versionSample (
samples/MsixPackaging/)SampleConsoleApp/— console app withAppxFragment.xml,MsixImages/, and<RuntimeIdentifiers>for bundlingSamplePackaging.msbuildproj— consumes the SDK; the default build demonstrates test-certificate signing, the symbol package, and the App Installer, with the multi-architecture bundle and Store upload available via documented opt-in propertiesPackage.base.appxmanifest— minimal manifest templateRepo Conventions
Microsoft.Build.MsixPackaging, task namespaceMicrosoft.Build.MsixPackaging.TasksMicrosoft.Windows.SDK.BuildTools.MSIXtasksglobal.jsonmsbuild-sdksFilesToSign(Authenticode + StrongName) for the task DLL1.0)BuildOutputTargetFolder=build\(Artifacts pattern)Notes
<RuntimeIdentifiers>so restore covers them;MsixStoreUploadEnabledrequires a bundle.MsixToolArchitectureis a no-op (the build-tools package resolves tool architecture automatically); retained for backward compatibility.Testing
dotnet build --no-incremental— SDK compiles with 0 warnings, 0 errors (StyleCop clean) and packs (task DLL +Sdk/content)dotnet test— 14/14 tests pass across all 4 TFMs.msix+.msixsym+.appinstallerx64|x86|arm64) — signed.msixbundle(all architectures) +.msixupload+.msixsym+.appinstaller