# p4bridge/ — C++ bridge patterns

Scoped instructions for the native bridge DLL. Loads when you read/edit files under `p4api.net/p4bridge/`.

Project overview: root `CLAUDE.md`. Cross-cutting PInvoke rules: `.claude/rules/pinvoke.md`. Full build details for every platform: [`P4Bridge.md`](P4Bridge.md).

## What this is

`p4bridge` is a C++ DLL that:
1. Links statically against the Perforce C++ API (`libclient.a`, `libsupp.a`, `libp4api.a`, `librpc.a`, `libp4script*.a`).
2. Exports a flat C ABI consumed by managed `P4Bridge.cs` via PInvoke.
3. Builds via CMake — eight presets (`linux-{Release,Debug}`, `osx-{Release,Debug}`, `x64-{Release,Debug}`, `x86-{Release,Debug}`).

The exported C ABI is `p4bridge-api.cpp` (+ `p4map-api.cpp`). Internal C++ glue is `P4BridgeServer.{cpp,h}`, `P4BridgeClient.{cpp,h}`, `P4Connection.{cpp,h}`, plus shared utility headers (`Lock.h`, `OutStr.h`, `utils.{cpp,h}`, `ticket.h`, `p4base.{cpp,h}`).

## Export discipline

- Every export in `p4bridge-api.cpp` must have a matching `[DllImport]` in `../p4api.net/P4Bridge.cs`. Type / marshalling / calling-convention mismatches **silently corrupt the stack** — they don't fail loudly. See `.claude/rules/pinvoke.md`.
- Use `extern "C"` on export signatures — name-mangling breaks PInvoke.
- New exports must be listed in `p4bridge_x64.def` and `p4bridge_x86.def`, or Windows won't expose them.
- Return primitive types or opaque pointers. For string returns, allocate via a known allocator and provide a matching `Free*` export so the managed side can release it. Never hand a `std::string`'s `c_str()` across the boundary.

## CMakeLists.txt is load-bearing

- The CMake config **hardcodes** the p4api install dir per platform:
  - Linux/macOS: `link_directories("../p4api/lib")`, `include_directories("../p4api/include/p4")`
  - Windows: `link_directories("../p4api$<...>/lib")` per variant
- `-DP4API_DIR=...` is **not consumed**. Don't add it to build docs.
- Per-platform sets must stay consistent — `SslLibs` / `ApiLibs` / `PlatformLibs` differ across Linux / Darwin / Windows blocks. New compile flags must be guarded per-platform.
- The eight CMake presets must all keep working. Adding a new preset means removing / fixing the others too.

## OpenSSL dependency

The CDN p4api tarball does **not** bundle OpenSSL — `CMakeLists.txt` links `-lssl -lcrypto` dynamically (Linux/macOS) or via static libs bundled into the Windows zip. The system must provide the right major version:

- `*-openssl3.tgz` ↔ OpenSSL 3 (Ubuntu 22.04+, RHEL 9+)
- `*-openssl1.1.1.tgz` ↔ OpenSSL 1.1.1 (Ubuntu 20.04, RHEL 8)

A mismatch link-succeeds but **crashes at runtime in `SSL_*` calls** — see `.claude/skills/build/SKILL.md`'s platform table.

## Threading & globals

- The Perforce C++ `ClientApi` is **not thread-safe**. `P4ServerMT` allocates one `ClientApi` per managed connection.
- New static / global state must use the existing locks in `Lock.h`. Don't add new mutable globals.
- `connectionError` was historically a process-global; modern code uses thread-local or per-connection state. Match the existing pattern before adding new state on this path.

## Recurring pitfalls

- **PInvoke drift.** Already covered above — the `pinvoke_sync_check` hook fires an advisory at edit time if you change one side without touching the other.
- **Building without p4api in place.** If `link_directories` can't resolve, the linker error is misleading (`cannot find -lclient`). Use `/build` — it extracts p4api into the right directory.
- **OpenSSL variant mismatch.** Link succeeds, runtime SIGSEGV in SSL paths. Verify with `pkg-config --modversion openssl` before building.
- **`-DP4API_DIR=...` shows up in docs.** It's a no-op; if you see it anywhere, remove or call out.

## When you change something here, also touch…

| You edit | Also update |
|---|---|
| An export in `p4bridge-api.cpp` | The matching `[DllImport]` in `../p4api.net/P4Bridge.cs` (same commit) |
| A new export | Add it to `p4bridge_x64.def` and `p4bridge_x86.def` |
| `CMakeLists.txt` or `CMakePresets.json` | Run `/build` end-to-end and verify all relevant presets still produce a binary |
| Bridge-side behavior change | Add or update a case in `../p4bridge-unit-test/` |
| Anything in this directory | Re-run the C++ harness: see `../p4bridge-unit-test/CLAUDE.md` |
