rebreak-monorepo/apps/rebreak-native/plugins/with-fmt-consteval-fix.js

145 lines
5.7 KiB
JavaScript

/* eslint-disable @typescript-eslint/no-var-requires */
/**
* Workaround für Xcode 16 + RN 0.79 + fmt 11.0.2:
* "Call to consteval function 'fmt::basic_format_string<...>' is not a constant expression"
*
* Grund: fmt/include/fmt/base.h definiert FMT_USE_CONSTEVAL UNCONDITIONAL —
* kein `#ifndef`-Guard. Daher hilft `-DFMT_USE_CONSTEVAL=0` als Compiler-Flag
* NICHT — fmt's eigener Header überschreibt es.
*
* Wir patchen daher direkt die Source-Datei nach `pod install`:
* - In ios/Pods/fmt/include/fmt/base.h einen Override-Block einfügen, der
* nach fmt's eigener Detection FMT_USE_CONSTEVAL auf 0 zwingt.
*
* Wirkung:
* - basic_format_string-Konstruktor ist nicht mehr consteval, sondern constexpr
* - FMT_STRING("{}{}") expansion compiliert wieder unter Apple Clang 16+
*
* Idempotent — markiert die Patch-Stelle mit einem Magic-Comment.
*/
const { withDangerousMod } = require('@expo/config-plugins');
const fs = require('fs');
const path = require('path');
const PATCH_MARKER = '/* REBREAK_FMT_CONSTEVAL_FIX */';
const SOURCE_PATCH = `
${PATCH_MARKER}
// Xcode 16 + Apple Clang 16+ haben einen consteval-Bug der fmt's
// FMT_STRING-basierte format_to-Calls bricht. Wir zwingen daher
// FMT_USE_CONSTEVAL=0 nach fmt's eigener Detection.
#undef FMT_USE_CONSTEVAL
#define FMT_USE_CONSTEVAL 0
#undef FMT_CONSTEVAL
#define FMT_CONSTEVAL
#undef FMT_CONSTEXPR20
#define FMT_CONSTEXPR20
${PATCH_MARKER}
`;
function patchFmtBaseHeader(podsDir) {
const baseHeader = path.join(podsDir, 'fmt', 'include', 'fmt', 'base.h');
if (!fs.existsSync(baseHeader)) {
console.warn('[with-fmt-consteval-fix] fmt/base.h not found at', baseHeader);
return false;
}
let content = fs.readFileSync(baseHeader, 'utf-8');
if (content.includes(PATCH_MARKER)) {
return false; // schon gepatcht
}
// Patch nach der Detection-Block einfügen, vor dem `#if FMT_USE_CONSTEVAL`
// Suche nach dem End-of-Detection (line endet mit `#endif` direkt vor
// `#if FMT_USE_CONSTEVAL`).
const anchor = '#if FMT_USE_CONSTEVAL';
const idx = content.indexOf(anchor);
if (idx === -1) {
console.warn('[with-fmt-consteval-fix] anchor not found in base.h');
return false;
}
// Patch direkt vor dem anchor einfügen
content = content.slice(0, idx) + SOURCE_PATCH + '\n' + content.slice(idx);
fs.writeFileSync(baseHeader, content);
console.log('[with-fmt-consteval-fix] patched', baseHeader);
return true;
}
module.exports = function withFmtConstevalFix(config) {
return withDangerousMod(config, [
'ios',
async (cfg) => {
const podsDir = path.join(cfg.modRequest.platformProjectRoot, 'Pods');
// Wenn Pods/ noch nicht existiert (= prebuild Phase, vor pod install):
// Patch wird automatisch beim nächsten Run angewendet sobald Pods da sind.
// Damit der User aber NICHT manuell nachpatchen muss, packen wir den
// Patch zusätzlich in einen Podfile-pre_install-Hook der bei JEDEM
// pod install läuft (auch beim ersten).
if (fs.existsSync(podsDir)) {
patchFmtBaseHeader(podsDir);
}
// Podfile pre_install-Hook injizieren — patched die fmt-Source bei
// jedem pod install (nachdem Pods/fmt/ bereits gedownloaded ist).
const podfilePath = path.join(cfg.modRequest.platformProjectRoot, 'Podfile');
let podfile = fs.readFileSync(podfilePath, 'utf-8');
// Alten Patch (frühere Plugin-Version) entfernen
const oldPatchRegex = /\s*# ─── Rebreak: fmt consteval fix[\s\S]*?# ───────────────────────────────────────────────────────/g;
podfile = podfile.replace(oldPatchRegex, '');
const oldPostInstallPatchRegex = /\s*# ═══ Rebreak: fmt consteval source-patch[\s\S]*?# ═══════════════════════════════════════════════════════/g;
podfile = podfile.replace(oldPostInstallPatchRegex, '');
// Neuen pre_install-Hook injizieren (vor `target` block).
// Wir patchen die fmt/base.h-Datei direkt im pre_install — pod install
// hat dann zu dem Zeitpunkt schon den Pod gedownloaded.
const PRE_INSTALL = `
# ═══ Rebreak: fmt consteval source-patch (Xcode 16 + RN 0.79) ═══
pre_install do |installer|
fmt_base_h = File.join(installer.sandbox.root, 'fmt', 'include', 'fmt', 'base.h')
if File.exist?(fmt_base_h)
content = File.read(fmt_base_h)
marker = '/* REBREAK_FMT_CONSTEVAL_FIX */'
unless content.include?(marker)
patch = <<~PATCH
#{marker}
// Xcode 16 + Apple Clang 16+ consteval-Bug Workaround
#undef FMT_USE_CONSTEVAL
#define FMT_USE_CONSTEVAL 0
#undef FMT_CONSTEVAL
#define FMT_CONSTEVAL
#undef FMT_CONSTEXPR20
#define FMT_CONSTEXPR20
#{marker}
PATCH
anchor = '#if FMT_USE_CONSTEVAL'
if content.include?(anchor)
content = content.sub(anchor, patch + "\\n" + anchor)
File.write(fmt_base_h, content)
Pod::UI.puts " -> Patched fmt/base.h with consteval workaround".green
end
end
end
end
# ═══════════════════════════════════════════════════════
`;
// Inject vor dem ersten `target` block (Top-level)
const targetMatch = podfile.match(/^target\s+['"][^'"]+['"]\s+do/m);
if (targetMatch) {
const insertAt = targetMatch.index;
podfile = podfile.slice(0, insertAt) + PRE_INSTALL + '\n' + podfile.slice(insertAt);
} else {
// Fallback: ans Ende anhängen
podfile += PRE_INSTALL;
}
fs.writeFileSync(podfilePath, podfile);
return cfg;
},
]);
};