From 0aced0012c50b1ec596d9896f7c7dada80fe845b Mon Sep 17 00:00:00 2001
From: zipg
Date: Mon, 27 Jul 2026 16:44:47 +0800
Subject: [PATCH] fix(updater): separate Windows 7 offline update path
---
.../scripts/prepare-webview2-win7-runtime.ps1 | 65 ++
.github/workflows/ci.yml | 66 ++
.github/workflows/release.yml | 108 ++-
Cargo.lock | 2 -
Cargo.toml | 4 +
.../components/layout/UpdateDialog.spec.ts | 20 +
.../src/components/layout/UpdateDialog.vue | 5 +-
apps/desktop/src/composables/useAppUpdater.ts | 2 +-
apps/desktop/src/i18n/locales/en.ts | 1 +
apps/desktop/src/i18n/locales/es.ts | 1 +
apps/desktop/src/i18n/locales/it.ts | 1 +
apps/desktop/src/i18n/locales/ja.ts | 1 +
apps/desktop/src/i18n/locales/pt-BR.ts | 1 +
apps/desktop/src/i18n/locales/zh-CN.ts | 1 +
apps/desktop/src/i18n/locales/zh-TW.ts | 1 +
.../windowsInstallerTemplate.spec.ts | 65 ++
.../lib/app/windowsWebView2RuntimePolicy.ts | 9 +
apps/desktop/src/lib/backend/tauri.ts | 1 +
crates/dbx-core/src/update.rs | 2 +
src-tauri/src/commands/update.rs | 22 +-
.../tauri.webview2-win7-offline.conf.json | 11 +
src-tauri/windows/nsis/installer.nsi | 69 +-
vendor/ctor/Cargo.toml | 63 ++
vendor/ctor/LICENSE-APACHE | 201 ++++++
vendor/ctor/LICENSE-MIT | 5 +
vendor/ctor/README.md | 144 ++++
vendor/ctor/src/example.rs | 111 +++
vendor/ctor/src/lib.rs | 251 +++++++
vendor/ctor/src/macros/mod.rs | 642 ++++++++++++++++++
29 files changed, 1851 insertions(+), 24 deletions(-)
create mode 100644 .github/scripts/prepare-webview2-win7-runtime.ps1
create mode 100644 apps/desktop/src/lib/__tests__/windowsInstallerTemplate.spec.ts
create mode 100644 apps/desktop/src/lib/app/windowsWebView2RuntimePolicy.ts
create mode 100644 src-tauri/tauri.webview2-win7-offline.conf.json
create mode 100644 vendor/ctor/Cargo.toml
create mode 100644 vendor/ctor/LICENSE-APACHE
create mode 100644 vendor/ctor/LICENSE-MIT
create mode 100644 vendor/ctor/README.md
create mode 100644 vendor/ctor/src/example.rs
create mode 100644 vendor/ctor/src/lib.rs
create mode 100644 vendor/ctor/src/macros/mod.rs
diff --git a/.github/scripts/prepare-webview2-win7-runtime.ps1 b/.github/scripts/prepare-webview2-win7-runtime.ps1
new file mode 100644
index 000000000..ed522bb49
--- /dev/null
+++ b/.github/scripts/prepare-webview2-win7-runtime.ps1
@@ -0,0 +1,65 @@
+[CmdletBinding()]
+param(
+ [string]$CacheRoot = (Join-Path $env:LOCALAPPDATA "tauri"),
+ [string]$DownloadDirectory = $env:RUNNER_TEMP
+)
+
+Set-StrictMode -Version Latest
+$ErrorActionPreference = "Stop"
+
+$runtimeVersion = "109.0.1518.140"
+$runtimeUrl = "https://catalog.s.download.windowsupdate.com/c/msdownload/update/software/updt/2023/09/microsoftedgestandaloneinstallerx64_1c890b4b8dd6b7c93da98ebdc08ecdc5e30e50cb.exe"
+$runtimeSha256 = "eac95c8095ec5f9971eade9827d8fb67fd251f5c16e702b5312d31067e39119b"
+$evergreenUrl = "https://go.microsoft.com/fwlink/?linkid=2124701"
+
+if ([string]::IsNullOrWhiteSpace($CacheRoot)) {
+ throw "A Tauri cache root is required."
+}
+if ([string]::IsNullOrWhiteSpace($DownloadDirectory)) {
+ $DownloadDirectory = [System.IO.Path]::GetTempPath()
+}
+
+New-Item -ItemType Directory -Force -Path $DownloadDirectory | Out-Null
+$downloadPath = Join-Path $DownloadDirectory "MicrosoftEdgeWebView2Runtime-$runtimeVersion-x64.exe"
+
+if (Test-Path $downloadPath) {
+ $downloadHash = (Get-FileHash -LiteralPath $downloadPath -Algorithm SHA256).Hash.ToLowerInvariant()
+ if ($downloadHash -ne $runtimeSha256) {
+ Remove-Item -LiteralPath $downloadPath -Force
+ }
+}
+
+if (!(Test-Path $downloadPath)) {
+ Write-Host "Downloading WebView2 Runtime $runtimeVersion for Windows 7..."
+ Invoke-WebRequest -Uri $runtimeUrl -OutFile $downloadPath
+}
+
+$actualHash = (Get-FileHash -LiteralPath $downloadPath -Algorithm SHA256).Hash.ToLowerInvariant()
+if ($actualHash -ne $runtimeSha256) {
+ throw "WebView2 Runtime SHA-256 mismatch. Expected $runtimeSha256, got $actualHash."
+}
+
+# Tauri 2.11 does not expose an offline-installer path override. It resolves the
+# Evergreen URL and reuses a matching cache entry, so place the verified 109
+# installer at that exact location before bundling.
+$response = Invoke-WebRequest -Uri $evergreenUrl -Method Head
+$resolvedUrl = $response.BaseResponse.RequestMessage.RequestUri.AbsoluteUri
+$match = [regex]::Match(
+ $resolvedUrl,
+ "/filestreamingservice/files/(?[^/]+)/(?[^/?]+)"
+)
+if (!$match.Success) {
+ throw "Unexpected Evergreen WebView2 URL: $resolvedUrl"
+}
+
+$cacheDirectory = Join-Path $CacheRoot (Join-Path "x64" $match.Groups["guid"].Value)
+$cachePath = Join-Path $cacheDirectory $match.Groups["filename"].Value
+New-Item -ItemType Directory -Force -Path $cacheDirectory | Out-Null
+Copy-Item -LiteralPath $downloadPath -Destination $cachePath -Force
+
+$cacheHash = (Get-FileHash -LiteralPath $cachePath -Algorithm SHA256).Hash.ToLowerInvariant()
+if ($cacheHash -ne $runtimeSha256) {
+ throw "Cached WebView2 Runtime SHA-256 mismatch. Expected $runtimeSha256, got $cacheHash."
+}
+
+Write-Host "Prepared WebView2 Runtime $runtimeVersion at $cachePath"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4a0913db2..e4415723b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -79,6 +79,60 @@ jobs:
- name: Node package publish dry run
run: pnpm publish:dry-run
+ windows-win7-bundle:
+ needs: changes
+ if: needs.changes.outputs.windows_win7_bundle == 'true'
+ runs-on: windows-2022
+ timeout-minutes: 90
+ env:
+ CARGO_INCREMENTAL: "0"
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v6
+ with:
+ version: 10.27.0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: 22.13.0
+ cache: pnpm
+
+ - name: Install frontend dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Setup Rust for Windows 7
+ uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2026-07-22
+ components: rust-src
+
+ - name: Build frontend
+ run: pnpm build
+
+ - name: Build DBX for Windows 7
+ run: cargo build --locked --package dbx --release --target x86_64-win7-windows-msvc -Z build-std=std,panic_abort
+
+ - name: Prepare WebView2 109 offline runtime
+ shell: pwsh
+ run: ./.github/scripts/prepare-webview2-win7-runtime.ps1
+
+ - name: Bundle Windows 7 offline installer
+ shell: pwsh
+ run: |
+ $bundleDir = "target/x86_64-win7-windows-msvc/release/bundle/nsis"
+ pnpm tauri bundle --bundles nsis --target x86_64-win7-windows-msvc --config src-tauri/tauri.webview2-win7-offline.conf.json
+ $installer = Get-ChildItem $bundleDir -Filter "*.exe" |
+ Sort-Object LastWriteTimeUtc -Descending |
+ Select-Object -First 1
+ if (!$installer) {
+ Write-Error "Missing Windows 7 WebView2 offline installer in ${bundleDir}"
+ exit 1
+ }
+ Get-FileHash -LiteralPath $installer.FullName -Algorithm SHA256
+
rust-fmt-clippy:
needs: changes
if: needs.changes.outputs.rust == 'true'
@@ -299,6 +353,7 @@ jobs:
jdbc: ${{ steps.filter.outputs.jdbc }}
agents: ${{ steps.filter.outputs.agents }}
nix: ${{ steps.filter.outputs.nix }}
+ windows_win7_bundle: ${{ steps.filter.outputs.windows_win7_bundle }}
steps:
- uses: actions/checkout@v5
with:
@@ -350,6 +405,17 @@ jobs:
- 'flake.lock'
- '.github/workflows/ci.yml'
- '.github/workflows/update-nix-pnpm-hash.yml'
+ windows_win7_bundle:
+ - '.github/scripts/prepare-webview2-win7-runtime.ps1'
+ - '.github/workflows/ci.yml'
+ - '.github/workflows/release.yml'
+ - 'src-tauri/tauri.webview2-win7-offline.conf.json'
+ - 'src-tauri/windows/nsis/**'
+ - 'src-tauri/src/commands/update.rs'
+ - 'crates/dbx-core/src/update.rs'
+ - 'Cargo.toml'
+ - 'Cargo.lock'
+ - 'vendor/ctor/**'
- name: Select Rust feature coverage
id: rust-mode
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bded38a75..2603f1cee 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -335,6 +335,112 @@ jobs:
Copy-Item $installer.FullName $offlineName -Force
gh release upload "${env:GITHUB_REF_NAME}" $offlineName --repo "${env:GITHUB_REPOSITORY}" --clobber
+ build-windows-7-offline:
+ runs-on: windows-2022
+ env:
+ CARGO_INCREMENTAL: "0"
+ RUSTC_WRAPPER: sccache
+ SCCACHE_GHA_ENABLED: ${{ secrets.SCCACHE_S3_BUCKET == '' && 'true' || 'false' }}
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: 22
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v6
+
+ - name: Install frontend dependencies
+ run: pnpm install
+
+ - name: Setup Rust for Windows 7
+ uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2026-07-22
+ components: rust-src
+
+ - name: Setup sccache
+ uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
+ with:
+ version: "v0.10.0"
+
+ - name: Configure S3 sccache
+ if: env.SCCACHE_GHA_ENABLED != 'true'
+ shell: bash
+ env:
+ CACHE_BUCKET: ${{ secrets.SCCACHE_S3_BUCKET }}
+ CACHE_ENDPOINT: ${{ secrets.SCCACHE_S3_ENDPOINT }}
+ CACHE_REGION: ${{ secrets.SCCACHE_S3_REGION }}
+ CACHE_KEY_PREFIX: ${{ secrets.SCCACHE_S3_KEY_PREFIX }}
+ CACHE_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY_ID }}
+ CACHE_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_ACCESS_KEY }}
+ run: |
+ {
+ echo "SCCACHE_BUCKET=${CACHE_BUCKET}"
+ echo "SCCACHE_ENDPOINT=${CACHE_ENDPOINT}"
+ echo "SCCACHE_REGION=${CACHE_REGION}"
+ echo "SCCACHE_S3_KEY_PREFIX=${CACHE_KEY_PREFIX}"
+ echo "SCCACHE_S3_USE_SSL=true"
+ echo "AWS_ACCESS_KEY_ID=${CACHE_ACCESS_KEY_ID}"
+ echo "AWS_SECRET_ACCESS_KEY=${CACHE_SECRET_ACCESS_KEY}"
+ } >> "$GITHUB_ENV"
+
+ - name: Rust cache
+ uses: swatinem/rust-cache@v2
+ with:
+ workspaces: "./ -> target"
+ shared-key: release-x86_64-win7-windows-msvc
+ add-rust-environment-hash-key: true
+ cache-targets: false
+ cache-on-failure: true
+
+ - name: Build frontend
+ run: pnpm build
+
+ - name: Build DBX for Windows 7
+ run: cargo build --locked --package dbx --release --target x86_64-win7-windows-msvc -Z build-std=std,panic_abort
+
+ - name: Prepare WebView2 109 offline runtime
+ shell: pwsh
+ run: ./.github/scripts/prepare-webview2-win7-runtime.ps1
+
+ - name: Bundle and upload Windows 7 offline installer
+ shell: pwsh
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ $version = "${env:GITHUB_REF_NAME}".TrimStart("v")
+ $bundleDir = "target/x86_64-win7-windows-msvc/release/bundle/nsis"
+ $offlineName = "DBX_${version}_x64-win7-webview2-109-offline-setup.exe"
+ $cargoMetadata = cargo metadata --no-deps --format-version 1 | ConvertFrom-Json
+ $appVersion = ($cargoMetadata.packages | Where-Object { $_.name -eq "dbx" } | Select-Object -First 1).version
+
+ if (!$appVersion -or $appVersion -ne $version) {
+ Write-Error "Release tag version $version does not match the built DBX package version $appVersion"
+ exit 1
+ }
+
+ pnpm tauri bundle --bundles nsis --target x86_64-win7-windows-msvc --config src-tauri/tauri.webview2-win7-offline.conf.json
+
+ $installer = Get-ChildItem $bundleDir -Filter "*.exe" |
+ Sort-Object LastWriteTimeUtc -Descending |
+ Select-Object -First 1
+ if (!$installer) {
+ Write-Error "Missing Windows 7 WebView2 offline installer in ${bundleDir}"
+ exit 1
+ }
+
+ Copy-Item $installer.FullName $offlineName -Force
+ gh release upload "${env:GITHUB_REF_NAME}" $offlineName --repo "${env:GITHUB_REPOSITORY}" --clobber
+
+ - name: Show sccache stats
+ if: always()
+ continue-on-error: true
+ shell: bash
+ run: ${SCCACHE_PATH} --show-stats
+
static-browser:
# Fully static musl builds of the browser (dbx-web) variant. No glibc
# dependency, so the tarball runs on any Linux distribution (verified in
@@ -438,7 +544,7 @@ jobs:
--repo "${GITHUB_REPOSITORY}" --clobber
cleanup-release-signatures:
- needs: build
+ needs: [build, build-windows-7-offline]
runs-on: ubuntu-latest
steps:
- name: Remove standalone updater signature assets
diff --git a/Cargo.lock b/Cargo.lock
index 995dc3025..72d489045 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1674,8 +1674,6 @@ dependencies = [
[[package]]
name = "ctor"
version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98"
dependencies = [
"ctor-proc-macro",
"dtor",
diff --git a/Cargo.toml b/Cargo.toml
index 3d96f4490..716cfc927 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,12 @@
[workspace]
resolver = "2"
members = ["src-tauri", "crates/dbx-core", "crates/dbx-web", "crates/dbx-mcp", "crates/dbx-cli"]
+exclude = ["vendor/ctor"]
[patch.crates-io]
+# Tauri 2.11 uses ctor 0.8, which excludes Rust's win7 vendor. This vendors
+# upstream rust-ctor#443 until tauri-utils moves to ctor 1.x.
+ctor = { path = "vendor/ctor" }
tokio-postgres = { git = "https://github.com/t8y2/tokio-postgres-gaussdb.git", branch = "master" }
postgres-types = { git = "https://github.com/t8y2/tokio-postgres-gaussdb.git", branch = "master" }
postgres-protocol = { git = "https://github.com/t8y2/tokio-postgres-gaussdb.git", branch = "master" }
diff --git a/apps/desktop/src/components/layout/UpdateDialog.spec.ts b/apps/desktop/src/components/layout/UpdateDialog.spec.ts
index 3a53913b8..00b7c7083 100644
--- a/apps/desktop/src/components/layout/UpdateDialog.spec.ts
+++ b/apps/desktop/src/components/layout/UpdateDialog.spec.ts
@@ -14,6 +14,7 @@ const mountedApps: App[] = [];
interface DialogState {
open: boolean;
portableMode: boolean;
+ manualUpdateOnly: boolean;
isDownloadingUpdate: boolean;
downloadProgress: number;
updateDownloaded: boolean;
@@ -30,6 +31,7 @@ async function mountDialog(activeTaskCount: number, initialState: Partial
-
{{ t("updates.activeTasksBlockUpdate", { count: activeTaskCount }) }}
diff --git a/apps/desktop/src/composables/useAppUpdater.ts b/apps/desktop/src/composables/useAppUpdater.ts
index 27b7daaa0..113e58d9b 100644
--- a/apps/desktop/src/composables/useAppUpdater.ts
+++ b/apps/desktop/src/composables/useAppUpdater.ts
@@ -19,7 +19,7 @@ export function shouldOpenUpdateDialog(options: { silent?: boolean }) {
}
export function canDownloadAndInstallUpdate(info: api.UpdateInfo | null, isDesktop: boolean) {
- return isDesktop && info?.update_available === true;
+ return isDesktop && info?.update_available === true && info.manual_update_only !== true;
}
export function normalizeUpdateDownloadSource(value: unknown): SettingsUpdateDownloadSource {
diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts
index 53825e0a8..38117ce90 100644
--- a/apps/desktop/src/i18n/locales/en.ts
+++ b/apps/desktop/src/i18n/locales/en.ts
@@ -71,6 +71,7 @@ export default {
downloadAndInstall: "Download & Install",
activeTasksBlockUpdate: "{count} task(s) are still running. Wait for them to finish before updating DBX.",
portableAutomaticUpdate: "DBX will download the signed portable ZIP, replace only DBX.exe after exit, and restart automatically. portable.dbx and data will be kept.",
+ windows7ManualUpdate: "Windows 7 requires the dedicated WebView2 109 offline installer. Open the release page and download the Windows 7 package to update.",
downloading: "Downloading {progress}%",
downloadFailed: "Update download failed: {error}",
installing: "Installing update...",
diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts
index b45db7353..442ad8cbb 100644
--- a/apps/desktop/src/i18n/locales/es.ts
+++ b/apps/desktop/src/i18n/locales/es.ts
@@ -73,6 +73,7 @@ export default withEnglishFallback({
downloadAndInstall: "Descargar e instalar",
activeTasksBlockUpdate: "Hay {count} tarea(s) en ejecución. Espera a que terminen antes de actualizar DBX.",
portableAutomaticUpdate: "DBX descargará el ZIP portable firmado, reemplazará solo DBX.exe después de salir y se reiniciará automáticamente. portable.dbx y data se conservarán.",
+ windows7ManualUpdate: "Windows 7 requiere el instalador sin conexión dedicado de WebView2 109. Abre la página de lanzamiento y descarga el paquete para Windows 7.",
downloading: "Descargando {progress}%",
downloadFailed: "Error al descargar la actualización: {error}",
installing: "Instalando actualización...",
diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts
index efc04acb7..6a9de8d2b 100644
--- a/apps/desktop/src/i18n/locales/it.ts
+++ b/apps/desktop/src/i18n/locales/it.ts
@@ -72,6 +72,7 @@ export default withEnglishFallback({
downloadAndInstall: "Scarica e Installa",
activeTasksBlockUpdate: "Ci sono {count} attività in esecuzione. Attendi che terminino prima di aggiornare DBX.",
portableAutomaticUpdate: "DBX scaricherà lo ZIP portatile firmato, sostituirà solo DBX.exe dopo l'uscita e si riavvierà automaticamente. portable.dbx e i dati verranno mantenuti.",
+ windows7ManualUpdate: "Windows 7 richiede il programma di installazione offline dedicato WebView2 109. Apri la pagina della release e scarica il pacchetto per Windows 7.",
downloading: "Download in corso {progress}%",
downloadFailed: "Download dell'aggiornamento non riuscito: {error}",
installing: "Installazione dell'aggiornamento...",
diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts
index 1ba0723a4..3faa78e38 100644
--- a/apps/desktop/src/i18n/locales/ja.ts
+++ b/apps/desktop/src/i18n/locales/ja.ts
@@ -73,6 +73,7 @@ export default withEnglishFallback({
downloadAndInstall: "ダウンロード & インストール",
activeTasksBlockUpdate: "{count} 件のタスクが実行中です。完了してから DBX を更新してください。",
portableAutomaticUpdate: "DBX は署名済みのポータブル ZIP をダウンロードし、終了後に DBX.exe のみを置き換えて自動的に再起動します。portable.dbx とデータは保持されます。",
+ windows7ManualUpdate: "Windows 7 では専用の WebView2 109 オフラインインストーラーが必要です。リリースページから Windows 7 用パッケージをダウンロードして更新してください。",
downloading: "ダウンロード中 {progress}%",
downloadFailed: "アップデートのダウンロードに失敗しました: {error}",
installing: "アップデートをインストール中...",
diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts
index 54c10085f..11b47b5bb 100644
--- a/apps/desktop/src/i18n/locales/pt-BR.ts
+++ b/apps/desktop/src/i18n/locales/pt-BR.ts
@@ -73,6 +73,7 @@ export default withEnglishFallback({
downloadAndInstall: "Baixar e Instalar",
activeTasksBlockUpdate: "Há {count} tarefa(s) em execução. Aguarde a conclusão antes de atualizar o DBX.",
portableAutomaticUpdate: "O DBX baixará o ZIP portátil assinado, substituirá apenas o DBX.exe após sair e reiniciará automaticamente. O portable.dbx e os dados serão mantidos.",
+ windows7ManualUpdate: "O Windows 7 requer o instalador offline dedicado do WebView2 109. Abra a página da versão e baixe o pacote para Windows 7.",
downloading: "Baixando {progress}%",
downloadFailed: "Falha ao baixar a atualização: {error}",
installing: "Instalando atualização...",
diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts
index 0ef82e8d2..eee232c1f 100644
--- a/apps/desktop/src/i18n/locales/zh-CN.ts
+++ b/apps/desktop/src/i18n/locales/zh-CN.ts
@@ -73,6 +73,7 @@ export default withEnglishFallback({
downloadAndInstall: "下载并安装",
activeTasksBlockUpdate: "有 {count} 个任务正在执行,请等待任务完成后再更新 DBX。",
portableAutomaticUpdate: "DBX 将下载已签名的便携版 ZIP,退出后仅替换 DBX.exe 并自动重启。portable.dbx 和 data 会保留。",
+ windows7ManualUpdate: "Windows 7 需要使用专用的 WebView2 109 离线安装包更新。请打开下载页并下载 Windows 7 安装包。",
downloading: "下载中 {progress}%",
downloadFailed: "更新下载失败:{error}",
installing: "正在安装更新...",
diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts
index 1a13dfde6..32a9b2155 100644
--- a/apps/desktop/src/i18n/locales/zh-TW.ts
+++ b/apps/desktop/src/i18n/locales/zh-TW.ts
@@ -73,6 +73,7 @@ export default withEnglishFallback({
downloadAndInstall: "下載並安裝",
activeTasksBlockUpdate: "有 {count} 個任務正在執行,請等待任務完成後再更新 DBX。",
portableAutomaticUpdate: "DBX 將下載已簽署的可攜版 ZIP,結束後僅替換 DBX.exe 並自動重新啟動。portable.dbx 和 data 會保留。",
+ windows7ManualUpdate: "Windows 7 需要使用專用的 WebView2 109 離線安裝套件更新。請開啟下載頁並下載 Windows 7 安裝套件。",
downloading: "下載中 {progress}%",
downloadFailed: "更新下載失敗:{error}",
installing: "正在安裝更新...",
diff --git a/apps/desktop/src/lib/__tests__/windowsInstallerTemplate.spec.ts b/apps/desktop/src/lib/__tests__/windowsInstallerTemplate.spec.ts
new file mode 100644
index 000000000..6eb32b5bb
--- /dev/null
+++ b/apps/desktop/src/lib/__tests__/windowsInstallerTemplate.spec.ts
@@ -0,0 +1,65 @@
+import { readFileSync } from "node:fs";
+import { resolve } from "node:path";
+import { describe, expect, it } from "vitest";
+import { shouldAbortWindowsWebView2RuntimeFallback } from "@/lib/app/windowsWebView2RuntimePolicy";
+
+const template = readFileSync(resolve(process.cwd(), "src-tauri/windows/nsis/installer.nsi"), "utf8");
+
+describe("Windows offline installer template", () => {
+ it.each([
+ {
+ name: "continues after installer failure when a compatible Runtime remains",
+ input: { installerExitCode: 1, runtimeDetected: true, runtimeMeetsMinimum: true },
+ expected: false,
+ },
+ {
+ name: "aborts after installer failure when the Runtime is missing",
+ input: { installerExitCode: 1, runtimeDetected: false, runtimeMeetsMinimum: false },
+ expected: true,
+ },
+ {
+ name: "aborts after installer failure when the Runtime is below the minimum",
+ input: { installerExitCode: 1, runtimeDetected: true, runtimeMeetsMinimum: false },
+ expected: true,
+ },
+ {
+ name: "continues after a successful installer even before the registry refresh",
+ input: { installerExitCode: 0, runtimeDetected: false, runtimeMeetsMinimum: false },
+ expected: false,
+ },
+ ])("$name", ({ input, expected }) => {
+ expect(shouldAbortWindowsWebView2RuntimeFallback(input)).toBe(expected);
+ });
+
+ it("binds the tested fallback inputs to the NSIS Runtime recheck contract", () => {
+ expect(template).toContain(`!macro ShouldAbortWebView2OfflineInstall INSTALL_RESULT RUNTIME_VERSION MINIMUM_COMPARISON RESULT
+ StrCpy \${RESULT} 0
+ \${If} \${INSTALL_RESULT} <> 0
+ \${If} \${RUNTIME_VERSION} == ""
+ StrCpy \${RESULT} 1
+ \${ElseIf} \${MINIMUM_COMPARISON} = 1
+ StrCpy \${RESULT} 1
+ \${EndIf}
+ \${EndIf}
+!macroend`);
+ expect(template).toContain(`!insertmacro ReadWebView2RuntimeVersion $4
+ \${If} $4 != ""
+ !if "\${MINIMUMWEBVIEW2VERSION}" != ""
+ \${VersionCompare} "\${MINIMUMWEBVIEW2VERSION}" "$4" $R0
+ !endif
+ \${EndIf}
+ \${EndIf}
+ !insertmacro ShouldAbortWebView2OfflineInstall $1 $4 $R0 $R1
+ \${If} $R1 = 1
+ Abort "$(webview2AbortError)"
+ \${EndIf}`);
+ });
+
+ it("uses the same registry detection before and after Runtime installation", () => {
+ const runtimeChecks = template.match(/!insertmacro ReadWebView2RuntimeVersion \$4/g) ?? [];
+
+ expect(runtimeChecks).toHaveLength(2);
+ expect(template).toContain('ReadRegStr ${RESULT} HKLM "SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\${WEBVIEW2APPGUID}" "pv"');
+ expect(template).toContain('ReadRegStr ${RESULT} HKCU "SOFTWARE\\Microsoft\\EdgeUpdate\\Clients\\${WEBVIEW2APPGUID}" "pv"');
+ });
+});
diff --git a/apps/desktop/src/lib/app/windowsWebView2RuntimePolicy.ts b/apps/desktop/src/lib/app/windowsWebView2RuntimePolicy.ts
new file mode 100644
index 000000000..d83c4d378
--- /dev/null
+++ b/apps/desktop/src/lib/app/windowsWebView2RuntimePolicy.ts
@@ -0,0 +1,9 @@
+export interface WindowsWebView2RuntimeFallbackInput {
+ installerExitCode: number;
+ runtimeDetected: boolean;
+ runtimeMeetsMinimum: boolean;
+}
+
+export function shouldAbortWindowsWebView2RuntimeFallback(input: WindowsWebView2RuntimeFallbackInput): boolean {
+ return input.installerExitCode !== 0 && (!input.runtimeDetected || !input.runtimeMeetsMinimum);
+}
diff --git a/apps/desktop/src/lib/backend/tauri.ts b/apps/desktop/src/lib/backend/tauri.ts
index d621cf7b7..64e13b457 100644
--- a/apps/desktop/src/lib/backend/tauri.ts
+++ b/apps/desktop/src/lib/backend/tauri.ts
@@ -1492,6 +1492,7 @@ export interface UpdateInfo {
latest_version: string;
update_available: boolean;
portable_mode: boolean;
+ manual_update_only: boolean;
release_name: string;
release_url: string;
release_notes: string;
diff --git a/crates/dbx-core/src/update.rs b/crates/dbx-core/src/update.rs
index 60392e4bf..8ba810b68 100644
--- a/crates/dbx-core/src/update.rs
+++ b/crates/dbx-core/src/update.rs
@@ -42,6 +42,7 @@ pub struct UpdateInfo {
pub latest_version: String,
pub update_available: bool,
pub portable_mode: bool,
+ pub manual_update_only: bool,
pub release_name: String,
pub release_url: String,
pub release_notes: String,
@@ -292,6 +293,7 @@ pub fn build_update_info(release: TauriRelease, current_version: &str) -> Update
UpdateInfo {
update_available: is_newer_version(&latest_version, current_version),
portable_mode: false,
+ manual_update_only: false,
current_version: current_version.to_string(),
release_name,
release_url,
diff --git a/src-tauri/src/commands/update.rs b/src-tauri/src/commands/update.rs
index 8ffa3098a..264757626 100644
--- a/src-tauri/src/commands/update.rs
+++ b/src-tauri/src/commands/update.rs
@@ -21,6 +21,7 @@ const GITHUB_RELEASE_DOWNLOAD_PREFIX: &str = "https://github.com/t8y2/dbx/releas
const UPDATE_DOWNLOAD_PROGRESS_EVENT: &str = "update-download-progress";
const MAX_PORTABLE_ARCHIVE_BYTES: usize = 512 * 1024 * 1024;
const MAX_PORTABLE_SIGNATURE_BYTES: usize = 64 * 1024;
+const IS_WINDOWS_7_TARGET: bool = cfg!(target_vendor = "win7");
#[derive(Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
@@ -208,9 +209,14 @@ pub async fn check_for_updates(
let current_version = env!("CARGO_PKG_VERSION");
let mut info = dbx_core::update::build_update_info(release, current_version);
info.portable_mode = crate::data_dir::is_portable_mode();
+ info.manual_update_only = requires_manual_update(IS_WINDOWS_7_TARGET);
Ok(info)
}
+fn requires_manual_update(is_windows_7_target: bool) -> bool {
+ is_windows_7_target
+}
+
#[tauri::command]
pub async fn fetch_changelog(lang: Option
) -> Result {
let lang = lang.unwrap_or_else(|| "en".to_string());
@@ -229,7 +235,11 @@ pub async fn download_update(
source: UpdateDownloadSource,
latest_version: Option,
) -> Result<(), String> {
- let portable_version = if crate::data_dir::is_portable_mode() {
+ let portable_mode = crate::data_dir::is_portable_mode();
+ if requires_manual_update(IS_WINDOWS_7_TARGET) {
+ return Err("Windows 7 builds must be updated with the dedicated Windows 7 offline installer.".to_string());
+ }
+ let portable_version = if portable_mode {
let requested_version =
latest_version.as_deref().ok_or_else(|| "Latest version is required for portable updates.".to_string())?;
Some(update_portable::validate_requested_portable_version(requested_version, env!("CARGO_PKG_VERSION"))?)
@@ -446,10 +456,16 @@ async fn update_url_is_available(url: &str) -> bool {
#[cfg(test)]
mod tests {
use super::{
- tag_version, UpdateDownloadSource, CNB_RELEASE_DOWNLOAD_PREFIX, GITHUB_RELEASE_DOWNLOAD_PREFIX,
- OFFICIAL_UPDATE_ENDPOINTS, R2_LATEST_RELEASE_DOWNLOAD_PREFIX,
+ requires_manual_update, tag_version, UpdateDownloadSource, CNB_RELEASE_DOWNLOAD_PREFIX,
+ GITHUB_RELEASE_DOWNLOAD_PREFIX, OFFICIAL_UPDATE_ENDPOINTS, R2_LATEST_RELEASE_DOWNLOAD_PREFIX,
};
+ #[test]
+ fn all_windows_7_builds_require_manual_updates() {
+ assert!(requires_manual_update(true));
+ assert!(!requires_manual_update(false));
+ }
+
#[test]
fn normalizes_update_tag_versions() {
assert_eq!(tag_version("0.5.39"), "v0.5.39");
diff --git a/src-tauri/tauri.webview2-win7-offline.conf.json b/src-tauri/tauri.webview2-win7-offline.conf.json
new file mode 100644
index 000000000..80c27a370
--- /dev/null
+++ b/src-tauri/tauri.webview2-win7-offline.conf.json
@@ -0,0 +1,11 @@
+{
+ "bundle": {
+ "createUpdaterArtifacts": false,
+ "windows": {
+ "webviewInstallMode": {
+ "silent": true,
+ "type": "offlineInstaller"
+ }
+ }
+ }
+}
diff --git a/src-tauri/windows/nsis/installer.nsi b/src-tauri/windows/nsis/installer.nsi
index 52b51a9eb..69190b640 100644
--- a/src-tauri/windows/nsis/installer.nsi
+++ b/src-tauri/windows/nsis/installer.nsi
@@ -65,6 +65,29 @@ ${StrLoc}
!define STARTMENUFOLDER "{{start_menu_folder}}"
!searchreplace WEBVIEW2LOADERSRCPATH "${MAINBINARYSRCPATH}" "\${MAINBINARYNAME}.exe" "\WebView2Loader.dll"
+!macro ReadWebView2RuntimeVersion RESULT
+ StrCpy ${RESULT} ""
+ ${If} ${RunningX64}
+ ReadRegStr ${RESULT} HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
+ ${Else}
+ ReadRegStr ${RESULT} HKLM "SOFTWARE\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
+ ${EndIf}
+ ${If} ${RESULT} == ""
+ ReadRegStr ${RESULT} HKCU "SOFTWARE\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
+ ${EndIf}
+!macroend
+
+!macro ShouldAbortWebView2OfflineInstall INSTALL_RESULT RUNTIME_VERSION MINIMUM_COMPARISON RESULT
+ StrCpy ${RESULT} 0
+ ${If} ${INSTALL_RESULT} <> 0
+ ${If} ${RUNTIME_VERSION} == ""
+ StrCpy ${RESULT} 1
+ ${ElseIf} ${MINIMUM_COMPARISON} = 1
+ StrCpy ${RESULT} 1
+ ${EndIf}
+ ${EndIf}
+!macroend
+
Var PassiveMode
Var UpdateMode
Var NoShortcutMode
@@ -542,15 +565,36 @@ Section EarlyChecks
SectionEnd
Section WebView2
+ ; Offline packages carry the runtime they were built for. Always run that
+ ; installer so an existing stale runtime is upgraded without network access.
+ !if "${INSTALLWEBVIEW2MODE}" == "offlineInstaller"
+ Delete "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
+ File "/oname=$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" "${WEBVIEW2INSTALLERPATH}"
+ DetailPrint "$(installingWebview2)"
+ ExecWait '"$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" ${WEBVIEW2INSTALLERARGS} /install' $1
+ Delete "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
+ StrCpy $4 ""
+ StrCpy $R0 0
+ ${If} $1 = 0
+ DetailPrint "$(webview2InstallSuccess)"
+ ${Else}
+ DetailPrint "$(webview2InstallError)"
+ ; Enterprise policy can make the bundled installer return a non-zero code.
+ ; Continue when a usable Runtime is already registered on the machine.
+ !insertmacro ReadWebView2RuntimeVersion $4
+ ${If} $4 != ""
+ !if "${MINIMUMWEBVIEW2VERSION}" != ""
+ ${VersionCompare} "${MINIMUMWEBVIEW2VERSION}" "$4" $R0
+ !endif
+ ${EndIf}
+ ${EndIf}
+ !insertmacro ShouldAbortWebView2OfflineInstall $1 $4 $R0 $R1
+ ${If} $R1 = 1
+ Abort "$(webview2AbortError)"
+ ${EndIf}
+ !else
; Check if Webview2 is already installed and skip this section
- ${If} ${RunningX64}
- ReadRegStr $4 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
- ${Else}
- ReadRegStr $4 HKLM "SOFTWARE\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
- ${EndIf}
- ${If} $4 == ""
- ReadRegStr $4 HKCU "SOFTWARE\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
- ${EndIf}
+ !insertmacro ReadWebView2RuntimeVersion $4
${If} $4 == ""
; Webview2 installation
@@ -580,14 +624,6 @@ Section WebView2
Goto install_webview2
!endif
- !if "${INSTALLWEBVIEW2MODE}" == "offlineInstaller"
- Delete "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
- File "/oname=$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" "${WEBVIEW2INSTALLERPATH}"
- DetailPrint "$(installingWebview2)"
- StrCpy $6 "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
- Goto install_webview2
- !endif
-
Goto webview2_done
install_webview2:
@@ -631,6 +667,7 @@ Section WebView2
${EndIf}
!endif
${EndIf}
+ !endif
SectionEnd
Section Install
diff --git a/vendor/ctor/Cargo.toml b/vendor/ctor/Cargo.toml
new file mode 100644
index 000000000..21b140fd5
--- /dev/null
+++ b/vendor/ctor/Cargo.toml
@@ -0,0 +1,63 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "ctor"
+version = "0.8.0"
+authors = ["Matt Mastracci "]
+build = false
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "__attribute__((constructor)) for Rust"
+readme = "README.md"
+categories = ["no-std"]
+license = "Apache-2.0 OR MIT"
+repository = "https://github.com/mmastrac/rust-ctor"
+
+[features]
+__no_warn_on_missing_unsafe = ["dtor?/__no_warn_on_missing_unsafe"]
+default = [
+ "std",
+ "dtor",
+ "proc_macro",
+ "__no_warn_on_missing_unsafe",
+]
+dtor = ["dep:dtor"]
+proc_macro = [
+ "dep:ctor-proc-macro",
+ "dtor?/proc_macro",
+]
+std = ["dtor?/std"]
+used_linker = ["dtor?/used_linker"]
+
+[lib]
+name = "ctor"
+path = "src/lib.rs"
+
+[[example]]
+name = "example"
+path = "src/example.rs"
+
+[dependencies.ctor-proc-macro]
+version = "=0.0.7"
+optional = true
+
+[dependencies.dtor]
+version = "0.3.0"
+optional = true
+default-features = false
+
+[dev-dependencies.libc-print]
+version = "0.1.20"
diff --git a/vendor/ctor/LICENSE-APACHE b/vendor/ctor/LICENSE-APACHE
new file mode 100644
index 000000000..5c304d1a4
--- /dev/null
+++ b/vendor/ctor/LICENSE-APACHE
@@ -0,0 +1,201 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/ctor/LICENSE-MIT b/vendor/ctor/LICENSE-MIT
new file mode 100644
index 000000000..e662c7862
--- /dev/null
+++ b/vendor/ctor/LICENSE-MIT
@@ -0,0 +1,5 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/ctor/README.md b/vendor/ctor/README.md
new file mode 100644
index 000000000..e15dde1bc
--- /dev/null
+++ b/vendor/ctor/README.md
@@ -0,0 +1,144 @@
+# rust-ctor
+
+
+
+`ctor` [](https://docs.rs/ctor)
+[](https://crates.io/crates/ctor)
+
+`dtor` [](https://docs.rs/dtor)
+[](https://crates.io/crates/dtor)
+
+Module initialization/teardown functions for Rust (like
+`__attribute__((constructor))` in C/C++) for Linux, OSX, FreeBSD, NetBSD,
+Illumos, OpenBSD, DragonFlyBSD, Android, iOS, WASM, and Windows.
+
+This library currently requires **Rust > 1.56.0** at a minimum for edition 2021
+support. Library versions 0.2.x should work for edition 2018, and 1.0 is planned
+to be released as 2021-only.
+
+## Zero Dependency
+
+As of `ctor 0.3.0+`, `ctor` has no dependencies (other than the
+`ctor-proc-macro` crate). The proc macro in this crate calls into the
+declarative macro that does the majority of the work.
+
+## Support
+
+This library works and is regularly tested on Linux, OSX, Windows, and FreeBSD,
+with both `+crt-static` and `-crt-static` where possible. Other platforms are
+supported but not tested as part of the automatic builds. This library will also
+work as expected in both `bin` and `cdylib` outputs, ie: the `ctor` and `dtor`
+will run at executable or library startup/shutdown respectively.
+
+This library supports WASM targets, but the MSRV for this target is 1.85.
+
+## Features
+
+| Feature | Description | Default |
+| ------------- | ---------------------------------------------------------------------------------------------------------------------- | ------- |
+| `std` | Enable support for the standard library. This is required for static ctor variables, but not for functions. | Yes |
+| `proc_macro` | Enable support for the proc macro. Required for `#[ctor]` and `#[dtor]` macros, but not for `ctor!` and `dtor!` forms. | Yes |
+| `dtor` | Include `#[dtor]` support in the `ctor` crate. | Yes |
+| `used_linker` | Enable support for `#[used(linker)]` (nightly only). | No |
+
+## Warnings
+
+Rust's philosophy is that nothing happens before or after main and this library
+explicitly subverts that. The code that runs in the `ctor` and `dtor` functions
+should be careful to limit itself to `libc` functions and code that does not
+rely on Rust's stdlib services.
+
+For example, using stdout in a `dtor` function is a guaranteed panic. Consider
+using the [`libc-print` crate](https://crates.io/crates/libc-print) for output
+to stderr/stdout during `#[ctor]` and `#[dtor]` methods. Other issues may
+involve signal processing or panic handling in that early code.
+
+Some linker configurations may cause `#[ctor]` and `#[dtor]` functions to be
+stripped from the final binary. The `used_linker` feature may prevent this, but
+is not supported outside of nightly Rust. Often, a simple `use module_with_ctor`
+is sufficient to ensure the linker does not strip the function.
+
+On some platforms, unloading of shared libraries may not actually happen until
+process exit, even if explicitly unloaded. The rules for this are arcane and
+difficult to understand. For example, thread-local storage on OSX will affect
+this (see
+[this comment](https://github.com/rust-lang/rust/issues/28794#issuecomment-368693049)).
+
+## Examples
+
+Marks the function `foo` as a module constructor, called when a static library
+is loaded or an executable is started:
+
+```rust
+ static INITED: AtomicBool = AtomicBool::new(false);
+
+ #[ctor]
+ fn foo() {
+ INITED.store(true, Ordering::SeqCst);
+ }
+```
+
+Creates a `HashMap` populated with strings when a static library is loaded or an
+executable is started (new in `0.1.7`):
+
+`static` items are equivalent to `std::sync::OnceLock`, with an automatic deref
+implementation and eager initialization at startup time. `#[ctor]` on `static`
+items requires the default `std` feature.
+
+```rust
+#[ctor]
+/// This is an immutable static, evaluated at init time
+static STATIC_CTOR: HashMap = {
+ let mut m = HashMap::new();
+ m.insert(0, "foo");
+ m.insert(1, "bar");
+ m.insert(2, "baz");
+ m
+};
+```
+
+Print a message at shutdown time. Note that Rust may have shut down some stdlib
+services at this time.
+
+```rust
+#[dtor]
+unsafe fn shutdown() {
+ // Using println or eprintln here will panic as Rust has shut down
+ libc::printf("Shutting down!\n\0".as_ptr() as *const i8);
+}
+```
+
+## Under the Hood
+
+The `#[ctor]` macro makes use of linker sections to ensure that a function is
+run at startup time.
+
+The above example translates into the following Rust code (approximately):
+
+```rust
+#[used]
+#[cfg_attr(target_os = "linux", link_section = ".init_array")]
+#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func,mod_init_funcs")]
+#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
+/* ... other platforms elided ... */
+static FOO: extern fn() = {
+ extern fn foo() { /* ... */ };
+ foo
+};
+```
+
+The `#[dtor]` macro effectively creates a constructor that calls `libc::atexit`
+with the provided function, ie roughly equivalent to:
+
+```rust
+#[ctor]
+fn dtor_atexit() {
+ libc::atexit(dtor);
+}
+```
+
+## Inspiration
+
+Idea inspired by
+[this code](https://github.com/neon-bindings/neon/blob/2277e943a619579c144c1da543874f4a7ec39879/src/lib.rs#L42)
+in the Neon project.
diff --git a/vendor/ctor/src/example.rs b/vendor/ctor/src/example.rs
new file mode 100644
index 000000000..5889c99f8
--- /dev/null
+++ b/vendor/ctor/src/example.rs
@@ -0,0 +1,111 @@
+//! This example demonstrates the various types of ctor/dtor in an executable
+//! context.
+
+#![cfg_attr(feature = "used_linker", feature(used_with_arg))]
+
+use ctor::{ctor, dtor};
+use libc_print::*;
+use std::collections::HashMap;
+
+#[ctor]
+/// This is an immutable static, evaluated at init time
+static STATIC_CTOR: HashMap = unsafe {
+ let mut m = HashMap::new();
+ _ = m.insert(0, "foo");
+ _ = m.insert(1, "bar");
+ _ = m.insert(2, "baz");
+ libc_eprintln!("STATIC_CTOR");
+ m
+};
+
+#[ctor(anonymous)]
+unsafe fn anonymous_ctor() {
+ libc_eprintln!("ctor_anonymous (#1)");
+ // We can still reference the function itself
+ let f = anonymous_ctor;
+}
+
+#[ctor(anonymous)]
+unsafe fn anonymous_ctor() {
+ libc_eprintln!("ctor_anonymous (#2)");
+}
+
+const _: () = {
+ #[ctor]
+ unsafe fn anonymous_ctor() {
+ libc_eprintln!("ctor_anonymous (#3)");
+ let f = anonymous_ctor;
+ }
+
+ #[dtor]
+ unsafe fn anonymous_dtor() {
+ libc_eprintln!("dtor_anonymous");
+ let f = anonymous_dtor;
+ }
+};
+
+#[ctor]
+#[allow(unsafe_code)]
+unsafe fn ctor() {
+ libc_eprintln!("ctor");
+ // We can still reference the function itself
+ let f = ctor;
+}
+
+#[ctor]
+#[allow(unsafe_code)]
+unsafe fn ctor_unsafe() {
+ libc_eprintln!("ctor_unsafe");
+}
+
+#[dtor]
+#[allow(unsafe_code)]
+unsafe fn dtor() {
+ libc_eprintln!("dtor");
+ // We can still reference the function itself
+ let f = dtor;
+}
+
+#[dtor]
+#[allow(unsafe_code)]
+unsafe fn dtor_unsafe() {
+ libc_eprintln!("dtor_unsafe");
+}
+
+#[dtor(anonymous)]
+unsafe fn anonymous_dtor() {
+ libc_eprintln!("dtor_anonymous (#1)");
+ let f = anonymous_dtor;
+}
+
+#[dtor(anonymous)]
+unsafe fn anonymous_dtor() {
+ libc_eprintln!("dtor_anonymous (#2");
+ let f = anonymous_dtor;
+}
+
+/// A module with a static ctor/dtor
+pub mod module {
+ use ctor::*;
+ use libc_print::*;
+
+ #[ctor]
+ pub(crate) static STATIC_CTOR: u8 = unsafe {
+ libc_eprintln!("module::STATIC_CTOR");
+ 42
+ };
+
+ #[dtor]
+ #[allow(unsafe_code)]
+ unsafe fn dtor_module() {
+ libc_eprintln!("module::dtor_module");
+ }
+}
+
+/// Executable main which demonstrates the various types of ctor/dtor.
+pub fn main() {
+ use libc_print::*;
+ libc_eprintln!("main!");
+ libc_eprintln!("STATIC_CTOR = {:?}", *STATIC_CTOR);
+ libc_eprintln!("module::STATIC_CTOR = {:?}", *module::STATIC_CTOR);
+}
diff --git a/vendor/ctor/src/lib.rs b/vendor/ctor/src/lib.rs
new file mode 100644
index 000000000..f91b78290
--- /dev/null
+++ b/vendor/ctor/src/lib.rs
@@ -0,0 +1,251 @@
+//! Procedural macro for defining global constructor/destructor functions.
+//!
+//! This provides module initialization/teardown functions for Rust (like
+//! `__attribute__((constructor))` in C/C++) for Linux, OSX, and Windows via
+//! the `#[ctor]` and `#[dtor]` macros.
+//!
+//! This library works and is regularly tested on Linux, OSX and Windows, with both `+crt-static` and `-crt-static`.
+//! Other platforms are supported but not tested as part of the automatic builds. This library will also work as expected in both
+//! `bin` and `cdylib` outputs, ie: the `ctor` and `dtor` will run at executable or library
+//! startup/shutdown respectively.
+//!
+//! This library currently requires Rust > `1.31.0` at a minimum for the
+//! procedural macro support.
+
+#![no_std]
+#![recursion_limit = "256"]
+
+#[cfg(feature = "std")]
+extern crate std;
+
+#[doc(hidden)]
+#[allow(unused)]
+pub use macros::__support;
+
+mod macros;
+
+pub use macros::features;
+
+/// Declarative forms of the `#[ctor]` and `#[dtor]` macros.
+///
+/// The declarative forms wrap and parse a proc_macro-like syntax like so, and
+/// are identical in expansion to the undecorated procedural macros. The
+/// declarative forms support the same attribute parameters as the procedural
+/// macros.
+///
+/// ```rust
+/// # mod test { use ctor::*; use libc_print::*;
+/// ctor::declarative::ctor! {
+/// #[ctor]
+/// fn foo() {
+/// libc_println!("Hello, world!");
+/// }
+/// }
+/// # }
+///
+/// // ... the above is identical to:
+///
+/// # mod test_2 { use ctor::*; use libc_print::*;
+/// #[ctor]
+/// fn foo() {
+/// libc_println!("Hello, world!");
+/// }
+/// # }
+/// ```
+pub mod declarative {
+ #[doc(inline)]
+ pub use crate::__support::ctor_parse as ctor;
+ #[doc(inline)]
+ #[cfg(feature = "dtor")]
+ pub use crate::__support::dtor_parse as dtor;
+}
+
+/// Marks a function or static variable as a library/executable constructor.
+/// This uses OS-specific linker sections to call a specific function at load
+/// time.
+///
+/// # Important notes
+///
+/// Rust does not make any guarantees about stdlib support for life-before or
+/// life-after main. This means that the `ctor` crate may not work as expected
+/// in some cases, such as when used in an `async` runtime or making use of
+/// stdlib services.
+///
+/// Multiple startup functions/statics are supported, but the invocation order
+/// is not guaranteed.
+///
+/// The `ctor` crate assumes it is available as a direct dependency, with
+/// `extern crate ctor`. If you re-export `ctor` items as part of your crate,
+/// you can use the `crate_path` parameter to redirect the macro's output to the
+/// correct crate.
+///
+/// # Attribute parameters
+///
+/// - `crate_path = ::path::to::ctor::crate`: The path to the `ctor` crate
+/// containing the support macros. If you re-export `ctor` items as part of
+/// your crate, you can use this to redirect the macro's output to the
+/// correct crate.
+/// - `used(linker)`: (Advanced) Mark the function as being used in the link
+/// phase.
+/// - `link_section = "section"`: The section to place the constructor in.
+/// - `anonymous`: Do not give the constructor a name in the generated code
+/// (allows for multiple constructors with the same name).
+/// - `priority = N`: The priority of the constructor. Higher-N-priority
+/// constructors are run last. This is not supported on all platforms.
+///
+/// # Examples
+///
+/// Print a startup message (using `libc_print` for safety):
+///
+/// ```rust
+/// # #![cfg_attr(feature="used_linker", feature(used_with_arg))]
+/// # extern crate ctor;
+/// # use ctor::*;
+/// use libc_print::std_name::println;
+///
+/// #[ctor]
+/// unsafe fn foo() {
+/// // Using libc_print which is safe in `#[ctor]`
+/// println!("Hello, world!");
+/// }
+///
+/// # fn main() {
+/// println!("main()");
+/// # }
+/// ```
+///
+/// Make changes to `static` variables:
+///
+/// ```rust
+/// # #![cfg_attr(feature="used_linker", feature(used_with_arg))]
+/// # extern crate ctor;
+/// # mod test {
+/// # use ctor::*;
+/// # use std::sync::atomic::{AtomicBool, Ordering};
+/// static INITED: AtomicBool = AtomicBool::new(false);
+///
+/// #[ctor]
+/// unsafe fn set_inited() {
+/// INITED.store(true, Ordering::SeqCst);
+/// }
+/// # }
+/// ```
+///
+/// Initialize a `HashMap` at startup time:
+///
+/// ```rust
+/// # #![cfg_attr(feature="used_linker", feature(used_with_arg))]
+/// # extern crate ctor;
+/// # mod test {
+/// # use std::collections::HashMap;
+/// # use ctor::*;
+/// #[ctor]
+/// pub static STATIC_CTOR: HashMap = unsafe {
+/// let mut m = HashMap::new();
+/// for i in 0..100 {
+/// m.insert(i, format!("x*100={}", i*100));
+/// }
+/// m
+/// };
+/// # }
+/// # pub fn main() {
+/// # assert_eq!(test::STATIC_CTOR.len(), 100);
+/// # assert_eq!(test::STATIC_CTOR[&20], "x*100=2000");
+/// # }
+/// ```
+///
+/// # Details
+///
+/// The `#[ctor]` macro makes use of linker sections to ensure that a function
+/// is run at startup time.
+///
+/// ```rust
+/// # #![cfg_attr(feature="used_linker", feature(used_with_arg))]
+/// # extern crate ctor;
+/// # mod test {
+/// # use ctor::*;
+/// #[ctor]
+///
+/// unsafe fn my_init_fn() {
+/// /* ... */
+/// }
+/// # }
+/// ```
+///
+/// The above example translates into the following Rust code (approximately):
+///
+/// ```rust
+/// # fn my_init_fn() {}
+/// #[used]
+/// #[cfg_attr(target_os = "linux", link_section = ".init_array")]
+/// #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func,mod_init_funcs")]
+/// #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
+/// /* ... other platforms elided ... */
+/// static INIT_FN: extern fn() = {
+/// extern fn init_fn() { my_init_fn(); };
+/// init_fn
+/// };
+/// ```
+///
+/// For `static` items, the macro generates a `std::sync::OnceLock` that is
+/// initialized at startup time. `#[ctor]` on `static` items requires the
+/// default `std` feature.
+///
+/// ```rust
+/// # extern crate ctor;
+/// # mod test {
+/// # use ctor::*;
+/// # use std::collections::HashMap;
+/// #[ctor]
+/// static FOO: HashMap = unsafe {
+/// let mut m = HashMap::new();
+/// for i in 0..100 {
+/// m.insert(i, format!("x*100={}", i*100));
+/// }
+/// m
+/// };
+/// # }
+/// ```
+///
+/// The above example translates into the following Rust code (approximately),
+/// which eagerly initializes the `HashMap` inside a `OnceLock` at startup time:
+///
+/// ```rust
+/// # extern crate ctor;
+/// # mod test {
+/// # use ctor::ctor;
+/// # use std::collections::HashMap;
+/// static FOO: FooStatic = FooStatic { value: ::std::sync::OnceLock::new() };
+/// struct FooStatic {
+/// value: ::std::sync::OnceLock>,
+/// }
+///
+/// impl ::core::ops::Deref for FooStatic {
+/// type Target = HashMap;
+/// fn deref(&self) -> &Self::Target {
+/// self.value.get_or_init(|| unsafe {
+/// let mut m = HashMap::new();
+/// for i in 0..100 {
+/// m.insert(i, format!("x*100={}", i*100));
+/// }
+/// m
+/// })
+/// }
+/// }
+///
+/// #[ctor]
+/// unsafe fn init_foo_ctor() {
+/// _ = &*FOO;
+/// }
+/// # }
+/// ```
+#[doc(inline)]
+#[cfg(feature = "proc_macro")]
+pub use ctor_proc_macro::ctor;
+
+/// Re-exported `#[dtor]` proc-macro from `dtor` crate.
+///
+/// See [`::dtor`] for more details.
+#[doc(inline)]
+#[cfg(feature = "dtor")]
+pub use dtor::__dtor_from_ctor as dtor;
diff --git a/vendor/ctor/src/macros/mod.rs b/vendor/ctor/src/macros/mod.rs
new file mode 100644
index 000000000..6fd450c8b
--- /dev/null
+++ b/vendor/ctor/src/macros/mod.rs
@@ -0,0 +1,642 @@
+#[doc(hidden)]
+#[allow(unused)]
+pub mod __support {
+ /// Return type for the constructor. Why is this needed?
+ ///
+ /// On Windows, `.CRT$XIA` … `.CRT$XIZ` constructors are required to return a `usize` value. We don't know
+ /// if the user is putting this function into a retval-requiring section or a non-retval section, so we
+ /// just return a `usize` value which is always valid and just ignored if not needed.
+ ///
+ /// Miri is pedantic about this, so we just return `()` if we're running under miri.
+ ///
+ /// See
+ #[cfg(all(windows, not(miri)))]
+ pub type CtorRetType = usize;
+ #[cfg(any(not(windows), miri))]
+ pub type CtorRetType = ();
+
+ pub use crate::__ctor_call as ctor_call;
+ pub use crate::__ctor_entry as ctor_entry;
+ pub use crate::__ctor_link_section as ctor_link_section;
+ pub use crate::__ctor_link_section_attr as ctor_link_section_attr;
+ pub use crate::__ctor_parse as ctor_parse;
+ pub use crate::__dtor_entry as dtor_entry;
+ pub use crate::__dtor_parse as dtor_parse;
+ pub use crate::__if_has_feature as if_has_feature;
+ pub use crate::__if_unsafe as if_unsafe;
+ pub use crate::__get_priority as get_priority;
+ pub use crate::__unify_features as unify_features;
+}
+
+/// Parse a `#[ctor]`-annotated item as if it were a proc-macro.
+///
+/// This macro supports both the `fn` and `static` forms of the `#[ctor]`
+/// attribute, including attribute parameters.
+///
+/// ```rust
+/// # #[cfg(any())] // disabled due to code sharing between ctor/dtor
+/// # mod test {
+/// # use ctor::declarative::ctor;
+/// ctor! {
+/// /// Create a ctor with a link section
+/// # #[cfg(any())]
+/// #[ctor(link_section = ".ctors")]
+/// unsafe fn foo() { /* ... */ }
+/// }
+///
+/// ctor! {
+/// #[ctor]
+/// # #[cfg(any())]
+/// static FOO: std::collections::HashMap = unsafe {
+/// let mut m = std::collections::HashMap::new();
+/// m.insert(1, "foo".to_string());
+/// m
+/// };
+/// }
+/// # }
+/// ```
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __ctor_parse {
+ (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub ( $($extra:tt)* ) $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub($($extra)*)], item=$($item)*);
+ };
+ (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub], item=$($item)*);
+ };
+ (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* fn $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=fn $($item)*);
+ };
+ (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* unsafe $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=unsafe $($item)*);
+ };
+ (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* static $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=static $($item)*);
+ };
+ // Reorder attributes that aren't `#[ctor]`
+ (#[$imeta:meta] $($rest:tt)*) => {
+ $crate::__support::ctor_parse!(__reorder__(#[$imeta],), $($rest)*);
+ };
+ (__reorder__($(#[$imeta:meta],)*), #[ctor $(($($meta:tt)*))?] $($rest:tt)*) => {
+ $crate::__support::ctor_parse!(#[ctor $(($($meta)*))?] $(#[$imeta])* $($rest)*);
+ };
+ (__reorder__($(#[$imeta:meta],)*), #[$imeta2:meta] $($rest:tt)*) => {
+ $crate::__support::ctor_parse!(__reorder__($(#[$imeta],)*#[$imeta2],), $($rest)*);
+ };
+}
+
+/// Parse a `#[dtor]`-annotated item as if it were a proc-macro.
+///
+/// ```rust
+/// # #[cfg(any())] mod test {
+/// dtor! {
+/// #[dtor]
+/// unsafe fn foo() { /* ... */ }
+/// }
+/// # }
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __dtor_parse {
+ (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub ( $($extra:tt)* ) $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub($($extra)*)], item=$($item)*);
+ };
+ (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub], item=$($item)*);
+ };
+ (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* fn $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=fn $($item)*);
+ };
+ (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* unsafe $($item:tt)*) => {
+ $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=unsafe $($item)*);
+ };
+ // Reorder attributes that aren't `#[dtor]`
+ (#[$imeta:meta] $($rest:tt)*) => {
+ $crate::__support::dtor_parse!(__reorder__(#[$imeta],), $($rest)*);
+ };
+ (__reorder__($(#[$imeta:meta],)*), #[dtor $(($($meta:tt)*))?] $($rest:tt)*) => {
+ $crate::__support::dtor_parse!(#[dtor $(($($meta)*))?] $(#[$imeta])* $($rest)*);
+ };
+ (__reorder__($(#[$imeta:meta],)*), #[$imeta2:meta] $($rest:tt)*) => {
+ $crate::__support::dtor_parse!(__reorder__($(#[$imeta],)*#[$imeta2],), $($rest)*);
+ };
+}
+
+/// A macro that generates the appropriate feature extraction macros.
+macro_rules! declare_features {
+ ( $(#[doc = $doc1:literal])* crate = $crate_features:tt; $(#[doc = $doc2:literal])* attr = $attrs:tt; ) => {
+ declare_features!( __ crate $crate_features );
+ };
+
+ ( __ crate [$(
+ $( #[doc = $doc:literal] )*
+ $feature_name:ident $feature_name_str:literal = $feature_include_macro:ident ;
+ )*] ) => {
+ /// # Crate features
+ ///
+ $(
+ #[doc = concat!("", stringify!($feature_name), ": ")]
+ $( #[doc = $doc] )*
+ #[doc = "\n"]
+ )*
+ pub mod features {
+ }
+
+ $(
+ #[doc(hidden)]
+ #[macro_export]
+ #[cfg(feature = $feature_name_str)]
+ macro_rules! $feature_include_macro {
+ ($true:item $false:item) => {
+ $true
+ };
+ }
+
+ #[doc(hidden)]
+ #[macro_export]
+ #[cfg(not(feature = $feature_name_str))]
+ macro_rules! $feature_include_macro {
+ ($true:item $false:item) => {
+ $false
+ };
+ }
+ )*
+ };
+}
+
+declare_features!(
+ /// Crate features: name/name as string/include macro.
+ crate = [
+ /// Enable support for the standard library. This is required for static ctor variables, but not for functions.
+ std "std" = __include_std_feature;
+ /// Mark all ctor functions with `used(linker)`.
+ used_linker "used_linker" = __include_used_linker_feature;
+ /// Enable support for the proc-macro `#[ctor]` and `#[dtor]` attributes.
+ proc_macro "proc_macro" = __include_proc_macro_feature;
+ /// Do not warn when a ctor or dtor is missing the `unsafe` keyword.
+ __no_warn_on_missing_unsafe "__no_warn_on_missing_unsafe" = __include_no_warn_on_missing_unsafe_feature;
+ ];
+
+ /// Attributes.
+ attr = [
+ /// Marks a ctor/dtor as unsafe. This will become a warning in 1.0.
+ unsafe = [unsafe];
+ /// Place the initialization function pointer in a custom link section. This may cause the initialization function
+ /// to fail to run or run earlier or later than other `ctor` functions.
+ link_section = [link_section($section:literal)];
+ /// Specify a custom crate path for the `ctor` crate. Used when re-exporting the ctor macro.
+ crate_path = [crate_path = $path:path];
+ /// Make the ctor function anonymous.
+ anonymous = [anonymous];
+ /// Mark this function with `used(linker)`.
+ used_linker = [used(linker)];
+ /// Set the ctor priority to a given value.
+ priority = [priority = $priority:literal];
+ ];
+);
+
+/// Extract #[ctor/dtor] attribute parameters and crate features and turn them
+/// into a unified feature array.
+///
+/// Supported attributes:
+///
+/// - `used(linker)` -> crate feature: `used_linker`
+/// - `std` -> crate feature: `std`
+/// - `link_section = ...` -> feature: `(link_section = ...)`
+/// - `crate_path = ...` -> feature: `(crate_path = ...)`
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __unify_features {
+ // Entry
+ (next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__support::unify_features!(std, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
+ };
+
+ // Add std feature if cfg(feature="std")
+ (std, next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__include_std_feature!(
+ $crate::__support::unify_features!(used_linker, next=$next_macro, meta=[$($meta)*], features=[std,$($features)*], $($rest)*);
+ $crate::__support::unify_features!(used_linker, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
+ );
+ };
+
+ // Add used_linker feature if cfg(feature="used_linker")
+ (used_linker, next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__include_used_linker_feature!(
+ $crate::__support::unify_features!(__no_warn_on_missing_unsafe, next=$next_macro, meta=[$($meta)*], features=[used_linker,$($features)*], $($rest)*);
+ $crate::__support::unify_features!(__no_warn_on_missing_unsafe, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
+ );
+ };
+ // Add __no_warn_on_missing_unsafe feature if cfg(feature="__no_warn_on_missing_unsafe")
+ (__no_warn_on_missing_unsafe, next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__include_no_warn_on_missing_unsafe_feature!(
+ $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($meta)*], features=[__no_warn_on_missing_unsafe,$($features)*], $($rest)*);
+ $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
+ );
+ };
+
+ // Parse meta into features
+ (continue, next=$next_macro:path, meta=[used(linker) $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[used_linker,$($features)*], $($rest)*);
+ };
+ (continue, next=$next_macro:path, meta=[link_section = $section:tt $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[(link_section=($section)),$($features)*], $($rest)*);
+ };
+ (continue, next=$next_macro:path, meta=[crate_path = $path:path $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[(crate_path=$path),$($features)*], $($rest)*);
+ };
+ (continue, next=$next_macro:path, meta=[anonymous $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[anonymous,$($features)*], $($rest)*);
+ };
+ (continue, next=$next_macro:path, meta=[priority = $priority:literal $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
+ $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[(priority=($priority)),$($features)*], $($rest)*);
+ };
+ (continue, next=$next_macro:path, meta=[$unknown_meta:meta $($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
+ compile_error!(concat!("Unknown attribute parameter: ", stringify!($unknown_meta)));
+ };
+
+ (continue, next=$next_macro:path, meta=[], features=[$($features:tt)*], $($rest:tt)*) => {
+ $next_macro!(features=[$($features)*], $($rest)*);
+ };
+}
+
+/// If the features array contains the requested feature, generates `if_true`, else `if_false`.
+///
+/// This macro matches the features recursively.
+///
+/// Example: `[(link_section = ".ctors") , used_linker , __warn_on_missing_unsafe ,]`
+#[doc(hidden)]
+#[macro_export]
+#[allow(unknown_lints, edition_2024_expr_fragment_specifier)]
+macro_rules! __if_has_feature {
+ (std, [std, $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
+ (used_linker, [used_linker, $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
+ (__no_warn_on_missing_unsafe, [__no_warn_on_missing_unsafe, $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
+ (anonymous, [anonymous, $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
+ ((link_section(c)), [(link_section=($section:literal)), $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { #[link_section = $section] $($if_true)* };
+ ((priority(p)), [(priority=($priority:literal)), $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
+
+ // Fallback rules
+ ($anything:tt, [$x:ident, $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $crate::__support::if_has_feature!($anything, [$($rest)*], {$($if_true)*}, {$($if_false)*}); };
+ ($anything:tt, [($x:ident=$y:tt), $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $crate::__support::if_has_feature!($anything, [$($rest)*], {$($if_true)*}, {$($if_false)*}); };
+ ($anything:tt, [], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_false)* };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __if_unsafe {
+ (, {$($if_unsafe:tt)*}, {$($if_safe:tt)*}) => { $($if_safe)* };
+ (unsafe, {$($if_unsafe:tt)*}, {$($if_safe:tt)*}) => { $($if_unsafe)* };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __get_priority {
+ ($next:path, $args:tt, [(priority=($priority:literal)), $($rest:tt)*]) => { $next!($args, (".", $priority)); };
+ ($next:path, $args:tt, [$x:ident, $($rest:tt)*]) => { $crate::__support::get_priority!($next, $args, [$($rest)*]); };
+ ($next:path, $args:tt, [($x:ident=$y:tt), $($rest:tt)*]) => { $crate::__support::get_priority!($next, $args, [$($rest)*]); };
+ ($next:path, $args:tt, []) => { $next!($args, ("")); };
+}
+
+#[doc(hidden)]
+#[macro_export]
+#[allow(unknown_lints, edition_2024_expr_fragment_specifier)]
+macro_rules! __ctor_entry {
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=unsafe fn $($item:tt)*) => {
+ $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=unsafe, item=fn $($item)*);
+ };
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=fn $($item:tt)*) => {
+ $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=, item=fn $($item)*);
+ };
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=static $ident:ident : $ty:ty = $(unsafe)? $({ $lit:literal })? $($lit2:literal)? ;) => {
+ compile_error!(concat!("Use `const ", stringify!($ident), " = ", stringify!($($lit)?$($lit2)?), ";` or `static ", stringify!($ident), ": ", stringify!($ty), " = ", stringify!($($lit)?$($lit2)?), ";` instead"));
+ };
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=static $ident:ident : $ty:ty = unsafe $($item:tt)*) => {
+ $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=unsafe, item=static $ident: $ty = $($item)*);
+ };
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=static $ident:ident : $ty:ty = $($item:tt)*) => {
+ $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=, item=static $ident: $ty = $($item)*);
+ };
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
+ $crate::__support::if_has_feature!(anonymous, $features, {
+ $crate::__support::ctor_entry!(unnamed, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
+ }, {
+ $crate::__support::ctor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
+ });
+ };
+ (unnamed,features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
+ const _: () = {
+ $crate::__support::ctor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
+ };
+ };
+ (named, features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
+ $(#[$fnmeta])*
+ #[allow(unused)]
+ $($vis)* $($unsafe)? fn $ident() {
+ #[allow(unsafe_code)]
+ {
+ $crate::__support::if_unsafe!($($unsafe)?, {}, {
+ $crate::__support::if_has_feature!( __warn_on_missing_unsafe, $features, {
+ #[deprecated="ctor deprecation note:\n\n \
+ Use of #[ctor] without `unsafe fn` is deprecated. As code execution before main\n\
+ is unsupported by most Rust runtime functions, these functions must be marked\n\
+ `unsafe`."]
+ const fn ctor_without_unsafe_is_deprecated() {}
+ #[allow(unused)]
+ static UNSAFE_WARNING: () = ctor_without_unsafe_is_deprecated();
+ }, {});
+ });
+
+ $crate::__support::ctor_call!(
+ features=$features,
+ { unsafe { $ident(); } }
+ );
+ }
+
+ #[cfg(target_family = "wasm")]
+ {
+ static __CTOR__INITILIZED: ::core::sync::atomic::AtomicBool = ::core::sync::atomic::AtomicBool::new(false);
+ if __CTOR__INITILIZED.swap(true, ::core::sync::atomic::Ordering::Relaxed) {
+ return;
+ }
+ }
+
+ $block
+ }
+ };
+ (features=$features:tt, imeta=$(#[$imeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=static $ident:ident : $ty:ty = $block:block;) => {
+ $crate::__support::if_has_feature!(std, $features, {
+ $(#[$imeta])*
+ $($vis)* static $ident: $ident::Static<$ty> = $ident::Static::<$ty> {
+ _storage: {
+ $crate::__support::ctor_call!(
+ features=$features,
+ { _ = &*$ident; }
+ );
+
+ ::std::sync::OnceLock::new()
+ }
+ };
+
+ impl ::core::ops::Deref for $ident::Static<$ty> {
+ type Target = $ty;
+ fn deref(&self) -> &$ty {
+ fn init() -> $ty $block
+
+ self._storage.get_or_init(move || {
+ init()
+ })
+ }
+ }
+
+ #[doc(hidden)]
+ #[allow(non_upper_case_globals, non_snake_case)]
+ #[allow(unsafe_code)]
+ mod $ident {
+ $crate::__support::if_unsafe!($($unsafe)?, {}, {
+ $crate::__support::if_has_feature!( __no_warn_on_missing_unsafe, $features, {
+ #[deprecated="ctor deprecation note:\n\n \
+ Use of #[ctor] without `unsafe { ... }` is deprecated. As code execution before main\n\
+ is unsupported by most Rust runtime functions, these functions must be marked\n\
+ `unsafe`."]
+ const fn ctor_without_unsafe_is_deprecated() {}
+ #[allow(unused)]
+ static UNSAFE_WARNING: () = ctor_without_unsafe_is_deprecated();
+ }, {});
+ });
+
+ #[allow(non_camel_case_types, unreachable_pub)]
+ pub struct Static {
+ pub _storage: ::std::sync::OnceLock
+ }
+ }
+ }, {
+ compile_error!("`#[ctor]` on `static` items requires the `std` feature");
+ });
+ };
+}
+
+// Code note:
+
+// You might wonder why we don't use `__attribute__((destructor))`/etc for
+// dtor. Unfortunately mingw doesn't appear to properly support section-based
+// hooks for shutdown, ie:
+
+// https://github.com/Alexpux/mingw-w64/blob/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-crt/crt/crtdll.c
+
+// In addition, OSX has removed support for section-based shutdown hooks after
+// warning about it for a number of years:
+
+// https://reviews.llvm.org/D45578
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __dtor_entry {
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=fn $ident:ident() $block:block) => {
+ $crate::__support::dtor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=, item=fn $ident() $block);
+ };
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=unsafe fn $ident:ident() $block:block) => {
+ $crate::__support::dtor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=unsafe, item=fn $ident() $block);
+ };
+ (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
+ $crate::__support::if_has_feature!(anonymous, $features, {
+ $crate::__support::dtor_entry!(unnamed, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
+ }, {
+ $crate::__support::dtor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
+ });
+ };
+ (unnamed, features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
+ const _: () = {
+ $crate::__support::dtor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
+ };
+ };
+ (named, features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
+ $(#[$fnmeta])*
+ #[allow(unused)]
+ $($vis)* $($unsafe)? fn $ident() {
+ #[allow(unsafe_code)]
+ {
+ $crate::__support::if_unsafe!($($unsafe)?, {}, {
+ $crate::__support::if_has_feature!( __warn_on_missing_unsafe, $features, {
+ #[deprecated="dtor deprecation note:\n\n \
+ Use of #[dtor] without `unsafe fn` is deprecated. As code execution after main\n\
+ is unsupported by most Rust runtime functions, these functions must be marked\n\
+ `unsafe`."]
+ const fn dtor_without_unsafe_is_deprecated() {}
+ #[allow(unused)]
+ static UNSAFE_WARNING: () = dtor_without_unsafe_is_deprecated();
+ }, {});
+ });
+
+ $crate::__support::ctor_call!(
+ features=$features,
+ { unsafe { do_atexit(__dtor); } }
+ );
+
+ $crate::__support::ctor_link_section!(
+ exit,
+ features=$features,
+ (""),
+
+ /*unsafe*/ extern "C" fn __dtor(
+ #[cfg(target_vendor = "apple")] _: *const u8
+ ) { unsafe { $ident() } }
+ );
+
+ #[cfg(not(target_vendor = "apple"))]
+ #[inline(always)]
+ unsafe fn do_atexit(cb: unsafe extern fn()) {
+ /*unsafe*/ extern "C" {
+ fn atexit(cb: unsafe extern fn());
+ }
+ unsafe {
+ atexit(cb);
+ }
+ }
+
+ // For platforms that have __cxa_atexit, we register the dtor as scoped to dso_handle
+ #[cfg(target_vendor = "apple")]
+ #[inline(always)]
+ unsafe fn do_atexit(cb: /*unsafe*/ extern "C" fn(_: *const u8)) {
+ /*unsafe*/ extern "C" {
+ static __dso_handle: *const u8;
+ fn __cxa_atexit(cb: /*unsafe*/ extern "C" fn(_: *const u8), arg: *const u8, dso_handle: *const u8);
+ }
+ unsafe {
+ __cxa_atexit(cb, ::core::ptr::null(), __dso_handle);
+ }
+ }
+ }
+
+ $block
+ }
+ };
+}
+
+/// Annotate a block with its appropriate link section.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __ctor_call {
+ (features=$features:tt, { $($block:tt)+ } ) => {
+ $crate::__support::get_priority!($crate::__support::ctor_call, [features=$features, { $($block)+ }], $features);
+ };
+ ([features=$features:tt, { $($block:tt)+ }], $priority:tt) => {
+ $crate::__support::ctor_link_section!(
+ array,
+ features=$features,
+ $priority,
+
+ #[allow(non_upper_case_globals, non_snake_case)]
+ #[doc(hidden)]
+ static f: /*unsafe*/ extern "C" fn() -> $crate::__support::CtorRetType =
+ {
+ $crate::__support::ctor_link_section!(
+ startup,
+ features=$features,
+ (""),
+
+ #[allow(non_snake_case)]
+ /*unsafe*/ extern "C" fn f() -> $crate::__support::CtorRetType {
+ $($block)+;
+ ::core::default::Default::default()
+ }
+ );
+
+ f
+ };
+ );
+ }
+}
+
+/// Annotate a block with its appropriate link section.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __ctor_link_section {
+ ($section:ident, features=$features:tt, $priority:tt, $($block:tt)+) => {
+ $crate::__support::if_has_feature!(used_linker, $features, {
+ $crate::__support::ctor_link_section_attr!($section, $features, used(linker), $priority, $($block)+);
+ }, {
+ $crate::__support::ctor_link_section_attr!($section, $features, used, $priority, $($block)+);
+ });
+
+ #[cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "illumos",
+ target_os = "haiku",
+ target_vendor = "apple",
+ target_family = "wasm",
+ target_arch = "xtensa",
+ target_os = "windows"
+ )))]
+ compile_error!("#[ctor]/#[dtor] is not supported on the current target");
+ }
+}
+
+/// Apply either the default cfg-based link section attributes, or
+/// the overridden link_section attribute.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __ctor_link_section_attr {
+ (array, $features:tt, $used:meta, ($($priority:tt)*), $item:item) => {
+ $crate::__support::if_has_feature!((priority(p)), $features, {
+ #[cfg(target_vendor="apple")]
+ const _: () = {
+ #[deprecated(note = "The priority parameter is not supported on target_vendor = \"apple\"")]
+ const fn ctor_priority_unsupported() {}
+ ctor_priority_unsupported();
+ };
+ }, {});
+ $crate::__support::if_has_feature!((link_section(c)), $features, {
+ #[allow(unsafe_code)]
+ #[$used]
+ $item
+ }, {
+ #[allow(unsafe_code)]
+ $crate::__support::ctor_link_section_attr!(
+ [[any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "illumos",
+ target_os = "haiku",
+ target_family = "wasm"
+ ), (concat!(".init_array", $($priority)*))],
+ [target_arch = "xtensa", (concat!(".ctors", $($priority)*))],
+ // macOS/Darwin do not support the priority parameter in the link section
+ [target_vendor = "apple", (concat!("__DATA,__mod_init_func,mod_init_funcs"))],
+ [all(target_os = "windows", any(target_env = "gnu", target_env = "msvc")), (concat!(".CRT$XCU", $($priority)*))],
+ // cygwin support: rustc 1.85 does not like the explicit target_os = "cygwin" condition (https://github.com/mmastrac/rust-ctor/issues/356)
+ // We can work around this by excluding gnu and msvc target envs
+ [all(target_os = "windows", not(any(target_env = "gnu", target_env = "msvc"))), (concat!(".ctors", $($priority)*))]
+ ],
+ #[$used]
+ $item
+ );
+ });
+ };
+ (startup, $features:tt, $used:meta, $priority:tt, $item:item) => {
+ #[cfg(not(clippy))]
+ $crate::__support::ctor_link_section_attr!([[any(target_os = "linux", target_os = "android"), (".text.startup")]], $item);
+
+ #[cfg(clippy)]
+ $item
+ };
+ (exit, $features:tt, $used:meta, $priority:tt, $item:item) => {
+ #[cfg(not(clippy))]
+ $crate::__support::ctor_link_section_attr!([[any(target_os = "linux", target_os = "android"), (".text.exit")]], $item);
+
+ #[cfg(clippy)]
+ $item
+ };
+ ([$( [$cond:meta, ($($literal:tt)*) ] ),+], $item:item) => {
+ $( #[cfg_attr($cond, link_section = $($literal)*)] )+
+ $item
+ };
+}