Files
terminalX/vendor/libghostty-spm/Script/test-xcframework-consumer.sh
kid aa92d0e676 初始提交:terminalX 可运行态(M0/M1/M1.5 已真机验证)
- M0: libghostty SSH 终端(渲染/输入/连接)+ 白屏修复(OutputGate) + 会话状态机/自动重连
- M1: tsnet 用户态组网 + SSH-over-tsnet(fd 桥),shell 级真机验证;R5(Go+gvisor+C+++Swift 同进程) retire
- M1.5: tmux -CC 原生 tab(MVP)
- 结构: packages/(TXCore·TXTransport), apps/TerminalX, vendor/(libghostty-spm/libssh2/mbedtls/tsnet-bridge), artifacts/
- 文档: CLAUDE.md + docs/HANDOFF.md(新会话入口)
- 环境: 认证代理→依赖 vendor 本地化;Go 在 ~/.local/go;仅模拟器/未签名
- 待续: M2 mosh, tmux 多 pane, M4 安全(host key/SE)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:20:46 +08:00

289 lines
7.4 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
cd "$(dirname "$0")/.."
if [ ! -f .root ]; then
echo "[*] malformed project structure"
exit 1
fi
XCFRAMEWORK_PATH=${1:-BinaryTarget/GhosttyKit.xcframework}
if [ ! -d "$XCFRAMEWORK_PATH" ]; then
echo "[!] xcframework not found: $XCFRAMEWORK_PATH"
exit 1
fi
format_output() {
if command -v xcbeautify >/dev/null 2>&1; then
xcbeautify
else
cat
fi
}
WORK_DIR=$(mktemp -d)
trap 'rm -rf "$WORK_DIR"' EXIT
mkdir -p "$WORK_DIR/Consumer/BinaryTarget" "$WORK_DIR/Consumer/Sources/Consumer"
ditto "$XCFRAMEWORK_PATH" "$WORK_DIR/Consumer/BinaryTarget/GhosttyKit.xcframework"
cat >"$WORK_DIR/Consumer/Package.swift" <<'EOF'
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "Consumer",
platforms: [
.iOS(.v15),
.macOS(.v13),
.macCatalyst(.v15),
],
products: [
.library(name: "Consumer", targets: ["Consumer"]),
],
targets: [
.binaryTarget(
name: "libghostty",
path: "BinaryTarget/GhosttyKit.xcframework"
),
.target(
name: "Consumer",
dependencies: ["libghostty"],
linkerSettings: [
.linkedLibrary("c++"),
.linkedFramework("Carbon", .when(platforms: [.macOS])),
]
),
]
)
EOF
cat >"$WORK_DIR/Consumer/Sources/Consumer/Consumer.swift" <<'EOF'
import libghostty
public func passThroughPlatform(_ platform: ghostty_platform_e) -> ghostty_platform_e {
platform
}
EOF
ARM64E_LINK_SOURCE="$WORK_DIR/arm64e-link.c"
cat >"$ARM64E_LINK_SOURCE" <<'EOF'
#include "ghostty.h"
#include <pthread.h>
#include <ptrauth.h>
#include <stdlib.h>
extern int pa_pthread_cr(
pthread_t *,
const pthread_attr_t *,
void *(*)(void *),
void *);
extern void pa_qs(
void *,
size_t,
size_t,
int (*)(const void *, const void *));
static void wakeup(void *userdata) {
(void)userdata;
}
static void *thread_main(void *context) {
int *value = context;
*value = 42;
return NULL;
}
static int compare_ints(const void *lhs, const void *rhs) {
const int left = *(const int *)lhs;
const int right = *(const int *)rhs;
return (left > right) - (left < right);
}
int main(void) {
if (ghostty_init(0, NULL) != GHOSTTY_SUCCESS) {
return 1;
}
ghostty_config_t config = ghostty_config_new();
if (config == NULL) {
return 2;
}
ghostty_config_finalize(config);
ghostty_runtime_config_s runtime = {0};
runtime.wakeup_cb = wakeup;
ghostty_app_t app = ghostty_app_new(&runtime, config);
if (app == NULL) {
ghostty_config_free(config);
return 3;
}
int thread_value = 0;
pthread_t thread;
void *(*raw_thread_main)(void *) =
ptrauth_strip(&thread_main, ptrauth_key_function_pointer);
if (pa_pthread_cr(&thread, NULL, raw_thread_main, &thread_value) != 0) {
return 4;
}
if (pthread_join(thread, NULL) != 0 || thread_value != 42) {
return 5;
}
int values[] = {3, 1, 2};
int (*raw_compare)(const void *, const void *) =
ptrauth_strip(&compare_ints, ptrauth_key_function_pointer);
pa_qs(values, 3, sizeof(values[0]), raw_compare);
if (values[0] != 1 || values[1] != 2 || values[2] != 3) {
return 6;
}
ghostty_app_free(app);
ghostty_config_free(config);
return 0;
}
EOF
find_slice_paths() {
local platform="$1"
local platform_variant="$2"
python3 - "$XCFRAMEWORK_PATH" "$platform" "$platform_variant" <<'PY'
import os
import plistlib
import sys
xcframework, expected_platform, expected_variant = sys.argv[1:]
expected_variant = expected_variant or None
with open(os.path.join(xcframework, "Info.plist"), "rb") as handle:
libraries = plistlib.load(handle)["AvailableLibraries"]
for library in libraries:
if library.get("SupportedPlatform") != expected_platform:
continue
if library.get("SupportedPlatformVariant") != expected_variant:
continue
root = os.path.join(xcframework, library["LibraryIdentifier"])
print(os.path.join(root, library["LibraryPath"]))
print(os.path.join(root, library["HeadersPath"]))
break
else:
raise SystemExit(
f"[!] xcframework slice not found: {expected_platform}/{expected_variant}"
)
PY
}
link_arm64e() {
local name="$1"
local sdk="$2"
local target="$3"
local platform="$4"
local platform_variant="$5"
local slice_paths
local library_path
local headers_path
local output_path="$WORK_DIR/arm64e-link-$name"
local command
slice_paths=$(find_slice_paths "$platform" "$platform_variant")
library_path=${slice_paths%%$'\n'*}
headers_path=${slice_paths#*$'\n'}
command=(
xcrun --sdk "$sdk" clang
-target "$target"
"$ARM64E_LINK_SOURCE"
-I "$headers_path"
"-Wl,-force_load,$library_path"
-lc++
-framework Foundation
-framework CoreFoundation
-framework CoreGraphics
-framework CoreText
-framework CoreVideo
-framework QuartzCore
-framework IOSurface
-framework Metal
-framework MetalKit
)
case "$name" in
macos)
command+=(-framework Carbon)
;;
ios)
command+=(-framework UIKit)
;;
maccatalyst)
local sdk_path
sdk_path=$(xcrun --sdk macosx --show-sdk-path)
command+=(
-F "$sdk_path/System/iOSSupport/System/Library/Frameworks"
-L "$sdk_path/System/iOSSupport/usr/lib"
-framework UIKit
)
;;
esac
command+=(-o "$output_path")
echo "[*] full link platform=$name architecture=arm64e"
"${command[@]}"
if [ "$(lipo -archs "$output_path")" != "arm64e" ]; then
echo "[!] linked executable is not a single arm64e slice: $output_path"
exit 1
fi
if [ "$name" = "macos" ] && [ "$(uname -m)" = "arm64" ]; then
echo "[*] runtime smoke platform=macos architecture=arm64e"
"$output_path"
fi
}
test_build() {
local destination="$1"
local architecture=${2:-}
local command=(
xcodebuild
-scheme Consumer
-destination "$destination"
-derivedDataPath "$WORK_DIR/DerivedData"
-packageCachePath "$WORK_DIR/PackageCache"
)
if [ -n "$architecture" ]; then
command+=("ARCHS=$architecture" "ONLY_ACTIVE_ARCH=YES")
fi
command+=(build)
echo "[*] consumer build destination=$destination architecture=${architecture:-default}"
"${command[@]}" 2>&1 | format_output
local exit_code=${PIPESTATUS[0]}
if [ "$exit_code" -ne 0 ]; then
echo "[!] consumer build failed destination=$destination"
exit "$exit_code"
fi
}
(
cd "$WORK_DIR/Consumer"
test_build "generic/platform=macOS"
test_build "generic/platform=macOS,variant=Mac Catalyst"
test_build "generic/platform=iOS"
test_build "generic/platform=iOS Simulator"
test_build "generic/platform=macOS" "arm64e"
test_build "generic/platform=macOS,variant=Mac Catalyst" "arm64e"
test_build "generic/platform=iOS" "arm64e"
)
link_arm64e "macos" "macosx" "arm64e-apple-macosx13.0" "macos" ""
link_arm64e "ios" "iphoneos" "arm64e-apple-ios15.0" "ios" ""
link_arm64e "maccatalyst" "macosx" "arm64e-apple-ios15.0-macabi" "ios" "maccatalyst"
echo "[*] xcframework consumer tests passed"