set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
set working-directory := '.'

xcode_derived_data_dev := "./.xcode-derived/tproxy-app-dev"
xcode_derived_data_dist := "./.xcode-derived/tproxy-app-dist"
tproxy_built_app_dev := xcode_derived_data_dev + "/Build/Products/Debug/RamaTransparentProxyExampleContainer.app"
tproxy_built_app_dist := xcode_derived_data_dist + "/Build/Products/Release/RamaTransparentProxyExampleContainer.app"

[working-directory: './tproxy_rs']
build-tproxy-rs:
    cargo build --target aarch64-apple-darwin &&     cargo build --target x86_64-apple-darwin &&     mkdir -p target/universal &&     lipo -create         -output target/universal/librama_tproxy_example.a         target/aarch64-apple-darwin/debug/librama_tproxy_example.a         target/x86_64-apple-darwin/debug/librama_tproxy_example.a

build-tproxy-app-dev:
    ./scripts/build_tproxy_app_with_signing.sh

build-tproxy-app-dist:
    ./scripts/build_tproxy_app_with_developer_id_signing.sh

notarize-tproxy-app-dist:
    ./scripts/notarize_tproxy_app_with_developer_id_signing.sh

install-tproxy-app-dev:
    ./scripts/build_tproxy_app_with_signing.sh
    INSTALLED_APP=/Applications/RamaTransparentProxyExampleContainer.app; \
    INSTALLED_SYSEXT="$INSTALLED_APP/Contents/Library/SystemExtensions/org.ramaproxy.example.tproxy.dev.provider.systemextension"; \
    ./scripts/install_tproxy_app_bundle.sh dev "{{tproxy_built_app_dev}}" 0; \
    echo "Installed Development system extension bundle at $INSTALLED_SYSEXT"

install-tproxy-app-dist:
    ./scripts/notarize_tproxy_app_with_developer_id_signing.sh
    INSTALLED_APP=/Applications/RamaTransparentProxyExampleContainer.app; \
    INSTALLED_SYSEXT="$INSTALLED_APP/Contents/Library/SystemExtensions/org.ramaproxy.example.tproxy.provider.systemextension"; \
    ./scripts/install_tproxy_app_bundle.sh dist "{{tproxy_built_app_dist}}" 0; \
    echo "Installed Developer ID system extension bundle at $INSTALLED_SYSEXT"

install-tproxy-app-dev-reset-profile:
    ./scripts/build_tproxy_app_with_signing.sh
    INSTALLED_APP=/Applications/RamaTransparentProxyExampleContainer.app; \
    INSTALLED_SYSEXT="$INSTALLED_APP/Contents/Library/SystemExtensions/org.ramaproxy.example.tproxy.dev.provider.systemextension"; \
    ./scripts/install_tproxy_app_bundle.sh dev "{{tproxy_built_app_dev}}" 1; \
    echo "Installed Development system extension bundle at $INSTALLED_SYSEXT"

install-tproxy-app-dist-reset-profile:
    ./scripts/notarize_tproxy_app_with_developer_id_signing.sh
    INSTALLED_APP=/Applications/RamaTransparentProxyExampleContainer.app; \
    INSTALLED_SYSEXT="$INSTALLED_APP/Contents/Library/SystemExtensions/org.ramaproxy.example.tproxy.provider.systemextension"; \
    ./scripts/install_tproxy_app_bundle.sh dist "{{tproxy_built_app_dist}}" 1; \
    echo "Installed Developer ID system extension bundle at $INSTALLED_SYSEXT"

build-tproxy-dev: build-tproxy-rs build-tproxy-app-dev

build-tproxy-dist: build-tproxy-rs build-tproxy-app-dist

build-tproxy: build-tproxy-dev build-tproxy-dist

notarize-tproxy-dist: build-tproxy-rs notarize-tproxy-app-dist

clear-tproxy-files:
    sudo rm -rf /var/root/Library/Application Support/rama/tproxy/dial9-traces

install-tproxy-dev: build-tproxy-rs clear-tproxy-files install-tproxy-app-dev

install-tproxy-dev-reset-profile: build-tproxy-rs install-tproxy-app-dev-reset-profile

install-tproxy-dist: build-tproxy-rs clear-tproxy-files install-tproxy-app-dist

install-tproxy-dist-reset-profile: build-tproxy-rs install-tproxy-app-dist-reset-profile

[working-directory: './tproxy_rs']
clean:
    cargo clean

clippy *ARGS:
    cd ./tproxy_rs && cargo clippy --all-targets --all-features {{ARGS}}
    cd ./tproxy_ffi_e2e && cargo clippy --all-targets --all-features {{ARGS}}

sort:
    @command -v cargo-sort >/dev/null || cargo install cargo-sort --locked
    cd ./tproxy_rs && cargo sort --workspace --grouped
    cd ./tproxy_ffi_e2e && cargo sort --workspace --grouped

[working-directory: './tproxy_rs']
clippy-fix *ARGS:
    just clippy --fix {{ARGS}}

[working-directory: './tproxy_rs']
rust-doc *ARGS:
    cargo doc --all-features --no-deps {{ARGS}}

