Skip to content

Commit 8304374

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

File tree

16 files changed

+446
-190
lines changed

16 files changed

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

0 commit comments

Comments
 (0)