Skip to content

Commit ba8191e

Browse files
committed
Switch to single project packaging
1 parent c3a0f1b commit ba8191e

File tree

16 files changed

+451
-190
lines changed

16 files changed

+451
-190
lines changed

.github/scripts/Configure-AppxManifest.ps1

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ if ($Branch -eq "SideloadPreview")
4242
# Save modified Package.appxmanifest
4343
$xmlDoc.Save($PackageManifestPath)
4444

45-
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
45+
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
4646
{ `
4747
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | `
4848
Set-Content $_ -NoNewline `
@@ -53,6 +53,7 @@ if ($Branch -eq "SideloadPreview")
5353
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-preview" }) | `
5454
Set-Content $_ -NoNewline `
5555
}
56+
5657
}
5758
elseif ($Branch -eq "StorePreview")
5859
{
@@ -75,7 +76,7 @@ elseif ($Branch -eq "StorePreview")
7576

7677
$xmlDoc.Save($PackageManifestPath)
7778

78-
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
79+
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
7980
{ `
8081
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | `
8182
Set-Content $_ -NoNewline `
@@ -86,6 +87,7 @@ elseif ($Branch -eq "StorePreview")
8687
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-preview" }) | `
8788
Set-Content $_ -NoNewline `
8889
}
90+
8991
}
9092
elseif ($Branch -eq "SideloadStable")
9193
{
@@ -102,7 +104,7 @@ elseif ($Branch -eq "SideloadStable")
102104
# Save modified Package.appxmanifest
103105
$xmlDoc.Save($PackageManifestPath)
104106

105-
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
107+
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
106108
{ `
107109
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | `
108110
Set-Content $_ -NoNewline `
@@ -113,6 +115,7 @@ elseif ($Branch -eq "SideloadStable")
113115
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-stable" }) | `
114116
Set-Content $_ -NoNewline `
115117
}
118+
116119
}
117120
elseif ($Branch -eq "StoreStable")
118121
{
@@ -133,7 +136,7 @@ elseif ($Branch -eq "StoreStable")
133136
# Save modified Package.appxmanifest
134137
$xmlDoc.Save($PackageManifestPath)
135138

136-
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach-Object -Process `
139+
Get-ChildItem $WorkingDir -Include *.csproj, *.appxmanifest, *.xaml -recurse | ForEach-Object -Process `
137140
{ `
138141
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | `
139142
Set-Content $_ -NoNewline `
@@ -144,6 +147,15 @@ elseif ($Branch -eq "StoreStable")
144147
(Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "files-dev", "files-stable" }) | `
145148
Set-Content $_ -NoNewline `
146149
}
150+
151+
}
152+
153+
# Remove unused tile assets so they don't end up in the package
154+
$keepTiles = if ($Branch -match 'Preview') { 'Preview' } elseif ($Branch -match 'Stable') { 'Release' } else { $null }
155+
foreach ($folder in @('Dev', 'Preview', 'Release')) {
156+
if ($folder -eq $keepTiles) { continue }
157+
$tilePath = Join-Path $WorkingDir "src\Files.App\Assets\AppTiles\$folder"
158+
if (Test-Path $tilePath) { Remove-Item $tilePath -Recurse -Force }
147159
}
148160

