mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-30 18:48:30 +01:00
Build #9 proved `-weak_framework SwiftUICore` does NOT bypass the allowed-client check, and applying it to the tvOS app target regressed tvOS — reverted that plugin (withSwiftUICoreWeakLink). Confirmed root cause from build #8/#9 logs: both iOS jobs fail at the app *executable* link (`Ld … Streamyfin`), not at any pod. SwiftUI was split into SwiftUI + SwiftUICore on iOS 26; the SwiftUI pods emit a `-framework SwiftUICore` autolink directive that, under use_frameworks :static, is inherited by the app's static link, and the app isn't an allowed client of the private SwiftUICore.tbd. Fix: in the pod post_install, compile pods with `-Xfrontend -disable-autolink-framework -Xfrontend SwiftUICore` so they stop emitting that direct autolink. SwiftUICore symbols then resolve through SwiftUI's re-export (SwiftUI.tbd re-exports SwiftUICore). Scoped to phone (ENV['EXPO_TV'] != '1') to leave the green tvOS build untouched. Also harden scripts/ios/build-ios.ts: displayBuildError now surfaces the "Undefined symbols for architecture …" linker block, which the error:-only pattern filter was swallowing (so unsigned-build failures show the real symbol).
106 lines
4.5 KiB
JavaScript
106 lines
4.5 KiB
JavaScript
const { withPodfile } = require("expo/config-plugins");
|
||
|
||
const PATCH_START = "## >>> runtime-framework headers";
|
||
const PATCH_END = "## <<< runtime-framework headers";
|
||
|
||
const EXTRA_HDRS = [
|
||
`\${PODS_CONFIGURATION_BUILD_DIR}/React-RuntimeApple/React_RuntimeApple.framework/Headers`,
|
||
`\${PODS_CONFIGURATION_BUILD_DIR}/React-RuntimeCore/React_RuntimeCore.framework/Headers`,
|
||
`\${PODS_CONFIGURATION_BUILD_DIR}/React-jserrorhandler/React_jserrorhandler.framework/Headers`,
|
||
`\${PODS_CONFIGURATION_BUILD_DIR}/React-jsinspector/jsinspector_modern.framework/Headers`,
|
||
`\${PODS_CONFIGURATION_BUILD_DIR}/React-runtimescheduler/React_runtimescheduler.framework/Headers`,
|
||
`\${PODS_CONFIGURATION_BUILD_DIR}/React-performancetimeline/React_performancetimeline.framework/Headers`,
|
||
`\${PODS_CONFIGURATION_BUILD_DIR}/React-rendererconsistency/React_rendererconsistency.framework/Headers`,
|
||
];
|
||
|
||
function buildPatch() {
|
||
return [
|
||
PATCH_START,
|
||
" extra_hdrs = [",
|
||
...EXTRA_HDRS.map((h) => ` "${h}",`),
|
||
" ]",
|
||
"",
|
||
" installer.pods_project.targets.each do |t|",
|
||
" t.build_configurations.each do |cfg|",
|
||
" cfg.build_settings['HEADER_SEARCH_PATHS'] ||= '$(inherited)'",
|
||
" cfg.build_settings['HEADER_SEARCH_PATHS'] << \" #{extra_hdrs.join(' ')}\"",
|
||
" cfg.build_settings['CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES'] = 'YES'",
|
||
" # iOS 26 / Xcode 26: SwiftUI was split into SwiftUI + SwiftUICore. The SwiftUI",
|
||
" # pods (ExpoUI, glass-effect, glass-poster, …) emit a `-framework SwiftUICore`",
|
||
" # autolink directive that, under use_frameworks :static, flows into the app",
|
||
" # executable's link. The app isn't an allowed client of the private",
|
||
" # SwiftUICore.tbd → `cannot link directly with 'SwiftUICore'`. Dropping that one",
|
||
" # autolink at the Swift frontend lets the symbols resolve via SwiftUI's",
|
||
" # re-export instead. Phone-only — tvOS links fine and must stay untouched.",
|
||
" if ENV['EXPO_TV'] != '1'",
|
||
" cfg.build_settings['OTHER_SWIFT_FLAGS'] ||= '$(inherited)'",
|
||
" cfg.build_settings['OTHER_SWIFT_FLAGS'] << ' -Xfrontend -disable-autolink-framework -Xfrontend SwiftUICore'",
|
||
" end",
|
||
" end",
|
||
" end",
|
||
"",
|
||
" # Safely patch RCTThirdPartyComponentsProvider.mm to avoid startup crash on unlinked Fabric components",
|
||
' filepath = "#{installer.sandbox.root}/../build/generated/ios/ReactCodegen/RCTThirdPartyComponentsProvider.mm"',
|
||
" if File.exist?(filepath)",
|
||
" content = File.read(filepath)",
|
||
" if content =~ /thirdPartyComponents = @\\{([\\s\\S]*?)\\};/",
|
||
" entries = $1",
|
||
' new_code = "NSMutableDictionary *dict = [NSMutableDictionary dictionary];\\n"',
|
||
' new_code += " Class cls;\\n"',
|
||
" entries.each_line do |line|",
|
||
" line = line.strip",
|
||
" next if line.empty?",
|
||
' if line =~ /@\\"(.*?)\\":\\s*NSClassFromString\\(@\\"(.*?)\\"\\),?(.*)/',
|
||
" key = $1",
|
||
" val = $2",
|
||
" comment = $3",
|
||
' new_code += " cls = NSClassFromString(@\\"#{val}\\"); if (cls) dict[@\\"#{key}\\"] = cls;#{comment}\\n"',
|
||
" else",
|
||
' new_code += " // #{line}\\n"',
|
||
" end",
|
||
" end",
|
||
' new_code += " thirdPartyComponents = dict;"',
|
||
" content = content.sub(/thirdPartyComponents = @\\{[\\s\\S]*?\\};/, new_code)",
|
||
" File.write(filepath, content)",
|
||
' puts "✅ Patched RCTThirdPartyComponentsProvider.mm for safety"',
|
||
" end",
|
||
" end",
|
||
PATCH_END,
|
||
].join("\n");
|
||
}
|
||
|
||
module.exports = function withRuntimeFrameworkHeaders(config) {
|
||
return withPodfile(config, (config) => {
|
||
let podfile = config.modResults.contents;
|
||
|
||
// 1️⃣ ensure there's a post_install block
|
||
if (!/^\s*post_install\s+do\s+\|installer\|/m.test(podfile)) {
|
||
podfile += `
|
||
|
||
post_install do |installer|
|
||
end
|
||
`;
|
||
}
|
||
|
||
const patch = buildPatch();
|
||
|
||
if (podfile.includes(PATCH_START)) {
|
||
// 🔄 update existing patch
|
||
podfile = podfile.replace(
|
||
new RegExp(`${PATCH_START}[\\s\\S]*?${PATCH_END}`),
|
||
patch,
|
||
);
|
||
} else {
|
||
// ➕ insert right after the post_install opening line
|
||
podfile = podfile.replace(
|
||
/^\s*post_install\s+do\s+\|installer\|.*$/m,
|
||
(match) => `${match}\n\n${patch}`,
|
||
);
|
||
}
|
||
|
||
console.log("✅ with-runtime-framework-headers: Podfile updated");
|
||
config.modResults.contents = podfile;
|
||
return config;
|
||
});
|
||
};
|