#!/bin/bash
# 把 gnutls-build 自建的 gmp/nettle 产物打成 MPVKit ZipBaseBuild 期望的包格式。
#
# MPVKit 侧的消费方式（base.swift ZipBaseBuild）：
#   dist/<lib>-<ver>/<lib>.zip        存在则跳过下载，直接用已解压内容
#   lib/<platform>/thin/<arch>/lib/   → dist/<lib>/<platform>/thin/<arch>/lib
#   include/                          → 同上 include
#   pkgconfig-example/<platform>/<arch>/ → .../lib/pkgconfig（/path/to/workdir 会被替换成真实路径）
set -euo pipefail
SRC="$1"      # gnutls-build 的 dist 目录
OUT="$2"      # 输出目录
VER=3.8.11
# 可复现打包：同一份 gnutls-build 产物无论何时、何地、打几次，zip 都逐字节相同。
# 这两个 zip 的 SHA256 记在 upstream-input-checksums.sha256 里由 build.sh 比对，打包不稳定时
# 每次重建都会误报「上游输入变了」。变量来源逐项钉死：
#   - 时间戳：统一成 1980-01-01 00:00（zip 的 DOS 时间下限），并在 UTC 下写入与换算
#   - 权限：目录 755、文件 644，不随 umask 或源文件而变
#   - 条目顺序：按字节序排序后经 -@ 逐条喂给 zip，不依赖目录遍历顺序
#   - 附加属性：-X 不写 uid/gid 与扩展时间戳
#   - 静态库：ar 头与 __.SYMDEF 里带着归档时刻的时间戳（两次独立构建的 .o 逐字节相同，
#     只有这里不同），用 ZERO_AR_DATE=1 ranlib 把时间戳与 uid/gid 清零，成员与顺序不变
#   - 工具：ranlib 经 xcrun 取所选 Xcode 的（build.sh 带着 DEVELOPER_DIR 调用），zip 固定用 /usr/bin/zip，
#     不让 PATH 里的 Homebrew 版本混进来。可复现的前提是同一个 Xcode（版本记在 build-versions.txt）
export TZ=UTC
# 构建时写进 .pc 的路径可能是逻辑路径，也可能是解析过符号链接的物理路径，两种都要换成占位符。
src_logical="$(cd "$SRC" && pwd)"
src_physical="$(cd "$SRC" && pwd -P)"

for lib in gmp nettle; do
  stage="$OUT/${lib}-stage"
  rm -rf "$stage"; mkdir -p "$stage"
  inc_done=0
  for pdir in "$SRC/$lib"/*/; do
    p=$(basename "$pdir")
    for adir in "$pdir"thin/*/; do
      [ -d "$adir" ] || continue
      a=$(basename "$adir")
      # lib 里只放 .a，与上游 gnutls-build / MPVKit 的 release 包同一格式。lib/pkgconfig 与 .la
      # 写着构建机的绝对路径：放进来，zip 就随 checkout 位置而变；而且 MPVKit 先整目录复制 lib，
      # 再用 try? copyItem 放 pkgconfig-example 时会因目标已存在而静默失败，占位符通道从此不生效。
      mkdir -p "$stage/lib/$p/thin/$a/lib"
      cp "$adir"lib/*.a "$stage/lib/$p/thin/$a/lib/"
      # 头文件跨平台一致，取第一份即可
      if [ $inc_done -eq 0 ] && [ -d "$adir/include" ]; then
        cp -R "$adir/include" "$stage/include"; inc_done=1
      fi
      # pkgconfig：把构建路径反写成占位符，MPVKit 的 ZipBaseBuild 再换成它自己的 dist 目录。
      # 占位符对应的是 dist 本身（与上游 release 包一致）：MPVKit 把 /path/to/workdir 换成
      # URL.currentDirectory，也就是 MPVKit/dist，prefix 才会落到 dist/<lib>/<平台>/thin/<架构>。
      if [ -d "$adir/lib/pkgconfig" ]; then
        mkdir -p "$stage/pkgconfig-example/$p/$a"
        for pc in "$adir/lib/pkgconfig"/*.pc; do
          [ -f "$pc" ] || continue
          # perl 的 \Q…\E 按字面量替换：路径里有 # 或正则元字符也不会出错。
          SRC_LOGICAL="$src_logical" SRC_PHYSICAL="$src_physical" \
            perl -pe 's/\Q$ENV{SRC_PHYSICAL}\E/\/path\/to\/workdir/g; s/\Q$ENV{SRC_LOGICAL}\E/\/path\/to\/workdir/g' \
            "$pc" > "$stage/pkgconfig-example/$p/$a/$(basename "$pc")"
        done
      fi
    done
  done
  # 逐个 ranlib，失败由 set -e 直接中止。不用 find -exec：{} \; 会吞掉 ranlib 的失败；
  # {} + 把各架构的归档一次交给同一个 ranlib，arm64 libnettle.a 的 __.SYMDEF 会与逐个处理时不同。
  while IFS= read -r archive; do
    ZERO_AR_DATE=1 xcrun ranlib "$archive"
  done < <(find "$stage/lib" -type f -name '*.a' | LC_ALL=C sort)
  find "$stage" -type d -exec chmod 755 {} +
  find "$stage" -type f -exec chmod 644 {} +
  find "$stage" -exec touch -h -t 198001010000.00 {} +
  # 先删旧包：zip 遇到已存在的同名文件会就地更新而不是重建，旧条目会留在里面。
  rm -f "$OUT/$lib.zip"
  (cd "$stage" && find include lib pkgconfig-example -print | LC_ALL=C sort | /usr/bin/zip -q -X -@ "$OUT/$lib.zip")
  echo "  $lib.zip  $(shasum -a 256 "$OUT/$lib.zip" | cut -c1-16)…  平台: $(ls "$stage/lib" | tr '\n' ' ')"
done
