From bd91be9cb01146beda0f161ff5335cd5c2bd575f Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sun, 5 Jul 2026 16:04:37 +0200 Subject: [PATCH 1/3] dev: add VS Code integration (build, test, CLI, debug, profile) Configure the repo for VS Code with parity to the existing CLion setup: - CMake Tools drives the Conan-generated presets for building. - clangd for IntelliSense (matches .clangd / .clang-tidy); cpptools marked unwanted to avoid engine conflicts. - Tasks for building odr/odr_test/CLI, running filtered gtest suites, running translate, and Instruments (xctrace) Time Profiler profiling. - CodeLLDB launch configs to debug odr_test/translate/meta/server. - scripts/gen-vscode-env.py extracts the Conan runtime env (pdf2htmlEX, poppler, fontconfig, wvWare, libmagic paths) from the test preset into .vscode/.env; scripts/run-with-env.sh loads it for tasks. - .vscode/.env (machine-specific Conan cache paths) is gitignored. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PvSNQg6fxTXkmLj3g3RrY2 --- .gitignore | 2 + .vscode/extensions.json | 11 ++++ .vscode/launch.json | 69 +++++++++++++++++++++++++ .vscode/settings.json | 32 ++++++++++++ .vscode/tasks.json | 105 ++++++++++++++++++++++++++++++++++++++ scripts/gen-vscode-env.py | 43 ++++++++++++++++ scripts/run-with-env.sh | 21 ++++++++ 7 files changed, 283 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100755 scripts/gen-vscode-env.py create mode 100755 scripts/run-with-env.sh diff --git a/.gitignore b/.gitignore index 9b1a06b3c..50a05bb03 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,8 @@ build/ cmake-build-*/ .clangd CMakeUserPresets.json +# machine-specific Conan cache paths, generated by scripts/gen-vscode-env.py +.vscode/.env offline/ ## PDF CJK CMap input data (fetched by tools/pdf/generate_cid_data.py) diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..751ec8340 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + "recommendations": [ + "ms-vscode.cmake-tools", + "llvm-vs-code-extensions.vscode-clangd", + "vadimcn.vscode-lldb", + "twxs.cmake" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..0cce01e4f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,69 @@ +{ + "version": "0.2.0", + "inputs": [ + { + "id": "gtestFilter", + "type": "promptString", + "description": "GoogleTest --gtest_filter", + "default": "*" + }, + { + "id": "translateInput", + "type": "promptString", + "description": "Input document to translate (absolute path)" + }, + { + "id": "translateOutput", + "type": "promptString", + "description": "Output directory for HTML", + "default": "${workspaceFolder}/cmake-build-relwithdebinfo/output" + }, + { + "id": "metaInput", + "type": "promptString", + "description": "Input document for meta" + } + ], + "configurations": [ + { + "name": "Debug odr_test (filtered)", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/cmake-build-relwithdebinfo/test/odr_test", + "args": ["--gtest_filter=${input:gtestFilter}"], + "cwd": "${workspaceFolder}/cmake-build-relwithdebinfo", + "envFile": "${workspaceFolder}/.vscode/.env", + "preLaunchTask": "build: odr_test" + }, + { + "name": "Debug translate ", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/cmake-build-relwithdebinfo/cli/translate", + "args": ["${input:translateInput}", "${input:translateOutput}"], + "cwd": "${workspaceFolder}", + "envFile": "${workspaceFolder}/.vscode/.env", + "preLaunchTask": "build: translate" + }, + { + "name": "Debug meta ", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/cmake-build-relwithdebinfo/cli/meta", + "args": ["${input:metaInput}"], + "cwd": "${workspaceFolder}", + "envFile": "${workspaceFolder}/.vscode/.env", + "preLaunchTask": "build: all CLI tools" + }, + { + "name": "Debug server", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/cmake-build-relwithdebinfo/cli/server", + "args": [], + "cwd": "${workspaceFolder}", + "envFile": "${workspaceFolder}/.vscode/.env", + "preLaunchTask": "build: all CLI tools" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..81eca727d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,32 @@ +{ + // ---- Build: CMake Tools drives the Conan-generated presets ---- + // Presets come from CMakeUserPresets.json -> cmake-build-*/CMakePresets.json, + // written by `conan install`. Pick the active one from the status bar. + "cmake.useCMakePresets": "always", + "cmake.options.statusBarVisibility": "visible", + + // ---- IntelliSense: clangd (matches the repo's .clangd / .clang-tidy) ---- + // Disable the built-in cpptools engine so it doesn't fight clangd. + "C_Cpp.intelliSenseEngine": "disabled", + "clangd.arguments": [ + "--compile-commands-dir=${workspaceFolder}/cmake-build-relwithdebinfo", + "--background-index", + "--clang-tidy", + "--header-insertion=never", + "--pretty" + ], + + // ---- Formatting: clang-format (.clang-format at repo root) ---- + "editor.formatOnSave": false, + "[cpp]": { + "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd" + }, + "[c]": { + "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd" + }, + + "files.associations": { + "*.hpp": "cpp", + "*.ipp": "cpp" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..2e49a6f1e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,105 @@ +{ + "version": "2.0.0", + "options": { + "cwd": "${workspaceFolder}" + }, + "inputs": [ + { + "id": "gtestFilter", + "type": "promptString", + "description": "GoogleTest --gtest_filter", + "default": "*" + }, + { + "id": "translateInput", + "type": "promptString", + "description": "Input document to translate (absolute path)" + }, + { + "id": "translateOutput", + "type": "promptString", + "description": "Output directory for HTML (absolute path)", + "default": "${workspaceFolder}/cmake-build-relwithdebinfo/output" + } + ], + "tasks": [ + { + "label": "build: odr (library)", + "type": "shell", + "command": "cmake --build cmake-build-relwithdebinfo --target odr", + "group": "build", + "problemMatcher": ["$gcc"] + }, + { + "label": "build: odr_test", + "type": "shell", + "command": "cmake --build cmake-build-relwithdebinfo --target odr_test", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": ["$gcc"] + }, + { + "label": "build: translate", + "type": "shell", + "command": "cmake --build cmake-build-relwithdebinfo --target translate", + "group": "build", + "problemMatcher": ["$gcc"] + }, + { + "label": "build: all CLI tools", + "type": "shell", + "command": "cmake --build cmake-build-relwithdebinfo --target translate back_translate meta server", + "group": "build", + "problemMatcher": ["$gcc"] + }, + { + "label": "test: run odr_test (filtered)", + "type": "shell", + "command": "scripts/run-with-env.sh ./test/odr_test --gtest_filter='${input:gtestFilter}'", + "options": { + "cwd": "${workspaceFolder}/cmake-build-relwithdebinfo" + }, + "dependsOn": ["build: odr_test"], + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "cli: translate ", + "type": "shell", + "command": "scripts/run-with-env.sh ./cmake-build-relwithdebinfo/cli/translate '${input:translateInput}' '${input:translateOutput}'", + "dependsOn": ["build: translate"], + "problemMatcher": [] + }, + { + "label": "env: regenerate .vscode/.env", + "detail": "Re-run after every `conan install` to refresh Conan cache paths.", + "type": "shell", + "command": "python3 scripts/gen-vscode-env.py", + "problemMatcher": [] + }, + { + "label": "profile: Instruments Time Profiler (odr_test, filtered)", + "detail": "Records a .trace with Instruments; opens in Instruments.app when done.", + "type": "shell", + "command": "rm -rf profile.trace && scripts/run-with-env.sh xctrace record --template 'Time Profiler' --output profile.trace --launch -- ./test/odr_test --gtest_filter='${input:gtestFilter}' && open profile.trace", + "options": { + "cwd": "${workspaceFolder}/cmake-build-relwithdebinfo" + }, + "dependsOn": ["build: odr_test"], + "problemMatcher": [] + }, + { + "label": "profile: Instruments Time Profiler (translate)", + "detail": "Records a .trace of the translate CLI on one file.", + "type": "shell", + "command": "rm -rf profile.trace && scripts/run-with-env.sh xctrace record --template 'Time Profiler' --output profile.trace --launch -- ./cmake-build-relwithdebinfo/cli/translate '${input:translateInput}' '${input:translateOutput}' && open profile.trace", + "dependsOn": ["build: translate"], + "problemMatcher": [] + } + ] +} diff --git a/scripts/gen-vscode-env.py b/scripts/gen-vscode-env.py new file mode 100755 index 000000000..3dd12b975 --- /dev/null +++ b/scripts/gen-vscode-env.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""Extract the runtime environment from the Conan-generated CMake test preset +into a plain KEY=VALUE file that VS Code launch configs (CodeLLDB `envFile`) and +tasks can consume. + +The paths point into the Conan cache and change whenever dependencies are +re-installed, so re-run this after every `conan install` (there is a VS Code task +"env: regenerate .vscode/.env" that does exactly this). + +Usage: scripts/gen-vscode-env.py [BUILD_DIR] + BUILD_DIR defaults to cmake-build-relwithdebinfo. +""" +import json +import os +import re +import sys +from pathlib import Path + +repo = Path(__file__).resolve().parent.parent +build_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else repo / "cmake-build-relwithdebinfo" +preset = build_dir / "CMakePresets.json" + +if not preset.is_file(): + sys.exit(f"no CMakePresets.json in {build_dir} — run `conan install` first") + +data = json.loads(preset.read_text()) +test_presets = data.get("testPresets") or [{}] +env = test_presets[0].get("environment", {}) + + +def resolve(value: str) -> str: + # Resolve `$penv{VAR}` (parent/current environment) references to the value + # present when this script runs; CodeLLDB envFile has no variable expansion. + return re.sub(r"\$penv\{(\w+)\}", lambda m: os.environ.get(m.group(1), ""), value) + + +out = repo / ".vscode" / ".env" +out.parent.mkdir(exist_ok=True) +lines = [f"{k}={resolve(v)}" for k, v in env.items()] +out.write_text("\n".join(lines) + "\n") +print(f"wrote {len(lines)} vars to {out}") +for line in lines: + print(" " + line.split("=", 1)[0]) diff --git a/scripts/run-with-env.sh b/scripts/run-with-env.sh new file mode 100755 index 000000000..3d951b835 --- /dev/null +++ b/scripts/run-with-env.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Load the Conan runtime env from .vscode/.env (see gen-vscode-env.py) and exec +# the given command with it. Used by VS Code tasks so the test binary and CLI +# tools can find pdf2htmlEX / poppler / fontconfig / wvWare / libmagic data. +# +# Usage: scripts/run-with-env.sh [args...] +set -euo pipefail + +repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +env_file="$repo/.vscode/.env" + +if [[ -f "$env_file" ]]; then + set -a + # shellcheck disable=SC1090 + source "$env_file" + set +a +else + echo "warning: $env_file missing — run scripts/gen-vscode-env.py" >&2 +fi + +exec "$@" From e49c9c93e21af93e0be6365d74833d5ccf310d76 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sun, 5 Jul 2026 16:19:12 +0200 Subject: [PATCH 2/3] dev: fix env wrapper path in build-dir VS Code tasks The odr_test test and profiling tasks override cwd to the build dir but invoked scripts/run-with-env.sh relatively, resolving it to a path inside the build tree that does not exist. Anchor the wrapper to ${workspaceFolder} while keeping ./test/odr_test relative to the build cwd. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PvSNQg6fxTXkmLj3g3RrY2 --- .vscode/tasks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2e49a6f1e..a008f0fec 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -57,7 +57,7 @@ { "label": "test: run odr_test (filtered)", "type": "shell", - "command": "scripts/run-with-env.sh ./test/odr_test --gtest_filter='${input:gtestFilter}'", + "command": "${workspaceFolder}/scripts/run-with-env.sh ./test/odr_test --gtest_filter='${input:gtestFilter}'", "options": { "cwd": "${workspaceFolder}/cmake-build-relwithdebinfo" }, @@ -86,7 +86,7 @@ "label": "profile: Instruments Time Profiler (odr_test, filtered)", "detail": "Records a .trace with Instruments; opens in Instruments.app when done.", "type": "shell", - "command": "rm -rf profile.trace && scripts/run-with-env.sh xctrace record --template 'Time Profiler' --output profile.trace --launch -- ./test/odr_test --gtest_filter='${input:gtestFilter}' && open profile.trace", + "command": "rm -rf profile.trace && ${workspaceFolder}/scripts/run-with-env.sh xctrace record --template 'Time Profiler' --output profile.trace --launch -- ./test/odr_test --gtest_filter='${input:gtestFilter}' && open profile.trace", "options": { "cwd": "${workspaceFolder}/cmake-build-relwithdebinfo" }, From 9a7f338aa1f4858a8d4950e98e45f4f1253a3238 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sun, 5 Jul 2026 16:31:42 +0200 Subject: [PATCH 3/3] dev: add VS Code tasks for reference-output comparison scripts Wrap test/scripts/compare_output.sh and compare_output_server.sh as tasks so the Docker-based visual diff can be run from VS Code. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PvSNQg6fxTXkmLj3g3RrY2 --- .vscode/tasks.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a008f0fec..576b74009 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -75,6 +75,21 @@ "dependsOn": ["build: translate"], "problemMatcher": [] }, + { + "label": "compare: reference output (headless)", + "detail": "Runs compare-html in Docker (Firefox headless) against test/data/reference-output. Build + run odr_test first to populate output.", + "type": "shell", + "command": "test/scripts/compare_output.sh", + "problemMatcher": [] + }, + { + "label": "compare: reference output (server on :8000)", + "detail": "Runs compare-html-server in Docker; open http://localhost:8000 to inspect diffs interactively. Ctrl-C in the terminal to stop.", + "type": "shell", + "command": "test/scripts/compare_output_server.sh", + "isBackground": true, + "problemMatcher": [] + }, { "label": "env: regenerate .vscode/.env", "detail": "Re-run after every `conan install` to refresh Conan cache paths.",