---
phase: 08-cpp-shim-infrastructure
plan: "02"
type: execute
wave: 2
depends_on:
  - "08-01"
files_modified:
  - build.rs
  - src/lib.rs
autonomous: true
requirements:
  - INFRA-01

must_haves:
  truths:
    - "cargo test passes — bundled test mode compiles without C++ toolchain involvement"
    - "cargo build --no-default-features --features extension succeeds — C++ shim compiled and linked"
    - "Symbol count check shows no Rust stdlib leakage on macOS/Linux"
    - "make debug succeeds (full Makefile build that mirrors CI)"
    - "just test passes — existing v0.1.0 SQLLogicTests still pass after C++ addition"
  artifacts:
    - path: "build.rs"
      provides: "Feature-gated cc::Build compilation of shim.cpp with symbol visibility linker flags"
      contains: "CARGO_FEATURE_EXTENSION"
    - path: "src/lib.rs"
      provides: "extern C declaration and call to semantic_views_register_shim in init_extension"
      contains: "semantic_views_register_shim"
  key_links:
    - from: "build.rs"
      to: "src/shim/shim.cpp"
      via: ".file(\"src/shim/shim.cpp\")"
      pattern: 'file\("src/shim/shim\.cpp"\)'
    - from: "build.rs"
      to: "duckdb_capi/"
      via: ".include(\"duckdb_capi/\")"
      pattern: 'include\("duckdb_capi/"\)'
    - from: "src/lib.rs"
      to: "semantic_views_register_shim"
      via: "unsafe extern C declaration + call in init_extension"
      pattern: "semantic_views_register_shim"
    - from: "build.rs"
      to: "linker flags"
      via: "println!(cargo:rustc-link-arg=...)"
      pattern: "rustc-link-arg"
---

<objective>
Wire the build system: create build.rs to compile the C++ shim when the extension feature is active, apply symbol visibility linker flags, and call semantic_views_register_shim from init_extension in lib.rs.

Purpose: This is the plan where everything compiles. After this plan, `make debug` produces a working extension binary with C++ embedded, all existing tests pass, and symbol leakage is controlled.

Output: build.rs at repo root, src/lib.rs updated with extern C declaration and call.
</objective>

<execution_context>
@/Users/paul/.claude/get-shit-done/workflows/execute-plan.md
@/Users/paul/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/08-cpp-shim-infrastructure/08-RESEARCH.md
@.planning/phases/08-cpp-shim-infrastructure/08-01-SUMMARY.md
</context>

<interfaces>
<!-- Key types and patterns from src/lib.rs that the executor needs. -->
<!-- The init_extension function is where semantic_views_register_shim must be called. -->

