Skip to main content

vcpkg

Authoritative guide for vcpkg library integration in imodel-native. USE FOR adding a new vcpkg-managed library, migrating an existing library to vcpkg, updating a vcpkg-managed library version, or diagnosing a failing Mend source scan. Covers the sequential install chain, PartFile wiring, mke patterns, triplet selection, version pinning, and Mend source-scan configuration (`vcpkg-mend.json`).

Quellinformationen

Repository
iTwin/imodel-native
Letzte Quellaktivität
22. September 2026 um 20:39
Erkannte Sprache von SKILL.md
Englisch
Sterne
26
Forks
17

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
vcpkg
description
Authoritative guide for vcpkg library integration in imodel-native. USE FOR adding a new vcpkg-managed library, migrating an existing library to vcpkg, updating a vcpkg-managed library version, or diagnosing a failing Mend source scan. Covers the sequential install chain, PartFile wiring, mke patterns, triplet selection, version pinning, and Mend source-scan configuration (`vcpkg-mend.json`).
# vcpkg Integration in imodel-native All vcpkg documentation for humans lives in [`iModelCore/libsrc/VCPKG.md`](../../../iModelCore/libsrc/VCPKG.md). This skill summarises the patterns an agent needs to get the build wiring right. --- ## The Sequential Install Chain All `vcpkg install` calls run through a **single sequential chain** defined in `iModelCore/libsrc/vcpkg.PartFile.xml`. The chain currently is: ``` vcpkg (bootstrap) └─► vcpkg_install_compress └─► vcpkg_install_png └─► vcpkg_install_pugixml └─► vcpkg_install_openssl └─► vcpkg_install_curl └─► vcpkg_install_crashpad ``` Each link is a separate Part with its own `vcpkg_install_<consumer>.mke` that calls `vcpkg_run_install.ps1` / `vcpkg_run_install.sh`. Chaining ensures no two `vcpkg` processes ever run concurrently — concurrent runs against the same install root collide on `vcpkg-running.lock` and corrupt the build. **Consumer `.mke` files do NOT call `vcpkg_run_install` themselves.** By the time a consumer `.mke` runs, its install is already complete. --- ## Adding a New vcpkg Library > **Always update `iModelCore/libsrc/README.md`** — add a row to the library table with the directory, library name, version, and `Yes` in the vcpkg column. ### 1. Create the manifest directory Under `iModelCore/libsrc/<mylib>/`: - `vcpkg.json` — list dependency with `version>=` under `dependencies` and exact version under `overrides` - `vcpkg-configuration.json` — copy from an existing consumer (e.g. `compress/`); update `baseline` if needed - `vcpkg-mend.json` — list the triplet graph(s) whose union downloads all upstream source used by the consumer; the scan never compiles for the selected target, so triplets need not match the Mend host; prefer one source-superset graph, and add multiple triplets only for platform-specific downloads - `triplets/` — platform-specific triplet files if the defaults in `iModelCore/libsrc/` are not sufficient (see `compress/triplets/` for examples) For every Apple overlay triplet, explicitly set `VCPKG_OSX_DEPLOYMENT_TARGET`. Before creating or changing an `arm64-osx.cmake` or `arm64-ios.cmake` triplet, check the effective `MACOS_DEPLOYMENT_TARGET` or `IOS_DEPLOYMENT_TARGET` used by BentleyBuild. Their public defaults are defined in [`$(SrcRoot)bsicommon/PublicSDK/ApplyToolSet_CLang.mki`](../../../../bsicommon/PublicSDK/ApplyToolSet_CLang.mki), but build strategies may override them. Mirror the effective build value, not the product's official OS support floor. Omitting this setting lets vcpkg inherit the host SDK's deployment target and can produce objects that are too new to link into BentleyBuild outputs. For static libraries rolled into `imodeljs.node`, preserve the previous build's effective symbol visibility and verify both the vcpkg archive and final add-on. Do not assume `-fvisibility=hidden` is sufficient: upstream public-API annotations may restore default visibility. Audit library-specific visibility options; curl, for example, requires `CURL_HIDDEN_SYMBOLS=OFF` so its public API inherits the triplet's hidden visibility instead of binding to another curl already loaded in the process. > **Check whether the library links cleanly into Windows DEBUG builds.** Some libraries fail to > link into Windows DEBUG unless their debug artifact is made release-CRT-compatible — either by > forcing release-only triplets (`set(VCPKG_BUILD_TYPE release)`) or by fixing up the vcpkg Debug > config to link the release CRT while keeping its diagnostics; others are fine without any > change. See the > [Windows debug builds link the release CRT](#windows-debug-builds-link-the-release-crt) > pitfall below to decide, and for both fixes. `crashpad/triplets/` and > `pugixml/triplets/` are working examples of libraries that needed it. ### 2. Create `iModelCore/libsrc/vcpkg_install_<mylib>.mke` ```makefile %include mdl.mki mylibDir = $(_MakeFilePath)<mylib> installRoot = $(OutputRootDir)vcpkg_installed/<mylib> # Add vcpkgWindowsMDCRT = 1 here if the library must link /MD on Windows (like openssl). # Add vcpkgUseVeracodeTriplet = 1 here ONLY if this library's base triplet sets explicit # -RTC flags and you have provided a triplets/x64-windows-static-veracode.cmake overlay # that omits them (see compress/ and crashpad/). Libraries whose triplets set no -RTC # flags need no veracode variant and should leave this undefined. %include $(_MakeFilePath)vcpkg.mki always: %if defined (winNT) powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "$(_MakeFilePath)vcpkg_run_install.ps1" "$(mylibDir)" "$(installRoot)" "$(vcpkgTriplet)" %else $(_MakeFilePath)vcpkg_run_install.sh $(mylibDir) $(installRoot) $(vcpkgTriplet) %endif ``` `$(_MakeFilePath)` resolves to `libsrc/` because the file lives there, so the paths to `<mylib>/`, `vcpkg_run_install.ps1`/`.sh`, and `vcpkg.mki` are all correct. ### 3. Extend the chain in `iModelCore/libsrc/vcpkg.PartFile.xml` Insert a new link into the chain. The chain must stay **linear**, and — with one important exception below — where a link sits does not affect correctness because every consumer depends on its own named part. Prefer placing a more basic/foundational library (e.g. a compression or image codec that other libraries build on) earlier in the chain, and insert a new link wherever it reads most naturally alongside its peers. **Platform-coverage constraint (do not violate).** The real invariant is that the chain must be **linear on every individual platform**: no two `vcpkg_install_*` parts that are *both built on a given platform* may run without a dependency edge between them, or they race on `vcpkg-running.lock` and corrupt the build. Across platforms the graph may **fork into a tree** — only each platform's projection of it has to be a single line. Consequences: - A chain link must depend on a predecessor built on **at least the same platforms** as the link itself. Some links are platform-restricted (e.g. `vcpkg_install_crashpad` carries `OnlyPlatforms="linux*,x*,macos*"` — desktop only). If an all-platform link were appended after a platform-restricted one, then on the excluded platforms (iOS/Android) its predecessor is skipped, the link loses its chain predecessor, and it can run concurrently with an earlier link. - **Keep platform-restricted links at (or near) the tail**, after every all-platform link. When adding an **all-platform** link, depend it on the last **all-platform** predecessor (not on a platform-restricted one). If a restricted link currently sits where the new one belongs, insert the new link **before** it and re-parent the restricted link onto the new one. - Two links whose platform sets **overlap** must stay linearly ordered (one depends on the other). - Two links whose platform sets are **disjoint** never build on the same platform, so they can never run concurrently — they may safely **fork** off a common all-platform predecessor. This is when the tail becomes a tree: e.g. a hypothetical iOS-only library and desktop-only crashpad would each depend on the last all-platform link (`vcpkg_install_curl`), one branch live on iOS, the other on desktop. Do **not** instead chain a desktop-only link behind an iOS-only link (or vice-versa): that edge is dead on *both* platforms yet still strands one link without a predecessor. Example: curl (all platforms) was inserted **before** crashpad (desktop-only) — `curl → openssl`, and crashpad was re-parented from openssl onto curl. On iOS/Android crashpad is skipped and curl is the tail, still ordered after openssl. To append at the end (only when the current last link is built on a superset of your platforms): ```xml <Part Name="vcpkg_install_<mylib>" BentleyBuildMakeFile="vcpkg_install_<mylib>.mke"> <SubPart PartName="vcpkg_install_<current-last>" LibType="Static"/> </Part> ``` To insert mid-chain, point the new part at its predecessor and re-parent the following link onto the new part, keeping the chain linear. Whichever position you choose, update the sibling `.mke` comment ("Runs after vcpkg_install_<prev>…") on every link whose predecessor changed. Also add the new part to the `vcpkg_install_all` aggregate so the shared binary-cache warmer includes it. Mend source scanning treats a directory as a consumer only when it holds all three of `vcpkg.json`, `vcpkg-configuration.json`, and `vcpkg-mend.json`; anything else is skipped, so vendored upstream trees that ship their own `vcpkg.json` are harmless. Because an omitted `vcpkg-mend.json` would just skip the library, `vcpkg_run_install.ps1` and `vcpkg_run_install.sh` refuse to install a manifest directory that has no `vcpkg-mend.json` with a non-empty `triplets` array — so the omission fails that library's normal build on every platform. Every configured triplet must also have a matching overlay file. Select the smallest triplet set whose manifest and portfile branches cover all upstream downloads, and make sure it reaches every port pinned in `overrides` — the scan verifies that the ports named in `dependencies` **and** `overrides` all produced extracted source. The selected triplet does not need to match the Mend host because nothing is compiled for that target; for example, curl's `x64-linux` graph includes its common sources plus conditional c-ares, so no Windows graph is needed even though Mend runs on Windows. The host still needs a working toolchain of its own, since vcpkg builds host-triplet helper ports (`vcpkg-cmake` and friends) either way. The cross-consumer checks the wrappers cannot make — a misplaced `vcpkg-mend.json`, or a triplet with no overlay file — also run during Windows builds via the `vcpkg_validate_mend` part, so those mistakes fail the PR rather than the Mend pipeline. #### Auditing platform coverage Every consumer currently lists only `x64-linux`, which is a source-superset claim rather than a default. Nothing enforces it: the materialization check only sees ports named in `dependencies` and `overrides`, so a download that happens solely on an unlisted platform is skipped in silence. Two things put a download there, and both are easy to grep for: ```bash # platform-qualified dependency or feature (curl's c-ares is "osx | linux") grep -rn --include='vcpkg.json' '"platform"' iModelCore/libsrc/ # source fetch inside a target branch (crashpad pulls linux-syscall-support on Linux/Android only) grep -rn --include='*.cmake' 'VCPKG_TARGET_IS_' iModelCore/libsrc/*/*ports/ ``` Neither grep reaches a port whose portfile we do not vendor, so for those compare the observed graphs instead — the ports a real build resolved on a given platform against the ports the scan materialized: ```bash ls $OutRoot/vcpkg_installed/<consumer>/<triplet>/share/*/vcpkg_abi_info.txt # per-platform ls -d <scan-root>/<consumer>/<triplet>/buildtrees/*/src # what Mend saw ``` Host helper ports (`vcpkg-cmake` and friends) appear only in the first list; ignore them. Anything else present on a supported platform but absent from the scan needs a covering triplet added to that consumer's `vcpkg-mend.json`. Pinning a platform-conditional port in `overrides` is worth doing even when its version is already correct, because the materialization check then demands that the port appear in some configured graph — turning an invisible coverage gap into a scan failure. That lever does nothing for a conditional sub-source fetched inside another port's portfile (crashpad's `lss`); only a covering triplet helps there. ### 4. Wire the consumer PartFile In your library's `.PartFile.xml`, depend on the chain part with **`LibType="Static"`**. **Critical:** `$(OutputRootDir)` differs between static and dynamic builds (`static/vcpkg_installed/…` vs `vcpkg_installed/…`). The chain always runs static-only; dynamic builds do **not** run `vcpkg install` at all — their `.mke` reads the packages the static chain produced under `static/vcpkg_installed/…` (see step 5). Using `LibType="Static"` here ensures the static chain completes (and populates that install root) before a dynamic build starts, without triggering a redundant dynamic chain build that would race against the static one on the shared vcpkg git repo. ```xml <Part Name="MyLib" BentleyBuildMakeFile="MyLib.mke"> <!-- LibType="Static": chain is static-only. Dynamic builds read the packages from the static install root; they do not run vcpkg themselves (see step 5). --> <SubPart PartName="vcpkg_install_<mylib>" PartFile="iModelCore/libsrc/vcpkg" LibType="Static"/> ... ``` If a separate prewire/PublicAPI part also needs the install, give it the same `LibType="Static"` SubPart (see `BeOpenSSL.PartFile.xml` for the `__PublicAPI` example). ### 5. Write the consumer `.mke` Include `vcpkg.mki` to get `vcpkgTriplet`, then set `vcpkgInstallRoot`. **Key rule:** the chain always runs as Static, so the installed packages always live under the **static** `OutputRootDir`. Dynamic builds must redirect `vcpkgInstallRoot` to that same static location — do **not** call `vcpkg_run_install` from the dynamic `.mke` path, because this creates a concurrent vcpkg process that races against the static chain on shared global locks (registry git lock, cmake download rename, etc.). Use `CREATE_STATIC_LIBRARIES` (defined by bmake for static builds) to pick the right root: ```makefile # Static builds: packages are at $(OutputRootDir)vcpkg_installed/<mylib>/ # Dynamic builds: chain ran as Static; redirect to the same location. %if defined (CREATE_STATIC_LIBRARIES) vcpkgInstallRoot = $(OutputRootDir)vcpkg_installed/<mylib>/ %else vcpkgInstallRoot = $(OutputRootDir)static/vcpkg_installed/<mylib>/ %endif vcpkgTripletDir = $(vcpkgInstallRoot)$(vcpkgTriplet)/ vcpkgIncludeDir = $(vcpkgTripletDir)include/ vcpkgLibDir = $(vcpkgTripletDir)lib/ ``` No `vcpkg_run_install` call in the `.mke` at all — the chain part handles it. Libraries that **only** build as static (e.g. compress, crashpad client) can skip the `%if defined (CREATE_STATIC_LIBRARIES)` conditional and use `$(OutputRootDir)vcpkg_installed/…` directly — their `OutputRootDir` is always the static one. **If this library needs the release-only Windows triplets** (see the pitfall below), also link the release archive on Windows. Because those triplets set `VCPKG_BUILD_TYPE release`, no `debug/lib/` archive is produced, so a `%if defined (DEBUG)` branch that reaches for `$(vcpkgDbgLibDir)` would point at a nonexistent file. Gate the archive selection on the platform first: ```makefile %if $(TARGET_PLATFORM) == "Windows" # Windows always links the release archive: the Windows triplets force VCPKG_BUILD_TYPE # release because bmake links the release CRT even in DEBUG builds. vcpkgMyLib = $(vcpkgLibDir)$(myLibName) %elif defined (DEBUG) vcpkgMyLib = $(vcpkgDbgLibDir)$(myLibName) %else vcpkgMyLib = $(vcpkgLibDir)$(myLibName) %endif ``` Libraries that do **not** need the release-only triplets keep the plain `%if defined (DEBUG)` → `$(vcpkgDbgLibDir)` selection (e.g. `openssl/BeOpenSSL.mke`). ### 6. Migrating an existing (previously vendored) library When the library you are moving to vcpkg was previously vendored (its source checked into `iModelCore/libsrc/<mylib>/`), the vendored source deletion belongs in the **same** PR as the vcpkg wiring, but do **not** delete it up front. Keep the vendored source in place (the PR will likely be draft/WIP at this stage) until **after** the PR has passed its Copilot review, then remove the vendored code in a separate standalone commit within that same PR. Deleting the vendored source up front produces too many modified files for Copilot to review, and the review may not run at all. --- ## Updating an Existing Library Version > **Always update `iModelCore/libsrc/README.md`** — bump the version in the library table to match the new version in `vcpkg.json`. 1. Edit `iModelCore/libsrc/<consumer>/vcpkg.json`: - Update the `version>=` value under `dependencies` - Update the matching entry in `overrides` - Re-run the [platform coverage audit](#auditing-platform-coverage): a new upstream version can add a platform-conditional download that the consumer's `vcpkg-mend.json` triplets miss. 2. **Audit every vcpkg consumer graph, not only the library's own manifest.** Search all `iModelCore/libsrc/**/vcpkg.json` files for the port name and old version. Inspect both `dependencies` and `overrides`: consumers deliberately use overrides to pin transitive ports, and an explicit override takes precedence over a newer registry baseline. Update every graph that should consume the new version. OpenSSL currently appears in its own, curl, and crashpad graphs; zlib appears in several graphs. Apply the same check to minizip and every other port. 3. For each affected consumer graph, ensure `vcpkg-configuration.json` uses a registry `baseline` that contains the requested version. Updating one consumer's baseline does not affect any other manifest directory. 4. Resolve or install every affected graph with a triplet that activates platform-conditional dependencies; use each consumer's `vcpkg-mend.json` triplets as the starting point. Search again for the old version and do not finish while a relevant manifest still pins it. 5. Update every `iModelCore/libsrc/**/*NugetLicense.json` sublicense entry for the library. Use the upstream release version, without a vcpkg port revision such as `#1`, and verify that the release-pinned `licenseUrl` and `SPDX-ID` still apply to the new release. A metadata file can describe multiple ports: `compress/CompressNugetLicense.json`, for example, has separate zlib and minizip entries. Search by sublicense name instead of assuming the filename matches the port. The `iTwinNativeThirdParty` build consumes these files when publishing the corresponding NuGets, including files referenced by `NuGetProduct` definitions outside this repository. 6. No changes to `.mke` or `.PartFile.xml` files are needed solely for a version bump — the next build will pick up the new version via the binary cache or a fresh build. --- ## Pitfall: Windows debug builds link the release CRT <a id="windows-debug-builds-link-the-release-crt"></a> The bmake link settings in this pipeline use the **release CRT even in Windows DEBUG builds**. Bentley wrapper objects (e.g. `BePugiXml`, the crashpad client wrapper) are therefore compiled `MD_DynamicRelease` with `_ITERATOR_DEBUG_LEVEL=0`. **This does not affect every library — only those whose debug artifact actually depends on the debug CRT.** A vcpkg debug build is compiled with `_DEBUG` defined. If the library's source gates on `_DEBUG` at compile time and then calls debug-CRT-only functionality (or raises `_ITERATOR_DEBUG_LEVEL` to 2), that debug artifact references symbols that do not exist in the release CRT, so linking it into our release-CRT DEBUG build fails with unresolved-symbol / CRT-mismatch / `_ITERATOR_DEBUG_LEVEL` (2 vs 0) errors. Libraries whose debug artifact does not touch debug-CRT-only functionality link fine and have deliberately been left on the normal debug/release selection (e.g. `openssl`). **So this is a per-library check, not a blanket rule.** If a new Windows vcpkg library links cleanly in a DEBUG build, leave it alone. If its DEBUG link fails with the symptoms above, you have two ways to fix it. ### Fix A (simplest): make the library release-only on Windows Produce only a release artifact and link it everywhere. Two halves, do both:
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen