274 lines
12 KiB
Markdown
274 lines
12 KiB
Markdown
# Phase 0 — Infrastructure
|
|
|
|
> Goal of Phase 0: **make the entire stack compile and run on Linux, macOS, and
|
|
> Windows.** No editing, no rendering features. Success = the engine library
|
|
> builds clean on all three platforms and the smoke test passes. Phase 1 does
|
|
> not begin until Gate G0 and G0b are reached.
|
|
|
|
## Phase 0 task board
|
|
|
|
| # | Task | Owner | This session |
|
|
|---|------|-------|--------------|
|
|
| 1 | CMake root + vcpkg + CI/CD pipeline | Dev 1 | **Done — scaffolded** |
|
|
| 2 | PDFium build (depot_tools + GN + Ninja) | Dev 1 | Build scripts staged; not yet run |
|
|
| 3 | Skia build integration | Dev 2 | Not started |
|
|
| 4 | FreeType + HarfBuzz vcpkg integration | Dev 3 | In manifest; wrappers not started |
|
|
| 5 | FastAPI service scaffolding | Dev 1 | Placeholder dir only |
|
|
| 6 | React + TypeScript frontend scaffolding | Dev 2 | Placeholder dir only |
|
|
| 7 | WASM hello-world build (Emscripten) | Dev 1 | Toolchain hook + preset stubbed |
|
|
| 8 | Frozen interface contracts (Gate G0b) | All | Placeholder header; **not designed** |
|
|
|
|
This session delivered **Task 1 in full** plus the repository structure for
|
|
everything else. Scope was deliberately limited to the build pipeline — see
|
|
"What is intentionally not done" below.
|
|
|
|
## What was built this session
|
|
|
|
```
|
|
Code/
|
|
├── CMakeLists.txt root build; refuses to configure without a pinned baseline
|
|
├── CMakePresets.json debug/release/asan per platform + a wasm stub
|
|
├── vcpkg.json dependency manifest (freetype, harfbuzz, spdlog, gtest)
|
|
├── .clang-format .clang-tidy style + naming rules from blueprint §16.1
|
|
├── .gitignore .gitattributes .editorconfig
|
|
├── cmake/
|
|
│ ├── pdfium.cmake turns the PDFium install tree into pdfium::pdfium
|
|
│ ├── CompilerWarnings.cmake high warning levels per compiler
|
|
│ ├── Sanitizers.cmake ASan/UBSan wiring
|
|
│ └── toolchains/wasm.cmake Emscripten hook (stub)
|
|
├── engine/
|
|
│ ├── CMakeLists.txt
|
|
│ ├── include/pdfengine/ public headers (version, umbrella, pdf_document placeholder)
|
|
│ ├── src/core/ engine_info.cpp — version/build introspection
|
|
│ ├── src/parser/ pdfium_loader — the ONLY FPDF_-allowed dir (Rule R2)
|
|
│ └── tests/ gtest smoke test backing Gate G0
|
|
├── third_party/pdfium/ from-source build scripts + pinned-ref file + args.gn
|
|
├── scripts/
|
|
│ ├── bootstrap.{sh,ps1} installs vcpkg, pins the dependency baseline
|
|
│ └── check_pdfium_boundary.{sh,ps1} Rule R2 enforcement
|
|
├── .github/workflows/ci.yml Linux/macOS/Windows build matrix + lint jobs
|
|
├── bindings/ gateway/ frontend/ wasm/ corpus/ placeholder dirs with READMEs
|
|
└── docs/phase0.md this file
|
|
```
|
|
|
|
## How to build (developer onboarding)
|
|
|
|
### Prerequisites
|
|
|
|
| Tool | Version | Notes |
|
|
|------|---------|-------|
|
|
| CMake | >= 3.25 | presets v6 |
|
|
| Ninja | any recent | the only generator used |
|
|
| C++ compiler | MSVC 19.36+ / GCC 13+ / Clang 16+ | needs C++23 |
|
|
| vcpkg | — | `scripts/bootstrap` installs it if `VCPKG_ROOT` is unset |
|
|
| Git | any recent | |
|
|
| clang-format | 22.1.5 | not natively packaged on Windows — `pip install clang-format==22.1.5` (CI is pinned to this exact version) |
|
|
|
|
> **Windows:** either Visual Studio 2022/2026 (with the *"Desktop development
|
|
> with C++"* workload) or **Build Tools 2026** (no IDE — installer product
|
|
> `Microsoft.VisualStudio.Product.BuildTools`) is supported. `cl.exe` is not on
|
|
> `PATH` by default — run builds from a *Developer PowerShell* or import
|
|
> `VC\Auxiliary\Build\vcvars64.bat` first. Build Tools is not a default
|
|
> `vswhere` product, so detection scripts need `vswhere -products *` (the
|
|
> PDFium build script already does this). CI uses `ilammy/msvc-dev-cmd`.
|
|
>
|
|
> `VCPKG_ROOT` set via `setx` (or the bootstrap script's persistent install)
|
|
> does **not** propagate into already-open shells — set `$env:VCPKG_ROOT`
|
|
> explicitly in that shell, or open a new terminal. PowerShell 5.1 is fine;
|
|
> `pwsh` (7+) is not required by anything in this repo.
|
|
|
|
### Steps
|
|
|
|
```sh
|
|
# 1. One-time setup — checks tools, installs vcpkg, pins the dependency baseline.
|
|
pwsh scripts/bootstrap.ps1 # Windows
|
|
./scripts/bootstrap.sh # Linux / macOS
|
|
|
|
# 2. Configure + build + test.
|
|
cmake --preset windows-debug # linux-debug | macos-debug
|
|
cmake --build --preset windows-debug
|
|
ctest --preset windows-debug
|
|
```
|
|
|
|
The first configure compiles the vcpkg dependencies (freetype, harfbuzz,
|
|
spdlog, gtest) — slow once, cached after.
|
|
|
|
### Building with PDFium
|
|
|
|
PDFium is built separately from source (Task 2):
|
|
|
|
```sh
|
|
# Pin the revision first — edit third_party/pdfium/pdfium.pinned (see its README).
|
|
pwsh third_party/pdfium/build_pdfium.ps1 # or .sh
|
|
```
|
|
|
|
Until then the engine builds with PDFium code paths `#ifdef`-ed out, which is
|
|
the correct Phase 0 default — it keeps the pipeline green while Task 2 runs.
|
|
|
|
On Linux/macOS, enabling PDFium is a single flag added to a debug build:
|
|
|
|
```sh
|
|
cmake --preset linux-debug -DPDFENGINE_WITH_PDFIUM=ON
|
|
```
|
|
|
|
On Windows there is more to it — see the next section.
|
|
|
|
### Windows + PDFium
|
|
|
|
PDFium's static-lib GN build forces the static CRT (`/MT`, `is_debug=false`)
|
|
and offers no knob for "static lib + dynamic CRT". The engine, vcpkg deps,
|
|
and PDFium must therefore all use the same static CRT, or the link dies with
|
|
`LNK2038: 'RuntimeLibrary' mismatch`. The repo is wired for this ("Option A,
|
|
all static CRT, release-flavored"):
|
|
|
|
- The hidden `windows-base` preset in [CMakePresets.json](../CMakePresets.json)
|
|
sets `VCPKG_TARGET_TRIPLET=x64-windows-static` (vcpkg deps as static lib +
|
|
static CRT — first configure rebuilds them, ~7 min one-time) and
|
|
`CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>`.
|
|
- PDFium's own [args.gn](../third_party/pdfium/args.gn) keeps `is_debug=false`
|
|
(i.e. `/MT`).
|
|
- The PDFium-linked engine build must be **RelWithDebInfo, not Debug** — a
|
|
Debug engine is `/MTd` and still mismatches PDFium's `/MT`. The plain
|
|
`windows-debug -DPDFENGINE_WITH_PDFIUM=ON` recipe will not link; use the
|
|
`win-local-pdfium` user preset below.
|
|
|
|
#### `win-local-pdfium` user preset
|
|
|
|
`CMakeUserPresets.json` is git-ignored (build dirs are per-developer and live
|
|
outside OneDrive). Drop this preset in at `Code/CMakeUserPresets.json`, with
|
|
your own username in `binaryDir`:
|
|
|
|
```json
|
|
{
|
|
"version": 6,
|
|
"configurePresets": [
|
|
{
|
|
"name": "win-local-pdfium",
|
|
"inherits": "windows-release",
|
|
"binaryDir": "C:/Users/<you>/pdfeng-build/win-local-pdfium",
|
|
"cacheVariables": { "PDFENGINE_WITH_PDFIUM": "ON" }
|
|
}
|
|
],
|
|
"buildPresets": [
|
|
{ "name": "win-local-pdfium", "configurePreset": "win-local-pdfium" }
|
|
],
|
|
"testPresets": [
|
|
{ "name": "win-local-pdfium", "inherits": "common", "configurePreset": "win-local-pdfium" }
|
|
]
|
|
}
|
|
```
|
|
|
|
Then, from a shell with `vcvars64.bat` imported:
|
|
|
|
```powershell
|
|
cmake --preset win-local-pdfium
|
|
cmake --build --preset win-local-pdfium
|
|
ctest --preset win-local-pdfium
|
|
```
|
|
|
|
Success looks like `pdfengine_smoke.exe` linking cleanly and logging
|
|
`pdfium=on`. The build dir is intentionally outside OneDrive and on a
|
|
space-free path — see the OneDrive section below for why.
|
|
|
|
#### Gotcha: depot_tools shadows `ninja`
|
|
|
|
The PDFium build adds `depot_tools` to `PATH` and the depot_tools `ninja` /
|
|
`ninja.bat` are not real Ninja — they fail with:
|
|
|
|
```
|
|
Running ninja --version failed with unknown error
|
|
... CMAKE_CXX_COMPILER not set, after EnableLanguage
|
|
```
|
|
|
|
If `depot_tools` ended up on your persistent `PATH`, CMake will pick its
|
|
broken `ninja` for the engine build. Two fixes:
|
|
|
|
- **Short-term:** point CMake at the real Ninja explicitly, e.g.
|
|
`cmake --preset win-local-pdfium -D CMAKE_MAKE_PROGRAM=C:/path/to/real/ninja.exe`.
|
|
This caches, so only the first configure needs the flag.
|
|
- **Long-term (recommended):** keep `depot_tools` **off** the persistent
|
|
`PATH`. `third_party/pdfium/build_pdfium.ps1` already prepends it
|
|
per-run, so the PDFium build still works.
|
|
|
|
## Dependency pinning
|
|
|
|
The blueprint rule is *"pin all dependency versions on Day 1, never track
|
|
rolling HEAD."* Two mechanisms:
|
|
|
|
- **vcpkg deps** — `scripts/bootstrap` runs `vcpkg x-update-baseline
|
|
--add-initial-baseline`, which writes a `builtin-baseline` commit into
|
|
`vcpkg.json`. That pins the entire dependency registry to one commit. The
|
|
root `CMakeLists.txt` **refuses to configure** until this is present.
|
|
→ **The first commit to the repo must include the bootstrapped `vcpkg.json`**,
|
|
otherwise CI fails at the configure step (by design).
|
|
- **PDFium** — `third_party/pdfium/pdfium.pinned` holds an exact commit SHA.
|
|
The build script refuses to run while it is the placeholder. Rebases are a
|
|
deliberate, scheduled (quarterly) action.
|
|
|
|
## Engineering conventions
|
|
|
|
- **Naming** (`.clang-tidy`): `CamelCase` types, `camelBack` functions,
|
|
`snake_case` file names.
|
|
- **Errors**: `std::expected<T, E>` internally; `int error_code` across the C ABI.
|
|
- **Branches**: `main`, `develop`, `feature/*`, `release/*`.
|
|
- **Rule R2**: only `engine/src/parser/` may use raw `FPDF_*` APIs —
|
|
enforced by `scripts/check_pdfium_boundary.*` locally and in CI.
|
|
|
|
## What is intentionally NOT done this session
|
|
|
|
- **PDFium is not actually built** — scripts are staged; the revision needs to
|
|
be pinned and the (long) build run as the second half of Task 2.
|
|
- **No interface contracts** — `engine/include/pdfengine/pdf_document.hpp` is a
|
|
placeholder. `PdfDocument` / `PdfPage` are designed and frozen at **Gate G0b**
|
|
in an all-devs session; nothing proceeds until it is signed off.
|
|
- **Skia / FreeType / HarfBuzz wrappers** — FreeType + HarfBuzz are in the vcpkg
|
|
manifest and link-tested, but the actual wrappers are Dev 3's Phase 0/1 work.
|
|
Skia is Dev 2's task.
|
|
- **FastAPI / React / WASM** — placeholder directories only. WASM has a toolchain
|
|
hook and preset stub so the integration point exists (Rule R5: WASM never
|
|
blocks shipping).
|
|
|
|
## Gates ahead
|
|
|
|
| Gate | Criterion | Unblocks |
|
|
|------|-----------|----------|
|
|
| **G0** | All platforms build clean; PDFium + Skia + FreeType + HarfBuzz compile | Phase 1 |
|
|
| **G0b** | `PdfDocument` / `PdfPage` contracts locked by all 3 devs | Coding begins |
|
|
|
|
The CI `build` matrix is the automated half of G0. The smoke test
|
|
(`engine/tests/smoke_test.cpp`) is what it runs.
|
|
|
|
## OneDrive warning
|
|
|
|
This checkout lives under `OneDrive\Work\Maskan\PDF Editor\Code`. The path is
|
|
both OneDrive-synced **and** contains a space (`PDF Editor`). Both bite C++
|
|
builds:
|
|
|
|
1. **Sync churn** — build output is thousands of `.obj`/`.o` files. `out/` is
|
|
git-ignored, but OneDrive still tries to upload it.
|
|
2. **File locks** — OneDrive can hold a handle on a file mid-sync, causing
|
|
intermittent "permission denied" errors during compile or link.
|
|
3. **Spaces in build paths break tooling** — vcpkg/meson (harfbuzz) fail
|
|
with `LNK1181` when `vcpkg_installed` is under the spaced path, and
|
|
`depot_tools` / GN / Ninja `.bat` wrappers cannot handle a space in their
|
|
own path at all.
|
|
|
|
**On Windows, building inside the repo path is not viable** — put the build
|
|
dir outside OneDrive on a space-free path. The `win-local-pdfium` preset
|
|
above already does this (`C:/Users/<you>/pdfeng-build/...`); do the same for
|
|
any non-PDFium preset by overriding `binaryDir`:
|
|
|
|
```powershell
|
|
cmake --preset windows-release -B C:/Users/<you>/pdfeng-build/windows-release
|
|
```
|
|
|
|
The PDFium build is even stricter: `third_party/pdfium/build_pdfium.ps1`
|
|
takes a `PDFIUM_BUILD_ROOT` env var and hard-errors if it contains a space.
|
|
Use e.g. `C:\Users\<you>\pdfium-build`.
|
|
|
|
On Linux/macOS the OneDrive path is still a sync nuisance but the toolchain
|
|
itself is fine. Either point the build dir outside OneDrive, or exclude
|
|
`out/` and `vcpkg/` from sync, or pause sync while building.
|
|
|
|
Long term, the repository should live outside OneDrive on a real Git remote.
|