From src/lib.rs (extension module, #[cfg(feature = "extension")]):
```rust
fn init_extension(
    con: &Connection,
    db_handle: ffi::duckdb_database,
) -> Result<(), Box<dyn Error>> {
    // ... catalog init, function registration ...
    // NEW: call semantic_views_register_shim as the LAST step
    // db_handle is ffi::duckdb_database which is *mut duckdb_database_struct (a *mut c_void equivalent)
    Ok(())
}

// The extern "C" declaration must be added INSIDE the `mod extension` block,
// gated by #[cfg(feature = "extension")] (which is already on the mod).
// Do NOT add it at the top level of lib.rs.

#[cfg(feature = "extension")]
extern "C" {
    fn semantic_views_register_shim(db_instance_ptr: *mut std::ffi::c_void);
}
```

From the research (PITFALLS.md — panic safety):
The call site should be wrapped in a way that panics cannot propagate across the FFI boundary.
For Phase 8 this is a no-op, so no panic risk exists. Use:
```rust
unsafe { semantic_views_register_shim(db_handle as *mut std::ffi::c_void); }
```
Place this call as the last statement before `Ok(())` in init_extension.
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Create build.rs with feature-gated C++ compilation and symbol visibility</name>
  <files>build.rs</files>
  <action>
Create `build.rs` at the repository root (same level as Cargo.toml). This file is automatically invoked by Cargo before compilation.

The complete file:

```rust
// build.rs
// Cargo build script — compiles the C++ shim when building the loadable extension.
//
// Design: Only the `extension` feature triggers C++ compilation. During `cargo test`
// (default/bundled feature), this script exits immediately — no C++ toolchain required.
//
// Symbol visibility: restricts the exported symbols of the cdylib to the two DuckDB
// entry points on Linux and macOS. Without this, Rust stdlib symbols leak into the
// extension binary.

fn main() {
    // Only compile the C++ shim when building the loadable extension binary.
    // CARGO_FEATURE_EXTENSION is set by Cargo when `--features extension` is passed.
    // During `cargo test` (uses default/bundled feature), this block is skipped.
    if std::env::var("CARGO_FEATURE_EXTENSION").is_err() {
        return;
    }

    cc::Build::new()
        .cpp(true)                          // C++ mode (uses CXX, not CC)
        .file("src/shim/shim.cpp")          // the only C++ source file
        .include("duckdb_capi/")            // vendored duckdb.hpp and header tree
        .flag_if_supported("-std=c++17")    // safe on GCC/clang; skipped on MSVC
        .warnings(false)                    // suppress DuckDB's own internal warnings
        .compile("semantic_views_shim");    // produces libsemantic_views_shim.a, auto-linked

    // Symbol visibility: restrict the cdylib's exported symbols to the DuckDB entry
    // points only. Without this, Rust stdlib symbols leak into the extension binary.
    // Windows: __declspec(dllexport) on the #[no_mangle] entry point handles visibility.
    let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
    let out_dir = std::env::var("OUT_DIR").unwrap();

    match target_os.as_str() {
        "linux" => {
            // ELF version script: only DuckDB entry points are globally visible.
            let map_path = format!("{out_dir}/semantic_views.map");
            std::fs::write(
                &map_path,
                "{\n  global:\n    semantic_views_init_c_api;\n    semantic_views_version;\n  local: *;\n};\n",
            )
            .expect("failed to write ELF version script");
            println!("cargo:rustc-link-arg=-Wl,--version-script={map_path}");
        }
        "macos" => {
            // Exported symbols list: only DuckDB entry points are externally visible.
            // macOS uses underscore-prefixed names in the exported symbols file.
            let exp_path = format!("{out_dir}/semantic_views.exp");
            std::fs::write(
                &exp_path,
                "_semantic_views_init_c_api\n_semantic_views_version\n",
            )
            .expect("failed to write macOS exported symbols list");
            println!("cargo:rustc-link-arg=-Wl,-exported_symbols_list,{exp_path}");
        }
        _ => {
            // Windows: no extra flags needed — MSVC dllexport handles visibility.
        }
    }
}
```

**Clippy note:** This file is a build script, not part of the main crate's lint scope. However, to stay consistent with the project's pedantic clippy setup, use `{out_dir}` format interpolation (Rust 2021 edition implicit captures) and avoid unnecessary `.to_string()` calls.
  </action>
  <verify>
    <automated>test -f /Users/paul/Documents/Dev/Personal/duckdb-semantic-views/build.rs && grep -q "CARGO_FEATURE_EXTENSION" /Users/paul/Documents/Dev/Personal/duckdb-semantic-views/build.rs && grep -q "shim.cpp" /Users/paul/Documents/Dev/Personal/duckdb-semantic-views/build.rs && grep -q "version-script\|exported_symbols_list" /Users/paul/Documents/Dev/Personal/duckdb-semantic-views/build.rs && echo "OK"</automated>
  </verify>
  <done>build.rs exists at repo root with: CARGO_FEATURE_EXTENSION guard, cc::Build compiling src/shim/shim.cpp with duckdb_capi/ include, symbol visibility linker flags for Linux and macOS.</done>
</task>

<task type="auto">
  <name>Task 2: Wire lib.rs to call semantic_views_register_shim and verify build</name>
  <files>src/lib.rs</files>
  <action>
**Step 1 — Add extern C declaration to src/lib.rs.**

Inside the `mod extension` block (which is already `#[cfg(feature = "extension")]`), add the following BEFORE the `init_extension` function definition:

```rust
// Extern C declaration for the C++ shim entry point.
// Compiled and linked when `--features extension` is active (see build.rs).
// Phase 8: no-op. Phases 10/11 add parser/pragma registration logic.
unsafe extern "C" {
    fn semantic_views_register_shim(db_instance_ptr: *mut std::ffi::c_void);
}
```

Note: Use `unsafe extern "C"` (Rust 2024 edition syntax) rather than `extern "C" { unsafe fn ... }`. Both forms work but `unsafe extern "C"` is preferred in recent Rust editions.

**Step 2 — Call semantic_views_register_shim in init_extension.**

In the `init_extension` function, add the shim call as the LAST step before `Ok(())`. Find the line:

```rust
        Ok(())
```

Replace it with:

```rust
        // Call C++ shim to register parser hooks and pragma callbacks.
        // Phase 8: no-op. Phases 10/11 add real logic.
        // Safety: db_handle is a valid duckdb_database for the extension lifetime.
        //         The shim does not outlive the database instance.
        unsafe { semantic_views_register_shim(db_handle.cast()); }

        Ok(())
```

`db_handle` is `ffi::duckdb_database` (a raw pointer). Use `.cast()` to convert to `*mut std::ffi::c_void` — this is cleaner than an `as` cast and avoids the pedantic clippy `ptr_as_ptr` lint.

**Step 3 — Verify the full build.**

Run the following checks in order:

```bash
# Check 1: cargo test must still pass (no C++ compilation, no regressions)
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo test

# Check 2: extension build must compile with C++ shim
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo build --no-default-features --features extension

# Check 3: symbol count on macOS (verify no stdlib leakage)
# Expected: exactly 1 line containing _semantic_views_init_c_api
nm -gU target/debug/libsemantic_views.dylib | grep ' T '

# Check 4: full Makefile build (mirrors CI)
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && make debug

# Check 5: SQLLogicTests (existing v0.1.0 functionality unaffected)
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && just test
```

If `cargo test` fails with a clippy or lint error related to the new code:
- The `unsafe extern "C"` block may need `#[allow(unsafe_code)]` if the project has a `#![deny(unsafe_code)]` lint. Check `src/lib.rs` — the existing code already uses `unsafe`, so this is not expected.
- If there is a lint about `cast()` on a pointer, the alternative `as *mut std::ffi::c_void` is acceptable.

If `cargo build --no-default-features --features extension` fails with a header not found error:
- Check that `duckdb_capi/duckdb.hpp` is present (Task 1 of plan 01).
- Check that the include paths in `shim.cpp` match the vendored tree structure. If `duckdb/main/config.hpp` is not found, the full header tree was not copied — rerun `just update-headers` after `cargo test` to repopulate.

If the symbol count check shows many symbols (stdlib leakage):
- Verify build.rs is actually being invoked: `cargo build --no-default-features --features extension -v 2>&1 | grep "Running.*build.rs"`.
- Verify the linker flags are being applied: look for `--version-script` or `-exported_symbols_list` in the verbose output.
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo test 2>&1 | tail -5 && cargo build --no-default-features --features extension 2>&1 | tail -5 && echo "BUILD_OK"</automated>
  </verify>
  <done>
src/lib.rs has unsafe extern "C" { fn semantic_views_register_shim(...) } inside mod extension, and calls it in init_extension before Ok(()). cargo test passes (no C++ in test mode). cargo build --no-default-features --features extension succeeds (C++ compiled and linked). make debug succeeds. just test passes (existing SQLLogicTests unaffected).
  </done>
</task>

</tasks>

<verification>
Overall phase checks — ALL must pass:

1. `cargo test` — no regressions, runs without any C++ compilation
2. `cargo build --no-default-features --features extension` — C++ shim compiled, linked, no errors
3. Symbol check: `nm -gU target/debug/libsemantic_views.dylib | grep ' T '` returns exactly 1 line (`_semantic_views_init_c_api`) — no stdlib leakage
4. `make debug` — full Makefile build mirrors CI and succeeds
5. `just test` — all existing SQLLogicTests pass (v0.1.0 functionality unaffected)
6. `grep "semantic_views_register_shim" src/lib.rs` — extern C declaration and call both present
7. `grep "CARGO_FEATURE_EXTENSION" build.rs` — build guard present
</verification>

<success_criteria>
Phase 8 complete when:
- cargo test passes (no C++ in test path)
- cargo build --no-default-features --features extension produces a working cdylib with C++ shim compiled in
- make debug succeeds (full CI-mirror build)
- just test passes (existing v0.1.0 SQLLogicTests unaffected)
- Symbol count on macOS/Linux confirms no stdlib leakage
- build.rs, src/lib.rs contain the wiring described in must_haves.artifacts
</success_criteria>

<output>
After completion, create `.planning/phases/08-cpp-shim-infrastructure/08-02-SUMMARY.md`
</output>
