diff --git a/eng/Signing.props b/eng/Signing.props
index 222ad3dc47a..22ef88fff52 100644
--- a/eng/Signing.props
+++ b/eng/Signing.props
@@ -5,6 +5,13 @@
+
+
+
+
+
true
diff --git a/eng/generate-catalog.ps1 b/eng/generate-catalog.ps1
new file mode 100644
index 00000000000..70dd8bda448
--- /dev/null
+++ b/eng/generate-catalog.ps1
@@ -0,0 +1,157 @@
+<#
+.SYNOPSIS
+ Generates a Catalog Definition File (.cdf) and optionally runs makecat.exe to
+ produce a signed catalog (.cat) file.
+.DESCRIPTION
+ Recursively scans a directory for files matching a filter and produces a .cdf
+ file suitable for makecat.exe. Optionally invokes makecat.exe to generate the
+ .cat file directly.
+
+ Used in component team pipelines (Arcade SDK, MicroBuild, or custom) to
+ catalog-sign customer-modifiable or non-PE files that cannot use direct
+ Authenticode signing.
+
+ If -RunMakecat is specified, the script finds and runs makecat.exe automatically.
+ Otherwise, it prints the commands to run manually.
+.PARAMETER RootPath
+ The directory containing files to include in the catalog.
+.PARAMETER CdfPath
+ The output path for the .cdf file. If not specified and CatOutputPath is set,
+ defaults to the CatOutputPath with a .cdf extension.
+.PARAMETER CatOutputPath
+ The path where makecat.exe will create the .cat file. Defaults to the CDF
+ path with a .cat extension.
+.PARAMETER Filter
+ File filter pattern (e.g., '*.js', '*.xml', '*.ttf'). Default: '*.*' (all files).
+.PARAMETER RunMakecat
+ If specified, finds and runs makecat.exe to produce the .cat file.
+.PARAMETER WindowsSdkDir
+ Optional path to the Windows SDK. Used to locate makecat.exe when -RunMakecat is set.
+.PARAMETER ErrorIfMakecatNotFound
+ If specified with -RunMakecat, throws an error when makecat.exe is not found
+ instead of warning and skipping. Use in CI/official builds.
+.EXAMPLE
+ # Generate CDF only (print manual steps):
+ .\New-CatalogDefinitionFile.ps1 -RootPath ".\content" -CdfPath ".\obj\my-files.cdf"
+.EXAMPLE
+ # Generate CDF and run makecat.exe:
+ .\New-CatalogDefinitionFile.ps1 -RootPath ".\content" -CatOutputPath ".\obj\my-files.cat" -Filter "*.js" -RunMakecat
+.EXAMPLE
+ # CI usage (error if makecat.exe not found):
+ .\New-CatalogDefinitionFile.ps1 -RootPath "$(_ContentRoot)" -CatOutputPath "$(_CatOutputPath)" -Filter "*.js" -RunMakecat -ErrorIfMakecatNotFound
+#>
+[CmdletBinding()]
+param(
+ [Parameter(Mandatory)]
+ [string]$RootPath,
+
+ [string]$CdfPath,
+
+ [string]$CatOutputPath,
+
+ [string]$Filter = '*.*',
+
+ [string]$WindowsSdkDir = '',
+
+ [switch]$RunMakecat,
+
+ [switch]$ErrorIfMakecatNotFound
+)
+
+$ErrorActionPreference = 'Stop'
+
+if (-not (Test-Path $RootPath)) {
+ Write-Error "Root path not found: $RootPath"
+ return
+}
+
+# Resolve paths: need at least one of CdfPath or CatOutputPath
+if (-not $CdfPath -and -not $CatOutputPath) {
+ Write-Error "Specify at least one of -CdfPath or -CatOutputPath."
+ return
+}
+if (-not $CatOutputPath) {
+ $CatOutputPath = [System.IO.Path]::ChangeExtension($CdfPath, '.cat')
+}
+if (-not $CdfPath) {
+ $CdfPath = [System.IO.Path]::ChangeExtension($CatOutputPath, '.cdf')
+}
+
+# Ensure output directories exist
+$cdfDir = Split-Path $CdfPath -Parent
+if ($cdfDir -and -not (Test-Path $cdfDir)) {
+ New-Item -ItemType Directory -Path $cdfDir -Force | Out-Null
+}
+$catDir = Split-Path $CatOutputPath -Parent
+if ($catDir -and -not (Test-Path $catDir)) {
+ New-Item -ItemType Directory -Path $catDir -Force | Out-Null
+}
+
+$files = Get-ChildItem -Path $RootPath -Recurse -Filter $Filter -File
+if ($files.Count -eq 0) {
+ Write-Warning "No files matching '$Filter' found under $RootPath - skipping catalog generation."
+ return
+}
+
+$cdfContent = @()
+$cdfContent += "[CatalogHeader]"
+$cdfContent += "Name=$CatOutputPath"
+$cdfContent += "CatalogVersion=2"
+$cdfContent += "HashAlgorithms=SHA256"
+$cdfContent += ""
+$cdfContent += "[CatalogFiles]"
+
+$i = 0
+foreach ($f in $files) {
+ $ext = $f.Extension.TrimStart('.').ToLower()
+ $label = "${ext}_${i}_" + ($f.Name -replace '[^\w\.-]', '_')
+ $cdfContent += "$label=$($f.FullName)"
+ $i++
+}
+
+$cdfContent | Set-Content -Path $CdfPath -Encoding ASCII
+
+Write-Host "Generated CDF with $($files.Count) file(s) matching '$Filter' at $CdfPath"
+
+if ($RunMakecat) {
+ # Find makecat.exe — ships with the Windows SDK
+ $makecat = $null
+ if ($WindowsSdkDir -and (Test-Path $WindowsSdkDir)) {
+ $makecat = Get-ChildItem -Path (Join-Path $WindowsSdkDir 'bin') -Recurse -Filter 'makecat.exe' -File |
+ Where-Object { $_.DirectoryName -match 'x64' } |
+ Sort-Object DirectoryName -Descending |
+ Select-Object -First 1
+ }
+ if (-not $makecat) { $makecat = Get-Command makecat.exe -ErrorAction SilentlyContinue }
+ if (-not $makecat) {
+ $sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
+ if (Test-Path $sdkRoot) {
+ $makecat = Get-ChildItem -Path $sdkRoot -Recurse -Filter 'makecat.exe' -File |
+ Where-Object { $_.DirectoryName -match 'x64' } |
+ Sort-Object DirectoryName -Descending |
+ Select-Object -First 1
+ }
+ }
+ if (-not $makecat) {
+ if ($ErrorIfMakecatNotFound) {
+ throw "makecat.exe not found. Catalog signing requires the Windows SDK."
+ }
+ Write-Warning "makecat.exe not found - skipping catalog generation. Install Windows SDK for catalog signing."
+ return
+ }
+
+ $makecatPath = if ($makecat -is [System.Management.Automation.CommandInfo]) { $makecat.Source } else { $makecat.FullName }
+ Write-Host "Using makecat.exe at: $makecatPath"
+
+ & $makecatPath $CdfPath
+ if ($LASTEXITCODE -ne 0) {
+ throw "makecat.exe failed with exit code $LASTEXITCODE"
+ }
+
+ Write-Host "Generated catalog file: $CatOutputPath"
+} else {
+ Write-Host ""
+ Write-Host "Next steps:"
+ Write-Host " 1. Run: makecat.exe `"$CdfPath`""
+ Write-Host " 2. Sign: dotnet ddsignfiles.dll -- /file:`"$CatOutputPath`" /certs:Microsoft400"
+}
diff --git a/vsintegration/Vsix/VisualFSharpFull/VisualFSharpDebug.csproj b/vsintegration/Vsix/VisualFSharpFull/VisualFSharpDebug.csproj
index 5a6671c4d24..f2f6b8ab992 100644
--- a/vsintegration/Vsix/VisualFSharpFull/VisualFSharpDebug.csproj
+++ b/vsintegration/Vsix/VisualFSharpFull/VisualFSharpDebug.csproj
@@ -84,4 +84,23 @@
+
+
+
+
+ <_ContentRoot>$(TargetVsixContainerIntermediateOutputPath)
+ <_CatOutputPath>$(TargetVsixContainerIntermediateOutputPath)FSharpDebugContent.cat
+ <_ErrorFlag Condition="'$(ContinuousIntegrationBuild)' == 'true' or '$(OfficialBuild)' == 'true'">-ErrorIfMakecatNotFound
+
+
+
+