#!/bin/sh
# Build the four device libraries (libplist, libimobiledevice-glue, libusbmuxd,
# libimobiledevice) from the digest-verified official source archives, against the
# release OpenSSL build (run Tools/build_release_openssl.sh first). pkgconf and
# libtatsu are build-only tools. Everything installs into a task-owned directory
# under .build; no package manager, global install, phone access or credentials.
set -eu
repo_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
cd "$repo_dir"
source_archives="${1:-$repo_dir/.build/dependency-preparation/sources}"
openssl_dir="$repo_dir/.build/dependency-preparation/openssl-build/openssl-3.6.4"
build_dir="$repo_dir/.build/dependency-preparation/natives-build"
/usr/bin/python3 - "$source_archives" "$openssl_dir" "$build_dir" <<'PY'
import hashlib, json, os, sys, tarfile
from pathlib import Path
root = Path.cwd()
source_dir, openssl_dir, build_dir = map(Path, sys.argv[1:4])
manifest = json.loads((root / 'Configuration/ReleaseDependencies.json').read_text())
records = [p for p in manifest['nativePackages'] if p['name'] != 'openssl@3'] + manifest['buildTools']
record = json.loads((openssl_dir / 'thumbwise-build-record.json').read_bytes())
for name, digest in record['images'].items():
    if hashlib.sha256((openssl_dir / name).read_bytes()).hexdigest() != digest:
        raise SystemExit('OpenSSL build output differs from its record; no build executed.')
if build_dir.exists():
    raise SystemExit('Native build directory exists; inspect/remove only this disposable build before rebuilding.')
archives = []
for package in records:
    archive = source_dir / package['source']['url'].rsplit('/', 1)[-1]
    if hashlib.sha256(archive.read_bytes()).hexdigest() != package['source']['archiveSHA256']:
        raise SystemExit(package['name'] + ' source hash mismatch; no build executed.')
    archives.append((package, archive))
(build_dir / 'src').mkdir(parents=True)
for package, archive in archives:
    top = package['name'] + '-' + package['version']
    with tarfile.open(archive) as source:
        for item in source.getmembers():
            relative = Path(item.name)
            if relative.is_absolute() or '..' in relative.parts or not relative.parts or relative.parts[0] != top:
                raise SystemExit('Unsafe source member; no build executed.')
            target = build_dir / 'src' / relative
            if item.isdir():
                target.mkdir(parents=True, exist_ok=True)
            elif item.isfile():
                target.parent.mkdir(parents=True, exist_ok=True)
                target.write_bytes(source.extractfile(item).read())
                target.chmod(item.mode & 0o777)
                # Keep archive timestamps so configure/Makefile.in stay newer than their inputs.
                os.utime(target, (item.mtime, item.mtime))
            else:
                raise SystemExit('Linked or special source member refused; no build executed.')
PY

src="$build_dir/src"
tools="$build_dir/tools"
root="$build_dir/root"
prefix=/ThumbwiseRuntime
rt="$root$prefix"
mkdir -p "$tools" "$rt/lib/pkgconfig" "$rt/include" "$build_dir/pcshim"

# Fixed deployment target, architecture, optimization, no debug info, and no
# checkout path in compiled strings. Fixed timestamp for reproducible metadata.
export MACOSX_DEPLOYMENT_TARGET=15.0
export SOURCE_DATE_EPOCH=0
export CC=/usr/bin/clang CXX=/usr/bin/clang++
export CFLAGS="-arch arm64 -mmacosx-version-min=15.0 -O2 -g0 -ffile-prefix-map=$build_dir=/build"
export CXXFLAGS="$CFLAGS"
export LDFLAGS="-arch arm64 -mmacosx-version-min=15.0"
export PATH="$tools/bin:/usr/bin:/bin:/usr/sbin:/sbin"
unset CPPFLAGS LIBS PKG_CONFIG_PATH

# Build-only pkgconf; later builds see only the task-owned prefix (as a sysroot
# at the product prefix, so install names are path-independent) plus a shim.
(
  cd "$src/pkgconf-3.0.10"
  ./configure --prefix="$tools" --disable-shared --enable-static
  /usr/bin/make --jobs=2
  /usr/bin/make install
)
ln -s pkgconf "$tools/bin/pkg-config"
export PKG_CONFIG="$tools/bin/pkgconf"
export PKG_CONFIG_LIBDIR="$rt/lib/pkgconfig:$build_dir/pcshim"
export PKG_CONFIG_SYSROOT_DIR="$root"