[working-directory: './tproxy_rs']
rust-test *ARGS:
    @command -v cargo-nextest >/dev/null || cargo install cargo-nextest --locked
    cargo nextest run --all-features --no-tests=pass {{ARGS}}

[working-directory: './tproxy_rs']
rust-test-ignored:
    @command -v cargo-nextest >/dev/null || cargo install cargo-nextest --locked
    cargo nextest run --all-features --run-ignored=only --no-tests=pass

[working-directory: './tproxy_rs']
build-tproxy-rs-debug:
    cargo build

update-deps:
    @cargo install cargo-edit --locked
    cd ./tproxy_rs && cargo upgrade --incompatible && cargo update
    cd ./tproxy_ffi_e2e && cargo upgrade --incompatible && cargo update

[working-directory: './tproxy_ffi_e2e']
clippy-tproxy-ffi-e2e:
    cargo clippy --all-targets -- -D warnings

[working-directory: './tproxy_ffi_e2e']
cleanup-tproxy-ffi-e2e:
    pkill -f 'transparent_proxy/tproxy_ffi_e2e/target/.*/deps/e2e-' >/dev/null 2>&1 || true

[working-directory: './tproxy_ffi_e2e']
run-tproxy-ffi-e2e:
    cargo test --test e2e -- --test-threads=1 --nocapture

[working-directory: '.']
run-tproxy-ffi-e2e-swift: build-tproxy-rs-debug
    swift test \
        -Xlinker -L$(pwd)/tproxy_rs/target/debug \
        -Xlinker -lrama_tproxy_example \
        -Xlinker -lz \
        -Xlinker -framework -Xlinker NetworkExtension \
        -Xlinker -framework -Xlinker SystemConfiguration

# Swift unit tests under ThreadSanitizer. The suppressions file silences known
# TEST-HARNESS races only (tests observing flowQueue-confined ctx state from the
# test thread; ChaosTests' deliberate raw-thread teardown idempotency probe) —
# every suppressed function is single-threaded in production, so a REAL
# production data race would be in a different function and still fail this. See
# the file header in RamaAppleNetworkExtension/tsan-suppressions.txt.
[working-directory: '.']
run-tproxy-ffi-e2e-swift-tsan: build-tproxy-rs-debug
    TSAN_OPTIONS="suppressions=$(pwd)/../../RamaAppleNetworkExtension/tsan-suppressions.txt" \
    swift test --sanitize=thread \
        -Xlinker -L$(pwd)/tproxy_rs/target/debug \
        -Xlinker -lrama_tproxy_example \
        -Xlinker -lz \
        -Xlinker -framework -Xlinker NetworkExtension \
        -Xlinker -framework -Xlinker SystemConfiguration

qa-rust: sort clippy rust-doc rust-test rust-test-ignored

qa: qa-rust build-tproxy-dev

test-e2e: build-tproxy-rs-debug clippy-tproxy-ffi-e2e cleanup-tproxy-ffi-e2e run-tproxy-ffi-e2e run-tproxy-ffi-e2e-swift

test-full: qa test-e2e

# Run the FFI e2e suite under AddressSanitizer to surface UAF / data races.
#
# Requires nightly Rust (`rustup toolchain install nightly`). The static lib
# (`tproxy_rs`) and the test crate (`tproxy_ffi_e2e`) must both be built with
# the sanitizer flag — otherwise instrumentation is incomplete and races slip
# past. See the `cases::stress::*` tests for the high-churn scenarios that
# give the sanitizer enough race windows to land on a real bug.
[working-directory: './tproxy_rs']
build-tproxy-rs-asan:
    RUSTFLAGS="-Z sanitizer=address -D warnings --cfg tokio_unstable" \
        cargo +nightly build --target aarch64-apple-darwin

[working-directory: './tproxy_ffi_e2e']
run-tproxy-ffi-e2e-asan:
    RUSTFLAGS="-Z sanitizer=address -D warnings --cfg tokio_unstable" \
        RAMA_TPROXY_EXAMPLE_LIB_DIR="../tproxy_rs/target/aarch64-apple-darwin/debug" \
        cargo +nightly test --target aarch64-apple-darwin \
        --test e2e -- --test-threads=1 --nocapture cases::stress

# Convenience target: build the staticlib with ASan and run the e2e stress
# cases under it. Slower than `test-e2e` and requires nightly.
test-e2e-asan: build-tproxy-rs-asan run-tproxy-ffi-e2e-asan

# Convenience target for the two sanitizer passes that exercise the Swift/C/Rust
# transparent-proxy bridge: Rust ASan stress cases plus Swift ThreadSanitizer.
test-e2e-sanitizers: test-e2e-asan run-tproxy-ffi-e2e-swift-tsan

# Generate live traffic against public HTTP/HTTPS endpoints to stress
# the running sysext. Mixes small/large GET, large POST, parallel
# connections, plain HTTP, and HTTP/1.1 ↔ HTTP/2. The sysext must be
# installed and active. Tune via STRESS_DURATION /
# STRESS_CONCURRENCY etc; with `MONITOR_PID=$(pgrep -f
# org.ramaproxy.example.tproxy.dev.provider)` it also samples the
# extension's RSS / VM regions / leaks every 5s.
stress-traffic *ARGS:
    bash ./scripts/stress_traffic.sh {{ARGS}}