149161
Get-ChildItem $WorkingDir -Include *.cs -recurse | ForEach-Object -Process `
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
# Copyright (c) Files Community
2+
# Licensed under the MIT License.
3+
4+
# Creates an .msixbundle from individual per-platform .msix packages produced
5+
# by single-project MSIX packaging. Optionally generates an .appinstaller file
6+
# for sideload deployments, and an .msixupload for Store submissions.
7+
8+
param(
9+
[Parameter(Mandatory)]
10+
[string]$AppxPackageDir,
11+
12+
[Parameter(Mandatory)]
13+
[string]$BundleName,
14+
15+
[string]$Version = "",
16+
17+
[string]$PackageManifestPath = "",
18+
19+
[string]$AppInstallerUri = "",
20+
21+
[ValidateSet("Sideload", "StoreUpload", "SideloadOnly")]
22+
[string]$BuildMode = "Sideload"
23+
)
24+
25+
$ErrorActionPreference = "Stop"
26+
27+
function Ensure-Directory([string]$Path) {
28+
if (-not (Test-Path $Path)) { New-Item -ItemType Directory -Path $Path | Out-Null }
29+
}
30+
31+
# Canonical arch names for the WAP-compatible folder layout
32+
$archMap = @{ 'arm64' = 'ARM64'; 'x64' = 'x64'; 'x86' = 'x86' }
33+
34+
# --- Locate makeappx.exe ---
35+
36+
$sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
37+
$makeAppx = Get-ChildItem -Path $sdkRoot -Filter "makeappx.exe" -Recurse -ErrorAction SilentlyContinue |
38+
Where-Object { $_.DirectoryName -match '\\x64$' } |
39+
Sort-Object { [version]($_.DirectoryName -replace '^.*\\bin\\([^\\]+)\\.*$','$1') } -Descending |
40+
Select-Object -First 1
41+
if ($null -eq $makeAppx) {
42+
Write-Error "makeappx.exe not found under '$sdkRoot'"
43+
exit 1
44+
}
45+
Write-Host "Using makeappx: $($makeAppx.FullName)"
46+
47+
# --- Discover per-platform .msix packages ---
48+
49+
$msixFiles = Get-ChildItem -Path $AppxPackageDir -Filter "*.msix" -Recurse |
50+
Where-Object { $_.DirectoryName -notmatch '\\Dependencies\\' }
51+
if ($msixFiles.Count -eq 0) {
52+
Write-Error "No .msix files found in '$AppxPackageDir'"
53+
exit 1
54+
}
55+
Write-Host "Found $($msixFiles.Count) .msix package(s):"
56+
$msixFiles | ForEach-Object { Write-Host " $_" }
57+
58+
if ($Version -eq "" -and $PackageManifestPath -ne "" -and (Test-Path $PackageManifestPath)) {
59+
[xml]$versionManifest = Get-Content $PackageManifestPath
60+
$Version = $versionManifest.Package.Identity.Version
61+
Write-Host "Version from manifest: $Version"
62+
}
63+
if ($Version -eq "") {
64+
$Version = $msixFiles[0].BaseName -replace '^[^_]+_([^_]+)_.*$','$1'
65+
Write-Host "Detected version from filename: $Version"
66+
}
67+
68+
$platformList = $msixFiles | ForEach-Object { $_.BaseName -replace '.*_(\w+)$','$1' } | Sort-Object -Descending
69+
$platforms = $platformList -join '_'
70+
71+
# --- Create .msixbundle ---
72+
73+
$mappingDir = Join-Path $AppxPackageDir "_bundletemp"
74+
if (Test-Path $mappingDir) { Remove-Item $mappingDir -Recurse -Force }
75+
New-Item -ItemType Directory -Path $mappingDir | Out-Null
76+
foreach ($msix in $msixFiles) { Copy-Item $msix.FullName -Destination $mappingDir }
77+
78+
$bundlePath = Join-Path $AppxPackageDir "$BundleName.msixbundle"
79+
if (Test-Path $bundlePath) { Remove-Item $bundlePath -Force }
80+
81+
Write-Host "Creating msixbundle at: $bundlePath"
82+
& $makeAppx.FullName bundle /d $mappingDir /p $bundlePath /bv $Version /o
83+
if ($LASTEXITCODE -ne 0) {
84+
Write-Error "MakeAppx bundle creation failed with exit code $LASTEXITCODE"
85+
exit 1
86+
}
87+
Remove-Item $mappingDir -Recurse -Force
88+
Write-Host "Successfully created: $bundlePath"
89+
90+
# --- Reorganize into WAP-compatible folder structure for CDN upload ---
91+
# Target layout: {BundleName}_{Version}_Test/{BundleName}_{Version}_{platforms}.msixbundle
92+
# {BundleName}_{Version}_Test/Dependencies/{arch}/...
93+
94+
$bundleFolder = "${BundleName}_${Version}_Test"
95+
$bundleFolderPath = Join-Path $AppxPackageDir $bundleFolder
96+
Ensure-Directory $bundleFolderPath
97+
98+
$organizedBundleName = "${BundleName}_${Version}_${platforms}.msixbundle"
99+
$organizedBundlePath = Join-Path $bundleFolderPath $organizedBundleName
100+
Move-Item $bundlePath $organizedBundlePath -Force
101+
Write-Host "Moved bundle to: $organizedBundlePath"
102+
103+
# --- Merge dependency folders from each per-platform build ---
104+
105+
$organizedDepsDir = Join-Path $bundleFolderPath "Dependencies"
106+
foreach ($msix in $msixFiles) {
107+
$perPlatDepsDir = Join-Path $msix.DirectoryName "Dependencies"
108+
if (-not (Test-Path $perPlatDepsDir)) { continue }
109+
110+
Get-ChildItem -Path $perPlatDepsDir -Directory | ForEach-Object {
111+
$archName = $_.Name
112+
$canonicalArch = $archMap[$archName]
113+
if (-not $canonicalArch) { return }
114+
if (-not ($platformList -contains $archName)) { return }
115+
116+
$targetArchDir = Join-Path $organizedDepsDir $canonicalArch
117+
Ensure-Directory $targetArchDir
118+
Get-ChildItem -Path $_.FullName -File | ForEach-Object {
119+
$destFile = Join-Path $targetArchDir $_.Name
120+
if (-not (Test-Path $destFile)) {
121+
Copy-Item $_.FullName -Destination $destFile
122+
Write-Host " Copied dependency: $destFile"
123+
}
124+
}
125+
}
126+
}
127+
128+
# --- Add VCLibs framework packages (not produced by single-project builds) ---
129+
130+
$vcLibsSdkBase = "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows Kits\10\ExtensionSDKs"
131+
$vcLibsPackages = @(
132+
@{ SdkFolder = "Microsoft.VCLibs"; FileTemplate = "Microsoft.VCLibs.{0}.14.00.appx" },
133+
@{ SdkFolder = "Microsoft.VCLibs.Desktop"; FileTemplate = "Microsoft.VCLibs.{0}.14.00.Desktop.appx" }
134+
)
135+
136+
foreach ($platform in $platformList) {
137+
$canonicalArch = $archMap[$platform]
138+
if (-not $canonicalArch) { continue }
139+
140+
$targetArchDir = Join-Path $organizedDepsDir $canonicalArch
141+
Ensure-Directory $targetArchDir
142+
143+
foreach ($vcLib in $vcLibsPackages) {
144+
$fileName = $vcLib.FileTemplate -f $canonicalArch
145+
$destPath = Join-Path $targetArchDir $fileName
146+
if (Test-Path $destPath) { continue }
147+
148+
$sdkPath = Join-Path $vcLibsSdkBase "$($vcLib.SdkFolder)\14.0\Appx\Retail\$platform\$fileName"
149+
if (Test-Path $sdkPath) {
150+
Copy-Item $sdkPath -Destination $destPath
151+
Write-Host " Copied VCLibs from SDK: $destPath"
152+
} else {
153+
Write-Host " Downloading $fileName for $platform..."
154+
try {
155+
Invoke-WebRequest -Uri "https://aka.ms/$fileName" -OutFile $destPath -UseBasicParsing
156+
Write-Host " Downloaded VCLibs: $destPath"
157+
} catch {
158+
Write-Warning " Failed to download $fileName for ${platform}: $_"
159+
}
160+
}
161+
}
162+
}
163+
164+
# --- Collect symbol files and clean up per-platform build folders ---
165+
166+
foreach ($msix in $msixFiles) {
167+
Get-ChildItem -Path $msix.DirectoryName -Filter "*.appxsym" -ErrorAction SilentlyContinue | ForEach-Object {
168+
$symPlatform = $_.BaseName -replace '.*_(\w+)$','$1'
169+
$newSymPath = Join-Path $bundleFolderPath "${BundleName}_${Version}_${symPlatform}.appxsym"
170+
Move-Item $_.FullName $newSymPath -Force
171+
Write-Host "Moved symbol file to: $newSymPath"
172+
}
173+
}
174+
175+
Get-ChildItem -Path $AppxPackageDir -Filter "*.msixupload" -ErrorAction SilentlyContinue |
176+
ForEach-Object { Remove-Item $_.FullName -Force; Write-Host "Removed per-platform upload: $($_.Name)" }
177+
178+
foreach ($msix in $msixFiles) {
179+
if (Test-Path $msix.DirectoryName) {
180+
Remove-Item $msix.DirectoryName -Recurse -Force
181+
Write-Host "Removed per-platform dir: $($msix.DirectoryName)"
182+
}
183+
}
184+
185+
# --- Generate .msixupload for Store submissions ---
186+
187+
if ($BuildMode -eq "StoreUpload") {
188+
$uploadName = "${BundleName}_${Version}_${platforms}_bundle.msixupload"
189+
$uploadPath = Join-Path $AppxPackageDir $uploadName
190+
if (Test-Path $uploadPath) { Remove-Item $uploadPath -Force }
191+
192+
Write-Host "Creating msixupload at: $uploadPath"
193+
$zipPath = "$uploadPath.zip"
194+
Compress-Archive -Path $organizedBundlePath -DestinationPath $zipPath -Force
195+
Move-Item $zipPath $uploadPath -Force
196+
Write-Host "Successfully created: $uploadPath"
197+
}
198+
199+
# --- Generate .appinstaller for sideload deployments ---
200+
201+
if ($AppInstallerUri -ne "" -and ($BuildMode -eq "Sideload" -or $BuildMode -eq "SideloadOnly")) {
202+
$appInstallerPath = Join-Path $AppxPackageDir "$BundleName.appinstaller"
203+
204+
if ($PackageManifestPath -ne "" -and (Test-Path $PackageManifestPath)) {
205+
[xml]$manifestXml = Get-Content $PackageManifestPath
206+
$packageName = $manifestXml.Package.Identity.Name
207+
$packagePublisher = $manifestXml.Package.Identity.Publisher
208+
} else {
209+
Write-Warning "PackageManifestPath not provided; falling back to BundleName for identity"
210+
$packageName = $BundleName
211+
$packagePublisher = ""
212+
}
213+
214+
$bundleUri = "${AppInstallerUri}${bundleFolder}/${organizedBundleName}"
215+
216+
# Build dependency XML entries by reading each package's embedded manifest
217+
$dependencyEntries = ""
218+
if (Test-Path $organizedDepsDir) {
219+
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction SilentlyContinue
220+
Get-ChildItem -Path $organizedDepsDir -Recurse -Include *.appx, *.msix | ForEach-Object {
221+
$depArch = Split-Path (Split-Path $_.FullName -Parent) -Leaf
222+
223+
$depZip = [System.IO.Compression.ZipFile]::OpenRead($_.FullName)
224+
try {
225+
$entry = $depZip.Entries | Where-Object { $_.FullName -eq "AppxManifest.xml" } | Select-Object -First 1
226+
if ($null -eq $entry) { return }
227+
$reader = New-Object System.IO.StreamReader($entry.Open())
228+
[xml]$depManifest = $reader.ReadToEnd()
229+
$reader.Close()
230+
} finally { $depZip.Dispose() }
231+
232+
$nsMgr = New-Object System.Xml.XmlNamespaceManager($depManifest.NameTable)
233+
$nsMgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10")
234+
$id = $depManifest.SelectSingleNode("/pkg:Package/pkg:Identity", $nsMgr)
235+
if ($null -eq $id) { return }
236+
237+
$arch = $id.GetAttribute("ProcessorArchitecture")
238+
if ([string]::IsNullOrEmpty($arch)) { $arch = $depArch }
239+
$depUri = "${AppInstallerUri}${bundleFolder}/Dependencies/$depArch/$($_.Name)"
240+
241+
$dependencyEntries += @"
242+
243+
<Package
244+
Name="$($id.GetAttribute("Name"))"
245+
Publisher="$($id.GetAttribute("Publisher"))"
246+
ProcessorArchitecture="$arch"
247+
Uri="$depUri"
248+
Version="$($id.GetAttribute("Version"))" />
249+
"@
250+
}
251+
}
252+
253+
if ($dependencyEntries -eq "") {
254+
Write-Warning "No dependency packages found"
255+
} else {
256+
Write-Host "Discovered dependencies:$dependencyEntries"
257+
}
258+
259+
@"
260+
<?xml version="1.0" encoding="utf-8"?>
261+
<AppInstaller
262+
Uri="${AppInstallerUri}${BundleName}.appinstaller"
263+
Version="$Version" xmlns="http://schemas.microsoft.com/appx/appinstaller/2018">
264+
<MainBundle
265+
Name="$packageName"
266+
Publisher="$packagePublisher"
267+
Version="$Version"
268+
Uri="$bundleUri" />
269+
<Dependencies>$dependencyEntries
270+
</Dependencies>
271+
</AppInstaller>
272+
"@ | Set-Content -Path $appInstallerPath -Encoding UTF8
273+
274+
Write-Host "Created appinstaller at: $appInstallerPath"
275+
}

0 commit comments

Comments
 (0)