# Apple's system libcurl, described only for libtatsu's configure (build-only).
cat > "$build_dir/pcshim/libcurl.pc" <<'EOF'
Name: libcurl
Description: Apple system libcurl (build-time metadata only)
Version: 8.7.1
Libs: -lcurl
EOF

# The release OpenSSL build tree, laid out as a link-time sysroot copy. The
# libraries keep their product install names; headers come from its build tree.
cp "$openssl_dir/libssl.3.dylib" "$openssl_dir/libcrypto.3.dylib" "$rt/lib/"
ln -s libssl.3.dylib "$rt/lib/libssl.dylib"
ln -s libcrypto.3.dylib "$rt/lib/libcrypto.dylib"
mkdir -p "$rt/include/openssl"
cp "$openssl_dir"/include/openssl/*.h "$rt/include/openssl/"
for module in libcrypto libssl openssl; do
  case $module in
    libcrypto) libs='-L${libdir} -lcrypto'; requires='' ;;
    libssl) libs='-L${libdir} -lssl'; requires='Requires.private: libcrypto' ;;
    openssl) libs=''; requires='Requires: libssl libcrypto' ;;
  esac
  {
    printf '%s\n' 'prefix=/ThumbwiseRuntime' 'libdir=${prefix}/lib' 'includedir=${prefix}/include' \
      "Name: $module" 'Description: Release OpenSSL 3.6.4 build' 'Version: 3.6.4' \
      "Libs: $libs" 'Cflags: -I${includedir}'
    [ -z "$requires" ] || printf '%s\n' "$requires"
  } > "$rt/lib/pkgconfig/$module.pc"
done

# build_one NAME "library subdirectories (empty: whole tree)" configure-args...
# Subdirectories skip the upstream command-line tools and documentation.
build_one() {
  name=$1; subdirs=$2; shift 2
  cd "$src/$name"
  ./configure --prefix=$prefix --disable-static --enable-shared "$@"
  if [ -n "$subdirs" ]; then
    for dir in $subdirs; do /usr/bin/make --jobs=2 -C "$dir"; done
    for dir in $subdirs; do /usr/bin/make -C "$dir" DESTDIR="$root" install; done
  else
    /usr/bin/make --jobs=2
    /usr/bin/make DESTDIR="$root" install
  fi
  # Libtool archives would point later links at the unpopulated product prefix.
  /bin/rm -f "$rt"/lib/*.la
}

build_one libplist-2.7.0 "" --without-cython --without-tools
build_one libimobiledevice-glue-1.3.2 ""
build_one libusbmuxd-2.1.1 "src include" --without-inotify
build_one libtatsu-1.0.5 ""
build_one libimobiledevice-1.4.0 "3rd_party common src include" --without-cython --without-readline

cd "$repo_dir"
/usr/bin/python3 - "$repo_dir" "$build_dir" "$openssl_dir" <<'PY'
import hashlib, json, shutil, sys
from pathlib import Path
root, build_dir, openssl_dir = map(Path, sys.argv[1:4])
manifest = json.loads((root / 'Configuration/ReleaseDependencies.json').read_text())
lib = build_dir / 'root/ThumbwiseRuntime/lib'
images = {}
for package in manifest['nativePackages']:
    if package['name'] == 'openssl@3':
        continue
    for image in package['images']:
        path = lib / Path(image['path']).name
        data = path.read_bytes()
        if str(build_dir).encode() in data or str(Path.home()).encode() in data:
            raise SystemExit('Private build path found in ' + path.name + '; refusing to record.')
        images[path.name] = hashlib.sha256(data).hexdigest()
openssl_record = (openssl_dir / 'thumbwise-build-record.json').read_bytes()
record = {
    'schemaVersion': 1,
    'sourceSHA256': {p['name'] + '-' + p['version']: p['source']['archiveSHA256']
                     for p in [q for q in manifest['nativePackages'] if q['name'] != 'openssl@3'] + manifest['buildTools']},
    'recipeSHA256': hashlib.sha256((root / 'Tools/build_release_natives.sh').read_bytes()).hexdigest(),
    'opensslBuildRecordSHA256': hashlib.sha256(openssl_record).hexdigest(),
    'policy': 'arm64/macOS 15.0; shared only; product prefix /ThumbwiseRuntime installed into a task-owned sysroot; '
              'libraries only (no tools, Python bindings or readline); pkgconf and libtatsu are build-only',
    'images': images,
}
(build_dir / 'thumbwise-build-record.json').write_text(json.dumps(record, indent=2) + '\n')
shutil.rmtree(build_dir / 'src')
print('PASS Verified upstream device libraries built locally against the release OpenSSL build; recorded', len(images), 'images.')
PY
